Skip to content

Commit

Permalink
Added API routes and db tests for user application association
Browse files Browse the repository at this point in the history
  • Loading branch information
Kishan Patel authored and Kishan Patel committed Jun 21, 2024
1 parent 1765974 commit ef72b12
Show file tree
Hide file tree
Showing 7 changed files with 275 additions and 12 deletions.
69 changes: 61 additions & 8 deletions api/routes/user_routes.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,67 @@
def get_all_users_associated_with_application():
pass
from db.queries.assessment_records.queries import create_user_application_association
from db.queries.assessment_records.queries import get_user_application_associations
from db.queries.assessment_records.queries import (
update_user_application_association as update_user_application_association_db,
)
from db.schemas.schemas import AllocationAssociationSchema
from flask import abort
from flask import current_app
from flask import request


def get_user_application_association():
pass
def get_all_users_associated_with_application(application_id, active=None):
associations = get_user_application_associations(application_id=application_id, active=active)
if associations:
serialiser = AllocationAssociationSchema()
return serialiser.dump(associations, many=True)

current_app.logger.error(f"Could not find any users associated with application {application_id}")
abort(404)

def add_user_application_association():
pass

def get_user_application_association(application_id, user_id):
association = get_user_application_associations(application_id=application_id, user_id=user_id)

def update_user_application_association():
pass
if association:
serialiser = AllocationAssociationSchema()
return serialiser.dump(association[0])

current_app.logger.error(f"Could not find association between {user_id} and application {application_id}")
abort(404)


def add_user_application_association(application_id, user_id):
association = create_user_application_association(application_id=application_id, user_id=user_id)

if association:
serialiser = AllocationAssociationSchema()
return serialiser.dump(association)

current_app.logger.error(f"Could not create association between {user_id} and application {application_id}")
abort(404)


def update_user_application_association(application_id, user_id):
args = request.get_json()
if "active" not in args:
abort(400)

active = True if args.get("active").lower() == "true" else False
association = update_user_application_association_db(application_id=application_id, user_id=user_id, active=active)

if association:
serialiser = AllocationAssociationSchema()
return serialiser.dump(association)

current_app.logger.error(f"Could not update association between {user_id} and application {application_id}")
abort(404)


def get_all_applications_associated_with_user(user_id, active=None):
associations = get_user_application_associations(user_id=user_id, active=active)
if associations:
serialiser = AllocationAssociationSchema()
return serialiser.dump(associations, many=True)

current_app.logger.error(f"Could not find any applications associated with user {user_id}")
abort(404)
51 changes: 51 additions & 0 deletions db/queries/assessment_records/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""
import json
from datetime import datetime
from datetime import timezone
from typing import Dict
from typing import List

Expand All @@ -15,6 +16,7 @@
from db import db
from db.models.assessment_record import AssessmentRecord
from db.models.assessment_record import TagAssociation
from db.models.assessment_record.allocation_association import AllocationAssociation
from db.models.assessment_record.enums import Status
from db.models.flags.flag_update import FlagStatus
from db.models.score import Score
Expand Down Expand Up @@ -968,3 +970,52 @@ def combine_dicts(applications_list, scores_list):
combined_list.append(application_with_nulls)

return combined_list


