Coverage for src/mesh/views/utils.py: 39%
66 statements
« prev ^ index » next coverage.py v7.7.0, created at 2025-04-28 07:45 +0000
« prev ^ index » next coverage.py v7.7.0, created at 2025-04-28 07:45 +0000
1from __future__ import annotations
3from collections.abc import Callable, Iterable
4from typing import TYPE_CHECKING, Any, TypeVar
6from allauth.account.models import EmailAddress
7from django.conf import settings
8from django.contrib.auth import REDIRECT_FIELD_NAME
9from django.template.loader import render_to_string
10from django.urls import reverse_lazy
11from ptf.url_utils import add_query_parameters_to_url
12from ptf.utils import send_email, template_from_string
14from mesh.model.roles.role_handler import RoleHandler
16from ..app_settings import app_settings
17from ..models.submission_models import Submission
18from ..models.user_models import USER_TOKEN_QUERY_PARAM, Suggestion, User, UserToken
20if TYPE_CHECKING: 20 ↛ 21line 20 didn't jump to line 21 because the condition on line 20 was never true
21 from ..models.review_models import Review
23# The below is correct for python > 3.11 instead of declaring a TypeVar
24# def group_by[_T](dataset: Iterable[_T], key: Callable[[_T], Any]) -> dict[Any, list[_T]]:
26_T = TypeVar("_T")
29def group_by(dataset: Iterable[_T], key: Callable[[_T], Any]) -> dict[Any, list[_T]]:
30 """
31 Group the given dataset using the provided key function to get the value
32 to group on.
34 Params:
35 - `dataset` The dataset to group by.
36 - `key` The function used to get the grouping value for each item
37 if the dataset.
38 Returns:
39 - `dict` The dictionnary with keys being the group key, and
40 values the list of items in the group.
41 """
42 grouped_data: dict[Any, list[_T]] = {}
43 for item in dataset:
44 value = key(item)
45 if value not in grouped_data:
46 grouped_data[value] = []
47 grouped_data[value].append(item)
49 return grouped_data
52def create_new_user(
53 email: str, first_name: str, last_name: str, password: str | None = None
54) -> User:
55 """
56 Create a new User and a new EmailAddress using the given e-mail address.
57 No verification are made regarding the provided e-mail address
58 """
59 user = User.objects.create_user(
60 email, password=password, first_name=first_name, last_name=last_name
61 )
62 EmailAddress.objects.create(user=user, email=email, primary=True, verified=False)
64 return user
67def get_review_request_email(
68 submission: Submission, base_url: str, template_name: str = ""
69) -> str:
70 """
71 Render the default e-mail template for the review request.
72 """
73 template_name = template_name or "mesh/emails/review/referee_request.html"
74 if base_url[-1] == "/":
75 base_url = base_url[:-1]
76 context = {
77 "submission_name": submission.name,
78 "submission_abstract": submission.abstract,
79 "submission_url": base_url
80 + reverse_lazy("mesh:submission_details", kwargs={"pk": submission.pk}),
81 }
83 return render_to_string(template_name, context)
86def send_review_request_email(review: Review, email: str, subject: str, base_url: str):
87 """
88 Sends the provided email to the review's assigned reviewer.
89 Params:
90 - `review` The review object.
91 - `email` The e-mail content (HTML).
92 - `subject` The e-mail subject.
93 - `base_url` The base URL to use when generating links.
94 """
95 recipient: User = review.reviewer
96 to = [recipient.email]
97 if base_url[-1] == "/":
98 base_url = base_url[:-1]
100 submission = review.version.submission
101 context = {"RECIPIENT": recipient.get_full_name()}
103 # Generate a token authentication URL if the user is allowed to
104 if getattr(recipient, "is_token_authentication_allowed", False):
105 role_handler = RoleHandler(recipient, partial_init=True)
107 if role_handler.token_authentication_allowed():
108 token = UserToken.get_token(recipient)
109 context["TOKEN_AUTH_URL"] = add_query_parameters_to_url(
110 base_url + reverse_lazy("mesh:token_login"),
111 {
112 USER_TOKEN_QUERY_PARAM: [token.key],
113 REDIRECT_FIELD_NAME: [
114 reverse_lazy("mesh:submission_details", kwargs={"pk": submission.pk})
115 ],
116 },
117 )
118 email_template = template_from_string(email)
119 rendered_email = email_template.render(context)
121 subject = f"{app_settings.EMAIL_PREFIX}{subject}"
122 from_email = f"{review.created_by.get_full_name()} via MESH <{settings.DEFAULT_FROM_EMAIL}>"
123 reply_to = [review.created_by.email] # type:ignore
125 send_email(rendered_email, subject, to, from_email=from_email, reply_to=reply_to)
127 review.request_email = rendered_email
128 review.save()
131def get_suggestion(submission):
132 qs = Suggestion.objects.filter(submission=submission).order_by("seq")
133 selected_reviewers = []
134 for suggestion in qs:
135 if suggestion.suggested_user is not None:
136 reviewer = suggestion.suggested_user
137 reviewer.type = "user"
138 else:
139 reviewer = suggestion.suggested_reviewer
140 reviewer.type = "reviewer"
141 selected_reviewers.append(reviewer)
142 return selected_reviewers