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-1228: Bug: Party service workers getting 139 SIGSEGV errors and restarting #433

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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.2
version: 2.5.3

# 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.2
appVersion: 2.5.3
17 changes: 12 additions & 5 deletions ras_party/controllers/business_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from flask import current_app
from werkzeug.exceptions import BadRequest, NotFound

from ras_party import unified_buisness_party_functions
from ras_party.controllers.queries import (
query_business_attributes,
query_business_attributes_by_collection_exercise,
Expand All @@ -25,7 +26,7 @@


@with_query_only_db_session
def get_business_by_ref(ref, session):
def get_business_by_ref(ref, session, retrieve_associations=True):
"""
Get a Business by its unique business reference

Expand All @@ -39,7 +40,9 @@ def get_business_by_ref(ref, session):
logger.info("Business with reference does not exist.", ru_ref=ref)
raise NotFound("Business with reference does not exist.")

return business.to_party_dict()
return unified_buisness_party_functions.to_unified_dict(
business, collection_exercise_id=None, attributes_required=True, retrieve_associations=retrieve_associations
)


@with_query_only_db_session
Expand All @@ -60,7 +63,7 @@ def get_businesses_by_ids(party_uuids, session):
raise BadRequest(f"'{party_uuid}' is not a valid UUID format for property 'id'")

businesses = query_businesses_by_party_uuids(party_uuids, session)
return [business.to_business_summary_dict() for business in businesses]
return [unified_buisness_party_functions.to_unified_dict(business) for business in businesses]


@with_query_only_db_session
Expand Down Expand Up @@ -126,10 +129,14 @@ def get_business_by_id(party_uuid, session, verbose=False, collection_exercise_i
logger.info("Business with id does not exist", party_uuid=party_uuid)
raise NotFound("Business with party id does not exist")

unified_dict = unified_buisness_party_functions.to_unified_dict(
business, collection_exercise_id=collection_exercise_id, attributes_required=True
)

if verbose:
return business.to_business_dict(collection_exercise_id=collection_exercise_id)
return dict(unified_dict, **unified_dict["attributes"])

return business.to_business_summary_dict(collection_exercise_id=collection_exercise_id)
return unified_buisness_party_functions.to_unified_dict(business, collection_exercise_id=collection_exercise_id)


@with_db_session
Expand Down
9 changes: 7 additions & 2 deletions ras_party/controllers/party_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from flask import current_app
from werkzeug.exceptions import BadRequest, Conflict, NotFound

from ras_party import unified_buisness_party_functions
from ras_party.controllers.queries import (
query_business_attributes_by_sample_summary_id,
query_business_by_party_uuid,
Expand Down Expand Up @@ -93,13 +94,17 @@ def get_party_by_id(sample_unit_type, party_id, session):
if not business:
logger.info("Business with id does not exist", business_id=party_id, status=404)
raise NotFound("Business with id does not exist")
return business.to_party_dict()
return unified_buisness_party_functions.to_unified_dict(
business, collection_exercise_id=None, attributes_required=True
)
elif sample_unit_type == Respondent.UNIT_TYPE:
respondent = query_respondent_by_party_uuid(party_id, session)
if not respondent:
logger.info("Respondent with id does not exist", respondent_id=party_id, status=404)
raise NotFound("Respondent with id does not exist")
return respondent.to_party_dict()
return unified_buisness_party_functions.to_unified_dict(
respondent, collection_exercise_id=None, attributes_required=True
)
else:
logger.info("Invalid sample unit type", type=sample_unit_type)
raise BadRequest(f"{sample_unit_type} is not a valid value for sampleUnitType. Must be one of ['B', 'BI']")
Expand Down
40 changes: 40 additions & 0 deletions ras_party/models/model_functions.py
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to the retrieval of the enrolment DTO, I had to extract these functions out of the unified_business_party_functions.py

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import logging

import structlog
from werkzeug.exceptions import BadRequest

from ras_party.models import models

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


def get_respondents_associations(respondents):
associations = []
for business_respondent in respondents:
respondent_dict = {
"partyId": business_respondent.respondent.party_uuid,
"businessRespondentStatus": business_respondent.respondent.status.name,
}
enrolments = business_respondent.enrolment
respondent_dict["enrolments"] = []
for enrolment in enrolments:
enrolments_dict = {
"surveyId": enrolment.survey_id,
"enrolmentStatus": models.EnrolmentStatus(enrolment.status).name,
}
respondent_dict["enrolments"].append(enrolments_dict)
associations.append(respondent_dict)
return associations


def get_attributes_for_collection_exercise(model_attributes, collection_exercise_id=None):
if collection_exercise_id:
for attributes in model_attributes.attributes:
if attributes.collection_exercise == collection_exercise_id:
return attributes

try:
return next((attributes for attributes in model_attributes.attributes if attributes.collection_exercise))
except StopIteration:
logger.error("No active attributes for business", reference=model_attributes.business_ref, status=400)
raise BadRequest("Business with reference does not have any active attributes.")
74 changes: 2 additions & 72 deletions ras_party/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
from sqlalchemy.dialects.postgresql import JSONB, UUID
from sqlalchemy.orm import declarative_base, relationship
from sqlalchemy.types import Enum
from werkzeug.exceptions import BadRequest

from ras_party.models import model_functions
from ras_party.support.util import filter_falsey_values, partition_dict

Base = declarative_base()
Expand Down Expand Up @@ -97,64 +97,6 @@ def _populate_name_and_trading_as(ba):
ba.name = name
ba.trading_as = trading_as

@staticmethod
def _get_respondents_associations(respondents):
associations = []
for business_respondent in respondents:
respondent_dict = {
"partyId": business_respondent.respondent.party_uuid,
"businessRespondentStatus": business_respondent.respondent.status.name,
}
enrolments = business_respondent.enrolment
respondent_dict["enrolments"] = []
for enrolment in enrolments:
enrolments_dict = {
"surveyId": enrolment.survey_id,
"enrolmentStatus": EnrolmentStatus(enrolment.status).name,
}
respondent_dict["enrolments"].append(enrolments_dict)
associations.append(respondent_dict)
return associations

def to_business_dict(self, collection_exercise_id=None):
"""
Gets a dict that contains both summary data and collection exercise data. The collection exercise data will be
for either the specified one if supplied, or the most recent one if not supplied

:param collection_exercise_id: A collection exercise uuid
:return: A dict containing both the summary data and business attributes for the business
:rtype: dict
"""
d = self.to_business_summary_dict()
attributes = self._get_attributes_for_collection_exercise(collection_exercise_id)
return dict(d, **attributes.attributes)

def to_business_summary_dict(self, collection_exercise_id=None):
attributes = self._get_attributes_for_collection_exercise(collection_exercise_id)
d = {
"id": self.party_uuid,
"sampleUnitRef": self.business_ref,
"sampleUnitType": self.UNIT_TYPE,
"sampleSummaryId": attributes.sample_summary_id,
"name": attributes.attributes.get("name"),
"trading_as": attributes.attributes.get("trading_as"),
"associations": self._get_respondents_associations(self.respondents),
}
return d

def to_party_dict(self):
attributes = self._get_attributes_for_collection_exercise()
return {
"id": self.party_uuid,
"sampleUnitRef": self.business_ref,
"sampleUnitType": self.UNIT_TYPE,
"sampleSummaryId": attributes.sample_summary_id,
"attributes": attributes.attributes,
"name": attributes.attributes.get("name"),
"trading_as": attributes.attributes.get("trading_as"),
"associations": self._get_respondents_associations(self.respondents),
}

def to_post_response_dict(self):
return {
"id": self.party_uuid,
Expand All @@ -164,21 +106,9 @@ def to_post_response_dict(self):
"attributes": self.attributes[-1].attributes,
"name": self.attributes[-1].name,
"trading_as": self.attributes[-1].trading_as,
"associations": self._get_respondents_associations(self.respondents),
"associations": model_functions.get_respondents_associations(self.respondents),
}

def _get_attributes_for_collection_exercise(self, collection_exercise_id=None):
if collection_exercise_id:
for attributes in self.attributes:
if attributes.collection_exercise == collection_exercise_id:
return attributes

try:
return next((attributes for attributes in self.attributes if attributes.collection_exercise))
except StopIteration:
logger.error("No active attributes for business", reference=self.business_ref, status=400)
raise BadRequest("Business with reference does not have any active attributes.")


class BusinessAttributes(Base):
__tablename__ = "business_attributes"
Expand Down
19 changes: 19 additions & 0 deletions ras_party/unified_buisness_party_functions.py
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I like the naming of this. But I couldn't think what would be a good name for this.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from ras_party.models import model_functions


def to_unified_dict(model, collection_exercise_id=None, attributes_required=False, retrieve_associations=True):
attributes = model_functions.get_attributes_for_collection_exercise(model, collection_exercise_id)
unified_dict = {
"id": model.party_uuid,
"sampleUnitRef": model.business_ref,
"sampleUnitType": model.UNIT_TYPE,
"sampleSummaryId": attributes.sample_summary_id,
"name": attributes.attributes.get("name"),
"trading_as": attributes.attributes.get("trading_as"),
}
if attributes_required:
unified_dict["attributes"] = attributes.attributes
if retrieve_associations:
unified_dict["associations"] = model_functions.get_respondents_associations(model.respondents)

return unified_dict
7 changes: 7 additions & 0 deletions ras_party/views/business_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ def get_business_by_ref(ref):
return jsonify(business)


@business_view.route("/businesses/ref/reporting-unit-only/<ref>", methods=["GET"])
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not keen on the naming of this endpoint. Any ideas?

def get_business_by_ref_only(ref):
# This endpoint will retrieve the reporting unit only and not the associations.
business = business_controller.get_business_by_ref(ref, retrieve_associations=True)
return jsonify(business)


@business_view.route("/businesses/sample/link/<sample>", methods=["PUT"])
def put_business_attributes_ce(sample):
payload = request.get_json() or {}
Expand Down
Loading