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

remotes/origin/pm_task_31703
Andrey 8 years ago
commit 6926b4c694
  1. 26
      README.md
  2. 89
      _utils/check_course_journal.py
  3. 22
      access/middleware.py
  4. 21
      access/migrations/0090_auto_20170918_0811.py
  5. 9
      access/models.py
  6. 12
      access/new_urls.py
  7. 100
      access/new_view.py
  8. 10
      access/serializers.py
  9. 6
      api_v1/urls.py
  10. 75
      configs/skillbox_ssl/privat.key
  11. 138
      configs/skillbox_ssl/public.pem
  12. 22
      courses/migrations/0045_auto_20170810_0904.py
  13. 53
      courses/migrations/0045_auto_20170918_0811.py
  14. 44
      courses/models.py
  15. 11
      courses/new_urls.py
  16. 159
      courses/serializers.py
  17. 2
      courses/templates.py
  18. 22
      courses/update_db.py
  19. 2
      fabfile.py
  20. 42
      finance/migrations/0071_auto_20170918_0811.py
  21. 22
      finance/migrations/0071_bill_flow.py
  22. 10
      finance/models.py
  23. 25
      journals/migrations/0073_auto_20170918_0811.py
  24. 12
      journals/models.py
  25. 36
      library/migrations/0023_auto_20170918_0811.py
  26. 8
      library/models.py
  27. 5
      lms/settings.py
  28. 1
      lms/urls.py
  29. 6
      lms/views.py
  30. 30
      management/migrations/0091_auto_20170918_0811.py
  31. 6
      management/models.py
  32. BIN
      oferta.pdf
  33. 31
      practice/migrations/0021_auto_20170918_0811.py
  34. 6
      practice/models.py
  35. 1
      requirements.txt
  36. 8
      templates/articles.html
  37. 2
      templates/contacts.html
  38. 2
      templates/mails/new_users.html

@ -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) Сайт будет доступен по адресу <branch_name>.lms.test.spicycms.com
Фактически в структуре данных изменилось только то, что появилась новая табличка NormalMap. Скрипт собирает все treeview курсов и кладёт в эту табличку
**Правила для разработчика**
Появилась новая зависимость django_rest_framework.
* Разрабока ведется в отдельном брэнче
* После комита изменений можно проверить свою работу по следующему адресу: .lms.test.spicycms.com
* После проверки тестового сайта по имени брэнча, брэнч помечается к мерж реквесту
* Ответсвенный инженер производит код ревью и запускает/отклоняет мерж
* Если сайт запустился на демо сайте, код в ручом режиме можно обновить на продакшен сервере
Изменения никак не трогают старый функционал.
**Замечания**
* Мерж реквест лучше производить в ветку develop(чтобы исключить случайного обновления кода на демо сайте)
* Внимание! Разработчик обязан проверить работоспособность тестового сайта по имени созданного брэнча .lms.test.spicycms.com
* Если необходимы тестовые данные разработчик их подготавливает дополнительно
* Если нет возможности добавить тестовые данные в авто режиме, разработчик добавляет инструкцию в README файл по запуску необходимых команд
(Под тестовыми данными подразумеваются фикстуры)
Имеет смысл пробежаться по middl

@ -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)

@ -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)

@ -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='Задержка до. Не включая этот день.'),
),
]

@ -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:

@ -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()),
]

@ -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)

@ -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']

@ -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')),
]

@ -1,52 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

@ -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-----

@ -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='Блок комментариев'),
),
]

@ -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='Кураторы'),
),
]

@ -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='')

@ -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()),
]

@ -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']

@ -193,7 +193,7 @@ BUTTON_TYPES = {
BUTTON_TYPE = ({
'flag': 'A',
'title': {'default': u'Открыть материал',
'L': u'Перейти к уроку',
'L': u'Перейти к следующему уроку',
'H': u'Перейти к заданию',
'E': u'Перейти к экзамену'},
'color': {'default': '#fff'},

@ -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()

2
fabfile.py vendored

@ -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'):

@ -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='Количество заявок'),
),
]

@ -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='Поток'),
),
]

@ -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)

@ -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='Доп изучения'),
),
]

@ -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)

@ -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='Теги'),
),
]

@ -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

@ -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'

@ -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')),

@ -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']

@ -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='Комментарии'),
),
]

@ -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):

Binary file not shown.

@ -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='Пользователи'),
),
]

@ -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'Добавление в /<body/>, после описаний и решений')
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):

@ -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

@ -66,13 +66,13 @@
</div>
<div class="row" style=" box-sizing: border-box;padding: 40px;clear: both;margin-top: 40px;">
{% include 'comments_block.html' %}
</div>
{# <div class="row" style=" box-sizing: border-box;padding: 40px;clear: both;margin-top: 40px;">#}
{# {% include 'comments_block.html' %}#}
{# </div>#}
<div id="nav_block">
<div id="nav_in">
<span id="button" data-toggle="tooltip" data-placement="bottom" title="Назад" onclick="location.href = '{{ DOMAIN }}library/articles/';"><i class="glyphicon glyphicon-arrow-left"></i> назад</span>
<span id="button" data-toggle="tooltip" data-placement="bottom" title="Назад" onclick="location.href = '{{ DOMAIN }}/library/articles/';"><i class="glyphicon glyphicon-arrow-left"></i> назад</span>
<hr noshade>
<span id="button" class="{% if article.like %}active{% endif %}" data-toggle="tooltip" data-placement="bottom" title="Нравится" onclick="{% if not article.like %}ajax_set_like{% else %}ajax_unset_like{% endif %}(this)" article_id="{{ article.id }}"><i class="glyphicon glyphicon-thumbs-up"></i> </span>
<span id="button" class="{% if article.favorite %}active{% endif %}" data-toggle="tooltip" data-placement="bottom" title="В избранное" onclick="{% if not article.favorite %}ajax_set_favorit{% else %}ajax_unset_favorit{% endif %}(this)" article_id="{{ article.id }}"><i class="glyphicon glyphicon-star "></i></span>

@ -23,7 +23,7 @@
<div class="col-xs-12 col-sm-12 col-md-6 col-lg-3" style=" padding-top: 30px;
padding-bottom: 20px;">
<b style="padding-bottom: 5px;">Юридическая информация</b><Br>
<p style="margin-top: 5px;">ИП Коропов Игорь Валентинович<br> ИНН 504793474705<br> ОГРНИП 314504727900026<br></p>
<p style="margin-top: 5px;">ООО «СКИЛБОКС» <br> ИНН 9701078611<br> ОГРН 1177746566140<br></p>
</div>
<div class="col-xs-12 col-sm-12 col-md-6 col-lg-3" style="padding-top: 30px; padding-bottom: 20px; width: 500px;">
<b style="padding-bottom: 5px;">Контакты</b><Br>

@ -21,7 +21,7 @@
• С главной страницы (http://go.skillbox.ru/) перейдите в раздел с вашим курсом, кликнув по иконке курса «Профессия PR-менеджер».<br>
• Вы попадёте на страницу с перечнем тем (модулей) и уроков которые вам сейчас доступны<br>
• Первая тема курса уже доступна для просмотра. Нажимайте на кнопку «Перейти к уроку», и приступайте к просмотру видеоурока.<br><br>
• Первая тема курса уже доступна для просмотра. Нажимайте на кнопку «Перейти к следующему уроку», и приступайте к просмотру видеоурока.<br><br>
Отправка домашних заданий<br>
• Просматриваете уроки, отмечаете их «просмотренными» (большая зеленая кнопка над и под видео)<br>

Loading…
Cancel
Save