parent
e3e14ebd21
commit
7fd5d662eb
16 changed files with 313 additions and 47 deletions
@ -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 |
||||
|
||||
@ -0,0 +1,6 @@ |
||||
from django.conf.urls import url |
||||
from access import new_view as views |
||||
|
||||
urlpatterns = [ |
||||
url(r'teachers/$', views.TeacherListView.as_view()), |
||||
] |
||||
@ -0,0 +1,16 @@ |
||||
from django.contrib.auth import get_user_model |
||||
from rest_framework.views import APIView |
||||
from rest_framework.renderers import JSONRenderer |
||||
from rest_framework.response import Response |
||||
|
||||
|
||||
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) |
||||
@ -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,10 @@ |
||||
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'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,50 @@ |
||||
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 |
||||
from courses.serializers import CourseTreeSerializer, CourseDetailSerializer, CourseListSerializer, ThemeSerializer |
||||
|
||||
|
||||
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 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): |
||||
return Response([CourseListSerializer(i).data for i in Course.objects.all()], self.status_code) |
||||
@ -0,0 +1,144 @@ |
||||
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): |
||||
|
||||
class Meta: |
||||
model = Lesson |
||||
fields = '__all__' |
||||
|
||||
|
||||
class HomeworkSerializer(serializers.ModelSerializer): |
||||
|
||||
class Meta: |
||||
model = Homework |
||||
fields = '__all__' |
||||
|
||||
|
||||
class ExamSerializer(serializers.ModelSerializer): |
||||
|
||||
class Meta: |
||||
model = Exam |
||||
fields = '__all__' |
||||
|
||||
|
||||
class ThemeSerializer(serializers.ModelSerializer): |
||||
|
||||
class Meta: |
||||
model = CourseTheme |
||||
fields = '__all__' |
||||
|
||||
|
||||
class CourseListSerializer(serializers.ModelSerializer): |
||||
|
||||
class Meta: |
||||
model = Course |
||||
fields = ['id', '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() |
||||
@ -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='Поток'), |
||||
), |
||||
] |
||||
Loading…
Reference in new issue