Skip to content

Commit 71272d3

Browse files
authored
Merge pull request #315 from PROCOLLAB-github/project_rates_finished_refactoring
Project rates finished refactoring
2 parents 06c3769 + 1928176 commit 71272d3

7 files changed

Lines changed: 217 additions & 199 deletions

File tree

project_rates/constants.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,26 @@
1+
from typing import Literal
2+
from dataclasses import dataclass
3+
4+
from rest_framework.generics import ListAPIView
5+
6+
from users.models import CustomUser
7+
8+
NumericTypes: list[str] = ["int", "float"]
9+
10+
ValidatableTypesNames = Literal[*NumericTypes, "bool", "str"]
11+
112
VERBOSE_TYPES = (
213
("str", "Текст"),
314
("int", "Целочисленное число"),
415
("float", "Число с плавающей точкой"),
516
("bool", "Да или нет"),
617
)
18+
19+
20+
@dataclass
21+
class RatesRequestData:
22+
program_id: int
23+
user: CustomUser
24+
view: ListAPIView
25+
scored: bool | None = None
26+
project_id: int | None = None

project_rates/models.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from django.contrib.auth import get_user_model
22
from django.db import models
3+
from .constants import VERBOSE_TYPES
34

45
from partner_programs.models import PartnerProgram
56
from projects.models import Project
6-
from .constants import VERBOSE_TYPES
7-
from .validators import ProjectScoreValidate
7+
from .validators import ProjectScoreValidator
88

99
User = get_user_model()
1010

@@ -80,12 +80,13 @@ def __str__(self):
8080
return f"ProjectScore<{self.id}> - {self.criteria.name}"
8181

8282
def save(self, *args, **kwargs):
83-
ProjectScoreValidate(
84-
criteria_type=self.criteria.type,
85-
value=self.value,
86-
criteria_min_value=self.criteria.min_value,
87-
criteria_max_value=self.criteria.max_value,
88-
)
83+
data_to_validate = {
84+
"criteria_type": self.criteria.type,
85+
"value": self.value,
86+
"criteria_min_value": self.criteria.min_value,
87+
"criteria_max_value": self.criteria.max_value,
88+
}
89+
ProjectScoreValidator.validate(**data_to_validate)
8990
super().save(*args, **kwargs)
9091

9192
class Meta:

project_rates/serializers.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,34 @@
33
from core.services import get_views_count
44
from .models import Criteria, ProjectScore
55
from projects.models import Project
6-
from .validators import ProjectScoreValidate
6+
from .validators import ProjectScoreValidator
77

88

99
class ProjectScoreCreateSerializer(serializers.ModelSerializer):
10+
def __init__(self, *args, **kwargs):
11+
self.criteria_to_get = kwargs.pop("criteria_to_get", None)
12+
super(ProjectScoreCreateSerializer, self).__init__(*args, **kwargs)
13+
1014
class Meta:
1115
model = ProjectScore
1216
fields = ["criteria", "user", "project", "value"]
17+
validators = []
18+
19+
def get_queryset(self):
20+
return self.Meta.model.objects.filter(
21+
criteria__id__in=self.criteria_to_get
22+
).select_related("criteria", "project", "user")
23+
24+
def validate(self, data):
25+
criteria = data["criteria"]
26+
data_to_validate = {
27+
"criteria_type": criteria.type,
28+
"value": data.get("value"),
29+
"criteria_min_value": criteria.min_value,
30+
"criteria_max_value": criteria.max_value,
31+
}
32+
ProjectScoreValidator.validate(**data_to_validate)
33+
return data
1334

1435

1536
class CriteriaSerializer(serializers.ModelSerializer):
@@ -75,7 +96,7 @@ def serialize_data_func(criteria_to_get: list, data: dict):
7596
for criterion in data:
7697
needed_criteria = criteria.get(int(criterion["criterion_id"]))
7798

