From d799464d5f3f153cedde0200dab82145434ebb12 Mon Sep 17 00:00:00 2001 From: LJBabbage Date: Thu, 9 Jan 2025 09:53:55 +0000 Subject: [PATCH] RAS-1400 use native sql (#450) * RAS-1400 use native sql --- _infra/helm/party/Chart.yaml | 4 +- openapi.yaml | 46 ++++++++++++------ .../controllers/enrolments_controller.py | 12 ++--- ras_party/controllers/queries.py | 28 ++++++----- test/test_enrolments_controller.py | 47 +++++++++++++------ 5 files changed, 88 insertions(+), 49 deletions(-) diff --git a/_infra/helm/party/Chart.yaml b/_infra/helm/party/Chart.yaml index 96975872..eb01a68d 100644 --- a/_infra/helm/party/Chart.yaml +++ b/_infra/helm/party/Chart.yaml @@ -14,8 +14,8 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. -version: 2.5.14 +version: 2.5.15 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. -appVersion: 2.5.14 +appVersion: 2.5.15 diff --git a/openapi.yaml b/openapi.yaml index 92f74b69..24d97ece 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -839,23 +839,11 @@ paths: format: uuid responses: 200: - description: list of dict enrolments + description: dict of respondent enrolments and associated business and survey details content: application/json: schema: - type: array - items: - type: object - properties: - business_id: - type: string - format: uuid - status: - type: string - example: ENABLED - survey_id: - type: string - format: uuid + $ref: '#/components/schemas/RespondentEnrolments' 400: description: Missing of malformed parameters 404: @@ -1726,4 +1714,32 @@ components: time_shared: type: string format: date-time - example: 2020-01-01T00:00:00Z \ No newline at end of file + example: 2020-01-01T00:00:00Z + RespondentEnrolments: + type: object + properties: + enrolment_status: + type: string + enum: [PENDING, ENABLED, DISABLED, SUSPENDED] + business_details: + type: object + properties: + id: + format: uuid + name: + type: string + trading_as: + type: string + ref: + type: string + survey_details: + type: object + properties: + id: + format: uuid + long_name: + type: string + short_name: + type: string + ref: + type: string diff --git a/ras_party/controllers/enrolments_controller.py b/ras_party/controllers/enrolments_controller.py index b9e3b198..7c9658fc 100644 --- a/ras_party/controllers/enrolments_controller.py +++ b/ras_party/controllers/enrolments_controller.py @@ -31,22 +31,22 @@ def respondent_enrolments( enrolments = query_respondent_enrolments(session, respondent.id, business_id, survey_id, status) - if not enrolments: + if enrolments.rowcount == 0: return [] surveys_details = get_surveys_details() respondents_enrolled = [] - for enrolment, business_ref, business_attributes in enrolments: + for enrolment in enrolments: survey_id = enrolment.survey_id respondents_enrolled.append( ( { - "enrolment_status": enrolment.status.name, + "enrolment_status": enrolment.status, "business_details": { "id": enrolment.business_id, - "name": business_attributes["name"], - "trading_as": business_attributes["trading_as"], - "ref": business_ref, + "name": enrolment.attributes["name"], + "trading_as": enrolment.attributes["trading_as"], + "ref": enrolment.business_ref, }, "survey_details": { "id": survey_id, diff --git a/ras_party/controllers/queries.py b/ras_party/controllers/queries.py index a3707213..36b39c88 100644 --- a/ras_party/controllers/queries.py +++ b/ras_party/controllers/queries.py @@ -5,6 +5,7 @@ import structlog from flask import session from sqlalchemy import and_, distinct, func, or_ +from sqlalchemy.sql import text from sqlalchemy.sql.functions import count from ras_party.models.models import ( @@ -649,21 +650,24 @@ def query_respondent_enrolments( session: session, respondent_id: int, business_id: UUID = None, survey_id: UUID = None, status: int = None ) -> list[Enrolment]: """ - Query to return a list of respondent Enrolments. Business_id, survey_id and status can also be added as conditions + Query to return a list of respondent Enrolments and business attributes. + Business_id, survey_id and status can also be added as conditions """ - additional_conditions = [] + where_clause = f"WHERE respondent_id = {respondent_id}" if business_id: - additional_conditions.append(Enrolment.business_id == business_id) + where_clause += f" and partysvc.enrolment.business_id='{business_id}'" if survey_id: - additional_conditions.append(Enrolment.survey_id == survey_id) + where_clause += f" and partysvc.enrolment.survey_id='{survey_id}'" if status: - additional_conditions.append(Enrolment.status == status) - - return ( - session.query(Enrolment, Business.business_ref, BusinessAttributes.attributes) - .join(Business, Business.party_uuid == Enrolment.business_id) - .join(BusinessAttributes, BusinessAttributes.business_id == Enrolment.business_id) - .filter(and_(Enrolment.respondent_id == respondent_id, *additional_conditions)) - .all() + where_clause += f" and partysvc.enrolment.status='{status}'" + + return session.execute( + text( + f"SELECT business_id, status, survey_id, business_ref, attributes from partysvc.enrolment " + f"inner join partysvc.business on partysvc.business.party_uuid = partysvc.enrolment.business_id, " + f"LATERAL (SELECT attributes FROM partysvc.business_attributes " + f"WHERE partysvc.enrolment.business_id = partysvc.business_attributes.business_id " + f"ORDER BY partysvc.business_attributes.created_on DESC limit 1) ba {where_clause}" + ) ) diff --git a/test/test_enrolments_controller.py b/test/test_enrolments_controller.py index fb0fbe18..2bca25b2 100644 --- a/test/test_enrolments_controller.py +++ b/test/test_enrolments_controller.py @@ -23,14 +23,23 @@ { "business_id": "75d9af56-1225-4d43-b41d-1199f5f89daa", "business_ref": "001", - "business_attributes": {"name": "Business 1", "trading_as": "1 Business"}, + "business_attributes": { + "id": "75d9af56-1225-4d43-b41d-1199f5f89daa", + "name": "Business 1", + "trading_as": "1 Business", + "created_on": "2024-01-01 12:00:00", + }, "survey_id": "9200d295-9d6e-41fe-b541-747ae67a279f", "status": EnrolmentStatus.ENABLED, }, { "business_id": "98e2c9dd-a760-47dd-ba18-439fd5fb93a3", "business_ref": "002", - "business_attributes": {"name": "Business 2", "trading_as": "2 Business"}, + "business_attributes": { + "name": "Business 2", + "trading_as": "2 Business", + "created_on": "2025-01-01 12:00:00", + }, "survey_id": "c641f6ad-a5eb-4d82-a647-7cd586549bbc", "status": EnrolmentStatus.ENABLED, }, @@ -42,14 +51,23 @@ { "business_id": "af25c9d5-6893-4342-9d24-4b88509e965f", "business_ref": "003", - "business_attributes": {"name": "Business 3", "trading_as": "3 Business"}, + "business_attributes": { + "name": "Business 3", + "trading_as": "3 Business", + "created_on": "2025-01-01 12:00:00", + }, "survey_id": "9200d295-9d6e-41fe-b541-747ae67a279f", "status": EnrolmentStatus.ENABLED, }, { "business_id": "75d9af56-1225-4d43-b41d-1199f5f89daa", - "business_ref": "004", - "business_attributes": {"name": "Business 4", "trading_as": "4 Business"}, + "business_ref": "001", + "business_attributes": { + "id": "75d9af56-1225-4d43-b41d-1199f5f89daa", + "name": "Business 4", + "trading_as": "4 Business", + "created_on": "2025-01-01 12:00:00", + }, "survey_id": "9200d295-9d6e-41fe-b541-747ae67a279f", "status": EnrolmentStatus.DISABLED, }, @@ -80,8 +98,8 @@ def test_get_enrolments_party_id(self, get_surveys_details): "enrolment_status": "ENABLED", "business_details": { "id": UUID("75d9af56-1225-4d43-b41d-1199f5f89daa"), - "name": "Business 1", - "trading_as": "1 Business", + "name": "Business 4", # Business 4 is the latest business_attributes for 75d9af56 + "trading_as": "4 Business", "ref": "001", }, "survey_details": { @@ -126,7 +144,7 @@ def test_get_enrolments_party_id_and_business_id_and_survey_id(self, get_surveys def test_get_enrolments_party_id_enabled(self, get_surveys_details): get_surveys_details.return_value = SURVEYS_DETAILS enrolments = respondent_enrolments( - party_uuid="5718649e-30bf-4c25-a2c0-aaa733e54ed6", status=EnrolmentStatus.ENABLED + party_uuid="5718649e-30bf-4c25-a2c0-aaa733e54ed6", status=EnrolmentStatus.ENABLED.name ) self.assertEqual(len(enrolments), 1) @@ -137,7 +155,7 @@ def test_get_enrolments_party_id_enabled(self, get_surveys_details): def test_get_enrolments_party_id_disabled(self, get_surveys_details): get_surveys_details.return_value = SURVEYS_DETAILS enrolments = respondent_enrolments( - party_uuid="5718649e-30bf-4c25-a2c0-aaa733e54ed6", status=EnrolmentStatus.DISABLED + party_uuid="5718649e-30bf-4c25-a2c0-aaa733e54ed6", status=EnrolmentStatus.DISABLED.name ) self.assertEqual(len(enrolments), 1) @@ -171,13 +189,14 @@ def _add_enrolments(self, session): for enrolment in respondent_enrolment["enrolment_details"]: if not (business := businesses.get(enrolment["business_id"])): business = Business(party_uuid=enrolment["business_id"], business_ref=enrolment["business_ref"]) - business_attributes = BusinessAttributes( - business_id=business.party_uuid, attributes=enrolment["business_attributes"] - ) session.add(business) - session.add(business_attributes) businesses[enrolment["business_id"]] = business - + business_attributes = BusinessAttributes( + business_id=business.party_uuid, + attributes=enrolment["business_attributes"], + created_on=enrolment["business_attributes"]["created_on"], + ) + session.add(business_attributes) business_respondent = BusinessRespondent(business=business, respondent=respondent) session.add(business_respondent) session.flush()