Ejemplo n.º 1
0
    def mark_election_as_complete(self, election_id: str,
                                  complete_reason: ElectionStatus):
        if (complete_reason != ElectionStatus.MANUALLY_COMPLETE
                or complete_reason != ElectionStatus.MARKED_COMPLETE):
            raise ValueError(
                f"Invalid complete reason: {complete_reason.value}")

        db_session = DbSession()
        # could lead to race conditions, but exact completed_at not support important
        partial_election = Election.get_election_by_id(
            db_session,
            election_id,
            query_modifier=lambda x: x.options(
                load_only(Election.id, Election.election_status)),
        )
        if partial_election == complete_reason:
            return

        if partial_election.election_status != ElectionStatus.VOTING:
            raise InvalidElectionStateException(
                message=
                f"Cannot move an election to complete with status: {partial_election.election_status.value}"
            )
        partial_election.election_status = complete_reason
        partial_election.election_completed_at = datetime.now()
        db_session.commit()
        self.__push_election_status_change_to_update_stream(
            election_id, complete_reason)
Ejemplo n.º 2
0
    def move_election_to_voting(self, election_id: str):
        db_session = DbSession()
        partial_election = Election.get_election_by_id(
            db_session,
            election_id,
            query_modifier=lambda x: x.options(
                load_only(Election.id, Election.election_status)),
        )
        if partial_election is None:
            raise HttpException(
                message=f"Election with election id: {election_id} not found",
                status_code=404,
            )
        # TODO: Refactor election status "is already status" check (not changing) for both move_election_to_voting and
        #  mark_as_complete into one function that fetches the partial electoin
        if partial_election.election_status == ElectionStatus.VOTING:
            return
        if partial_election.election_status != ElectionStatus.IN_CREATION:
            raise InvalidElectionStateException(
                message=
                f"Cannot move an election to status {ElectionStatus.VOTING.name} when status is {partial_election.election_status.value}"
            )

        # TODO: validate change made by using WHERE
        partial_election.election_status = ElectionStatus.VOTING
        db_session.commit()
        self.__push_election_status_change_to_update_stream(
            election_id, ElectionStatus.VOTING)
Ejemplo n.º 3
0
 def consume(self, election_id: str):
     db_session = DbSession()
     Election.delete_election_results_for_election(db_session, election_id)
     db_session.flush()
     db_session.commit()
     rankings_by_voter: Dict[
         str, List[str]
     ] = Election.get_rankings_by_user_for_election(db_session, election_id)
     self.__run_election_round(
         db_session=db_session,
         election_id=election_id,
         rankings=list(rankings_by_voter.values()),
     )
     db_session.commit()
     ElectionUpdateStream.for_election(election_id).publish_message(
         ServerSentEvent(
             type=ElectionUpdateEventType.RESULTS_UPDATED, payload=None
         )
     )
Ejemplo n.º 4
0
    def get_election_update_stream(self,
                                   election_id: str) -> ElectionUpdateStream:
        db_session = DbSession()
        if (Election.get_election_by_id(
                db_session, election_id,
                lambda x: x.options(load_only(Election.id))) is None):
            raise HttpException(
                message=f"Election with id: {election_id} not found",
                status_code=404)

        return ElectionUpdateStream.for_election(election_id)
Ejemplo n.º 5
0
    def vote(self, user_id: str, election_id: str, votes: [str]):
        db_session = DbSession()
        partial_election = Election.get_election_by_id(
            db_session,
            election_id,
            query_modifier=lambda x: x.options(
                load_only(Election.id, Election.election_status)),
        )

        if partial_election is None:
            raise HttpException(
                message=f"Election with id: {election_id} not found",
                status_code=404)

        if partial_election.election_status != ElectionStatus.VOTING:
            raise InvalidElectionStateException(
                f"Cannot vote unless election is in voting status. Current status: {partial_election.election_status.name}"
            )

        candidate_ids = set([
            candidate.business_id for candidate in partial_election.candidates
        ])
        for business_id in votes:
            if business_id not in candidate_ids:
                raise HttpException(
                    message=f"Unknown or duplicate candidate id {business_id}",
                    status_code=404,
                )
            candidate_ids.remove(business_id)

        if len(candidate_ids) > 0:
            raise HttpException(
                message=
                f"Missing votes for candidates: {', '.join(candidate_ids)}",
                status_code=400,
            )

        rankings = [
            Ranking(
                user_id=user_id,
                election_id=election_id,
                business_id=business_id,
                rank=index,
            ) for index, business_id in enumerate(votes)
        ]
        Ranking.delete_users_rankings_for_election(db_session,
                                                   user_id=user_id,
                                                   election_id=election_id)
        db_session.add_all(rankings)
        db_session.commit()

        self.__queue_election_for_result_update(election=partial_election)
