Skip to content

Commit

Permalink
RAS-1350 Create enrolment end point for party
Browse files Browse the repository at this point in the history
  • Loading branch information
LJBabbage committed Nov 14, 2024
1 parent 1a9fb2f commit 58d5df4
Show file tree
Hide file tree
Showing 10 changed files with 353 additions and 2 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ build:
pipenv install --dev

lint:
pipenv check -i 42194 -i 70612 -i 70624 -i 72731
pipenv check -i 42194 -i 70612 -i 70624 -i 72731 -i 73655
pipenv run isort .
pipenv run black --line-length 120 .
pipenv run flake8 --exclude=./scripts

lint-check:
pipenv check -i 42194 -i 70612 -i 70624 -i 72731
pipenv check -i 42194 -i 70612 -i 70624 -i 72731 -i 73655
pipenv run isort . --check-only
pipenv run black --line-length 120 --check .
pipenv run flake8 --exclude=./scripts
Expand Down
33 changes: 33 additions & 0 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ tags:
description: Respondent endpoints
- name: businesses
description: Business endpoints
- name: enrolments
description: enrolments endpoints
- name: info
description: Information endpoints
- name: misc
Expand Down Expand Up @@ -821,6 +823,37 @@ paths:
description: The password reset counter has been reset
404:
description: The respondent does not exist
/enrolments:
get:
tags:
- enrolments
summary: get enrolment details
description: returns a list of all enrolments that match given parameters
responses:
200:
description: list of dict enrolments
content:
application/json:
schema:
type: array
items:
type: object
properties:
business_id:
type: string
format: uuid
respondent_id:
type: integer
status:
type: string
example: ENABLED
survey_id:
type: string
format: uuid
400:
description: Missing of malformed parameters
404:
description: Respondent doesn't exist
/batch/respondents:
delete:
tags:
Expand Down
33 changes: 33 additions & 0 deletions ras_party/controllers/enrolments_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import logging
from uuid import UUID

import structlog
from flask import session
from sqlalchemy.orm.exc import NoResultFound

from ras_party.controllers.queries import (
query_enrolments_by_parameters,
query_respondent_by_party_uuid,
)
from ras_party.models.models import Enrolment
from ras_party.support.session_decorator import with_query_only_db_session

logger = structlog.wrap_logger(logging.getLogger(__name__))


@with_query_only_db_session
def enrolments_by_parameters(
session: session, party_uuid: UUID = None, business_id: UUID = None, survey_id: UUID = None, status: int = None
) -> list[Enrolment]:
"""
returns a list of Enrolments based on provided parameters
"""

if party_uuid:
respondent = query_respondent_by_party_uuid(party_uuid, session)
if not respondent:
raise NoResultFound

respondent_id = respondent.id if party_uuid else None

return query_enrolments_by_parameters(session, respondent_id, business_id, survey_id, status)
20 changes: 20 additions & 0 deletions ras_party/controllers/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -643,3 +643,23 @@ def count_enrolment_by_survey_business(business_id, survey_id, session):
.count()
)
return response


def query_enrolments_by_parameters(
session: session, respondent_id: int = None, business_id: UUID = None, survey_id: UUID = None, status: int = None
) -> list[Enrolment]:
"""
Query to return a list of enrolments based on parameters
"""
conditions = []

if respondent_id:
conditions.append(Enrolment.respondent_id == respondent_id)
if business_id:
conditions.append(Enrolment.business_id == business_id)
if survey_id:
conditions.append(Enrolment.survey_id == survey_id)
if status:
conditions.append(Enrolment.status == status)

return session.query(Enrolment).filter(and_(*conditions)).all()
8 changes: 8 additions & 0 deletions ras_party/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,14 @@ class Enrolment(Base):
),
)

def to_dict(self) -> dict:
return {
"business_id": self.business_id,
"respondent_id": self.respondent_id,
"survey_id": self.survey_id,
"status": self.status.name,
}


class PendingSurveys(Base):
__tablename__ = "pending_surveys"
Expand Down
60 changes: 60 additions & 0 deletions ras_party/views/enrolments_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import logging

import structlog
from flask import Blueprint, Response, current_app, make_response, request
from flask_httpauth import HTTPBasicAuth
from sqlalchemy.exc import DataError
from sqlalchemy.orm.exc import NoResultFound
from werkzeug.exceptions import BadRequest, NotFound

from ras_party.controllers.enrolments_controller import enrolments_by_parameters

