Skip to content

Commit

Permalink
Added assigner column and API endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
Kishan Patel authored and Kishan Patel committed Jul 3, 2024
1 parent 8d80af6 commit e796f49
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 9 deletions.
30 changes: 29 additions & 1 deletion api/routes/user_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,12 @@ def add_user_application_association(application_id, user_id):
404: If the association cannot be created.
"""
association = create_user_application_association(application_id=application_id, user_id=user_id)
args = request.get_json()
if "assigner_id" not in args:
abort(400, "Post body must contain assigner_id")
association = create_user_application_association(
application_id=application_id, user_id=user_id, assigner_id=args["assigner_id"]
)

if association:
serialiser = AllocationAssociationSchema()
Expand Down Expand Up @@ -132,3 +137,26 @@ def get_all_applications_associated_with_user(user_id, active=None):

current_app.logger.error(f"Could not find any applications associated with user {user_id}")
abort(404)


def get_all_associations_assigned_by_user(assigner_id, active=None):
"""Fetches all associations where the user is the assigner.
Parameters:
assigner_id (str): UUID of the assigner.
active (bool, optional): Filter for active associations. Defaults to None.
Returns:
list: Serialized list of application associations.
Raises:
404: If no applications are found for the given user.
"""
associations = get_user_application_associations(assigner_id=assigner_id, active=active)
if associations:
serialiser = AllocationAssociationSchema()
return serialiser.dump(associations, many=True)

current_app.logger.error(f"Could not find any applications assigned by user {assigner_id}")
abort(404)
32 changes: 32 additions & 0 deletions db/migrations/versions/~2024_07_02_1335-2737a83d7605_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""Empty message.
Revision ID: 2737a83d7605
Revises: cf1169998c27
Create Date: 2024-07-02 13:35:20.268046
"""
import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = "2737a83d7605"
down_revision = "cf1169998c27"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("allocation_association", schema=None) as batch_op:
batch_op.add_column(sa.Column("assigner_id", sa.UUID(), nullable=False))

# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###

with op.batch_alter_table("allocation_association", schema=None) as batch_op:
batch_op.drop_column("assigner_id")

# ### end Alembic commands ###
2 changes: 2 additions & 0 deletions db/models/assessment_record/allocation_association.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class AllocationAssociation(BaseModel):
Attributes:
application_id: UUID of the application, serving as a foreign key to the assessment_records table.
user_id: UUID of the user associated with the application.
assigner_id: UUID of the user creating the association (usually the lead assessor).
created_at: Timestamp of when the association was created, defaulting to the current time.
active: Boolean indicating whether the association is active, defaulting to True.
log: JSONB object containing a log of changes made to the association.
Expand All @@ -38,6 +39,7 @@ class AllocationAssociation(BaseModel):
nullable=False,
)
user_id = db.Column(UUID(as_uuid=True), nullable=False)
assigner_id = db.Column(UUID(as_uuid=True), nullable=False)
created_at = db.Column(db.DateTime(timezone=True), server_default=func.now())
active = db.Column(db.Boolean(), nullable=False, default=True)
log = db.Column(JSONB, nullable=False)
Expand Down
8 changes: 6 additions & 2 deletions db/queries/assessment_records/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -979,24 +979,28 @@ def combine_dicts(applications_list, scores_list):
return combined_list


def get_user_application_associations(application_id=None, user_id=None, active=None):
def get_user_application_associations(application_id=None, user_id=None, assigner_id=None, active=None):
query = db.session.query(AllocationAssociation)
if application_id:
query = query.filter(AllocationAssociation.application_id == application_id)

if user_id:
query = query.filter(AllocationAssociation.user_id == user_id)

if assigner_id:
query = query.filter(AllocationAssociation.assigner_id == assigner_id)

if active is not None:
query = query.filter(AllocationAssociation.active == active)

return query.all()


