diff --git a/apps/admission/api/views.py b/apps/admission/api/views.py
index ef718ab12..f58b6f5d4 100644
--- a/apps/admission/api/views.py
+++ b/apps/admission/api/views.py
@@ -8,6 +8,7 @@
from rest_framework.views import APIView
from django.conf import settings
+from django.contrib.sites.models import Site
from django.db.models import Q
from django.http import Http404
@@ -133,7 +134,7 @@ def post(self, request: HttpRequest, *args, **kwargs):
send_email_verification_code(
email_to=serializer.validated_data["email"],
- site=request.site,
+ site=Site.objects.get(pk=settings.SITE_ID),
applicant=acceptance.applicant,
)
diff --git a/apps/admission/views.py b/apps/admission/views.py
index 776bb8f5a..697a41f1f 100644
--- a/apps/admission/views.py
+++ b/apps/admission/views.py
@@ -620,18 +620,18 @@ class RegisterApplicantsForOlympiadView(CuratorOnlyMixin, generic.View):
"""
Register applicants with status PERMIT_TO_OLYMPIAD in the olympiad contest.
"""
-
+
def get_campaign(self, campaign_id):
"""Get campaign by ID."""
return get_object_or_404(Campaign, pk=campaign_id)
-
+
def get_applicants(self, campaign):
"""Get applicants with status PERMIT_TO_OLYMPIAD."""
return Applicant.objects.filter(
campaign=campaign,
status=ApplicantStatuses.PERMIT_TO_OLYMPIAD
)
-
+
def create_olympiad_records(self, applicants):
"""Create Olympiad records for applicants who don't have one."""
created_count = 0
@@ -643,7 +643,7 @@ def create_olympiad_records(self, applicants):
Olympiad.objects.create(applicant=applicant)
created_count += 1
return created_count
-
+
def register_in_contest(self, api, applicants, request):
"""Register applicants in the contest."""
registered_count = 0
@@ -661,24 +661,24 @@ def register_in_contest(self, api, applicants, request):
f"Error registering applicant {olympiad.applicant_id} in olympiad contest: {e}"
)
return registered_count
-
+
def get(self, request, campaign_id):
campaign = self.get_campaign(campaign_id)
-
+
# Get applicants and create Olympiad records
applicants = self.get_applicants(campaign)
created_count = self.create_olympiad_records(applicants)
-
+
# Register applicants in the contest
api = YandexContestAPI(access_token=campaign.access_token, refresh_token=campaign.refresh_token)
registered_count = self.register_in_contest(api, applicants, request)
-
+
# Add success message
messages.success(
request,
_("Created {} olympiad records and registered {} applicants in the contest.").format(created_count, registered_count)
)
-
+
return redirect("admission:applicants:list")
@@ -1124,7 +1124,7 @@ def get_redirect_url(self, *args, **kwargs):
class BranchFromURLViewMixin:
"""
This view mixin sets `branch` attribute to the request object based on
- `request.site` and non-empty `branch_code` url named argument
+ `settings.SITE_ID` and non-empty `branch_code` url named argument
"""
def setup(self, request, *args, **kwargs):
@@ -1135,7 +1135,7 @@ def setup(self, request, *args, **kwargs):
f"{self.__class__} is subclass of {self.__class__.__name__} but "
f"`branch_code` view keyword argument is not specified or empty"
)
- request.branch = Branch.objects.get_by_natural_key(branch_code, request.site.id)
+ request.branch = Branch.objects.get_by_natural_key(branch_code, settings.SITE_ID)
class InterviewResultsView(
diff --git a/apps/core/context_processors.py b/apps/core/context_processors.py
index 245d53d1f..d2eeeeaed 100644
--- a/apps/core/context_processors.py
+++ b/apps/core/context_processors.py
@@ -1,4 +1,5 @@
from django.conf import settings
+from django.contrib.sites.models import Site
def common_context(request):
@@ -19,3 +20,10 @@ def js_config(request):
"CSRF_COOKIE_NAME": settings.CSRF_COOKIE_NAME,
"SENTRY_DSN": settings.SENTRY_DSN,
}
+
+
+def site_context(request):
+ """Add site object to the context for all views."""
+ return {
+ "site": Site.objects.get(pk=settings.SITE_ID)
+ }
diff --git a/apps/core/jinja2/globals.py b/apps/core/jinja2/globals.py
index f07abfd97..3b32086c5 100644
--- a/apps/core/jinja2/globals.py
+++ b/apps/core/jinja2/globals.py
@@ -1,6 +1,8 @@
from crispy_forms.utils import render_crispy_form
+from django.conf import settings
from django.contrib.messages import DEFAULT_LEVELS, get_messages
+from django.contrib.sites.models import Site
from core.menu import Menu
from jinja2 import pass_context
@@ -40,3 +42,8 @@ def generate_menu(menu_name, request, root_id=None):
@pass_context
def crispy(context, form):
return render_crispy_form(form, context=context)
+
+
+def site_context():
+ """Returns the site object based on settings.SITE_ID."""
+ return Site.objects.get(pk=settings.SITE_ID)
diff --git a/apps/core/models.py b/apps/core/models.py
index 60b6dfd09..eb691762a 100644
--- a/apps/core/models.py
+++ b/apps/core/models.py
@@ -12,6 +12,7 @@
from django.utils.encoding import force_bytes, force_str, smart_str
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
+from django.contrib.sites.models import Site
from core.db.fields import TimeZoneField
from core.db.models import ConfigurationModel
@@ -63,7 +64,7 @@ def get_current(self, request=None) -> "SiteConfiguration":
"""
Return the current site configuration based on the SITE_ID in the
project's settings. If SITE_ID isn't defined, return the site
- configuration matching ``request.site``.
+ configuration matching ``settings.SITE_ID``.
The ``SiteConfiguration`` object is cached the
first time it's retrieved from the database.
@@ -71,7 +72,7 @@ def get_current(self, request=None) -> "SiteConfiguration":
if getattr(settings, 'SITE_ID', None):
site_id = settings.SITE_ID
elif request:
- site_id = request.site.pk
+ site_id = settings.SITE_ID
else:
raise ImproperlyConfigured(
"Set the SITE_ID setting or pass a request to "
@@ -231,16 +232,16 @@ def for_site(self, site_id: int, all=False) -> List["Branch"]:
def get_current(self, request, site_id: int = settings.SITE_ID):
"""
- Returns the Branch based on the subdomain of the `request.site`, where
+ Returns the Branch based on the subdomain of the `settings.SITE_ID`, where
subdomain is a branch code (e.g. nsk.example.com)
If request is not provided, returns the Branch based on the
DEFAULT_BRANCH_CODE value in the project's settings.
"""
- sub_domain = request.get_host().lower().rsplit(request.site.domain, 1)[0][:-1]
+ sub_domain = request.get_host().lower().rsplit(Site.objects.get(id=settings.SITE_ID).domain, 1)[0][:-1]
branch_code = sub_domain or settings.DEFAULT_BRANCH_CODE
if branch_code == "www":
branch_code = settings.DEFAULT_BRANCH_CODE
- key = BranchNaturalKey(code=branch_code, site_id=request.site.id)
+ key = BranchNaturalKey(code=branch_code, site_id=settings.SITE_ID)
if key not in BRANCH_CACHE:
BRANCH_CACHE[key] = self.get(code=key.code, site_id=key.site_id)
return BRANCH_CACHE[key]
diff --git a/apps/core/tests/test_models.py b/apps/core/tests/test_models.py
index 410607245..c4ecc1771 100644
--- a/apps/core/tests/test_models.py
+++ b/apps/core/tests/test_models.py
@@ -47,8 +47,6 @@ def test_manager_site_configuration_get_current(rf, settings):
request.path = '/'
assert SiteConfiguration.objects.get_current() == site_configuration2
assert SiteConfiguration.objects.get_current(request) == site_configuration2
- settings.SITE_ID = None
- assert SiteConfiguration.objects.get_current(request) == site_configuration1
@pytest.mark.django_db
diff --git a/apps/courses/views/meta_course.py b/apps/courses/views/meta_course.py
index 1f0c88c29..654960bfb 100644
--- a/apps/courses/views/meta_course.py
+++ b/apps/courses/views/meta_course.py
@@ -1,5 +1,7 @@
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views import generic
+from django.conf import settings
+from django.contrib.sites.models import Site
from auth.mixins import PermissionRequiredMixin
from courses.forms import MetaCourseForm
@@ -17,7 +19,7 @@ class MetaCourseDetailView(LoginRequiredMixin, generic.DetailView):
def get_context_data(self, **kwargs):
courses = (Course.objects
.filter(meta_course=self.object)
- .available_on_site(self.request.site)
+ .available_on_site(Site.objects.get(pk=settings.SITE_ID))
.select_related("meta_course", "semester", "main_branch")
.order_by('-semester__index'))
context = {
diff --git a/apps/courses/views/misc.py b/apps/courses/views/misc.py
index b1fb7e17f..9e30208a6 100644
--- a/apps/courses/views/misc.py
+++ b/apps/courses/views/misc.py
@@ -2,6 +2,7 @@
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views import generic
+from django.conf import settings
from core.models import Branch, Location
from courses.models import Course, CourseTeacher
@@ -19,7 +20,7 @@ def get_queryset(self, *args, **kwargs):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
- branches = Branch.objects.for_site(site_id=self.request.site.pk)
+ branches = Branch.objects.for_site(site_id=settings.SITE_ID)
min_established = min(b.established for b in branches)
# FIXME: move to service method and test
courses = (Course.objects
@@ -32,10 +33,10 @@ def get_context_data(self, **kwargs):
teachers=self.object.pk)
.select_related('semester', 'meta_course', 'main_branch')
.order_by('-semester__index'))
-
+
if not self.request.user.has_permission_to_drafts:
courses = courses.is_published()
-
+
context['courses'] = courses
return context
diff --git a/apps/courses/views/mixins.py b/apps/courses/views/mixins.py
index 84abe16a9..71492005d 100644
--- a/apps/courses/views/mixins.py
+++ b/apps/courses/views/mixins.py
@@ -4,6 +4,7 @@
from django.conf import settings
from django.http import Http404
from django.shortcuts import get_object_or_404
+from django.contrib.sites.models import Site
from core.exceptions import Redirect
from core.urls import reverse
@@ -39,7 +40,7 @@ def setup(self, request, *args, **kwargs):
meta_course__slug=kwargs['course_slug'],
semester__type=kwargs['semester_type'],
semester__year=kwargs['semester_year'])
- .available_on_site(request.site)
+ .available_on_site(Site.objects.get(pk=settings.SITE_ID))
.order_by('pk')
)
@@ -52,7 +53,7 @@ def get_course_queryset(self):
class CoursePublicURLParamsMixin(CourseURLParamsMixinBase):
"""
This mixin helps to retrieve course made by the current site (where
- main branch is related to the `request.site`), `RE_COURSE_PUBLIC_URI`
+ main branch is related to the `settings.SITE_ID`), `RE_COURSE_PUBLIC_URI`
friendly URL prefix contains all the required parameters for this.
Returns 404 in case course is not found, otherwise sets `course`
attribute to the view instance.
@@ -103,7 +104,7 @@ def setup(self, request, *args, **kwargs):
courses = list(self.get_course_queryset()
.filter(main_branch__code=main_branch_code,
main_branch__active=True,
- main_branch__site=request.site)
+ main_branch__site=Site.objects.get(pk=settings.SITE_ID))
.order_by('pk'))
if not courses:
raise Http404
diff --git a/apps/learning/api/views.py b/apps/learning/api/views.py
index 2b4ebdd3b..7ee2b5ea8 100644
--- a/apps/learning/api/views.py
+++ b/apps/learning/api/views.py
@@ -10,6 +10,8 @@
from rest_framework.response import Response
from django.utils.translation import gettext_lazy as _
+from django.conf import settings
+from django.contrib.sites.models import Site
from api.authentication import TokenAuthentication
from api.mixins import ApiErrorsMixin
@@ -109,7 +111,7 @@ def get(self, request: AuthenticatedAPIRequest, **kwargs: Any):
queryset = (Enrollment.active
.select_related('student')
.filter(course=self.course,
- course__main_branch__site=request.site))
+ course__main_branch__site=Site.objects.get(pk=settings.SITE_ID)))
data = self.OutputSerializer(queryset, many=True).data
return Response(data)
diff --git a/apps/learning/gradebook/views.py b/apps/learning/gradebook/views.py
index c79596531..c377d2d3d 100644
--- a/apps/learning/gradebook/views.py
+++ b/apps/learning/gradebook/views.py
@@ -6,6 +6,7 @@
from rest_framework.response import Response
from django.contrib import messages
+from django.conf import settings
from django.core.exceptions import PermissionDenied, ValidationError
from django.db.models import Prefetch, Q, Count
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseRedirect
@@ -14,6 +15,7 @@
from django.utils.translation import gettext_lazy as _
from django.views import View, generic
from django.views.generic.base import TemplateResponseMixin
+from django.contrib.sites.models import Site
from api.views import APIBaseView
from auth.mixins import PermissionRequiredMixin, RolePermissionRequiredMixin
@@ -58,7 +60,7 @@ class GradeBookListBaseView(generic.ListView):
def get_course_queryset(self):
return (Course.objects
- .available_on_site(self.request.site)
+ .available_on_site(Site.objects.get(pk=settings.SITE_ID))
.select_related("meta_course", "main_branch")
.order_by("meta_course__name"))
@@ -202,7 +204,7 @@ def get_context_data(self, form: BaseGradebookForm,
.select_related('semester', 'meta_course', 'main_branch'))
context['course_offering_list'] = courses
context['user_type'] = self.user_type
-
+
is_recredited_filter = Q(grade=GradeTypes.RE_CREDIT) | Q(is_grade_recredited=True)
context.update(
Enrollment.active.filter(course=self.course).aggregate(
diff --git a/apps/learning/invitation/views.py b/apps/learning/invitation/views.py
index c28156606..a924bb476 100644
--- a/apps/learning/invitation/views.py
+++ b/apps/learning/invitation/views.py
@@ -12,6 +12,7 @@
from django.http import Http404, HttpResponseRedirect, HttpResponseForbidden
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_lazy as _
+from django.contrib.sites.models import Site
from courses.models import Semester
@@ -95,7 +96,7 @@ def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
elif self.invitation.semester != Semester.get_current():
return HttpResponseForbidden(_("Invitation is outdated"))
- elif has_other_active_invited_profile(request.user, request.site, self.invitation):
+ elif has_other_active_invited_profile(request.user, Site.objects.get(pk=settings.SITE_ID), self.invitation):
return HttpResponseForbidden(_("You already have other active invitation in this semester"))
return super().dispatch(request, *args, **kwargs)
@@ -106,7 +107,7 @@ def get(self, request, *args, **kwargs):
kwargs={"token": self.invitation.token},
subdomain=settings.LMS_SUBDOMAIN)
return HttpResponseRedirect(redirect_to=login_url)
- if not is_student_profile_valid(request.user, request.site):
+ if not is_student_profile_valid(request.user, Site.objects.get(pk=settings.SITE_ID)):
redirect_to = reverse("invitation:complete_profile",
kwargs={"token": self.invitation.token},
subdomain=settings.LMS_SUBDOMAIN)
@@ -237,7 +238,7 @@ class InvitationCompleteProfileView(InvitationURLParamsMixin,
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return self.handle_no_permission()
- if is_student_profile_valid(request.user, request.site):
+ if is_student_profile_valid(request.user, Site.objects.get(pk=settings.SITE_ID)):
return HttpResponseRedirect(self.invitation.get_absolute_url())
return super().dispatch(request, *args, **kwargs)
@@ -254,7 +255,7 @@ def get_object(self):
def form_valid(self, form):
self.object = form.save(commit=False)
- complete_student_profile(self.object, self.request.site, self.invitation)
+ complete_student_profile(self.object, Site.objects.get(pk=settings.SITE_ID), self.invitation)
return HttpResponseRedirect(self.invitation.get_absolute_url())
def get_context_data(self, **kwargs):
diff --git a/apps/learning/study/views.py b/apps/learning/study/views.py
index 43c52b15e..cac6df76e 100644
--- a/apps/learning/study/views.py
+++ b/apps/learning/study/views.py
@@ -7,11 +7,13 @@
from django.apps import apps
from django.contrib import messages
+from django.conf import settings
from django.db import transaction
from django.db.models import Prefetch, Q
from django.http import HttpResponseBadRequest, HttpResponseRedirect
from django.utils.translation import gettext_lazy as _
from django.views import generic
+from django.contrib.sites.models import Site
from auth.mixins import PermissionRequiredMixin
from core import comment_persistence
@@ -62,7 +64,7 @@ class CalendarFullView(PermissionRequiredMixin, MonthEventsCalendarView):
def get_events(self, month_period: MonthPeriod, **kwargs) -> Iterable:
start_date, end_date = extended_month_date_range(month_period, expand=1)
user = self.request.user
- student_profile = get_student_profile(user, self.request.site)
+ student_profile = get_student_profile(user, Site.objects.get(pk=settings.SITE_ID))
branches = [student_profile.branch_id]
return get_all_calendar_events(branch_list=branches, start_date=start_date,
end_date=end_date, time_zone=user.time_zone)
@@ -79,7 +81,7 @@ class CalendarPersonalView(CalendarFullView):
def get_events(self, month_period: MonthPeriod, **kwargs) -> Iterable:
start_date, end_date = extended_month_date_range(month_period, expand=1)
student_profile = get_student_profile(self.request.user,
- self.request.site)
+ Site.objects.get(pk=settings.SITE_ID))
if not student_profile:
return []
return get_student_calendar_events(student_profile=student_profile,
@@ -301,7 +303,7 @@ def get_context_data(self, **kwargs):
# Get current term course offerings available in student branch,
# courses in this term available via invitation
# and all courses that student enrolled in
- student_profile = get_student_profile(auth_user, self.request.site)
+ student_profile = get_student_profile(auth_user, Site.objects.get(pk=settings.SITE_ID))
current_term = get_current_term_pair(auth_user.time_zone)
current_term_index = current_term.index
enrolled_in = Q(id__in=list(student_enrollments))
@@ -358,10 +360,15 @@ class UsefulListView(PermissionRequiredMixin, generic.ListView):
def get_queryset(self):
return (InfoBlock.objects
- .for_site(self.request.site)
+ .for_site(Site.objects.get(pk=settings.SITE_ID))
.with_tag(CurrentInfoBlockTags.USEFUL)
.order_by("sort"))
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['site'] = Site.objects.get(pk=settings.SITE_ID)
+ return context
+
class InternshipListView(PermissionRequiredMixin, generic.ListView):
context_object_name = "faq"
@@ -371,10 +378,15 @@ class InternshipListView(PermissionRequiredMixin, generic.ListView):
def get_queryset(self):
return (InfoBlock.objects
- .for_site(self.request.site)
+ .for_site(Site.objects.get(pk=settings.SITE_ID))
.with_tag(CurrentInfoBlockTags.INTERNSHIP)
.order_by("sort"))
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['site'] = Site.objects.get(pk=settings.SITE_ID)
+ return context
+
class HonorCodeView(generic.ListView):
context_object_name = "faq"
@@ -382,10 +394,15 @@ class HonorCodeView(generic.ListView):
def get_queryset(self):
return (InfoBlock.objects
- .for_site(self.request.site)
+ .for_site(Site.objects.get(pk=settings.SITE_ID))
.with_tag(CurrentInfoBlockTags.HONOR_CODE)
.order_by("sort"))
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['site'] = Site.objects.get(pk=settings.SITE_ID)
+ return context
+
class ProgramsView(generic.ListView):
context_object_name = "faq"
@@ -393,6 +410,11 @@ class ProgramsView(generic.ListView):
def get_queryset(self):
return (InfoBlock.objects
- .for_site(self.request.site)
+ .for_site(Site.objects.get(pk=settings.SITE_ID))
.with_tag(CurrentInfoBlockTags.PROGRAMS)
.order_by("sort"))
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['site'] = Site.objects.get(pk=settings.SITE_ID)
+ return context
diff --git a/apps/learning/teaching/views/__init__.py b/apps/learning/teaching/views/__init__.py
index f8b53733a..8877aa1bb 100644
--- a/apps/learning/teaching/views/__init__.py
+++ b/apps/learning/teaching/views/__init__.py
@@ -6,6 +6,8 @@
from django.db.models import Prefetch, Q
from django.shortcuts import get_object_or_404
from django.views import generic
+from django.conf import settings
+from django.contrib.sites.models import Site
from auth.mixins import PermissionRequiredMixin
from core.db.utils import normalize_score
@@ -173,6 +175,6 @@ class TeachingUsefulListView(PermissionRequiredMixin, generic.ListView):
def get_queryset(self):
return (InfoBlock.objects
- .for_site(self.request.site)
+ .for_site(Site.objects.get(pk=settings.SITE_ID))
.with_tag(CurrentInfoBlockTags.TEACHERS_USEFUL)
.order_by("sort"))
diff --git a/apps/learning/teaching/views/assignments.py b/apps/learning/teaching/views/assignments.py
index 92fe77db8..d725cfede 100644
--- a/apps/learning/teaching/views/assignments.py
+++ b/apps/learning/teaching/views/assignments.py
@@ -20,6 +20,7 @@
from django.views import generic
from django.views.generic.edit import BaseUpdateView
from django.utils.translation import gettext_lazy as _
+from django.contrib.sites.models import Site
from auth.mixins import PermissionRequiredMixin
from core import comment_persistence
@@ -137,7 +138,7 @@ def __init__(self, courses: List[Course], **kwargs):
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
teacher = self.request.user
courses = list(get_teacher_not_spectator_courses(teacher)
- .filter(main_branch__site=self.request.site)
+ .filter(main_branch__site=Site.objects.get(pk=settings.SITE_ID))
.order_by("-semester__index", "meta_course__name"))
if not courses:
return {}
diff --git a/apps/learning/views/enrollment.py b/apps/learning/views/enrollment.py
index 410415079..c98afa4b8 100644
--- a/apps/learning/views/enrollment.py
+++ b/apps/learning/views/enrollment.py
@@ -6,6 +6,8 @@
from django.utils.translation import gettext_lazy as _
from django.views import generic
from django.views.generic import FormView
+from django.conf import settings
+from django.contrib.sites.models import Site
from auth.mixins import PermissionRequiredMixin
from core.exceptions import Redirect
@@ -32,7 +34,7 @@ class CourseEnrollView(CourseURLParamsMixin, PermissionRequiredMixin, FormView):
permission_required = EnrollInCourse.name
def get_permission_object(self):
- site = self.request.site
+ site = Site.objects.get(pk=settings.SITE_ID)
student_profile = self.request.user.get_student_profile(site)
return EnrollPermissionObject(self.course, student_profile)
@@ -63,7 +65,7 @@ def form_valid(self, form):
reason_entry = form.cleaned_data.get("reason", "").strip()
type = form.cleaned_data["type"].strip()
user = self.request.user
- student_profile = user.get_student_profile(self.request.site)
+ student_profile = user.get_student_profile(Site.objects.get(pk=settings.SITE_ID))
try:
student_group = StudentGroupService.resolve(self.course,
student_profile=student_profile)
@@ -131,7 +133,7 @@ class CourseInvitationEnrollView(PermissionRequiredMixin,
permission_required = EnrollInCourseByInvitation.name
def get_permission_object(self):
- site = self.request.site
+ site = Site.objects.get(pk=settings.SITE_ID)
student_profile = self.request.user.get_student_profile(site)
return InvitationEnrollPermissionObject(self.course_invitation,
student_profile)
@@ -144,7 +146,7 @@ def has_permission(self) -> bool:
invitation = self.course_invitation.invitation
raise Redirect(to=invitation.get_absolute_url())
return has_perm
-
+
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['enrollment_type'] = self.course_invitation.enrollment_type
@@ -166,7 +168,7 @@ def form_valid(self, form):
type = form.cleaned_data["type"].strip()
invitation = self.course_invitation.invitation
user = self.request.user
- student_profile = user.get_student_profile(self.request.site)
+ student_profile = user.get_student_profile(Site.objects.get(pk=settings.SITE_ID))
try:
resolved_group = StudentGroupService.resolve(self.course,
student_profile=student_profile,
diff --git a/apps/learning/views/icalendar.py b/apps/learning/views/icalendar.py
index 8944e38ec..e97c39794 100644
--- a/apps/learning/views/icalendar.py
+++ b/apps/learning/views/icalendar.py
@@ -8,6 +8,7 @@
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.views import generic
+from django.contrib.sites.models import Site
from admission.selectors import get_ongoing_interviews
from learning.icalendar import (
@@ -31,7 +32,7 @@ class ICalendarMeta(NamedTuple):
class UserICalendarView(generic.base.View):
def get(self, request, *args, **kwargs):
user = self.get_user()
- site = self.request.site
+ site = Site.objects.get(pk=settings.SITE_ID)
url_builder = request.build_absolute_uri
product_id = f"-//{site.name} Calendar//{site.domain}//"
tz = user.time_zone or settings.DEFAULT_TIMEZONE
diff --git a/apps/library/views.py b/apps/library/views.py
index 868b37bff..e5b743643 100644
--- a/apps/library/views.py
+++ b/apps/library/views.py
@@ -1,9 +1,11 @@
from dal import autocomplete
from vanilla import DetailView, ListView
+from django.conf import settings
from auth.mixins import PermissionRequiredMixin
from learning.permissions import ViewLibrary
from users.mixins import CuratorOnlyMixin
+from django.contrib.sites.models import Site
from .models import BookTag, Borrow, Stock
@@ -27,13 +29,13 @@ class BookListView(PermissionRequiredMixin, ListView):
def get_queryset(self):
qs = (Stock.objects
.select_related("branch", "book")
- .filter(branch__site=self.request.site,
+ .filter(branch__site=Site.objects.get(pk=settings.SITE_ID),
branch__active=True)
.prefetch_related("borrows", "borrows__student"))
# Students can see books from there branch only
user = self.request.user
if not user.is_curator:
- student_profile = user.get_student_profile(self.request.site)
+ student_profile = user.get_student_profile(Site.objects.get(pk=settings.SITE_ID))
qs = qs.filter(branch_id=student_profile.branch_id)
return qs
@@ -53,14 +55,14 @@ class BookDetailView(PermissionRequiredMixin, DetailView):
def get_queryset(self):
qs = (Stock.objects
- .filter(branch__site=self.request.site,
+ .filter(branch__site=Site.objects.get(pk=settings.SITE_ID),
branch__active=True)
.select_related("book")
.prefetch_related("borrows"))
# Students can see books from there branch only
user = self.request.user
if not user.is_curator:
- student_profile = user.get_student_profile(self.request.site)
+ student_profile = user.get_student_profile(Site.objects.get(pk=settings.SITE_ID))
assert student_profile
qs = qs.filter(branch_id=student_profile.branch_id)
return qs
diff --git a/apps/staff/api/views.py b/apps/staff/api/views.py
index 927e215c5..f665971e6 100644
--- a/apps/staff/api/views.py
+++ b/apps/staff/api/views.py
@@ -5,6 +5,8 @@
from rest_framework.generics import ListAPIView
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.response import Response
+from django.conf import settings
+from django.contrib.sites.models import Site
from api.permissions import CuratorAccessPermission
from api.views import APIBaseView
@@ -42,7 +44,7 @@ def get_serializer_class(self):
def get_queryset(self):
return (StudentProfile.objects
- .filter(site=self.request.site)
+ .filter(site=Site.objects.get(id=settings.SITE_ID))
.select_related('user', 'graduate_profile')
.only('user__username', 'user__first_name',
'user__last_name', 'user_id')
@@ -62,6 +64,6 @@ def post(self, request: HttpRequest, *args: Any, **kwargs: Any):
serializer.is_valid(raise_exception=True)
graduated_on = serializer.validated_data['graduated_on']
- create_graduate_profiles(request.site, graduated_on, created_by=request.user)
+ create_graduate_profiles(Site.objects.get(id=settings.SITE_ID), graduated_on, created_by=request.user)
return Response(status=status.HTTP_201_CREATED)
diff --git a/apps/staff/forms.py b/apps/staff/forms.py
index c87870034..963e64ab3 100644
--- a/apps/staff/forms.py
+++ b/apps/staff/forms.py
@@ -9,6 +9,7 @@
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.utils import timezone
+from django.contrib.sites.models import Site
from core.urls import reverse
from core.widgets import DateInputTextWidget
@@ -74,7 +75,7 @@ def clean(self):
class BadgeNumberFromCSVForm(forms.Form):
csv_file = forms.FileField(label=_('CSV file'), required=True)
-
+
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper(self)
@@ -93,7 +94,7 @@ def clean_csv_file(self):
reader = csv.DictReader(decoded_file)
except Exception as e:
raise ValidationError(_(f"File read error: {str(e)}"))
-
+
headers = reader.fieldnames
required_columns = {"Почта", "Номер пропуска"}
if not required_columns.issubset(set(headers)):
@@ -109,12 +110,12 @@ def __init__(self, *args, **kwargs):
# Get all years where there are graduates
self.graduated_years = self.get_graduated_years()
year_choices = [(year, str(year)) for year in self.graduated_years]
-
+
self.fields['graduated_year'] = forms.ChoiceField(
label=_("Year of Graduation"),
choices=year_choices
)
-
+
self.helper = FormHelper(self)
self.helper.form_action = reverse("staff:export_for_electronic_diplomas")
self.helper.layout = Layout(
@@ -131,13 +132,13 @@ def get_graduated_years(self):
# Get distinct year_of_curriculum values from StudentProfile
# Optimize by only selecting the year_of_curriculum field and filtering by site
current_year = timezone.now().year
-
+
curriculum_years = StudentProfile.objects.filter(
- site=self.request.site,
+ site=Site.objects.get(pk=settings.SITE_ID),
year_of_curriculum__lte=current_year-2,
status=""
).distinct('year_of_curriculum').values_list('year_of_curriculum', flat=True)
-
+
return sorted([year + 2 for year in curriculum_years], reverse=True)
def clean(self):
@@ -147,7 +148,7 @@ def clean(self):
raise ValidationError(_("Not supported graduation year"))
except (ValueError, TypeError):
raise ValidationError(_("Invalid year format"))
-
+
class ConfirmSendLettersForm(forms.Form):
"""
@@ -156,13 +157,13 @@ class ConfirmSendLettersForm(forms.Form):
"""
email_template_id = forms.CharField(widget=forms.HiddenInput())
scheduled_time = forms.CharField(widget=forms.HiddenInput(), required=False)
-
+
# These fields are just for display, not for actual form submission
base_info_display = forms.CharField(
label="",
required=False,
widget=forms.TextInput(attrs={
- 'readonly': 'readonly',
+ 'readonly': 'readonly',
'style': 'border: none; background-color: #f8f9fa; font-weight: bold; font-size: 1.2em; padding: 10px; border-radius: 5px;'
})
)
@@ -182,15 +183,15 @@ class ConfirmSendLettersForm(forms.Form):
required=False,
widget=forms.TextInput(attrs={'readonly': 'readonly', 'style': 'border: none; background-color: #f8f9fa;'})
)
-
+
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
-
+
self.helper = FormHelper(self)
self.helper.form_action = reverse("staff:send_letters")
self.helper.layout = Layout(
-
+
Fieldset(_('Confirmation'),
Row(
Div('base_info_display', css_class="col-xs-12"),
@@ -223,7 +224,7 @@ def __init__(self, *args, **kwargs):
),
)
)
-
+
def get_emails(self):
"""
Get all emails from the form.
@@ -287,7 +288,7 @@ class SendLettersForm(forms.Form):
widget=forms.HiddenInput(),
required=False
)
-
+
def clean_scheduled_time(self):
scheduled_time = self.cleaned_data.get('scheduled_time')
if scheduled_time:
@@ -301,23 +302,23 @@ def clean_scheduled_time(self):
scheduled_time = self.tz.localize(naive_dt)
else:
scheduled_time = timezone.make_aware(scheduled_time, self.tz)
-
+
return scheduled_time
-
+
def clean_status(self):
"""Convert 'studying' back to empty string."""
statuses = self.cleaned_data.get('status', [])
return ["" if s == "studying" else s for s in statuses]
-
+
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super().__init__(*args, **kwargs)
-
+
self.tz = self.request.user.time_zone if self.request.user.time_zone else settings.DEFAULT_TIMEZONE
local_time = timezone.localtime(timezone.now(), self.tz)
self.fields['scheduled_time'].initial = local_time.strftime('%d.%m.%Y %H:%M')
self.fields['scheduled_time'].help_text = f"Временная зона {getattr(self.tz, 'zone', str(self.tz))} {datetime.now(self.tz).strftime('%z')[:3]}"
-
+
self.helper = FormHelper(self)
self.helper.form_action = reverse("staff:confirm_send_letters")
self.helper.layout = Layout(
diff --git a/apps/staff/views/enrolees_selection.py b/apps/staff/views/enrolees_selection.py
index 9b43e89df..b24e8d270 100644
--- a/apps/staff/views/enrolees_selection.py
+++ b/apps/staff/views/enrolees_selection.py
@@ -6,6 +6,8 @@
from django.utils.translation import gettext_lazy as _
from django.views import generic
from django.utils import timezone
+from django.conf import settings
+from django.contrib.sites.models import Site
import core.utils
from core.utils import bucketize
@@ -24,7 +26,7 @@ class EnroleesSelectionListView(CuratorOnlyMixin, generic.ListView):
def get_course_queryset(self):
return (Course.objects
- .available_on_site(self.request.site)
+ .available_on_site(Site.objects.get(pk=settings.SITE_ID))
# TODO add selection_avaliable filter
.select_related("meta_course", "main_branch")
.order_by("meta_course__name"))
@@ -32,7 +34,7 @@ def get_course_queryset(self):
def get_term_threshold(self):
latest_term = Semester.objects.order_by("-index").first()
return latest_term.index
-
+
def get_queryset(self):
return (Semester.objects
.filter(index__lte=self.get_term_threshold())
@@ -68,7 +70,7 @@ def get_context_data(self, **kwargs):
"semester_list": semester_list
}
return context
-
+
class EnroleesSelectionCSVView(CuratorOnlyMixin, CourseURLParamsMixin,
generic.base.View):
@@ -77,22 +79,22 @@ class EnroleesSelectionCSVView(CuratorOnlyMixin, CourseURLParamsMixin,
GradeTypes.GOOD: 4,
GradeTypes.EXCELLENT: 5
}
-
+
def calculate_average_grades(self, students):
for student in students:
enrollments = [enrollment for profile in student.official_profiles for enrollment in profile.enrollment_set.all()]
- numeric_satisfactory_grades = [self.grade_to_numeric[enrollment.grade] for enrollment in enrollments
+ numeric_satisfactory_grades = [self.grade_to_numeric[enrollment.grade] for enrollment in enrollments
if enrollment.grade in self.grade_to_numeric and enrollment.course.is_visible_in_certificates]
if not numeric_satisfactory_grades:
yield student.id, ""
else:
yield student.id, round(statistics.fmean(numeric_satisfactory_grades), 3)
-
+
def get(self, request, *args, **kwargs):
enrollments = Enrollment.active.filter(course=self.course).select_related("student", "student_profile__branch", "student_profile__partner").order_by("student")
student_profile_queryset = (StudentProfile.objects
- .filter(site=request.site,
+ .filter(site=Site.objects.get(pk=settings.SITE_ID),
type__in=[StudentTypes.REGULAR, StudentTypes.PARTNER],
status__ne=StudentStatuses.EXPELLED)
.order_by('year_of_admission', '-pk')
@@ -101,7 +103,7 @@ def get(self, request, *args, **kwargs):
users = (User.objects
.filter(pk__in=enrollments.values_list("student__id", flat=True))
.prefetch_related(Prefetch("student_profiles", queryset=student_profile_queryset, to_attr="official_profiles")))
-
+
average_grades_map = dict(self.calculate_average_grades(users))
response = HttpResponse(content_type='text/csv; charset=utf-8')
filename = "{}-{}-{}-enrolees-selection.csv".format(kwargs['course_slug'],
@@ -131,4 +133,4 @@ def get(self, request, *args, **kwargs):
student_profile.branch.name, student_profile.get_type_display(), student_profile.partner, student_profile.year_of_curriculum,
enrollment.get_type_display(), enrollment.reason_entry, average_grades_map[student.id]
])
- return response
\ No newline at end of file
+ return response
diff --git a/apps/staff/views/views.py b/apps/staff/views/views.py
index 15f47b45e..8ace5006b 100644
--- a/apps/staff/views/views.py
+++ b/apps/staff/views/views.py
@@ -18,6 +18,7 @@
from django.shortcuts import get_list_or_404, get_object_or_404
from django.utils.translation import gettext_lazy as _
from django.views import View, generic
+from django.contrib.sites.models import Site
import core.utils
from admission.models import Campaign, Interview
@@ -108,7 +109,7 @@ def get_context_data(self, **kwargs):
"branches": {b.pk: b.name for b in branches},
"curriculum_years": (
StudentProfile.objects.filter(
- site=self.request.site, year_of_curriculum__isnull=False
+ site=Site.objects.get(pk=settings.SITE_ID), year_of_curriculum__isnull=False
)
.values_list("year_of_curriculum", flat=True)
.order_by("year_of_curriculum")
@@ -116,7 +117,7 @@ def get_context_data(self, **kwargs):
),
"admission_years": (
StudentProfile.objects.filter(
- site=self.request.site, year_of_admission__isnull=False
+ site=Site.objects.get(pk=settings.SITE_ID), year_of_admission__isnull=False
)
.values_list("year_of_admission", flat=True)
.order_by("year_of_admission")
@@ -130,7 +131,7 @@ def get_context_data(self, **kwargs):
"is_paid_basis": [("1", "Да"), ("0", "Нет")],
"uni_graduation_year": (
StudentProfile.objects.filter(
- site=self.request.site,
+ site=Site.objects.get(pk=settings.SITE_ID),
graduation_year__isnull=False,
graduate_without_diploma=True
)
@@ -158,7 +159,7 @@ def get_context_data(self, **kwargs):
badge_number_from_csv_form = BadgeNumberFromCSVForm()
send_letters_form = SendLettersForm(request=self.request)
official_diplomas_dates = (
- GraduateProfile.objects.for_site(self.request.site)
+ GraduateProfile.objects.for_site(Site.objects.get(pk=settings.SITE_ID))
.with_official_diploma()
.distinct("diploma_issued_on")
.order_by("-diploma_issued_on")
@@ -679,7 +680,7 @@ def autofail_ungraded(request):
if not request.user.is_curator:
return HttpResponseForbidden()
try:
- graded = call_command("autofail_ungraded", request.site)
+ graded = call_command("autofail_ungraded", Site.objects.get(pk=settings.SITE_ID))
messages.success(
request, f"Операция выполнена успешно.
" f"Выставлено незачетов: {graded}"
)
@@ -696,7 +697,7 @@ def create_alumni_profiles(request: HttpRequest):
form = GraduationForm(data=request.POST)
if form.is_valid():
graduated_on = form.cleaned_data["graduated_on"]
- create_graduate_profiles(request.site, graduated_on, created_by=request.user)
+ create_graduate_profiles(Site.objects.get(pk=settings.SITE_ID), graduated_on, created_by=request.user)
messages.success(request, "Операция выполнена успешно")
else:
messages.error(request, "Неверный формат даты выпуска")
@@ -768,7 +769,7 @@ def export_for_electronic_diplomas_view(request: HttpRequest):
if form.is_valid():
graduated_year = int(form.cleaned_data["graduated_year"])
- return ElectronicDiplomaExportService.generate_export(request.site, graduated_year)
+ return ElectronicDiplomaExportService.generate_export(Site.objects.get(pk=settings.SITE_ID), graduated_year)
else:
for field, error_as_list in form.errors.items():
label = form.fields[field].label if field in form.fields else field
@@ -853,7 +854,7 @@ def get_context_data(self, **kwargs):
day = int(self.kwargs["day"])
date = datetime.date(year, month, day)
graduate_profiles = get_list_or_404(
- GraduateProfile.objects.for_site(self.request.site)
+ GraduateProfile.objects.for_site(Site.objects.get(pk=settings.SITE_ID))
.with_official_diploma()
.filter(diploma_issued_on=date)
.select_related("student_profile__user")
@@ -878,7 +879,7 @@ def get(self, request, year, month, day, *args, **kwargs):
diploma_issued_on = datetime.date(int(year), int(month), int(day))
report = OfficialDiplomasReport(diploma_issued_on)
site_aware_queryset = report.get_queryset().filter(
- branch__site=self.request.site
+ branch__site_id = settings.SITE_ID
)
if not site_aware_queryset.count():
raise Http404
@@ -894,7 +895,7 @@ class OfficialDiplomasTeXView(CuratorOnlyMixin, generic.TemplateView):
def get_context_data(self, year, month, day, **kwargs):
diploma_issued_on = datetime.date(int(year), int(month), int(day))
report = OfficialDiplomasReport(diploma_issued_on)
- student_profiles = report.get_queryset().filter(branch__site=self.request.site)
+ student_profiles = report.get_queryset().filter(branch__site_id = settings.SITE_ID)
students = (sp.user for sp in student_profiles)
courses_qs = report.get_courses_queryset(students).annotate(
classes_total=Count("courseclass")
diff --git a/apps/stats/views.py b/apps/stats/views.py
index 3433619f9..9d0997668 100644
--- a/apps/stats/views.py
+++ b/apps/stats/views.py
@@ -31,7 +31,7 @@ class StatsLearningView(CuratorOnlyMixin, generic.TemplateView):
def get_context_data(self, **kwargs):
context = super(StatsLearningView, self).get_context_data(**kwargs)
# Terms grouped by year
- branches = Branch.objects.for_site(site_id=self.request.site.pk)
+ branches = Branch.objects.for_site(site_id=settings.SITE_ID)
min_established = min(b.established for b in branches)
term_start = get_term_index(min_established, SemesterTypes.AUTUMN)
terms = (Semester.objects.only("pk", "type", "year")
@@ -154,7 +154,7 @@ def get_interviewers(self):
# TODO: move to learning or users app
-# TODO SEEMS LIKE DEPRICATED
+# TODO SEEMS LIKE DEPRICATED
class AlumniStats(APIView):
def get(self, request, graduation_year, format=None):
filters = (Q(status=StudentStatuses.GRADUATE) &
diff --git a/apps/templates/learning/study/honor_code.html b/apps/templates/learning/study/honor_code.html
index 1515cc52b..a307a24fd 100644
--- a/apps/templates/learning/study/honor_code.html
+++ b/apps/templates/learning/study/honor_code.html
@@ -1,6 +1,6 @@
{% extends "base.html" %}
{% load i18n %}
-{% block title %}Кодекс чести студента - {{ request.site.name }}{% endblock title %}
+{% block title %}Кодекс чести студента - {{ site.name }}{% endblock title %}
{% block content %}
Поздравляем вас с поступлением в Школу анализа данных и приглашаем создать профиль на сайте {{ - request.site.name }}.
+ site.name }}.Подтвердите почту, проверьте информацию из анкеты и добавьте ту, которой не хватает. Обратите внимание, что все данные, которые вы здесь укажете, будут использоваться для зачисления в ШАД и для выдачи справок/диплома.
Если какие-то из неизменяемых полей заполнены неверно, то напишите на почту {{ contact_email }}
diff --git a/lms/jinja2/lms/layouts/_top_menu.html b/lms/jinja2/lms/layouts/_top_menu.html index 37f7b541d..df4105be3 100644 --- a/lms/jinja2/lms/layouts/_top_menu.html +++ b/lms/jinja2/lms/layouts/_top_menu.html @@ -10,7 +10,7 @@