def get_user_application_associations(application_id=None, user_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 active is not None:
query = query.filter(AllocationAssociation.active == active)

return query.all()


def create_user_application_association(application_id, user_id):
allocation_association = AllocationAssociation(
user_id=user_id,
application_id=application_id,
active=True,
log={datetime.now(tz=timezone.utc).isoformat(): "activated"},
)
try:
db.session.add(allocation_association)
db.session.commit()
db.session.refresh(allocation_association)
except exc.IntegrityError:
db.session.rollback()
return None

return allocation_association


def update_user_application_association(application_id, user_id, active):
allocation_association = (
db.session.query(AllocationAssociation)
.filter(AllocationAssociation.application_id == application_id, AllocationAssociation.user_id == user_id)
.one_or_none()
)
allocation_association.active = active
allocation_association.log = {
**allocation_association.log,
datetime.now(tz=timezone.utc).isoformat(): "activated" if active else "deactivated",
}
db.session.commit()
db.session.refresh(allocation_association)

return allocation_association
9 changes: 9 additions & 0 deletions db/schemas/schemas.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from db.models.assessment_record import AssessmentRecord
from db.models.assessment_record.allocation_association import AllocationAssociation
from db.models.assessment_record.enums import Language
from db.models.assessment_record.enums import Status
from db.models.assessment_record.tag_association import TagAssociation
Expand Down Expand Up @@ -196,3 +197,11 @@ class AssessmentRoundMetadata(SQLAlchemyAutoSchema):

class Meta:
model = AssessmentRound


class AllocationAssociationSchema(SQLAlchemyAutoSchema):
log = String()
application_id = UUID()

class Meta:
model = AllocationAssociation
35 changes: 31 additions & 4 deletions openapi/api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1385,11 +1385,11 @@ paths:
required: true
responses:
200:
description: SUCCESS - A list of users
description: SUCCESS - An user application association
content:
application/json:
schema:
type: array
type: object
items:
$ref: 'components.yml#/components/schemas/UserAssociation'
post:
Expand Down Expand Up @@ -1446,10 +1446,37 @@ paths:
$ref: 'components.yml#/components/schemas/UserAssociation'
responses:
200:
description: SUCCESS - Tag Updated
description: SUCCESS - Association Updated
content:
application/json:
schema:
type: object
items:
$ref: 'components.yml#/components/schemas/UserAssociation'
'/user/{user_id}/applications':
get:
tags:
- application user association
summary: Get all applications that are assigned to the user
description: returns all applications assigned to the user
operationId: api.routes.user_routes.get_all_applications_associated_with_user
parameters:
- name: user_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/Tag'
$ref: 'components.yml#/components/schemas/UserAssociation'
4 changes: 4 additions & 0 deletions openapi/components.yml
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,10 @@ components:
type: string
description: uuid of user who associated
example: "uuif7eaf61b-093e-4464-a4da-b36b849f0a1b"
created:
type: string
description: Date time the association was created
example : "2024-06-10T15:35:47Z"
active:
type: boolean
description: If the association is active
Expand Down
2 changes: 2 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from app import create_app
from db.models import AssessmentRound
from db.models.assessment_record import AssessmentRecord
from db.models.assessment_record.allocation_association import AllocationAssociation
from db.models.assessment_record.tag_association import TagAssociation
from db.models.comment import Comment
from db.models.comment import CommentsUpdate
Expand Down Expand Up @@ -90,6 +91,7 @@ def seed_application_records(request, app, clear_test_data, enable_preserve_test
# Supplied the rows we inserted for tests to use in their actions
yield inserted_applications

AllocationAssociation.query.delete()
FlagUpdate.query.delete()
AssessmentFlag.query.delete()
TagAssociation.query.delete()
Expand Down
117 changes: 117 additions & 0 deletions tests/test_allocation_associations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import uuid
from datetime import datetime
from datetime import timezone

import pytest
from db.models.assessment_record.allocation_association import AllocationAssociation
from db.queries.assessment_records.queries import create_user_application_association
from db.queries.assessment_records.queries import get_user_application_associations
from db.queries.assessment_records.queries import update_user_application_association
from tests.conftest import test_input_data


@pytest.mark.apps_to_insert([{**test_input_data[0]}])
def test_get_users_for_application(_db, seed_application_records):
user_id_1 = str(uuid.uuid4())
user_id_2 = str(uuid.uuid4())
app_id = seed_application_records[0]["application_id"]
allocation_association_1 = AllocationAssociation(
user_id=user_id_1,
application_id=app_id,
active=True,
log={datetime.now(tz=timezone.utc).isoformat(): "activated"},
)
allocation_association_2 = AllocationAssociation(
user_id=user_id_2,
application_id=app_id,
active=False,
log={datetime.now(tz=timezone.utc).isoformat(): "deactivated"},
)
_db.session.add(allocation_association_1)
_db.session.commit()
_db.session.add(allocation_association_2)
_db.session.commit()
users = get_user_application_associations(application_id=app_id)
assert len(users) == 2

active_users = get_user_application_associations(application_id=app_id, active=True)
assert len(active_users) == 1
assert str(active_users[0].user_id) == user_id_1

inactive_users = get_user_application_associations(application_id=app_id, active=False)
assert len(inactive_users) == 1
assert str(inactive_users[0].user_id) == user_id_2


@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())
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,
application_id=app_id_1,
active=True,
log={datetime.now(tz=timezone.utc).isoformat(): "activated"},
)
allocation_association_2 = AllocationAssociation(
user_id=user_id,
application_id=app_id_2,
active=False,
log={datetime.now(tz=timezone.utc).isoformat(): "deactivated"},
)
_db.session.add(allocation_association_1)
_db.session.commit()
_db.session.add(allocation_association_2)
_db.session.commit()
applications = get_user_application_associations(user_id=user_id)
assert len(applications) == 2

active_applications = get_user_application_associations(user_id=user_id, active=True)
assert len(active_applications) == 1
assert str(active_applications[0].application_id) == app_id_1

inactive_applications = get_user_application_associations(user_id=user_id, active=False)
assert len(inactive_applications) == 1
assert str(inactive_applications[0].application_id) == app_id_2


@pytest.mark.apps_to_insert([{**test_input_data[0]}])
def test_create_user_application_association(_db, seed_application_records):
user_id = str(uuid.uuid4())
app_id = seed_application_records[0]["application_id"]
new_association = create_user_application_association(app_id, user_id)
assert new_association is not None
assert str(new_association.application_id) == app_id
assert str(new_association.user_id) == user_id
assert new_association.active is True
assert "activated" == list(new_association.log.values())[0]
try:
datetime.fromisoformat(list(new_association.log.keys())[0])
except ValueError:
assert False, "log keys should be in isoformat"


@pytest.mark.apps_to_insert([{**test_input_data[0]}])
def test_update_user_application_association(_db, seed_application_records):
user_id = str(uuid.uuid4())
app_id = seed_application_records[0]["application_id"]
create_user_application_association(app_id, user_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
logs = [(datetime.fromisoformat(key), value) for key, value in updated_association.log.items()]

# The timestamp for deactivated should be after activated
assert logs[0][1] == "activated" if logs[0][0] < logs[1][0] else "deactivated"
assert logs[1][1] == "deactivated" if logs[0][0] < logs[1][0] else "activated"


@pytest.mark.apps_to_insert([{**test_input_data[0]}])
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)
assert updated_association is not None
assert duplicate_return_value is None

0 comments on commit ef72b12

Please sign in to comment.