Merge branch 'develop' into 'master'

Develop

See merge request skillbox/go.skillbox.ru!56
remotes/origin/pm_task_31703
Zolotarev Alexander 8 years ago
commit c6430ebc4f
  1. 26
      README.md
  2. 23
      access/middleware.py
  3. 9
      access/models.py
  4. 7
      access/new_urls.py
  5. 48
      access/new_view.py
  6. 9
      access/serializers.py
  7. 6
      api_v1/urls.py
  8. 24
      courses/migrations/0048_normalmap.py
  9. 25
      courses/migrations/0049_auto_20170907_1753.py
  10. 20
      courses/models.py
  11. 11
      courses/new_urls.py
  12. 72
      courses/new_view.py
  13. 159
      courses/serializers.py
  14. 22
      courses/update_db.py
  15. 2
      fabfile.py
  16. 22
      finance/migrations/0071_bill_flow.py
  17. 10
      finance/models.py
  18. 4
      journals/models.py
  19. 8
      library/models.py
  20. 1
      lms/settings.py
  21. 1
      lms/urls.py
  22. 6
      management/models.py
  23. 6
      practice/models.py
  24. 1
      requirements.txt

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

@ -1,11 +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 "/management" 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()
@ -14,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)

@ -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,7 @@
from django.conf.urls import url
from access import new_view as views
urlpatterns = [
url(r'teachers/$', views.TeacherListView.as_view()),
url(r'check/$', views.CheckUserView.as_view()),
]

@ -0,0 +1,48 @@
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
class TeacherListView(APIView):
renderer_classes = (JSONRenderer,)
status_code = 200
def get(self, request):
return Response([teacher.full_name() for teacher in get_user_model().objects.filter(
in_role='T',
is_active=True,
reg_status=4,
)], 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 LoginView(APIView):
renderer_classes = (JSONRenderer,)
status_code = 200
def get(self, request):
if not request.user.is_authenticated():
user = auth.authenticate(email=request.JSON.get('email'), password=request.JSON.get('password'))
return Response(UserInitSerializer(user).data, status=self.status_code)
return Response(status=403)
class LogoutView(APIView):
renderer_classes = (JSONRenderer,)
status_code = 204
def get(self, request):
return Response(False, status=self.status_code)

@ -0,0 +1,9 @@
from django.contrib.auth import get_user_model
from rest_framework import serializers
class UserInitSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = '__all__'

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

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.3 on 2017-09-04 13:56
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('courses', '0047_auto_20170904_1355'),
]
operations = [
migrations.CreateModel(
name='NormalMap',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('json_tree', models.TextField(default='')),
('course', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='courses.Course')),
],
),
]

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.3 on 2017-09-07 17:53
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('courses', '0048_normalmap'),
]
operations = [
migrations.AddField(
model_name='normalmap',
name='independent_elements',
field=models.TextField(default=''),
),
migrations.AlterField(
model_name='course',
name='level',
field=models.CharField(choices=[('B', 'Базовый'), ('A', 'Продвинутый'), ('E', 'Экспертный'), ('B+A', 'Базовый + Продвинутый')], default='B', max_length=3, 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)
@ -732,7 +732,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 +743,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 +973,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,72 @@
from rest_framework.views import APIView
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
from courses.models import Course, MaterialDirection, CourseTheme, Lesson
from courses.serializers import CourseTreeSerializer, CourseDetailSerializer, CourseListSerializer,\
ThemeSerializer, LessonSerializer
from finance.models import Bill
class DirectionListView(APIView):
renderer_classes = (JSONRenderer,)
status_code = 200
def get(self, request):
return Response([direction.title for direction in MaterialDirection.objects.all()], self.status_code)
class TreeView(APIView):
renderer_classes = (JSONRenderer,)
status_code = 200
@staticmethod
def post(request):
print(request)
return Response(status=204)
def get(self, request, id):
return Response(CourseTreeSerializer(Course.objects.get(id=id)).data, self.status_code)
class ThemeDetailView(APIView):
renderer_classes = (JSONRenderer,)
status_code = 200
def get(self, request, id):
return Response(ThemeSerializer(CourseTheme.objects.get(id=id)).data, self.status_code)
class LessonDetailView(APIView):
renderer_classes = (JSONRenderer,)
status_code = 200
def get(self, request, id):
return Response(LessonSerializer(Lesson.objects.get(id=id)).data, self.status_code)
class CourseDetailView(APIView):
renderer_classes = (JSONRenderer,)
status_code = 200
def get(self, request, id):
return Response(CourseDetailSerializer(Course.objects.get(id=id)).data, self.status_code)
class CourseListView(APIView):
renderer_classes = (JSONRenderer,)
status_code = 200
def get(self, request):
if not request.GET.get('staff', 'true') == 'false':
return Response([CourseListSerializer(i).data for i in Course.objects.all()], self.status_code)
res = []
for course in Course.objects.all():
if course.public:
course_serialize = CourseListSerializer(course).data
course_serialize['is_mine'] = False
if Bill.objects.filter(service__course=course, user=request.user, status='F').exists():
course_serialize['is_mine'] = True
res.append(course_serialize)
return Response(res, self.status_code)

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

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

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

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

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

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

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

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

Loading…
Cancel
Save