Merge remote-tracking branch 'origin/key_field' into key_field

remotes/origin/key_field
Andrey 8 years ago
commit 488f29588c
  1. 2
      .gitlab-ci.yml
  2. 106
      Envoy.blade.php
  3. 4
      access/models/user.py
  4. 8
      access/views.py
  5. 14
      courses/api.py
  6. 4
      courses/models.py
  7. 4
      finance/models.py
  8. 2
      finance/signals.py
  9. 1
      finance/tasks.py
  10. 29
      finance/views.py
  11. 5
      lms/celery.py
  12. 20
      progress/migrations/0011_auto_20180427_1213.py
  13. 20
      progress/migrations/0012_auto_20180427_1220.py
  14. 22
      progress/migrations/0013_progresslesson_dif_check_timestamps.py
  15. 25
      progress/models.py
  16. 53
      progress/tasks.py
  17. 2
      progress/urls.py
  18. 145
      progress/views.py

@ -15,7 +15,7 @@ deploy_branch:
- ssh-add <(echo "$SSH_PRIVATE_KEY") - ssh-add <(echo "$SSH_PRIVATE_KEY")
- mkdir -p ~/.ssh - mkdir -p ~/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- ~/.composer/vendor/bin/envoy run deploy --cleanup --branch="$CI_BUILD_REF_SLUG" - ~/.composer/vendor/bin/envoy run deploydev --cleanup --branch="$CI_BUILD_REF_SLUG"
environment: environment:
name: production name: production
url: https://go.skillbox.ru url: https://go.skillbox.ru

