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-----
-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
+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
-----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' %}#}
+{#
#}
-
назад
+
назад
diff --git a/templates/contacts.html b/templates/contacts.html
index 141fbd8..b4d7229 100755
--- a/templates/contacts.html
+++ b/templates/contacts.html
@@ -23,7 +23,7 @@
Юридическая информация
-
ИП Коропов Игорь Валентинович
ИНН 504793474705
ОГРНИП 314504727900026
+
ООО «СКИЛБОКС»
ИНН 9701078611
ОГРН 1177746566140
Контакты
diff --git a/templates/mails/new_users.html b/templates/mails/new_users.html
index c720e63..9542bfe 100644
--- a/templates/mails/new_users.html
+++ b/templates/mails/new_users.html
@@ -21,7 +21,7 @@
• С главной страницы (http://go.skillbox.ru/) перейдите в раздел с вашим курсом, кликнув по иконке курса «Профессия PR-менеджер».
• Вы попадёте на страницу с перечнем тем (модулей) и уроков которые вам сейчас доступны
-• Первая тема курса уже доступна для просмотра. Нажимайте на кнопку «Перейти к уроку», и приступайте к просмотру видеоурока.
+• Первая тема курса уже доступна для просмотра. Нажимайте на кнопку «Перейти к следующему уроку», и приступайте к просмотру видеоурока.
Отправка домашних заданий
• Просматриваете уроки, отмечаете их «просмотренными» (большая зеленая кнопка над и под видео)