def create_user_application_association(application_id, user_id):
def create_user_application_association(application_id, user_id, assigner_id):
allocation_association = AllocationAssociation(
user_id=user_id,
application_id=application_id,
assigner_id=assigner_id,
active=True,
log={datetime.now(tz=timezone.utc).isoformat(): "activated"},
)
Expand Down
36 changes: 36 additions & 0 deletions openapi/api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1418,6 +1418,15 @@ paths:
type: object
items:
$ref: 'components.yml#/components/schemas/UserAssociation'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
assigner_id:
type: string
put:
tags:
- application user association
Expand Down Expand Up @@ -1480,3 +1489,30 @@ paths:
type: array
items:
$ref: 'components.yml#/components/schemas/UserAssociation'
'/user/{assigner_id}/assignees':
get:
tags:
- application user association
summary: Get all users associations that have been assigned by the user
description: returns all users associations that have been assigned by the user
operationId: api.routes.user_routes.get_all_associations_assigned_by_user
parameters:
- name: assigner_id
in: path
schema:
type: string
required: true
- name: active
in: query
schema:
type: boolean
required: false
responses:
200:
description: SUCCESS - A list of applications
content:
application/json:
schema:
type: array
items:
$ref: 'components.yml#/components/schemas/UserAssociation'
21 changes: 17 additions & 4 deletions tests/test_db_allocation_associations.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,18 @@
def test_get_users_for_application(_db, seed_application_records):
user_id_1 = str(uuid.uuid4())
user_id_2 = str(uuid.uuid4())
assigner_id = str(uuid.uuid4())
app_id = seed_application_records[0]["application_id"]
allocation_association_1 = AllocationAssociation(
user_id=user_id_1,
assigner_id=assigner_id,
application_id=app_id,
active=True,
log={datetime.now(tz=timezone.utc).isoformat(): "activated"},
)
allocation_association_2 = AllocationAssociation(
user_id=user_id_2,
assigner_id=assigner_id,
application_id=app_id,
active=False,
log={datetime.now(tz=timezone.utc).isoformat(): "deactivated"},
Expand All @@ -46,16 +49,20 @@ def test_get_users_for_application(_db, seed_application_records):
@pytest.mark.apps_to_insert([{**test_input_data[0]}, {**test_input_data[1]}])
def test_get_applications_for_user(_db, seed_application_records):
user_id = str(uuid.uuid4())
assigner_id_1 = str(uuid.uuid4())
assigner_id_2 = str(uuid.uuid4())
app_id_1 = seed_application_records[0]["application_id"]
app_id_2 = seed_application_records[1]["application_id"]
allocation_association_1 = AllocationAssociation(
user_id=user_id,
assigner_id=assigner_id_1,
application_id=app_id_1,
active=True,
log={datetime.now(tz=timezone.utc).isoformat(): "activated"},
)
allocation_association_2 = AllocationAssociation(
user_id=user_id,
assigner_id=assigner_id_2,
application_id=app_id_2,
active=False,
log={datetime.now(tz=timezone.utc).isoformat(): "deactivated"},
Expand All @@ -75,12 +82,17 @@ def test_get_applications_for_user(_db, seed_application_records):
assert len(inactive_applications) == 1
assert str(inactive_applications[0].application_id) == app_id_2

assigner_1_applications = get_user_application_associations(assigner_id=assigner_id_1)
assert len(assigner_1_applications) == 1
assert str(assigner_1_applications[0].application_id) == app_id_1


@pytest.mark.apps_to_insert([{**test_input_data[0]}])
def test_create_user_application_association(_db, seed_application_records):
user_id = str(uuid.uuid4())
assigner_id = str(uuid.uuid4())
app_id = seed_application_records[0]["application_id"]
new_association = create_user_application_association(app_id, user_id)
new_association = create_user_application_association(app_id, user_id, assigner_id)
assert new_association is not None
assert str(new_association.application_id) == app_id
assert str(new_association.user_id) == user_id
Expand All @@ -95,8 +107,9 @@ def test_create_user_application_association(_db, seed_application_records):
@pytest.mark.apps_to_insert([{**test_input_data[0]}])
def test_update_user_application_association(_db, seed_application_records):
user_id = str(uuid.uuid4())
assigner_id = str(uuid.uuid4())
app_id = seed_application_records[0]["application_id"]
create_user_application_association(app_id, user_id)
create_user_application_association(app_id, user_id, assigner_id)
updated_association = update_user_application_association(app_id, user_id, active=False)
assert updated_association.active is False
assert len(updated_association.log.keys()) == 2
Expand All @@ -111,7 +124,7 @@ def test_update_user_application_association(_db, seed_application_records):
def test_duplicate_user_application_association(_db, seed_application_records):
user_id = str(uuid.uuid4())
app_id = seed_application_records[0]["application_id"]
updated_association = create_user_application_association(app_id, user_id)
duplicate_return_value = create_user_application_association(app_id, user_id)
updated_association = create_user_application_association(app_id, user_id, str(uuid.uuid4()))
duplicate_return_value = create_user_application_association(app_id, user_id, str(uuid.uuid4()))
assert updated_association is not None
assert duplicate_return_value is None
45 changes: 43 additions & 2 deletions tests/test_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,13 +421,15 @@ def test_get_all_users_associated_with_application(client):
{
"application_id": "app1",
"user_id": "user1",
"assigner_id": "assigner1",
"created_at": datetime(2024, 6, 10, 15, 35, 47, 999),
"active": True,
"log": "{'activated': '2024-06-10T15:35:47Z'}",
},
{
"application_id": "app1",
"user_id": "user2",
"assigner_id": "assigner1",
"created_at": datetime(2024, 6, 10, 15, 35, 47, 999),
"active": False,
"log": "{'activated': '2024-06-10T15:35:47Z', 'deactivated': '2024-06-11T15:35:47Z'}",
Expand All @@ -452,6 +454,7 @@ def test_get_user_application_association(client):
mock_association = {
"application_id": "app1",
"user_id": "user1",
"assigner_id": "assigner1",
"created_at": datetime(2024, 6, 10, 15, 35, 47, 999),
"active": True,
"log": "{'activated': '2024-06-10T15:35:47Z'}",
Expand All @@ -474,6 +477,7 @@ def test_add_user_application_association(client):
mock_association = {
"application_id": "app1",
"user_id": "user1",
"assigner_id": "assigner1",
"created_at": datetime(2024, 6, 10, 15, 35, 47, 999),
"active": True,
"log": "{'activated': '2024-06-10T15:35:47Z'}",
Expand All @@ -485,17 +489,18 @@ def test_add_user_application_association(client):
with mock.patch(
"api.routes.user_routes.create_user_application_association", return_value=mock_association
) as mock_create_association:
response = client.post("/application/app1/user/user1")
response = client.post("/application/app1/user/user1", json={"assigner_id": "assigner1"})

assert response.status_code == 201
assert response.json == expected_response
mock_create_association.assert_called_once_with(application_id="app1", user_id="user1")
mock_create_association.assert_called_once_with(application_id="app1", user_id="user1", assigner_id="assigner1")


def test_update_user_application_association(client):
mock_association = {
"application_id": "app1",
"user_id": "user1",
"assigner_id": "assigner1",
"created_at": datetime(2024, 6, 10, 15, 35, 47, 999),
"active": False,
"log": "{'activated': '2024-06-10T15:35:47Z', 'deactivated': '2024-06-11T15:35:47Z'}",
Expand All @@ -519,13 +524,15 @@ def test_get_all_applications_associated_with_user(client):
{
"application_id": "app1",
"user_id": "user1",
"assigner_id": "assigner1",
"created_at": datetime(2024, 6, 10, 15, 35, 47, 999),
"active": True,
"log": "{'activated': '2024-06-10T15:35:47Z'}",
},
{
"application_id": "app2",
"user_id": "user1",
"assigner_id": "assigner1",
"created_at": datetime(2024, 6, 11, 15, 35, 47, 999),
"active": False,
"log": "{'activated': '2024-06-10T15:35:47Z', 'deactivated': '2024-06-11T15:35:47Z'}",
Expand All @@ -544,3 +551,37 @@ def test_get_all_applications_associated_with_user(client):
assert response.status_code == 200
assert response.json == expected_response
mock_get_applications.assert_called_once_with(user_id="user1", active=None)


def test_get_all_applications_assigned_by_user(client):
mock_applications = [
{
"application_id": "app1",
"user_id": "user1",
"assigner_id": "assigner1",
"created_at": datetime(2024, 6, 10, 15, 35, 47, 999),
"active": True,
"log": "{'activated': '2024-06-10T15:35:47Z'}",
},
{
"application_id": "app2",
"user_id": "user2",
"assigner_id": "assigner1",
"created_at": datetime(2024, 6, 11, 15, 35, 47, 999),
"active": False,
"log": "{'activated': '2024-06-10T15:35:47Z', 'deactivated': '2024-06-11T15:35:47Z'}",
},
]

expected_response = deepcopy(mock_applications)
expected_response[0]["created_at"] = expected_response[0]["created_at"].isoformat()
expected_response[1]["created_at"] = expected_response[1]["created_at"].isoformat()

with mock.patch(
"api.routes.user_routes.get_user_application_associations", return_value=mock_applications
) as mock_get_applications:
response = client.get("/user/assigner1/assignees")

assert response.status_code == 200
assert response.json == expected_response
mock_get_applications.assert_called_once_with(assigner_id="assigner1", active=None)

0 comments on commit e796f49

Please sign in to comment.