@ -1,4 +1,5 @@
@servers(['localhost' => 'deployer@192.168.0.13']) @servers(['localhost' => 'deployer@192.168.0.13'])
@servers(['dev' => 'deployer@192.168.0.5'])
@setup @setup
$repository = 'ssh://git@g.skillbox.ru:22004/kor_a_m/skill-back.git'; $repository = 'ssh://git@g.skillbox.ru:22004/kor_a_m/skill-back.git';
@ -13,72 +14,83 @@
create_symlinks create_symlinks
install_req install_req
run_tests run_tests
run_migrate
update_symlinks update_symlinks
deployment_option_cleanup deployment_option_cleanup
@endstory @endstory
@task('clone_repository', ['on' => 'localhost']) @story('deploydev', ['on' => 'dev'])
echo '>> Клонируем репозиторий' clone_repository
create_symlinks
install_req
run_tests
run_migrate
update_symlinks
deployment_option_cleanup
@endstory
echo '{{ $new_release_dir }}' @task('clone_repository')
[ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }} echo '>> Клонируем репозиторий'
@if ($branch)
git clone -b {{ $branch }} {{ $repository }} {{ $new_release_dir }} echo '{{ $new_release_dir }}'
@endif [ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }}
@if ($branch)
git clone -b {{ $branch }} {{ $repository }} {{ $new_release_dir }}
@endif
@endtask @endtask
@task('create_symlinks', ['on' => 'localhost']) @task('create_symlinks')
echo '>> Создание симлинков' echo '>> Создание симлинков'
ln -s {{ $app_dir }}/configs/master/ssl {{ $new_release_dir }}/ssl ln -s {{ $app_dir }}/configs/master/ssl {{ $new_release_dir }}/ssl
@if ($branch == 'master') @if ($branch == 'master')
ln -nfs {{ $app_dir }}/configs/master/prod.env {{ $new_release_dir }}/config_app/settings/local.env ln -nfs {{ $app_dir }}/configs/master/prod.env {{ $new_release_dir }}/config_app/settings/local.env
ln -nfs {{ $app_dir }}/media/master {{ $new_release_dir }}/media ln -nfs {{ $app_dir }}/media/master {{ $new_release_dir }}/media
@endif @endif
@if ($branch == 'dev') @if ($branch == 'dev')
ln -nfs {{ $app_dir }}/configs/dev/dev.env {{ $new_release_dir }}/config_app/settings/local.env ln -nfs {{ $app_dir }}/configs/dev/dev.env {{ $new_release_dir }}/config_app/settings/local.env
ln -nfs {{ $app_dir }}/media/master {{ $new_release_dir }}/media ln -nfs {{ $app_dir }}/media/master {{ $new_release_dir }}/media
@endif @endif
ln -nfs {{ $app_dir }}/logs/{{ $branch }} {{ $new_release_dir }}/logs ln -nfs {{ $app_dir }}/logs/{{ $branch }} {{ $new_release_dir }}/logs
@endtask @endtask
@task('install_req', ['on' => 'localhost']) @task('install_req')
echo '>> Подтягиваем зависимости' echo '>> Подтягиваем зависимости'
@if ($branch) @if ($branch)
cd {{ $new_release_dir }} cd {{ $new_release_dir }}
source /env/bin/activate && pip install -r requirements.txt source /env/bin/activate && pip install -r requirements.txt
@endif @endif
@endtask @endtask
@task('run_tests', ['on' => 'localhost']) @task('run_tests')
echo '>> Запускаем тесты' echo '>> Запускаем тесты'
@if ($branch) @if ($branch)
cd {{ $new_release_dir }} cd {{ $new_release_dir }}
source /env/bin/activate && python manage.py test source /env/bin/activate && python manage.py test
@endif @endif
@endtask @endtask
@task('run_migrate', ['on' => 'localhost']) @task('run_migrate')
echo '>> Запускаем миграции' echo '>> Запускаем миграции'
@if ($branch) @if ($branch)
cd {{ $new_release_dir }} cd {{ $new_release_dir }}
source /env/bin/activate && python manage.py migrate source /env/bin/activate && python manage.py migrate
@endif @endif
@endtask @endtask
@task('update_symlinks', ['on' => 'localhost']) @task('update_symlinks')
@if ($branch) @if ($branch)
ln -nfs {{ $app_dir }}/configs/{{ $branch }}/wsgi.py {{ $new_release_dir }}/lms/wsgi.py ln -nfs {{ $app_dir }}/configs/{{ $branch }}/wsgi.py {{ $new_release_dir }}/lms/wsgi.py
echo '>> Создаем симлинк на папку текущего релиза' echo '>> Создаем симлинк на папку текущего релиза'
ln -nfs {{ $new_release_dir }} {{ $app_dir }}/current_{{ $branch }} ln -nfs {{ $new_release_dir }} {{ $app_dir }}/current_{{ $branch }}
echo '>> restart Supervisor' echo '>> restart Supervisor'
supervisorctl restart {{ $branch }}: supervisorctl restart {{ $branch }}:
@endif @endif
@endtask @endtask
@task('deployment_option_cleanup') @task('deployment_option_cleanup')
cd {{ $releases_dir }} cd {{ $releases_dir }}
@if ( isset($cleanup) && $cleanup ) @if ( isset($cleanup) && $cleanup )
find . -maxdepth 1 -name "20*{{ $branch }}" -mmin +30 | head -n 3 | xargs rm -Rf find . -maxdepth 1 -name "20*{{ $branch }}" -mmin +30 | head -n 3 | xargs rm -Rf
echo "Cleaned up old deployments" echo "Cleaned up old deployments"
@endif @endif
@endtask @endtask