Ejemplo n.º 6
0
    def get_candidates(self, active_id: str) -> [Candidate]:
        db_session = DbSession()
        partial_election = Election.get_active_election_by_active_id(
            db_session,
            active_id,
            query_modifier=lambda x: x.options(load_only(Election.id)),
        )

        if partial_election is None:
            raise HttpException(
                message=f"Election with active id: {active_id} not found",
                status_code=404,
            )
        return partial_election.candidates
Ejemplo n.º 7
0
 def create_election(self, user: SerializableBasicUser) -> Election:
     db_session = DbSession()
     election_id = str(uuid4())
     while True:
         try:
             election = Election(
                 id=election_id,
                 active_id=self.__generate_election_active_id(),
                 election_creator_id=user.id,
             )
             db_session.add(election)
             db_session.commit()
             return election
         except BaseException as error:
             raise error
Ejemplo n.º 8
0
    def add_candidate(self, active_id: str, business_id: str,
                      user_id: str) -> bool:
        db_session = DbSession()

        partial_election = Election.get_active_election_by_active_id(
            db_session=db_session,
            active_id=active_id,
            query_modifier=lambda x: x.options(
                load_only(Election.id, Election.election_status)),
        )
        if partial_election is None:
            raise HttpException(
                message=f"Election with active id: {active_id} not found",
                status_code=404,
            )
        if partial_election.election_status != ElectionStatus.IN_CREATION:
            raise InvalidElectionStateException(
                message=
                f"Cannot add a candidate to an election with status: {partial_election.election_status.value}"
            )

        # Verify business exists? Cache business
        # Calculate distance

        candidate = Candidate(
            election_id=partial_election.id,
            business_id=business_id,
            distance=0,
            nominator_id=user_id,
        )
        db_session.add(candidate)
        try:
            db_session.commit()
        except IntegrityError as error:
            if is_unique_key_error(error):
                return False
            raise error
        # TODO: Accelerate fetching process here
        business: DisplayableBusiness = self.__business_manager.get_displayable_business(
            business_id=business_id)
        # TODO: Maybe just pass the name stored within the cookie instead of refetching from the database
        nickname = self.__user_manager.get_nickname_by_user_id(user_id)
        ElectionUpdateStream.for_election(partial_election.id).publish_message(
            CandidateAddedEvent(business_id=business_id,
                                name=business.name,
                                nominator_nickname=nickname
                                if nickname is not None else "Unknown"))
        return True
Ejemplo n.º 9
0
 def get_displayable_election_by_id(
         self, id: str) -> Optional[DisplayableElection]:
     db_session = DbSession()
     election = Election.get_election_by_id(db_session=db_session, id=id)
     if election is None:
         return None
     return DisplayableElection(
         id=election.id,
         active_id=election.active_id,
         election_status=election.election_status,
         candidates=[
             DisplayableCandidate(
                 business_id=candidate.business_id,
                 name=self.__business_manager.get_displayable_business(
                     candidate.business_id).name,
                 nominator_nickname=candidate.nominator.nickname)
             for candidate in election.candidates
         ])
Ejemplo n.º 10
0
 def get_active_election_by_active_id(self, active_id: str) -> Election:
     db_session = DbSession()
     return Election.get_active_election_by_active_id(db_session, active_id)