Подготовка api

remotes/origin/pm_task_31703
Andrey 8 years ago
parent e3e14ebd21
commit 7fd5d662eb
  1. 26
      README.md
  2. 24
      access/middleware.py
  3. 3
      access/models.py
  4. 6
      access/new_urls.py
  5. 16
      access/new_view.py
  6. 6
      api_v1/urls.py
  7. 24
      courses/migrations/0048_normalmap.py
  8. 2
      courses/models.py
  9. 10
      courses/new_urls.py
  10. 50
      courses/new_view.py
  11. 144
      courses/serializers.py
  12. 22
      courses/update_db.py
  13. 22
      finance/migrations/0071_bill_flow.py
  14. 3
      lms/settings.py
  15. 1
      lms/urls.py
  16. 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,17 @@
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 "/manag" == request.path[:6]
or'/analy' == request.path[:6]):
#or "/api/v" == request.path[:6]):
if not request.user.is_authenticated():
return HttpResponseForbidden()
@ -14,3 +20,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)

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

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

@ -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'Базовый + Продвинутый')
)

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

@ -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'
@ -137,7 +138,7 @@ DATABASES = {
'NAME': os.environ.get('DB_NAME', 'codemy'),
'USER': os.environ.get('PG_ENV_POSTGRES_USER', 'team'),
'PASSWORD': os.environ.get('PG_ENV_POSTGRES_PASSWORD', 'nu5Xefise'),
'HOST': os.environ.get('PG_PORT_5432_TCP_ADDR', '192.168.0.6'),
'HOST': os.environ.get('PG_PORT_5432_TCP_ADDR', '127.0.0.1'),
'PORT': os.environ.get('PG_PORT_5432_TCP_PORT', '5432'),
},
}

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

@ -1,3 +1,4 @@
djangorestframework
Babel==2.3.4
Django==1.9.3
Jinja2==2.8

Loading…
Cancel
Save