@ -71,8 +71,8 @@ class CustomUserManager(BaseUserManager):
body = { body = {
"subject": 'Спасибо за регистрацию', "subject": 'Спасибо за регистрацию',
"body": ''' "body": '''
Вы были успешны зарегистрированны на портале go.skillbox.ru Вы были успешно зарегистрированы на портале go.skillbox.ru
ваш пароль (он будет дествителен после активации по ссылке) %s ваш пароль (он будет действителен после активации по ссылке) %s
для подтверждения регистрации перейдите по ссылке для подтверждения регистрации перейдите по ссылке
%s/api/v1/users/registration/?hash=%s''' % (password, settings.DOMAIN, invite.hash), %s/api/v1/users/registration/?hash=%s''' % (password, settings.DOMAIN, invite.hash),
"from_email": 'robo@skillbox.ru', "from_email": 'robo@skillbox.ru',

@ -83,10 +83,10 @@ class ResetPasswordView(APIView):
subject="Сброс пароля", subject="Сброс пароля",
message=''' message='''
Ваш новый пароль (может быть использован после перехода по ссылке) %s, Ваш новый пароль (может быть использован после перехода по ссылке) %s,
(в последствии вы сможите сменить его в личном кабинете), (в последствии вы сможете сменить его в личном кабинете),
если вы не отправляли заявку на сброс пароля просто проигнорируйте это сообщение, если вы не отправляли заявку на сброс пароля просто проигнорируйте это сообщение,
для подтверждения смены пароля перейдите по %s/api/v1/users/reset/?hash=%s для подтверждения смены пароля перейдите по %s/api/v1/users/reset/?hash=%s
(ссылке ссылка действительна в течении одного дня)''' % (invite.password, settings.DOMAIN, invite.hash), ссылке (ссылка действительна в течении одного дня)''' % (invite.password, settings.DOMAIN, invite.hash),
from_email='robo@skillbox.ru', from_email='robo@skillbox.ru',
recipient_list=[user.email], recipient_list=[user.email],
) )
@ -314,7 +314,7 @@ class ManagementPassword(generics.GenericAPIView):
password = ''.join(random.choice(string.ascii_letters) for _x in range(8)) password = ''.join(random.choice(string.ascii_letters) for _x in range(8))
try: try:
user = get_user_model().objects.get(email=email) user = get_user_model().objects.get(email=email.lower())
except get_user_model().DoesNotExist: except get_user_model().DoesNotExist:
return Response('user not found', status=404) return Response('user not found', status=404)
@ -327,7 +327,7 @@ class ManagementPassword(generics.GenericAPIView):
msg = EmailMessage( msg = EmailMessage(
subject='Установлен новый пароль', subject='Установлен новый пароль',
body='''Ваш новый пароль %s body='''Ваш новый пароль %s
(в последствии вы сможите сменить его в личном кабинете).''' % password, (в последствии вы сможете сменить его в личном кабинете).''' % password,
from_email='robo@skillbox.ru', from_email='robo@skillbox.ru',
to=[user.email], to=[user.email],
bcc=[request.user.email], bcc=[request.user.email],

@ -1,4 +1,4 @@
from courses.models import Course from courses.models import Course, Lesson
class InApiTeacher: class InApiTeacher:
@ -53,7 +53,15 @@ class CourseProgressApi:
class CourseParamsApi: class CourseParamsApi:
def __init__(self, token): def __init__(self, token):
self.course = Course.objects.get(token=token) self.__course = Course.objects.get(token=token)
def get_slug_and_title(self): def get_slug_and_title(self):
return {"title": self.course.title, "slug": self.course.slug} return {"title": self.__course.title, "slug": self.__course.slug}
def get_length(self):
return self.__course.topic_set.count()
def get_topic_by_lesson(self, lesson_token):
for idx, topic in enumerate(self.__course.topic_set.all()):
if topic.lesson_set.filter(token=lesson_token).exists():
return {"title": topic.title, "idx": idx}

@ -77,6 +77,7 @@ class CourseManager(models.Manager):
level=None, direction=None, **kwargs): level=None, direction=None, **kwargs):
slug = slug if slug else slugify(unidecode.unidecode(kwargs['title'])) slug = slug if slug else slugify(unidecode.unidecode(kwargs['title']))
old_slug = slug if old_slug is None else old_slug
kwargs['teacher_tokens'] = teacher_tokens kwargs['teacher_tokens'] = teacher_tokens
@ -109,7 +110,8 @@ class CourseManager(models.Manager):
course.slug = slug course.slug = slug
course.save() course.save()
except ObjectDoesNotExist: # TODO Костылище
except Exception:
kwargs['slug'] = slug kwargs['slug'] = slug
course = self.create(**kwargs) course = self.create(**kwargs)