logger = structlog.wrap_logger(logging.getLogger(__name__))
enrolments_view = Blueprint("enrolments_view", __name__)
auth = HTTPBasicAuth()


@enrolments_view.before_request
@auth.login_required
def before_respondent_view():
pass


@auth.get_password
def get_pw(username):
config_username = current_app.config["SECURITY_USER_NAME"]
config_password = current_app.config["SECURITY_USER_PASSWORD"]
if username == config_username:
return config_password


@enrolments_view.route("/enrolments", methods=["GET"])
def get_enrolments() -> Response:
json = request.get_json()
party_uuid = json.get("party_uuid")
business_id = json.get("business_id")
survey_id = json.get("survey_id")
status = json.get("status")

if not (party_uuid or business_id or survey_id):
logger.error("No parameters passed to get_enrolments")
return BadRequest()

try:
enrolments = enrolments_by_parameters(
party_uuid=party_uuid, business_id=business_id, survey_id=survey_id, status=status
)
except NoResultFound:
logger.error(f"Respondent not found for party_uuid {party_uuid}")
return NotFound()
except DataError:
logger.error(
"Data error, enrolment search parameters are not valid",
party_uuid=party_uuid,
business_id=business_id,
survey_id=survey_id,
status=status,
)
return BadRequest()

return make_response([enrolment.to_dict() for enrolment in enrolments], 200)
2 changes: 2 additions & 0 deletions run.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def create_app(config=None):
from ras_party.views.account_view import account_view
from ras_party.views.batch_request import batch_request
from ras_party.views.business_view import business_view
from ras_party.views.enrolments_view import enrolments_view
from ras_party.views.info_view import info_view
from ras_party.views.party_view import party_view
from ras_party.views.pending_survey_view import pending_survey_view
Expand All @@ -40,6 +41,7 @@ def create_app(config=None):
app.register_blueprint(respondent_view, url_prefix="/party-api/v1")
app.register_blueprint(batch_request, url_prefix="/party-api/v1")
app.register_blueprint(pending_survey_view, url_prefix="/party-api/v1")
app.register_blueprint(enrolments_view, url_prefix="/party-api/v1")
app.register_blueprint(info_view)
app.register_blueprint(error_handlers.blueprint)

Expand Down
4 changes: 4 additions & 0 deletions test/party_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,3 +444,7 @@ def get_respondents_by_survey_and_business_id(self, survey_id, business_id):
def get_respondents_by_party_id(self, party_id):
response = self.client.get(f"/party-api/v1/respondents/party_id/{party_id}", headers=self.auth_headers)
return response

def get_enrolments(self, payload):
response = self.client.get("/party-api/v1/enrolments", json=payload, headers=self.auth_headers)
return response
138 changes: 138 additions & 0 deletions test/test_enrolments_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
from test.party_client import PartyTestClient

from sqlalchemy.exc import DataError
from sqlalchemy.orm.exc import NoResultFound

from ras_party.controllers.enrolments_controller import enrolments_by_parameters
from ras_party.models.models import (
Business,
BusinessRespondent,
Enrolment,
EnrolmentStatus,
Respondent,
)
from ras_party.support.session_decorator import with_db_session

respondents_enrolments = [
{
"respondent": "b6f9d6e8-b840-4c95-a6ce-9ef145dd1f85",
"enrolment_details": [
{
"business": "75d9af56-1225-4d43-b41d-1199f5f89daa",
"survey_id": "9200d295-9d6e-41fe-b541-747ae67a279f",
"status": EnrolmentStatus.ENABLED,
},
{
"business": "98e2c9dd-a760-47dd-ba18-439fd5fb93a3",
"survey_id": "c641f6ad-a5eb-4d82-a647-7cd586549bbc",
"status": EnrolmentStatus.ENABLED,
},
],
},
{
"respondent": "5718649e-30bf-4c25-a2c0-aaa733e54ed6",
"enrolment_details": [
{
"business": "af25c9d5-6893-4342-9d24-4b88509e965f",
"survey_id": "9200d295-9d6e-41fe-b541-747ae67a279f",
"status": EnrolmentStatus.ENABLED,
},
{
"business": "75d9af56-1225-4d43-b41d-1199f5f89daa",
"survey_id": "9200d295-9d6e-41fe-b541-747ae67a279f",
"status": EnrolmentStatus.DISABLED,
},
],
},
]


class TestEnrolments(PartyTestClient):

def setUp(self):
self._add_enrolments()

