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

feat: voting outliers endpoint #22

Open
wants to merge 1 commit 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
feat: voting outliers endpoint
Signed-off-by: inimaz <93inigo93@gmail.com>
  • Loading branch information
inimaz committed Oct 9, 2024
commit 76802b7f9ffceddc9d42ba84d45bc056a55e04c6
41 changes: 41 additions & 0 deletions tipi_backend/api/business.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@

from tipi_backend.settings import Config
from tipi_backend.api.parsers import SearchInitiativeParser, InitiativeParser
from tipi_backend.api.types import VotingOutlier


""" TOPICS METHODS """
Expand Down Expand Up @@ -164,6 +165,46 @@ def get_voting(reference):
return VotingSchema(many=True).dump(Votings.get_by(reference))


def get_voting_outliers(exclude_group: str = None) -> list[VotingOutlier]:
"""Get voting outliers. Outliers are defined as people who vote differently from their parlamentary group.

Args:
exclude_group (str, optional): . Defaults to None.

Returns:
list[VotingOutlier]: List of voting outliers.
"""
pipeline = [
{"$unwind": "$by_groups"},
{
"$match": {
"by_groups.votes.yes": {"$gt": 0},
"by_groups.votes.no": {"$gt": 0},
}
},
{
"$group": {
"_id": "$_id",
"reference": {"$first": "$reference"},
"title": {"$first": "$title"},
"by_groups": {"$push": "$by_groups"},
}
},
]
if exclude_group:
pipeline[1]["$match"]["by_groups.name"] = {"$ne": exclude_group}

outliers = VotingSchema(many=True).dump(Voting.objects.aggregate(*pipeline))
def _transform_outlier(outlier: Voting) -> VotingOutlier:
return VotingOutlier(
reference=outlier["reference"],
title=outlier["title"],
group_votes=outlier["by_groups"],
).__dict__

return list(map(_transform_outlier, outliers))


""" STATS METHODS """


Expand Down
29 changes: 26 additions & 3 deletions tipi_backend/api/endpoints/voting.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

from flask_restx import Namespace, Resource

from tipi_backend.api.business import get_voting
from tipi_backend.api.business import get_voting, get_voting_outliers
from tipi_backend.api.parsers import parser_voting_outliers


log = logging.getLogger(__name__)

ns = Namespace('voting', description='Operations related to votes')
ns = Namespace("voting", description="Operations related to votes")


@ns.route('/<initiative_id>')
Expand All @@ -24,4 +25,26 @@ def to_reference(id):
return get_voting(to_reference(initiative_id))
except Exception as e:
log.error(e)
return {'Error': 'No votings found'}, 404
return {"Error": "No votings found"}, 404


ns = Namespace(
"voting-outliers",
description="Operations related to votes from outliers (people who vote differently from their group)",
)


@ns.route("/")
@ns.expect(parser_voting_outliers)
@ns.response(404, "Voting not found.")
class VotingOutlierItem(Resource):
def get(self, exclude_group=None):
"""Returns details of a voting."""
try:
log.info("Voting outliers called")
if exclude_group is None:
return get_voting_outliers()
return get_voting_outliers(exclude_group)
except Exception as e:
log.error(e)
return {"Error": "No voting outliers found"}, 404
3 changes: 3 additions & 0 deletions tipi_backend/api/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@
parser_footprint_by_parliamentarygroup.add_argument('parliamentarygroup', type=str, required=True, location='args', help='To get the values, check out /parliamentary-groups')


parser_voting_outliers = reqparse.RequestParser()
parser_voting_outliers.add_argument('exclude_group', type=str, required=True, location='args', help='To get the values, check out /parliamentary-groups')

class ParameterBag():

EMPTY_VALUES = ['', None, []]
Expand Down
9 changes: 9 additions & 0 deletions tipi_backend/api/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from dataclasses import dataclass
from typing import Any, Dict, List


@dataclass
class VotingOutlier:
reference: str
title: str
group_votes: List[Dict[str, Any]]