@ -44,8 +44,8 @@ class Bill(models.Model):
}) })
msg = EmailMessage( msg = EmailMessage(
'Вы откозались от оплаты по счёту', 'Вы отказались от оплаты по счёту',
"""Вы откозались от оплаты по счёту. """Вы отказались от оплаты по счёту.
Вы сможете продолжить оплату в личном кабинете""", Вы сможете продолжить оплату в личном кабинете""",
to=[self.user.email], to=[self.user.email],
bcc=[self.opener.email], bcc=[self.opener.email],

@ -58,7 +58,7 @@ def invoice_signal(instance, **kwargs):
if instance.status == 'C': if instance.status == 'C':
msg = EmailMessage( msg = EmailMessage(
'Ошибка платежа!' 'Ошибка платежа!'
"""Внимание не прошёл платёж пользавателю %s, """Внимание не прошёл платёж пользавателя %s,
по курсу "%s" ID платежа: %s. Если не получается по курсу "%s" ID платежа: %s. Если не получается
решить проблему самостоятельно, ответьте на это письмо, решить проблему самостоятельно, ответьте на это письмо,
постарайтесь подробно описать последовательность действий, постарайтесь подробно описать последовательность действий,

@ -1,4 +1,3 @@
import logging
import os import os
import requests import requests

@ -187,6 +187,7 @@ class InvoiceDetailView(APIView):
) )
if invoice.status == "F": if invoice.status == "F":
invoice.real_price = invoice.real_price if real_price is None else real_price
return Response(InvoiceSerializer(invoice).data, status=200) return Response(InvoiceSerializer(invoice).data, status=200)
invoice.real_price = real_price invoice.real_price = real_price
@ -205,19 +206,21 @@ class InvoiceDetailView(APIView):
if pay_count > 1: if pay_count > 1:
invoice.create_child_pays(pay_count) invoice.create_child_pays(pay_count)
if invoice.method == 'Y' and invoice.yandex_pay is None: if invoice.method == 'Y' and invoice.status == 'P':
yandex_pay = Payment( if invoice.yandex_pay is None:
order_amount=invoice.price, yandex_pay = Payment(
shop_amount=0, order_amount=invoice.price,
customer_number=bill.user.id, shop_amount=0,
user=bill.user, customer_number=bill.user.id,
cps_email=bill.user.email, user=bill.user,
) cps_email=bill.user.email,
if pay_count > 1: )
yandex_pay.shop_id = settings.YANDEX_MONEY_REBILLING_SHOP_ID if pay_count > 1:
yandex_pay.scid = settings.YANDEX_MONEY_REBILLING_SCID yandex_pay.shop_id = settings.YANDEX_MONEY_REBILLING_SHOP_ID
yandex_pay.save() yandex_pay.scid = settings.YANDEX_MONEY_REBILLING_SCID
invoice.yandex_pay = yandex_pay yandex_pay.save()
invoice.yandex_pay = yandex_pay
invoice.send_link() invoice.send_link()
context = { context = {

@ -21,9 +21,12 @@ register_signal(client)
app.conf.beat_schedule = { app.conf.beat_schedule = {
'periodic_billing': { 'periodic_billing': {
# 'schedule': crontab(day_of_month='1', hour='1', minute='1'), # заглушка на время отладки
'schedule': crontab(minute='0', hour='*/3',), 'schedule': crontab(minute='0', hour='*/3',),
'task': 'finance.tasks.periodic_billing' 'task': 'finance.tasks.periodic_billing'
},
'statistic_teachers': {
'schedule': crontab(hour='16', minute='0'),
'task': 'progress.tasks.statistic_teachers'
} }
} }

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-04-27 12:13
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('progress', '0010_progress_exp_date'),
]
operations = [
migrations.AlterField(
model_name='progresslesson',
name='last_update',
field=models.DateTimeField(verbose_name='Дата последней проверки'),
),
]

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-04-27 12:20
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('progress', '0011_auto_20180427_1213'),
]
operations = [
migrations.AlterField(
model_name='progresslesson',
name='last_update',
field=models.DateTimeField(auto_now_add=True, verbose_name='Дата последней здачи'),
),
]

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-04-27 12:26
from __future__ import unicode_literals
import django.contrib.postgres.fields
from django.db import migrations, models
import lms.tools
class Migration(migrations.Migration):
dependencies = [
('progress', '0012_auto_20180427_1220'),
]
operations = [
migrations.AddField(
model_name='progresslesson',
name='dif_check_timestamps',
field=django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(verbose_name='Время проверки'), default=lms.tools.get_empty_list, size=None, verbose_name='Временные интервалы проверок'),
),
]

@ -3,6 +3,7 @@ from django.contrib.postgres.fields import ArrayField
from django.db import models from django.db import models
from model_utils import Choices from model_utils import Choices
from django.utils import timezone
from lms.tools import get_empty_list from lms.tools import get_empty_list
@ -55,8 +56,30 @@ class ProgressLesson(models.Model):
finish_date = models.DateTimeField(verbose_name='Дата зачтения задания', blank=True, null=True) finish_date = models.DateTimeField(verbose_name='Дата зачтения задания', blank=True, null=True)
start_date = models.DateTimeField(verbose_name='Дата начала прохождения задания', auto_now_add=True) start_date = models.DateTimeField(verbose_name='Дата начала прохождения задания', auto_now_add=True)
status = models.CharField(choices=STATUSES, default=STATUSES.start, max_length=20) status = models.CharField(choices=STATUSES, default=STATUSES.start, max_length=20)
last_update = models.DateTimeField(verbose_name='Дата последнего изменения', auto_now=True) last_update = models.DateTimeField(verbose_name='Дата последней сдачи', auto_now_add=True)
comment_tokens = ArrayField(models.UUIDField(verbose_name="Токен комента", editable=False), default=get_empty_list) comment_tokens = ArrayField(models.UUIDField(verbose_name="Токен комента", editable=False), default=get_empty_list)
dif_check_timestamps = ArrayField(
models.IntegerField(
verbose_name="Время проверки"
),
verbose_name='Временные интервалы проверок',
default=get_empty_list,
)
def __init__(self, *args, **kwargs):
super(ProgressLesson, self).__init__(*args, **kwargs)
self.old_status = self.status
def save(self, force_insert=False, force_update=False, **kwargs):
if self.status == 'wait' and (self.old_status == 'start' or self.old_status == 'fail'):
self.last_update = timezone.now()
if self.old_status == 'wait' and (self.status == 'fail' or self.status == 'done'):
dif = int(timezone.now().timestamp() - self.last_update.timestamp())
self.dif_check_timestamps.append(dif)
super(ProgressLesson, self).save(force_insert, force_update, **kwargs)
self.old_status = self.status
def __str__(self): def __str__(self):
return self.progress.user.email return self.progress.user.email

@ -1,6 +1,16 @@
from courses.api import CourseProgressApi from dateutil.relativedelta import relativedelta
from courses.api import CourseProgressApi, CourseParamsApi
from progress.models import ProgressLesson from progress.models import ProgressLesson
from django.utils import timezone
import os
import csv
from lms import celery_app
from django.conf import settings
from django.core.mail import EmailMessage
def add_next_lesson(progress): def add_next_lesson(progress):
lesson_list = CourseProgressApi.get_next( lesson_list = CourseProgressApi.get_next(
@ -25,3 +35,44 @@ def add_next_lesson(progress):
) )
return pl return pl
@celery_app.task
def statistic_teachers():
file_dir = "%s/analytics/teachers/" % (settings.MEDIA_ROOT,)
os.makedirs(os.path.dirname(file_dir), exist_ok=True)
# TODO Почему-то при выставлении расширения всё ломается
path = '%s%s' % (file_dir, timezone.now().strftime('%Y-%m-%d__%H-%M'))
progresses = ProgressLesson.objects.filter(status='wait')
with open(path, 'w') as csv_file:
csv_writer = csv.writer(csv_file)
csv_writer.writerow(
['Курс', 'Препод', 'Ранее', '6 Дней', '5 Дней', "4 Дня", "3 Дня", "2 Дня", "Вчера", "Сегодня"])
for i in progresses.values('progress__teacher__email', 'progress__course_token').distinct():
row = []
course_token = i['progress__course_token']
teacher_email = i['progress__teacher__email']
progresses_sub = progresses.filter(progress__course_token=course_token, progress__teacher__email=teacher_email)
row.append(CourseParamsApi(course_token).get_slug_and_title()['title'])
row.append(teacher_email)
row.append(progresses_sub.filter(last_update__lt=(timezone.now() - relativedelta(days=7))).count())
for j in range(7):
from_d = timezone.now() - relativedelta(days=(7 - j))
to_d = timezone.now() - relativedelta(days=(6 - j))
row.append(progresses_sub.filter(last_update__gte=from_d, last_update__lt=to_d).count())
csv_writer.writerow(row)
message = EmailMessage(
'Выгрузка по преподам.',
'Файл приложен',
'robo@skillbox.ru',
[settings.SUPPORT_EMAIL,],
['andrey.korolev@skillbox.ru',],
reply_to=['andrey.korolev@skillbox.ru'],
)
message.attach_file(path)
message.send()

@ -4,6 +4,8 @@ from progress import views
urlpatterns = [ urlpatterns = [
url(r'progress_dynamic/$', views.CourseProgressDynamicView.as_view()), url(r'progress_dynamic/$', views.CourseProgressDynamicView.as_view()),
url(r'progress_table_detail/$', views.ProgressTableDetailView.as_view()),
url(r'progress_table/$', views.CourseProgressTableView.as_view()),
url(r'students/(?P<teacher_token>[0-9A-Fa-f-]+)/$', views.StudentWorkView.as_view()), url(r'students/(?P<teacher_token>[0-9A-Fa-f-]+)/$', views.StudentWorkView.as_view()),
url(r'student/$', views.StudentUpdateProgress.as_view()), url(r'student/$', views.StudentUpdateProgress.as_view()),
url(r'find/$', views.FindProgressView.as_view()), url(r'find/$', views.FindProgressView.as_view()),

@ -15,14 +15,16 @@ from rest_framework.permissions import IsAuthenticated
from rest_framework.renderers import JSONRenderer from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from django.db.models import Q, Count from django.db.models import Q, Count, F
import jwt import jwt
from django.conf import settings from django.conf import settings
from django.db.models.functions import Cast
from django.db.models.fields import DateField
from access.serializers import UserProgressSearchSerializer from access.serializers import UserProgressSearchSerializer
from courses.models import Course, Lesson from courses.models import Course, Lesson
from progress.models import ProgressLesson, Progress from progress.models import ProgressLesson, Progress
from progress.serializers import ProgressAnalyticSerializer, ProgressLessonSerializer, ProgressSerializer, \ from progress.serializers import ProgressAnalyticSerializer, ProgressLessonSerializer, \
SecureProgressSerializer SecureProgressSerializer
from courses.api import CourseProgressApi, CourseParamsApi from courses.api import CourseProgressApi, CourseParamsApi
from progress.tasks import add_next_lesson from progress.tasks import add_next_lesson
@ -65,27 +67,134 @@ class CourseProgressDynamicView(APIView):
@staticmethod @staticmethod
def get(request): def get(request):
course_token = request.GET.get('course_token', None) course_token = request.GET.get('course_token', '')
teacher_email = request.GET.get('teacher_email', '').lower()
from_date = int(request.GET.get('from', '7')) from_date = int(request.GET.get('from', '7'))
to_date = int(request.GET.get('to', '0')) to_date = int(request.GET.get('to', '0'))
only_hw = request.GET.get('only_hw', 'yes') == 'yes'
if request.user.is_authenticated() and request.user.is_staff: if request.user.is_authenticated() and request.user.is_staff:
progresses = ProgressLesson.objects.filter(status='done') progresses = ProgressLesson.objects.all()
if not course_token is None: waiters = ProgressLesson.objects.filter(status='wait')
try:
progresses = progresses.filter(progress__course_token=course_token) if only_hw:
except ProgressLesson.DoesNotExist: progresses = progresses.filter(checker=F('progress__teacher'))
return Response('incorrect course token', status=404)
if not course_token is '':
progresses = progresses.filter(progress__course_token=course_token)
progresses = progresses.filter(finish_date__gt=timezone.now() - relativedelta(days=from_date)) if not teacher_email is '':
progresses = progresses.filter(finish_date__lt=timezone.now() - relativedelta(days=to_date)) waiters = waiters.filter(checker__email=teacher_email)
progresses = progresses.order_by('finish_date') progresses = progresses.filter(checker__email=teacher_email)
res = {} res = {}
progresses = progresses.extra({'finish_date_day': "date(finish_date)"})\
.values('progress__course_token', 'finish_date_day')\ progresses_finish = progresses.filter(finish_date__gt=timezone.now() - relativedelta(days=from_date))
.annotate(total=Count('progress')) progresses_finish = progresses_finish.filter(finish_date__lt=timezone.now() - relativedelta(days=to_date))
#TODO Доделать
progresses_start = progresses.filter(start_date__gt=timezone.now() - relativedelta(days=from_date))
progresses_start = progresses_start.filter(start_date__lt=timezone.now() - relativedelta(days=to_date))
progresses_finish = progresses_finish.annotate(name=Cast('finish_date', DateField())) \
.values('name') \
.annotate(value=Count('id'))
progresses_start = progresses_start.annotate(name=Cast('start_date', DateField())) \
.values('name') \
.annotate(value=Count('id'))
progresses_finish = progresses_finish.order_by('name')
progresses_start = progresses_start.order_by('name')
res['finish'] = progresses_finish
res['start'] = progresses_start
res['in_progress'] = waiters.count()
return Response(res, status=200)
return Response('Доступно только персоналу', status=403)
class CourseProgressTableView(APIView):
renderer_classes = (JSONRenderer,)
@staticmethod
def get(request):
from_date = request.GET.get('from', None)
to_date = request.GET.get('to', None)
if request.user.is_authenticated() and request.user.is_staff:
progresses = ProgressLesson.objects.filter(checker=F('progress__teacher'), progress__only_watch=False) \
.exclude(status='done')
if from_date is not None:
date = timezone.now() - relativedelta(days=int(from_date))
progresses = progresses.filter(progress__user__useractivity__last_request__gt=date)
if to_date is not None:
date = timezone.now() - relativedelta(days=int(to_date))
progresses = progresses.filter(progress__user__useractivity__last_request__lt=date)
res = {}
progresses = progresses.values('progress__course_token', 'lesson_token').annotate(count=Count('id'))
course_tokens = progresses.values('progress__course_token').distinct()
res['val'] = []
res['max_length'] = 0
for course_token in course_tokens:
ct = course_token['progress__course_token']
api = CourseParamsApi(ct)
course_title = api.get_slug_and_title()['title']
course_length = api.get_length()
res['max_length'] = course_length if course_length > res['max_length'] else res['max_length']
f_progresses = progresses.filter(progress__course_token=ct)
res_elem = {
"course_title": course_title,
"course_token": course_token['progress__course_token'],
'stat': [0 for _i in range(course_length)],
"sum": 0
}
res['val'].append(res_elem)
for i in f_progresses:
topic = api.get_topic_by_lesson(i['lesson_token'])
if topic is not None:
res_elem['stat'][topic['idx']] += i["count"]
res_elem['sum'] += i["count"]
return Response(res, status=200)
return Response('Доступно только персоналу', status=403)
class ProgressTableDetailView(APIView):
renderer_classes = (JSONRenderer,)
@staticmethod
def get(request):
course_token = request.GET.get('course_token', None)
topic_idx = request.GET.get('topic_idx', None)
if course_token is None:
return Response('Должен быть передан токен курса', 400)
if request.user.is_authenticated() and request.user.is_staff:
progresses = ProgressLesson.objects.filter(
checker=F('progress__teacher'),
progress__only_watch=False,
progress__course_token=course_token,
) \
.exclude(status='done')
api = CourseParamsApi(course_token)
res = []
for progress in progresses:
topic = api.get_topic_by_lesson(progress.lesson_token)
if topic_idx is not None and not int(topic_idx)-1 == topic['idx']:
continue
res.append({
'email': progress.progress.user.email,
'name': progress.progress.user.get_full_name(),
'status': progress.status,
'last_request': progress.progress.user.useractivity.last_request,
'topic_title': topic['title'],
})
return Response(res, status=200) return Response(res, status=200)
return Response('Доступно только персоналу', status=403) return Response('Доступно только персоналу', status=403)
@ -180,7 +289,7 @@ class TeacherUpdateProgress(APIView):
res = {"current": ProgressLessonSerializer(pv).data} res = {"current": ProgressLessonSerializer(pv).data}
if pv.status == ProgressLesson.STATUSES.done: if pv.status == ProgressLesson.STATUSES.done:
# TODO: Ассинхроннаязадача для celery # TODO: Ассинхронная задача для celery
res['next'] = ProgressLessonSerializer(add_next_lesson(p)).data res['next'] = ProgressLessonSerializer(add_next_lesson(p)).data
return Response(res, status=200) return Response(res, status=200)
@ -337,7 +446,7 @@ class ChangeTeacherView(APIView):
except get_user_model().DoesNotExist: except get_user_model().DoesNotExist:
return Response("Нет пользователя c таким email", status=404) return Response("Нет пользователя c таким email", status=404)
p.teacher = teacher p.teacher = teacher
p.progresslesson_set.filter(status=ProgressLesson.STATUSES.wait).update(checker=teacher) p.progresslesson_set.exclude(status=ProgressLesson.STATUSES.done).update(checker=teacher)
p.save() p.save()
except Progress.DoesNotExist: except Progress.DoesNotExist:
return Response("не найден прогресс по заданному id", status=404) return Response("не найден прогресс по заданному id", status=404)

Loading…
Cancel
Save