def test_get_enrolments_party_id(self):
enrolments = enrolments_by_parameters(party_uuid="b6f9d6e8-b840-4c95-a6ce-9ef145dd1f85")

self.assertEqual(len(enrolments), 2)
self.assertIn(str(enrolments[0].business_id), "75d9af56-1225-4d43-b41d-1199f5f89daa")
self.assertIn(str(enrolments[1].business_id), "98e2c9dd-a760-47dd-ba18-439fd5fb93a3")

def test_get_enrolments_business_id(self):
enrolments = enrolments_by_parameters(business_id="75d9af56-1225-4d43-b41d-1199f5f89daa")

self.assertEqual(len(enrolments), 2)
self.assertIn(str(enrolments[0].survey_id), "9200d295-9d6e-41fe-b541-747ae67a279f")
self.assertIn(str(enrolments[1].survey_id), "9200d295-9d6e-41fe-b541-747ae67a279f")

def test_get_enrolments_survey_id(self):
enrolments = enrolments_by_parameters(survey_id="9200d295-9d6e-41fe-b541-747ae67a279f")

self.assertEqual(len(enrolments), 3)
self.assertIn(str(enrolments[0].business_id), "75d9af56-1225-4d43-b41d-1199f5f89daa")
self.assertIn(str(enrolments[1].business_id), "af25c9d5-6893-4342-9d24-4b88509e965f")
self.assertIn(str(enrolments[2].business_id), "75d9af56-1225-4d43-b41d-1199f5f89daa")

def test_get_enrolments_party_id_and_business_id_and_survey_id(self):
enrolments = enrolments_by_parameters(
party_uuid="b6f9d6e8-b840-4c95-a6ce-9ef145dd1f85",
business_id="75d9af56-1225-4d43-b41d-1199f5f89daa",
survey_id="9200d295-9d6e-41fe-b541-747ae67a279f",
)

self.assertEqual(len(enrolments), 1)
self.assertIn(str(enrolments[0].respondent_id), "b6f9d6e8-b840-4c95-a6ce-9ef145dd1f85")
self.assertIn(str(enrolments[0].business_id), "75d9af56-1225-4d43-b41d-1199f5f89daa")
self.assertIn(str(enrolments[0].survey_id), "9200d295-9d6e-41fe-b541-747ae67a279f")

def test_get_enrolments_party_id_enabled(self):
enrolments = enrolments_by_parameters(
party_uuid="5718649e-30bf-4c25-a2c0-aaa733e54ed6", status=EnrolmentStatus.ENABLED
)

self.assertEqual(len(enrolments), 1)
self.assertIn(str(enrolments[0].business_id), "af25c9d5-6893-4342-9d24-4b88509e965f")
self.assertIn(str(enrolments[0].survey_id), "9200d295-9d6e-41fe-b541-747ae67a279f")

def test_get_enrolments_party_id_disabled(self):
enrolments = enrolments_by_parameters(
party_uuid="5718649e-30bf-4c25-a2c0-aaa733e54ed6", status=EnrolmentStatus.DISABLED
)

self.assertEqual(len(enrolments), 1)
self.assertIn(str(enrolments[0].business_id), "75d9af56-1225-4d43-b41d-1199f5f89daa")
self.assertIn(str(enrolments[0].survey_id), "9200d295-9d6e-41fe-b541-747ae67a279f")

def test_get_enrolments_party_id_not_found_respondent(self):
with self.assertRaises(NoResultFound):
enrolments_by_parameters(party_uuid="e6a016da-f7e8-4cb0-88da-9d34a7c1382a")

def test_get_enrolments_party_id_data_error(self):
with self.assertRaises(DataError):
enrolments_by_parameters(party_uuid="malformed_id")

@with_db_session
def _add_enrolments(self, session):
businesses = {}

for respondent_enrolments in respondents_enrolments:
respondent = Respondent(party_uuid=respondent_enrolments["respondent"])
session.add(respondent)

for enrolment in respondent_enrolments["enrolment_details"]:
if not (business := businesses.get(enrolment["business"])):
business = Business(party_uuid=enrolment["business"])
session.add(business)
businesses[enrolment["business"]] = business

business_respondent = BusinessRespondent(business=business, respondent=respondent)
session.add(business_respondent)
session.flush()
enrolment = Enrolment(
business_id=business.party_uuid,
survey_id=enrolment["survey_id"],
respondent_id=respondent.id,
status=enrolment["status"],
)
session.add(enrolment)
Loading

0 comments on commit 58d5df4

Please sign in to comment.