diff --git a/results-tabulation-api/api/ElectionApi.py b/results-tabulation-api/api/ElectionApi.py index 4deae891..e782f3b3 100644 --- a/results-tabulation-api/api/ElectionApi.py +++ b/results-tabulation-api/api/ElectionApi.py @@ -6,7 +6,7 @@ from exception.messages import MESSAGE_CODE_ELECTION_NOT_FOUND from orm.entities.Election.election_helper import get_root_token from orm.entities import Election -from schemas import ElectionSchema as Schema +from schemas import ElectionSchema as Schema, AreaMapSchema from util import RequestBody, get_paginated_query @@ -60,3 +60,18 @@ def create(body): @authorize(required_roles=[ADMIN_ROLE]) def getRootToken(electionId): return get_root_token(electionId=electionId) + + +@authorize(required_roles=ALL_ROLES) +def get_area_map(electionId=None): + election = Election.get_by_id(electionId=electionId) + if election is None: + raise NotFoundException( + message="Election not found (electionId=%d)" % electionId, + code=MESSAGE_CODE_ELECTION_NOT_FOUND + ) + + extended_election = election.get_extended_election() + area_map = extended_election.get_area_map() + + return AreaMapSchema(many=True).dump(area_map).data diff --git a/results-tabulation-api/auth/__init__.py b/results-tabulation-api/auth/__init__.py index 91b17786..3681da02 100644 --- a/results-tabulation-api/auth/__init__.py +++ b/results-tabulation-api/auth/__init__.py @@ -183,7 +183,6 @@ def authenticate(func, *args, **kwargs): return func(*args, **kwargs) -@cache.memoize(300) def _get_role_area_ids(parentAreaIds, areaType, voteTypes=[]): from orm.entities import Area, Election @@ -216,12 +215,18 @@ def _get_role_area_ids(parentAreaIds, areaType, voteTypes=[]): return _role_area_ids -@cache.memoize(300) -def get_user_access_area_ids_from_claims(claims, required_roles): +@decorator +@authenticate +def authorize(func, required_roles=None, *args, **kwargs): from orm.enums import AreaTypeEnum - user_access_area_ids = [] + if required_roles is None: + return func(*args, **kwargs) + + claims: Dict = get_claims() + claim_found = False + user_access_area_ids = [] for role in required_roles: claim = AREA_CLAIM_PREFIX + role @@ -341,19 +346,6 @@ def get_user_access_area_ids_from_claims(claims, required_roles): ) ]) - return user_access_area_ids, claim_found - - -@decorator -@authenticate -def authorize(func, required_roles=None, *args, **kwargs): - if required_roles is None: - return func(*args, **kwargs) - - claims: Dict = get_claims() - user_access_area_ids, claim_found = get_user_access_area_ids_from_claims(required_roles=required_roles, - claims=claims) - if not claim_found: UnauthorizedException( message="No matching claim found.", diff --git a/results-tabulation-api/ext/ExtendedElection/ExtendedElectionParliamentaryElection2020/__init__.py b/results-tabulation-api/ext/ExtendedElection/ExtendedElectionParliamentaryElection2020/__init__.py index 68807164..95077982 100644 --- a/results-tabulation-api/ext/ExtendedElection/ExtendedElectionParliamentaryElection2020/__init__.py +++ b/results-tabulation-api/ext/ExtendedElection/ExtendedElectionParliamentaryElection2020/__init__.py @@ -120,7 +120,8 @@ def get_area_map_query(self): electoral_district.c.areaName.label("electoralDistrictName"), counting_centre.c.areaId.label("countingCentreId"), counting_centre.c.areaName.label("countingCentreName"), - Election.Model.voteType + Election.Model.voteType, + Election.Model.electionId ] query_filter = [ diff --git a/results-tabulation-api/ext/ExtendedElection/ExtendedElectionPresidentialElection2019/__init__.py b/results-tabulation-api/ext/ExtendedElection/ExtendedElectionPresidentialElection2019/__init__.py index ebff251c..660d7a51 100644 --- a/results-tabulation-api/ext/ExtendedElection/ExtendedElectionPresidentialElection2019/__init__.py +++ b/results-tabulation-api/ext/ExtendedElection/ExtendedElectionPresidentialElection2019/__init__.py @@ -107,7 +107,8 @@ def get_area_map_query(self): electoral_district.c.areaName.label("electoralDistrictName"), counting_centre.c.areaId.label("countingCentreId"), counting_centre.c.areaName.label("countingCentreName"), - Election.Model.voteType + Election.Model.voteType, + Election.Model.electionId ] query_filter = [ diff --git a/results-tabulation-api/ext/ExtendedElection/__init__.py b/results-tabulation-api/ext/ExtendedElection/__init__.py index 3c1bdbab..302e171b 100644 --- a/results-tabulation-api/ext/ExtendedElection/__init__.py +++ b/results-tabulation-api/ext/ExtendedElection/__init__.py @@ -55,124 +55,17 @@ def build_election(self, party_candidate_dataset_file=None, invalid_vote_categories_dataset_file=None, number_of_seats_dataset_file=None): pass - @cache.memoize(300) def get_area_map_for_tally_sheet(self, tally_sheet): area = tally_sheet.area return self.get_area_map(area=area) - @cache.memoize(300) - def get_area_map(self, area, group_by=None, filter=None): - from orm.enums import AreaTypeEnum - + def get_area_map(self): area_map_subquery = self.get_area_map_query().subquery() - column_name_list = [ - "pollingStationId", "pollingStationName", - "pollingDistrictId", "pollingDistrictName", - "countingCentreId", "countingCentreName", - "pollingDivisionId", "pollingDivisionName", - "electoralDistrictId", "electoralDistrictName", - "countryId", "countryName" - ] - column_name_to_column_map = { - "pollingStationId": area_map_subquery.c.pollingStationId, - "pollingStationName": area_map_subquery.c.pollingStationName, - "pollingDistrictId": area_map_subquery.c.pollingDistrictId, - "pollingDistrictName": area_map_subquery.c.pollingDistrictName, - "countingCentreId": area_map_subquery.c.countingCentreId, - "countingCentreName": area_map_subquery.c.countingCentreName, - "pollingDivisionId": area_map_subquery.c.pollingDivisionId, - "pollingDivisionName": area_map_subquery.c.pollingDivisionName, - "electoralDistrictId": area_map_subquery.c.electoralDistrictId, - "electoralDistrictName": area_map_subquery.c.electoralDistrictName, - "countryId": area_map_subquery.c.countryId, - "countryName": area_map_subquery.c.countryName - } - query_args = [] - query_filter = [] - query_group_by = [] - area_and_vote_type_wise_group_by_map = { - AreaTypeEnum.CountingCentre: [ - "countingCentreId", - "countingCentreName", - "pollingDivisionId", - "pollingDivisionName", - "electoralDistrictId", - "electoralDistrictName", - "countryId", - "countryName" - ], - AreaTypeEnum.PollingStation: [ - "pollingDistrictId", - "pollingDistrictName", - "pollingStationId", - "pollingStationName", - "countingCentreId", - "countingCentreName", - "pollingDivisionId", - "pollingDivisionName", - "electoralDistrictId", - "electoralDistrictName", - "countryId", - "countryName" - ], - AreaTypeEnum.PollingDivision: [ - "pollingDivisionId", - "pollingDivisionName", - "electoralDistrictId", - "electoralDistrictName", - "countryId", - "countryName" - ], - AreaTypeEnum.ElectoralDistrict: [ - "electoralDistrictId", - "electoralDistrictName", - "countryId", - "countryName" - ], - AreaTypeEnum.Country: [ - "countryId", - "countryName" - ] - } - - area_and_vote_type_wise_filter_map = { - AreaTypeEnum.PollingStation: [area_map_subquery.c.pollingStationId == area.areaId], - AreaTypeEnum.PollingDistrict: [area_map_subquery.c.pollingDistrictId == area.areaId], - AreaTypeEnum.CountingCentre: [area_map_subquery.c.countingCentreId == area.areaId], - AreaTypeEnum.PollingDivision: [area_map_subquery.c.pollingDivisionId == area.areaId], - AreaTypeEnum.ElectoralDistrict: [area_map_subquery.c.electoralDistrictId == area.areaId], - AreaTypeEnum.Country: [area_map_subquery.c.countryId == area.areaId] - } - - if group_by is None: - if area.areaType in area_and_vote_type_wise_group_by_map: - group_by = area_and_vote_type_wise_group_by_map[area.areaType] - else: - group_by = [] - - for column_name in column_name_list: - column = column_name_to_column_map[column_name] - if column_name in group_by: - query_group_by.append(column) - - # Append the column to query. - query_args.append(column) - else: - query_args.append(bindparam(column_name, None)) - - if filter is None: - if area.areaType in area_and_vote_type_wise_filter_map: - filter = area_and_vote_type_wise_filter_map[area.areaType] - else: - filter = [] - - query_filter = filter - - area_map = db.session.query(*query_args).filter(*query_filter).group_by(*query_group_by).all() - - return area_map + return db.session.query(area_map_subquery).filter( + area_map_subquery.c.electionId.in_(self.election.get_this_and_below_election_ids()) + ) def get_area_map_query(self): @@ -218,7 +111,8 @@ def get_area_map_query(self): polling_station.c.areaName.label("pollingStationName"), counting_centre.c.areaId.label("countingCentreId"), counting_centre.c.areaName.label("countingCentreName"), - Election.Model.voteType + Election.Model.voteType, + Election.Model.electionId ).filter( country__electoral_district.parentAreaId == country.c.areaId, country__electoral_district.childAreaId == electoral_district.c.areaId, diff --git a/results-tabulation-api/orm/entities/Area/__init__.py b/results-tabulation-api/orm/entities/Area/__init__.py index d8e100e7..21231b01 100644 --- a/results-tabulation-api/orm/entities/Area/__init__.py +++ b/results-tabulation-api/orm/entities/Area/__init__.py @@ -1,4 +1,4 @@ -from app import db, cache +from app import db from sqlalchemy.orm import relationship from sqlalchemy import func @@ -158,7 +158,6 @@ def get_associated_areas_query(areas, areaType, electionId=None): return db.session.query(*query_args).filter(*query_filters).group_by(*query_group_by) -@cache.memoize(300) def get_associated_areas(area, areaType, electionId=None): result = get_associated_areas_query(areas=[area], areaType=areaType, electionId=electionId).all() diff --git a/results-tabulation-api/orm/entities/Election/ElectionCandidate.py b/results-tabulation-api/orm/entities/Election/ElectionCandidate.py index 40a00d70..5a7eafc7 100644 --- a/results-tabulation-api/orm/entities/Election/ElectionCandidate.py +++ b/results-tabulation-api/orm/entities/Election/ElectionCandidate.py @@ -14,8 +14,8 @@ class ElectionCandidateModel(db.Model): qualifiedForPreferences = db.Column(db.Boolean, default=False, nullable=False) election = relationship("ElectionModel", foreign_keys=[electionId]) - party = relationship(Party.Model, foreign_keys=[partyId]) - candidate = relationship(Candidate.Model, foreign_keys=[candidateId]) + party = relationship(Party.Model, foreign_keys=[partyId], lazy='subquery') + candidate = relationship(Candidate.Model, foreign_keys=[candidateId], lazy='subquery') candidateName = association_proxy("candidate", "candidateName") candidateNumber = association_proxy("candidate", "candidateNumber") diff --git a/results-tabulation-api/orm/entities/Election/__init__.py b/results-tabulation-api/orm/entities/Election/__init__.py index 2c7b8b21..13618e27 100644 --- a/results-tabulation-api/orm/entities/Election/__init__.py +++ b/results-tabulation-api/orm/entities/Election/__init__.py @@ -23,7 +23,7 @@ class ElectionModel(db.Model): isListed = db.Column(db.Boolean, nullable=False, default=False) metaId = db.Column(db.Integer, db.ForeignKey(Meta.Model.__table__.c.metaId), nullable=True) - parties = relationship("ElectionPartyModel", order_by="ElectionPartyModel.electionPartyId") + parties = relationship("ElectionPartyModel", order_by="ElectionPartyModel.electionPartyId", lazy='subquery') _invalidVoteCategories = relationship("InvalidVoteCategoryModel") subElections = relationship("ElectionModel", foreign_keys=[parentElectionId]) rootElection = relationship("ElectionModel", remote_side=[electionId], foreign_keys=[rootElectionId]) diff --git a/results-tabulation-api/orm/entities/Meta/__init__.py b/results-tabulation-api/orm/entities/Meta/__init__.py index 84353344..5cc290c0 100644 --- a/results-tabulation-api/orm/entities/Meta/__init__.py +++ b/results-tabulation-api/orm/entities/Meta/__init__.py @@ -8,7 +8,7 @@ class MetaModel(db.Model): metaId = db.Column(db.Integer, primary_key=True, autoincrement=True) - metaDataList = relationship(MetaData.Model) + metaDataList = relationship(MetaData.Model, lazy='subquery') @classmethod def create(cls, metaDataDict=None): diff --git a/results-tabulation-api/orm/entities/Submission/TallySheet/__init__.py b/results-tabulation-api/orm/entities/Submission/TallySheet/__init__.py index ec323051..6508b145 100644 --- a/results-tabulation-api/orm/entities/Submission/TallySheet/__init__.py +++ b/results-tabulation-api/orm/entities/Submission/TallySheet/__init__.py @@ -3,20 +3,18 @@ from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm import relationship from app import db -from auth import get_user_access_area_ids, get_user_name, has_role_based_access +from auth import get_user_access_area_ids, has_role_based_access from constants.TALLY_SHEET_COLUMN_SOURCE import TALLY_SHEET_COLUMN_SOURCE_META from exception import NotFoundException, UnauthorizedException from exception.messages import MESSAGE_CODE_TALLY_SHEET_NOT_FOUND, MESSAGE_CODE_TALLY_SHEET_NOT_AUTHORIZED_TO_VIEW from ext.ExtendedElection.WORKFLOW_ACTION_TYPE import WORKFLOW_ACTION_TYPE_VIEW -from ext.ExtendedTallySheet import ExtendedTallySheet -from orm.entities import Submission, Election, Template, TallySheetVersionRow, Candidate, Party, Area, Meta +from orm.entities import Submission, Election, Template, TallySheetVersionRow, Meta from orm.entities.Dashboard import StatusReport -from orm.entities.Election import ElectionCandidate, ElectionParty, InvalidVoteCategory from orm.entities.SubmissionVersion import TallySheetVersion from orm.entities.Template import TemplateRow_DerivativeTemplateRow_Model, TemplateRowModel from orm.entities.Workflow import WorkflowInstance, WorkflowActionModel from orm.enums import SubmissionTypeEnum, AreaTypeEnum -from sqlalchemy import and_, func, or_, case, bindparam +from sqlalchemy import func, bindparam from util import get_dict_key_value_or_none @@ -31,33 +29,33 @@ class TallySheetModel(db.Model): workflowInstanceId = db.Column(db.Integer, db.ForeignKey(WorkflowInstance.Model.__table__.c.workflowInstanceId), nullable=True) - submission = relationship("SubmissionModel", foreign_keys=[tallySheetId]) + submission = relationship("SubmissionModel", foreign_keys=[tallySheetId], lazy='subquery') statusReport = relationship(StatusReport.Model, foreign_keys=[statusReportId]) - template = relationship(Template.Model, foreign_keys=[templateId]) - meta = relationship(Meta.Model, foreign_keys=[metaId]) - _workflowInstance = relationship(WorkflowInstance.Model, foreign_keys=[workflowInstanceId]) + template = relationship(Template.Model, foreign_keys=[templateId], lazy='subquery') + meta = relationship(Meta.Model, foreign_keys=[metaId], lazy='subquery') + workflowInstance = relationship(WorkflowInstance.Model, foreign_keys=[workflowInstanceId], lazy='subquery') electionId = association_proxy("submission", "electionId") election = association_proxy("submission", "election") areaId = association_proxy("submission", "areaId") area = association_proxy("submission", "area") latestVersionId = association_proxy("submission", "latestVersionId") - latestStamp = association_proxy("submission", "latestStamp") - lockedVersionId = association_proxy("submission", "lockedVersionId") - lockedVersion = association_proxy("submission", "lockedVersion") - notifiedVersionId = association_proxy("submission", "notifiedVersionId") - notifiedVersion = association_proxy("submission", "notifiedVersion") - releasedVersionId = association_proxy("submission", "releasedVersionId") - releasedVersion = association_proxy("submission", "releasedVersion") - lockedStamp = association_proxy("submission", "lockedStamp") - submittedVersionId = association_proxy("submission", "submittedVersionId") - submittedStamp = association_proxy("submission", "submittedStamp") - locked = association_proxy("submission", "locked") - submitted = association_proxy("submission", "submitted") - notified = association_proxy("submission", "notified") - released = association_proxy("submission", "released") - submissionProofId = association_proxy("submission", "submissionProofId") - submissionProof = association_proxy("submission", "submissionProof") + # latestStamp = association_proxy("submission", "latestStamp") + # lockedVersionId = association_proxy("submission", "lockedVersionId") + # lockedVersion = association_proxy("submission", "lockedVersion") + # notifiedVersionId = association_proxy("submission", "notifiedVersionId") + # notifiedVersion = association_proxy("submission", "notifiedVersion") + # releasedVersionId = association_proxy("submission", "releasedVersionId") + # releasedVersion = association_proxy("submission", "releasedVersion") + # lockedStamp = association_proxy("submission", "lockedStamp") + # submittedVersionId = association_proxy("submission", "submittedVersionId") + # submittedStamp = association_proxy("submission", "submittedStamp") + # locked = association_proxy("submission", "locked") + # submitted = association_proxy("submission", "submitted") + # notified = association_proxy("submission", "notified") + # released = association_proxy("submission", "released") + # submissionProofId = association_proxy("submission", "submissionProofId") + # submissionProof = association_proxy("submission", "submissionProof") versions = association_proxy("submission", "versions") metaDataList = association_proxy("meta", "metaDataList") @@ -70,15 +68,17 @@ class TallySheetModel(db.Model): secondaryjoin="TallySheetModel.tallySheetId==TallySheetTallySheetModel.parentTallySheetId" ) - def get_tally_sheet_workflow_instance_actions(self, workflow_instance): + def get_tally_sheet_workflow_instance_actions(self): tally_sheet_workflow_instance_actions = db.session.query( WorkflowActionModel.workflowActionId, WorkflowActionModel.actionName, WorkflowActionModel.actionType, WorkflowActionModel.fromStatus, - WorkflowActionModel.toStatus + WorkflowActionModel.toStatus, + WorkflowInstance.Model.status ).filter( - WorkflowActionModel.workflowId == workflow_instance.workflowId + WorkflowInstance.Model.workflowInstanceId == self.workflowInstanceId, + WorkflowActionModel.workflowId == WorkflowInstance.Model.workflowId ).order_by( WorkflowActionModel.workflowActionId ).all() @@ -91,7 +91,7 @@ def get_tally_sheet_workflow_instance_actions(self, workflow_instance): "actionType": tally_sheet_workflow_instance_action.actionType, "fromStatus": tally_sheet_workflow_instance_action.fromStatus, "toStatus": tally_sheet_workflow_instance_action.toStatus, - "allowed": tally_sheet_workflow_instance_action.fromStatus == workflow_instance.status, + "allowed": tally_sheet_workflow_instance_action.fromStatus == tally_sheet_workflow_instance_action.status, "authorized": has_role_based_access(tally_sheet=self, access_type=tally_sheet_workflow_instance_action.actionType) }) @@ -99,12 +99,8 @@ def get_tally_sheet_workflow_instance_actions(self, workflow_instance): return processed_tally_sheet_workflow_instance_actions @hybrid_property - def workflowInstance(self): - tally_sheet_workflow_instance = self._workflowInstance - setattr(tally_sheet_workflow_instance, "actions", - self.get_tally_sheet_workflow_instance_actions(tally_sheet_workflow_instance)) - - return tally_sheet_workflow_instance + def workflowInstanceActions(self): + return self.get_tally_sheet_workflow_instance_actions() @hybrid_property def areaMapList(self): @@ -138,92 +134,6 @@ def add_child(self, childTallySheet): def tallySheetCode(self): return self.template.templateName - def get_status_report_type(self): - electoral_district_name = "" - polling_division_name = "" - status_report_type = "" - - election = self.submission.election - submission_area = self.submission.area - # if self.tallySheetCode == PRE_30_PD: - # if election.voteType is Postal: - # electoral_district_name = submission_area.areaName - # status_report_type = "PV" - # else: - # electoral_district_name = _get_electoral_district_name(submission_area) - # polling_division_name = submission_area.areaName - # status_report_type = "PD" - # elif self.tallySheetCode == PRE_34_PD: - # if election.voteType is Postal: - # electoral_district_name = submission_area.areaName - # status_report_type = "PV [Revised]" - # else: - # electoral_district_name = _get_electoral_district_name(submission_area) - # polling_division_name = submission_area.areaName - # status_report_type = "PD [Revised]" - # elif self.tallySheetCode == PRE_30_ED: - # electoral_district_name = submission_area.areaName - # status_report_type = "ED" - # elif self.tallySheetCode == PRE_34_ED: - # electoral_district_name = submission_area.areaName - # status_report_type = "ED [Revised]" - # else: - # TODO - status_report_type = self.tallySheetCode - - return electoral_district_name, polling_division_name, status_report_type - - def get_report_status(self): - pass - # if self.template.has_data_entry(): - # if self.locked: - # if self.released: - # return "RELEASED" - # elif self.notified: - # return "NOTIFIED" - # elif self.submissionProof.size() > 0: - # return "CERTIFIED" - # else: - # return "VERIFIED" - # elif self.submitted: - # return "SUBMITTED" - # elif self.latestVersionId is not None: - # return "ENTERED" - # else: - # return "NOT ENTERED" - # else: - # if self.locked: - # if self.released: - # return "RELEASED" - # elif self.notified: - # return "NOTIFIED" - # elif self.submissionProof.size() > 0: - # return "CERTIFIED" - # else: - # return "VERIFIED" - # else: - # return "PENDING" - - def update_status_report(self): - pass - # election = self.submission.election.get_root_election() - # - # if self.statusReportId is None: - # electoral_district_name, polling_division_name, status_report_type = self.get_status_report_type() - # status_report = StatusReport.create( - # electionId=election.electionId, - # reportType=status_report_type, - # electoralDistrictName=electoral_district_name, - # pollingDivisionName=polling_division_name, - # status=self.get_report_status() - # ) - # - # self.statusReportId = status_report.statusReportId - # else: - # self.statusReport.update_status( - # status=self.get_report_status() - # ) - def set_latest_version(self, tallySheetVersion: TallySheetVersion): if tallySheetVersion is None: self.submission.set_latest_version(submissionVersion=None) diff --git a/results-tabulation-api/orm/entities/Submission/__init__.py b/results-tabulation-api/orm/entities/Submission/__init__.py index 99c1665b..c1ceac26 100644 --- a/results-tabulation-api/orm/entities/Submission/__init__.py +++ b/results-tabulation-api/orm/entities/Submission/__init__.py @@ -1,4 +1,3 @@ -from sqlalchemy.ext.hybrid import hybrid_property from app import db from sqlalchemy.orm import relationship from exception import MethodNotAllowedException @@ -26,20 +25,10 @@ class SubmissionModel(db.Model): releasedVersionId = db.Column(db.Integer, db.ForeignKey("submissionVersion.submissionVersionId"), nullable=True) releasedStampId = db.Column(db.Integer, db.ForeignKey("stamp.stampId"), nullable=True) - election = relationship(Election.Model, foreign_keys=[electionId]) - area = relationship(Area.Model, foreign_keys=[areaId]) - submissionProof = relationship(Proof.Model, foreign_keys=[submissionProofId]) + election = relationship(Election.Model, foreign_keys=[electionId], lazy='subquery') + area = relationship(Area.Model, foreign_keys=[areaId], lazy='subquery') submissionHistory = relationship(History.Model, foreign_keys=[submissionId]) latestVersion = relationship("SubmissionVersionModel", foreign_keys=[latestVersionId]) - latestStamp = relationship(Stamp.Model, foreign_keys=[latestStampId]) - lockedVersion = relationship("SubmissionVersionModel", foreign_keys=[lockedVersionId]) - lockedStamp = relationship(Stamp.Model, foreign_keys=[lockedStampId]) - submittedVersion = relationship("SubmissionVersionModel", foreign_keys=[lockedVersionId]) - submittedStamp = relationship(Stamp.Model, foreign_keys=[submittedStampId]) - notifiedVersion = relationship("SubmissionVersionModel", foreign_keys=[lockedVersionId]) - notifiedStamp = relationship(Stamp.Model, foreign_keys=[submittedStampId]) - releasedVersion = relationship("SubmissionVersionModel", foreign_keys=[lockedVersionId]) - releasedStamp = relationship(Stamp.Model, foreign_keys=[submittedStampId]) versions = relationship("SubmissionVersionModel", order_by="desc(SubmissionVersionModel.submissionVersionId)", primaryjoin="SubmissionModel.submissionId==SubmissionVersionModel.submissionId") diff --git a/results-tabulation-api/schemas/__init__.py b/results-tabulation-api/schemas/__init__.py index 8c09cebd..521e5aad 100644 --- a/results-tabulation-api/schemas/__init__.py +++ b/results-tabulation-api/schemas/__init__.py @@ -47,7 +47,6 @@ class Meta: sqla_session = db.session - class StatusActionSchema_1(ma.ModelSchema): class Meta: fields = ( @@ -108,7 +107,6 @@ class Meta: proof = ma.Nested("Proof_Schema") - class StampSchema(ma.ModelSchema): class Meta: fields = ( @@ -327,13 +325,14 @@ class SubmissionSchema(ma.ModelSchema): class Meta: fields = ( "submissionId", - "submissionType", + # "submissionType", "electionId", "areaId", + "area", "latestVersionId", # "tallySheetProofId", - "submissionProofId", - "versions" + # "submissionProofId", + # "versions" ) model = Submission.Model @@ -344,6 +343,7 @@ class Meta: # latestVersion = ma.Nested(TallySheetVersionSchema) submissionType = EnumField(SubmissionTypeEnum) submissionProof = ma.Nested(Proof_Schema) + area = ma.Nested(AreaSchema, only=["areaId", "areaName"]) class SubmissionVersionSchema(ma.ModelSchema): @@ -393,12 +393,12 @@ class Meta: "templateId", "template", "electionId", - "areaId", - "area", - "areaMapList", + # "areaId", + # "area", + # "areaMapList", "latestVersion", "metaDataList", - "workflowInstance", + # "workflowInstance", "latestVersionId" ) @@ -407,7 +407,7 @@ class Meta: # to use for deserialization sqla_session = db.session - template = ma.Nested("TemplateSchema") + template = ma.Nested("TemplateSchema", only=["templateId", "templateName", "isDerived"]) area = ma.Nested(AreaSchema, only=["areaId", "areaName"]) versions = ma.Nested(SubmissionVersionSchema, only="submissionVersionId", many=True) latestVersion = ma.Nested(TallySheetVersionSchema) @@ -417,7 +417,7 @@ class Meta: submissionProof = ma.Nested(Proof_Schema) metaDataList = ma.Nested(MetaDataSchema, many=True) areaMapList = ma.Nested('AreaMapSchema', many=True, partial=True) - workflowInstance = ma.Nested(WorkflowInstanceSchema) + workflowInstance = ma.Nested(WorkflowInstanceSchema, only=["workflowId", "statuses", "status"]) class TallySheetSchema(ma.ModelSchema): @@ -428,12 +428,14 @@ class Meta: "templateId", "template", "electionId", - "areaId", - "area", - "areaMapList", + # "areaId", + # "area", + # "areaMapList", "metaDataList", "workflowInstance", - "latestVersionId" + "workflowInstanceActions", + "latestVersionId", + "submission" ) model = TallySheet.Model @@ -441,17 +443,19 @@ class Meta: # to use for deserialization sqla_session = db.session - template = ma.Nested("TemplateSchema") + template = ma.Nested("TemplateSchema", only=["templateId", "templateName", "isDerived"]) area = ma.Nested(AreaSchema, only=["areaId", "areaName"]) versions = ma.Nested(SubmissionVersionSchema, only="submissionVersionId", many=True) latestVersion = ma.Nested(SubmissionVersionSchema) latestStamp = ma.Nested(StampSchema) lockedStamp = ma.Nested(StampSchema) submittedStamp = ma.Nested(StampSchema) + submission = ma.Nested(SubmissionSchema) submissionProof = ma.Nested(Proof_Schema) metaDataList = ma.Nested(MetaDataSchema, many=True) areaMapList = ma.Nested('AreaMapSchema', many=True, partial=True) - workflowInstance = ma.Nested(WorkflowInstanceSchema) + workflowInstance = ma.Nested(WorkflowInstanceSchema, only=["workflowId", "status"]) + workflowInstanceActions = ma.Nested(StatusActionSchema, many=True) class TemplateRowSchema(ma.ModelSchema): diff --git a/results-tabulation-api/swagger.yml b/results-tabulation-api/swagger.yml index 401493e5..b3474846 100644 --- a/results-tabulation-api/swagger.yml +++ b/results-tabulation-api/swagger.yml @@ -178,6 +178,53 @@ paths: schema: $ref: '#/components/schemas/ApiResponse' + /election/{electionId}/area-map: + get: + tags: + - Election + summary: Get Area Map + operationId: api.ElectionApi.get_area_map + parameters: + - name: electionId + in: path + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: Successful operation. + content: + application/json: + schema: + type: array + items: + type: object + '400': + description: Bad request. + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '401': + description: Unauthorized. + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '404': + description: Resource was not found. + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '500': + description: Unexpected error. + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + /election: get: tags: diff --git a/results-tabulation-ui/src/services/election.provider.js b/results-tabulation-ui/src/services/election.provider.js index 986cd10f..10df937c 100644 --- a/results-tabulation-ui/src/services/election.provider.js +++ b/results-tabulation-ui/src/services/election.provider.js @@ -1,5 +1,6 @@ import React, {useState} from "react"; import { + ENDPOINT_PATH_ELECTION_AREA_MAP_BY_ID, ENDPOINT_PATH_ELECTIONS, ENDPOINT_PATH_ELECTIONS_BY_ID, request @@ -17,8 +18,13 @@ export function ElectionProvider(props) { const saveElectionToState = (election) => { const {electionId, metaDataList = [], parties} = election; - election.metaDataMap = getMetaDataMap(metaDataList); - election.partyMap = getPartyMap(parties); + if (!election.metaDataMap) { + election.metaDataMap = getMetaDataMap(metaDataList); + } + + if (!election.partyMap) { + election.partyMap = getPartyMap(parties); + } // To avoid duplicated election fetches while state update wait state.electionMap[electionId] = {...election}; @@ -96,12 +102,29 @@ export function ElectionProvider(props) { return election; }; + + const getElectionAreaMap = async (electionId) => { + let election = await getElectionById(electionId); + + if (!election.electionAreaMap) { + const electionAreaMap = await request({ + url: ENDPOINT_PATH_ELECTION_AREA_MAP_BY_ID(electionId), + method: 'get', // default, + }); + + saveElectionToState(Object.assign(election, {electionAreaMap})); + } + + return election.electionAreaMap; + }; + return {props.children} diff --git a/results-tabulation-ui/src/services/tabulation-api/index.js b/results-tabulation-ui/src/services/tabulation-api/index.js index 26fd5e8b..3a66e2ed 100644 --- a/results-tabulation-ui/src/services/tabulation-api/index.js +++ b/results-tabulation-ui/src/services/tabulation-api/index.js @@ -4,6 +4,7 @@ import {getAccessToken} from "../../auth"; export const ENDPOINT_PATH_ELECTIONS = () => "/election"; export const ENDPOINT_PATH_ELECTIONS_BY_ID = (electionId) => `/election/${electionId}`; +export const ENDPOINT_PATH_ELECTION_AREA_MAP_BY_ID = (electionId) => `/election/${electionId}/area-map`; export const ENDPOINT_PATH_AREAS = () => "/area"; export const ENDPOINT_PATH_AREAS_BY_ID = (areaId) => `/area/${areaId}`; export const ENDPOINT_PATH_TALLY_SHEETS = () => "/tally-sheet"; diff --git a/results-tabulation-ui/src/services/tally-sheet.provider.js b/results-tabulation-ui/src/services/tally-sheet.provider.js index 1a791219..093e2d5c 100644 --- a/results-tabulation-ui/src/services/tally-sheet.provider.js +++ b/results-tabulation-ui/src/services/tally-sheet.provider.js @@ -29,6 +29,11 @@ export function TallySheetProvider(props) { async function refactorTallySheetObject(tallySheet) { tallySheet.tallySheetCode = tallySheet.tallySheetCode.replace(/_/g, "-"); tallySheet.election = await electionContext.getElectionById(tallySheet.electionId); + tallySheet.workflowInstance.actions = tallySheet.workflowInstanceActions; + tallySheet.area = tallySheet.submission.area; + tallySheet.areaMapList = await electionContext.getElectionAreaMap(tallySheet.electionId); + + // TODO fetch actions and area maps const {metaDataList = []} = tallySheet; tallySheet.metaDataMap = getMetaDataMap(metaDataList);