diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index c76d809c1..354f98a95 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -1,13 +1,13 @@
# Base Python image
-FROM python:3.9.1-buster
+FROM python:3.10-bullseye
# Update system and install backup utilities
COPY back-end/docker/image/install-postgres-client.sh /
RUN sh install-postgres-client.sh
-# Install node 14
-RUN curl -sL https://deb.nodesource.com/setup_14.x | bash -
-RUN apt -y install nodejs
+# Install node 16
+RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
+RUN . ~/.nvm/nvm.sh && nvm install 16
# Setup fish
RUN apt -y install fish; chsh -s /usr/bin/fish; mkdir /root/.config; mkdir /root/.config/fish
diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml
index 1a2b05c01..544dbed0e 100644
--- a/.devcontainer/docker-compose.yml
+++ b/.devcontainer/docker-compose.yml
@@ -18,7 +18,8 @@ services:
- ../back-end/.env
volumes:
# Forwards the local Docker socket to the container.
- - /var/run/docker.sock:/var/run/docker-host.sock
+ - ~/.ssh:/root/.ssh
+ - /var/run/docker.sock:/var/run/docker.sock
# Update this to wherever you want VS Code to mount the folder of your project
- ..:/workspace:cached
diff --git a/.github/workflows/back-end.yaml b/.github/workflows/back-end.yaml
index 197db8ed1..97482e92b 100644
--- a/.github/workflows/back-end.yaml
+++ b/.github/workflows/back-end.yaml
@@ -12,15 +12,15 @@ jobs:
- run: cp back-end/.env.example back-end/.env
- run: cp tgbot/.env.example tgbot/.env
- run: cp watchdoc/.env.example watchdoc/.env
- - name: "Build back-end container"
- run: docker-compose build back-end
- name: "Run Postgres in background"
- run: docker-compose up -d postgres
+ run: docker compose up -d postgres
+ - name: "Build back-end container"
+ run: docker compose build back-end
- name: "Run tests"
- run: docker-compose run back-end scripts/test.sh
+ run: docker compose run back-end scripts/test.sh
- name: "Check code style"
- run: docker-compose run back-end scripts/format.sh --check
+ run: docker compose run back-end scripts/format.sh --check
# - name: "Lint code"
# run: docker-compose run back-end scripts/lint.sh
- name: "Stop all containers"
- run: docker-compose down
+ run: docker compose down
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index c61669a5f..4b6dee64e 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -55,7 +55,7 @@ jobs:
sudo chmod 777 vpn.log
sudo killall openvpn
- name: Upload VPN logs
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
if: always()
with:
name: VPN logs
diff --git a/.github/workflows/front-end.yaml b/.github/workflows/front-end.yaml
index f3a93ed77..7c4d8ba1d 100644
--- a/.github/workflows/front-end.yaml
+++ b/.github/workflows/front-end.yaml
@@ -20,4 +20,4 @@ jobs:
- name: "Check code style"
run: cd front-end && npm run lint .
- name: "Build front end container"
- run: docker-compose build front-end
+ run: docker compose build front-end
diff --git a/back-end/docker/image/dev.dockerfile b/back-end/docker/image/dev.dockerfile
index a326ad901..a6628b7f7 100644
--- a/back-end/docker/image/dev.dockerfile
+++ b/back-end/docker/image/dev.dockerfile
@@ -1,5 +1,5 @@
# Base Python image
-FROM python:3.10-buster
+FROM python:3.10-bullseye
# Update system and install backup utilities
COPY back-end/docker/image/install-postgres-client.sh /
diff --git a/back-end/docker/image/prod.dockerfile b/back-end/docker/image/prod.dockerfile
index d028e9993..1e608eb72 100644
--- a/back-end/docker/image/prod.dockerfile
+++ b/back-end/docker/image/prod.dockerfile
@@ -1,5 +1,5 @@
# Base Python image
-FROM python:3.10-buster
+FROM python:3.10-bullseye
# Update system and install backup utilities
COPY back-end/docker/image/install-postgres-client.sh /
diff --git a/back-end/src/ams/utils/common.py b/back-end/src/ams/utils/common.py
index ef283c3c5..5ea2a99ca 100644
--- a/back-end/src/ams/utils/common.py
+++ b/back-end/src/ams/utils/common.py
@@ -3,6 +3,6 @@
def get_current_admission_year():
now = datetime.now()
- if (now.month, now.day) > (9, 1):
+ if (now.month, now.day) >= (9, 1):
return now.year + 1
return now.year
diff --git a/back-end/src/ams/views/applicants.py b/back-end/src/ams/views/applicants.py
index 61af435f6..ef136e659 100644
--- a/back-end/src/ams/views/applicants.py
+++ b/back-end/src/ams/views/applicants.py
@@ -32,6 +32,8 @@
from ams.models.applicants import Applicant
+from common.models.universities import Program
+
from ams.serializers.applicants import (
ApplicantSerializer,
ApplicantMutateSerializer,
@@ -147,6 +149,15 @@ def create(self, request, *args, **kwargs):
request.data["user"] = self.request.user.id
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
+ if not self.milspecialty_is_selectable(
+ request.data["milspecialty"], request.data["university_info"]["program"]
+ ):
+ return Response(
+ {
+ "detail": "You can't select this milspecialty with your educational program"
+ },
+ status=status.HTTP_400_BAD_REQUEST,
+ )
self.request.user.campuses = [self.request.data["university_info"]["campus"]]
self.request.user.save()
if self.is_creation_allowed_by_scope(request.data):
@@ -177,6 +188,15 @@ def update(self, request, *args, **kwargs):
{"detail": "Bad request"},
status=status.HTTP_400_BAD_REQUEST,
)
+ if not self.milspecialty_is_selectable(
+ request.data["milspecialty"], request.data["university_info"]["program"]
+ ):
+ return Response(
+ {
+ "detail": "You can't select this milspecialty with your educational program"
+ },
+ status=status.HTTP_400_BAD_REQUEST,
+ )
result = super(ApplicantViewSet, self).update(request, **kwargs)
applicant.user.campuses = [request.data["university_info"]["campus"]]
applicant.user.save()
@@ -187,6 +207,10 @@ def update(self, request, *args, **kwargs):
generate_documents_for_applicant(updated_applicant)
return result
+ def milspecialty_is_selectable(self, milspecialty_id: int, program_id: int):
+ milspecialty = Milspecialty.objects.filter(pk=milspecialty_id).first()
+ return milspecialty.is_selectable_by_program(program_id)
+
@transaction.atomic
def perform_create(self, serializer):
return serializer.save()
diff --git a/back-end/src/common/admin.py b/back-end/src/common/admin.py
index eceb30e40..cadbd12e3 100644
--- a/back-end/src/common/admin.py
+++ b/back-end/src/common/admin.py
@@ -30,5 +30,5 @@
admin.site.register(Program)
admin.site.register(Faculty)
-# Milspeciality
+# Milspecialty
admin.site.register(Milspecialty)
diff --git a/back-end/src/common/management/commands/populate.py b/back-end/src/common/management/commands/populate.py
index 3d47435fd..6189853be 100644
--- a/back-end/src/common/management/commands/populate.py
+++ b/back-end/src/common/management/commands/populate.py
@@ -20,6 +20,9 @@
create_faculties,
create_programs,
)
+from common.populate.milspecialities_selectable_by_programs import (
+ create_milspecialities_selectable_by_programs,
+)
from common.utils.date import get_date_range
@@ -169,6 +172,9 @@ def handle(self, *args, **options):
subjects = create_subjects(milspecialties)
faculties = create_faculties()
programs = create_programs(faculties)
+ create_milspecialities_selectable_by_programs(
+ milspecialties=milspecialties, programs=programs
+ )
print(" OK")
diff --git a/back-end/src/common/migrations/0005_add_selectable_by_to_milspecs.py b/back-end/src/common/migrations/0005_add_selectable_by_to_milspecs.py
new file mode 100644
index 000000000..fb7936676
--- /dev/null
+++ b/back-end/src/common/migrations/0005_add_selectable_by_to_milspecs.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.2.20 on 2024-10-12 16:39
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('common', '0004_add_personal_documents_info'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='milspecialty',
+ name='selectable_by',
+ field=models.ManyToManyField(to='common.Program'),
+ ),
+ migrations.AddField(
+ model_name='milspecialty',
+ name='selectable_by_every_program',
+ field=models.BooleanField(default=True),
+ ),
+ ]
diff --git a/back-end/src/common/models/milspecialties.py b/back-end/src/common/models/milspecialties.py
index 6c86b39c1..3834d4915 100644
--- a/back-end/src/common/models/milspecialties.py
+++ b/back-end/src/common/models/milspecialties.py
@@ -1,8 +1,12 @@
+from typing import Union
+
from django.db import models
from django.contrib.postgres.fields import ArrayField
-
-from common.models.universities import Campus
+from common.models.universities import Campus, Program
+from rest_framework.exceptions import ValidationError
+from django.db.models.signals import m2m_changed
+from django.dispatch import receiver
class Milspecialty(models.Model):
@@ -18,9 +22,41 @@ class Milspecialty(models.Model):
)
)
+ selectable_by = models.ManyToManyField(to=Program, blank=True)
+
+ selectable_by_every_program = models.BooleanField(default=True)
+
class Meta:
verbose_name = "Military Specialty"
verbose_name_plural = "Military Specialties"
def __str__(self) -> str:
return self.title
+
+ def is_selectable_by_program(self, program_: Union[Program, int]):
+ if isinstance(program_, Program):
+ program = program_.pk
+ else:
+ program = program_
+ return (
+ self.selectable_by.filter(pk=program).exists()
+ or self.selectable_by_every_program
+ )
+
+
+@receiver(m2m_changed, sender=Milspecialty.selectable_by.through)
+def validate_b_titles(
+ sender, instance: Milspecialty, action, reverse, model, pk_set, **kwargs
+):
+ if action in ["post_add", "post_remove", "post_clear"]:
+ mismatched_progs = instance.selectable_by.exclude(
+ faculty__campus__in=instance.available_for
+ )
+ if mismatched_progs.exists():
+ program: Program = mismatched_progs.first()
+ raise ValidationError(
+ {
+ "selectable_by": f"Can't make milspecialty {instance} be selectable by program {program}: "
+ f"program's campus ({program.faculty.campus}) is not available for this program"
+ }
+ )
diff --git a/back-end/src/common/populate/milspecialities_selectable_by_programs.py b/back-end/src/common/populate/milspecialities_selectable_by_programs.py
new file mode 100644
index 000000000..0ee0568a7
--- /dev/null
+++ b/back-end/src/common/populate/milspecialities_selectable_by_programs.py
@@ -0,0 +1,20 @@
+from common.models.milspecialties import Milspecialty
+
+from common.models.universities import (
+ Program,
+)
+
+
+def create_milspecialities_selectable_by_programs(
+ milspecialties: dict[str, Milspecialty], programs: dict[str, Milspecialty]
+):
+ milspecialties["453000"].selectable_by.add(
+ programs["Информационная безопасность"],
+ programs["Информатика и вычислительная техника"],
+ programs["Программная инженерия"],
+ )
+ milspecialties["453100"].selectable_by.add(
+ programs["Информационная безопасность"],
+ programs["Информатика и вычислительная техника"],
+ programs["Программная инженерия"],
+ )
diff --git a/back-end/src/common/populate/milspecialties.py b/back-end/src/common/populate/milspecialties.py
index 09be19b3c..9b3bd3734 100644
--- a/back-end/src/common/populate/milspecialties.py
+++ b/back-end/src/common/populate/milspecialties.py
@@ -10,11 +10,13 @@ def create_milspecialties() -> dict[str, Milspecialty]:
"code": "453000",
"title": "Организация эксплуатации и ремонта автоматизированных систем управления и вычислительных комплексов ракетно-космической обороны",
"available_for": [Campus.MOSCOW.value],
+ "selectable_by_every_program": False,
},
{
"code": "453100",
"title": "Математическое и программное обеспечение функционирования вычислительных комплексов ракетно-космической обороны",
"available_for": [Campus.MOSCOW.value],
+ "selectable_by_every_program": False,
},
{
"code": "461300",
diff --git a/back-end/src/common/serializers/milspecialties.py b/back-end/src/common/serializers/milspecialties.py
index e8723a19e..d2b2bbeff 100644
--- a/back-end/src/common/serializers/milspecialties.py
+++ b/back-end/src/common/serializers/milspecialties.py
@@ -6,4 +6,15 @@
class MilspecialtySerializer(serializers.ModelSerializer):
class Meta:
model = Milspecialty
- fields = "__all__"
+ exclude = ["selectable_by", "selectable_by_every_program"]
+
+
+class WithSelectableByProgramMilspecialtySerializer(MilspecialtySerializer):
+ selectable_by_program = serializers.SerializerMethodField()
+
+ def get_selectable_by_program(self, milspecialty: Milspecialty):
+ request = self.context["request"]
+ program = request.query_params.get("program")
+ if program is not None:
+ return milspecialty.is_selectable_by_program(program)
+ return False
diff --git a/back-end/src/common/tests/conftest.py b/back-end/src/common/tests/conftest.py
index eb3cc8a9e..36bc4891c 100644
--- a/back-end/src/common/tests/conftest.py
+++ b/back-end/src/common/tests/conftest.py
@@ -3,19 +3,27 @@
from auth.models import User
from common.models.milspecialties import Milspecialty
from common.models.subjects import Subject
+from common.models.universities import Program, Faculty
+from typing import Optional
@pytest.fixture
-def create_milspeciality():
- def call_me(title: str, code: str, available_for=None):
+def create_milspecialty():
+ def call_me(
+ title: str,
+ code: str,
+ available_for: Optional[list[str]] = None,
+ selectable_by_every_program: bool = True,
+ ):
if available_for is None:
available_for = ["MO"]
- milspeciality, _ = Milspecialty.objects.get_or_create(
+ milspecialty, _ = Milspecialty.objects.get_or_create(
title=title,
code=code,
available_for=available_for,
+ selectable_by_every_program=selectable_by_every_program,
)
- return milspeciality
+ return milspecialty
return call_me
@@ -71,3 +79,35 @@ def call_me(
return data
return call_me
+
+
+@pytest.fixture
+def create_faculty():
+ def call_me(title: str, abbreviation: str, campus: str = "MO"):
+ faculty, _ = Faculty.objects.get_or_create(
+ title=title,
+ abbreviation=abbreviation,
+ campus=campus,
+ )
+ return faculty
+
+ return call_me
+
+
+@pytest.fixture
+def create_program():
+ def call_me(
+ title: str,
+ code: str,
+ faculty: Faculty,
+ available_to_choose_for_applicants: bool = True,
+ ):
+ program, _ = Program.objects.get_or_create(
+ title=title,
+ code=code,
+ faculty=faculty,
+ available_to_choose_for_applicants=available_to_choose_for_applicants,
+ )
+ return program
+
+ return call_me
diff --git a/back-end/src/common/tests/test_milsepcialties_listing.py b/back-end/src/common/tests/test_milsepcialties_listing.py
new file mode 100644
index 000000000..d418c1270
--- /dev/null
+++ b/back-end/src/common/tests/test_milsepcialties_listing.py
@@ -0,0 +1,81 @@
+import pytest
+
+
+@pytest.mark.django_db
+def test_milspecialty_selectable(
+ su_client, create_faculty, create_milspecialty, create_program
+):
+ cs = create_faculty(
+ title="Факультет Компьютерных Наук", abbreviation="ФКН", campus="MO"
+ )
+ law = create_faculty(title="Юридический факультет", abbreviation="ЮФ", campus="SP")
+
+ ami = create_program(
+ title="Прикладная математика и информатика",
+ code="01.03.02 Прикладная математика и информатика",
+ faculty=cs,
+ )
+ economy = create_program(
+ title="Экономика",
+ code="38.03.01 Экономика",
+ faculty=cs,
+ )
+
+ # SPB campus
+ jurisprudence = create_program(
+ title="40.03.01 Юриспруденция",
+ code="40.03.01 Юриспруденция",
+ faculty=law,
+ )
+
+ officers = create_milspecialty(
+ title="Математическое и программное обеспечение комплексов ПРО",
+ code="453100",
+ selectable_by_every_program=False,
+ )
+
+ sergeants = create_milspecialty(
+ title="Стрелковые, командир стрелкового отделения",
+ code="100182",
+ selectable_by_every_program=True,
+ )
+
+ # Allow selection of "Математическое и программное обеспечение" by ami
+ officers.selectable_by.add(ami)
+
+ # Default case (no program specified) – do not show selectable_by_program flag
+ milspecialties_no_program = su_client.get(
+ f"/api/lms/milspecialties/?campus={cs.campus}"
+ ).json()
+ for milspecialty in milspecialties_no_program:
+ assert "selectable_by_program" not in milspecialty
+
+ milspecialties_for_economy = su_client.get(
+ f"/api/lms/milspecialties/?campus={cs.campus}&program={economy.pk}"
+ ).json()
+ assert len(milspecialties_for_economy) == 2
+ golden_selectable_by_economy = {
+ "453100": False, # "Математическое и программное обеспечение" is not selectable by every program, and economy can't select it
+ "100182": True,
+ }
+ for milspecialty in milspecialties_for_economy:
+ assert milspecialty["code"] in golden_selectable_by_economy
+ assert (
+ milspecialty["selectable_by_program"]
+ == golden_selectable_by_economy[milspecialty["code"]]
+ ), f"Incorrect selectable_by_program (law) for milspecialty \"{milspecialty['title']}\""
+
+ milspecialties_for_ami = su_client.get(
+ f"/api/lms/milspecialties/?campus={cs.campus}&program={ami.pk}"
+ ).json()
+ assert len(milspecialties_for_ami) == 2
+ golden_selectable_by_ami = {
+ "453100": True, # "Математическое и программное обеспечение" is not selectable by every program, BUT ami CAN select it
+ "100182": True,
+ }
+ for milspecialty in milspecialties_for_ami:
+ assert milspecialty["code"] in golden_selectable_by_ami
+ assert (
+ milspecialty["selectable_by_program"]
+ == golden_selectable_by_ami[milspecialty["code"]]
+ ), f"Incorrect selectable_by_program (ami) for milspecialty \"{milspecialty['title']}\""
diff --git a/back-end/src/common/tests/test_subjects.py b/back-end/src/common/tests/test_subjects.py
index 74d0a91c6..44a85624f 100644
--- a/back-end/src/common/tests/test_subjects.py
+++ b/back-end/src/common/tests/test_subjects.py
@@ -22,13 +22,13 @@ def test_trailing_slash_redirect(su_client):
@pytest.mark.django_db
def test_get_subject_by_id(
- su_client, create_milspeciality, get_new_subject_data, create_subject
+ su_client, create_milspecialty, get_new_subject_data, create_subject
):
- milspeciality = create_milspeciality(
+ milspecialty = create_milspecialty(
title="Математическое обеспечения комплексов ПРО", code="453100"
)
subj_data = get_new_subject_data(
- title="Строевая подготовка", milspecialty=milspeciality
+ title="Строевая подготовка", milspecialty=milspecialty
)
subject = create_subject(**subj_data)
subj_data = unpack_subject(subject)
@@ -41,13 +41,13 @@ def test_get_subject_by_id(
@pytest.mark.django_db
def test_get_subjects_by_title(
- su_client, create_milspeciality, get_new_subject_data, create_subject
+ su_client, create_milspecialty, get_new_subject_data, create_subject
):
- milspeciality = create_milspeciality(
+ milspecialty = create_milspecialty(
title="Математическое обеспечения комплексов ПРО", code="453100"
)
subj_data = get_new_subject_data(
- title="Тактическая подготовка", milspecialty=milspeciality
+ title="Тактическая подготовка", milspecialty=milspecialty
)
subject = create_subject(**subj_data)
subj_data = unpack_subject(subject)
@@ -64,13 +64,13 @@ def test_get_subjects_by_title(
@pytest.mark.django_db
def test_get_subjects_by_search(
- su_client, create_milspeciality, get_new_subject_data, create_subject
+ su_client, create_milspecialty, get_new_subject_data, create_subject
):
- milspeciality = create_milspeciality(
+ milspecialty = create_milspecialty(
title="Математическое обеспечения комплексов ПРО", code="453100"
)
subj_data = get_new_subject_data(
- title="Тактико-специальная подготовка 2", milspecialty=milspeciality
+ title="Тактико-специальная подготовка 2", milspecialty=milspecialty
)
subject = create_subject(**subj_data)
word = subject.title.split()[1]
diff --git a/back-end/src/dms/tests/test_end_to_end_numbering.py b/back-end/src/dms/tests/test_end_to_end_numbering.py
index 5806e8eff..c627e332a 100644
--- a/back-end/src/dms/tests/test_end_to_end_numbering.py
+++ b/back-end/src/dms/tests/test_end_to_end_numbering.py
@@ -123,7 +123,6 @@ def test_sections_reordering(
topic8,
topic9,
):
-
check_order([section1, section2, section3, section4])
check_order([topic1, topic2, topic3, topic4, topic5, topic6, topic7])
check_order([section5, section6])
diff --git a/back-end/src/lms/models/students.py b/back-end/src/lms/models/students.py
index b36105afe..7c7642b65 100644
--- a/back-end/src/lms/models/students.py
+++ b/back-end/src/lms/models/students.py
@@ -208,6 +208,13 @@ def student_post_callback(sender, instance: Student, *args, **kwargs):
@receiver(models.signals.post_delete, sender=Student)
def post_delete_fields(sender, instance: Student, **kwargs):
+ # Если у пользователя есть модель абитуриента, удаляем и её
+ if instance.user:
+ try:
+ applicant = Applicant.objects.get(user=instance.user)
+ applicant.delete()
+ except Applicant.DoesNotExist:
+ pass
# pylint: disable=unused-argument
attributes_to_delete = [
"user",
diff --git a/back-end/src/lms/views/reference_book.py b/back-end/src/lms/views/reference_book.py
index 3baeb13ad..d1e0be111 100644
--- a/back-end/src/lms/views/reference_book.py
+++ b/back-end/src/lms/views/reference_book.py
@@ -2,6 +2,9 @@
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from rest_framework.views import APIView
+from rest_framework.exceptions import ValidationError
+from rest_framework import status
+from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
@@ -12,7 +15,10 @@
from common.models.milspecialties import Milspecialty
from common.models.universities import Program
-from common.serializers.milspecialties import MilspecialtySerializer
+from common.serializers.milspecialties import (
+ MilspecialtySerializer,
+ WithSelectableByProgramMilspecialtySerializer,
+)
from common.serializers.universities import ProgramSerializer
from common.filters.milspecialties import MilspecialtyFilter
@@ -106,7 +112,11 @@ class MilfacultyViewSet(ModelViewSet):
@extend_schema(tags=["reference-book"])
class MilspecialtyViewSet(ModelViewSet):
- serializer_class = MilspecialtySerializer
+ def get_serializer_class(self):
+ if "program" in self.request.query_params:
+ return WithSelectableByProgramMilspecialtySerializer
+ return MilspecialtySerializer
+
queryset = Milspecialty.objects.all()
permission_classes = [ReadOnly | ReferenceBookPermission]
@@ -114,6 +124,30 @@ class MilspecialtyViewSet(ModelViewSet):
filter_backends = [DjangoFilterBackend]
filterset_class = MilspecialtyFilter
+ @extend_schema(
+ parameters=[
+ OpenApiParameter(
+ name="program",
+ type=int,
+ location=OpenApiParameter.QUERY,
+ description="Show if selectable by program",
+ ),
+ ]
+ )
+ def list(self, request, *args, **kwargs):
+ program = request.query_params.get("program")
+ if program is not None:
+ try:
+ # Attempt to convert the program to an integer
+ int(program)
+ except ValueError:
+ # If conversion fails, raise a validation error
+ raise ValidationError(
+ {"program": "Program must be a valid integer."},
+ code=status.HTTP_400_BAD_REQUEST,
+ )
+ return super().list(request, *args, **kwargs)
+
@extend_schema(tags=["reference-book"])
class MilgroupViewSet(QuerySetScopingMixin, ModelViewSet):
diff --git a/back-end/src/lms/views/students.py b/back-end/src/lms/views/students.py
index b5ec2eebf..c45769032 100644
--- a/back-end/src/lms/views/students.py
+++ b/back-end/src/lms/views/students.py
@@ -22,7 +22,7 @@
from common.views.choices import GenericChoicesList
-from auth.models import Permission
+from auth.models import Group, Permission
from auth.permissions import BasePermission
from auth.tokens.registration import generate_regconf_token
@@ -50,6 +50,8 @@
from lms.types.personnel import Personnel
+from common.models.personal import ContactInfo, PersonalDocumentsInfo
+
class StudentPermission(BasePermission):
permission_class = "students"
@@ -107,6 +109,7 @@ def get_serializer_class(self):
mutate_actions = MUTATE_ACTIONS + [
"registration",
"registration_for_existing_students",
+ "register_from_applicant",
]
if self.action in mutate_actions:
return StudentMutateSerializer
@@ -157,6 +160,48 @@ def registration(self, request):
serializer = self.get_serializer(instance)
return Response(serializer.data)
+ @action(
+ detail=False, methods=["post"], permission_classes=[permissions.IsAdminUser]
+ )
+ def register_from_applicant(self, request):
+ request.data[
+ "status"
+ ] = "ST" # Выставляем входной структуре статус обучающегося
+
+ # Если студент регистрировался в 2022 или 2023 году, у него нет ИНН и СНИЛСа в системе,
+ # нужно заполнить
+ if request.data.get("personal_documents_info") is None:
+ request.data["personal_documents_info"] = {
+ "tax_id": "-",
+ "insurance_number": "-",
+ }
+
+ serializer = self.get_serializer_class()(
+ data=request.data
+ ) # TODO: write new serializer
+
+ serializer.is_valid(raise_exception=True)
+
+ corporate_email = serializer.validated_data["contact_info"]["corporate_email"]
+ user = serializer.validated_data["user"]
+
+ contact_info = ContactInfo.objects.get(corporate_email=corporate_email)
+
+ student = serializer.save()
+
+ student.user = user
+ student.contact_info = contact_info
+
+ student.save()
+
+ students, _ = Group.objects.get_or_create(name="Студент")
+ students.user_set.add(user)
+
+ applicants, _ = Group.objects.get_or_create(name="Абитуриент")
+ applicants.user_set.remove(user)
+
+ return Response(self.get_serializer(student).data)
+
@registration.mapping.post
def registration_for_existing_students(self, request):
request.data["status"] = "ST"
diff --git a/front-end/src/api/reference-book.js b/front-end/src/api/reference-book.js
index 871e4272a..a0dca9a0b 100644
--- a/front-end/src/api/reference-book.js
+++ b/front-end/src/api/reference-book.js
@@ -31,6 +31,14 @@ export function getMilSpecialties(campus) {
});
}
+export function getMilSpecialtiesSelectableByProgram(campus, program) {
+ return request({
+ url: BASE_API_URL + milspecialties,
+ method: "get",
+ params: { campus, program },
+ });
+}
+
export function editMilSpecialty(id, data) {
return request({
url: `${BASE_API_URL}${milspecialties}${id}/`,
diff --git a/front-end/src/api/user.js b/front-end/src/api/user.js
index 85c6b948b..8ff6403be 100644
--- a/front-end/src/api/user.js
+++ b/front-end/src/api/user.js
@@ -32,6 +32,14 @@ export function registerStudent(data) {
});
}
+export function registerStudentFromApplicant(data) {
+ return request({
+ url: BASE_API_URL + LMS_URLS.register.studentFromApplicant,
+ method: "post",
+ data,
+ });
+}
+
export function registerTeacher(data) {
return request({
url: BASE_API_URL + LMS_URLS.register.teachers,
diff --git a/front-end/src/common/inputs/Select.vue b/front-end/src/common/inputs/Select.vue
index 0211ada9c..38cd2dcbe 100644
--- a/front-end/src/common/inputs/Select.vue
+++ b/front-end/src/common/inputs/Select.vue
@@ -14,10 +14,11 @@
v-bind="$attrs"
>
@@ -55,6 +56,7 @@ class SelectInput extends InputsMixin {
return {
label: _isObject(rawLabel) ? JSON.stringify(rawLabel) : rawLabel,
optionValue: getValue(option),
+ class_: option.class,
};
});
}
diff --git a/front-end/src/components/@ApplicantsDocuments/Table.vue b/front-end/src/components/@ApplicantsDocuments/Table.vue
index 5f7f4373c..78fef3668 100644
--- a/front-end/src/components/@ApplicantsDocuments/Table.vue
+++ b/front-end/src/components/@ApplicantsDocuments/Table.vue
@@ -1,5 +1,6 @@
@@ -42,7 +43,13 @@
v-if="field === 'index'"
#body="{ index }"
>
- {{ startIndex + index + 1 }}
+
+ {{ startIndex + index + 1 }}
+
diff --git a/front-end/src/views/ApplicantsDocuments/index.vue b/front-end/src/views/ApplicantsDocuments/index.vue
index 2dfc301a8..c85ac965e 100644
--- a/front-end/src/views/ApplicantsDocuments/index.vue
+++ b/front-end/src/views/ApplicantsDocuments/index.vue
@@ -181,7 +181,7 @@ export default {
selectedCampus,
selectedProgram: this.$route.query.program,
selectedExportOption: APPLICATIONS_EXPORT_OPTIONS.DEF,
- currentYear: date.getMonth() >= 9 ? date.getFullYear() + 1 : date.getFullYear(),
+ currentYear: date.getMonth() >= 8 ? date.getFullYear() + 1 : date.getFullYear(),
};
},
computed: {
@@ -195,7 +195,8 @@ export default {
admissionYears() {
const possibleYears = [];
const date = new Date();
- const maxYear = date.getMonth() >= 9 ? date.getFullYear() + 1 : date.getFullYear();
+ // Enumeration is from 0. Next admission year is from the Sept, 1st
+ const maxYear = date.getMonth() >= 8 ? date.getFullYear() + 1 : date.getFullYear();
for (let year = 2022; year <= maxYear; year += 1) {
possibleYears.push(year);
}
@@ -267,6 +268,8 @@ export default {
this.data = data.results.map(item => ({
birthday: moment(item.birth_date).format("DD.MM.yyyy"),
faculty: item.faculty,
+ phone: item.phone,
+ email: item.email,
fullname: item.fullname,
id: item.id,
passport: item.passport,
diff --git a/tgbot/Dockerfile b/tgbot/Dockerfile
index 120eba2d3..40970fac8 100644
--- a/tgbot/Dockerfile
+++ b/tgbot/Dockerfile
@@ -1,5 +1,5 @@
# Base Python image
-FROM python:3.10-buster
+FROM python:3.10-bullseye
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
diff --git a/watchdoc/.env.example b/watchdoc/.env.example
index e561e144b..43580838f 100644
--- a/watchdoc/.env.example
+++ b/watchdoc/.env.example
@@ -5,3 +5,5 @@ EMAIL_PORT=1118
EMAIL_USE_TLS=true
EMAIL_HOST_USER=user@example.com
EMAIL_HOST_PASSWORD=password
+
+YADISK_TOKEN=yadisk_token
\ No newline at end of file
diff --git a/watchdoc/Dockerfile b/watchdoc/Dockerfile
index 5f1eea537..da38dd187 100644
--- a/watchdoc/Dockerfile
+++ b/watchdoc/Dockerfile
@@ -1,5 +1,5 @@
# Base Python image
-FROM python:3.9.1-buster
+FROM python:3.9.20-bullseye
# Set working directory
WORKDIR /watchdoc
diff --git a/watchdoc/Pipfile b/watchdoc/Pipfile
index 8e1a59caf..07b8b7710 100644
--- a/watchdoc/Pipfile
+++ b/watchdoc/Pipfile
@@ -12,6 +12,7 @@ google-auth-oauthlib = "*"
jinja2 = "*"
uvicorn = {extras = ["standard"], version = "*"}
pydantic = "*"
+yadisk = "*"
[dev-packages]
diff --git a/watchdoc/Pipfile.lock b/watchdoc/Pipfile.lock
index 63133f251..c17526274 100644
--- a/watchdoc/Pipfile.lock
+++ b/watchdoc/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "a9847ba2605b6ba9a408dcf24635bea118eb4ec432388eda28f81f1bf825e7a5"
+ "sha256": "1ea25b6db2d05bd5a4bca6361c35467cff186770322095c1b8323f76d9a0456d"
},
"pipfile-spec": 6,
"requires": {
@@ -16,13 +16,29 @@
]
},
"default": {
+ "anyio": {
+ "hashes": [
+ "sha256:a0aeffe2fb1fdf374a8e4b471444f0f3ac4fb9f5a5b542b48824475e0042a5a6",
+ "sha256:b5fa16c5ff93fa1046f2eeb5bbff2dad4d3514d6cda61d02816dba34fa8c3c2e"
+ ],
+ "markers": "python_full_version >= '3.6.2'",
+ "version": "==3.5.0"
+ },
+ "asgiref": {
+ "hashes": [
+ "sha256:2f8abc20f7248433085eda803936d98992f1343ddb022065779f37c5da0181d0",
+ "sha256:88d59c13d634dcffe0510be048210188edd79aeccb6a6c9028cdad6f31d730a9"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==3.5.0"
+ },
"cachetools": {
"hashes": [
- "sha256:89ea6f1b638d5a73a4f9226be57ac5e4f399d22770b92355f92dcb0f7f001693",
- "sha256:92971d3cb7d2a97efff7c7bb1657f21a8f5fb309a37530537c71b1774189f2d1"
+ "sha256:486471dfa8799eb7ec503a8059e263db000cdda20075ce5e48903087f79d5fd6",
+ "sha256:8fecd4203a38af17928be7b90689d8083603073622229ca7077b72d8e5a976e4"
],
- "markers": "python_version ~= '3.5'",
- "version": "==4.2.4"
+ "markers": "python_version ~= '3.7'",
+ "version": "==5.0.0"
},
"certifi": {
"hashes": [
@@ -33,59 +49,65 @@
},
"charset-normalizer": {
"hashes": [
- "sha256:1eecaa09422db5be9e29d7fc65664e6c33bd06f9ced7838578ba40d58bdf3721",
- "sha256:b0b883e8e874edfdece9c28f314e3dd5badf067342e42fb162203335ae61aa2c"
+ "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597",
+ "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"
],
"markers": "python_version >= '3'",
- "version": "==2.0.9"
+ "version": "==2.0.12"
},
"click": {
"hashes": [
- "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
- "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
+ "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1",
+ "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
- "version": "==7.1.2"
+ "markers": "python_version >= '3.6'",
+ "version": "==8.0.4"
+ },
+ "docxcompose": {
+ "hashes": [
+ "sha256:b1a40d4f95751565822350174971012875f82a32b83a9d1be098edd965047029"
+ ],
+ "version": "==1.3.4"
},
"docxtpl": {
"hashes": [
- "sha256:18b81c072254b2eef3cbf878951a93515f4d287c319856d3dd7ada53a3ffd5ad",
- "sha256:c26ad7a0e6e9aedc7dfaf6b09af60ee0a4f001a065896503056ce9171bd9024a"
+ "sha256:d6f7a0d383bcb4e779b9c6abaef29f1af639c038e767fa53a9cb3d628bc0512e",
+ "sha256:de0988b2a7a92402d6da3aa9dec474cf3cfb941ed69927248a8e97d0ff50d6f9"
],
"index": "pypi",
- "version": "==0.11.3"
+ "version": "==0.15.2"
},
"fastapi": {
"hashes": [
- "sha256:39569a18914075b2f1aaa03bcb9dc96a38e0e5dabaf3972e088c9077dfffa379",
- "sha256:8359e55d8412a5571c0736013d90af235d6949ec4ce978e9b63500c8f4b6f714"
+ "sha256:124774ce4cb3322841965f559669b233a0b8d343ea24fdd8b293253c077220d7",
+ "sha256:43d12891b78fc497a50623e9c7c24640c569489f060acd9ce2c4902080487a93"
],
"index": "pypi",
- "version": "==0.65.2"
+ "version": "==0.75.0"
},
"google-api-core": {
"hashes": [
- "sha256:c77ffc8b4981b44efdb9d68431fd96d21dbd39545c29552bbe79b9b7dd2c3689",
- "sha256:ed59c6a695a81f601e4ba0f37ca9dbde3c43b3309e161a1a8946f266db4a0c4e"
+ "sha256:6be1fc59e2a7ba9f66808bbc22f976f81e4c3e7ab20fa0620ce42686288787d0",
+ "sha256:b0fa577e512f0c8e063386b974718b8614586a798c5894ed34bedf256d9dae24"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
- "version": "==1.31.4"
+ "markers": "python_version >= '3.6'",
+ "version": "==2.7.1"
},
"google-api-python-client": {
"hashes": [
- "sha256:48686cceb0dc8cb8b9ee1920ad7c0d9b499ef4fca0ca51c1c69f1e462a628011",
- "sha256:b665afbb38df357222bac695e76f2ceb3c42346bfbfb64146061285e175c38d8"
+ "sha256:080d4be6c66aa95fb530916844cb2601645daf5e5a1ce8d2bc57b8c72f1c7cf6",
+ "sha256:bf969a84cf882958999cf6de35928bf5ae3957637d5842060798e5d39b0ba1e5"
],
"index": "pypi",
- "version": "==2.0.2"
+ "version": "==2.40.0"
},
"google-auth": {
"hashes": [
- "sha256:997516b42ecb5b63e8d80f5632c1a61dddf41d2a4c2748057837e06e00014258",
- "sha256:b7033be9028c188ee30200b204ea00ed82ea1162e8ac1df4aa6ded19a191d88e"
+ "sha256:218ca03d7744ca0c8b6697b6083334be7df49b7bf76a69d555962fd1a7657b5f",
+ "sha256:ad160fc1ea8f19e331a16a14a79f3d643d813a69534ba9611d2c80dc10439dad"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
- "version": "==1.35.0"
+ "version": "==2.6.0"
},
"google-auth-httplib2": {
"hashes": [
@@ -97,258 +119,243 @@
},
"google-auth-oauthlib": {
"hashes": [
- "sha256:54431535309cfab50897d9c181e8c2226268825aa6e42e930b05b99c5041a18c",
- "sha256:dabffbf594a6be2fd6d054060846d1201569252efb10dfb749b504a7591f8af0"
+ "sha256:06c4ceb3ab2a93b85b8976bbe86cbb82ae1d1c02d2ded3cfd0847a8b6955263b",
+ "sha256:8b7ff4d2fe81e3bd034306aa665444360b3c67195b9dea582dddc7dfb8d89d34"
],
"index": "pypi",
- "version": "==0.4.3"
+ "version": "==0.5.0"
},
"googleapis-common-protos": {
"hashes": [
- "sha256:a4031d6ec6c2b1b6dc3e0be7e10a1bd72fb0b18b07ef9be7b51f2c1004ce2437",
- "sha256:e54345a2add15dc5e1a7891c27731ff347b4c33765d79b5ed7026a6c0c7cbcae"
+ "sha256:183bb0356bd614c4330ad5158bc1c1bcf9bcf7f5e7f911317559fe209496eeee",
+ "sha256:53eb313064738f45d5ac634155ae208e121c963659627b90dfcb61ef514c03e1"
],
"markers": "python_version >= '3.6'",
- "version": "==1.54.0"
+ "version": "==1.55.0"
},
"h11": {
"hashes": [
- "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6",
- "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"
+ "sha256:70813c1135087a248a4d38cc0e1a0181ffab2188141a93eaf567940c3957ff06",
+ "sha256:8ddd78563b633ca55346c8cd41ec0af27d3c79931828beffb46ce70a379e7442"
],
"markers": "python_version >= '3.6'",
- "version": "==0.12.0"
+ "version": "==0.13.0"
},
"httplib2": {
"hashes": [
- "sha256:6b937120e7d786482881b44b8eec230c1ee1c5c1d06bce8cc865f25abbbf713b",
- "sha256:e404681d2fbcec7506bcb52c503f2b021e95bee0ef7d01e5c221468a2406d8dc"
+ "sha256:58a98e45b4b1a48273073f905d2961666ecf0fbac4250ea5b47aef259eb5c585",
+ "sha256:8b6a905cb1c79eefd03f8669fd993c36dc341f7c558f056cb5a33b5c2f458543"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==0.20.2"
+ "version": "==0.20.4"
},
"httptools": {
"hashes": [
- "sha256:07659649fe6b3948b6490825f89abe5eb1cec79ebfaaa0b4bf30f3f33f3c2ba8",
- "sha256:08b79e09114e6ab5c3dbf560bba2cb2257ea38cdaeaf99b7cb80d8f92622fcd9",
- "sha256:1e35aa179b67086cc600a984924a88589b90793c9c1b260152ca4908786e09df",
- "sha256:31629e1f1b89959f8c0927bad12184dc07977dcf71e24f4772934aa490aa199b",
- "sha256:851026bd63ec0af7e7592890d97d15c92b62d9e17094353f19a52c8e2b33710a",
- "sha256:8fcca4b7efe353b13a24017211334c57d055a6e132c7adffed13a10d28efca57",
- "sha256:9abd788465aa46a0f288bd3a99e53edd184177d6379e2098fd6097bb359ad9d6",
- "sha256:aebdf0bd7bf7c90ae6b3be458692bf6e9e5b610b501f9f74c7979015a51db4c4",
- "sha256:bda99a5723e7eab355ce57435c70853fc137a65aebf2f1cd4d15d96e2956da7b",
- "sha256:c1c63d860749841024951b0a78e4dec6f543d23751ef061d6ab60064c7b8b524",
- "sha256:c4111a0a8a00eff1e495d43ea5230aaf64968a48ddba8ea2d5f982efae827404",
- "sha256:dce59ee45dd6ee6c434346a5ac527c44014326f560866b4b2f414a692ee1aca8",
- "sha256:f759717ca1b2ef498c67ba4169c2b33eecf943a89f5329abcff8b89d153eb500",
- "sha256:fb7199b8fb0c50a22e77260bb59017e0c075fa80cb03bb2c8692de76e7bb7fe7",
- "sha256:fbf7ecd31c39728f251b1c095fd27c84e4d21f60a1d079a0333472ff3ae59d34"
- ],
- "version": "==0.1.2"
+ "sha256:1a99346ebcb801b213c591540837340bdf6fd060a8687518d01c607d338b7424",
+ "sha256:1ee0b459257e222b878a6c09ccf233957d3a4dcb883b0847640af98d2d9aac23",
+ "sha256:20a45bcf22452a10fa8d58b7dbdb474381f6946bf5b8933e3662d572bc61bae4",
+ "sha256:29bf97a5c532da9c7a04de2c7a9c31d1d54f3abd65a464119b680206bbbb1055",
+ "sha256:2c9a930c378b3d15d6b695fb95ebcff81a7395b4f9775c4f10a076beb0b2c1ff",
+ "sha256:2db44a0b294d317199e9f80123e72c6b005c55b625b57fae36de68670090fa48",
+ "sha256:3194f6d6443befa8d4db16c1946b2fc428a3ceb8ab32eb6f09a59f86104dc1a0",
+ "sha256:34d2903dd2a3dd85d33705b6fde40bf91fc44411661283763fd0746723963c83",
+ "sha256:48e48530d9b995a84d1d89ae6b3ec4e59ea7d494b150ac3bbc5e2ac4acce92cd",
+ "sha256:54bbd295f031b866b9799dd39cb45deee81aca036c9bff9f58ca06726f6494f1",
+ "sha256:5d1fe6b6661022fd6cac541f54a4237496b246e6f1c0a6b41998ee08a1135afe",
+ "sha256:645373c070080e632480a3d251d892cb795be3d3a15f86975d0f1aca56fd230d",
+ "sha256:6a1a7dfc1f9c78a833e2c4904757a0f47ce25d08634dd2a52af394eefe5f9777",
+ "sha256:701e66b59dd21a32a274771238025d58db7e2b6ecebbab64ceff51b8e31527ae",
+ "sha256:72aa3fbe636b16d22e04b5a9d24711b043495e0ecfe58080addf23a1a37f3409",
+ "sha256:7af6bdbd21a2a25d6784f6d67f44f5df33ef39b6159543b9f9064d365c01f919",
+ "sha256:7ee9f226acab9085037582c059d66769862706e8e8cd2340470ceb8b3850873d",
+ "sha256:7f7bfb74718f52d5ed47d608d507bf66d3bc01d4a8b3e6dd7134daaae129357b",
+ "sha256:8e2eb957787cbb614a0f006bfc5798ff1d90ac7c4dd24854c84edbdc8c02369e",
+ "sha256:903f739c9fb78dab8970b0f3ea51f21955b24b45afa77b22ff0e172fc11ef111",
+ "sha256:98993805f1e3cdb53de4eed02b55dcc953cdf017ba7bbb2fd89226c086a6d855",
+ "sha256:9967d9758df505975913304c434cb9ab21e2c609ad859eb921f2f615a038c8de",
+ "sha256:a113789e53ac1fa26edf99856a61e4c493868e125ae0dd6354cf518948fbbd5c",
+ "sha256:a522d12e2ddbc2e91842ffb454a1aeb0d47607972c7d8fc88bd0838d97fb8a2a",
+ "sha256:abe829275cdd4174b4c4e65ad718715d449e308d59793bf3a931ee1bf7e7b86c",
+ "sha256:c286985b5e194ca0ebb2908d71464b9be8f17cc66d6d3e330e8d5407248f56ad",
+ "sha256:cd1295f52971097f757edfbfce827b6dbbfb0f7a74901ee7d4933dff5ad4c9af",
+ "sha256:ceafd5e960b39c7e0d160a1936b68eb87c5e79b3979d66e774f0c77d4d8faaed",
+ "sha256:d1f27bb0f75bef722d6e22dc609612bfa2f994541621cd2163f8c943b6463dfe",
+ "sha256:d3a4e165ca6204f34856b765d515d558dc84f1352033b8721e8d06c3e44930c3",
+ "sha256:d9b90bf58f3ba04e60321a23a8723a1ff2a9377502535e70495e5ada8e6e6722",
+ "sha256:f72b5d24d6730035128b238decdc4c0f2104b7056a7ca55cf047c106842ec890",
+ "sha256:fcddfe70553be717d9745990dfdb194e22ee0f60eb8f48c0794e7bfeda30d2d5",
+ "sha256:fdb9f9ed79bc6f46b021b3319184699ba1a22410a82204e6e89c774530069683"
+ ],
+ "version": "==0.4.0"
},
"idna": {
"hashes": [
"sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
"sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
],
- "markers": "python_version >= '3'",
+ "markers": "python_version >= '3.5'",
"version": "==3.3"
},
"jinja2": {
"hashes": [
- "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419",
- "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"
+ "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8",
+ "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"
],
"index": "pypi",
- "version": "==2.11.3"
+ "version": "==3.0.3"
},
"lxml": {
"hashes": [
- "sha256:11ae552a78612620afd15625be9f1b82e3cc2e634f90d6b11709b10a100cba59",
- "sha256:121fc6f71c692b49af6c963b84ab7084402624ffbe605287da362f8af0668ea3",
- "sha256:124f09614f999551ac65e5b9875981ce4b66ac4b8e2ba9284572f741935df3d9",
- "sha256:12ae2339d32a2b15010972e1e2467345b7bf962e155671239fba74c229564b7f",
- "sha256:12d8d6fe3ddef629ac1349fa89a638b296a34b6529573f5055d1cb4e5245f73b",
- "sha256:1a2a7659b8eb93c6daee350a0d844994d49245a0f6c05c747f619386fb90ba04",
- "sha256:1ccbfe5d17835db906f2bab6f15b34194db1a5b07929cba3cf45a96dbfbfefc0",
- "sha256:2f77556266a8fe5428b8759fbfc4bd70be1d1d9c9b25d2a414f6a0c0b0f09120",
- "sha256:3534d7c468c044f6aef3c0aff541db2826986a29ea73f2ca831f5d5284d9b570",
- "sha256:3884476a90d415be79adfa4e0e393048630d0d5bcd5757c4c07d8b4b00a1096b",
- "sha256:3b95fb7e6f9c2f53db88f4642231fc2b8907d854e614710996a96f1f32018d5c",
- "sha256:46515773570a33eae13e451c8fcf440222ef24bd3b26f40774dd0bd8b6db15b2",
- "sha256:46f21f2600d001af10e847df9eb3b832e8a439f696c04891bcb8a8cedd859af9",
- "sha256:473701599665d874919d05bb33b56180447b3a9da8d52d6d9799f381ce23f95c",
- "sha256:4b9390bf973e3907d967b75be199cf1978ca8443183cf1e78ad80ad8be9cf242",
- "sha256:4f415624cf8b065796649a5e4621773dc5c9ea574a944c76a7f8a6d3d2906b41",
- "sha256:534032a5ceb34bba1da193b7d386ac575127cc39338379f39a164b10d97ade89",
- "sha256:558485218ee06458643b929765ac1eb04519ca3d1e2dcc288517de864c747c33",
- "sha256:57cf05466917e08f90e323f025b96f493f92c0344694f5702579ab4b7e2eb10d",
- "sha256:59d77bfa3bea13caee95bc0d3f1c518b15049b97dd61ea8b3d71ce677a67f808",
- "sha256:5d5254c815c186744c8f922e2ce861a2bdeabc06520b4b30b2f7d9767791ce6e",
- "sha256:5ea121cb66d7e5cb396b4c3ca90471252b94e01809805cfe3e4e44be2db3a99c",
- "sha256:60aeb14ff9022d2687ef98ce55f6342944c40d00916452bb90899a191802137a",
- "sha256:642eb4cabd997c9b949a994f9643cd8ae00cf4ca8c5cd9c273962296fadf1c44",
- "sha256:6548fc551de15f310dd0564751d9dc3d405278d45ea9b2b369ed1eccf142e1f5",
- "sha256:68a851176c931e2b3de6214347b767451243eeed3bea34c172127bbb5bf6c210",
- "sha256:6e84edecc3a82f90d44ddee2ee2a2630d4994b8471816e226d2b771cda7ac4ca",
- "sha256:73e8614258404b2689a26cb5d002512b8bc4dfa18aca86382f68f959aee9b0c8",
- "sha256:7679bb6e4d9a3978a46ab19a3560e8d2b7265ef3c88152e7fdc130d649789887",
- "sha256:76b6c296e4f7a1a8a128aec42d128646897f9ae9a700ef6839cdc9b3900db9b5",
- "sha256:7f00cc64b49d2ef19ddae898a3def9dd8fda9c3d27c8a174c2889ee757918e71",
- "sha256:8021eeff7fabde21b9858ed058a8250ad230cede91764d598c2466b0ba70db8b",
- "sha256:87f8f7df70b90fbe7b49969f07b347e3f978f8bd1046bb8ecae659921869202b",
- "sha256:916d457ad84e05b7db52700bad0a15c56e0c3000dcaf1263b2fb7a56fe148996",
- "sha256:925174cafb0f1179a7fd38da90302555d7445e34c9ece68019e53c946be7f542",
- "sha256:9801bcd52ac9c795a7d81ea67471a42cffe532e46cfb750cd5713befc5c019c0",
- "sha256:99cf827f5a783038eb313beee6533dddb8bdb086d7269c5c144c1c952d142ace",
- "sha256:a21b78af7e2e13bec6bea12fc33bc05730197674f3e5402ce214d07026ccfebd",
- "sha256:a52e8f317336a44836475e9c802f51c2dc38d612eaa76532cb1d17690338b63b",
- "sha256:a702005e447d712375433ed0499cb6e1503fadd6c96a47f51d707b4d37b76d3c",
- "sha256:a708c291900c40a7ecf23f1d2384ed0bc0604e24094dd13417c7e7f8f7a50d93",
- "sha256:a7790a273225b0c46e5f859c1327f0f659896cc72eaa537d23aa3ad9ff2a1cc1",
- "sha256:abcf7daa5ebcc89328326254f6dd6d566adb483d4d00178892afd386ab389de2",
- "sha256:add017c5bd6b9ec3a5f09248396b6ee2ce61c5621f087eb2269c813cd8813808",
- "sha256:af4139172ff0263d269abdcc641e944c9de4b5d660894a3ec7e9f9db63b56ac9",
- "sha256:b4015baed99d046c760f09a4c59d234d8f398a454380c3cf0b859aba97136090",
- "sha256:ba0006799f21d83c3717fe20e2707a10bbc296475155aadf4f5850f6659b96b9",
- "sha256:bdb98f4c9e8a1735efddfaa995b0c96559792da15d56b76428bdfc29f77c4cdb",
- "sha256:c34234a1bc9e466c104372af74d11a9f98338a3f72fae22b80485171a64e0144",
- "sha256:c580c2a61d8297a6e47f4d01f066517dbb019be98032880d19ece7f337a9401d",
- "sha256:ca9a40497f7e97a2a961c04fa8a6f23d790b0521350a8b455759d786b0bcb203",
- "sha256:cab343b265e38d4e00649cbbad9278b734c5715f9bcbb72c85a1f99b1a58e19a",
- "sha256:ce52aad32ec6e46d1a91ff8b8014a91538800dd533914bfc4a82f5018d971408",
- "sha256:da07c7e7fc9a3f40446b78c54dbba8bfd5c9100dfecb21b65bfe3f57844f5e71",
- "sha256:dc8a0dbb2a10ae8bb609584f5c504789f0f3d0d81840da4849102ec84289f952",
- "sha256:e5b4b0d9440046ead3bd425eb2b852499241ee0cef1ae151038e4f87ede888c4",
- "sha256:f33d8efb42e4fc2b31b3b4527940b25cdebb3026fb56a80c1c1c11a4271d2352",
- "sha256:f6befb83bca720b71d6bd6326a3b26e9496ae6649e26585de024890fe50f49b8",
- "sha256:fcc849b28f584ed1dbf277291ded5c32bb3476a37032df4a1d523b55faa5f944",
- "sha256:ff44de36772b05c2eb74f2b4b6d1ae29b8f41ed5506310ce1258d44826ee38c1"
+ "sha256:078306d19a33920004addeb5f4630781aaeabb6a8d01398045fcde085091a169",
+ "sha256:0c1978ff1fd81ed9dcbba4f91cf09faf1f8082c9d72eb122e92294716c605428",
+ "sha256:1010042bfcac2b2dc6098260a2ed022968dbdfaf285fc65a3acf8e4eb1ffd1bc",
+ "sha256:1d650812b52d98679ed6c6b3b55cbb8fe5a5460a0aef29aeb08dc0b44577df85",
+ "sha256:20b8a746a026017acf07da39fdb10aa80ad9877046c9182442bf80c84a1c4696",
+ "sha256:2403a6d6fb61c285969b71f4a3527873fe93fd0abe0832d858a17fe68c8fa507",
+ "sha256:24f5c5ae618395ed871b3d8ebfcbb36e3f1091fd847bf54c4de623f9107942f3",
+ "sha256:28d1af847786f68bec57961f31221125c29d6f52d9187c01cd34dc14e2b29430",
+ "sha256:31499847fc5f73ee17dbe1b8e24c6dafc4e8d5b48803d17d22988976b0171f03",
+ "sha256:31ba2cbc64516dcdd6c24418daa7abff989ddf3ba6d3ea6f6ce6f2ed6e754ec9",
+ "sha256:330bff92c26d4aee79c5bc4d9967858bdbe73fdbdbacb5daf623a03a914fe05b",
+ "sha256:5045ee1ccd45a89c4daec1160217d363fcd23811e26734688007c26f28c9e9e7",
+ "sha256:52cbf2ff155b19dc4d4100f7442f6a697938bf4493f8d3b0c51d45568d5666b5",
+ "sha256:530f278849031b0eb12f46cca0e5db01cfe5177ab13bd6878c6e739319bae654",
+ "sha256:545bd39c9481f2e3f2727c78c169425efbfb3fbba6e7db4f46a80ebb249819ca",
+ "sha256:5804e04feb4e61babf3911c2a974a5b86f66ee227cc5006230b00ac6d285b3a9",
+ "sha256:5a58d0b12f5053e270510bf12f753a76aaf3d74c453c00942ed7d2c804ca845c",
+ "sha256:5f148b0c6133fb928503cfcdfdba395010f997aa44bcf6474fcdd0c5398d9b63",
+ "sha256:5f7d7d9afc7b293147e2d506a4596641d60181a35279ef3aa5778d0d9d9123fe",
+ "sha256:60d2f60bd5a2a979df28ab309352cdcf8181bda0cca4529769a945f09aba06f9",
+ "sha256:6259b511b0f2527e6d55ad87acc1c07b3cbffc3d5e050d7e7bcfa151b8202df9",
+ "sha256:6268e27873a3d191849204d00d03f65c0e343b3bcb518a6eaae05677c95621d1",
+ "sha256:627e79894770783c129cc5e89b947e52aa26e8e0557c7e205368a809da4b7939",
+ "sha256:62f93eac69ec0f4be98d1b96f4d6b964855b8255c345c17ff12c20b93f247b68",
+ "sha256:6d6483b1229470e1d8835e52e0ff3c6973b9b97b24cd1c116dca90b57a2cc613",
+ "sha256:6f7b82934c08e28a2d537d870293236b1000d94d0b4583825ab9649aef7ddf63",
+ "sha256:6fe4ef4402df0250b75ba876c3795510d782def5c1e63890bde02d622570d39e",
+ "sha256:719544565c2937c21a6f76d520e6e52b726d132815adb3447ccffbe9f44203c4",
+ "sha256:730766072fd5dcb219dd2b95c4c49752a54f00157f322bc6d71f7d2a31fecd79",
+ "sha256:74eb65ec61e3c7c019d7169387d1b6ffcfea1b9ec5894d116a9a903636e4a0b1",
+ "sha256:7993232bd4044392c47779a3c7e8889fea6883be46281d45a81451acfd704d7e",
+ "sha256:80bbaddf2baab7e6de4bc47405e34948e694a9efe0861c61cdc23aa774fcb141",
+ "sha256:86545e351e879d0b72b620db6a3b96346921fa87b3d366d6c074e5a9a0b8dadb",
+ "sha256:891dc8f522d7059ff0024cd3ae79fd224752676447f9c678f2a5c14b84d9a939",
+ "sha256:8a31f24e2a0b6317f33aafbb2f0895c0bce772980ae60c2c640d82caac49628a",
+ "sha256:8b99ec73073b37f9ebe8caf399001848fced9c08064effdbfc4da2b5a8d07b93",
+ "sha256:986b7a96228c9b4942ec420eff37556c5777bfba6758edcb95421e4a614b57f9",
+ "sha256:a1547ff4b8a833511eeaceacbcd17b043214fcdb385148f9c1bc5556ca9623e2",
+ "sha256:a2bfc7e2a0601b475477c954bf167dee6d0f55cb167e3f3e7cefad906e7759f6",
+ "sha256:a3c5f1a719aa11866ffc530d54ad965063a8cbbecae6515acbd5f0fae8f48eaa",
+ "sha256:a9f1c3489736ff8e1c7652e9dc39f80cff820f23624f23d9eab6e122ac99b150",
+ "sha256:aa0cf4922da7a3c905d000b35065df6184c0dc1d866dd3b86fd961905bbad2ea",
+ "sha256:ad4332a532e2d5acb231a2e5d33f943750091ee435daffca3fec0a53224e7e33",
+ "sha256:b2582b238e1658c4061ebe1b4df53c435190d22457642377fd0cb30685cdfb76",
+ "sha256:b6fc2e2fb6f532cf48b5fed57567ef286addcef38c28874458a41b7837a57807",
+ "sha256:b92d40121dcbd74831b690a75533da703750f7041b4bf951befc657c37e5695a",
+ "sha256:bbab6faf6568484707acc052f4dfc3802bdb0cafe079383fbaa23f1cdae9ecd4",
+ "sha256:c0b88ed1ae66777a798dc54f627e32d3b81c8009967c63993c450ee4cbcbec15",
+ "sha256:ce13d6291a5f47c1c8dbd375baa78551053bc6b5e5c0e9bb8e39c0a8359fd52f",
+ "sha256:db3535733f59e5605a88a706824dfcb9bd06725e709ecb017e165fc1d6e7d429",
+ "sha256:dd10383f1d6b7edf247d0960a3db274c07e96cf3a3fc7c41c8448f93eac3fb1c",
+ "sha256:e01f9531ba5420838c801c21c1b0f45dbc9607cb22ea2cf132844453bec863a5",
+ "sha256:e11527dc23d5ef44d76fef11213215c34f36af1608074561fcc561d983aeb870",
+ "sha256:e1ab2fac607842ac36864e358c42feb0960ae62c34aa4caaf12ada0a1fb5d99b",
+ "sha256:e1fd7d2fe11f1cb63d3336d147c852f6d07de0d0020d704c6031b46a30b02ca8",
+ "sha256:e9f84ed9f4d50b74fbc77298ee5c870f67cb7e91dcdc1a6915cb1ff6a317476c",
+ "sha256:ec4b4e75fc68da9dc0ed73dcdb431c25c57775383fec325d23a770a64e7ebc87",
+ "sha256:f10ce66fcdeb3543df51d423ede7e238be98412232fca5daec3e54bcd16b8da0",
+ "sha256:f63f62fc60e6228a4ca9abae28228f35e1bd3ce675013d1dfb828688d50c6e23",
+ "sha256:fa56bb08b3dd8eac3a8c5b7d075c94e74f755fd9d8a04543ae8d37b1612dd170",
+ "sha256:fa9b7c450be85bfc6cd39f6df8c5b8cbd76b5d6fc1f69efec80203f9894b885f"
],
- "index": "pypi",
- "version": "==4.6.5"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==4.8.0"
},
"markupsafe": {
"hashes": [
- "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298",
- "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64",
- "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b",
- "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194",
- "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567",
- "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff",
- "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724",
- "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74",
- "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646",
- "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35",
- "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6",
- "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a",
- "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6",
- "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad",
- "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26",
- "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38",
- "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac",
- "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7",
- "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6",
- "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047",
- "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75",
- "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f",
- "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b",
- "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135",
- "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8",
- "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a",
- "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a",
- "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1",
- "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9",
- "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864",
- "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914",
- "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee",
- "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f",
- "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18",
- "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8",
- "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2",
- "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d",
- "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b",
- "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b",
- "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86",
- "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6",
- "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f",
- "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb",
- "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833",
- "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28",
- "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e",
- "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415",
- "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902",
- "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f",
- "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d",
- "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9",
- "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d",
- "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145",
- "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066",
- "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c",
- "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1",
- "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a",
- "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207",
- "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f",
- "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53",
- "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd",
- "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134",
- "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85",
- "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9",
- "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5",
- "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94",
- "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509",
- "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51",
- "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"
- ],
- "markers": "python_version >= '3.6'",
- "version": "==2.0.1"
+ "sha256:023af8c54fe63530545f70dd2a2a7eed18d07a9a77b94e8bf1e2ff7f252db9a3",
+ "sha256:09c86c9643cceb1d87ca08cdc30160d1b7ab49a8a21564868921959bd16441b8",
+ "sha256:142119fb14a1ef6d758912b25c4e803c3ff66920635c44078666fe7cc3f8f759",
+ "sha256:1d1fb9b2eec3c9714dd936860850300b51dbaa37404209c8d4cb66547884b7ed",
+ "sha256:204730fd5fe2fe3b1e9ccadb2bd18ba8712b111dcabce185af0b3b5285a7c989",
+ "sha256:24c3be29abb6b34052fd26fc7a8e0a49b1ee9d282e3665e8ad09a0a68faee5b3",
+ "sha256:290b02bab3c9e216da57c1d11d2ba73a9f73a614bbdcc027d299a60cdfabb11a",
+ "sha256:3028252424c72b2602a323f70fbf50aa80a5d3aa616ea6add4ba21ae9cc9da4c",
+ "sha256:30c653fde75a6e5eb814d2a0a89378f83d1d3f502ab710904ee585c38888816c",
+ "sha256:3cace1837bc84e63b3fd2dfce37f08f8c18aeb81ef5cf6bb9b51f625cb4e6cd8",
+ "sha256:4056f752015dfa9828dce3140dbadd543b555afb3252507348c493def166d454",
+ "sha256:454ffc1cbb75227d15667c09f164a0099159da0c1f3d2636aa648f12675491ad",
+ "sha256:598b65d74615c021423bd45c2bc5e9b59539c875a9bdb7e5f2a6b92dfcfc268d",
+ "sha256:599941da468f2cf22bf90a84f6e2a65524e87be2fce844f96f2dd9a6c9d1e635",
+ "sha256:5ddea4c352a488b5e1069069f2f501006b1a4362cb906bee9a193ef1245a7a61",
+ "sha256:62c0285e91414f5c8f621a17b69fc0088394ccdaa961ef469e833dbff64bd5ea",
+ "sha256:679cbb78914ab212c49c67ba2c7396dc599a8479de51b9a87b174700abd9ea49",
+ "sha256:6e104c0c2b4cd765b4e83909cde7ec61a1e313f8a75775897db321450e928cce",
+ "sha256:736895a020e31b428b3382a7887bfea96102c529530299f426bf2e636aacec9e",
+ "sha256:75bb36f134883fdbe13d8e63b8675f5f12b80bb6627f7714c7d6c5becf22719f",
+ "sha256:7d2f5d97fcbd004c03df8d8fe2b973fe2b14e7bfeb2cfa012eaa8759ce9a762f",
+ "sha256:80beaf63ddfbc64a0452b841d8036ca0611e049650e20afcb882f5d3c266d65f",
+ "sha256:84ad5e29bf8bab3ad70fd707d3c05524862bddc54dc040982b0dbcff36481de7",
+ "sha256:8da5924cb1f9064589767b0f3fc39d03e3d0fb5aa29e0cb21d43106519bd624a",
+ "sha256:961eb86e5be7d0973789f30ebcf6caab60b844203f4396ece27310295a6082c7",
+ "sha256:96de1932237abe0a13ba68b63e94113678c379dca45afa040a17b6e1ad7ed076",
+ "sha256:a0a0abef2ca47b33fb615b491ce31b055ef2430de52c5b3fb19a4042dbc5cadb",
+ "sha256:b2a5a856019d2833c56a3dcac1b80fe795c95f401818ea963594b345929dffa7",
+ "sha256:b8811d48078d1cf2a6863dafb896e68406c5f513048451cd2ded0473133473c7",
+ "sha256:c532d5ab79be0199fa2658e24a02fce8542df196e60665dd322409a03db6a52c",
+ "sha256:d3b64c65328cb4cd252c94f83e66e3d7acf8891e60ebf588d7b493a55a1dbf26",
+ "sha256:d4e702eea4a2903441f2735799d217f4ac1b55f7d8ad96ab7d4e25417cb0827c",
+ "sha256:d5653619b3eb5cbd35bfba3c12d575db2a74d15e0e1c08bf1db788069d410ce8",
+ "sha256:d66624f04de4af8bbf1c7f21cc06649c1c69a7f84109179add573ce35e46d448",
+ "sha256:e67ec74fada3841b8c5f4c4f197bea916025cb9aa3fe5abf7d52b655d042f956",
+ "sha256:e6f7f3f41faffaea6596da86ecc2389672fa949bd035251eab26dc6697451d05",
+ "sha256:f02cf7221d5cd915d7fa58ab64f7ee6dd0f6cddbb48683debf5d04ae9b1c2cc1",
+ "sha256:f0eddfcabd6936558ec020130f932d479930581171368fd728efcfb6ef0dd357",
+ "sha256:fabbe18087c3d33c5824cb145ffca52eccd053061df1d79d4b66dafa5ad2a5ea",
+ "sha256:fc3150f85e2dbcf99e65238c842d1cfe69d3e7649b19864c1cc043213d9cd730"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==2.1.0"
},
"oauthlib": {
"hashes": [
- "sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc",
- "sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3"
+ "sha256:23a8208d75b902797ea29fd31fa80a15ed9dc2c6c16fe73f5d346f83f6fa27a2",
+ "sha256:6db33440354787f9b7f3a6dbd4febf5d0f93758354060e802f6c06cb493022fe"
],
"markers": "python_version >= '3.6'",
- "version": "==3.1.1"
- },
- "packaging": {
- "hashes": [
- "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
- "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"
- ],
- "markers": "python_version >= '3.6'",
- "version": "==21.3"
+ "version": "==3.2.0"
},
"protobuf": {
"hashes": [
- "sha256:038daf4fa38a7e818dd61f51f22588d61755160a98db087a046f80d66b855942",
- "sha256:28ccea56d4dc38d35cd70c43c2da2f40ac0be0a355ef882242e8586c6d66666f",
- "sha256:36d90676d6f426718463fe382ec6274909337ca6319d375eebd2044e6c6ac560",
- "sha256:3cd0458870ea7d1c58e948ac8078f6ba8a7ecc44a57e03032ed066c5bb318089",
- "sha256:5935c8ce02e3d89c7900140a8a42b35bc037ec07a6aeb61cc108be8d3c9438a6",
- "sha256:615b426a177780ce381ecd212edc1e0f70db8557ed72560b82096bd36b01bc04",
- "sha256:62a8e4baa9cb9e064eb62d1002eca820857ab2138440cb4b3ea4243830f94ca7",
- "sha256:655264ed0d0efe47a523e2255fc1106a22f6faab7cc46cfe99b5bae085c2a13e",
- "sha256:6e8ea9173403219239cdfd8d946ed101f2ab6ecc025b0fda0c6c713c35c9981d",
- "sha256:71b0250b0cfb738442d60cab68abc166de43411f2a4f791d31378590bfb71bd7",
- "sha256:74f33edeb4f3b7ed13d567881da8e5a92a72b36495d57d696c2ea1ae0cfee80c",
- "sha256:77d2fadcf369b3f22859ab25bd12bb8e98fb11e05d9ff9b7cd45b711c719c002",
- "sha256:8b30a7de128c46b5ecb343917d9fa737612a6e8280f440874e5cc2ba0d79b8f6",
- "sha256:8e51561d72efd5bd5c91490af1f13e32bcba8dab4643761eb7de3ce18e64a853",
- "sha256:a529e7df52204565bcd33738a7a5f288f3d2d37d86caa5d78c458fa5fabbd54d",
- "sha256:b691d996c6d0984947c4cf8b7ae2fe372d99b32821d0584f0b90277aa36982d3",
- "sha256:d80f80eb175bf5f1169139c2e0c5ada98b1c098e2b3c3736667f28cbbea39fc8",
- "sha256:d83e1ef8cb74009bebee3e61cc84b1c9cd04935b72bca0cbc83217d140424995",
- "sha256:d8919368410110633717c406ab5c97e8df5ce93020cfcf3012834f28b1fab1ea",
- "sha256:db3532d9f7a6ebbe2392041350437953b6d7a792de10e629c1e4f5a6b1fe1ac6",
- "sha256:e7b24c11df36ee8e0c085e5b0dc560289e4b58804746fb487287dda51410f1e2",
- "sha256:e7e8d2c20921f8da0dea277dfefc6abac05903ceac8e72839b2da519db69206b",
- "sha256:e813b1c9006b6399308e917ac5d298f345d95bb31f46f02b60cd92970a9afa17",
- "sha256:fd390367fc211cc0ffcf3a9e149dfeca78fecc62adb911371db0cec5c8b7472d"
- ],
- "markers": "python_version >= '3.1'",
- "version": "==3.19.1"
+ "sha256:072fbc78d705d3edc7ccac58a62c4c8e0cec856987da7df8aca86e647be4e35c",
+ "sha256:09297b7972da685ce269ec52af761743714996b4381c085205914c41fcab59fb",
+ "sha256:16f519de1313f1b7139ad70772e7db515b1420d208cb16c6d7858ea989fc64a9",
+ "sha256:1c91ef4110fdd2c590effb5dca8fdbdcb3bf563eece99287019c4204f53d81a4",
+ "sha256:3112b58aac3bac9c8be2b60a9daf6b558ca3f7681c130dcdd788ade7c9ffbdca",
+ "sha256:36cecbabbda242915529b8ff364f2263cd4de7c46bbe361418b5ed859677ba58",
+ "sha256:4276cdec4447bd5015453e41bdc0c0c1234eda08420b7c9a18b8d647add51e4b",
+ "sha256:435bb78b37fc386f9275a7035fe4fb1364484e38980d0dd91bc834a02c5ec909",
+ "sha256:48ed3877fa43e22bcacc852ca76d4775741f9709dd9575881a373bd3e85e54b2",
+ "sha256:54a1473077f3b616779ce31f477351a45b4fef8c9fd7892d6d87e287a38df368",
+ "sha256:69da7d39e39942bd52848438462674c463e23963a1fdaa84d88df7fbd7e749b2",
+ "sha256:6cbc312be5e71869d9d5ea25147cdf652a6781cf4d906497ca7690b7b9b5df13",
+ "sha256:7bb03bc2873a2842e5ebb4801f5c7ff1bfbdf426f85d0172f7644fcda0671ae0",
+ "sha256:7ca7da9c339ca8890d66958f5462beabd611eca6c958691a8fe6eccbd1eb0c6e",
+ "sha256:835a9c949dc193953c319603b2961c5c8f4327957fe23d914ca80d982665e8ee",
+ "sha256:84123274d982b9e248a143dadd1b9815049f4477dc783bf84efe6250eb4b836a",
+ "sha256:8961c3a78ebfcd000920c9060a262f082f29838682b1f7201889300c1fbe0616",
+ "sha256:96bd766831596d6014ca88d86dc8fe0fb2e428c0b02432fd9db3943202bf8c5e",
+ "sha256:9df0c10adf3e83015ced42a9a7bd64e13d06c4cf45c340d2c63020ea04499d0a",
+ "sha256:b38057450a0c566cbd04890a40edf916db890f2818e8682221611d78dc32ae26",
+ "sha256:bd95d1dfb9c4f4563e6093a9aa19d9c186bf98fa54da5252531cc0d3a07977e7",
+ "sha256:c1068287025f8ea025103e37d62ffd63fec8e9e636246b89c341aeda8a67c934",
+ "sha256:c438268eebb8cf039552897d78f402d734a404f1360592fef55297285f7f953f",
+ "sha256:cdc076c03381f5c1d9bb1abdcc5503d9ca8b53cf0a9d31a9f6754ec9e6c8af0f",
+ "sha256:f358aa33e03b7a84e0d91270a4d4d8f5df6921abe99a377828839e8ed0c04e07",
+ "sha256:f51d5a9f137f7a2cec2d326a74b6e3fc79d635d69ffe1b036d39fc7d75430d37"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==3.19.4"
},
"pyasn1": {
"hashes": [
@@ -388,39 +395,52 @@
},
"pydantic": {
"hashes": [
- "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd",
- "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739",
- "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f",
- "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840",
- "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23",
- "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287",
- "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62",
- "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b",
- "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb",
- "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820",
- "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3",
- "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b",
- "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e",
- "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3",
- "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316",
- "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b",
- "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4",
- "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20",
- "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e",
- "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505",
- "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1",
- "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"
+ "sha256:085ca1de245782e9b46cefcf99deecc67d418737a1fd3f6a4f511344b613a5b3",
+ "sha256:086254884d10d3ba16da0588604ffdc5aab3f7f09557b998373e885c690dd398",
+ "sha256:0b6037175234850ffd094ca77bf60fb54b08b5b22bc85865331dd3bda7a02fa1",
+ "sha256:0fe476769acaa7fcddd17cadd172b156b53546ec3614a4d880e5d29ea5fbce65",
+ "sha256:1d5278bd9f0eee04a44c712982343103bba63507480bfd2fc2790fa70cd64cf4",
+ "sha256:2cc6a4cb8a118ffec2ca5fcb47afbacb4f16d0ab8b7350ddea5e8ef7bcc53a16",
+ "sha256:2ee7e3209db1e468341ef41fe263eb655f67f5c5a76c924044314e139a1103a2",
+ "sha256:3011b975c973819883842c5ab925a4e4298dffccf7782c55ec3580ed17dc464c",
+ "sha256:3c3b035103bd4e2e4a28da9da7ef2fa47b00ee4a9cf4f1a735214c1bcd05e0f6",
+ "sha256:4c68c3bc88dbda2a6805e9a142ce84782d3930f8fdd9655430d8576315ad97ce",
+ "sha256:574936363cd4b9eed8acdd6b80d0143162f2eb654d96cb3a8ee91d3e64bf4cf9",
+ "sha256:5a79330f8571faf71bf93667d3ee054609816f10a259a109a0738dac983b23c3",
+ "sha256:5e48ef4a8b8c066c4a31409d91d7ca372a774d0212da2787c0d32f8045b1e034",
+ "sha256:6c5b77947b9e85a54848343928b597b4f74fc364b70926b3c4441ff52620640c",
+ "sha256:742645059757a56ecd886faf4ed2441b9c0cd406079c2b4bee51bcc3fbcd510a",
+ "sha256:7bdfdadb5994b44bd5579cfa7c9b0e1b0e540c952d56f627eb227851cda9db77",
+ "sha256:815ddebb2792efd4bba5488bc8fde09c29e8ca3227d27cf1c6990fc830fd292b",
+ "sha256:8b5ac0f1c83d31b324e57a273da59197c83d1bb18171e512908fe5dc7278a1d6",
+ "sha256:96f240bce182ca7fe045c76bcebfa0b0534a1bf402ed05914a6f1dadff91877f",
+ "sha256:a733965f1a2b4090a5238d40d983dcd78f3ecea221c7af1497b845a9709c1721",
+ "sha256:ab624700dc145aa809e6f3ec93fb8e7d0f99d9023b713f6a953637429b437d37",
+ "sha256:b2571db88c636d862b35090ccf92bf24004393f85c8870a37f42d9f23d13e032",
+ "sha256:bbbc94d0c94dd80b3340fc4f04fd4d701f4b038ebad72c39693c794fd3bc2d9d",
+ "sha256:c0727bda6e38144d464daec31dff936a82917f431d9c39c39c60a26567eae3ed",
+ "sha256:c556695b699f648c58373b542534308922c46a1cda06ea47bc9ca45ef5b39ae6",
+ "sha256:c86229333cabaaa8c51cf971496f10318c4734cf7b641f08af0a6fbf17ca3054",
+ "sha256:c8d7da6f1c1049eefb718d43d99ad73100c958a5367d30b9321b092771e96c25",
+ "sha256:c8e9dcf1ac499679aceedac7e7ca6d8641f0193c591a2d090282aaf8e9445a46",
+ "sha256:cb23bcc093697cdea2708baae4f9ba0e972960a835af22560f6ae4e7e47d33f5",
+ "sha256:d1e4c28f30e767fd07f2ddc6f74f41f034d1dd6bc526cd59e63a82fe8bb9ef4c",
+ "sha256:d9c9bdb3af48e242838f9f6e6127de9be7063aad17b32215ccc36a09c5cf1070",
+ "sha256:dee5ef83a76ac31ab0c78c10bd7d5437bfdb6358c95b91f1ba7ff7b76f9996a1",
+ "sha256:e0896200b6a40197405af18828da49f067c2fa1f821491bc8f5bde241ef3f7d7",
+ "sha256:f5a64b64ddf4c99fe201ac2724daada8595ada0d102ab96d019c1555c2d6441d",
+ "sha256:f947352c3434e8b937e3aa8f96f47bdfe6d92779e44bb3f41e4c213ba6a32145"
],
"index": "pypi",
- "version": "==1.8.2"
+ "version": "==1.9.0"
},
"pyparsing": {
"hashes": [
- "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4",
- "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"
+ "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea",
+ "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"
],
"markers": "python_version >= '3.1'",
- "version": "==3.0.6"
+ "version": "==3.0.7"
},
"python-docx": {
"hashes": [
@@ -435,13 +455,6 @@
],
"version": "==0.19.2"
},
- "pytz": {
- "hashes": [
- "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c",
- "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"
- ],
- "version": "==2021.3"
- },
"pyyaml": {
"hashes": [
"sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293",
@@ -482,19 +495,19 @@
},
"requests": {
"hashes": [
- "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
- "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
+ "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61",
+ "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
- "version": "==2.26.0"
+ "version": "==2.27.1"
},
"requests-oauthlib": {
"hashes": [
- "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d",
- "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a",
- "sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"
+ "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5",
+ "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"
],
- "version": "==1.3.0"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==1.3.1"
},
"rsa": {
"hashes": [
@@ -506,11 +519,11 @@
},
"setuptools": {
"hashes": [
- "sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373",
- "sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e"
+ "sha256:2347b2b432c891a863acadca2da9ac101eae6169b1d3dfee2ec605ecd50dbfe5",
+ "sha256:e4f30b9f84e5ab3decf945113119649fec09c1fc3507c6ebffec75646c56e62b"
],
- "markers": "python_version >= '3.6'",
- "version": "==59.6.0"
+ "markers": "python_version >= '3.7'",
+ "version": "==60.9.3"
},
"six": {
"hashes": [
@@ -520,48 +533,56 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0"
},
+ "sniffio": {
+ "hashes": [
+ "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663",
+ "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==1.2.0"
+ },
"starlette": {
"hashes": [
- "sha256:3c8e48e52736b3161e34c9f0e8153b4f32ec5d8995a3ee1d59410d92f75162ed",
- "sha256:7d49f4a27f8742262ef1470608c59ddbc66baf37c148e938c7038e6bc7a998aa"
+ "sha256:26a18cbda5e6b651c964c12c88b36d9898481cd428ed6e063f5f29c418f73050",
+ "sha256:57eab3cc975a28af62f6faec94d355a410634940f10b30d68d31cb5ec1b44ae8"
],
"markers": "python_version >= '3.6'",
- "version": "==0.14.2"
+ "version": "==0.17.1"
},
"typing-extensions": {
"hashes": [
- "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e",
- "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"
+ "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42",
+ "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"
],
"markers": "python_version >= '3.6'",
- "version": "==4.0.1"
+ "version": "==4.1.1"
},
"uritemplate": {
"hashes": [
- "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f",
- "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"
+ "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0",
+ "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==3.0.1"
+ "markers": "python_version >= '3.6'",
+ "version": "==4.1.1"
},
"urllib3": {
"hashes": [
- "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece",
- "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"
+ "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed",
+ "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
- "version": "==1.26.7"
+ "version": "==1.26.8"
},
"uvicorn": {
"extras": [
"standard"
],
"hashes": [
- "sha256:3292251b3c7978e8e4a7868f4baf7f7f7bb7e40c759ecc125c37e99cdea34202",
- "sha256:7587f7b08bd1efd2b9bad809a3d333e972f1d11af8a5e52a9371ee3a5de71524"
+ "sha256:19e2a0e96c9ac5581c01eb1a79a7d2f72bb479691acd2b8921fce48ed5b961a6",
+ "sha256:5180f9d059611747d841a4a4c4ab675edf54c8489e97f96d0583ee90ac3bfc23"
],
"index": "pypi",
- "version": "==0.13.4"
+ "version": "==0.17.6"
},
"uvloop": {
"hashes": [
@@ -593,30 +614,64 @@
},
"websockets": {
"hashes": [
- "sha256:0e4fb4de42701340bd2353bb2eee45314651caa6ccee80dbd5f5d5978888fed5",
- "sha256:1d3f1bf059d04a4e0eb4985a887d49195e15ebabc42364f4eb564b1d065793f5",
- "sha256:20891f0dddade307ffddf593c733a3fdb6b83e6f9eef85908113e628fa5a8308",
- "sha256:295359a2cc78736737dd88c343cd0747546b2174b5e1adc223824bcaf3e164cb",
- "sha256:2db62a9142e88535038a6bcfea70ef9447696ea77891aebb730a333a51ed559a",
- "sha256:3762791ab8b38948f0c4d281c8b2ddfa99b7e510e46bd8dfa942a5fff621068c",
- "sha256:3db87421956f1b0779a7564915875ba774295cc86e81bc671631379371af1170",
- "sha256:3ef56fcc7b1ff90de46ccd5a687bbd13a3180132268c4254fc0fa44ecf4fc422",
- "sha256:4f9f7d28ce1d8f1295717c2c25b732c2bc0645db3215cf757551c392177d7cb8",
- "sha256:5c01fd846263a75bc8a2b9542606927cfad57e7282965d96b93c387622487485",
- "sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f",
- "sha256:751a556205d8245ff94aeef23546a1113b1dd4f6e4d102ded66c39b99c2ce6c8",
- "sha256:7ff46d441db78241f4c6c27b3868c9ae71473fe03341340d2dfdbe8d79310acc",
- "sha256:965889d9f0e2a75edd81a07592d0ced54daa5b0785f57dc429c378edbcffe779",
- "sha256:9b248ba3dd8a03b1a10b19efe7d4f7fa41d158fdaa95e2cf65af5a7b95a4f989",
- "sha256:9bef37ee224e104a413f0780e29adb3e514a5b698aabe0d969a6ba426b8435d1",
- "sha256:c1ec8db4fac31850286b7cd3b9c0e1b944204668b8eb721674916d4e28744092",
- "sha256:c8a116feafdb1f84607cb3b14aa1418424ae71fee131642fc568d21423b51824",
- "sha256:ce85b06a10fc65e6143518b96d3dca27b081a740bae261c2fb20375801a9d56d",
- "sha256:d705f8aeecdf3262379644e4b55107a3b55860eb812b673b28d0fbc347a60c55",
- "sha256:e898a0863421650f0bebac8ba40840fc02258ef4714cb7e1fd76b6a6354bda36",
- "sha256:f8a7bff6e8664afc4e6c28b983845c5bc14965030e3fb98789734d416af77c4b"
- ],
- "version": "==8.1"
+ "sha256:038afef2a05893578d10dadbdbb5f112bd115c46347e1efe99f6a356ff062138",
+ "sha256:05f6e9757017270e7a92a2975e2ae88a9a582ffc4629086fd6039aa80e99cd86",
+ "sha256:0b66421f9f13d4df60cd48ab977ed2c2b6c9147ae1a33caf5a9f46294422fda1",
+ "sha256:0cd02f36d37e503aca88ab23cc0a1a0e92a263d37acf6331521eb38040dcf77b",
+ "sha256:0f73cb2526d6da268e86977b2c4b58f2195994e53070fe567d5487c6436047e6",
+ "sha256:117383d0a17a0dda349f7a8790763dde75c1508ff8e4d6e8328b898b7df48397",
+ "sha256:1c1f3b18c8162e3b09761d0c6a0305fd642934202541cc511ef972cb9463261e",
+ "sha256:1c9031e90ebfc486e9cdad532b94004ade3aa39a31d3c46c105bb0b579cd2490",
+ "sha256:2349fa81b6b959484bb2bda556ccb9eb70ba68987646a0f8a537a1a18319fb03",
+ "sha256:24b879ba7db12bb525d4e58089fcbe6a3df3ce4666523183654170e86d372cbe",
+ "sha256:2aa9b91347ecd0412683f28aabe27f6bad502d89bd363b76e0a3508b1596402e",
+ "sha256:56d48eebe9e39ce0d68701bce3b21df923aa05dcc00f9fd8300de1df31a7c07c",
+ "sha256:5a38a0175ae82e4a8c4bac29fc01b9ee26d7d5a614e5ee11e7813c68a7d938ce",
+ "sha256:5b04270b5613f245ec84bb2c6a482a9d009aefad37c0575f6cda8499125d5d5c",
+ "sha256:6193bbc1ee63aadeb9a4d81de0e19477401d150d506aee772d8380943f118186",
+ "sha256:669e54228a4d9457abafed27cbf0e2b9f401445c4dfefc12bf8e4db9751703b8",
+ "sha256:6a009eb551c46fd79737791c0c833fc0e5b56bcd1c3057498b262d660b92e9cd",
+ "sha256:71a4491cfe7a9f18ee57d41163cb6a8a3fa591e0f0564ca8b0ed86b2a30cced4",
+ "sha256:7b38a5c9112e3dbbe45540f7b60c5204f49b3cb501b40950d6ab34cd202ab1d0",
+ "sha256:7bb9d8a6beca478c7e9bdde0159bd810cc1006ad6a7cb460533bae39da692ca2",
+ "sha256:82bc33db6d8309dc27a3bee11f7da2288ad925fcbabc2a4bb78f7e9c56249baf",
+ "sha256:8351c3c86b08156337b0e4ece0e3c5ec3e01fcd14e8950996832a23c99416098",
+ "sha256:8beac786a388bb99a66c3be4ab0fb38273c0e3bc17f612a4e0a47c4fc8b9c045",
+ "sha256:97950c7c844ec6f8d292440953ae18b99e3a6a09885e09d20d5e7ecd9b914cf8",
+ "sha256:98f57b3120f8331cd7440dbe0e776474f5e3632fdaa474af1f6b754955a47d71",
+ "sha256:9ca2ca05a4c29179f06cf6727b45dba5d228da62623ec9df4184413d8aae6cb9",
+ "sha256:a03a25d95cc7400bd4d61a63460b5d85a7761c12075ee2f51de1ffe73aa593d3",
+ "sha256:a10c0c1ee02164246f90053273a42d72a3b2452a7e7486fdae781138cf7fbe2d",
+ "sha256:a72b92f96e5e540d5dda99ee3346e199ade8df63152fa3c737260da1730c411f",
+ "sha256:ac081aa0307f263d63c5ff0727935c736c8dad51ddf2dc9f5d0c4759842aefaa",
+ "sha256:b22bdc795e62e71118b63e14a08bacfa4f262fd2877de7e5b950f5ac16b0348f",
+ "sha256:b4059e2ccbe6587b6dc9a01db5fc49ead9a884faa4076eea96c5ec62cb32f42a",
+ "sha256:b7fe45ae43ac814beb8ca09d6995b56800676f2cfa8e23f42839dc69bba34a42",
+ "sha256:bef03a51f9657fb03d8da6ccd233fe96e04101a852f0ffd35f5b725b28221ff3",
+ "sha256:bffc65442dd35c473ca9790a3fa3ba06396102a950794f536783f4b8060af8dd",
+ "sha256:c21a67ab9a94bd53e10bba21912556027fea944648a09e6508415ad14e37c325",
+ "sha256:c67d9cacb3f6537ca21e9b224d4fd08481538e43bcac08b3d93181b0816def39",
+ "sha256:c6e56606842bb24e16e36ae7eb308d866b4249cf0be8f63b212f287eeb76b124",
+ "sha256:cb316b87cbe3c0791c2ad92a5a36bf6adc87c457654335810b25048c1daa6fd5",
+ "sha256:cef40a1b183dcf39d23b392e9dd1d9b07ab9c46aadf294fff1350fb79146e72b",
+ "sha256:cf931c33db9c87c53d009856045dd524e4a378445693382a920fa1e0eb77c36c",
+ "sha256:d4d110a84b63c5cfdd22485acc97b8b919aefeecd6300c0c9d551e055b9a88ea",
+ "sha256:d5396710f86a306cf52f87fd8ea594a0e894ba0cc5a36059eaca3a477dc332aa",
+ "sha256:f09f46b1ff6d09b01c7816c50bd1903cf7d02ebbdb63726132717c2fcda835d5",
+ "sha256:f14bd10e170abc01682a9f8b28b16e6f20acf6175945ef38db6ffe31b0c72c3f",
+ "sha256:f5c335dc0e7dc271ef36df3f439868b3c790775f345338c2f61a562f1074187b",
+ "sha256:f8296b8408ec6853b26771599990721a26403e62b9de7e50ac0a056772ac0b5e",
+ "sha256:fa35c5d1830d0fb7b810324e9eeab9aa92e8f273f11fdbdc0741dcded6d72b9f"
+ ],
+ "version": "==10.2"
+ },
+ "yadisk": {
+ "hashes": [
+ "sha256:73312c1263d660ac79401400e65c7b7a93d90f132d33107e25c8ac88ebf01110",
+ "sha256:ea45dc57dc9a91795e08849fb07d39ce04f2130f09af55dc4980e75786dcba49"
+ ],
+ "index": "pypi",
+ "version": "==1.2.15"
}
},
"develop": {}
diff --git a/watchdoc/src/auth.py b/watchdoc/src/auth.py
deleted file mode 100644
index 8a3a9c087..000000000
--- a/watchdoc/src/auth.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from google_auth_oauthlib.flow import InstalledAppFlow
-from google.auth.transport.requests import Request
-from google.oauth2.credentials import Credentials
-
-from config import (
- WATCHDOC_AUTH_PORT,
- CREDENTIALS_PATH,
- TOKENS_PATH,
- SCOPES,
-)
-
-
-def obtain_credentials():
- creds = None
-
- if TOKENS_PATH.exists():
- creds = Credentials.from_authorized_user_file(TOKENS_PATH, SCOPES)
-
- if not creds or not creds.valid:
- if creds and creds.expired and creds.refresh_token:
- creds.refresh(Request())
- else:
- flow = InstalledAppFlow.from_client_secrets_file(
- CREDENTIALS_PATH,
- SCOPES,
- )
- creds = flow.run_local_server(
- port=WATCHDOC_AUTH_PORT,
- open_browser=False,
- )
-
- with open(TOKENS_PATH, "w") as token:
- token.write(creds.to_json())
-
- return creds
diff --git a/watchdoc/src/config.py b/watchdoc/src/config.py
index d9cd3c28f..7eb652348 100644
--- a/watchdoc/src/config.py
+++ b/watchdoc/src/config.py
@@ -6,21 +6,10 @@
# Navigation
BASE_DIR = Path(__file__).resolve().parent.parent
-SECRETS_DIR = BASE_DIR / "secrets"
TEMPLATES_DIR = BASE_DIR / "templates"
GENERATED_DIR = BASE_DIR / "generated"
TEMP_DIR = BASE_DIR / "temp"
-# ------------------------------------------------------------------------------
-# Google Auth
-
-CREDENTIALS_PATH = SECRETS_DIR / "credentials.json"
-TOKENS_PATH = SECRETS_DIR / "tokens.json"
-
-SCOPES = [
- "https://www.googleapis.com/auth/drive",
-]
-
# ------------------------------------------------------------------------------
# Internal
@@ -31,9 +20,15 @@
# ------------------------------------------------------------------------------
# Email settings
+
EMAIL_HOST = os.environ["EMAIL_HOST"]
EMAIL_PORT = os.environ["EMAIL_PORT"]
EMAIL_USE_TLS = os.environ["EMAIL_USE_TLS"].lower() == "true"
EMAIL_HOST_USER = os.environ["EMAIL_HOST_USER"]
EMAIL_HOST_PASSWORD = os.environ["EMAIL_HOST_PASSWORD"]
EMAIL_FROM_NAME = "Даль ВУЦ ВШЭ"
+
+# ------------------------------------------------------------------------------
+# YaDisk
+
+YADISK_TOKEN = os.environ["YADISK_TOKEN"]
diff --git a/watchdoc/src/disk.py b/watchdoc/src/disk.py
new file mode 100644
index 000000000..4140ed0f8
--- /dev/null
+++ b/watchdoc/src/disk.py
@@ -0,0 +1,30 @@
+import yadisk
+
+
+class DiskService:
+ """Yandex Disk API wrapper.
+
+ Usage:
+ ds = DiskService(token=yadisk_token)
+ """
+
+ def __init__(self, token) -> None:
+ self._service = yadisk.YaDisk(token=token)
+
+ def obtain_folder(self, path, **kwargs):
+ if not self._service.exists(path):
+ self._service.mkdir(path, **kwargs)
+ return path
+
+ def upload_docx(self, local_path, remote_path, **kwargs):
+ with open(local_path, "rb") as f:
+ return self._service.upload(f, remote_path, **kwargs)
+
+ def delete_file(self, path, **kwargs):
+ self._service.remove(path, **kwargs)
+
+ def read_share_folder_with_anyone(self, remote_path):
+ self._service.publish(remote_path)
+ public_key = self._service.get_meta(remote_path)["public_key"]
+ public_resource = self._service.get_public_meta(public_key)
+ return public_resource["public_url"]
diff --git a/watchdoc/src/drive.py b/watchdoc/src/drive.py
deleted file mode 100644
index 7533437bc..000000000
--- a/watchdoc/src/drive.py
+++ /dev/null
@@ -1,123 +0,0 @@
-from googleapiclient.discovery import build
-from googleapiclient.http import MediaFileUpload
-
-DEFAULT_FILE_FIELDS: str = "id, name, webViewLink"
-
-FOLDER_MIME_TYPE: str = "application/vnd.google-apps.folder"
-DOCX_MIME_TYPE: str = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
-
-
-class DriveService:
- """Google Drive API wrapper.
-
- Usage:
- ds = DriveService()
- response = ds.list_files()
- files = response["files"]
- """
- def __init__(self, credentials) -> None:
- self._service = build("drive", "v3", credentials=credentials)
-
- # --------------------------------------------------------------------------
- # Files & folders
-
- def list_files(self, **kwargs):
- return self._service.files().list(**kwargs).execute()
-
- def list_folders(self, **kwargs):
- response = self.list_files(**kwargs)
- files = response["files"]
- return [f for f in files if f["mimeType"] == FOLDER_MIME_TYPE]
-
- def obtain_folder(
- self,
- name,
- fields=DEFAULT_FILE_FIELDS,
- parents=None,
- list_kwargs=None,
- create_kwargs=None,
- delete_if_exists=False
- ):
- parents = parents or []
- list_kwargs = list_kwargs or {}
- create_kwargs = create_kwargs or {}
-
- q_mime = f"mimeType = '{FOLDER_MIME_TYPE}'"
- q_name = f"name = '{name}'"
-
- response = self.list_files(
- q=f"{q_mime} and {q_name}",
- spaces="drive",
- fields=f"files({fields})",
- **list_kwargs,
- )
-
- if response["files"]:
- if delete_if_exists:
- for file in response["files"]:
- self.delete_file(file["id"])
- else:
- return response["files"][0]
-
- body = {
- "mimeType": FOLDER_MIME_TYPE,
- "name": name,
- "parents": parents,
- }
- body.update(create_kwargs.pop("body", {}))
-
- return self.create_file(
- body=body,
- fields=fields,
- **create_kwargs,
- )
-
- def create_file(self, **kwargs):
- return self._service.files().create(**kwargs).execute()
-
- def upload_docx(self, body, path, **kwargs):
- media = MediaFileUpload(path, mimetype=DOCX_MIME_TYPE)
- return self._service.files().create(
- body=body,
- media_body=media,
- fields=DEFAULT_FILE_FIELDS,
- **kwargs,
- ).execute()
-
- def delete_file(self, file_id):
- return self._service.files().delete(fileId=file_id).execute()
-
- # --------------------------------------------------------------------------
- # Permissions
-
- def list_permissions(self, file_id):
- return self._service.permissions().list(fileId=file_id).execute()
-
- def create_permission(self, file_id, **kwargs):
- return self._service.permissions().create(
- fileId=file_id,
- **kwargs,
- ).execute()
-
- def delete_permissions(self, file_id, permission_id):
- return self._service.permissions().delete(
- fileId=file_id,
- permissionId=permission_id,
- ).execute()
-
- # --------------------------------------------------------------------------
- # Sharing
-
- def read_share_folder_with_anyone(self, folder_id):
- response = self.list_permissions(file_id=folder_id)
- for perm in response["permissions"]:
- if perm["type"] == "anyone":
- break
- else:
- self.create_permission(
- body={
- "role": "reader",
- "type": "anyone",
- },
- file_id=folder_id,
- )
diff --git a/watchdoc/src/main.py b/watchdoc/src/main.py
index f74393710..bbda805e1 100644
--- a/watchdoc/src/main.py
+++ b/watchdoc/src/main.py
@@ -2,23 +2,15 @@
import os
import zipfile
-from fastapi import (
- FastAPI,
- Response,
- BackgroundTasks,
-)
+from fastapi import BackgroundTasks, FastAPI, Response
+from fastapi.responses import FileResponse
-from service import WatchDocService
+from config import DEBUG, GENERATED_DIR, TEMP_DIR, WATCHDOC_PORT
from proto import Applicant
-from config import (
- TEMP_DIR,
- WATCHDOC_PORT,
- DEBUG, GENERATED_DIR,
-)
+from service import WatchDocService
from typing import List
-from fastapi.responses import FileResponse
log.basicConfig(
level="INFO",
@@ -53,8 +45,7 @@ def get_docs(applicants: list[Applicant], background_tasks: BackgroundTasks):
email = applicant.contact_info.corporate_email
log.info(f"{email}: generating documents...")
- wds.generate_documents(applicant,
- docs=[("applicant-form.docx", "Анкета абитуриента.docx")])
+ wds.generate_documents(applicant, docs=[("applicant-form.docx", "Анкета абитуриента.docx")])
docs_zip = zipfile.ZipFile(TEMP_DIR / "docs.zip", "w")
diff --git a/watchdoc/src/proto.py b/watchdoc/src/proto.py
index 664b360cf..6e1acd278 100644
--- a/watchdoc/src/proto.py
+++ b/watchdoc/src/proto.py
@@ -80,4 +80,3 @@ class Applicant(Person):
recruitment_office: str
milspecialty: Milspecialty
-
diff --git a/watchdoc/src/service.py b/watchdoc/src/service.py
index 60dbe1bb2..af7433087 100644
--- a/watchdoc/src/service.py
+++ b/watchdoc/src/service.py
@@ -1,15 +1,12 @@
import base64
+from pathlib import PurePath
import logging as log
import jinja2
-import shutil
-
from docxtpl import DocxTemplate
-from auth import obtain_credentials
from email_service import EmailService
-from drive import DriveService
from campuses import Campus
from family import RelativeType
from proto import Applicant
@@ -21,6 +18,7 @@
EMAIL_HOST_USER,
EMAIL_HOST_PASSWORD,
EMAIL_USE_TLS,
+ YADISK_TOKEN,
)
from email_utils import create_message
@@ -29,10 +27,11 @@
month_to_russian_title,
today,
)
+from src.disk import DiskService
-WATCHDOC_FOLDER: str = "watchdoc"
-APPLICANTS_FOLDER: str = "Абитуриенты"
-CAMPUSES_FOLDER: str = "Кампусы"
+WATCHDOC_FOLDER: PurePath = PurePath("watchdoc")
+APPLICANTS_FOLDER: PurePath = WATCHDOC_FOLDER / "Абитуриенты"
+CAMPUSES_FOLDER: PurePath = APPLICANTS_FOLDER / "Кампусы"
DUMMY_IMAGE = TEMPLATES_DIR / "dummy.png"
@@ -56,21 +55,19 @@ class WatchDocService:
folder_link = wds.upload_documents(applicant)
wds.notify(applicant, folder_link)
"""
- def generate_documents(self, applicant: Applicant, docs=DOCUMENTS):
- applicant_path = applicant.full_name + ' ' +\
- applicant.contact_info.corporate_email
- applicant_dir = GENERATED_DIR / applicant_path
- if applicant_dir.exists():
- shutil.rmtree(applicant_dir, ignore_errors=True)
+ def generate_documents(self, applicant: Applicant):
+ applicant_dir = GENERATED_DIR / applicant.contact_info.corporate_email
applicant_dir.mkdir(exist_ok=True)
parents = [
- r for r in applicant.family
+ r
+ for r in applicant.family
if r.type in [RelativeType.FATHER, RelativeType.MOTHER]
]
siblings = [
- r for r in applicant.family
+ r
+ for r in applicant.family
if r.type in [RelativeType.BROTHER, RelativeType.SISTER]
]
@@ -89,7 +86,7 @@ def generate_documents(self, applicant: Applicant, docs=DOCUMENTS):
**data,
}
- for (en, rus) in docs:
+ for (en, rus) in DOCUMENTS:
doc = DocxTemplate(TEMPLATES_DIR / en)
doc.render(context, self.jinja_env)
doc.replace_media(DUMMY_IMAGE, photo_path)
@@ -101,26 +98,18 @@ def upload_documents(self, applicant: Applicant):
folder_name = f"{applicant.full_name} {email}"
applicant_folder = self.ds.obtain_folder(
- name=folder_name,
- parents=[self._campus_folders[campus]["id"]],
- delete_if_exists=True
+ self._campus_folders[campus] / folder_name
)
for (_, rus) in DOCUMENTS:
- body = {
- "name": rus,
- "parents": [applicant_folder["id"]],
- }
self.ds.upload_docx(
- body=body,
- path=GENERATED_DIR / folder_name / rus,
+ local_path=GENERATED_DIR / email / rus,
+ remote_path=applicant_folder / rus,
+ overwrite=True,
)
- self.ds.read_share_folder_with_anyone(
- folder_id=applicant_folder["id"],
- )
-
- return applicant_folder["webViewLink"]
+ link = self.ds.read_share_folder_with_anyone(applicant_folder)
+ return link
def notify(self, applicant: Applicant, folder_link: str):
email = applicant.contact_info.corporate_email
@@ -134,20 +123,13 @@ def __init__(self) -> None:
# Templates
self.jinja_env = self._init_jinja_env()
- # Credentials
- credentials = obtain_credentials()
-
# Email
self.email_service = EmailService(
- EMAIL_HOST,
- EMAIL_PORT,
- EMAIL_HOST_USER,
- EMAIL_HOST_PASSWORD,
- EMAIL_USE_TLS
+ EMAIL_HOST, EMAIL_PORT, EMAIL_HOST_USER, EMAIL_HOST_PASSWORD, EMAIL_USE_TLS
)
- # Drive
- self.ds = DriveService(credentials)
+ # Disk
+ self.ds = DiskService(token=YADISK_TOKEN)
self._watchdoc_folder = self._init_watchdoc_folder()
self._applicants_folder = self._init_applicants_folder()
self._campuses_folder = self._init_campuses_folder()
@@ -160,34 +142,22 @@ def _init_jinja_env(self):
return env
def _init_watchdoc_folder(self):
- folder = self.ds.obtain_folder(name=WATCHDOC_FOLDER)
- log.info(f"`{WATCHDOC_FOLDER}` web view link: "
- f"{folder['webViewLink']}")
+ folder = self.ds.obtain_folder(WATCHDOC_FOLDER)
+ log.info(f"Created applicant folder in `{folder}`")
return folder
def _init_applicants_folder(self):
- folder = self.ds.obtain_folder(
- name=APPLICANTS_FOLDER,
- parents=[self._watchdoc_folder["id"]],
- )
- log.info(f"`{APPLICANTS_FOLDER}` web view link: "
- f"{folder['webViewLink']}")
+ folder = self.ds.obtain_folder(APPLICANTS_FOLDER)
+ log.info(f"Created applicant folder in `{folder}`")
return folder
def _init_campuses_folder(self):
- folder = self.ds.obtain_folder(
- name=CAMPUSES_FOLDER,
- parents=[self._applicants_folder["id"]],
- )
- log.info(f"`{CAMPUSES_FOLDER}` web view link: "
- f"{folder['webViewLink']}")
+ folder = self.ds.obtain_folder(CAMPUSES_FOLDER)
+ log.info(f"Created applicant folder in `{folder}`")
return folder
def _init_campus_folders(self):
folders = {}
for abbr, campus in Campus.choices():
- folders[abbr] = self.ds.obtain_folder(
- name=campus,
- parents=[self._campuses_folder["id"]],
- )
+ folders[abbr] = self.ds.obtain_folder(CAMPUSES_FOLDER / campus)
return folders