Exemplo 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)
Exemplo n.º 2
0
    def new_session(
        self,
        search_request: BusinessSearchRequest,
        dinner_party_active_id: Optional[str],
        user_maybe: Optional[SerializableBasicUser],
    ) -> SearchSession:
        session_id = str(uuid4())

        dinner_party_id = None
        if dinner_party_active_id is not None:
            dinner_party = self.__rcv_manager.get_active_election_by_active_id(
                dinner_party_active_id)
            if dinner_party is None:
                raise HttpException(
                    message=
                    f"Election with active id {dinner_party_active_id} not found",
                    error_code=ErrorCode.NO_ELECTION_FOUND)
            dinner_party_id = dinner_party.id

        new_session = SearchSession(
            id=session_id,
            search_request=search_request,
            dinner_party_id=dinner_party_id,
            created_by_id=None if user_maybe is None else user_maybe.id)
        db_session = DbSession()
        db_session.add(new_session)
        db_session.commit()
        return new_session
Exemplo n.º 3
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)
Exemplo n.º 4
0
    def create_anonymous_user(self, nickname: str) -> BasicUser:
        user_id = str(uuid4())
        user: BasicUser = BasicUser(id=user_id, nickname=nickname)
        db_session = DbSession()
        db_session.add(user)
        db_session.commit()

        return user
Exemplo 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)
Exemplo n.º 6
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
Exemplo n.º 7
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
Exemplo n.º 8
0
    def apply_recommendation_action_to_current(
        self,
        session_id: str,
        current_recommendation_id: str,
        recommendation_action: RecommendationAction,
    ):
        db_session = DbSession()
        current_session: SearchSession = SearchSession.get_session_by_id(
            db_session, session_id)

        if current_recommendation_id != current_session.current_recommendation_id:
            raise ValueError(
                f"Attempting to {recommendation_action} recommendation of id {current_recommendation_id}"
                f" but current recommendation is {current_session.current_recommendation_id} for "
                f"session {current_session.id}")
        current_recommendation: Recommendation = Recommendation.get_recommendation_by_key(
            db_session, session_id, current_recommendation_id)
        if current_recommendation is None:
            return ValueError(
                f"Attempting to {recommendation_action} recommendation of id {current_recommendation_id},"
                f"but current recommendation is None. This indicates the state of the session is incorrect."
            )
        if recommendation_action == RecommendationAction.MAYBE:
            current_session.maybe_recommendations.append(
                current_recommendation)
            current_recommendation.status = RecommendationAction.MAYBE
        elif recommendation_action == RecommendationAction.REJECT:
            current_session.rejected_recommendations.append(
                current_recommendation)
            current_recommendation.status = RecommendationAction.REJECT
        else:
            current_session.accepted_recommendations.append(
                current_recommendation)
            current_recommendation.status = RecommendationAction.ACCEPT
            db_session.commit(
            )  # complete transaction so rcv_manager can add candidate for SqlLite
            if current_session.is_dinner_party:
                self.__rcv_manager.add_candidate(
                    active_id=current_session.dinner_party.active_id,
                    business_id=current_recommendation_id,
                    user_id=current_session.created_by_id,
                )
            else:
                self.__complete_session(db_session=db_session,
                                        current_session=current_session)

        db_session.commit()
        current_session.current_recommendation = None
Exemplo n.º 9
0
    def get_next_recommendation_for_session(
            self, session_id: str) -> DisplayableRecommendation:
        db_session = DbSession()
        search_session: SearchSession = SearchSession.get_session_by_id(
            db_session, session_id)
        if search_session.current_recommendation is not None:
            raise ValueError(
                "Cannot get a new recommendation. The search session has a current recommendation"
            )

        if search_session.is_complete:
            return None

        recommendation: Recommendation = self.__recommendation_manager.generate_new_recommendation_for_session(
            search_session)
        db_session.add(recommendation)
        db_session.commit()
        return self.__recommendation_manager.get_displayable_recommendation_from_recommendation(
            recommendation)
Exemplo n.º 10
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
         )
     )
Exemplo n.º 11
0
    def apply_action_to_maybe(self, session_id: str, recommendation_id: str,
                              recommendation_action: RecommendationAction):
        db_session = DbSession()
        current_session: SearchSession = SearchSession.get_session_by_id(
            db_session, session_id)

        if recommendation_action == RecommendationAction.MAYBE:
            raise ValueError(
                f"Cannot maybe a recommendation that is already maybed")

        if current_session.is_dinner_party:
            raise ValueError(
                "Cannot apply maybe recommendation to dinner party search session"
            )

        if recommendation_id not in current_session.maybe_recommendation_ids:
            raise ValueError(
                f"Recommendation of id {recommendation_id} not in maybe recommendations"
            )

        recommendation = Recommendation.get_recommendation_by_key(
            db_session, current_session.id, recommendation_id)

        recommendation.status = recommendation_action
        current_session.maybe_recommendations = [
            rec for rec in current_session.maybe_recommendations
            if rec.business_id != recommendation_id
        ]

        if recommendation_action == RecommendationAction.ACCEPT:
            current_session.accepted_recommendations.append(recommendation)
            recommendation.session_id = session_id  # ORM workaround
            self.__complete_session(db_session=db_session,
                                    current_session=current_session)
        elif recommendation_action == RecommendationAction.REJECT:
            current_session.rejected_recommendations.append(recommendation)
        else:
            raise ValueError(
                f"Unsupported operation to a maybe operation: {recommendation_action}"
            )
        db_session.commit()