Skip to content

Commit

Permalink
Add interviews ics calendar, interview creation script, reformat inte…
Browse files Browse the repository at this point in the history
…rview csv report (#855)

* add interview calendar

* refactor InterviewListCSVView

* fix tables

* fix tables

* create interview creation script
  • Loading branch information
Dmi4er4 authored Jul 2, 2024
1 parent f52fd68 commit c3149be
Show file tree
Hide file tree
Showing 17 changed files with 578 additions and 370 deletions.
588 changes: 306 additions & 282 deletions apps/admission/locale/ru/LC_MESSAGES/django.po

Large diffs are not rendered by default.

115 changes: 115 additions & 0 deletions apps/admission/management/commands/create_interview_from_csv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
from admission.constants import InterviewSections
from admission.management.commands._utils import CurrentCampaignMixin
from admission.models import Applicant, Campaign, Comment, Interview
import csv
from core.models import Location
from datetime import datetime
from django.core.management import BaseCommand, CommandError
from django.conf import settings
from django.db import transaction
from django.db.models import Q
from django.utils import timezone
from users.models import User


class Command(CurrentCampaignMixin, BaseCommand):
help = """
Create interview and interview comment for applicants with id from csv
Example: ./manage.py create_interview_from_csv --date=2024-06-24 --section=PROGRAMMING --comment='МФТИ'
"""

def add_arguments(self, parser):
super().add_arguments(parser)
parser.add_argument(
"--filename",
type=str,
default='ids.csv',
help="csv file name",
)
parser.add_argument(
"--delimiter",
type=str,
default=',',
help="csv delimiter",
)
parser.add_argument(
"--date",
type=str,
default="today",
help="date of interview in YYYY-MM-DD format",
)
parser.add_argument(
"--venue",
type=str,
default='Онлайн-собеседование в ШАД Москва 2024',
help="venue of interview",
)
parser.add_argument(
"--interviewer_id",
type=int,
default=15418,
help="ID of interviewer",
)
parser.add_argument(
"--score",
type=int,
default=5,
help="interview score",
)
parser.add_argument(
"--section",
type=str,
required=True,
help="Section of interview",
)
parser.add_argument(
"--comment",
type=str,
required=True,
help="Comment text",
)

def handle(self, *args, **options):
campaigns = self.get_current_campaigns(options, confirm=False)
delimiter = options["delimiter"]
filename = options["filename"]
venue = Location.objects.get(name=options["venue"])
date = timezone.now().date() if options["date"] == "today" \
else datetime.strptime(options["date"], '%Y-%m-%d').date()
interviewer = User.objects.get(pk=options["interviewer_id"])
section = getattr(InterviewSections, options["section"])
comment_text = options["comment"]
score = options["score"]
with open(filename) as csvfile:
reader = csv.reader(csvfile, delimiter=delimiter)
headers = next(reader)
with transaction.atomic():
for applicant_number, row in enumerate(reader):
id = row[0][-6:-1]
try:
applicant = Applicant.objects.get(id=id, campaign__in=campaigns)
except Applicant.DoesNotExist:
print(f'{id} does not exists')
raise
interview = Interview(
applicant=applicant,
status=Interview.COMPLETED,
section=section,
venue=venue,
date=date)
print(applicant)
interview.save()
interview.interviewers.add(interviewer)
comment = Comment(
interview=interview,
interviewer=interviewer,
text=comment_text,
score=score)
comment.save()
if input(f'\nБудут созданы {applicant_number} завершенных собеседований по секции '
f'"{InterviewSections.get_choice(section).label}" на {date} c локацией "{venue}".\n'
f'Собеседущий {interviewer} поставит оценку {score} с комментарием "{comment_text}"\n'
f'Введите "y" для подтверждения: ') != "y":
raise CommandError("Error asking for approval. Canceled")


5 changes: 5 additions & 0 deletions apps/admission/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1581,6 +1581,11 @@ def datetime_local(self):
date = datetime.datetime.combine(self.stream.date, self.start_at)
return timezone.make_aware(date, self.stream.get_timezone())

@property
def datetime_end_local(self):
date = datetime.datetime.combine(self.stream.date, self.end_at)
return timezone.make_aware(date, self.stream.get_timezone())


class InterviewInvitationQuerySet(query.QuerySet):
# FIXME: move to selectors
Expand Down
3 changes: 1 addition & 2 deletions apps/admission/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,7 @@ class AdmissionApplicantsYearReport(AdmissionApplicantsReport):
"preferred_study_programs_cs_note",
"your_future_plans",
"admin_note",
"interview_format",
"miss_count"
"interview_format"
}

