diff --git a/README.md b/README.md index a50f717..012c98c 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,4 @@ #**SkillBox LMS** -Нужно выполнить миграции и запустить скрипт update_db.py без параметров в модуле courses (скрипт на всякий случай пока не удалять) - -Фактически в структуре данных изменилось только то, что появилась новая табличка NormalMap. Скрипт собирает все treeview курсов и кладёт в эту табличку - -Появилась новая зависимость django_rest_framework. - -Изменения никак не трогают старый функционал. - -Имеет смысл пробежаться по middl +DROP TABLE courses_normalmap; +снести табличку вручную \ No newline at end of file diff --git a/access/migrations/0091_privilege.py b/access/migrations/0091_privilege.py index 450510e..3ba3ba7 100644 --- a/access/migrations/0091_privilege.py +++ b/access/migrations/0091_privilege.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.3 on 2017-09-21 17:07 +# Generated by Django 1.9.3 on 2017-09-25 18:29 from __future__ import unicode_literals from django.db import migrations, models @@ -16,7 +16,7 @@ class Migration(migrations.Migration): name='Privilege', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('value', models.CharField(choices=[('r', 'Отображение'), ('u', 'Использование'), ('w', 'Изменение')], default='r', max_length=1, verbose_name='Права')), + ('value', models.CharField(choices=[('r', 'Доступно для выполнению'), ('w', 'Ждёт ответа'), ('d', 'Выполнено')], default='r', max_length=1, verbose_name='Права')), ], ), ] diff --git a/access/migrations/0092_auto_20170921_1707.py b/access/migrations/0092_auto_20170925_1829.py similarity index 89% rename from access/migrations/0092_auto_20170921_1707.py rename to access/migrations/0092_auto_20170925_1829.py index fbdf0c3..29ef648 100644 --- a/access/migrations/0092_auto_20170921_1707.py +++ b/access/migrations/0092_auto_20170925_1829.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.3 on 2017-09-21 17:07 +# Generated by Django 1.9.3 on 2017-09-25 18:29 from __future__ import unicode_literals from django.conf import settings @@ -10,8 +10,8 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ + ('courses', '0046_auto_20170925_1829'), ('access', '0091_privilege'), - ('courses', '0046_auto_20170921_1707'), ] operations = [ diff --git a/access/new_view.py b/access/new_view.py index 80c05aa..6df40c0 100644 --- a/access/new_view.py +++ b/access/new_view.py @@ -32,7 +32,8 @@ class CheckUserView(APIView): 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): + if request.user.is_authenticated() and\ + (request.user.in_role in ['M', 'S', 'S2', 'A', 'T'] or request.user.is_admin): return Response(True, status=self.status_code) return Response(False, status=self.status_code) diff --git a/access/serializers.py b/access/serializers.py index ecf2693..bcb2386 100644 --- a/access/serializers.py +++ b/access/serializers.py @@ -3,8 +3,13 @@ from rest_framework import serializers class UserInitSerializer(serializers.ModelSerializer): + in_role = serializers.SerializerMethodField() class Meta: model = get_user_model() - fields = ['id', 'email', 'phone', 'name', - 'fname', 'oname', 'city', 'b_day'] \ No newline at end of file + fields = ['id', 'email', 'phone', 'name', 'in_avatar', + 'fname', 'oname', 'city', 'b_day', 'in_role',] + + @staticmethod + def get_in_role(self): + return self.get_in_role_display() \ No newline at end of file diff --git a/api_v1/urls.py b/api_v1/urls.py index ab83d98..b37227a 100644 --- a/api_v1/urls.py +++ b/api_v1/urls.py @@ -3,4 +3,5 @@ from django.conf.urls import url, include urlpatterns = [ url(r'courses/', include('courses.new_urls')), url(r'users/', include('access.new_urls')), + url(r'journals/', include('journals.new_urls')), ] \ No newline at end of file diff --git a/courses/course_update.py b/courses/course_update.py index 867b29d..e443b48 100644 --- a/courses/course_update.py +++ b/courses/course_update.py @@ -6,13 +6,11 @@ os.environ['PG_PORT_5432_TCP_ADDR'] = '127.0.0.1' os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings") django.setup() -from django.contrib.contenttypes.models import ContentType -from courses.models import Course, NormalMap, CourseTheme, Lesson, Homework, Exam, \ - Tutorial, Task, Topic, Vertex +from courses.models import Course, NormalMap, CourseTheme, Lesson, Homework, Exam, Vertex if __name__ == '__main__': for course in Course.objects.all(): - tree = [] + course_list = [] for theme in CourseTheme.objects.filter(course=course).order_by('sort'): topic_vertex = Vertex.manager.create_with_dependencies( @@ -22,54 +20,50 @@ if __name__ == '__main__': model='topic', icon=theme.icon, ) + course_list.append(topic_vertex.id) + + topic_list = [] - tree.append(topic_vertex.id) - children = [] for i in Lesson.objects.filter(theme=theme).order_by('sort'): on_comment = i.on_comment == 'N' or i.on_comment == 'T' and i.theme.on_comment - tutor = Tutorial.objects.create(video=i.video, on_comment=on_comment) - [tutor.materials.add(j) for j in i.materials.all()] - lesson_type = ContentType.objects.get(app_label="courses", model="tutorial") - vertex = Vertex.objects.create( + vertex = Vertex.manager.create_with_dependencies( course=course, title=i.title, description=i.description, - content_type=lesson_type, - object_id=tutor.id, + model='tutorial', + on_comment=on_comment, + video=i.video, + materials=i.materials.all(), ) topic_vertex.children.add(vertex) - children.append(vertex.id) + topic_list.append(vertex.id) for i in Homework.objects.filter(theme=theme).order_by('sort'): - task = Task.objects.create(is_exam=False,) - [task.materials.add(j) for j in i.materials.all()] - task_type = ContentType.objects.get(app_label="courses", model="task") - vertex = Vertex.objects.create( + vertex = Vertex.manager.create_with_dependencies( course=course, title='Домашняя работа', description=i.description, - content_type=task_type, - object_id=task.id, + model='task', + is_exam=False, + materials=i.materials.all(), ) topic_vertex.children.add(vertex) - children.append(vertex.id) + topic_list.append(vertex.id) for i in Exam.objects.filter(theme=theme).order_by('sort'): - task=Task.objects.create(is_exam=True,) - [task.materials.add(j) for j in i.materials.all()] - task_type = ContentType.objects.get(app_label="courses", model="task") - vertex = Vertex.objects.create( + vertex = Vertex.manager.create_with_dependencies( course=course, title='Экзамен', description=i.description, - content_type=task_type, - object_id=task.id, + model='task', + is_exam=True, + materials=i.materials.all(), ) topic_vertex.children.add(vertex) - children.append(vertex.id) + topic_list.append(vertex.id) - tree.append(children) + course_list.append(topic_list) course_map, _is_create = NormalMap.objects.get_or_create(course=course) - course_map.json_tree=json.dumps(tree) - course_map.save() \ No newline at end of file + course_map.dependent_elements = json.dumps(course_list) + course_map.save() diff --git a/courses/migrations/0046_auto_20170925_1829.py b/courses/migrations/0046_auto_20170925_1829.py new file mode 100644 index 0000000..c52a541 --- /dev/null +++ b/courses/migrations/0046_auto_20170925_1829.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.3 on 2017-09-25 18:29 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import django.db.models.manager + + +class Migration(migrations.Migration): + + dependencies = [ + ('storage', '0002_auto_20160831_1638'), + ('contenttypes', '0002_remove_content_type_name'), + ('courses', '0045_auto_20170918_0811'), + ] + + operations = [ + migrations.CreateModel( + name='NormalMap', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('dependent_elements', models.TextField(default='[]')), + ('independent_elements', models.TextField(default='[]')), + ], + ), + migrations.CreateModel( + name='Task', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('is_exam', models.BooleanField(default=False, verbose_name='Экзамен или домашка')), + ('materials', models.ManyToManyField(blank=True, to='storage.Storage', verbose_name='Материалы для домашней работы')), + ], + ), + migrations.CreateModel( + name='Topic', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('icon', models.ImageField(blank=True, null=True, upload_to='CourseTheme', verbose_name='Иконка темы')), + ], + ), + migrations.CreateModel( + name='Tutorial', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('on_comment', models.BooleanField(default=True, verbose_name='Комментарии')), + ('video', models.TextField(blank=True, default='', verbose_name='Код видео')), + ('materials', models.ManyToManyField(blank=True, to='storage.Storage', verbose_name='Материалы урока')), + ], + ), + migrations.CreateModel( + name='Vertex', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=255, verbose_name='Название')), + ('description', models.TextField(blank=True, default='', null=True, verbose_name='Описание')), + ('object_id', models.PositiveIntegerField()), + ('children', models.ManyToManyField(blank=True, to='courses.Vertex')), + ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), + ], + managers=[ + ('manager', django.db.models.manager.Manager()), + ], + ), + migrations.AlterField( + model_name='course', + name='level', + field=models.CharField(choices=[('B', 'Базовый'), ('A', 'Продвинутый'), ('E', 'Экспертный'), ('B+A', 'Базовый + Продвинутый')], default='B', max_length=3, verbose_name='Уровень'), + ), + migrations.AddField( + model_name='vertex', + name='course', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Course'), + ), + migrations.AddField( + model_name='normalmap', + name='course', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='courses.Course'), + ), + ] diff --git a/courses/migrations/0047_auto_20170921_1746.py b/courses/migrations/0047_auto_20170921_1746.py deleted file mode 100644 index ae9025a..0000000 --- a/courses/migrations/0047_auto_20170921_1746.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.3 on 2017-09-21 17:46 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('courses', '0046_auto_20170921_1707'), - ] - - 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='')), - ('independent_elements', models.TextField(default='')), - ], - ), - migrations.AddField( - model_name='normalmap', - name='course', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='courses.Course'), - ), - ] diff --git a/courses/models.py b/courses/models.py index a1d2edb..6903cd8 100755 --- a/courses/models.py +++ b/courses/models.py @@ -980,26 +980,52 @@ class CourseBuilder: # Модели нового API со временем всё, что выше будет выпилено class Tutorial(models.Model): + """ + Модель урока. + Урок может быть открыт для комментирования и закрыт, по дефолту открыт, + вероятно закрывать нужно будет крайне редко. + Видео к уроку фрейм который лежит прямо в базе, конечно же костыль и со временем + мы уйдём от этого, все видео хостятся на двух онлайн сервисах на клиент нужно передовать + id и сервис на котором, это дело хостится, а правило отображения оставить клиенту. + Материалы для урока по сути FileField, нужна только для создания лишней связи в таблице + и дублирования метазаголовков файла + """ + on_comment = models.BooleanField(verbose_name=u'Комментарии', default=True) video = models.TextField(verbose_name=u'Код видео', default='', blank=True) materials = models.ManyToManyField(Storage, verbose_name=u'Материалы урока', blank=True) class Task(models.Model): + """ + Модель таска. + Исторически сложилось, что на сервере хостятся два типа тасков отличающихся лишь наименованием + домашние работы и экзамены, не нужно быть гением, чтобы понять для чего нужно булево значение + is_exam + Материалы для урока по сути FileField, нужна только для создания лишней связи в таблице + и дублирования метазаголовков файла + """ materials = models.ManyToManyField(Storage, verbose_name=u'Материалы для домашней работы', blank=True) is_exam = models.BooleanField(default=False, verbose_name=u'Экзамен или домашка') class Topic(models.Model): + """ + Модель темы, нужно просто для объединения тасков и уроков. + У некоторых тем есть иконка. + Возможно поле icon перекачует в Vertex, а данная модель отвалится за ненадобностью + """ icon = models.ImageField(verbose_name=u'Иконка темы', upload_to='CourseTheme', null=True, blank=True) class VertexManager(models.Manager): - # Менеджер вершин графа. На самом деле + # Менеджер вершин графа. - def create_with_dependencies(self, model, course, title, description, **kwargs): + def create_with_dependencies(self, model, course, title, description, + materials=None, **kwargs): content_type = ContentType.objects.get(app_label='courses', model=model) obj = content_type.model_class().objects.create(**kwargs) + [obj.materials.add(i) for i in materials] if materials else None return self.create( content_type=content_type, object_id=obj.id, @@ -1010,6 +1036,13 @@ class VertexManager(models.Manager): class Vertex(models.Model): + """ + Основная структурная единица узел графа курса. + Может быть привязана к теме уроку или заданию. + Модель создана для минимизации трёх выше указанных. + Позволяет работать со структурой курса на более высоком уровне абстракции. + """ + course = models.ForeignKey(to=Course) title = models.CharField(verbose_name=u'Название', max_length=255) description = models.TextField( @@ -1029,6 +1062,10 @@ class Vertex(models.Model): class NormalMap(models.Model): + """ + Так как курс евляется связным графом мы можем отобразить его бесконечным количеством способов, + а нам нужен один самый красивый, мы должный в явном виде указать способ отображения. + """ course = models.OneToOneField(to=Course) - json_tree = models.TextField(default='') - independent_elements = models.TextField(default='') + dependent_elements = models.TextField(default='[]') + independent_elements = models.TextField(default='[]') diff --git a/courses/new_urls.py b/courses/new_urls.py index f531528..8e2fbbf 100644 --- a/courses/new_urls.py +++ b/courses/new_urls.py @@ -2,9 +2,8 @@ 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'vertex/([0-9]{1,99})/$', views.VertexDetail.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()), diff --git a/courses/new_view.py b/courses/new_view.py index b331557..c2a4dce 100644 --- a/courses/new_view.py +++ b/courses/new_view.py @@ -1,9 +1,8 @@ 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 courses.models import Course, MaterialDirection, Vertex +from courses.serializers import CourseDetailSerializer, CourseListSerializer, VertexSerializer, CourseTreeSerializer from finance.models import Bill @@ -24,32 +23,22 @@ class TreeView(APIView): 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) + def get(self, request, pk): + try: + return Response(CourseTreeSerializer(Course.objects.get(id=pk)).data, self.status_code) + except Course.DoesNotExist: + return Response("Course doesn't exist", status=404) 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) + def get(self, request, pk): + try: + return Response(CourseDetailSerializer(Course.objects.get(id=pk)).data, self.status_code) + except Course.DoesNotExist: + return Response("Course doesn't exist", status=404) class CourseListView(APIView): @@ -69,4 +58,16 @@ class CourseListView(APIView): course_serialize['is_mine'] = True res.append(course_serialize) - return Response(res, self.status_code) \ No newline at end of file + return Response(res, self.status_code) + + +class VertexDetail(APIView): + renderer_classes = (JSONRenderer,) + + def get(self, request, pk): + if not request.user.is_authenticated: + return Response("Access to detail of vertex, exist only for authenticated users", status=404) + try: + return Response(VertexSerializer(Vertex.manager.get(id=pk)).data, status=200) + except Vertex.DoesNotExist: + return Response("Vertex doesn't exist", status=404) diff --git a/courses/serializers.py b/courses/serializers.py index 96f467d..e9e64ab 100644 --- a/courses/serializers.py +++ b/courses/serializers.py @@ -3,29 +3,65 @@ 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 +from courses.models import Course, Vertex, Tutorial, Topic, Task -class LessonSerializer(serializers.ModelSerializer): - on_comment = serializers.SerializerMethodField() +class TutorialSerializer(serializers.ModelSerializer): + class Meta: + model = Tutorial + exclude = ['id'] + +class TaskSerializer(serializers.ModelSerializer): class Meta: - model = Lesson - fields = ( - 'id', 'title', 'on_comment', 'materials', - 'free', 'video', 'video_id', - ) + model = Task + exclude = ['id'] + + +class TopicSerializer(serializers.ModelSerializer): + class Meta: + model = Topic + exclude = ['id'] + + +class MiniVertexSerializer(serializers.ModelSerializer): + object = serializers.SerializerMethodField() + type = serializers.SerializerMethodField() + children = serializers.SerializerMethodField() + + class Meta: + model = Vertex + fields = ('id', 'title', 'type', 'object', 'children') @staticmethod - def get_on_comment(self): - return self.on_comment == 'N' or self.on_comment == 'T' and self.theme.on_comment + def get_object(self): + if self.content_type.model == 'tutorial': + return TutorialSerializer(self.content_object).data + elif self.content_type.model == 'topic': + return TopicSerializer(self.content_object).data -class ThemeSerializer(serializers.ModelSerializer): + elif self.content_type.model == 'task': + return TaskSerializer(self.content_object).data + @staticmethod + def get_type(self): + return self.content_type.model + + @staticmethod + def get_children(self): + return json.loads(self.sort_map.dependent_elements) + + +class VertexSerializer(MiniVertexSerializer): class Meta: - model = CourseTheme - exclude = ('price_type', '_type', 'sort', 'on_comment') + model = Vertex + fields = ('id', 'title', 'type', 'object', 'children', 'description') + + @staticmethod + def get_children(self): + return [VertexSerializer(Vertex.manager.get(id=i)).data + for i in json.loads(self.sort_map.dependent_elements)] class CourseListSerializer(serializers.ModelSerializer): @@ -36,16 +72,11 @@ class CourseListSerializer(serializers.ModelSerializer): class Meta: model = Course fields = ['id', 'title', 'length', - 'level', 'direction', 'image',] + '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 + return 15 @staticmethod def get_level(self): @@ -61,46 +92,12 @@ class CourseTreeSerializer(serializers.ModelSerializer): class Meta: model = Course - fields = ['id', 'title', 'children'] + fields = ['id', '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 + return [MiniVertexSerializer(Vertex.manager.get(id=i)).data + for i in json.loads(self.sort_map.dependent_elements)] class CourseDetailSerializer(serializers.ModelSerializer): @@ -128,32 +125,3 @@ class CourseDetailSerializer(serializers.ModelSerializer): @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/signals.py b/courses/signals.py index 6d41363..fa8bf8a 100644 --- a/courses/signals.py +++ b/courses/signals.py @@ -6,4 +6,8 @@ from courses.models import Vertex @receiver(pre_delete, sender=Vertex) def delete_dependencies(instance, **kwargs): - instance.content_object.delete() + """Удаляем зависимости вместе с узлом""" + if instance.content_object: + instance.content_object.delete() + if instance.sort_map: + instance.sort_map.delete() diff --git a/courses/update_db.py b/courses/update_db.py deleted file mode 100644 index c7075cc..0000000 --- a/courses/update_db.py +++ /dev/null @@ -1,22 +0,0 @@ -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/journals/migrations/0074_auto_20170925_1829.py b/journals/migrations/0074_auto_20170925_1829.py new file mode 100644 index 0000000..91318e7 --- /dev/null +++ b/journals/migrations/0074_auto_20170925_1829.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.3 on 2017-09-25 18:29 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('contenttypes', '0002_remove_content_type_name'), + ('journals', '0073_auto_20170918_0811'), + ] + + operations = [ + migrations.CreateModel( + name='Action', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='Наименование действия (на английском)')), + ('text', models.TextField(verbose_name='Описание действия')), + ], + ), + migrations.CreateModel( + name='Journal', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('object_id', models.PositiveIntegerField()), + ('date', models.DateTimeField(auto_now=True)), + ('action_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journals.Action')), + ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), + ], + ), + migrations.CreateModel( + name='Thread', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('key', models.CharField(max_length=200)), + ('text', models.TextField(default='', verbose_name='Описание треда')), + ('is_staff', models.BooleanField(default=False, verbose_name='Админская ли табличка')), + ('parent', models.ManyToManyField(blank=True, related_name='_thread_parent_+', to='journals.Thread')), + ('subscribers', models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name='Подписчики')), + ], + ), + migrations.AddField( + model_name='journal', + name='thread', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journals.Thread', verbose_name='Тред'), + ), + migrations.AddField( + model_name='journal', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Инициатор действия'), + ), + ] diff --git a/journals/migrations/0079_auto_20170922_1123.py b/journals/migrations/0075_auto_20170926_1238.py similarity index 51% rename from journals/migrations/0079_auto_20170922_1123.py rename to journals/migrations/0075_auto_20170926_1238.py index 57085c9..90e3389 100644 --- a/journals/migrations/0079_auto_20170922_1123.py +++ b/journals/migrations/0075_auto_20170926_1238.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.3 on 2017-09-22 11:23 +# Generated by Django 1.9.3 on 2017-09-26 12:38 from __future__ import unicode_literals from django.db import migrations, models @@ -8,17 +8,18 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('journals', '0078_auto_20170922_1118'), + ('journals', '0074_auto_20170925_1829'), ] operations = [ - migrations.RemoveField( + migrations.AddField( model_name='thread', - name='children', + name='x', + field=models.SmallIntegerField(default=0), ), migrations.AddField( model_name='thread', - name='parent', - field=models.ManyToManyField(blank=True, related_name='_thread_parent_+', to='journals.Thread'), + name='y', + field=models.SmallIntegerField(default=0), ), ] diff --git a/journals/migrations/0076_auto_20170922_1116.py b/journals/migrations/0076_auto_20170922_1116.py deleted file mode 100644 index 1aeccbb..0000000 --- a/journals/migrations/0076_auto_20170922_1116.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.3 on 2017-09-22 11:16 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('journals', '0075_remove_journal_body'), - ] - - operations = [ - migrations.AddField( - model_name='thread', - name='is_staff', - field=models.BooleanField(default=True, verbose_name='Видно ли в админке'), - ), - migrations.AlterField( - model_name='thread', - name='text', - field=models.TextField(default='', verbose_name='Описание треда'), - ), - ] diff --git a/journals/migrations/0077_auto_20170922_1117.py b/journals/migrations/0077_auto_20170922_1117.py deleted file mode 100644 index c4cccbb..0000000 --- a/journals/migrations/0077_auto_20170922_1117.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.3 on 2017-09-22 11:17 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('journals', '0076_auto_20170922_1116'), - ] - - operations = [ - migrations.AlterField( - model_name='thread', - name='is_staff', - field=models.BooleanField(default=False, verbose_name='Видно ли в админке'), - ), - ] diff --git a/journals/migrations/0078_auto_20170922_1118.py b/journals/migrations/0078_auto_20170922_1118.py deleted file mode 100644 index 505fa53..0000000 --- a/journals/migrations/0078_auto_20170922_1118.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.3 on 2017-09-22 11:18 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('journals', '0077_auto_20170922_1117'), - ] - - operations = [ - migrations.AlterField( - model_name='thread', - name='is_staff', - field=models.BooleanField(default=False, verbose_name='Админская ли табличка'), - ), - ] diff --git a/journals/models.py b/journals/models.py index acac6af..6634002 100755 --- a/journals/models.py +++ b/journals/models.py @@ -1688,6 +1688,8 @@ class Thread(models.Model): is_staff = models.BooleanField(default=False, verbose_name=u'Админская ли табличка') subscribers = models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name=u'Подписчики') parent = models.ManyToManyField(to='self', blank=True) + x = models.SmallIntegerField(default=300) + y = models.SmallIntegerField(default=300) def get_journals(self, **filter_extra): threads = [i for i in self.thread_set.all()].append(self) diff --git a/journals/new_urls.py b/journals/new_urls.py index e69de29..398a4ef 100644 --- a/journals/new_urls.py +++ b/journals/new_urls.py @@ -0,0 +1,7 @@ +from django.conf.urls import url +from journals import new_view as views + +urlpatterns = [ + url(r'thread/$', views.ThreadListView.as_view()), + url(r'thread/([0-9]{1,99})/$', views.ThreadDetailView.as_view()), +] \ No newline at end of file diff --git a/journals/new_view.py b/journals/new_view.py index e69de29..cfcd9a7 100644 --- a/journals/new_view.py +++ b/journals/new_view.py @@ -0,0 +1,34 @@ +from rest_framework.views import APIView +from rest_framework.renderers import JSONRenderer +from rest_framework.response import Response + +from journals.models import Thread +from journals.serilizers import ThreadSerializer + + +class ThreadListView(APIView): + renderer_classes = (JSONRenderer,) + status_code = 200 + + def get(self, request): + return Response( + [ThreadSerializer(thread).data for thread in Thread.objects.filter(is_staff=True)], + self.status_code, + ) + + +class ThreadDetailView(APIView): + renderer_classes = (JSONRenderer,) + status_code = 200 + + def post(self, request, pk): + try: + thread = Thread.objects.get(id=pk) + thread.text = request.JSON.get('text', thread.text) + thread.key = request.JSON.get('key', thread.key) + thread.x = request.JSON.get('x', thread.x) + thread.y = request.JSON.get('y', thread.y) + thread.save() + return Response(ThreadSerializer(thread).data, self.status_code,) + except Thread.DoesNotExist: + return Response("Thread doesn't exist.", self.status_code,) \ No newline at end of file diff --git a/journals/serilizers.py b/journals/serilizers.py new file mode 100644 index 0000000..0142cf7 --- /dev/null +++ b/journals/serilizers.py @@ -0,0 +1,10 @@ +from rest_framework import serializers + +from journals.models import Thread + + +class ThreadSerializer(serializers.ModelSerializer): + + class Meta: + model = Thread + exclude = ['is_staff'] \ No newline at end of file