Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RAS-1400 Extend party enrolment to include business and survey details #449

Merged
merged 3 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions _infra/helm/party/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.13
version: 2.5.14

# 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.13
appVersion: 2.5.14
6 changes: 6 additions & 0 deletions _infra/helm/party/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@ spec:
{{- else }}
value: "http://$(IAC_SERVICE_HOST):$(IAC_SERVICE_PORT)"
{{- end }}
- name: SURVEY_URL
{{- if .Values.dns.enabled }}
value: "http://survey.{{ .Values.namespace }}.svc.cluster.local:{{ .Values.dns.wellKnownPort }}"
{{- else }}
value: "http://$(SURVEY_SERVICE_HOST):$(SURVEY_SERVICE_PORT)"
{{- end }}
- name: NOTIFY_URL
{{- if .Values.dns.enabled }}
value: "http://notify-gateway.{{ .Values.namespace }}.svc.cluster.local:{{ .Values.dns.wellKnownPort }}/emails/"
Expand Down
2 changes: 2 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class Config(object):
COLLECTION_EXERCISE_URL = os.getenv("COLLECTION_EXERCISE_URL")
FRONTSTAGE_URL = os.getenv("FRONTSTAGE_URL")
IAC_URL = os.getenv("IAC_URL")
SURVEY_URL = os.getenv("SURVEY_URL")

GOOGLE_CLOUD_PROJECT = os.getenv("GOOGLE_CLOUD_PROJECT", "test-project-id")
PUBSUB_TOPIC = os.getenv("PUBSUB_TOPIC", "ras-rm-notify-test")
Expand Down Expand Up @@ -96,6 +97,7 @@ class Config(object):
class DevelopmentConfig(Config):
DEBUG = True
LOGGING_LEVEL = "DEBUG"
SURVEY_URL = os.getenv("SURVEY_URL", "http://localhost:8080")


class TestingConfig(DevelopmentConfig):
Expand Down
47 changes: 44 additions & 3 deletions ras_party/controllers/enrolments_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
from sqlalchemy.orm.exc import NoResultFound

from ras_party.controllers.queries import (
query_enrolment_by_survey_business_respondent,
query_respondent_by_party_uuid,
query_respondent_enrolments,
)
from ras_party.models.models import Enrolment
from ras_party.controllers.survey_controller import get_surveys_details
from ras_party.models.models import Enrolment, RespondentStatus
from ras_party.support.session_decorator import with_query_only_db_session

logger = structlog.wrap_logger(logging.getLogger(__name__))
Expand All @@ -20,11 +22,50 @@ def respondent_enrolments(
session: session, party_uuid: UUID, business_id: UUID = None, survey_id: UUID = None, status: int = None
) -> list[Enrolment]:
"""
returns a list of respondent Enrolments. Business_id, survey_id and status can also be added as conditions
returns a list of respondent enrolments with business and survey details
"""

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

return query_respondent_enrolments(session, respondent.id, business_id, survey_id, status)
enrolments = query_respondent_enrolments(session, respondent.id, business_id, survey_id, status)

if not enrolments:
return []

surveys_details = get_surveys_details()
respondents_enrolled = []
for enrolment, business_ref, business_attributes in enrolments:
survey_id = enrolment.survey_id
respondents_enrolled.append(
(
{
"enrolment_status": enrolment.status.name,
"business_details": {
"id": enrolment.business_id,
"name": business_attributes["name"],
"trading_as": business_attributes["trading_as"],
"ref": business_ref,
},
"survey_details": {
"id": survey_id,
"long_name": surveys_details.get(survey_id)["long_name"],
"short_name": surveys_details.get(survey_id)["short_name"],
"ref": surveys_details.get(survey_id)["ref"],
},
}
)
)
return respondents_enrolled


@with_query_only_db_session
def is_respondent_enrolled(party_uuid: UUID, business_id: UUID, survey_id: UUID, session: session) -> bool:
LJBabbage marked this conversation as resolved.
Show resolved Hide resolved
respondent = query_respondent_by_party_uuid(party_uuid, session)