def __init__(self, year):
Expand Down
7 changes: 6 additions & 1 deletion apps/admission/selectors.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Any, Dict, List, Optional
from uuid import UUID

from django.utils import timezone
from django_filters import NumberFilter, OrderingFilter
from django_filters.rest_framework import FilterSet
from rest_framework.exceptions import ValidationError
Expand All @@ -13,7 +14,7 @@
InterviewInvitation,
InterviewSlot,
ResidenceCity,
CampaignCity,
CampaignCity
)
from core.timezone import get_now_utc

Expand Down Expand Up @@ -140,3 +141,7 @@ def residence_city_campaigns_queryset(
if "ordering" in filters:
return filter_set.qs
return filter_set.qs.order_by()

def get_ongoing_interviews(user):
return (interview for interview in user.interview_set.select_related('slot__stream')
if interview.slot.datetime_local >= timezone.now())
20 changes: 15 additions & 5 deletions apps/admission/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -850,15 +850,18 @@ def get(self, request, *args, **kwargs):
if self.request.user.time_zone:
time_zone = self.request.user.time_zone
headers = [
"date",
f"time ({time_zone})",
"applicant_name",
"interviewer_name",
_("Date"),
_("Time") + " " + str(time_zone),
_("Section"),
_("Applicant"),
_("Interviewer"),
_("Status"),
_("Format")
]
campaign_filter = Q(applicant__campaign=campaign) if campaign else Q()
writer.writerow(headers)
interviews = (
Interview.objects.select_related("applicant")
Interview.objects.select_related("applicant", "slot__stream")
.prefetch_related("interviewers")
.filter(
campaign_filter,
Expand All @@ -869,17 +872,24 @@ def get(self, request, *args, **kwargs):
)
for interview in interviews:
dt = interview.date.astimezone(time_zone)
try:
interview_format = interview.slot.stream.get_format_display()
except Interview.slot.RelatedObjectDoesNotExist:
interview_format = ''
writer.writerow(
[
dt.date().strftime("%d.%m.%Y"),
dt.time().strftime("%H:%M"),
interview.get_section_display(),
interview.applicant.full_name,
", ".join(
map(
lambda u: u.get_full_name(last_name_first=True),
interview.interviewers.all(),
)
),
interview.get_status_display(),
interview_format
]
)
return response
Expand Down
2 changes: 1 addition & 1 deletion apps/htmlpages/locale/ru/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-12 12:42+0000\n"
"POT-Creation-Date: 2024-06-30 17:39+0000\n"
"PO-Revision-Date: 2015-03-18 08:34+0000\n"
"Last-Translator: Jannis Leidel <[email protected]>\n"
"Language-Team: Russian (http://www.transifex.com/projects/p/django/language/"
Expand Down
27 changes: 27 additions & 0 deletions apps/learning/icalendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from admission.models import Interview
from admission.roles import Roles
from core.urls import reverse
from courses.models import Assignment, CourseClass
from learning.models import Event, StudentAssignment
Expand Down Expand Up @@ -211,6 +213,26 @@ def _model_to_dict(self, instance: Event):
'categories': vInline('CSC,EVENT')
}

class InteviewICalendarEvent(ICalendarEvent):
def get_calendar_event_id(self, instance: Interview, user):
return f"interviews-{user.pk}-{instance.pk}-admission@{self.domain}"

def _model_to_dict(self, instance: Interview):
absolute_url = self.url_builder(instance.get_absolute_url())
description = str(instance)
summary = f"Собеседование - {instance.get_section_display()} ({instance.slot.stream.get_format_display()})"
starts_at = instance.slot.datetime_local
ends_at = instance.slot.datetime_end_local
return {
'url': vUri(absolute_url),
'summary': vText(summary),
'description': vText(description),
'dtstart': starts_at,
'dtend': ends_at,
'created': instance.created,
'last-modified': instance.modified
}


class ICalendarURL(NamedTuple):
code: Literal["classes", "assignments", "events"]
Expand All @@ -234,4 +256,9 @@ def get_icalendar_links(account: User,
ICalendarURL(code="assignments", title=str(_("Assignments")), url=url_builder(url_assignments)),
ICalendarURL(code="events", title=str(_("Events")), url=url_builder(url_events)),
]
if Roles.INTERVIEWER in account.roles:
url_interviews = reverse('user_ical_interviews',
subdomain=settings.LMS_SUBDOMAIN,
args=[account.pk])
urls.append(ICalendarURL(code="interviews", title=str(_("Interviews")), url=url_builder(url_interviews)))
return urls
25 changes: 23 additions & 2 deletions apps/learning/views/icalendar.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
from typing import Iterable, NamedTuple

from braces.views import UserPassesTestMixin
from django.conf import settings
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.views import generic

from admission.selectors import get_ongoing_interviews
from learning.icalendar import (
StudentAssignmentICalendarEvent, StudentClassICalendarEvent,
StudyEventICalendarEvent, TeacherAssignmentICalendarEvent,
TeacherClassICalendarEvent, generate_icalendar
TeacherClassICalendarEvent, generate_icalendar, InteviewICalendarEvent
)
from learning.models import StudentAssignment
from learning.selectors import (
get_student_classes, get_study_events, get_teacher_assignments, get_teacher_classes
)
from users.models import User

class ICalendarOnlyMixin(UserPassesTestMixin):
raise_exception = False

def test_func(self, user):
return self.get_user() == user or user.is_curator


class ICalendarMeta(NamedTuple):
name: str
Expand All @@ -26,7 +34,7 @@ class ICalendarMeta(NamedTuple):


# TODO: add secret link for each student
class UserICalendarView(generic.base.View):
class UserICalendarView(ICalendarOnlyMixin, generic.base.View):
def get(self, request, *args, **kwargs):
user = self.get_user()
site = self.request.site
Expand Down Expand Up @@ -123,3 +131,16 @@ def get_calendar_events(self, user, site, url_builder, tz):
filters.append(Q(branch_id=user.branch_id))
for e in get_study_events(filters).select_related('venue'):
yield event_factory.create(e, user)

class ICalInterviewsView(UserICalendarView):
@staticmethod
def get_calendar_meta(user, site, url_builder, tz) -> ICalendarMeta:
return ICalendarMeta(
name=f"Собеседования {site.name}",
description=f"Календарь собеседований {site.name} ({user.get_full_name()})",
file_name="interviews.ics"
)
def get_calendar_events(self, user, site, url_builder, tz):
event_factory = InteviewICalendarEvent(tz, url_builder, site)
for interview in get_ongoing_interviews(user):
yield event_factory.create(interview, user)
2 changes: 1 addition & 1 deletion apps/projects/locale/ru/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-12 12:42+0000\n"
"POT-Creation-Date: 2024-06-30 17:39+0000\n"
"PO-Revision-Date: 2022-02-21 15:24+0000\n"
"Last-Translator: Сергей Жеревчук <[email protected]>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
Expand Down
2 changes: 1 addition & 1 deletion apps/surveys/locale/ru/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-12 12:42+0000\n"
"POT-Creation-Date: 2024-06-30 17:39+0000\n"
"PO-Revision-Date: 2019-10-31 16:30+0000\n"
"Last-Translator: b' <[email protected]>'\n"
"Language-Team: LANGUAGE <[email protected]>\n"
Expand Down
3 changes: 2 additions & 1 deletion apps/users/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from learning.views import EventDetailView
from learning.views.icalendar import (
ICalAssignmentsView, ICalClassesView, ICalEventsView
ICalAssignmentsView, ICalClassesView, ICalEventsView, ICalInterviewsView
)
from users.views import (
ConnectedAuthServicesView, ProfileImageUpdate, UserDetailView, UserUpdateView
Expand All @@ -25,6 +25,7 @@
re_path(r'^events.ics', ICalEventsView.as_view(), name='ical_events'),
path('users/<int:pk>/classes.ics', ICalClassesView.as_view(), name='user_ical_classes'),
path('users/<int:pk>/assignments.ics', ICalAssignmentsView.as_view(), name='user_ical_assignments'),
path('users/<int:pk>/interviews.ics', ICalInterviewsView.as_view(), name='user_ical_interviews'),

path('api/v1/', include(([
path('', include((user_api_patterns, 'api'))),
Expand Down
2 changes: 1 addition & 1 deletion compscicenter_ru/locale/ru/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-12 12:42+0000\n"
"POT-Creation-Date: 2024-06-30 17:39+0000\n"
"PO-Revision-Date: 2020-02-03 16:52+0000\n"
"Last-Translator: b' <[email protected]>'\n"
"Language-Team: LANGUAGE <[email protected]>\n"
Expand Down
2 changes: 1 addition & 1 deletion compsciclub_ru/locale/ru/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-12 12:42+0000\n"
"POT-Creation-Date: 2024-06-30 17:39+0000\n"
"PO-Revision-Date: 2020-09-09 04:43+0000\n"
"Last-Translator: b' <[email protected]>'\n"
"Language-Team: LANGUAGE <[email protected]>\n"
Expand Down
Loading

0 comments on commit c3149be

Please sign in to comment.