diff --git a/README.md b/README.md index 3451111..a50f717 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,11 @@ #**SkillBox LMS** -Подробные шаги для проверки работоспособности CI +Нужно выполнить миграции и запустить скрипт update_db.py без параметров в модуле courses (скрипт на всякий случай пока не удалять) -1) Проверяем доступ к репозиторию кода https://gitlab.com/skillbox/go.skillbox.ru -2) Клонируем проект -3) Вносим изменения в код -4) Комитим изменения в новый branch (ОБЯЗАТЕЛЬНО в отдельный Branch) -4.1) Пушим изменения -5) Ждем выполнения создания тестового сайта https://gitlab.com/skillbox/go.skillbox.ru/pipelines -6) Сайт будет доступен по адресу .lms.test.spicycms.com +Фактически в структуре данных изменилось только то, что появилась новая табличка NormalMap. Скрипт собирает все treeview курсов и кладёт в эту табличку -**Правила для разработчика** +Появилась новая зависимость django_rest_framework. -* Разрабока ведется в отдельном брэнче -* После комита изменений можно проверить свою работу по следующему адресу: .lms.test.spicycms.com -* После проверки тестового сайта по имени брэнча, брэнч помечается к мерж реквесту -* Ответсвенный инженер производит код ревью и запускает/отклоняет мерж -* Если сайт запустился на демо сайте, код в ручом режиме можно обновить на продакшен сервере +Изменения никак не трогают старый функционал. -**Замечания** - -* Мерж реквест лучше производить в ветку develop(чтобы исключить случайного обновления кода на демо сайте) -* Внимание! Разработчик обязан проверить работоспособность тестового сайта по имени созданного брэнча .lms.test.spicycms.com -* Если необходимы тестовые данные разработчик их подготавливает дополнительно -* Если нет возможности добавить тестовые данные в авто режиме, разработчик добавляет инструкцию в README файл по запуску необходимых команд -(Под тестовыми данными подразумеваются фикстуры) +Имеет смысл пробежаться по middl diff --git a/_utils/check_course_journal.py b/_utils/check_course_journal.py index a82cc7b..04c60f2 100644 --- a/_utils/check_course_journal.py +++ b/_utils/check_course_journal.py @@ -2,10 +2,10 @@ import os import django -import sys +import sys import os -from datetime import datetime +from datetime import datetime sys.path.append("/var/www/skillbox") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings") @@ -16,48 +16,49 @@ from courses.models import Course from finance.models import Price from journals.models import TeacherJ, check_journal + def check_course_journal(course_id): - print('===========\nКурс: %s\n==============\n' % Course.objects.get(id=course_id).title) - - print('Начало проверки журнала курса') - global_start = start = datetime.now() - course = Course.objects.get(id=course_id) - course.build_map() - finish = datetime.now() - print('Конец проверки журнала курса. Длительность: %s мсек\n' % (finish-start).microseconds) - - print('Начало проверки услуг') - start = datetime.now() - all = Price.objects.filter(by_time=0, post_fire=0, course__id=course_id).count() - print('Количество услуг на проверку: %s' % all) - for i in Price.objects.filter(by_time=0, post_fire=0, course__id=course_id): - i.check_points() - finish = datetime.now() - print('Конец проверки услуг. Длительность: %s мсек\n' % (finish-start).microseconds) - - print('Начало очистки двойных журналов') - start = datetime.now() - os.system('python clean_twice_course_journals.py %s' % course_id) - finish = datetime.now() - print('Конец очистки двойных журналов. Длительность: %s\n' % (finish-start).microseconds) - - print('Начало проверки журналов') - all = TeacherJ.objects.filter(progress__gt=0, course__id=course_id).count() - print('Количество журналов на проверку: %s' % all) - for i in TeacherJ.objects.filter(progress__gt=0, course__id=course_id).order_by('-id'): - check_journal(i, from_console=True) - print('Конец проверки журналов. Длительность: %s сек\n' % (finish-start).microseconds) - - print('Начало открытия уроков курса') - start = datetime.now() - os.system('python open_course_lesson.py %s' % course_id) - finish = datetime.now() - print('Конец открытия уроков курса. Длительность: %s\n' % (finish-start).microseconds) - - global_finish = datetime.now() - print('==============================\nДлительность: %s сек' % (global_finish - global_start).seconds) + print('===========\nКурс: %s\n==============\n' % Course.objects.get(id=course_id).title) -if __name__ == "__main__": - course_id = sys.argv[1] - check_course_journal(course_id) + print('Начало проверки журнала курса') + global_start = start = datetime.now() + course = Course.objects.get(id=course_id) + course.build_map() + finish = datetime.now() + print('Конец проверки журнала курса. Длительность: %s мсек\n' % (finish - start).microseconds) + + print('Начало проверки услуг') + start = datetime.now() + all = Price.objects.filter(by_time=0, post_fire=0, course__id=course_id).count() + print('Количество услуг на проверку: %s' % all) + for i in Price.objects.filter(by_time=0, post_fire=0, course__id=course_id): + i.check_points() + finish = datetime.now() + print('Конец проверки услуг. Длительность: %s мсек\n' % (finish - start).microseconds) + + print('Начало очистки двойных журналов') + start = datetime.now() + os.system('python clean_twice_course_journals.py %s' % course_id) + finish = datetime.now() + print('Конец очистки двойных журналов. Длительность: %s\n' % (finish - start).microseconds) + + print('Начало проверки журналов') + all = TeacherJ.objects.filter(progress__gt=0, course__id=course_id).count() + print('Количество журналов на проверку: %s' % all) + for i in TeacherJ.objects.filter(progress__gt=0, course__id=course_id).order_by('-id'): + check_journal(i, from_console=True) + print('Конец проверки журналов. Длительность: %s сек\n' % (finish - start).microseconds) + print('Начало открытия уроков курса') + start = datetime.now() + os.system('python open_course_lesson.py %s' % course_id) + finish = datetime.now() + print('Конец открытия уроков курса. Длительность: %s\n' % (finish - start).microseconds) + + global_finish = datetime.now() + print('==============================\nДлительность: %s сек' % (global_finish - global_start).seconds) + + +if __name__ == "__main__": + course_id = sys.argv[1] + check_course_journal(course_id) diff --git a/access/middleware.py b/access/middleware.py index f225bda..d749b62 100644 --- a/access/middleware.py +++ b/access/middleware.py @@ -1,10 +1,16 @@ from django.http import HttpResponseForbidden +from django.http import QueryDict +import json + class CheckPerm(object): @staticmethod def process_request(request): - if '/admin' in request.path or '/analytics' in request.path: + if len(request.path) > 6 and \ + ('/admin' == request.path[:6] + or'/analy' == request.path[:6]): + #or "/api/v" == request.path[:6]): if not request.user.is_authenticated(): return HttpResponseForbidden() @@ -13,3 +19,17 @@ class CheckPerm(object): or request.user.in_role == "A" or request.user.is_admin): return HttpResponseForbidden() + + +class RequestToApi(object): + @staticmethod + def process_request(request): + if len(request.path) > 4 and '/api' == request.path[:4]: + if request.method == 'POST': + data = json.loads(request.body.decode('utf-8')) + q_data = QueryDict('', mutable=True) + for value in data: + q_data.update({value: data[value]}) + request.JSON = q_data + if request.method == 'POST' or request.method == 'DELETE': + setattr(request, '_dont_enforce_csrf_checks', True) \ No newline at end of file diff --git a/access/migrations/0090_auto_20170918_0811.py b/access/migrations/0090_auto_20170918_0811.py new file mode 100644 index 0000000..abd841b --- /dev/null +++ b/access/migrations/0090_auto_20170918_0811.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.3 on 2017-09-18 08:11 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0089_auto_20170810_0904'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='delay_date', + field=models.DateTimeField(blank=True, default=django.utils.timezone.now, null=True, verbose_name='Задержка до. Не включая этот день.'), + ), + ] diff --git a/access/models.py b/access/models.py index 9735915..6b82ed8 100755 --- a/access/models.py +++ b/access/models.py @@ -133,7 +133,7 @@ class User(AbstractBaseUser): deactivate = models.BooleanField(verbose_name=u'Не давать новых студентов', default=False) delay = models.BooleanField(verbose_name=u'Могут быть задержки', default=False, help_text=u'Если возможны задержки с проверками') delay_description = models.TextField(verbose_name=u'Повод просрочки', blank=True) - delay_date = models.DateTimeField(verbose_name=u'Задержка до. Не включая этот день.', blank=True, default=datetime.datetime.now(), null=True) + delay_date = models.DateTimeField(verbose_name=u'Задержка до. Не включая этот день.', blank=True, default=django.utils.timezone.now, null=True) email = models.EmailField(verbose_name='email', max_length=255, blank=True, db_index=True, unique=True, help_text=u'Будьте аккуратны. Менять почту можно. Но только если очень нужно.') changed_email = models.EmailField(verbose_name=u'Ящик на замену', blank=True, null=True) @@ -185,6 +185,9 @@ class User(AbstractBaseUser): def get_ip_len(self): UserRequest.objects.filter(user=self).count() + def full_name(self): + return str(self.id) + ": " + self.fname + " " + self.name + " " + self.oname + def set_request_data(self, request): ip = get_client_ip(request) try: @@ -345,10 +348,10 @@ class User(AbstractBaseUser): def check_phone(self, _type='actual'): # actual = self.phone, back = self.back_phone phone = self.phone if _type == 'actual' else self.back_phone - return bool(len(''.join([n for n in phone if n in [str(x) for x in xrange(0,10)]])) > 9) + return bool(len(''.join([n for n in phone if n in [str(x) for x in range(0,10)]])) > 9) def clean_phone(self, _type='actual'): - return ''.join([n for n in self.phone if n in [str(x) for x in xrange(0,10)]]) if self.check_phone() else '' + return ''.join([n for n in self.phone if n in [str(x) for x in range(0,10)]]) if self.check_phone() else '' class Meta: diff --git a/access/new_urls.py b/access/new_urls.py new file mode 100644 index 0000000..dea2ecb --- /dev/null +++ b/access/new_urls.py @@ -0,0 +1,12 @@ +from django.conf.urls import url +from access import new_view as views + +urlpatterns = [ + url(r'teachers/$', views.TeacherListView.as_view()), + url(r'info/$', views.InfoUserView.as_view()), + url(r'check/$', views.CheckUserView.as_view()), + url(r'registration/$', views.RegistrationView.as_view()), + url(r'change_password/$', views.ChangePasswordView.as_view()), + url(r'login/$', views.LoginView.as_view()), + url(r'logout/$', views.LogoutView.as_view()), +] \ No newline at end of file diff --git a/access/new_view.py b/access/new_view.py new file mode 100644 index 0000000..80c05aa --- /dev/null +++ b/access/new_view.py @@ -0,0 +1,100 @@ +from django.contrib.auth import get_user_model +from django.contrib import auth +from rest_framework.views import APIView +from rest_framework.renderers import JSONRenderer +from rest_framework.response import Response + +from access.serializers import UserInitSerializer +from courses.models import Course + + +class TeacherListView(APIView): + renderer_classes = (JSONRenderer,) + status_code = 200 + + def get(self, request): + kwargs = dict( + in_role='T', + is_active=True, + reg_status=4, + ) + res = [] + course_id = request.GET.get('course_id', 0) + if course_id: + kwargs['course_teachers'] = Course.objects.get(id=course_id) + for teacher in get_user_model().objects.filter(**kwargs): + res.append(teacher.full_name()) + return Response(res, self.status_code) + + +class CheckUserView(APIView): + renderer_classes = (JSONRenderer,) + status_code = 200 + + def get(self, request): + if request.user.is_authenticated() and (request.user.in_role in ['M', 'S', 'S2', 'A'] or request.user.is_admin): + return Response(True, status=self.status_code) + return Response(False, status=self.status_code) + + +class InfoUserView(APIView): + renderer_classes = (JSONRenderer,) + status_code = 200 + + def get(self, request): + if request.user.is_authenticated(): + return Response(UserInitSerializer(request.user).data, status=self.status_code) + return Response('anonymous', status=self.status_code) + + +class RegistrationView(APIView): + renderer_classes = (JSONRenderer,) + + @staticmethod + def post(request): + try: + user = get_user_model().objects.get(email=request.JSON['email'].lower()) + except get_user_model().DoesNotExist: + user = get_user_model().objects.create_user( + email=request.JSON['email'].lower(), + ) + user.set_password(request.JSON['password']) + user.reg_status = '2' + user.save() + return Response(UserInitSerializer(user).data, status=200) + + +class ChangePasswordView(APIView): + renderer_classes = (JSONRenderer,) + + @staticmethod + def post(request): + if request.user.is_authenticated and not request.user.check_password(request.JSON['old_password']): + return Response("Неверный пароль", status=404) + request.user.set_password(request.JSON['new_password']) + request.user.save() + return Response("Пароль был изменён", status=200) + + +class LoginView(APIView): + renderer_classes = (JSONRenderer,) + + @staticmethod + def post(request): + if not request.user.is_authenticated(): + user = auth.authenticate(email=request.JSON.get('email'), password=request.JSON.get('password')) + try: + auth.login(request, user) + except AttributeError: + return Response("Неверный пароль", status=404) + return Response(UserInitSerializer(request.user).data, status=200) + + +class LogoutView(APIView): + renderer_classes = (JSONRenderer,) + + @staticmethod + def post(request): + if request.user.is_authenticated(): + auth.logout(request) + return Response(status=204) \ No newline at end of file diff --git a/access/serializers.py b/access/serializers.py new file mode 100644 index 0000000..ecf2693 --- /dev/null +++ b/access/serializers.py @@ -0,0 +1,10 @@ +from django.contrib.auth import get_user_model +from rest_framework import serializers + + +class UserInitSerializer(serializers.ModelSerializer): + + class Meta: + model = get_user_model() + fields = ['id', 'email', 'phone', 'name', + 'fname', 'oname', 'city', 'b_day'] \ No newline at end of file diff --git a/api_v1/urls.py b/api_v1/urls.py new file mode 100644 index 0000000..ab83d98 --- /dev/null +++ b/api_v1/urls.py @@ -0,0 +1,6 @@ +from django.conf.urls import url, include + +urlpatterns = [ + url(r'courses/', include('courses.new_urls')), + url(r'users/', include('access.new_urls')), +] \ No newline at end of file diff --git a/configs/skillbox_ssl/privat.key b/configs/skillbox_ssl/privat.key index 64a65c7..e89e0d3 100644 --- a/configs/skillbox_ssl/privat.key +++ b/configs/skillbox_ssl/privat.key @@ -1,52 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -MIIJJwIBAAKCAgEAyVFxVUcMQcO+g6KEE6BmnvmT5b3y5+qnm5YklpmFaJ1k3HUH -NG7cvEv4h0yfwcUFCEUimlrVeQ4mDMgkIgzWEPdTOg6fUPiu8N4zoZ90Cec0LJrl -Ul1uxEmVE4d2VoFE0iDL2XrSkmfBaO1JcydQ5DPie4+zBjgaybsT/zeQbGrHR7b3 -2AcderNVGeSS0p9ruqedi3esUS71L4ACBQFuZIW5FCGkyabAXzUz8SlmU4gcT1vg -Vzm5C4ZDikOl+HIOdspibKYwlmpd2RYU39spFLs/xOLG9agMDqVVec1exTiBLNB/ -YFcLrf1yGx0BBJWGxv1UvQ2j3tJI2NKnVFlPRMQ/8F9aVjR5Y2kKo6ur9JkoLLz9 -hTda7wY9q43DtHdhtWurZR371eYs8nNabh+oy4b2+HRla+BJy3p5iL5S/S3iM8Nd -6YJdp5f4hUNjs3CBShHGLPrsA8bSMGaOlebD0OdcOPpQjBaUG2os2OlSaNBcf8Ap -Ch9ptRDnpQhvro5tH/FzR9jyQJSVtc1t2SyvKPZU4BtWuQEkRp9+uq/Xdy7iJCUa -9ngxwWTQBsGYuQLEhEGZcSnpn5G101L5pa4pfLbEfi2nKN4U5NrWfEPVKj6krvaw -MYULxzfOVPbfu8ijbsV14PRydJFT2HfqAmDNCVkC5oafG1JkPYdEYzm10q8CAwEA -AQKCAgB4UscMExTOUbISdxW9rUboiX7N94Ow/V0D7u8bW9KSvk/EO+X8fq5LlJSM -OU3awGjLkpal7thcLAqt5QlDmKxxI4GI16h8+sWEjSoK1YTDTn6ukj2xxsonY1tI -etRNy6FYfeWYrIIiowTEM96MhtkgK6qX80Eq3GmWXMwM5Cbbxc4pnL/xS/9gkMKy -YR0n6cS3oOrWqsZBF3HP3BBY8YwLZKlD0P/uzk14GhJbd0UDc/yHqgAh4gwpSSwg -/WG7ZmAiu7+r9IBhxl0lHL+JNDIPhAMPKAOGW9QpBMRbslWMRsNeB5e5+wyuBycl -8WXkWYJeeJcnD3n7+W80uGLuVRq8jLeU8pBzygtLNdZl0WsQ+uu23BWTA8wKYBb9 -AeQr+hmJ+4/l0Xa9ZvE+hfP4cf/0ORCmMEQl2z9BTH3SMgZ5UAC7QGA7MqRpnnBJ -rHAe71aC1hbpHhz1mdP+pWRrno9AapEINXY4fqLXYGV44E+13cgOPFGUGk0OEFm3 -LMTilnmZ4ZPwh6mW+gYieKTiGU8kdzoCJKjYuDOG61lLsJyx+XfzukntJhdCJoK9 -9/O/dXeWzDwNVjvGcNX8bw2is53HlOSTSH8UjkiArQhvlf1CShPgnwTqVaoUeuGb -7Mi98VMicRrHa2knGC9snF0PSSNWUHv93LSwdd3ks3gJJM8V8QKCAQEA92DBDfBp -3bRLrw0vqx2rlUIcqfzTYUjXZ+uDzrmHEKt68Jxlk3d0T+vUjB5ecc3ZHCC2dJCo -FtWPMQCYsq+NkvjP4QM8/2NjxpaPfv2vxked/VXCkYZWgi2znZfzBBYW9P0w+dZc -wcl2KuSyPnX1sPDbYP7y4QrRraHQ7vhWdwkkkV1GDFHHJLXpcovKcAfEn0yvdbYJ -OQNT2JeA9edgS3Tn8aSE7qc77QQAYlWHoBmwcsY1eJvj5+trJDSpGKYylvIcdHJq -KFOAyRdyz+QTsfZ3KT31q/5d0sU8Dv0n/c4Ef7B6HoNBnuJnfATl4ySa0lt/IaxN -HKZm3sXByYpdKwKCAQEA0FW3iEcSr8p7n13UXU2vQipchB5H3zNYJnZANbgDMURJ -N8TIqL9JncrQB7agpv2BfQNpJEWA2mZxGK7q2BgdM3PfI33TP8E7wq2aPNnobZQS -WF84GAc9NA7K6m6O5KWM6uXofznmn5pBd2d2HaLAjARJqwSZidYbx+wzw76jh5Rl -O0P2+pxTnISs7TuT2mH/jvKWYECQnPPseI2JeXfRNGCWZBOq/DZaqUQHWRGNX26N -4MQw5NYTGxOBjBJKn8if5K/BQQo0TYtwji1T8WuGQrWZQXgvdzLOusg+42JbXQc+ -gi2nhiF7ELnMZ9AsaQpgxrbJVR2X1HnqrKWAHTSGjQKCAQA5P/QNxMjLXVFypquU -f15ZQZ6enHtS9Wecjj3H1tnyOq1OB/PeB/QsFtjuwIx86/XF87AmE+BCwJPN9GRi -Xgx2qgx596TjOd46dFerPQyz55khqvpHcX6NzAPYgfTkyjPv6LSv7TM0SeqGDL1P -hT8h/uvWa8pL2JQkCeHvnBtumvgSiDKQ4VAkJ9FqB/4GpVWUxEuLA2DpB+McqhtX -TX/Ulq6jFr8I+Dz/IkDwgPjNTYSvWz2+tt0srCf0jhdgM/5bPpblQCFfScvxPfL9 -N7xzICX9rnAvqMOP5sN6eI4Lx+T0pCPu8dN68MygQAXv5mXKLxCh+10xNzVxcvTh -LptXAoIBAADUZo+3YL6sesOOtxrMBWepDYdFK5oa9T3p+KV+Hx0iHXyIXxg08tEb -DqsJMiiYF9zsl42abUHxaH/lqm/D4NHkoz3b8Wxuk5NZ56RYZdGR5mTNtEZ8OOUt -LeezGwSUGDSTR9LB40Y9cgdMtS63CDdGiRiSQx5GaQPpz+b2qmYO1DxhL+mdl8Om -z51+QD99ZmwmUSz+9NR7MDTms2Z1cLLZs5+gaGzqtn+4EqNrK3n20b/UaDFxHNBd -yoVJSVzLir95Z7afmMe6R295hL0QZY98c6jMFrxNyn757y4upRiAv+H61pD1Ly1I -4CSFsreUYABKmBCrtoAVIDchzZnQLL0CggEALIzVFuEPzRB8jd7eUbNV54doBfy7 -ScXJbVt43UCXj+K2lTvtnERa48Wf+O24u7veZc47sWNHV6baSXDip2aIT2cl29yk -nkz7NlIHzmQJBdXsVMXWWQ0SPj6EN3POoWwb9a03dQ0SKDhL5OLml1peH93L9vaV -RebrYAXthjz/OvOwkhTCSlynI9lCWcGAEDD2xyN03G9pAjwUeLoPcdnFr5um51fh -fA9r8TjO7laCIAgpW+FNG6YuDUW5NQjKaCSgqAWkQiPxwaaFDojJoNVVA1bHtv+g -bhQlbg1C/Dqxbi2XAP92pMUqmQdnY11YEVzps809mRPl1WrRxdspXOWN1w== +MIIEpgIBAAKCAQEA7gHgjZhv3+EWgg5adHGcPgoetI/zOIijQ+xtIhxVUx7oFv3E +eve+aYafc8dr+pv8bAvJxbdF46gHnxDUrgkEaYQtO42aJfziP3QfEeDWdqONYauW +KGghfPQeE370LU16IrqsQ7xhOpVgPA+fU8DsGDDms4/dko/E84bz/72xYhiqpcEq +QR6hAkGdu8P8xcvJA1qVAyZJ65HJD9WhKUcEaZCywjIXbQUwGwUawvScUyGhsCg2 +vTIBFuuGg3rxHWmSNFSPKZlr9cu0fbtY/e9SPl6kxTdssPQMEHJwROvs+5XNf6kD +AGPSda8qbJg2rNsWOpoN2AVz8sZsNChIf8KDaQIDAQABAoIBAQDRukm7vDXpHBmP +ZUKEFZDOgnBoP+jdSlpEAlRwmHL/md5+VAQNL75UktJzXali3o1iUEjutv8ZCvoc +SuJ+sWd364dIUREipGJh89tlD7EsPhbZmC/rT0cjKqJOH7LfMMSNUd12mss6qmJw +yntlfD0PXBgO5pkjCxlMVwEvvGJQwySMwmSWECTSm3tlYavT7mfwM1zQWhN9krj2 +iSDeb3de3byGYijyzKzV4UV2JbQA2BOzVwsK5SJ1NY1kp+a56ktrk4Fmh/loAVWZ +qzl6du8Hng03DJ6i+tebHHsbsrVlrCMsCHb0jGNmRZcC2VTkAchM6+F6J140xCCb +vXrCJ2rhAoGBAPwVR6W+KiQsc3Otv73y5/4UKtqCpX2/AmeRmlFhja5U9xxJadb4 +918GZBQu8lMvbrDkU4Q70NgvhbsDfhCN1YckgsDmUBdd5uAz73+I85HonDaUUh0n +IsY9XUfUq5dLmZPbq6P0ml1kU4RIm/CP9+nQPHSw4rA0m9IE0K3Y2vj1AoGBAPG0 +m4VXyMHw1fdWqsZ2o97xFjnaAh+qNIxfE6zffd/KnmnZhddM+aYNaWnXnUbnIKJ+ +/NcGHhWBBjE77Zzu7y/ES+s+tuc1CXJesryFNwDyTphVifDu49FB17wWt2XG9WdT +Ru9wWIQed1TJOYdiw8Bx1G8FvlKGpbTqeObfdWglAoGBAODoe4w9JowvvLIxCyoG +V0Yx6Coc9lpC2XpeRjjluOsZk4ABYBUU/GAj65a2bC4b276neHw6ghLgE4jPjOJo +KWLyCFbjJgIzHZJbxAsdvXMo/ncTzk+EY1pL1bYI4S+Tm+p3y6P00OUSZhFYVwlC +6gYKbGYQi7B6v6uZ8DWrjXMhAoGBAIZ7ZsyaOyktRt+HaR1jhWnV2ff2RJg5q2Mu +w1O8Rdzub5WVYdKZEY5CiBRL00ZP8uh2n1dEIBj24Osj3OEkUBDTtyn8iue5qrfn +2Krrs97i8rfvYACd6kPTzqToPsJyILiMrTsr1xQrn1mdXFTnruK0cu4S1AI2yJwQ +mZRbYIfBAoGBAMsZuG7KAJbIfQL8LR4kyvkwP9oyXd4MEydimoSqTygjvJ+V3rWJ +PXQL0m0Ug+vkhh1kB2akbHdcN6xmxhowPxAXZw+qgjtC1rS/cqIIQluPX2TJ2d/1 +ghUy3ewwybQIQtPmE9NN9N5WANDZ9h2pO8WkPSKL5u98cl78LyL54Rg7 -----END RSA PRIVATE KEY----- - diff --git a/configs/skillbox_ssl/public.pem b/configs/skillbox_ssl/public.pem index 747569e..0ceac8a 100644 --- a/configs/skillbox_ssl/public.pem +++ b/configs/skillbox_ssl/public.pem @@ -1,52 +1,69 @@ -----BEGIN CERTIFICATE----- -MIIITDCCBzSgAwIBAgIMeU6S68+Azcq3a9oDMA0GCSqGSIb3DQEBCwUAMEwxCzAJ -BgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSIwIAYDVQQDExlB -bHBoYVNTTCBDQSAtIFNIQTI1NiAtIEcyMB4XDTE2MTIwMTA1NDcwMloXDTE3MTEx -MTE1NDI1MFowOzEhMB8GA1UECxMYRG9tYWluIENvbnRyb2wgVmFsaWRhdGVkMRYw -FAYDVQQDDA0qLnNraWxsYm94LnJ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC -CgKCAgEAyVFxVUcMQcO+g6KEE6BmnvmT5b3y5+qnm5YklpmFaJ1k3HUHNG7cvEv4 -h0yfwcUFCEUimlrVeQ4mDMgkIgzWEPdTOg6fUPiu8N4zoZ90Cec0LJrlUl1uxEmV -E4d2VoFE0iDL2XrSkmfBaO1JcydQ5DPie4+zBjgaybsT/zeQbGrHR7b32AcderNV -GeSS0p9ruqedi3esUS71L4ACBQFuZIW5FCGkyabAXzUz8SlmU4gcT1vgVzm5C4ZD -ikOl+HIOdspibKYwlmpd2RYU39spFLs/xOLG9agMDqVVec1exTiBLNB/YFcLrf1y -Gx0BBJWGxv1UvQ2j3tJI2NKnVFlPRMQ/8F9aVjR5Y2kKo6ur9JkoLLz9hTda7wY9 -q43DtHdhtWurZR371eYs8nNabh+oy4b2+HRla+BJy3p5iL5S/S3iM8Nd6YJdp5f4 -hUNjs3CBShHGLPrsA8bSMGaOlebD0OdcOPpQjBaUG2os2OlSaNBcf8ApCh9ptRDn -pQhvro5tH/FzR9jyQJSVtc1t2SyvKPZU4BtWuQEkRp9+uq/Xdy7iJCUa9ngxwWTQ -BsGYuQLEhEGZcSnpn5G101L5pa4pfLbEfi2nKN4U5NrWfEPVKj6krvawMYULxzfO -VPbfu8ijbsV14PRydJFT2HfqAmDNCVkC5oafG1JkPYdEYzm10q8CAwEAAaOCBD0w -ggQ5MA4GA1UdDwEB/wQEAwIFoDCBiQYIKwYBBQUHAQEEfTB7MEIGCCsGAQUFBzAC -hjZodHRwOi8vc2VjdXJlMi5hbHBoYXNzbC5jb20vY2FjZXJ0L2dzYWxwaGFzaGEy -ZzJyMS5jcnQwNQYIKwYBBQUHMAGGKWh0dHA6Ly9vY3NwMi5nbG9iYWxzaWduLmNv -bS9nc2FscGhhc2hhMmcyMFcGA1UdIARQME4wQgYKKwYBBAGgMgEKCjA0MDIGCCsG -AQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBvc2l0b3J5LzAI -BgZngQwBAgEwCQYDVR0TBAIwADA+BgNVHR8ENzA1MDOgMaAvhi1odHRwOi8vY3Js -Mi5hbHBoYXNzbC5jb20vZ3MvZ3NhbHBoYXNoYTJnMi5jcmwwJQYDVR0RBB4wHIIN -Ki5za2lsbGJveC5ydYILc2tpbGxib3gucnUwHQYDVR0lBBYwFAYIKwYBBQUHAwEG -CCsGAQUFBwMCMB0GA1UdDgQWBBS+YITz/+bonvjgdKmiWj1x08BcwDAfBgNVHSME -GDAWgBT1zdU8CFD5ak86t5faVoPmadJo9zCCAm8GCisGAQQB1nkCBAIEggJfBIIC -WwJZAHYAVhQGmi/XwuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFYuOwAiwAA -BAMARzBFAiA5q04XH6RwIsnGSYRIcZAZkzUgUsk5G3Muz6oSX5nGcwIhAOZLQTmV -pvJu2OtkJtOKKIMqFKKAsM+tTlyB941pAxOJAHcA3esdK3oNT6Ygi4GtgWhwfi6O -nQHVXIiNPRHEzbbsvswAAAFYuOwCAAAABAMASDBGAiEAxgwM+HRk9dytVQpO5S78 -8vF+5Ie4pO9fThMxz00//XQCIQCH+GXad2Ks2pi3Qelgd4qi1W0r3f+abXiVwELL -uAnlngB3AKS5CZC0GFgUh7sTosxncAo8NZgE+RvfuON3zQ7IDdwQAAABWLjsAOQA -AAQDAEgwRgIhAJuJdYe0gYq11fjKiE5QWeB4CvAJhCWpnjbSCQA0WDFVAiEAu6HE -kuwhUWc0wkWK4+0co9ZpKZRfWLSlwF1xKJYw+h4AdQC72d+8H4pxtZOUI5eqkntH -OFeVCqtS6BqQlmQ2jh7RhQAAAVi47AHSAAAEAwBGMEQCIFY1HI20nUjJCrdHnATT -j1xoMZa27XWgQc2+RrB9LF7TAiAEVoM6TVoSH6BZbFEeyJFAWQX4kE0JqSUFy1za -lLXtwwB2AO5Lvbd1zmC64UJpH6vhnmajD35fsHLYgwDEe4l6qP3LAAABWLjsA/kA -AAQDAEcwRQIgTLHS0ANLIgD+xA9lG9gIWajGNUU4lovJJu4bzigmnKkCIQDoUn5G -XrK1a+nM55ij1zxiTm18cmFcfeKxkCc/aApzEzANBgkqhkiG9w0BAQsFAAOCAQEA -r5yreADdlFBcV/YJJhYZ/pYsDufmqAN8dC5RQ2DcRqj8NXR80jabujj8mzbld677 -ixcj/j0OnxdFE2J88IEZmbNvQVeVw74a2Z87314iI9VHjFVRnlGr2Kxnz8z83ijW -pHjKLFgm6v7Qv1ZSEN8e8AD1J1+4Vljbu575FKOEMuKyQ6aau4cc7BJQ1uBx6Do4 -p3yHK/I9duGKH9NUg6wof64V9m7lZ6Wg4BwBNkZkCSifbrACZbqj2sUszQYnAYAH -ieiQMM5pA9qNwROZnVnKhdm81d2Qu0qa37kZRonNlJ1WDUgewWjgnDBVwP+LZGOL -6u+zckWVD6iKWIPy8PC7LQ== +MIIG9jCCBd6gAwIBAgIMCl+oiI0i4cQUsYsgMA0GCSqGSIb3DQEBCwUAMGAxCzAJ +BgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMTYwNAYDVQQDEy1H +bG9iYWxTaWduIERvbWFpbiBWYWxpZGF0aW9uIENBIC0gU0hBMjU2IC0gRzIwHhcN +MTcxMTAyMDkxMjM5WhcNMTgxMTAzMDkxMjM5WjA7MSEwHwYDVQQLExhEb21haW4g +Q29udHJvbCBWYWxpZGF0ZWQxFjAUBgNVBAMMDSouc2tpbGxib3gucnUwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDuAeCNmG/f4RaCDlp0cZw+Ch60j/M4 +iKND7G0iHFVTHugW/cR6975php9zx2v6m/xsC8nFt0XjqAefENSuCQRphC07jZol +/OI/dB8R4NZ2o41hq5YoaCF89B4TfvQtTXoiuqxDvGE6lWA8D59TwOwYMOazj92S +j8TzhvP/vbFiGKqlwSpBHqECQZ27w/zFy8kDWpUDJknrkckP1aEpRwRpkLLCMhdt +BTAbBRrC9JxTIaGwKDa9MgEW64aDevEdaZI0VI8pmWv1y7R9u1j971I+XqTFN2yw +9AwQcnBE6+z7lc1/qQMAY9J1rypsmDas2xY6mg3YBXPyxmw0KEh/woNpAgMBAAGj +ggPTMIIDzzAOBgNVHQ8BAf8EBAMCBaAwgZQGCCsGAQUFBwEBBIGHMIGEMEcGCCsG +AQUFBzAChjtodHRwOi8vc2VjdXJlLmdsb2JhbHNpZ24uY29tL2NhY2VydC9nc2Rv +bWFpbnZhbHNoYTJnMnIxLmNydDA5BggrBgEFBQcwAYYtaHR0cDovL29jc3AyLmds +b2JhbHNpZ24uY29tL2dzZG9tYWludmFsc2hhMmcyMFYGA1UdIARPME0wQQYJKwYB +BAGgMgEKMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29t +L3JlcG9zaXRvcnkvMAgGBmeBDAECATAJBgNVHRMEAjAAMEMGA1UdHwQ8MDowOKA2 +oDSGMmh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vZ3MvZ3Nkb21haW52YWxzaGEy +ZzIuY3JsMCUGA1UdEQQeMByCDSouc2tpbGxib3gucnWCC3NraWxsYm94LnJ1MB0G +A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUZIga+NQLvi5f +3Gfib17HFFF6e0wwHwYDVR0jBBgwFoAU6k581IAt5RWBhiaMgm3AmKTPlw8wggH2 +BgorBgEEAdZ5AgQCBIIB5gSCAeIB4AB2AFYUBpov18Ls0/XhvUSyPsdGdrm8mRFc +wO+UmFXWidDdAAABX3wA/i0AAAQDAEcwRQIhAL/dmHjVMPgKEhsG7/Al3FVaMtRa +GTrPMYus43M5X732AiAPRqgGYThB+PE2SB07DLEdr45Nt4PK1uI98ftfGF1e1AB1 +AKS5CZC0GFgUh7sTosxncAo8NZgE+RvfuON3zQ7IDdwQAAABX3wA/h4AAAQDAEYw +RAIgY+gMa0qV69iPZT5npkH9Hl48irOBZuur/9rde+tn1EQCIECmOYBV2LP+9T0M +YYBorr2UQpvKpabrsZCY8zeKNkLTAHYAb1N2rDHwMRnYmQCkURX/dxUcEdkCwQAp +Bo2yCJo32RMAAAFffAEACwAABAMARzBFAiAjqnHwHHqjfH5yS78e2PBJyNkSoQSM +wElxtNa/J4MyGAIhAOEHtNlKYVjxuS5iNYVWdHirXUu5Hdw5HoMJBJ73Hy8KAHcA +7ku9t3XOYLrhQmkfq+GeZqMPfl+wctiDAMR7iXqo/csAAAFffAEDCgAABAMASDBG +AiEAw012b0yOolsEpBFcx0ByQDGWwg9klb2/cilAq8RiaFICIQDFSUs4JJqFXF02 +1/nr6JCl+Wvwv/v1l+Pzql4JWC4VzTANBgkqhkiG9w0BAQsFAAOCAQEAlBsgPIUV +CwWgtCQ/nKU0xqC+vXux7zFez4wpZr1Wbdf2H5bjqmxMrL5K5I54wDVk8LA1sPds +ViUTe/BShMHSrCKlr1d4LNeyCNmveEM1ClNurHoEcBGf9AUleqHMYLggMVUVpGD7 +bYpx0qWDQQLuk7JW1Jk5f0PRV3pzHAtYa/Y1XAmy2uo0Df5UGX46+B68ptYVWhiS +holbhCB8UxyBeT5sqmsMSkSQ7O+8VGeTgmq5RkpxAdMgKyNgC2/aDAt+shBoEeRe +vpsq+DSJ+xQnxlS4bpT9L8qH87sq/2xnMtu6nN83O5qOrc2Wu0JlA+s/9ucDR3+M +zH8FdHJuhx9nOg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgILBAAAAAABRE7wPiAwDQYJKoZIhvcNAQELBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNDAyMjAxMDAw +MDBaFw0yNDAyMjAxMDAwMDBaMGAxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMTYwNAYDVQQDEy1HbG9iYWxTaWduIERvbWFpbiBWYWxpZGF0 +aW9uIENBIC0gU0hBMjU2IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCp3cwOs+IyOd1JIqgTaZOHiOEM7nF9vZCHll1Z8syz0lhXV/lG72wm2DZC +jn4wsy+aPlN7H262okxFHzzTFZMcie089Ffeyr3sBppqKqAZUn9R0XQ5CJ+r69eG +ExWXrjbDVGYOWvKgc4Ux47JkFGr/paKOJLu9hVIVonnu8LXuPbj0fYC82ZA1ZbgX +qa2zmJ+gfn1u+z+tfMIbWTaW2jcyS0tdNQJjjtunz2LuzC7Ujcm9PGqRcqIip3It +INH6yjfaGJjmFiRxJUvE5XuJUgkC/VkrBG7KB4HUs9ra2+PMgKhWBwZ8lgg3nds4 +tmI0kWIHdAE42HIw4uuQcSZiwFfzAgMBAAGjggElMIIBITAOBgNVHQ8BAf8EBAMC +AQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU6k581IAt5RWBhiaMgm3A +mKTPlw8wRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8v +d3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMDMGA1UdHwQsMCowKKAmoCSG +Imh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5uZXQvcm9vdC5jcmwwPQYIKwYBBQUHAQEE +MTAvMC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9yb290 +cjEwHwYDVR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJKoZIhvcNAQEL +BQADggEBANdFnqDc4ONhWgt9d4QXLWVagpqNoycqhffJ7+mG/dRHzQFSlsVDvTex +4bjyqdKKEYRxkRWJ3AKdC8tsM4U0KJ4gsrGX3G0LEME8zV/qXdeYMcU0mVwAYVXE +GwJbxeOJyLS4bx448lYm6UHvPc2smU9ZSlctS32ux4j71pg79eXw6ImJuYsDy1oj +H6T9uOr7Lp2uanMJvPzVoLVEgqtEkS5QLlfBQ9iRBIvpES5ftD953x77PzAAi1Pj +tywdO02L3ORkHQRYM68bVeerDL8wBHTk8w4vMDmNSwSMHnVmZkngvkA0x1xaUZK6 +EjxS1QSCVS1npd+3lXzuP8MIugS+wEY= -----END CERTIFICATE----- - -Корневой сертификат -----BEGIN CERTIFICATE----- MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv @@ -68,30 +85,3 @@ AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== -----END CERTIFICATE----- - -Промежуточный сертификат ------BEGIN CERTIFICATE----- -MIIETTCCAzWgAwIBAgILBAAAAAABRE7wNjEwDQYJKoZIhvcNAQELBQAwVzELMAkG -A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv -b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNDAyMjAxMDAw -MDBaFw0yNDAyMjAxMDAwMDBaMEwxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i -YWxTaWduIG52LXNhMSIwIAYDVQQDExlBbHBoYVNTTCBDQSAtIFNIQTI1NiAtIEcy -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2gHs5OxzYPt+j2q3xhfj -kmQy1KwA2aIPue3ua4qGypJn2XTXXUcCPI9A1p5tFM3D2ik5pw8FCmiiZhoexLKL -dljlq10dj0CzOYvvHoN9ItDjqQAu7FPPYhmFRChMwCfLew7sEGQAEKQFzKByvkFs -MVtI5LHsuSPrVU3QfWJKpbSlpFmFxSWRpv6mCZ8GEG2PgQxkQF5zAJrgLmWYVBAA -cJjI4e00X9icxw3A1iNZRfz+VXqG7pRgIvGu0eZVRvaZxRsIdF+ssGSEj4k4HKGn -kCFPAm694GFn1PhChw8K98kEbSqpL+9Cpd/do1PbmB6B+Zpye1reTz5/olig4het -ZwIDAQABo4IBIzCCAR8wDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8C -AQAwHQYDVR0OBBYEFPXN1TwIUPlqTzq3l9pWg+Zp0mj3MEUGA1UdIAQ+MDwwOgYE -VR0gADAyMDAGCCsGAQUFBwIBFiRodHRwczovL3d3dy5hbHBoYXNzbC5jb20vcmVw -b3NpdG9yeS8wMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5nbG9iYWxzaWdu -Lm5ldC9yb290LmNybDA9BggrBgEFBQcBAQQxMC8wLQYIKwYBBQUHMAGGIWh0dHA6 -Ly9vY3NwLmdsb2JhbHNpZ24uY29tL3Jvb3RyMTAfBgNVHSMEGDAWgBRge2YaRQ2X -yolQL30EzTSo//z9SzANBgkqhkiG9w0BAQsFAAOCAQEAYEBoFkfnFo3bXKFWKsv0 -XJuwHqJL9csCP/gLofKnQtS3TOvjZoDzJUN4LhsXVgdSGMvRqOzm+3M+pGKMgLTS -xRJzo9P6Aji+Yz2EuJnB8br3n8NA0VgYU8Fi3a8YQn80TsVD1XGwMADH45CuP1eG -l87qDBKOInDjZqdUfy4oy9RU0LMeYmcI+Sfhy+NmuCQbiWqJRGXy2UzSWByMTsCV -odTvZy84IOgu/5ZR8LrYPZJwR2UcnnNytGAMXOLRc3bgr07i5TelRS+KIz6HxzDm -MTh89N1SyvNTBCVXVmaU6Avu5gMUTu79bZRknl7OedSyps9AsUSoPocZXun4IRZZUw== ------END CERTIFICATE----- \ No newline at end of file diff --git a/courses/migrations/0045_auto_20170810_0904.py b/courses/migrations/0045_auto_20170810_0904.py deleted file mode 100644 index 4f300cd..0000000 --- a/courses/migrations/0045_auto_20170810_0904.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.3 on 2017-08-10 09:04 -from __future__ import unicode_literals - -import datetime -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('courses', '0044_auto_20170621_1548'), - ] - - operations = [ - migrations.AlterField( - model_name='lesson', - name='on_comment', - field=models.CharField(choices=[('N', 'Включены'), ('F', 'Отключены'), ('T', 'Подчиняется теме')], default='T', help_text='https://go.skillbox.ru/management/faq/37', max_length=1, verbose_name='Блок комментариев'), - ), - ] diff --git a/courses/migrations/0045_auto_20170918_0811.py b/courses/migrations/0045_auto_20170918_0811.py new file mode 100644 index 0000000..9144b64 --- /dev/null +++ b/courses/migrations/0045_auto_20170918_0811.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.3 on 2017-09-18 08:11 +from __future__ import unicode_literals + +import datetime +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('courses', '0044_auto_20170621_1548'), + ] + + operations = [ + migrations.AlterField( + model_name='course', + name='mentors', + field=models.ManyToManyField(blank=True, related_name='course_mentors', to=settings.AUTH_USER_MODEL, verbose_name='Кураторы'), + ), + migrations.AlterField( + model_name='diploma', + name='date_size', + field=models.IntegerField(null=True, verbose_name='Размер даты'), + ), + migrations.AlterField( + model_name='diploma', + name='key', + field=models.IntegerField(verbose_name='Последний ключ'), + ), + migrations.AlterField( + model_name='diploma', + name='key_size', + field=models.IntegerField(null=True, verbose_name='Размер ключа'), + ), + migrations.AlterField( + model_name='diploma', + name='name_size', + field=models.IntegerField(null=True, verbose_name='Размер имени'), + ), + migrations.AlterField( + model_name='lesson', + name='on_comment', + field=models.CharField(choices=[('N', 'Включены'), ('F', 'Отключены'), ('T', 'Подчиняется теме')], default='T', help_text='https://go.skillbox.ru/management/faq/37', max_length=1, verbose_name='Блок комментариев'), + ), + migrations.AlterField( + model_name='materialdirection', + name='mentors', + field=models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name='Кураторы'), + ), + ] diff --git a/courses/models.py b/courses/models.py index fd25d8c..4916f00 100755 --- a/courses/models.py +++ b/courses/models.py @@ -33,7 +33,7 @@ class MaterialDirection(models.Model): title = models.CharField(verbose_name=u'Заголовок', max_length=255) color = models.CharField(verbose_name=u'Цвет', max_length=50) description = RedactorField(verbose_name=u'Описание', blank=True) - mentors = models.ManyToManyField(User, verbose_name=u'Кураторы', null=True) + mentors = models.ManyToManyField(User, verbose_name=u'Кураторы') def __unicode__(self): return u'%s' % self.title @@ -66,7 +66,7 @@ class MaterialDirection(models.Model): class Course(models.Model): COURSE_LEVEL = ( ('B', u'Базовый'), - ('A', u'Продвинутый '), + ('A', u'Продвинутый'), ('E', u'Экспертный'), ('B+A', u'Базовый + Продвинутый') ) @@ -75,7 +75,7 @@ class Course(models.Model): slug = models.SlugField(max_length=255, editable=False, blank=True, default='', unique=True) icon = models.ImageField(verbose_name=u'Иконка курса', blank=True, null=True, upload_to='course') direction = models.ForeignKey(MaterialDirection, verbose_name=u'Направление', null=True) - mentors = models.ManyToManyField(User, verbose_name=u'Кураторы', null=True, blank=True, related_name='course_mentors') + mentors = models.ManyToManyField(User, verbose_name=u'Кураторы', blank=True, related_name='course_mentors') public = models.BooleanField(verbose_name=u'Опубликовать', default=False) title = models.CharField(verbose_name=u"Заголовок", max_length=255) description = RedactorField(verbose_name=u'Описание', blank=True) @@ -407,7 +407,10 @@ class Lesson(models.Model): video = gen_vzaar_bb(self.video_id) if video: self.video = video + must_rebuild_map = False if Lesson.objects.filter(theme=self.theme, sort=self.sort).exclude(id=self.id).exists(): + # Обновить текущий урок с новым порядком, не вызывая при этом метод save() + Lesson.objects.filter(id=self.id).update(sort=self.sort) # Переформировать порядок test_in = self.sort + 1 for lesson in Lesson.objects.filter(theme=self.theme, sort__gte=self.sort).exclude(id=self.id): @@ -415,8 +418,11 @@ class Lesson(models.Model): lesson.sort = test_in lesson.save() test_in += 1 - self.token = self.course.build_map(material=self) + must_rebuild_map = True super(Lesson, self).save(*args, **kwargs) + # Переформировать карту после сохранения текущего урока + if must_rebuild_map: + self.course.build_map() class Meta: verbose_name = u'Урок' @@ -474,7 +480,10 @@ class Homework(models.Model): return result def save(self, *args, **kwargs): + must_rebuild_map = False if Homework.objects.filter(theme=self.theme, sort=self.sort).exclude(id=self.id).exists(): + # Обновить текущее дз с новым порядком, не вызывая при этом метод save() + Homework.objects.filter(id=self.id).update(sort=self.sort) # Переформировать порядок test_in = self.sort + 1 for homework in Homework.objects.filter(theme=self.theme, sort__gte=self.sort).exclude(id=self.id): @@ -482,8 +491,11 @@ class Homework(models.Model): homework.sort = test_in homework.save() test_in += 1 - self.token = self.course.build_map(material=self) + must_rebuild_map = True super(Homework, self).save(*args, **kwargs) + # Переформировать карту после сохранения текущего дз + if must_rebuild_map: + self.course.build_map() class Meta: verbose_name = u'Добашнее задание' @@ -541,7 +553,10 @@ class Exam(models.Model): return self.theme.get_title() def save(self, *args, **kwargs): + must_rebuild_map = False if Exam.objects.filter(theme=self.theme, sort=self.sort).exclude(id=self.id).exists(): + # Обновить текущий экзамен с новым порядком, не вызывая при этом метод save() + Exam.objects.filter(id=self.id).update(sort=self.sort) # Переформировать порядок test_in = self.sort + 1 for exam in Exam.objects.filter(theme=self.theme, sort__gte=self.sort).exclude(id=self.id): @@ -549,8 +564,11 @@ class Exam(models.Model): exam.sort = test_in exam.save() test_in += 1 - self.token = self.course.build_map(material=self) + must_rebuild_map = True super(Exam, self).save(*args, **kwargs) + # Переформировать карту после сохранения текущего экзамена + if must_rebuild_map: + self.course.build_map() class Meta: verbose_name = u'Экзамен' @@ -732,7 +750,7 @@ class AchievementsMap(models.Model): class Diploma(models.Model): - key = models.IntegerField(verbose_name=u'Последний ключ', max_length=255) + key = models.IntegerField(verbose_name=u'Последний ключ') icon = models.ImageField(verbose_name=u'Мини картинка', upload_to='diploms', null=True) course = models.ForeignKey(Course, verbose_name=u'Курс') out_image = models.ImageField(verbose_name=u'Диплом без печати', upload_to='diploms') @@ -743,21 +761,21 @@ class Diploma(models.Model): date_color = models.CharField(verbose_name=u'Цвет даты', max_length=255, null=True, help_text=u'RGB. Пример: 0,0,0') date_font = models.FileField(verbose_name=u'Шрифт даты', upload_to='diploms', null=True) - date_size = models.IntegerField(verbose_name=u'Размер даты', max_length=255, null=True) + date_size = models.IntegerField(verbose_name=u'Размер даты', null=True) # key_place = models.CharField(verbose_name=u'Размещение ключа', max_length=255, null=True, help_text=u'Пикселей сверху:пикселей слева - 256:256') key_color = models.CharField(verbose_name=u'Цвет ключа', max_length=255, null=True, help_text=u'RGB. Пример: 0,0,0') key_font = models.FileField(verbose_name=u'Шрифт ключа', upload_to='diploms', null=True) - key_size = models.IntegerField(verbose_name=u'Размер ключа', max_length=255, null=True) + key_size = models.IntegerField(verbose_name=u'Размер ключа', null=True) # name_place = models.CharField(verbose_name=u'Размещение имени', max_length=255, null=True, help_text=u'Пикселей сверху:пикселей слева - 256:256') name_color = models.CharField(verbose_name=u'Цвет имени', max_length=255, null=True, help_text=u'RGB. Пример: 0,0,0') name_font = models.FileField(verbose_name=u'Шрифт имени', upload_to='diploms', null=True) - name_size = models.IntegerField(verbose_name=u'Размер имени', max_length=255, null=True) + name_size = models.IntegerField(verbose_name=u'Размер имени', null=True) def __str__(self): return '%s' % self.course @@ -973,3 +991,9 @@ class Flow(models.Model): verbose_name = u'Поток' verbose_name_plural = u'Потоки' ordering = ['-modified'] + + +class NormalMap(models.Model): + course = models.OneToOneField(to=Course) + json_tree = models.TextField(default='') + independent_elements = models.TextField(default='') diff --git a/courses/new_urls.py b/courses/new_urls.py new file mode 100644 index 0000000..f531528 --- /dev/null +++ b/courses/new_urls.py @@ -0,0 +1,11 @@ +from django.conf.urls import url +from courses import new_view as views + +urlpatterns = [ + url(r'theme/detail/([0-9]{1,99})/$', views.ThemeDetailView.as_view()), + url(r'lesson/detail/([0-9]{1,99})/$', views.LessonDetailView.as_view()), + url(r'detail/([0-9]{1,99})/$', views.CourseDetailView.as_view()), + url(r'tree/([0-9]{1,99})/$', views.TreeView.as_view()), + url(r'directions/$', views.DirectionListView.as_view()), + url(r'^$', views.CourseListView.as_view()), +] \ No newline at end of file diff --git a/courses/serializers.py b/courses/serializers.py new file mode 100644 index 0000000..96f467d --- /dev/null +++ b/courses/serializers.py @@ -0,0 +1,159 @@ +from rest_framework import serializers +import json +# from django.contrib.auth import get_user_model +# from django.core.exceptions import ObjectDoesNotExist + +from courses.models import Course, CourseTheme, Lesson, Homework, Exam + + +class LessonSerializer(serializers.ModelSerializer): + on_comment = serializers.SerializerMethodField() + + class Meta: + model = Lesson + fields = ( + 'id', 'title', 'on_comment', 'materials', + 'free', 'video', 'video_id', + ) + + @staticmethod + def get_on_comment(self): + return self.on_comment == 'N' or self.on_comment == 'T' and self.theme.on_comment + + +class ThemeSerializer(serializers.ModelSerializer): + + class Meta: + model = CourseTheme + exclude = ('price_type', '_type', 'sort', 'on_comment') + + +class CourseListSerializer(serializers.ModelSerializer): + length = serializers.SerializerMethodField() + level = serializers.SerializerMethodField() + direction = serializers.SerializerMethodField() + + class Meta: + model = Course + fields = ['id', 'title', 'length', + 'level', 'direction', 'image',] + + @staticmethod + def get_length(self): + summary = 0 + for theme_slim in json.loads(self.normalmap.json_tree): + for simple_object in theme_slim['body']: + if simple_object.split('_')[1] == 'L': + summary += 1 + return summary + + @staticmethod + def get_level(self): + return self.get_level_display() + + @staticmethod + def get_direction(self): + return self.direction.title + + +class CourseTreeSerializer(serializers.ModelSerializer): + children = serializers.SerializerMethodField() + + class Meta: + model = Course + fields = ['id', 'title', 'children'] + + @staticmethod + def get_children(self): + theme_list = json.loads(self.normalmap.json_tree) + map = [] + for theme_slim in theme_list: + theme = CourseTheme.objects.get(id=theme_slim['id']) + theme_obj = { + 'id': theme.id, + 'title': theme.title, + 'lessons': [], + 'tasks': [], + } + for simple_object in theme_slim['body']: + val = simple_object.split('_')[0] + if simple_object.split('_')[1] == 'L': + lesson = Lesson.objects.get(id=val) + lesson_obj = {'id': lesson.id, 'title': lesson.title} + theme_obj['lessons'].append(lesson_obj) + + if simple_object.split('_')[1] == 'H': + task = Homework.objects.get(id=val) + task_obj = { + 'id': task.id, + 'is_exam': False, + } + theme_obj['tasks'].append(task_obj) + + if simple_object.split('_')[1] == 'E': + task = Exam.objects.get(id=val) + task_obj = { + 'id': task.id, + 'is_exam': True, + } + theme_obj['tasks'].append(task_obj) + + map.append(theme_obj) + + return map + + +class CourseDetailSerializer(serializers.ModelSerializer): + level = serializers.SerializerMethodField() + direction = serializers.SerializerMethodField() + teachers = serializers.SerializerMethodField() + + class Meta: + model = Course + exclude = ( + 'slug', 'mentors', 'page', 'sort', + 'preview', 'use_fail', 'basic_len', + 'addition_len', 'min_price', 'buy_icon', + 'must_build', 'keywords', 'recommend', + ) + + @staticmethod + def get_level(self): + return self.get_level_display() + + @staticmethod + def get_direction(self): + return self.direction.title + + @staticmethod + def get_teachers(self): + return [teacher.full_name() for teacher in self.teachers.all()] + + +# class UserSerializer(serializers.ModelSerializer): +# statistics = serializers.SerializerMethodField('get_statistic') +# games = serializers.SerializerMethodField('get_my_games') +# is_anonymous = serializers.BooleanField() +# +# @staticmethod +# def get_my_games(self): +# res = {} +# try: +# res['active'] = GameSerializer(self.game_set.get(state__lte=1)).data +# except ObjectDoesNotExist: +# res['active'] = {} +# +# res['archive'] = [GameSerializer(i).data for i in self.game_set.all().filter(state=2)] +# return res +# +# @staticmethod +# def get_statistic(self): +# try: +# statistics = StatisticSerializer(Statistic.objects.get(user=self)).data +# except ObjectDoesNotExist: +# statistics = {} +# return statistics +# +# class Meta: +# model = get_user_model() +# fields = ['id', 'username', 'email', 'is_active', 'statistics', 'games', 'is_anonymous'] \ No newline at end of file diff --git a/courses/templates.py b/courses/templates.py index 2c9a6ab..5c43473 100755 --- a/courses/templates.py +++ b/courses/templates.py @@ -193,7 +193,7 @@ BUTTON_TYPES = { BUTTON_TYPE = ({ 'flag': 'A', 'title': {'default': u'Открыть материал', - 'L': u'Перейти к уроку', + 'L': u'Перейти к следующему уроку', 'H': u'Перейти к заданию', 'E': u'Перейти к экзамену'}, 'color': {'default': '#fff'}, diff --git a/courses/update_db.py b/courses/update_db.py new file mode 100644 index 0000000..c7075cc --- /dev/null +++ b/courses/update_db.py @@ -0,0 +1,22 @@ +import os, sys +import django, json + +sys.path.append("../") +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings") +django.setup() + +from courses.models import Course, NormalMap, CourseTheme, Lesson, Homework, Exam + +if __name__ == '__main__': + for course in Course.objects.all(): + tree = [] + for theme in CourseTheme.objects.filter(course=course).order_by('sort'): + tree.append({'id': theme.id, 'body': + [str(i.id) + "_L" for i in Lesson.objects.filter(theme=theme).order_by('sort')] + + [str(i.id) + "_H" for i in Homework.objects.filter(theme=theme).order_by('sort')] + + [str(i.id) + "_E" for i in Exam.objects.filter(theme=theme).order_by('sort')] + }) + + obj, _is_create = NormalMap.objects.get_or_create(course=course) + obj.json_tree = json.dumps(tree) + obj.save() \ No newline at end of file diff --git a/fabfile.py b/fabfile.py index ff94072..2daf56a 100644 --- a/fabfile.py +++ b/fabfile.py @@ -296,7 +296,7 @@ def create(): with virtualenv(): #manage('migrate --run-syncdb --noinput') #manage('migrate --list') - manage("migrate --noinput") + #manage("migrate --noinput") manage("collectstatic -l --noinput") #if exists('%sgunicorn.pid'): diff --git a/finance/migrations/0071_auto_20170918_0811.py b/finance/migrations/0071_auto_20170918_0811.py new file mode 100644 index 0000000..ddf5f41 --- /dev/null +++ b/finance/migrations/0071_auto_20170918_0811.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.3 on 2017-09-18 08:11 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('courses', '0045_auto_20170918_0811'), + ('finance', '0070_auto_20170424_1638'), + ] + + operations = [ + migrations.AlterField( + model_name='bill', + name='create_letters', + field=models.ManyToManyField(blank=True, editable=False, related_name='bill_create_letter', to='service.MailBox', verbose_name='Письма при создании'), + ), + migrations.AlterField( + model_name='bill', + name='finish_letters', + field=models.ManyToManyField(blank=True, editable=False, related_name='bill_finish_letter', to='service.MailBox', verbose_name='Письма завершения'), + ), + migrations.AlterField( + model_name='installment', + name='payments', + field=models.ManyToManyField(blank=True, related_name='bill_point', to='finance.Bill', verbose_name='Платежи'), + ), + migrations.AlterField( + model_name='price', + name='included', + field=models.ManyToManyField(blank=True, help_text='Если задействовать эту функцию, стандартная схема подписок будет не активна', to='courses.CourseMap', verbose_name='Включены'), + ), + migrations.AlterField( + model_name='requestalert', + name='requests', + field=models.ManyToManyField(blank=True, editable=False, to='finance.ServiceRequest', verbose_name='Количество заявок'), + ), + ] diff --git a/finance/migrations/0071_bill_flow.py b/finance/migrations/0071_bill_flow.py deleted file mode 100644 index 0732ea7..0000000 --- a/finance/migrations/0071_bill_flow.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.3 on 2017-08-10 09:04 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('courses', '0045_auto_20170810_0904'), - ('finance', '0070_auto_20170424_1638'), - ] - - operations = [ - migrations.AddField( - model_name='bill', - name='flow', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='courses.Flow', verbose_name='Поток'), - ), - ] diff --git a/finance/models.py b/finance/models.py index 3ac1b01..e67ccb8 100755 --- a/finance/models.py +++ b/finance/models.py @@ -30,7 +30,7 @@ class Installment(models.Model): u'предоставлять доступ после ' u'просрочки') expired = models.BooleanField(verbose_name=u'Просрочено', default=False) - payments = models.ManyToManyField('Bill', verbose_name=u'Платежи', blank=True, null=True, related_name='bill_point') + payments = models.ManyToManyField('Bill', verbose_name=u'Платежи', blank=True, related_name='bill_point') def __str__(self): return '%s %s' % (self.user, self.price) @@ -54,7 +54,7 @@ class Price(models.Model): cost = models.IntegerField(verbose_name=u'Цена') course = models.ForeignKey(Course, verbose_name=u'Курс', null=True, blank=True) description = models.TextField(verbose_name=u'Описание', help_text=u'Будет показано менеджерам') - included = models.ManyToManyField(CourseMap, verbose_name=u'Включены', null=True, blank=True, + included = models.ManyToManyField(CourseMap, verbose_name=u'Включены', blank=True, help_text=u'Если задействовать эту функцию, стандартная схема подписок будет ' u'не активна') by_time = models.IntegerField(verbose_name=u'Дней доступа', default=0, blank=True, null=True, @@ -157,8 +157,8 @@ class Bill(models.Model): fire_date_sent = models.BooleanField(verbose_name=u'Письмо сгорания', default=False, editable=False) modals_show = models.BooleanField(verbose_name=u'Модалки показаны', default=False, editable=False) created_sent = models.BooleanField(verbose_name=u'Отправлено письмо сформированного счета', default=False, editable=False) - create_letters = models.ManyToManyField(MailBox, verbose_name=u'Письма при создании', null=True, blank=True, related_name='bill_create_letter', editable=False) - finish_letters = models.ManyToManyField(MailBox, verbose_name=u'Письма завершения', null=True, blank=True, related_name='bill_finish_letter', editable=False) + create_letters = models.ManyToManyField(MailBox, verbose_name=u'Письма при создании', blank=True, related_name='bill_create_letter', editable=False) + finish_letters = models.ManyToManyField(MailBox, verbose_name=u'Письма завершения', blank=True, related_name='bill_finish_letter', editable=False) def __unicode__(self): return u'%s:%s %s %s' % (self.id, self.get_status_display(), self.user, self.manager if self.manager else '') @@ -444,7 +444,7 @@ class ServiceRequest(models.Model): class RequestAlert(models.Model): title = models.CharField(verbose_name=u'Классификатор', max_length=255, editable=False) - requests = models.ManyToManyField(ServiceRequest, verbose_name=u'Количество заявок', blank=True, null=True, editable=False) + requests = models.ManyToManyField(ServiceRequest, verbose_name=u'Количество заявок', blank=True, editable=False) mails = models.TextField(verbose_name=u'Список почт', blank=True, help_text=u'Кому разослать дубли заявки. ЧЕРЕЗ ТОЧКУ С ЗАПЯТОЙ') date = models.DateTimeField(verbose_name=u'Начало заявок', default=datetime.datetime.now, editable=False) diff --git a/journals/migrations/0073_auto_20170918_0811.py b/journals/migrations/0073_auto_20170918_0811.py new file mode 100644 index 0000000..cb51589 --- /dev/null +++ b/journals/migrations/0073_auto_20170918_0811.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.3 on 2017-09-18 08:11 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0072_auto_20170810_0904'), + ] + + operations = [ + migrations.AlterField( + model_name='diplomaj', + name='key', + field=models.IntegerField(blank=True, null=True, verbose_name='Ключ'), + ), + migrations.AlterField( + model_name='teacherj', + name='waiting', + field=models.ManyToManyField(blank=True, related_name='map_waiting', to='courses.CourseMap', verbose_name='Доп изучения'), + ), + ] diff --git a/journals/models.py b/journals/models.py index d37b418..d033516 100755 --- a/journals/models.py +++ b/journals/models.py @@ -34,8 +34,8 @@ class TeacherJ(models.Model): student = models.ForeignKey(User, verbose_name=u'Студент', related_name=u'teacherJ_student', null=True, blank=True) full = models.BooleanField(verbose_name=u'Журнал создан', default=False) course = models.ForeignKey(Course, verbose_name=u'Курс') - homework = models.ForeignKey(Homework, verbose_name=u'ДЗ', blank=True, null=True) - lesson = models.ForeignKey(Lesson, verbose_name=u'Урок', blank=True, null=True) + homework = models.ForeignKey(Homework, verbose_name=u'ДЗ', blank=True, null=True, on_delete=None) + lesson = models.ForeignKey(Lesson, verbose_name=u'Урок', blank=True, null=True, on_delete=None) progress = models.IntegerField(verbose_name=u'Прогресс', default=0) current_token = models.CharField(verbose_name=u'Текущий ключ', blank=True, default='', max_length=100) teacher = models.ForeignKey(User, verbose_name=u'Преподаватель', related_name=u'teacherJ_teacher', blank=True, @@ -43,7 +43,7 @@ class TeacherJ(models.Model): start_date = models.DateTimeField(verbose_name=u'Дата начала', blank=True, null=True) opens = models.ManyToManyField(CourseMap, verbose_name=u'Насильные открытия', blank=True, editable=False, related_name='map_opens') - waiting = models.ManyToManyField(CourseMap, verbose_name=u'Доп изучения', blank=True, null=True, + waiting = models.ManyToManyField(CourseMap, verbose_name=u'Доп изучения', blank=True, related_name='map_waiting') def __str__(self): @@ -246,7 +246,7 @@ class CourseThemeJ(models.Model): material = models.ForeignKey(CourseTheme, verbose_name=u'Тема') date = models.DateTimeField(verbose_name=u'Дата начала', blank=True, null=True) f_date = models.DateTimeField(verbose_name=u'Дата завершения', blank=True, null=True) - actual_lesson = models.ForeignKey(Lesson, verbose_name=u'Актуальны урок для этой темы', blank=True, null=True) + actual_lesson = models.ForeignKey(Lesson, verbose_name=u'Актуальны урок для этой темы', blank=True, null=True, on_delete=None) def __unicode__(self): return u'%s: %s %s' % (self.id, self.material, self.student) @@ -785,7 +785,7 @@ class HomeworkJ(models.Model): elif _type == 'E': obj = ExamJ - j = obj.objects.get(material=_next, student=self.student) + j = obj.objects.get_or_create(material=_next, student=self.student) if self.success: j.open_material() return {'button': search_in_collection(BUTTON_TYPE, @@ -1398,7 +1398,7 @@ class DiplomaJ(models.Model): parent = models.ForeignKey(ExamJ, verbose_name=u'Журнал сдачи', blank=True, null=True) material = models.ForeignKey(Diploma, verbose_name=u'Шаблон диплома') date = models.DateField(verbose_name=u'Дата выдачи', default=datetime.date.today) - key = models.IntegerField(verbose_name=u'Ключ', max_length=255, blank=True, null=True) + key = models.IntegerField(verbose_name=u'Ключ', blank=True, null=True) student = models.ForeignKey(User, verbose_name=u'Студент', null=True) out_image = models.ImageField(verbose_name=u'Картинка без печати', upload_to='diploms', blank=True) in_image = models.ImageField(verbose_name=u'Картинка c печатью', upload_to='diploms', blank=True) diff --git a/library/migrations/0023_auto_20170918_0811.py b/library/migrations/0023_auto_20170918_0811.py new file mode 100644 index 0000000..cec297c --- /dev/null +++ b/library/migrations/0023_auto_20170918_0811.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.3 on 2017-09-18 08:11 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('library', '0022_auto_20161101_1321'), + ] + + operations = [ + migrations.AlterField( + model_name='article', + name='comments', + field=models.ManyToManyField(blank=True, editable=False, to='management.Comment', verbose_name='Комментарии'), + ), + migrations.AlterField( + model_name='article', + name='favorite', + field=models.ManyToManyField(blank=True, editable=False, related_name='article_as_favorites', to=settings.AUTH_USER_MODEL, verbose_name='В фаворитах'), + ), + migrations.AlterField( + model_name='article', + name='likes', + field=models.ManyToManyField(blank=True, editable=False, max_length=255, related_name='acticle_likes', to=settings.AUTH_USER_MODEL, verbose_name='Лайки'), + ), + migrations.AlterField( + model_name='article', + name='tags', + field=models.ManyToManyField(blank=True, to='library.Tags', verbose_name='Теги'), + ), + ] diff --git a/library/models.py b/library/models.py index 94bad25..3dd0c20 100755 --- a/library/models.py +++ b/library/models.py @@ -36,12 +36,12 @@ class Article(models.Model): css = models.TextField(verbose_name=u'CSS', blank=True, default='', help_text=u'Можно добавлять все, но только исправить http на https') js = models.TextField(verbose_name=u'JS', blank=True, default='', help_text=u'Удалить bootstrap, jquery и заменить http на https') date = models.DateTimeField(verbose_name=u'Дата публикации', default=datetime.datetime.now, editable=False) - tags = models.ManyToManyField('Tags', verbose_name=u'Теги', blank=True, null=True) - likes = models.ManyToManyField(User, verbose_name=u'Лайки', max_length=255, null=True, blank=True, editable=False, related_name=u'acticle_likes') + tags = models.ManyToManyField('Tags', verbose_name=u'Теги', blank=True) + likes = models.ManyToManyField(User, verbose_name=u'Лайки', max_length=255, blank=True, editable=False, related_name=u'acticle_likes') views = models.ManyToManyField(User, verbose_name=u'Просмотры', max_length=255, default=0, related_name='article_views', editable=False) all_views = models.IntegerField(verbose_name=u'Всего просмотров', default=0, blank=True) - comments = models.ManyToManyField(Comment, verbose_name=u'Комментарии', null=True, blank=True, editable=False) - favorite = models.ManyToManyField(User, verbose_name=u'В фаворитах', blank=True, null=True, related_name='article_as_favorites', editable=False) + comments = models.ManyToManyField(Comment, verbose_name=u'Комментарии', blank=True, editable=False) + favorite = models.ManyToManyField(User, verbose_name=u'В фаворитах', blank=True, related_name='article_as_favorites', editable=False) def __str__(self): return '%s' % self.title diff --git a/lms/settings.py b/lms/settings.py index 930b62a..6dafaf5 100644 --- a/lms/settings.py +++ b/lms/settings.py @@ -61,8 +61,8 @@ EMAIL_PORT = '587' EMAIL_USE_TLS = True DEFAULT_FROM_EMAIL = 'robo@skillbox.ru' YANDEX_MONEY_SHOP_PASSWORD = 'nu5Xefise' -YANDEX_SHOP_ID = '84348' -YANDEX_scid = '78309' +YANDEX_SHOP_ID = '157133' +YANDEX_scid = '149639' # Application definition # место куда сохраняем пользовательские файлы @@ -104,6 +104,7 @@ MIDDLEWARE_CLASSES = [ 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'access.middleware.CheckPerm', + 'access.middleware.RequestToApi', ] ROOT_URLCONF = 'lms.urls' diff --git a/lms/urls.py b/lms/urls.py index a389dde..8d6616d 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -24,6 +24,7 @@ urlpatterns = [ url(r'^management/', include('management.urls')), url(r'^teacher/', include('access.teach_urls')), url(r'^wallet/', include('finance.urls')), + url(r'^api/v1/', include('api_v1.urls')), url(r'^courses/', include('courses.urls')), url(r'^journals/', include('journals.urls')), url(r'^library/', include('library.urls')), diff --git a/lms/views.py b/lms/views.py index 41f3d5b..e403538 100644 --- a/lms/views.py +++ b/lms/views.py @@ -10,7 +10,11 @@ from finance.models import Bill from courses.templates import comment_fabric from library.models import Article from management.reports import get_now_success_hw, get_second_success_hw -from django.http import HttpResponse +from django.http import HttpResponse +from django.template.defaulttags import register +from lms.settings import STATIC_ROOT +import csv + def sortByTimeStamp(inputStr): return inputStr['sort'] diff --git a/management/migrations/0091_auto_20170918_0811.py b/management/migrations/0091_auto_20170918_0811.py new file mode 100644 index 0000000..233c02b --- /dev/null +++ b/management/migrations/0091_auto_20170918_0811.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.3 on 2017-09-18 08:11 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('management', '0090_auto_20161230_1643'), + ] + + operations = [ + migrations.AlterField( + model_name='comment', + name='answers', + field=models.ManyToManyField(blank=True, editable=False, related_name='_comment_answers_+', to='management.Comment', verbose_name='Форумные ответы'), + ), + migrations.AlterField( + model_name='comment', + name='replies', + field=models.ManyToManyField(blank=True, editable=False, related_name='_comment_replies_+', to='management.Comment', verbose_name='Ответы'), + ), + migrations.AlterField( + model_name='faq', + name='comments', + field=models.ManyToManyField(blank=True, editable=False, to='management.Comment', verbose_name='Комментарии'), + ), + ] diff --git a/management/models.py b/management/models.py index 3333cec..2594365 100755 --- a/management/models.py +++ b/management/models.py @@ -154,8 +154,8 @@ class Comment(models.Model): date = models.DateTimeField(verbose_name=u'Отправка', default=datetime.datetime.now) files = models.ManyToManyField(Storage, verbose_name=u'Прикрепленые файлы', blank=True, editable=False) send = models.BooleanField(verbose_name=u'Отправлено', default=False, editable=False) - replies = models.ManyToManyField('self', verbose_name=u'Ответы', blank=True, null=True, related_name='all_text_response', editable=False) - answers = models.ManyToManyField('self', verbose_name=u'Форумные ответы', blank=True, null=True, related_name='forum_answers', editable=False) + replies = models.ManyToManyField('self', verbose_name=u'Ответы', blank=True, related_name='all_text_response', editable=False) + answers = models.ManyToManyField('self', verbose_name=u'Форумные ответы', blank=True, related_name='forum_answers', editable=False) def __unicode__(self): return str(self.id) @@ -329,7 +329,7 @@ class Faq(models.Model): question = models.CharField(verbose_name=u'Вопрос', max_length=255) answer = RedactorField(verbose_name=u'Ответ') rating = models.IntegerField(verbose_name=u'Рейтинг статьи', default=0) - comments = models.ManyToManyField(Comment, verbose_name=u'Комментарии', blank=True, null=True, editable=False) + comments = models.ManyToManyField(Comment, verbose_name=u'Комментарии', blank=True, editable=False) date = models.DateTimeField(verbose_name=u'Дата создания', default=datetime.datetime.now, editable=False) def __str__(self): diff --git a/oferta.pdf b/oferta.pdf index 5aab4e1..00a26f1 100644 Binary files a/oferta.pdf and b/oferta.pdf differ diff --git a/practice/migrations/0021_auto_20170918_0811.py b/practice/migrations/0021_auto_20170918_0811.py new file mode 100644 index 0000000..1ea42b9 --- /dev/null +++ b/practice/migrations/0021_auto_20170918_0811.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.3 on 2017-09-18 08:11 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('practice', '0020_workshop_body'), + ] + + operations = [ + migrations.AlterField( + model_name='datasheet', + name='users', + field=models.ManyToManyField(blank=True, editable=False, to=settings.AUTH_USER_MODEL, verbose_name='Пользователи'), + ), + migrations.AlterField( + model_name='workshop', + name='tools', + field=models.ManyToManyField(blank=True, to='practice.WorkshopTools', verbose_name='Используемые инструменты'), + ), + migrations.AlterField( + model_name='workshop', + name='users', + field=models.ManyToManyField(blank=True, editable=False, to=settings.AUTH_USER_MODEL, verbose_name='Пользователи'), + ), + ] diff --git a/practice/models.py b/practice/models.py index d2fc2a1..1fcb7aa 100644 --- a/practice/models.py +++ b/practice/models.py @@ -53,7 +53,7 @@ class DataSheet(models.Model): data = models.TextField(verbose_name=u'Данные') split_char = models.CharField(verbose_name=u'Разделяющий символ', max_length=10, default=', ', blank=True) date = models.DateTimeField(verbose_name=u'Дата создания', default=datetime.datetime.now, editable=False) - users = models.ManyToManyField(User, verbose_name=u'Пользователи', blank=True, null=True, editable=False) + users = models.ManyToManyField(User, verbose_name=u'Пользователи', blank=True, editable=False) def __unicode__(self): return u'%s' % self.title @@ -117,13 +117,13 @@ class Workshop(models.Model): content = RedactorField(verbose_name=u'Описание методов', blank=True, default='', help_text=u'Добавление в /, после описаний и решений') variables = RedactorField(verbose_name=u'Описание переменных', blank=True, default='') - tools = models.ManyToManyField(WorkshopTools, verbose_name=u'Используемые инструменты', blank=True, null=True) + tools = models.ManyToManyField(WorkshopTools, verbose_name=u'Используемые инструменты', blank=True) open_resolve = models.BooleanField(verbose_name=u'Открыть ответ', default=False) resolve = RedactorField(verbose_name=u'Ответ', blank=True, default='') will_open_resolve = models.DateTimeField(verbose_name=u'Открыть ответ в дату', blank=True, null=True, help_text=u'Доступ к ответу открыть в указанную дату, если это вообще нужно') date = models.DateTimeField(verbose_name=u'Дата создания', default=datetime.datetime.now, editable=False) - users = models.ManyToManyField(User, verbose_name=u'Пользователи', blank=True, null=True, editable=False) + users = models.ManyToManyField(User, verbose_name=u'Пользователи', blank=True, editable=False) views = models.IntegerField(verbose_name=u'Просмотров комнаты', default=0) def __unicode__(self): diff --git a/requirements.txt b/requirements.txt index 08a729c..8acb06a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,6 +13,7 @@ contextlib2==0.5.4 cycler==0.10.0 decorator==4.0.10 diff-match-patch==20110725.1 +djangorestframework==3.6.4 django-admin-tools==0.8.0 django-celery==3.1.17 django-import-export==0.4.5 diff --git a/templates/articles.html b/templates/articles.html index 45075dd..3a7df14 100755 --- a/templates/articles.html +++ b/templates/articles.html @@ -66,13 +66,13 @@ -
- {% include 'comments_block.html' %} -
+{#
#} +{# {% include 'comments_block.html' %}#} +{#
#}