if respondent and respondent.status == RespondentStatus.ACTIVE:
enrolment = query_enrolment_by_survey_business_respondent(respondent.id, business_id, survey_id, session)
if enrolment:
return True
return False
8 changes: 7 additions & 1 deletion ras_party/controllers/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,4 +660,10 @@ def query_respondent_enrolments(
if status:
additional_conditions.append(Enrolment.status == status)

return session.query(Enrolment).filter(and_(Enrolment.respondent_id == respondent_id, *additional_conditions)).all()
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()
)
13 changes: 0 additions & 13 deletions ras_party/controllers/respondent_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
)
from ras_party.controllers.notify_gateway import NotifyGateway
from ras_party.controllers.queries import (
query_enrolment_by_survey_business_respondent,
query_respondent_by_email,
query_respondent_by_names_and_emails,
query_respondent_by_party_uuid,
Expand All @@ -26,7 +25,6 @@
Enrolment,
PendingEnrolment,
Respondent,
RespondentStatus,
)
from ras_party.support.session_decorator import (
with_db_session,
Expand Down Expand Up @@ -251,14 +249,3 @@ def get_respondents_by_survey_and_business_id(survey_id: UUID, business_id: UUID
)

return respondents_enrolled


@with_query_only_db_session
def is_user_enrolled(party_uuid: UUID, business_id: UUID, survey_id: UUID, session: session) -> bool:
respondent = query_respondent_by_party_uuid(party_uuid, session)

if respondent and respondent.status == RespondentStatus.ACTIVE:
enrolment = query_enrolment_by_survey_business_respondent(respondent.id, business_id, survey_id, session)
if enrolment:
return True
return False
31 changes: 31 additions & 0 deletions ras_party/controllers/survey_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import logging

import requests
import structlog
from flask import current_app
from requests.exceptions import ConnectionError, HTTPError, Timeout

from ras_party.exceptions import ServiceUnavailableException

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


def get_surveys_details() -> dict:
LJBabbage marked this conversation as resolved.
Show resolved Hide resolved
url = f'{current_app.config["SURVEY_URL"]}/surveys'
try:
response = requests.get(
url, auth=(current_app.config["SECURITY_USER_NAME"], current_app.config["SECURITY_USER_PASSWORD"])
)
response.raise_for_status()
except HTTPError:
logger.error("Survey returned a HTTPError")
raise
except ConnectionError:
raise ServiceUnavailableException("Survey service returned a connection error", 503)
except Timeout:
raise ServiceUnavailableException("Survey service has timed out", 504)

return {
survey["id"]: {"short_name": survey["shortName"], "long_name": survey["longName"], "ref": survey["surveyRef"]}
for survey in response.json()
}
11 changes: 11 additions & 0 deletions ras_party/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,14 @@ def __init__(self, description=None, error=None, **kwargs):
self.error = error
for k, v in kwargs.items():
self.__dict__[k] = v


class ServiceUnavailableException(Exception):
status_code = 500

def __init__(self, errors, status_code=None):
self.errors = errors if isinstance(errors, list) else [errors]
self.status_code = status_code

def to_dict(self):
return {"errors": self.errors}
20 changes: 18 additions & 2 deletions ras_party/views/enrolments_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
from sqlalchemy.orm.exc import NoResultFound
from werkzeug.exceptions import BadRequest, NotFound

from ras_party.controllers.enrolments_controller import respondent_enrolments
from ras_party.controllers.enrolments_controller import (
is_respondent_enrolled,
respondent_enrolments,
)
from ras_party.uuid_helper import is_valid_uuid4

logger = structlog.wrap_logger(logging.getLogger(__name__))
Expand Down Expand Up @@ -58,4 +61,17 @@ def get_respondent_enrolments(party_uuid: UUID) -> Response:
)
return BadRequest()

return make_response([enrolment.to_dict() for enrolment in enrolments], 200)
return make_response(enrolments, 200)


@enrolments_view.route(
"/is_respondent_enrolled/<party_uuid>/business_id/<business_id>/survey_id/<survey_id>", methods=["GET"]
)
def get_is_respondent_enrolled(party_uuid: UUID, business_id: UUID, survey_id: UUID) -> Response:
LJBabbage marked this conversation as resolved.
Show resolved Hide resolved
if not (is_valid_uuid4(party_uuid) and is_valid_uuid4(business_id) and is_valid_uuid4(survey_id)):
return make_response("Bad request, party_uuid, business or survey id not UUID", 400)

enrolled_status = is_respondent_enrolled(party_uuid=party_uuid, business_id=business_id, survey_id=survey_id)
if enrolled_status:
return make_response({"enrolled": True}, 200)
return make_response({"enrolled": False}, 200)
3 changes: 2 additions & 1 deletion ras_party/views/respondent_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from werkzeug.exceptions import BadRequest

from ras_party.controllers import respondent_controller
from ras_party.controllers.enrolments_controller import is_respondent_enrolled
from ras_party.uuid_helper import is_valid_uuid4

logger = structlog.wrap_logger(logging.getLogger(__name__))
Expand Down Expand Up @@ -132,7 +133,7 @@ def validate_respondent_claim():
if not (is_valid_uuid4(party_uuid) and is_valid_uuid4(business_id) and is_valid_uuid4(survey_id)):
return make_response("Bad request, party_uuid, business or survey id not UUID", 400)

if respondent_controller.is_user_enrolled(party_uuid, business_id, survey_id):
if is_respondent_enrolled(party_uuid, business_id, survey_id):
return make_response("Valid", 200)

return make_response("Invalid", 200)
Expand Down
9 changes: 9 additions & 0 deletions test/party_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,3 +450,12 @@ def get_respondent_enrolments(self, party_id, payload={}):
f"/party-api/v1/enrolments/respondent/{party_id}", json=payload, headers=self.auth_headers
)
return response

def get_respondent_is_enrolments(self, party_uuid, business_id, survey_id):
response = self.client.get(
f"/party-api/v1/enrolments/is_respondent_enrolled/{party_uuid}"
f"/business_id/{business_id}"
f"/survey_id/{survey_id}",
headers=self.auth_headers,
)
return response
Loading
Loading