78-
ProjectScoreValidate(
99+
ProjectScoreValidator(
79100
criteria_type=needed_criteria.type,
80101
value=criterion["value"],
81102
criteria_min_value=needed_criteria.min_value,

project_rates/services.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
from django.db.models import Count, Q, QuerySet
2+
3+
from project_rates.models import Criteria, ProjectScore
4+
from project_rates.serializers import (
5+
CriteriaSerializer,
6+
ProjectScoreSerializer,
7+
ProjectScoreGetSerializer,
8+
)
9+
from projects.models import Project
10+
11+
12+
def get_querysets(RatesRequestData) -> dict[str, QuerySet]:
13+
program_id = RatesRequestData.program_id
14+
project_id = RatesRequestData.project_id
15+
user = RatesRequestData.user
16+
17+
criterias = Criteria.objects.prefetch_related("partner_program").filter(
18+
partner_program_id=program_id
19+
)
20+
scores = ProjectScore.objects.prefetch_related("criteria").filter(
21+
criteria__in=criterias.values_list("id", flat=True), user=user
22+
)
23+
24+
if project_id:
25+
projects = [Project.objects.get(id=project_id)]
26+
else:
27+
projects = Project.objects.filter(
28+
partner_program_profiles__partner_program_id=program_id
29+
).distinct()
30+
31+
if RatesRequestData.scored:
32+
criterias_quantity = len(criterias)
33+
projects = projects.annotate(
34+
user_scores_count=Count("scores", filter=Q(scores__user=user))
35+
).filter(user_scores_count=criterias_quantity)
36+
37+
if not project_id:
38+
projects = RatesRequestData.view.paginate_queryset(projects)
39+
40+
return {
41+
"criterias_queryset": criterias,
42+
"scores_queryset": scores,
43+
"projects_queryset": projects,
44+
}
45+
46+
47+
def serialize_project_criterias(querysets: dict[str, QuerySet]) -> list[dict]:
48+
criteria_serializer = CriteriaSerializer(querysets["criterias_queryset"], many=True)
49+
scores_serializer = ProjectScoreSerializer(querysets["scores_queryset"], many=True)
50+
51+
projects_serializer = ProjectScoreGetSerializer(
52+
querysets["projects_queryset"],
53+
context={
54+
"data_criterias": criteria_serializer.data,
55+
"data_scores": scores_serializer.data,
56+
},
57+
many=True,
58+
)
59+
return projects_serializer.data
60+
61+
62+
def count_scored_criterias(project_data: dict):
63+
filled_values = sum(
64+
1
65+
for criteria in project_data["criterias"]
66+
if criteria["name"] == "Комментарий" or criteria.get("value")
67+
)
68+
69+
if filled_values == len(project_data["criterias"]):
70+
project_data["is_scored"] = True

project_rates/urls.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@
44
RateProject,
55
RateProjects,
66
RateProjectsDetails,
7-
ScoredProjects,
87
)
98

109
urlpatterns = [
1110
path("rate/<int:project_id>", RateProject.as_view()),
1211
path("<int:program_id>", RateProjects.as_view()),
13-
path("scored/<int:program_id>", ScoredProjects.as_view()),
1412
path("details", RateProjectsDetails.as_view()),
1513
]

project_rates/validators.py

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,38 @@
1-
class ProjectScoreValidate:
2-
def __init__(self, **kwargs):
3-
self.criteria_type = kwargs.get("criteria_type")
4-
self.value = kwargs.get("value")
5-
self.criteria_min_value = kwargs.get("criteria_min_value")
6-
self.criteria_max_value = kwargs.get("criteria_max_value")
1+
from project_rates.constants import ValidatableTypesNames, NumericTypes
72

8-
self._validate_data_type()
9-
self._validate_numeric_limits()
103

11-
def _validate_data_type(self):
12-
if self.criteria_type in ["float", "int"]:
4+
class ProjectScoreValidator:
5+
@classmethod
6+
def validate(cls, **kwargs):
7+
criteria_type: ValidatableTypesNames = kwargs.get("criteria_type")
8+
value: str = kwargs.get("value")
9+
criteria_min_value: float | None = kwargs.get("criteria_min_value")
10+
criteria_max_value: float | None = kwargs.get("criteria_max_value")
11+
12+
cls._validate_data_type(criteria_type, value)
13+
if criteria_type in NumericTypes:
14+
cls._validate_numeric_limits(
15+
criteria_min_value, criteria_max_value, float(value)
16+
)
17+
18+
@staticmethod
19+
def _validate_data_type(criteria_type: str, value: str):
20+
if criteria_type in NumericTypes:
1321
try:
14-
float(self.value)
22+
float(value)
1523
except ValueError:
1624
raise ValueError("Введённое значение не соответствует формату!")
1725
except TypeError:
1826
raise TypeError("Вы не ввели никакие данные!")
1927

20-
elif (self.criteria_type == "bool") and (self.value not in ["True", "False"]):
28+
elif (criteria_type == "bool") and (value not in ["True", "False"]):
2129
raise TypeError("Введённое значение не соответствует формату!")
2230

23-
def _validate_numeric_limits(self):
24-
if self.criteria_type in ["int", "float"]:
25-
if self.criteria_min_value is not None and self.criteria_min_value > float(
26-
self.value
27-
):
28-
raise ValueError("Оценка этого критерия принизила допустимые значения!")
29-
elif self.criteria_max_value is not None and self.criteria_max_value < float(
30-
self.value
31-
):
32-
raise ValueError("Оценка этого критерия превысила допустимые значения!")
31+
@staticmethod
32+
def _validate_numeric_limits(
33+
min_value: float | None, max_value: float | None, value: float
34+
):
35+
if min_value is not None and min_value > value:
36+
raise ValueError("Оценка этого критерия ниже допустимого значения!")
37+
elif max_value is not None and max_value < value:
38+
raise ValueError("Оценка этого критерия превысила допустимое значение!")

0 commit comments

Comments
 (0)