Merge branch 'new_lms_dev' into new_lms_dev__refactor

# Conflicts:
#	access/admin.py
#	access/models/__init__.py
#	access/models/other.py
#	access/serializers.py
#	access/urls.py
#	access/views.py
#	csv/load_bills.py
#	csv/load_perm.py
feature/fix_generate_pass
Evgeniy Shabanov 8 years ago
commit 8d695f1d18
  1. 5
      access/admin.py
  2. 28
      access/middleware.py
  3. 52
      access/migrations/0001_initial.py
  4. 21
      access/migrations/0002_auto_20171217_1808.py
  5. 25
      access/migrations/0002_init_group.py
  6. 20
      access/migrations/0003_pivotprogressvertex_date.py
  7. 31
      access/migrations/0003_useractivity.py
  8. 21
      access/migrations/0004_auto_20171218_1438.py
  9. 20
      access/migrations/0005_auto_20171218_1516.py
  10. 20
      access/migrations/0006_auto_20171220_1228.py
  11. 21
      access/migrations/0007_progress_course.py
  12. 20
      access/migrations/0008_auto_20171220_1325.py
  13. 16
      access/models/other.py
  14. 88
      access/models/progress.py
  15. 8
      access/models/user.py
  16. 64
      access/serializers.py
  17. 7
      access/urls.py
  18. 128
      access/views.py
  19. 6
      achievements/admin.py
  20. 42
      achievements/migrations/0001_initial.py
  21. 10
      achievements/models.py
  22. 4
      achievements/serialers.py
  23. 2
      api_v1/urls.py
  24. 1
      course_service/courses/__init__.py
  25. 7
      course_service/courses/admin.py
  26. 21
      course_service/courses/api.py
  27. 24
      course_service/courses/migrations/0002_course_route.py
  28. 20
      course_service/courses/migrations/0003_auto_20171217_1821.py
  29. 0
      course_service/courses/migrations/__init__.py
  30. 11
      course_service/courses/signals.py
  31. 0
      course_service/maps/__init__.py
  32. 8
      course_service/maps/admin.py
  33. 16
      course_service/maps/api.py
  34. 7
      course_service/maps/apps.py
  35. 2
      course_service/maps/exeptions.py
  36. 83
      course_service/maps/migrations/0001_initial.py
  37. 0
      course_service/maps/migrations/__init__.py
  38. 67
      course_service/maps/models.py
  39. 27
      course_service/maps/serializers.py
  40. 23
      course_service/maps/tests.py
  41. 3
      course_service/maps/views.py
  42. 1
      courses/__init__.py
  43. 6
      courses/admin.py
  44. 29
      courses/api.py
  45. 2
      courses/apps.py
  46. 53
      courses/migrations/0001_initial.py
  47. 0
      courses/migrations/__init__.py
  48. 121
      courses/models.py
  49. 29
      courses/serializers.py
  50. 4
      courses/tsets.py
  51. 4
      courses/urls.py
  52. 12
      courses/views.py
  53. 39639
      csv/access/account.csv
  54. 24919
      csv/access/progress.csv
  55. 8672
      csv/access/users.csv
  56. 49
      csv/achievement/achievement.csv
  57. 66
      csv/achievement/diploma.csv
  58. 2230
      csv/achievement/user_achievement.csv
  59. 53
      csv/course/course.csv
  60. 78891
      csv/course/storage.csv
  61. 3158
      csv/course/vertex.csv
  62. 16176
      csv/finance/bill.csv
  63. 26
      csv/load_bills.py
  64. 66
      csv/load_comments.py
  65. 88
      csv/load_courses.py
  66. 13
      csv/load_diploma.py
  67. 61
      csv/load_perm.py
  68. 2
      csv/load_storage.py
  69. 7
      csv/load_users.py
  70. 581266
      csv/management/comment.csv
  71. 10
      finance/migrations/0001_initial.py
  72. 20
      finance/migrations/0002_auto_20171225_1142.py
  73. 6
      finance/models.py
  74. 0
      gitlab_service/__init__.py
  75. 5
      gitlab_service/admin.py
  76. 52
      gitlab_service/api.py
  77. 7
      gitlab_service/apps.py
  78. 106
      gitlab_service/gitlab_wrapper.py
  79. 0
      gitlab_service/migrations/__init__.py
  80. 21
      gitlab_service/models.py
  81. 4
      gitlab_service/tests.py
  82. 5
      gitlab_service/views.py
  83. 2
      library/migrations/0001_initial.py
  84. 6
      lms/settings.py
  85. 57
      requirements.txt
  86. 22
      storage/api.py
  87. 9
      storage/migrations/0001_initial.py
  88. 14
      storage/models.py
  89. 2
      storage/serializers.py
  90. 60
      storage/tests.py
  91. 19
      storage/views.py

@ -1,8 +1,11 @@
from django.contrib import admin from django.contrib import admin
from access.models.other import Invite, Account, ResetPassword from access.models.other import Invite, Account, ResetPassword, UserActivity
from access.models.progress import ProgressLesson
from access.models.user import User from access.models.user import User
admin.site.register(User) admin.site.register(User)
admin.site.register(Account) admin.site.register(Account)
admin.site.register(Invite) admin.site.register(Invite)
admin.site.register(ResetPassword) admin.site.register(ResetPassword)
admin.site.register(ProgressLesson)
admin.site.register(UserActivity)

@ -1,6 +1,7 @@
from django.http import HttpResponseForbidden from django.http import HttpResponseForbidden
from django.http import QueryDict from django.http import QueryDict
from access.models.other import UserActivity
import json import json
@ -8,16 +9,15 @@ class CheckPerm(object):
@staticmethod @staticmethod
def process_request(request): def process_request(request):
if len(request.path) > 6 and \ if len(request.path) > 6 and \
('/admin' == request.path[:6] ('/admin' == request.path[:6]
or'/analy' == request.path[:6]): or '/analy' == request.path[:6]):
#or "/api/v" == request.path[:6]): # or "/api/v" == request.path[:6]):
if not request.user.is_authenticated(): if not request.user.is_authenticated():
return HttpResponseForbidden() return HttpResponseForbidden()
if not (request.user.in_role == "M" or request.user.in_role == "S" if not (request.user.in_role == "M" or request.user.in_role == "S"
or request.user.in_role == "A" or request.user.is_admin): or request.user.in_role == "A" or request.user.is_admin):
return HttpResponseForbidden() return HttpResponseForbidden()
@ -32,4 +32,22 @@ class RequestToApi(object):
q_data.update({value: data[value]}) q_data.update({value: data[value]})
request.JSON = q_data request.JSON = q_data
if request.method == 'POST' or request.method == 'DELETE': if request.method == 'POST' or request.method == 'DELETE':
setattr(request, '_dont_enforce_csrf_checks', True) # TODO или выпилить или в зависимость от settings
setattr(request, '_dont_enforce_csrf_checks', True)
class UpdateActivity(object):
@staticmethod
def process_request(request):
if not request.user.is_anonymous:
user_activity = UserActivity.objects.get(owner=request.user)
ip = request.META.get('REMOTE_ADDR', None)
if user_activity.ip_list is None:
user_activity.ip_list = []
if not ip is None:
new_list = list(user_activity.ip_list)
new_list.append(ip)
user_activity.ip_list = list(set(new_list))
user_activity.save()

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-17 18:03 # Generated by Django 1.11.6 on 2018-01-22 09:27
from __future__ import unicode_literals from __future__ import unicode_literals
import access.models.user import access.models.user
@ -8,6 +8,7 @@ import django.contrib.postgres.fields
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import phonenumber_field.modelfields import phonenumber_field.modelfields
import uuid
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -26,7 +27,8 @@ class Migration(migrations.Migration):
('password', models.CharField(max_length=128, verbose_name='password')), ('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('out_key', models.CharField(editable=False, max_length=15, unique=True, verbose_name='Ключ для внешних сервисов')), ('out_key', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='Токен')),
('in_key', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='Токен')),
('email', models.EmailField(max_length=254, unique=True, verbose_name='email address')), ('email', models.EmailField(max_length=254, unique=True, verbose_name='email address')),
('first_name', models.CharField(blank=True, default='Гость', max_length=63, verbose_name='first name')), ('first_name', models.CharField(blank=True, default='Гость', max_length=63, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=63, verbose_name='last name')), ('last_name', models.CharField(blank=True, max_length=63, verbose_name='last name')),
@ -53,7 +55,7 @@ class Migration(migrations.Migration):
('city', models.CharField(blank=True, max_length=63, null=True)), ('city', models.CharField(blank=True, max_length=63, null=True)),
('gender', models.SmallIntegerField(choices=[(0, 'undefined'), (1, 'male'), (2, 'female')], default=0)), ('gender', models.SmallIntegerField(choices=[(0, 'undefined'), (1, 'male'), (2, 'female')], default=0)),
('photo', models.ImageField(default='/static/default/access/default.png', upload_to='user/photo/')), ('photo', models.ImageField(default='/static/default/access/default.png', upload_to='user/photo/')),
('phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128)), ('phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True)),
('owner', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ('owner', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
], ],
options={ options={
@ -73,20 +75,12 @@ class Migration(migrations.Migration):
'verbose_name_plural': 'Приглошения в систему', 'verbose_name_plural': 'Приглошения в систему',
}, },
), ),
migrations.CreateModel(
name='PivotProgressVertex',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('vertex', models.CharField(blank=True, max_length=15, unique=True, verbose_name='Ссылки на узлы')),
('status', models.SmallIntegerField(choices=[(2, 'Выполненно'), (1, 'Ожидание'), (0, 'Не выполненно')], default=0)),
('comment', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=15, unique=True, verbose_name='Ссылки на комменты'), size=None)),
],
),
migrations.CreateModel( migrations.CreateModel(
name='Progress', name='Progress',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('route', models.CharField(max_length=15, verbose_name='Токен прохождения')), ('hidden_lessons', django.contrib.postgres.fields.ArrayField(base_field=models.UUIDField(editable=False, unique=True, verbose_name='Токен урока'), default=[], size=None, verbose_name='Список скрытых уроков')),
('course_token', models.UUIDField(editable=False, verbose_name='Токен курса')),
('teacher', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='teacher_progress', to=settings.AUTH_USER_MODEL, verbose_name='Преподователь по умолчанию')), ('teacher', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='teacher_progress', to=settings.AUTH_USER_MODEL, verbose_name='Преподователь по умолчанию')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Студент')), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Студент')),
], ],
@ -95,6 +89,22 @@ class Migration(migrations.Migration):
'verbose_name_plural': 'Прогресс пользователя', 'verbose_name_plural': 'Прогресс пользователя',
}, },
), ),
migrations.CreateModel(
name='ProgressLesson',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lesson_token', models.UUIDField(editable=False, verbose_name='Токен урока')),
('date', models.DateTimeField(blank=True, null=True, verbose_name='Дата зачтения задания')),
('status', models.CharField(choices=[('done', 'done'), ('wait', 'wait'), ('fail', 'fail')], default='wait', max_length=20)),
('comment_tokens', django.contrib.postgres.fields.ArrayField(base_field=models.UUIDField(editable=False, verbose_name='Токен комента'), default=[], size=None)),
('progress', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='access.Progress')),
('teacher', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Преподователь')),
],
options={
'verbose_name': 'Прохождение уроков',
'verbose_name_plural': 'Прохождение урока',
},
),
migrations.CreateModel( migrations.CreateModel(
name='ResetPassword', name='ResetPassword',
fields=[ fields=[
@ -107,23 +117,17 @@ class Migration(migrations.Migration):
}, },
bases=('access.invite',), bases=('access.invite',),
), ),
migrations.AddField(
model_name='pivotprogressvertex',
name='progress',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='access.Progress'),
),
migrations.AddField(
model_name='pivotprogressvertex',
name='teacher',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Преподователь'),
),
migrations.AddField( migrations.AddField(
model_name='invite', model_name='invite',
name='owner', name='owner',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Кому приглошение'), field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Кому приглошение'),
), ),
migrations.AlterUniqueTogether(
name='progresslesson',
unique_together=set([('progress', 'lesson_token')]),
),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='progress', name='progress',
unique_together=set([('user', 'route')]), unique_together=set([('user', 'course_token')]),
), ),
] ]

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-17 18:08
from __future__ import unicode_literals
from django.db import migrations
import phonenumber_field.modelfields
class Migration(migrations.Migration):
dependencies = [
('access', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='account',
name='phone',
field=phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True),
),
]

@ -1,13 +1,11 @@
import os, sys, django from __future__ import unicode_literals
sys.path.append("../") from django.contrib.auth.models import Group
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings")
django.setup()
from django.db import migrations
from django.contrib.auth.models import Group
if __name__ == '__main__': def init_group(*_args, **_kwargs):
Group.objects.get_or_create(name='admin') Group.objects.get_or_create(name='admin')
Group.objects.get_or_create(name='students') Group.objects.get_or_create(name='students')
Group.objects.get_or_create(name='teachers') Group.objects.get_or_create(name='teachers')
@ -17,4 +15,17 @@ if __name__ == '__main__':
Group.objects.get_or_create(name='partners') Group.objects.get_or_create(name='partners')
Group.objects.get_or_create(name='supports') Group.objects.get_or_create(name='supports')
Group.objects.get_or_create(name='finance') Group.objects.get_or_create(name='finance')
Group.objects.get_or_create(name='project_managers') Group.objects.get_or_create(name='project_managers')
class Migration(migrations.Migration):
initial = True
dependencies = [
('access', '0001_initial'),
]
operations = [
migrations.RunPython(init_group)
]

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-18 14:37
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0002_auto_20171217_1808'),
]
operations = [
migrations.AddField(
model_name='pivotprogressvertex',
name='date',
field=models.DateTimeField(blank=True, null=True, verbose_name='Дата зачтения задания'),
),
]

@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-01-23 10:11
from __future__ import unicode_literals
from django.conf import settings
import django.contrib.postgres.fields
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('access', '0002_init_group'),
]
operations = [
migrations.CreateModel(
name='UserActivity',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('ip_list', django.contrib.postgres.fields.ArrayField(base_field=models.GenericIPAddressField(editable=False, verbose_name='Ip адресс'), default=[], size=None)),
('last_request', models.DateTimeField(auto_now=True, verbose_name='Был в сети')),
('owner', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Пользовательская активность',
'verbose_name_plural': 'Пользовательская активность',
},
),
]

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-18 14:38
from __future__ import unicode_literals
import django.contrib.postgres.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0003_pivotprogressvertex_date'),
]
operations = [
migrations.AlterField(
model_name='pivotprogressvertex',
name='comment',
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=15, unique=True, verbose_name='Ссылки на комменты'), default=[], size=None),
),
]

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-18 15:16
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0004_auto_20171218_1438'),
]
operations = [
migrations.AlterField(
model_name='pivotprogressvertex',
name='vertex',
field=models.CharField(blank=True, max_length=15, verbose_name='Ссылки на узлы'),
),
]

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-20 12:28
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0005_auto_20171218_1516'),
]
operations = [
migrations.AlterField(
model_name='progress',
name='route',
field=models.CharField(blank=True, max_length=15, null=True, verbose_name='Токен прохождения'),
),
]

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-20 13:21
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0006_auto_20171220_1228'),
]
operations = [
migrations.AddField(
model_name='progress',
name='course',
field=models.CharField(default='', max_length=15, verbose_name='Токен курса'),
preserve_default=False,
),
]

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-20 13:25
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0007_progress_course'),
]
operations = [
migrations.AlterField(
model_name='progress',
name='course',
field=models.CharField(max_length=127, verbose_name='Слаг курса'),
),
]

@ -1,7 +1,7 @@
from django.conf import settings from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from django.db import models from django.db import models
from phonenumber_field.modelfields import PhoneNumberField from phonenumber_field.modelfields import PhoneNumberField
from django.contrib.postgres.fields import ArrayField
class Invite(models.Model): class Invite(models.Model):
@ -22,6 +22,19 @@ class ResetPassword(Invite):
verbose_name_plural = "Запросы на сброс пароля" verbose_name_plural = "Запросы на сброс пароля"
class UserActivity(models.Model):
owner = models.OneToOneField(to=settings.AUTH_USER_MODEL)
ip_list = ArrayField(models.GenericIPAddressField(verbose_name="Ip адресс", editable=False), default=[])
last_request = models.DateTimeField(verbose_name="Был в сети", auto_now=True)
def __str__(self):
return self.owner.email
class Meta:
verbose_name = 'Пользовательская активность'
verbose_name_plural = 'Пользовательская активность'
class Account(models.Model): class Account(models.Model):
GENDER_CHOICES = ( GENDER_CHOICES = (
(0, 'undefined'), (0, 'undefined'),
@ -42,3 +55,4 @@ class Account(models.Model):
verbose_name = 'Дополнительная информация о пользователе' verbose_name = 'Дополнительная информация о пользователе'
verbose_name_plural = 'Дополнительная информация о пользователе' verbose_name_plural = 'Дополнительная информация о пользователе'

@ -0,0 +1,88 @@
from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from django.db import models
from model_utils import Choices
from courses.api import CourseProgressApi
class Progress(models.Model):
hidden_lessons = ArrayField(
models.UUIDField(verbose_name="Токен урока", unique=True, editable=False),
default=[],
verbose_name='Список скрытых уроков',
)
teacher = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name="Преподователь по умолчанию",
related_name='teacher_progress')
user = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name='Студент')
course_token = models.UUIDField(verbose_name="Токен курса", editable=False)
def progress_status(self, sorted_token_list):
"""
Возвращает порядок активной темы и урока или ничего
:return: (topic_sort, lesson_sort) or None
"""
token_list = [i.lesson_token for i in self.progresslesson_set.filter(status='done')]
x = (1, 1)
for idx_topic, lesson_list in enumerate(sorted_token_list):
for idx_lesson, lesson_token in enumerate(lesson_list):
if lesson_token in token_list:
x = None
elif x is None:
x = (idx_topic+1, idx_lesson+1)
return x
def __str__(self):
return '%s' % (self.user.email,)
class Meta:
verbose_name = 'Прогресс пользователя'
verbose_name_plural = 'Прогресс пользователя'
unique_together = (("user", "course_token"),)
class ProgressLesson(models.Model):
progress = models.ForeignKey(to=Progress)
lesson_token = models.UUIDField(verbose_name="Токен урока", editable=False)
teacher = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name="Преподователь",)
date = models.DateTimeField(verbose_name='Дата зачтения задания', blank=True, null=True)
STATUSES = Choices('done', 'wait', 'fail')
status = models.CharField(choices=STATUSES, default=STATUSES.wait, max_length=20)
comment_tokens = ArrayField(models.UUIDField(verbose_name="Токен комента", editable=False), default=[])
def __str__(self):
return self.progress.user.email
class Meta:
verbose_name = 'Прохождение уроков'
verbose_name_plural = 'Прохождение урока'
unique_together = ('progress', 'lesson_token')
# class UserLessonAnswer(models.Model):
# progress_lesson = models.ForeignKey(to=ProgressLesson)
# date = models.DateTimeField(verbose_name='Дата сдачи', auto_now_add=True)
# reviewer = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name="Проверяющий",)
#
# class Meta:
# verbose_name = 'Блок ответов пользователя'
# verbose_name_plural = 'Блоки ответов пользователя'
#
#
# class AnswerItem(models.Model):
# STATUSES = Choices('done', 'wait', 'fail')
#
# lesson_answer = models.ForeignKey(to=UserLessonAnswer)
# question = models.TextField(verbose_name='Вопрос') # TODO подумать над хранением токена вопроса
# value = models.TextField(verbose_name='Ответ')
# comment = models.TextField(verbose_name='Комент', blank=True, null=True)
# status = models.CharField(choices=STATUSES, default=STATUSES.wait, max_length=20)
#
# class Meta:
# verbose_name = 'Ответ пользователя'
# verbose_name_plural = 'Ответы пользователя'

@ -1,5 +1,6 @@
import random import random
import string import string
import uuid
from django.contrib.auth.base_user import BaseUserManager, AbstractBaseUser from django.contrib.auth.base_user import BaseUserManager, AbstractBaseUser
from django.contrib.auth.models import Group, PermissionsMixin from django.contrib.auth.models import Group, PermissionsMixin
@ -8,7 +9,7 @@ from django.db import models
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from access.models.other import Invite, Account from access.models.other import Invite, Account, UserActivity
from lms.global_decorators import transaction_decorator from lms.global_decorators import transaction_decorator
@ -44,7 +45,6 @@ class CustomUserManager(BaseUserManager):
email = self.normalize_email(email) email = self.normalize_email(email)
user = self.model(email=email, is_staff=is_staff, is_active=is_active, first_name=first_name, user = self.model(email=email, is_staff=is_staff, is_active=is_active, first_name=first_name,
out_key=''.join(random.choice(string.ascii_letters) for x in range(15)),
is_superuser=is_superuser, date_joined=date_joined, last_login=last_login, **extra_fields) is_superuser=is_superuser, date_joined=date_joined, last_login=last_login, **extra_fields)
if not password: if not password:
@ -58,6 +58,7 @@ class CustomUserManager(BaseUserManager):
user.save(using=self._db) user.save(using=self._db)
Account.objects.create(owner=user) Account.objects.create(owner=user)
UserActivity.objects.create(owner=user)
if role_list: if role_list:
for group in role_list: for group in role_list:
@ -93,7 +94,8 @@ class CustomUserManager(BaseUserManager):
class User(AbstractBaseUser, PermissionsMixin): class User(AbstractBaseUser, PermissionsMixin):
out_key = models.CharField(max_length=15, unique=True, verbose_name="Ключ для внешних сервисов", editable=False) out_key = models.UUIDField(verbose_name="Токен", default=uuid.uuid4, editable=False)
in_key = models.UUIDField(verbose_name="Токен", default=uuid.uuid4, editable=False)
email = models.EmailField(_('email address'), unique=True) email = models.EmailField(_('email address'), unique=True)
first_name = models.CharField(_('first name'), max_length=63, blank=True, default='Гость') first_name = models.CharField(_('first name'), max_length=63, blank=True, default='Гость')
last_name = models.CharField(_('last name'), max_length=63, blank=True) last_name = models.CharField(_('last name'), max_length=63, blank=True)

@ -2,8 +2,52 @@ from django.contrib.auth import get_user_model
from rest_framework import serializers from rest_framework import serializers
from access.models.other import Account from access.models.other import Account
from access.models.progress import ProgressLesson
from progress.models import Progress
from achievements.serialers import DiplomaSerializer, AchievementsSerializer from achievements.serialers import DiplomaSerializer, AchievementsSerializer
from progress.serializers import ProgressSerializer from courses.api import CourseProgressApi
class ProgressLessonSerializer(serializers.ModelSerializer):
teacher = serializers.SerializerMethodField()
class Meta:
model = ProgressLesson
exclude = ('id', 'progress')
@staticmethod
def get_teacher(self):
return self.teacher.get_full_name()
class ProgressAnalyticSerializer(serializers.ModelSerializer):
name = serializers.SerializerMethodField()
email = serializers.SerializerMethodField()
class Meta:
model = Progress
fields = ('name', 'email',)
@staticmethod
def get_name(self):
return self.user.get_full_name()
@staticmethod
def get_email(self):
return self.user.email
class ProgressSerializer(serializers.ModelSerializer):
lessons = serializers.SerializerMethodField()
class Meta:
model = Progress
fields = ('lessons', 'course_token')
@staticmethod
def get_lessons(self):
return [ProgressLessonSerializer(i).data for i in self.progresslesson_set.all()]
class AccountSerializer(serializers.ModelSerializer): class AccountSerializer(serializers.ModelSerializer):
@ -27,7 +71,7 @@ class UserSelfSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = get_user_model() model = get_user_model()
fields = ('id', 'email', 'first_name', 'last_name', 'progress', 'achievements', fields = ('out_key', 'email', 'first_name', 'last_name', 'progress', 'achievements',
'account', 'groups', 'is_staff', 'is_superuser', 'diplomas', 'is_active') 'account', 'groups', 'is_staff', 'is_superuser', 'diplomas', 'is_active')
@staticmethod @staticmethod
@ -36,7 +80,7 @@ class UserSelfSerializer(serializers.ModelSerializer):
@staticmethod @staticmethod
def get_achievements(self): def get_achievements(self):
return [AchievementsSerializer(i).data for i in self.achievements_set.all()] return [AchievementsSerializer(i).data for i in self.achievement_set.all()]
@staticmethod @staticmethod
def get_account(self): def get_account(self):
@ -58,7 +102,7 @@ class UserProfileSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = get_user_model() model = get_user_model()
fields = ( fields = (
'id', 'email', 'first_name', 'last_name', 'out_key', 'email', 'first_name', 'last_name',
'account', 'groups', 'is_staff', 'is_superuser', 'account', 'groups', 'is_staff', 'is_superuser',
) )
@ -74,10 +118,12 @@ class UserProfileSerializer(serializers.ModelSerializer):
class UserSearchSerializer(serializers.ModelSerializer): class UserSearchSerializer(serializers.ModelSerializer):
pay = serializers.SerializerMethodField() pay = serializers.SerializerMethodField()
phone = serializers.SerializerMethodField() phone = serializers.SerializerMethodField()
photo = serializers.SerializerMethodField()
last_request = serializers.SerializerMethodField()
class Meta: class Meta:
model = get_user_model() model = get_user_model()
fields = ('id', 'email', 'first_name', 'last_name', 'phone', 'pay') fields = ('photo', 'out_key', 'email', 'first_name', 'last_name', 'phone', 'pay', 'last_request')
@staticmethod @staticmethod
def get_phone(self): def get_phone(self):
@ -86,3 +132,11 @@ class UserSearchSerializer(serializers.ModelSerializer):
@staticmethod @staticmethod
def get_pay(self): def get_pay(self):
return sum([i.get_full_price() for i in self.bill_user.all()]) return sum([i.get_full_price() for i in self.bill_user.all()])
@staticmethod
def get_photo(self):
return self.account.photo.url
@staticmethod
def get_last_request(self):
return self.useractivity.last_request

@ -5,6 +5,7 @@ urlpatterns = [
url(r'teachers/$', views.TeacherListView.as_view()), url(r'teachers/$', views.TeacherListView.as_view()),
url(r'detail/$', views.DetailUserView.as_view()), url(r'detail/$', views.DetailUserView.as_view()),
url(r'detail/([0-9]{1,99})/$', views.DetailUserView.as_view()), url(r'detail/([0-9]{1,99})/$', views.DetailUserView.as_view()),
url(r'info/(?P<out_key>[0-9A-Fa-f-]+)/$', views.MinUserView.as_view()),
url(r'guard/(?P<pk>[0-9]{1,99})/(?P<page>.+)/$', views.UserGuardView.as_view()), url(r'guard/(?P<pk>[0-9]{1,99})/(?P<page>.+)/$', views.UserGuardView.as_view()),
url(r'find/$', views.FindUserView.as_view()), url(r'find/$', views.FindUserView.as_view()),
url(r'registration/$', views.RegistrationView.as_view()), url(r'registration/$', views.RegistrationView.as_view()),
@ -12,4 +13,8 @@ urlpatterns = [
url(r'login/$', views.LoginView.as_view()), url(r'login/$', views.LoginView.as_view()),
url(r'logout/$', views.LogoutView.as_view()), url(r'logout/$', views.LogoutView.as_view()),
url(r'reset/$', views.ResetPasswordView.as_view()), url(r'reset/$', views.ResetPasswordView.as_view()),
] url(r'progress/$', views.UpdateProgress.as_view()),
url(r'progress_detail/upload/(?P<token>[0-9A-Fa-f-]+)/$', views.UploadCourseProgressUserView.as_view()),
url(r'progress_detail/(?P<token>[0-9A-Fa-f-]+)/$', views.CourseProgressUserView.as_view()),
url(r'progress_dynamic/(?P<token>[0-9A-Fa-f-]+)/$', views.CourseProgressDynamicView.as_view()),
]

@ -1,11 +1,14 @@
import csv
import datetime import datetime
import random import random
import string import string
from django.contrib import auth from django.contrib import auth
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.core.mail import send_mail from django.core.mail import send_mail
from django.db.models import Q from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import redirect from django.shortcuts import redirect
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from rest_framework.renderers import JSONRenderer from rest_framework.renderers import JSONRenderer
@ -13,7 +16,11 @@ from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from access.models.other import Invite, ResetPassword from access.models.other import Invite, ResetPassword
from access.serializers import UserSelfSerializer, UserSearchSerializer from access.models.progress import ProgressLesson
from progress.models import Progress
from access.serializers import UserSelfSerializer, UserSearchSerializer, ProgressLessonSerializer, \
ProgressAnalyticSerializer
from courses.api import CourseProgressApi
class TeacherListView(APIView): class TeacherListView(APIView):
@ -129,10 +136,10 @@ class DetailUserView(APIView):
return Response('anonymous', status=200) return Response('anonymous', status=200)
if request.user.is_authenticated() and request.user.is_superuser \ if request.user.is_authenticated() and request.user.is_superuser \
or request.user.is_staff or request.user.id == int(pk): or request.user.is_staff or request.user.out_key == pk:
try: try:
user = get_user_model().objects.get(id=pk) user = get_user_model().objects.get(out_key=pk)
except get_user_model().DoesNotExist: except get_user_model().DoesNotExist:
return Response("User doesn't exist", status=404) return Response("User doesn't exist", status=404)
@ -229,6 +236,41 @@ class LogoutView(APIView):
return Response(status=204) return Response(status=204)
class UpdateProgress(APIView):
renderer_classes = (JSONRenderer,)
@staticmethod
def post(request):
"""
На вход обязательно передаётся параметр id (id узла).
"""
lesson_token = request.JSON.get('lesson_token', None)
course_token = request.JSON.get('course_token', None)
if lesson_token is None or course_token is None:
return Response('Не передан слаг курса или токен урока', status=400)
try:
p = Progress.objects.get(user=request.user, course_token=course_token)
try:
pv = ProgressLesson.objects.get(
progress=p,
lesson_token=lesson_token,
)
except ProgressLesson.DoesNotExist:
pv = ProgressLesson.objects.create(
date=datetime.datetime.now(),
teacher=p.teacher,
progress=p,
lesson_token=lesson_token,
)
pv.status = ProgressLesson.STATUSES.done
pv.save()
return Response(ProgressLessonSerializer(pv).data, status=200)
except Progress.DoesNotExist:
return Response('Не найден прогресс по заданным параметрам', status=404)
class UserGuardView(APIView): class UserGuardView(APIView):
renderer_classes = (JSONRenderer,) renderer_classes = (JSONRenderer,)
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
@ -236,7 +278,7 @@ class UserGuardView(APIView):
@staticmethod @staticmethod
def get(request, pk, page): def get(request, pk, page):
try: try:
user = get_user_model().objects.get(id=pk) user = get_user_model().objects.get(out_key=pk)
except get_user_model().DoesNotExist: except get_user_model().DoesNotExist:
return Response("User doesn't exist", status=404) return Response("User doesn't exist", status=404)
@ -259,3 +301,81 @@ class UserGuardView(APIView):
return res_204 return res_204
return res_403 return res_403
class CourseProgressDynamicView(APIView):
renderer_classes = (JSONRenderer,)
@staticmethod
def get(request, token):
if request.user.is_authenticated() and request.user.is_staff:
try:
progresses = Progress.objects.filter(course_token=token)
res = {}
for i in progresses:
key = i.progresslesson_set.filter(status="done").count()
res[key] = 1 if not key in res.keys() else res[key] + 1
return Response(res, status=200)
except ValidationError:
return Response("Bad request", status=400)
return Response(status=403)
class CourseProgressUserView(APIView):
renderer_classes = (JSONRenderer,)
@staticmethod
def get(request, token):
if request.user.is_authenticated() and request.user.is_staff:
try:
res = []
sorted_token_list = CourseProgressApi.get_topic_lesson(token)
for p in Progress.objects.filter(course_token=token):
progress = ProgressAnalyticSerializer(p).data
progress['progress_course'] = p.progress_status(sorted_token_list)
res.append(progress)
return Response(res, status=200)
except ValidationError:
return Response("Bad request", status=400)
return Response(status=403)
class UploadCourseProgressUserView(APIView):
renderer_classes = (JSONRenderer,)
@staticmethod
def get(request, token):
if request.user.is_authenticated() and request.user.is_staff:
try:
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="%s.csv"' % token
sorted_token_list = CourseProgressApi.get_topic_lesson(token)
writer = csv.writer(response)
writer.writerow(['Имя', 'Почта', 'Последняя тема', 'Последний урок'])
for p in Progress.objects.filter(course_token=token):
progress = ProgressAnalyticSerializer(p).data
progress['progress_course'] = p.progress_status(sorted_token_list)
writer.writerow([
progress['name'],
progress['email'],
progress['progress_course'] and progress['progress_course'][0],
progress['progress_course'] and progress['progress_course'][1],
])
return response
except ValidationError:
return Response("Bad request", status=400)
return Response(status=403)
class MinUserView(APIView):
renderer_classes = (JSONRenderer,)
@staticmethod
def get(request, out_key):
try:
return Response(UserSearchSerializer(get_user_model().objects.get(out_key=out_key)).data, status=200)
except get_user_model().DoesNotExist:
return Response("User not found", status=404)

@ -1,9 +1,9 @@
from django.contrib import admin from django.contrib import admin
from achievements.models import Skills, Achievements, SkillJ, DiplomaGen, Diploma from achievements.models import Skill, Achievement, SkillJ, DiplomaGen, Diploma
admin.site.register(Skills) admin.site.register(Skill)
admin.site.register(Achievements) admin.site.register(Achievement)
admin.site.register(SkillJ) admin.site.register(SkillJ)
admin.site.register(Diploma) admin.site.register(Diploma)
admin.site.register(DiplomaGen) admin.site.register(DiplomaGen)

@ -1,8 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-17 18:03 # Generated by Django 1.11.6 on 2018-01-22 09:27
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -10,15 +12,18 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('courses', '0001_initial'),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Achievements', name='Achievement',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('img', models.ImageField(upload_to='achives', verbose_name='Отображение достижения')), ('img', models.ImageField(upload_to='achives', verbose_name='Отображение достижения')),
('text', models.CharField(max_length=255, verbose_name='Текст достижения')), ('text', models.CharField(max_length=255, verbose_name='Текст достижения')),
('users', models.ManyToManyField(to=settings.AUTH_USER_MODEL)),
], ],
options={ options={
'verbose_name': 'Достижение', 'verbose_name': 'Достижение',
@ -41,6 +46,7 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('template', models.URLField(verbose_name='Путь до шаблона')), ('template', models.URLField(verbose_name='Путь до шаблона')),
('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Course')),
], ],
options={ options={
'verbose_name': 'Генератор дипломов', 'verbose_name': 'Генератор дипломов',
@ -48,27 +54,39 @@ class Migration(migrations.Migration):
}, },
), ),
migrations.CreateModel( migrations.CreateModel(
name='SkillJ', name='Skill',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255, verbose_name='Наименование')),
('color', models.CharField(max_length=255, verbose_name='Цвет')),
('icon', models.ImageField(help_text='65x65', upload_to='skills', verbose_name='Картинка')),
('description', models.TextField(blank=True, verbose_name='Описание')),
], ],
options={ options={
'verbose_name': 'Размер навыка', 'verbose_name': 'Навык',
'verbose_name_plural': 'Размеры навыков', 'verbose_name_plural': 'Навыки',
}, },
), ),
migrations.CreateModel( migrations.CreateModel(
name='Skills', name='SkillJ',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255, verbose_name='Наименование')), ('lesson', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Lesson', verbose_name='Урок')),
('color', models.CharField(max_length=255, verbose_name='Цвет')), ('skill', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='achievements.Skill', verbose_name='Навык')),
('icon', models.ImageField(help_text='65x65', upload_to='skills', verbose_name='Картинка')),
('description', models.TextField(blank=True, verbose_name='Описание')),
], ],
options={ options={
'verbose_name': 'Навык', 'verbose_name': 'Размер навыка',
'verbose_name_plural': 'Навыки', 'verbose_name_plural': 'Размеры навыков',
}, },
), ),
migrations.AddField(
model_name='diploma',
name='template',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='achievements.DiplomaGen', verbose_name='Использовать шаблон'),
),
migrations.AddField(
model_name='diploma',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
] ]

@ -1,10 +1,10 @@
from django.conf import settings from django.conf import settings
from django.db import models from django.db import models
from course_service.courses.models import Course, Vertex from courses.models import Course, Lesson
class Achievements(models.Model): class Achievement(models.Model):
img = models.ImageField(verbose_name='Отображение достижения', upload_to='achives') img = models.ImageField(verbose_name='Отображение достижения', upload_to='achives')
text = models.CharField(max_length=255, verbose_name="Текст достижения") text = models.CharField(max_length=255, verbose_name="Текст достижения")
users = models.ManyToManyField(to=settings.AUTH_USER_MODEL) users = models.ManyToManyField(to=settings.AUTH_USER_MODEL)
@ -14,7 +14,7 @@ class Achievements(models.Model):
verbose_name_plural = 'Достижения' verbose_name_plural = 'Достижения'
class Skills(models.Model): class Skill(models.Model):
title = models.CharField(verbose_name='Наименование', max_length=255) title = models.CharField(verbose_name='Наименование', max_length=255)
color = models.CharField(verbose_name='Цвет', max_length=255) color = models.CharField(verbose_name='Цвет', max_length=255)
icon = models.ImageField(verbose_name='Картинка', upload_to='skills', help_text='65x65') icon = models.ImageField(verbose_name='Картинка', upload_to='skills', help_text='65x65')
@ -28,8 +28,8 @@ class Skills(models.Model):
class SkillJ(models.Model): class SkillJ(models.Model):
skill = models.ForeignKey(to=Skills, verbose_name='Навык') skill = models.ForeignKey(to=Skill, verbose_name='Навык')
lesson = models.ForeignKey(to=Vertex, verbose_name='Урок') lesson = models.ForeignKey(to=Lesson, verbose_name='Урок')
def __str__(self): return '%s' % self.skill def __str__(self): return '%s' % self.skill

@ -1,5 +1,5 @@
from rest_framework import serializers from rest_framework import serializers
from achievements.models import Diploma, Achievements from achievements.models import Diploma, Achievement
class DiplomaSerializer(serializers.ModelSerializer): class DiplomaSerializer(serializers.ModelSerializer):
@ -12,5 +12,5 @@ class DiplomaSerializer(serializers.ModelSerializer):
class AchievementsSerializer(serializers.ModelSerializer): class AchievementsSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Achievements model = Achievement
fields = ('img', 'text') fields = ('img', 'text')

@ -1,7 +1,7 @@
from django.conf.urls import url, include from django.conf.urls import url, include
urlpatterns = [ urlpatterns = [
url(r'courses/', include('course_service.courses.urls')), url(r'courses/', include('courses.urls')),
url(r'users/', include('access.urls')), url(r'users/', include('access.urls')),
url(r'library/', include('library.urls')), url(r'library/', include('library.urls')),
# url(r'finance/', include('finance.urls')), # url(r'finance/', include('finance.urls')),

@ -1 +0,0 @@
default_app_config = "course_service.courses.apps.CoursesAppConfig"

@ -1,7 +0,0 @@
from django.contrib import admin
from course_service.courses.models import Course, Topic, Vertex
admin.site.register(Topic)
admin.site.register(Vertex)
admin.site.register(Course)

@ -1,21 +0,0 @@
from course_service.courses.models import Course
class InApiTeacher:
@staticmethod
def add_teacher(slug: str, token: str) -> Course:
course = Course.objects.get(slug=slug)
course.teachers.append(token)
course.save()
return course
@staticmethod
def delete_teacher(slug: str, token: str) -> None:
course = Course.objects.get(slug=slug)
course.teachers.remove(token)
course.save()
return None
@staticmethod
def get_token_list(slug: str) -> list:
return Course.objects.get(slug=slug).teachers

@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-17 18:03
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('courses', '0001_initial'),
('maps', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='course',
name='route',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='maps.CourseRoute', verbose_name='Порядок прохождения по умолчанию'),
),
]

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-17 18:21
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('courses', '0002_course_route'),
]
operations = [
migrations.AlterField(
model_name='course',
name='slug',
field=models.SlugField(editable=False, max_length=127, unique=True),
),
]

@ -1,11 +0,0 @@
from django.db.models.signals import pre_delete
from django.dispatch import receiver
from course_service.courses.models import Vertex
@receiver(pre_delete, sender=Vertex)
def delete_dependencies(instance, **kwargs):
"""Удаляем зависимости вместе с узлом"""
if instance.content_object:
instance.content_object.delete()

@ -1,8 +0,0 @@
from django.contrib import admin
from course_service.maps.models import CourseMap, CourseRoute, PivotCourseMap, PivotVertex
admin.site.register(CourseMap)
admin.site.register(CourseRoute)
admin.site.register(PivotCourseMap)
admin.site.register(PivotVertex)

@ -1,16 +0,0 @@
from course_service.maps.models import CourseRoute
class OutApiRoute:
@staticmethod
def change_id(id: int) -> str:
return CourseRoute.objects.get(id=id).out_key
@staticmethod
def get_route_matrix(out_key):
map_list = [i.map_course for i in CourseRoute.objects.get(out_key=out_key).pivotcoursemap_set.all()]
return [[j.vertex.token for j in i.pivotvertex_set.all()] for i in map_list]
@staticmethod
def get_route(out_key):
return CourseRoute.objects.get(out_key=out_key)

@ -1,7 +0,0 @@
from django.apps import AppConfig
class MapsConfig(AppConfig):
name = "course_service.maps"
label = 'maps'
verbose_name = "Отображение курсов"

@ -1,2 +0,0 @@
class MapTypeError(ValueError):
pass

@ -1,83 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-17 18:03
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('courses', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='CourseMap',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, verbose_name='Имя прохождения')),
('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Course', verbose_name='К какому курсу привязан')),
],
options={
'verbose_name': 'Карта линейного прохождения курсов',
'verbose_name_plural': 'Карты линейного прохождения курсов',
},
),
migrations.CreateModel(
name='CourseRoute',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('out_key', models.CharField(editable=False, max_length=15, unique=True, verbose_name='Ключ для внешних сервисов')),
('name', models.CharField(max_length=255, verbose_name='Имя шаблона')),
('is_template', models.BooleanField(default=True, verbose_name='Может ли быть использован как шаблон')),
],
options={
'verbose_name': 'Маршрут прохождения',
'verbose_name_plural': 'Маршруты прохождения',
},
),
migrations.CreateModel(
name='PivotCourseMap',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sort', models.SmallIntegerField(verbose_name='Порядок сортировки')),
('map_course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='maps.CourseMap', verbose_name='К какой сортировке имеетотношение')),
('route', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='maps.CourseRoute', verbose_name='К какому узлу')),
],
options={
'verbose_name': 'Порядок сортировки маршрута',
'verbose_name_plural': 'Порядки сортировок маршрутов',
'ordering': ('sort',),
},
),
migrations.CreateModel(
name='PivotVertex',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sort', models.SmallIntegerField(verbose_name='Порядок сортировки')),
('map_course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='maps.CourseMap', verbose_name='К какой сортировке имеетотношение')),
('vertex', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Vertex', verbose_name='К какому узлу')),
],
options={
'verbose_name': 'Порядок сортировки узла',
'verbose_name_plural': 'Порядки сортировок узла',
'ordering': ('sort',),
},
),
migrations.AlterUniqueTogether(
name='pivotvertex',
unique_together=set([('sort', 'map_course'), ('map_course', 'vertex')]),
),
migrations.AlterUniqueTogether(
name='pivotcoursemap',
unique_together=set([('sort', 'route'), ('map_course', 'route')]),
),
migrations.AlterUniqueTogether(
name='coursemap',
unique_together=set([('course', 'name')]),
),
]

@ -1,67 +0,0 @@
from django.db import models
from course_service.maps.exeptions import MapTypeError
from lms.global_decorators import transaction_decorator
class CourseRoute(models.Model):
"""
Объединение нескольких мап курса, одназначно
определяет способ прохождения по курсу.
"""
out_key = models.CharField(max_length=15, unique=True, verbose_name="Ключ для внешних сервисов", editable=False)
name = models.CharField(max_length=255, verbose_name='Имя шаблона')
is_template = models.BooleanField(default=True, verbose_name='Может ли быть использован как шаблон')
def __str__(self):
return self.name
class Meta:
verbose_name = 'Маршрут прохождения'
verbose_name_plural = 'Маршруты прохождения'
class CourseMap(models.Model):
"""
Способы отображения курса. Упорядочены в порядке возрастания приоретета.
"""
course = models.ForeignKey(to='courses.Course', verbose_name='К какому курсу привязан')
name = models.CharField(max_length=255, verbose_name="Имя прохождения")
def __str__(self):
return '''Линейное прохождение по курсу "%s"''' % self.course.title
class Meta:
verbose_name = 'Карта линейного прохождения курсов'
verbose_name_plural = 'Карты линейного прохождения курсов'
unique_together =('course', 'name',)
class PivotCourseMap(models.Model):
route = models.ForeignKey(to=CourseRoute, verbose_name="К какому узлу")
sort = models.SmallIntegerField(verbose_name='Порядок сортировки')
map_course = models.ForeignKey(to=CourseMap, verbose_name='К какой сортировке имеетотношение')
def __str__(self):
return '''Карта с №%s по маршруту ID%s''' % (self.sort, self.route_id)
class Meta:
verbose_name = 'Порядок сортировки маршрута'
verbose_name_plural = 'Порядки сортировок маршрутов'
unique_together = (('map_course', 'route'), ('sort', 'route'),)
ordering = ('sort',)
class PivotVertex(models.Model):
vertex = models.ForeignKey(to='courses.Vertex', verbose_name="К какому узлу")
sort = models.SmallIntegerField(verbose_name='Порядок сортировки')
map_course = models.ForeignKey(to=CourseMap, verbose_name='К какой сортировке имеетотношение')
def __str__(self):
return '''Карта с №%s по линейному прохождению ID%s''' % (self.sort, self.map_course_id)
class Meta:
verbose_name = 'Порядок сортировки узла'
verbose_name_plural = 'Порядки сортировок узла'
unique_together = (('map_course', 'vertex'), ('sort', 'map_course'))
ordering = ('sort',)

@ -1,27 +0,0 @@
from rest_framework import serializers
from course_service.maps.models import CourseRoute, CourseMap
class CourseRouteSerializer(serializers.ModelSerializer):
maps = serializers.SerializerMethodField()
class Meta:
model = CourseRoute
fields = ('maps', 'name')
@staticmethod
def get_maps(self):
return [CourseMapSerializer(i.map_course).data for i in self.pivotcoursemap_set.all()]
class CourseMapSerializer(serializers.ModelSerializer):
vertexes = serializers.SerializerMethodField()
class Meta:
model = CourseMap
fields = ('name', 'vertexes')
@staticmethod
def get_vertexes(self):
return [i.vertex.token for i in self.pivotvertex_set.all()]

@ -1,23 +0,0 @@
# from django.test import TestCase
# from course_service.maps.models import CourseRoute
#
# from course_service.maps.api import OutApiRoute
#
#
# class RouteOutApiTestCase(TestCase):
# def setUp(self):
# self.first_course = Course.objects.create(
# title='Первый курс',
# slug='perviy-kuourse',
# )
# self.CDTeacher = OutApiRoute()
#
# def test_teacher(self):
# token1 = "token1"
# token2 = "token2"
# token3 = "token3"
# self.CDTeacher.add_teacher(slug=self.first_course.slug, token=token1)
# self.CDTeacher.add_teacher(slug=self.first_course.slug, token=token2)
# self.CDTeacher.add_teacher(slug=self.first_course.slug, token=token3)
# self.CDTeacher.delete_teacher(slug=self.first_course.slug, token=token2)
# self.assertEqual(self.CDTeacher.get_token_list(self.first_course.slug), ['token1', 'token3'])

@ -1,3 +0,0 @@
from django.shortcuts import render
# Create your views here.

@ -0,0 +1 @@
default_app_config = "courses.apps.CoursesAppConfig"

@ -0,0 +1,6 @@
from django.contrib import admin
from courses.models import Course, Topic, Lesson
admin.site.register(Topic)
admin.site.register(Lesson)
admin.site.register(Course)

@ -0,0 +1,29 @@
from courses.models import Course
class InApiTeacher:
@staticmethod
def add_teacher(slug: str, token: str) -> Course:
course = Course.objects.get(slug=slug)
course.teacher_tokens.append(token)
course.save()
return course
@staticmethod
def delete_teacher(slug: str, token: str) -> None:
course = Course.objects.get(slug=slug)
course.teacher_tokens.remove(token)
course.save()
return None
@staticmethod
def get_token_list(slug: str) -> list:
return Course.objects.get(slug=slug).teacher_tokens
class CourseProgressApi:
@staticmethod
def get_topic_lesson(course_token: str):
course = Course.objects.get(token=course_token)
return [[lesson.token for lesson in topic.lesson_set.all()] for topic in course.topic_set.all()]

@ -3,7 +3,7 @@ from django.apps import AppConfig
class CoursesAppConfig(AppConfig): class CoursesAppConfig(AppConfig):
name = "course_service.courses" name = "courses"
label = 'courses' label = 'courses'
verbose_name = "Курсы" verbose_name = "Курсы"

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-17 18:03 # Generated by Django 1.11.6 on 2018-01-22 09:27
from __future__ import unicode_literals from __future__ import unicode_literals
import django.contrib.postgres.fields import django.contrib.postgres.fields
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import uuid
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -19,17 +20,18 @@ class Migration(migrations.Migration):
name='Course', name='Course',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('hidden', models.BooleanField(default=False, verbose_name='Видно только оплатившим')), ('token', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='Токен')),
('level', models.CharField(choices=[('B', 'Базовый'), ('A', 'Продвинутый'), ('E', 'Экспертный'), ('B+A', 'Базовый + Продвинутый')], default='B', max_length=3, verbose_name='Уровень')), ('slug', models.SlugField(editable=False, max_length=127, unique=True)),
('slug', models.SlugField(editable=False, unique=True)),
('direction', models.SmallIntegerField(choices=[(3, 'Бизнес'), (2, 'Веб-дизайн'), (1, 'Разработка'), (4, 'Рисование')], verbose_name='Направление')),
('public', models.BooleanField(default=False, verbose_name='Опубликовать')),
('title', models.CharField(max_length=255, unique=True, verbose_name='Заголовок')), ('title', models.CharField(max_length=255, unique=True, verbose_name='Заголовок')),
('description', models.TextField(blank=True, verbose_name='Описание')), ('description', models.TextField(blank=True, verbose_name='Описание')),
('level', models.CharField(choices=[('B', 'Базовый'), ('A', 'Продвинутый'), ('E', 'Экспертный'), ('B+A', 'Базовый + Продвинутый')], default='B', max_length=3, verbose_name='Уровень')),
('direction', models.SmallIntegerField(choices=[(3, 'Бизнес'), (2, 'Веб-дизайн'), (1, 'Разработка'), (4, 'Рисование'), (5, 'Музыка')], verbose_name='Направление')),
('public', models.BooleanField(default=False, verbose_name='Опубликовать')),
('teacher_tokens', django.contrib.postgres.fields.ArrayField(base_field=models.UUIDField(editable=False, verbose_name='Токен препода'), default=[], size=None, verbose_name='Преподователи курса')),
('image', models.URLField(blank=True, max_length=255, verbose_name='Изображение')), ('image', models.URLField(blank=True, max_length=255, verbose_name='Изображение')),
('big_image', models.URLField(blank=True, max_length=255, verbose_name='Большое изображение')), ('big_image', models.URLField(blank=True, max_length=255, verbose_name='Большое изображение')),
('big_mobile_image', models.URLField(blank=True, help_text='Большая картинка для мобильной версии', max_length=255, verbose_name='Под мобилку')), ('big_mobile_image', models.URLField(blank=True, help_text='Большая картинка для мобильной версии', max_length=255, verbose_name='Под мобилку')),
('teachers', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=15, verbose_name='Ссылки на преподов'), default=[], size=None)), ('hidden', models.BooleanField(default=False, verbose_name='Видно только оплатившим')),
], ],
options={ options={
'verbose_name': 'Курс', 'verbose_name': 'Курс',
@ -37,35 +39,44 @@ class Migration(migrations.Migration):
}, },
), ),
migrations.CreateModel( migrations.CreateModel(
name='Topic', name='Lesson',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('token', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='Токен')),
('key', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='Внутрений ключ используется для расшивровки')),
('title', models.CharField(max_length=255, verbose_name='Название')), ('title', models.CharField(max_length=255, verbose_name='Название')),
('icon', models.ImageField(blank=True, null=True, upload_to='', verbose_name='Иконка темы')),
('description', models.TextField(blank=True, null=True, verbose_name='Описание')), ('description', models.TextField(blank=True, null=True, verbose_name='Описание')),
('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Course', verbose_name='курс')), ('video', models.TextField(blank=True, null=True, verbose_name='Код видео')),
('material_tokens', django.contrib.postgres.fields.ArrayField(base_field=models.UUIDField(editable=False, verbose_name='Токен материала'), default=[], size=None, verbose_name='Материалы курса')),
('free', models.BooleanField(default=False, verbose_name='Привилегии для узла не будут проверяться')),
('sort', models.SmallIntegerField(unique=True)),
('is_hm', models.BooleanField(default=False)),
('old_id', models.IntegerField(blank=True, null=True)),
], ],
options={ options={
'verbose_name': 'Тема', 'verbose_name': 'Урок',
'verbose_name_plural': 'Темы', 'verbose_name_plural': 'Уроки',
'ordering': ('sort',),
}, },
), ),
migrations.CreateModel( migrations.CreateModel(
name='Vertex', name='Topic',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255, verbose_name='Название')), ('title', models.CharField(max_length=255, verbose_name='Название')),
('free', models.BooleanField(default=False, verbose_name='Привилегии для узла не будут проверяться')),
('description', models.TextField(blank=True, null=True, verbose_name='Описание')), ('description', models.TextField(blank=True, null=True, verbose_name='Описание')),
('video', models.TextField(blank=True, null=True, verbose_name='Код видео')), ('icon', models.ImageField(blank=True, null=True, upload_to='', verbose_name='Иконка темы')),
('materials', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=15, verbose_name='Материалы урока'), size=None)), ('sort', models.SmallIntegerField(unique=True)),
('valid_type', models.SmallIntegerField(choices=[(3, 'Автаматическая валидация'), (2, 'Полуавтаматическая валидация'), (1, 'Ручная валидация'), (0, 'Без валидации')], default=0)), ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Course', verbose_name='курс')),
('token', models.CharField(editable=False, max_length=15, unique=True, verbose_name='Ключ доступа к узлу')),
('topic', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Topic', verbose_name='Тема')),
], ],
options={ options={
'verbose_name': 'Урок', 'verbose_name': 'Тема',
'verbose_name_plural': 'Уроки', 'verbose_name_plural': 'Темы',
}, },
), ),
migrations.AddField(
model_name='lesson',
name='topic',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Topic', verbose_name='Тема'),
),
] ]

@ -1,15 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import random import uuid
import string
import unidecode import unidecode
from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db import models from django.db import models
from django.template.defaultfilters import slugify from django.template.defaultfilters import slugify
from model_utils import Choices
from course_service.maps.models import CourseRoute, CourseMap
from lms.global_decorators import transaction_decorator
from lms.tools import decode_base64, get_real_name from lms.tools import decode_base64, get_real_name
COURSE_LEVEL = ( COURSE_LEVEL = (
@ -24,6 +22,7 @@ COURSE_DIRECTION = (
(2, 'Веб-дизайн'), (2, 'Веб-дизайн'),
(1, 'Разработка'), (1, 'Разработка'),
(4, 'Рисование'), (4, 'Рисование'),
(5, 'Музыка'),
) )
@ -63,37 +62,33 @@ class CourseManager(models.Manager):
return course return course
def change_route(self, pk, route):
route = CourseRoute.objects.get(id=route)
course = self.get(id=pk)
course.route = route
course.save()
return course
class Course(models.Model): class Course(models.Model):
hidden = models.BooleanField(verbose_name='Видно только оплатившим', default=False) token = models.UUIDField(verbose_name="Токен", default=uuid.uuid4, editable=False)
level = models.CharField(verbose_name='Уровень', choices=COURSE_LEVEL, default='B', max_length=3)
slug = models.SlugField(unique=True, editable=False, max_length=127) slug = models.SlugField(unique=True, editable=False, max_length=127)
direction = models.SmallIntegerField(choices=COURSE_DIRECTION, verbose_name='Направление')
public = models.BooleanField(verbose_name='Опубликовать', default=False)
title = models.CharField(verbose_name="Заголовок", max_length=255, unique=True) title = models.CharField(verbose_name="Заголовок", max_length=255, unique=True)
description = models.TextField(verbose_name='Описание', blank=True) description = models.TextField(verbose_name='Описание', blank=True)
level = models.CharField(verbose_name='Уровень', choices=COURSE_LEVEL, default='B', max_length=3)
direction = models.SmallIntegerField(choices=COURSE_DIRECTION, verbose_name='Направление')
public = models.BooleanField(verbose_name='Опубликовать', default=False)
teacher_tokens = ArrayField(
models.UUIDField(verbose_name="Токен препода", editable=False),
default=[],
verbose_name='Преподователи курса',
)
image = models.URLField(verbose_name='Изображение', blank=True, max_length=255) image = models.URLField(verbose_name='Изображение', blank=True, max_length=255)
big_image = models.URLField(verbose_name='Большое изображение', blank=True, max_length=255) big_image = models.URLField(verbose_name='Большое изображение', blank=True, max_length=255)
big_mobile_image = models.URLField(verbose_name='Под мобилку', blank=True, big_mobile_image = models.URLField(verbose_name='Под мобилку', blank=True,
help_text='Большая картинка для мобильной версии', max_length=255) help_text='Большая картинка для мобильной версии', max_length=255)
teachers = ArrayField( hidden = models.BooleanField(verbose_name='Видно только оплатившим', default=False)
models.CharField(max_length=15, blank=True, verbose_name='Ссылки на преподов'), default=[])
route = models.OneToOneField(to=CourseRoute, verbose_name="Порядок прохождения по умолчанию", blank=True, null=True)
def __str__(self): def __str__(self):
return self.title return self.title
def get_statistic(self): def get_statistic(self):
return { return {
'topic_count': self.topic_set.count(), 'topic_count': self.topic_set.all().count(),
'task_count': sum([topic.vertex_set.count() for topic in self.topic_set.all()]) 'task_count': sum([topic.lesson_set.count() for topic in self.topic_set.all()])
} }
objects = CourseManager() objects = CourseManager()
@ -104,31 +99,35 @@ class Course(models.Model):
class Topic(models.Model): class Topic(models.Model):
course = models.ForeignKey(to=Course, verbose_name='курс')
title = models.CharField(verbose_name='Название', max_length=255) title = models.CharField(verbose_name='Название', max_length=255)
icon = models.ImageField(verbose_name='Иконка темы', null=True, blank=True)
description = models.TextField(verbose_name='Описание', blank=True, null=True) description = models.TextField(verbose_name='Описание', blank=True, null=True)
course = models.ForeignKey(to=Course, verbose_name='курс') icon = models.ImageField(verbose_name='Иконка темы', null=True, blank=True)
sort = models.SmallIntegerField(unique=True)
class Meta: class Meta:
verbose_name = "Тема" verbose_name = "Тема"
verbose_name_plural = "Темы" verbose_name_plural = "Темы"
class Vertex(models.Model): class Lesson(models.Model):
VALID_TYPE = ( token = models.UUIDField(verbose_name="Токен", default=uuid.uuid4, editable=False)
(3, 'Автаматическая валидация'), key = models.UUIDField(
(2, 'Полуавтаматическая валидация'), verbose_name="Внутрений ключ используется для расшивровки", default=uuid.uuid4, editable=False)
(1, 'Ручная валидация'),
(0, 'Без валидации'),
)
topic = models.ForeignKey(to=Topic, verbose_name='Тема') topic = models.ForeignKey(to=Topic, verbose_name='Тема')
title = models.CharField(verbose_name='Название', max_length=255) title = models.CharField(verbose_name='Название', max_length=255)
free = models.BooleanField(default=False, verbose_name='Привилегии для узла не будут проверяться')
description = models.TextField(verbose_name='Описание', blank=True, null=True) description = models.TextField(verbose_name='Описание', blank=True, null=True)
video = models.TextField(verbose_name='Код видео', blank=True, null=True) video = models.TextField(verbose_name='Код видео', blank=True, null=True)
materials = ArrayField(models.CharField(max_length=15, blank=True, verbose_name='Материалы урока')) material_tokens = ArrayField(
valid_type = models.SmallIntegerField(choices=VALID_TYPE, default=0) models.UUIDField(verbose_name="Токен материала", editable=False),
token = models.CharField(max_length=15, verbose_name="Ключ доступа к узлу", unique=True, editable=False) default=[],
verbose_name='Материалы курса',
)
free = models.BooleanField(default=False, verbose_name='Привилегии для узла не будут проверяться')
sort = models.SmallIntegerField(unique=True)
is_hm = models.BooleanField(default=False) #TODO костыли
old_id = models.IntegerField(null=True, blank=True)
def __str__(self): def __str__(self):
return self.title return self.title
@ -136,3 +135,59 @@ class Vertex(models.Model):
class Meta: class Meta:
verbose_name = "Урок" verbose_name = "Урок"
verbose_name_plural = "Уроки" verbose_name_plural = "Уроки"
ordering = ('sort', )
# class LessonRequirement(models.Model):
# lesson = models.ForeignKey(to=Lesson)
# requirement = models.ForeignKey(to='courses.Requirement')
# sort = models.SmallIntegerField(default=1)
#
# class Meta:
# verbose_name = "Порядок требований"
# verbose_name_plural = "Порядок требований"
# ordering = ('sort', )
# unique_together = ('lesson', 'requirement', 'sort')
#
#
# class Requirement(models.Model):
# CHECK_TYPES = Choices('student', 'teacher', 'auto',)
#
# token = models.UUIDField(verbose_name="Токен", default=uuid.uuid4, primary_key=True, editable=False)
# name = models.CharField(max_length=31, verbose_name="Название", unique=True)
# checker = models.CharField(
# choices=CHECK_TYPES, default=CHECK_TYPES.teacher, max_length=15, verbose_name="Проверяющий",)
# min_balls = models.SmallIntegerField(default=50, verbose_name='Проходной бал')
#
# def __str__(self):
# return self.name
#
# class Meta:
# verbose_name = "Требования"
# verbose_name_plural = "Требования"
#
#
# class Question(models.Model):
# FIELD_TYPES = Choices('text', 'char', 'boolean', 'file')
#
# requirement = models.ForeignKey(to=Requirement)
# text = models.TextField(verbose_name="Вопрос")
# type = models.CharField(choices=FIELD_TYPES, default=FIELD_TYPES.char, max_length=20)
# multiple = models.BooleanField(default=False)
# null = models.BooleanField(default=False)
# balls = models.SmallIntegerField(default=100, verbose_name='Вознаграждение')
#
# class Meta:
# verbose_name = "Вопрос"
# verbose_name_plural = "Вопросы"
#
#
# class RightAnswer(models.Model):
# question = models.OneToOneField(to=Question)
# text = models.TextField(verbose_name="Верный ответ")
# success_comment = models.TextField(blank=True, null=True, verbose_name="Комментарий при верном ответе")
# error_comment = models.TextField(blank=True, null=True, verbose_name="Комментарий при ошибке")
#
# class Meta:
# verbose_name = "Верный ответ"
# verbose_name_plural = "Верные ответы"

@ -1,7 +1,6 @@
from rest_framework import serializers from rest_framework import serializers
from course_service.courses.models import Course, Vertex, Topic from courses.models import Course, Lesson, Topic
from course_service.maps.serializers import CourseRouteSerializer
class TopicSerializer(serializers.ModelSerializer): class TopicSerializer(serializers.ModelSerializer):
@ -13,26 +12,21 @@ class TopicSerializer(serializers.ModelSerializer):
@staticmethod @staticmethod
def get_children(self): def get_children(self):
return [MiniVertexSerializer(i).data for i in self.vertex_set.all()] return [MiniLessonSerializer(i).data for i in self.lesson_set.all()]
class MiniVertexSerializer(serializers.ModelSerializer): class MiniLessonSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Vertex model = Lesson
fields = ('title', 'free', 'token') fields = ('title', 'free', 'token')
class VertexSerializer(MiniVertexSerializer): class LessonSerializer(MiniLessonSerializer):
valid_type = serializers.SerializerMethodField()
class Meta: class Meta:
model = Vertex model = Lesson
exclude = ('id', 'topic', 'free') exclude = ('id', 'topic', 'free', 'key')
@staticmethod
def get_valid_type(self):
return self.get_valid_type_display()
class CourseInitSerializer(serializers.ModelSerializer): class CourseInitSerializer(serializers.ModelSerializer):
@ -44,20 +38,15 @@ class CourseInitSerializer(serializers.ModelSerializer):
class CourseTreeSerializer(serializers.ModelSerializer): class CourseTreeSerializer(serializers.ModelSerializer):
tree = serializers.SerializerMethodField() tree = serializers.SerializerMethodField()
route = serializers.SerializerMethodField()
class Meta: class Meta:
model = Course model = Course
fields = ('tree', 'route', 'slug') fields = ('tree', 'slug')
@staticmethod @staticmethod
def get_tree(self): def get_tree(self):
return [TopicSerializer(i).data for i in self.topic_set.all()] return [TopicSerializer(i).data for i in self.topic_set.all()]
@staticmethod
def get_route(self):
return CourseRouteSerializer(self.route).data
class CourseDetailSerializer(serializers.ModelSerializer): class CourseDetailSerializer(serializers.ModelSerializer):
level = serializers.SerializerMethodField() level = serializers.SerializerMethodField()
@ -66,7 +55,7 @@ class CourseDetailSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Course model = Course
exclude = ('route', 'id') exclude = ('id', )
@staticmethod @staticmethod
def get_level(self): def get_level(self):

@ -1,8 +1,8 @@
from django.test import TestCase from django.test import TestCase
from course_service.courses.models import Course from courses.models import Course
from lms.tools import EXAMPLE_BASE64 from lms.tools import EXAMPLE_BASE64
from course_service.courses.api import InApiTeacher from courses.api import InApiTeacher
class CourseInApiTestCase(TestCase): class CourseInApiTestCase(TestCase):

@ -1,9 +1,9 @@
from django.conf.urls import url from django.conf.urls import url
from course_service.courses import views as views from courses import views as views
urlpatterns = [ urlpatterns = [
url(r'vertex/(?P<token>.+)/$', views.VertexDetail.as_view()), url(r'vertex/(?P<token>.+)/$', views.LessonDetail.as_view()),
url(r'tree/(?P<slug>.+)/$', views.TreeView.as_view()), url(r'tree/(?P<slug>.+)/$', views.TreeView.as_view()),
url(r'^$', views.CourseListView.as_view()), url(r'^$', views.CourseListView.as_view()),
] ]

@ -1,9 +1,9 @@
from course_service.courses.models import Course, Vertex from courses.models import Course, Lesson
from rest_framework.renderers import JSONRenderer from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from course_service.courses.serializers import CourseDetailSerializer, CourseTreeSerializer, VertexSerializer from courses.serializers import CourseDetailSerializer, CourseTreeSerializer, LessonSerializer
class TreeView(APIView): class TreeView(APIView):
@ -32,22 +32,22 @@ class CourseListView(APIView):
return Response(res, self.status_code) return Response(res, self.status_code)
class VertexDetail(APIView): class LessonDetail(APIView):
renderer_classes = (JSONRenderer,) renderer_classes = (JSONRenderer,)
@staticmethod @staticmethod
def get(request, token): def get(request, token):
try: try:
vertex = Vertex.objects.get(token=token) vertex = Lesson.objects.get(token=token)
except Vertex.DoesNotExist: except Lesson.DoesNotExist:
return Response("Vertex doesn't exist", status=404) return Response("Vertex doesn't exist", status=404)
# if not vertex.check_vertex(request.user): # if not vertex.check_vertex(request.user):
# return Response("permission denied", status=403) # return Response("permission denied", status=403)
# TODO: Доделать систему прав на курс # TODO: Доделать систему прав на курс
res = VertexSerializer(vertex).data res = LessonSerializer(vertex).data
# progress = vertex.course.progress_set.filter(user=request.user) # progress = vertex.course.progress_set.filter(user=request.user)
# try: # try:
# if progress.exists(): # if progress.exists():

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

@ -0,0 +1,49 @@
id,img,text
17,achives/14t_rpXjXAR.png,Java: Основы построения веб-приложений
16,achives/15t.png,"Java: Веб-приложения:формы, заголовки и сессии"
15,achives/16_JHghTAS.png,Java: Дипломная работа
18,achives/13t_8wJaFO6.png,Java: Многопоточность
19,achives/12t.png,Java: Работа с базами данных
20,achives/11t_PlIJyKE.png,Java: Работа с файлами и сетью
21,achives/10t.png,Java: Внешний вид GUI и рисование
22,achives/9t_XB04aQi.png,Java: Основы построения GUI
23,achives/8t.png,Java: Тестирование и отладка
24,achives/7t_IcXWV0J.png,Java: Наследование и полиморфизм
25,achives/6t_6GPa4Cm.png,Java: Объекты и классы
26,achives/5t_MwDFKjE.png,Java: Типы данных: Коллекции
27,achives/4t_Wicy963.png,Java: Типы данных: Строки и даты
28,achives/3t_LEiFMmW.png,Java: Типы данных: числа
29,achives/2t_4GxlPla.png,Java: Синтаксис языка
30,achives/1t_95r5sEe.png,Java: Первые шаги
31,achives/16t_ElpMZT4.png,iOS: Обзор публикации приложений
32,achives/15t_G2dHbG0.png,iOS: WatchKit
33,achives/14t_zjLTw3C.png,"iOS: iPad, Swift 2 и iOS 9"
34,achives/13t_r5AUBkb.png,iOS: Анимация в iOS
35,achives/12t_O9ziZtQ.png,iOS: CoreData и CoreGraphics
36,achives/11t_R6ZFGPo.png,iOS: Работа с Сетью
37,achives/10t_rmSFxlx.png,iOS: Структуры данных и парсинг
38,achives/9t_FNgfsfx.png,iOS: Функциональные возможности
39,achives/8t_6caS4Mz.png,iOS: Инструменты ООП
40,achives/7t_U1hXqEE.png,"iOS: Enums, Structs"
41,achives/6t_6Q4gH9C.png,iOS: Optionals и Кортежи и отображение данных
42,achives/5t_Vg8scWj.png,"iOS: Классы, UIViewController"
43,achives/4t_M29zQP7.png,iOS: Программирование на Swift и основы UIKit
44,achives/3t_EzvoNos.png,iOS: Ветвление и коллекции
45,achives/2t_yq8f6nn.png,iOS: Основы Swift как языка программирования
46,achives/1t_VRlv8Gf.png,iOS: Основы программирования под iOS
47,achives/16t_9WuvuNR.png,Excel: Введение в макросы и язык VBA (продолжение)
48,achives/15t_B3SKKLm.png,Excel: Введение в макросы и язык VBA
49,achives/14t_1w3a59I.png,Excel: Работа с внешними источниками данных
50,achives/13t_QyBcCES.png,Excel: Сложные диаграммы
51,achives/12t_W1ecROl.png,Excel: Прогнозирование
52,achives/11t_Fzu2ICH.png,Excel: Инструменты анализа и оптимизации 2
53,achives/10t_z5VaRjv.png,Excel: Инструменты анализа и оптимизации
54,achives/9t_nBtNR8G.png,Excel: Формулы массивов
55,achives/8t_rFqrijw.png,Excel: Сводные таблицы
56,achives/7t_yXjsXUR.png,Excel: Визуализация данных
57,achives/6t_JYzPjTH.png,Excel: Формулы и функции. Работа с ошибками
58,achives/5t_noq5RnK.png,Excel: Формулы и функции.Более сложные формулы
59,achives/4t_Xl2DpdL.png,Excel: Формулы и функции.Работать с формулами
60,achives/3t_afQm2YN.png,Excel: Формулы и функции.Введение
61,achives/2t_79u4GW7.png,Excel: Основной функционал (продолжение)
62,achives/1t_xLDdZ54.png,Excel: Основной функционал
unable to load file from base commit

@ -0,0 +1,66 @@
user,img
volnov_mv@mail.ru,diploms/378-005-104-3681735945.png
flintl@bk.ru,diploms/366-005-081-3414761253.png
slava-sh@yandex.ru,diploms/378-005-106-5622100597.png
rock_n_vladok@mail.ru,diploms/373-005-088-8460749285.png
mahneva-mariya@mail.ru,diploms/365-005-080-7809812978.png
krrera@mail.ru,diploms/378-005-116-0406189146.png
a.michurin1783@mail.ru,diploms/367-005-082-4390339652.png
dron84@gmail.com,diploms/378-005-100-0453317915.png
garuskin.dima@gmail.com,diploms/372-005-087-7268502359.png
artemshchurev@gmail.com,diploms/378-005-110-5734330734.png
vladislav-avdeev@mail.ru,diploms/378-005-101-0579235306.png
serge-meb@mail.ru,diploms/378-005-093-9287022829.png
walerock@mail.ru,diploms/371-005-086-2010728249.png
threed@rsgc.ru,diploms/370-005-085-1445403214.png
ebortnikov@mail.ru,diploms/363-005-078-8013223405.png
tsubasa@tut.by,diploms/378-005-105-1400814657.png
susenko_a@ukr.net,diploms/363-005-077-4635813020.png
vladimir_glight@mail.ru,diploms/378-005-107-7721009995.png
aselya.bekenova@gmail.com,diploms/378-005-097-9345817764.png
kovalev.home@mail.ru,diploms/363-005-079-4953079839.png
grapeperson@inbox.ru,diploms/sharp378-005-123-4175944962.png
irinki@mail.ru,diploms/378-005-108-3003245596.png
komplekt_17@mail.ru,diploms/378-005-098-9891625101.png
o.gricenko@yandex.ru,diploms/378-005-117-1653742224.png
softmay@inbox.ru,diploms/378-005-099-6127989680.png
orloff.eg@yandex.ru,diploms/375-005-090-0013188516.png
izumovn@gmail.com,diploms/378-005-112-1318768199.png
anton_semenchuk@rambler.ru,diploms/374-005-089-1818186998.png
misha41192@mail.ru,diploms/378-005-109-8808693661.png
nadin12233@mail.ru,diploms/377-005-092-2649819525.png
lukin68@mail.ru,diploms/378-005-119-1396775116.png
caplinos@mail.ru,diploms/sharp378-005-124-8590405231.png
artyomzolotykh@gmail.com,diploms/363-005-076-8109754653.png
joy76@mail.ru,diploms/378-005-118-7596974049.png
vitalikbog@gmail.com,diploms/378-005-113-6643879642.png
artyomzykov@gmail.com,diploms/369-005-084-6514008928.png
v.vahmintsev@gmail.com,diploms/378-005-120-1857123638.png
malkolm063@yandex.ru,diploms/378-005-102-3225259917.png
cathayraht@gmail.com,diploms/sharp378-005-127-5465822465.png
ol88@bk.ru,diploms/378-005-103-4917135722.png
tatyana.best@list.ru,diploms/378-005-114-5045779647.png
kaa-work@yandex.ru,diploms/sharp378-005-121-6828207500.png
lissy@mail.ru,diploms/sharp378-005-125-7455009303.png
host510@mail.ru,diploms/378-005-115-8424668311.png
viktorbeznosov@mail.ru,diploms/sharp378-005-122-2445336751.png
danikanisa@gmail.com,diploms/sharp378-005-126-9693925734.png
aivis@ukr.net,diploms/sharp378-005-128-0750907505.png
chiganu@gmail.com,diploms/sharp378-005-129-3202020273.png
my-post@mail.ru,diploms/sharp378-005-131-1850096338.png
mr.titarev@mail.ru,diploms/sharp378-005-130-3534666982.png
roman@proskurnin.ru,diploms/sharp378-005-132-0213523725.png
miklik@yandex.ru,diploms/sharp378-005-133-1393432393.png
vikpointer@yandex.ru,diploms/sharp378-005-134-0649133567.png
firenskiy@yandex.ru,diploms/sharp378-005-137-3289702004.png
uglukfearless@gmail.com,diploms/sharp378-005-135-0135161426.png
hazhaev@gmail.com,diploms/sharp378-005-136-2411124105.png
d6232@bigmir.net,diploms/sharp378-005-138-8182572282.png
alexplatinum@yandex.ru,diploms/sharp378-005-139-5436334541.png
oleguch@ya.ru,diploms/sharp378-005-141-3016802392.png
volkovv71@gmail.com,diploms/sharp378-005-140-1452821946.png
cyrius@yandex.ru,diploms/sharp378-005-142-9487486532.png
orlenko23@mail.ru,diploms/sharp378-005-143-3541435391.png
vilorovna@yandex.ru,diploms/sharp378-005-144-3873082140.png
bpkprog@yandex.ru,diploms/sharp378-005-145-0913411150.png
roman@kislovs.ru,diploms/sharp378-005-146-1693156186.png
unable to load file from base commit

File diff suppressed because it is too large Load Diff

@ -0,0 +1,53 @@
hidden,level,slug,direction,public,title,image,big_image,big_mobile_image,teachers
False,B,SMM-–menedzher,3,True,SMM –менеджер,course/670х270_LMS_SMM.png,course/1480х238_LMS_SMM.png,,['lubov.loginova@ingate.ru']
False,B,Draw&Go,4,True,Акварельная живопись,course/mal_1.png,course/bol_1.png,course/bol_6A56hsO.jpg,['kristinagalockina@gmail.com']
False,B,ux-analitik,2,True,ux аналитик,course/analyticsprm.jpg,course/analyticsprw.jpg,course/analyticsprm_2J0YORM.jpg,['aic.analytics@skill-box.ru']
False,B+A,Android-razrabotchik,1,True,Android-разработчик,course/android_TK1Lu3x.jpg,course/android1.jpg,,"['raynor73@gmail.com', 'gerasimenkosv@bk.ru']"
False,B,UI-animatsiya,2,True,UI анимация,course/motion.jpg,course/motionlong.jpg,course/motion_sqnScls.jpg,['aic.motion@skill-box.ru']
True,B,"diplomnyy-proekt-""professiya-internet-marketolog""",3,True,"Дипломный проект ""Профессия Интернет-маркетолог""",course/670х270_LMS_маркетолог___uTutc3u.png,course/LMS_hard_ingate_1EM8sL7.png,,['lubov.loginova@ingate.ru']
True,E,diplomnyy-proekt-«ntv-plyus»,2,True,Дипломный проект «НТВ ПЛЮС»,course/нтв_плюс_маленькая.jpg,course/нтв_плюс_большая.jpg,,['ntvplus@skill-box.ru']
False,B,Key-Visual,2,True,Key Visual,course/670х270_Ретушь.png,course/1480х238_Ретушь.png,,['pajasu@gmail.com']
False,A,Excel-prodvinutyy,1,True,Excel продвинутый,course/excel_ni9vwCu.jpg,course/excel1_CcJM6br.jpg,course/excel_EEMEX0r.jpg,['buyavets@gmail.com']
True,E,diplomnyy-proekt-«festival'-molodezhi»,2,True,Дипломный проект «Фестиваль молодежи»,course/Фестиваль_молодежи_маленькая.jpg,course/Untitled-1.jpg,,['fm@skill-box.ru']
True,B,programmist-1s-bitriks-(2018),1,True,Программист 1С-Битрикс (2018),course/670х270_Xr9I0hT.png,course/1480х238_Om0nUCO.png,,['hex2bomb@gmail.com']
True,E,diplomnyy-proekt-«gazprombank»,2,True,Дипломный проект «Газпромбанк»,course/gazprom2.jpg,course/gazprom1.jpg,course/gazprom2_ZShOr5R.jpg,['gpb@skillbox.ru']
True,E,diplomnyy-proekt-«pochta-bank»,2,True,Дипломный проект «ПОЧТА БАНК»,course/почта_банк_маленькая.jpg,course/почтабанк_большая.jpg,,['pochtabank@skill-box.ru']
True,E,diplomnyy-proekt-«smp-bank»,2,True,Дипломный проект «СМП БАНК»,course/спм_маленькая_IGl1R5y.jpg,course/спм_большая_BBAUsvw.jpg,,['cmpbank@skill-box.ru']
False,B,veb-dizayn-dlya-nachinayuschih,2,True,Веб-Дизайн для начинающих,course/design.jpg,course/design1.jpg,,['camb@ya.ru']
False,B,JavaScript,1,True,JavaScript,course/js.jpg,course/js2.jpg,course/js_bBp5dM2.jpg,"['gerasimenkosv@bk.ru', 'yegor.yakovishen@me.com']"
True,B,"diplomnyy-proekt-""upravlenie-proektami""",3,True,"Дипломный проект ""Управление проектами""",course/670х238_Sibirix.jpg,course/1480х238_Sibirix.jpg,,['sibirix@skill-box.ru']
True,E,diplomnyy-proekt-«Yota»,2,True,Дипломный проект «Yota»,course/yota2.jpg,course/yota1.jpg,course/yota2_D06GbKi.jpg,['yota@skillbox.ru']
False,B+A,veb-razrabotchik,1,True,Веб-разработчик,course/web.jpg,course/web1.jpg,,"['gerasimenkosv@bk.ru', 'html@bedev.ru', 't@skillbox.ru']"
True,B,diplomnyy-proekt-«promsvyaz'bank»,2,True,Дипломный проект «Промсвязьбанк»,course/670х270_Промсвязьбанк_1.png,course/1480х238_Промсвязьбанк_1.png,,['komanda.promsvyazbank@yandex.ru']
True,B,test,1,False,Таргетированная реклама,,,,['t@skillbox.ru']
False,A,kak-otkryt-veb-studiyu,3,True,Как открыть веб-студию,course/Studio.jpg,course/webs.jpg,,['inna.spirina@gmail.com']
True,B,Sound-design,5,True,Sound design,course/670х270_Саунд_topSg6J.png,course/1480х238_Саунд_JpMiW6C.png,,['basie.ru@gmail.com']
False,B,kak-otkryt'-salon-krasoty,3,True,Как открыть салон красоты,course/670х270.png,course/1480х238_pX5zoz0.png,,['dbeleshko@gmail.com']
False,B,videobloging,3,True,Видеоблогинг,course/vb1.jpg,course/vb2.jpg,course/vb1_6ZsdrSE.jpg,['yoola@skill-box.ru']
True,B+A,razrabotchik-1s-bitriks,1,False,Разработчик 1С-Битрикс,course/bitrix_waWRT5T.jpg,course/bitrix1.jpg,,['t@skillbox.ru']
True,E,diplomnyy-proekt-«Kaspi.kz»,2,True,Дипломный проект «Kaspi.kz»,course/kaspi2.jpg,course/kaspi1.jpg,course/kaspi2_QzZi0IX.jpg,['kaspi@skillbox.ru']
True,E,diplomnyy-proekt-«MOS.RU»,2,True,Дипломный проект «MOS.RU»,course/mosru2.jpg,course/mosru1.jpg,course/mosru2_hDuLl1H.jpg,['mosru@skillbox.ru']
False,A,veb-dizayn-PRO,2,True,Веб-Дизайн PRO,course/Pro.jpg,course/pro1.jpg,,['colloum.ux@gmail.com']
True,B,1--s-bitriks-(2017),1,False,1 -с битрикс (2017),,,,['t@skillbox.ru']
True,B,sinema-4D-dlya-veb-dizayna,2,True,Cinema 4D для веб-дизайна,course/Обложка_LMS_Cinema_4D.jpg,course/Обложка_LMS_большая_Cinema_4D_aXJ3Ocs.jpg,,['rovshen.eyubov@yahoo.com']
True,B,diplomnyy-proekt-«Mos.ru-2»,2,True,Дипломный проект «Mos.ru-2»,course/mosru2_иконка_дипломныи_проект.jpg,course/mosru1_экран_в_курсе_большое_изображение.jpg,course/mosru2_hDuLl1H_изображение_под_мобилку.jpg,['mosru@skillbox.ru']
False,A,professiya-internet-marketolog-ot-a-do-ya,3,True,Профессия интернет-маркетолог от А до Я,course/marketolog_Ykj1b1J.jpg,course/marketolog1_bOfeJXW.jpg,,['buyavets@gmail.com']
True,B,Python-razrabotchik-s-nulya-(2018),1,True,Python-разработчик с нуля (2018),course/670х238_Python_dQO3TkP.jpg,course/1480х238_Python.jpg,,['vadim.shandrinov@skillbox.ru']
True,B,diplomnyy-proekt-«Ticketland»,2,True,Дипломный проект «Ticketland»,course/670х270_Тикетлэнд_1.png,course/1480х238_Тикетлэнд_1.png,,['komanda.ticketland@yandex.ru']
False,B,Blockchain,3,True,Blockchain,course/670x270.png,course/1480x238.png,,['blockchain.skillbox@yandex.ru']
True,B,ux-design-special-AIC-,2,True,ux design special AIC,course/ux.jpg,course/aic1.jpg,,"['ux@aic.ru', 'aic@skillbox.ru', 't@skillbox.ru']"
False,B,sketching-dlya-dizaynera,2,True,Скетчинг для дизайнера,course/sketch1.jpg,course/sketch2.jpg,course/sketch1_fUHb6TG.jpg,['sketch@skill-box.ru']
False,E,ux-design,2,True,ux design,course/ux.jpg,course/aic1.jpg,,['aic@skillbox.ru']
True,B,Python,1,False,Python,,,,['t@skillbox.ru']
False,B,upravlenie-proektami,3,True,Управление проектами,course/иконки-тем-для-LMS.jpg,course/обложкА-для-страницы-курса-в-ЛМС.jpg,,['sibirix@skill-box.ru']
True,B,zadanie-k-kursu-«professiya-PR-menedzher»-dlya-komandy-«megafon-–-severo-zapad»,3,True,ЗАДАНИЕ К КУРСУ «ПРОФЕССИЯ PR-МЕНЕДЖЕР» ДЛЯ КОМАНДЫ «МЕГАФОН – СЕВЕРО-ЗАПАД»,,,,['kstrifonova@gmail.com']
False,A,professiya---internet-marketolog,3,True,Профессия — интернет-маркетолог,course/LMS_mini_ingate.png,course/LMS_hard_ingate.png,,['lubov.loginova@ingate.ru']
False,B,reklamnaya-grafika,2,True,Рекламная графика,course/670х270_Тикетлэнд_1_wE1a3an.png,course/1480х238_Тикетлэнд_1_ObFQyCr.png,,['korumart@gmail.com']
False,A,professiya-PR-menedzher,3,True,Профессия PR-менеджер,course/pr.jpg,course/pr1.jpg,,['nataliya.akimova47@gmail.com']
True,B,rasprodazha,3,True,Распродажа,,,,['t@skillbox.ru']
False,B,dizayn-mobil'nyh-prilozheniy,2,True,Дизайн мобильных приложений RMR,course/photoRB_BZ5JhPj.jpg,course/photo5467609895021946983.jpg,,['skillbox@redmadrobot.com']
True,B,diplomnyy-proekt-«vtb24»,2,True,Дипломный проект «ВТБ24»,,,,['komanda.vtb24@yandex.ru']
False,B+A,Java-razrabotchik,1,True,Java разработчик,course/java.jpg,course/java1.jpg,,"['it.is.the.g@list.ru', 'gerasimenkosv@bk.ru', 't@skillbox.ru']"
False,B+A,iOS-razrabotchik,1,True,iOS Разработчик,course/swift.jpg,course/ios1.jpg,,"['exeypanteleev@gmail.com', 't@skillbox.ru']"
False,B,Excel-bazovyy,1,True,Excel базовый,course/excel.jpg,course/excel1.jpg,,['buyavets@gmail.com']
False,B+A,Csharp-razrabotchik,1,True,C# разработчик,course/csharp.jpg,course/csharp1.jpg,,"['codemy.csharp@gmail.com', 't@skillbox.ru', 'johntorohov@mail.ru']"
unable to load file from base commit

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

@ -16,7 +16,7 @@ django.setup()
from yandex_money.models import Payment from yandex_money.models import Payment
from finance.models import Bill, Invoice from finance.models import Bill, Invoice
from progress.models import Progress from progress.models import Progress
from course_service.courses.models import Course from courses.models import Course
if __name__ == '__main__': if __name__ == '__main__':
Payment.objects.all().delete() Payment.objects.all().delete()
@ -28,12 +28,11 @@ if __name__ == '__main__':
bill_kwarg = dict() bill_kwarg = dict()
row = dict(row) row = dict(row)
bill_kwarg['id'] = row.pop('id', None) bill_kwarg['id'] = row.pop('id', None)
bill_kwarg['route'] = Course.objects.get(id=row.pop('course__id', None)).route.out_key opener = row.pop('opener', None)
opener_id = row.pop('opener__id', None) bill_kwarg['opener'] = get_user_model().objects.get(email=opener) if opener \
bill_kwarg['opener'] = get_user_model().objects.get(id=opener_id) if opener_id \
else get_user_model().objects.get(email="kate.gazukina@skillbox.ru") else get_user_model().objects.get(email="kate.gazukina@skillbox.ru")
email = row.pop('user__email', None) email = row.pop('user', None)
try: try:
email = email[:email.index("\n")] email = email[:email.index("\n")]
except ValueError: except ValueError:
@ -46,12 +45,15 @@ if __name__ == '__main__':
bill_kwarg['comment'] = row.pop('comment', None) bill_kwarg['comment'] = row.pop('comment', None)
bill_kwarg['description'] = row.pop('description', None) bill_kwarg['description'] = row.pop('description', None)
bill_kwarg['course_token'] = Course.objects.get(slug=row.pop('course', None)).token
try: try:
bill = Bill.objects.create(**bill_kwarg) bill = Bill.objects.create(**bill_kwarg)
except IntegrityError: except IntegrityError:
pass bill = Bill.objects.get(course_token=bill_kwarg['course_token'], user=bill_kwarg['user'])
method = row.pop('bill_method', None) method = row.pop('bill_method', None)
try: try:
price = int(row.pop('price', None)) price = int(row.pop('price', None))
except ValueError: except ValueError:
@ -71,12 +73,16 @@ if __name__ == '__main__':
inv.save() inv.save()
if method == 'Y' and not row['status'] == 'W' and price: if method == 'Y' and not row['status'] == 'W' and price:
row['yandex_pay'], _is_create = Payment.objects.get_or_create( yandex_pay, _is_create = Payment.objects.get_or_create(
order_amount=price, order_amount=price,
order_number=inv.id, order_number=inv.id,
customer_number=bill.user.id, customer_number=bill.user.out_key,
user=bill.user, user=bill.user,
cps_email=bill.user.email, cps_email=bill.user.email,
shop_amount=real_price, shop_amount=real_price,
status='Processed' if 'P' else ('Success' if 'F' else 'Fail') status=Payment.STATUS.PROCESSED if row['status'] == 'P' else
) (Payment.STATUS.SUCCESS if row['status'] == 'F' else Payment.STATUS.FAIL)
)
inv.yandex_pay = yandex_pay
inv.save()

@ -14,30 +14,66 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings")
django.setup() django.setup()
from storage.models import Comment, File from storage.models import Comment, File
from courses.models import Lesson
from access.models.progress import ProgressLesson, Progress
if __name__ == '__main__': if __name__ == '__main__':
csv.field_size_limit(500 * 1024 * 1024) csv.field_size_limit(500 * 1024 * 1024)
Comment.objects.all().delete()
with open('./management/comment.csv') as comment_csv: with open('./management/comment.csv') as comment_csv:
comment_reader = csv.DictReader(comment_csv) comment_reader = csv.DictReader(comment_csv)
for row in comment_reader: for row in comment_reader:
if row['type'] == 'task' or row['type'] == 'exam': if row['type'] == 'task' or row['type'] == 'exam':
l = Lesson.objects.get(old_id=row['parent_id'])
try: try:
c = Comment.objects.create( p = ProgressLesson.objects.get(
id=row['id'], lesson_token=l.token,
email=row['owner__email'], progress__user__email=row['student'],
text=row['text'], )
key=''.join(random.choice(string.ascii_letters) for x in range(15)),
except ProgressLesson.DoesNotExist:
try:
pr = Progress.objects.create(
course_token=l.topic.course.token,
user=get_user_model().objects.get(email=row['student']),
teacher=get_user_model().objects.get(email=row['teacher']),
)
except IntegrityError:
pr = Progress.objects.get(
course_token=l.topic.course.token,
user=get_user_model().objects.get(email=row['student']),
)
p = ProgressLesson.objects.create(
lesson_token=l.token,
teacher=get_user_model().objects.get(email=row['teacher']),
progress=pr,
) )
except IntegrityError:
c = Comment.objects.get(id=row['id'])
for file_id in row['files'].split("[")[1].split("]")[0].split(", "): files = [File.objects.get(id=file) for file in row['files'].split("[")[1].split("]")[0].split(',')
if file_id: if not file == '']
c.files.add(File.objects.get(id=file_id))
comment = Comment.objects.create(
text=row['text'] or row['bb_text'],
user_key=get_user_model().objects.get(email=row['owner__email']).out_key,
)
if row['status'] == 'Одобренно':
p.status = ProgressLesson.STATUSES.done
p.date = row['date']
elif row['status'] == 'Отклонено':
p.status = ProgressLesson.STATUSES.fail
p.comment_tokens.append(comment.token)
p.save()
c.date = row['date'] [comment.files.add(file) for file in files]
c.save() comment.date = row['date']
comment.save()
parent_id = int(row['parent_id']) for i in ProgressLesson.objects.filter(comment_tokens__isnull=False):
if row['type'] == 'task': if len(i.comment_tokens) % 2 == 1:
parent_id += 50 i.status = ProgressLesson.STATUSES.wait
i.save()

@ -1,7 +1,5 @@
import csv import csv
import os import os
import random
import string
import sys import sys
import django import django
@ -10,18 +8,12 @@ sys.path.append("../")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings")
django.setup() django.setup()
from course_service.courses.api import InApiTeacher from courses.api import InApiTeacher
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from course_service.courses.models import Course, Vertex, Topic from courses.models import Course, Lesson, Topic
from course_service.maps.models import CourseRoute, CourseMap, PivotVertex, PivotCourseMap
from storage.models import File from storage.models import File
if __name__ == '__main__': if __name__ == '__main__':
CourseMap.objects.all().delete()
CourseRoute.objects.all().delete()
PivotVertex.objects.all().delete()
PivotCourseMap.objects.all().delete()
Vertex.objects.all().delete()
Course.objects.all().delete() Course.objects.all().delete()
with open('./course/course.csv') as user_csv: with open('./course/course.csv') as user_csv:
@ -29,28 +21,21 @@ if __name__ == '__main__':
for row in user_reader: for row in user_reader:
row = dict(row) row = dict(row)
teachers = row.pop('teachers', None).split("[")[1].split("]")[0].split(", ") teachers = row.pop('teachers', None).split("[")[1].split("]")[0].split(", ")
row.pop('mentors', None).split("[")[1].split("]")[0].split(", ")
course, _is_create = Course.objects.get_or_create(**row) course, _is_create = Course.objects.get_or_create(**row)
map_obj = CourseMap.objects.create(course=course)
route_obj = CourseRoute.objects.create(
name='''%s''' % course.title,
out_key=''.join(random.choice(string.ascii_letters) for x in range(15)),
)
PivotCourseMap.objects.create(map_course=map_obj, route=route_obj, sort=0)
course.route = route_obj
course.save()
try: try:
for teacher in teachers: for teacher in teachers:
teacher = teacher.replace("\'", "")
if teacher: if teacher:
teacher = get_user_model().objects.get(id=teacher).out_key teacher = get_user_model().objects.get(email=teacher).out_key
InApiTeacher.add_teacher(course.slug, teacher) InApiTeacher.add_teacher(course.slug, teacher)
except get_user_model().DoesNotExist: except get_user_model().DoesNotExist:
print('Плохо') print('Плохо')
with open('./course/vertex.csv') as vertex_csv: with open('./course/vertex.csv') as vertex_csv:
t_sort = 0
l_sort = 0
vertex_reader = csv.DictReader(vertex_csv) vertex_reader = csv.DictReader(vertex_csv)
for row in vertex_reader: for row in vertex_reader:
row = dict(row) row = dict(row)
@ -58,61 +43,38 @@ if __name__ == '__main__':
description = row.pop('description', None) description = row.pop('description', None)
title = row.pop('title', None) title = row.pop('title', None)
pk = row.pop('id', None) pk = row.pop('id', None)
materials = []
try: try:
m = row.pop('materials', None) m = row.pop('materials', None)
materials = []
if m: if m:
materials = [File.objects.get(id=i).key for i in m.split("[")[1].split("]")[0].split(", ")] materials = [File.objects.get(id=i).token for i in m.split("[")[1].split("]")[0].split(", ")]
except ValueError: except ValueError:
pass pass
if model_type == 'topic': if model_type == 'topic':
course = Course.objects.get(id=row.pop('course', None)) t_sort += 1
map_obj = CourseMap.objects.get(course=course) course = Course.objects.get(slug=row.pop('course', None))
Topic.objects.create( Topic.objects.create(
id=pk, id=pk,
icon=row.pop('icon', None), icon=row.pop('icon', None),
course=course, course=course,
description=description, description=description,
title=title, title=title,
sort=t_sort,
) )
try: else:
topic_id = row.pop('topic', None) topic_id = row.pop('topic', None)
last_pivot = PivotVertex.objects.filter(map_course=map_obj).last() l_sort += 1
if model_type == 'tutorial': topic = Topic.objects.get(id=topic_id)
topic = Topic.objects.get(id=topic_id) small_vertex = Lesson.objects.create(
small_vertex = Vertex.objects.create( video=row.pop('video', None),
id=pk, material_tokens=materials,
video=row.pop('video', None), topic=topic,
materials=materials, free=row['free'],
topic=topic, description=description,
free=row['free'], title=title,
description=description, sort=l_sort,
title=title, old_id=pk,
token=''.join(random.choice(string.ascii_letters) for x in range(15)) is_hm=model_type == 'task',
) )
PivotVertex.objects.create(
map_course=map_obj,
vertex=small_vertex,
sort=last_pivot.sort+1 if last_pivot else 1,
)
if model_type == 'task':
topic = Topic.objects.get(id=topic_id)
small_vertex = Vertex.objects.create(
id=pk,
materials=materials,
topic=topic,
description=description,
title=title,
valid_type=1,
token=''.join(random.choice(string.ascii_letters) for x in range(15))
)
PivotVertex.objects.create(
map_course=map_obj,
vertex=small_vertex,
sort=last_pivot.sort+1 if last_pivot else 1,
)
except Topic.DoesNotExist:
pass

@ -6,23 +6,24 @@ sys.path.append("../")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings")
django.setup() django.setup()
from achievements.models import Diploma, Achievements from achievements.models import Diploma, Achievement
if __name__ == '__main__': if __name__ == '__main__':
Diploma.objects.all().delete() Diploma.objects.all().delete()
Achievements.objects.all().delete() Achievement.objects.all().delete()
with open('./achievement/achievement.csv') as achievements_csv: with open('./achievement/achievement.csv') as achievements_csv:
achievements_reader = csv.DictReader(achievements_csv) achievements_reader = csv.DictReader(achievements_csv)
for row in achievements_reader: for row in achievements_reader:
Achievements.objects.create(**row) Achievement.objects.create(**row)
with open('./achievement/user_achievement.csv') as achievements_csv: with open('./achievement/user_achievement.csv') as achievements_csv:
achievements_reader = csv.DictReader(achievements_csv) achievements_reader = csv.DictReader(achievements_csv)
for row in achievements_reader: for row in achievements_reader:
a = Achievements.objects.get(id=row['id']) a = Achievement.objects.get(id=row['id'])
a.users.add(get_user_model().objects.get(id=row['student_id'])) a.users.add(get_user_model().objects.get(email=row['user']))
with open('./achievement/diploma.csv') as achievements_csv: with open('./achievement/diploma.csv') as achievements_csv:
achievements_reader = csv.DictReader(achievements_csv) achievements_reader = csv.DictReader(achievements_csv)
for row in achievements_reader: for row in achievements_reader:
Diploma.objects.create(**row) user=get_user_model().objects.get(email=row.pop('user', None))
Diploma.objects.create(user=user, **row)

@ -6,62 +6,53 @@ import json
from datetime import datetime from datetime import datetime
from django.db import IntegrityError
sys.path.append("../") sys.path.append("../")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings")
django.setup() django.setup()
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from progress.models import Progress, PivotProgressVertex from access.models.progress import ProgressLesson
from course_service.courses.models import Vertex, Course from progress.models import Progress
from course_service.maps.api import OutApiRoute from courses.models import Lesson, Course
if __name__ == '__main__': if __name__ == '__main__':
Progress.objects.all().delete() Progress.objects.all().delete()
ProgressLesson.objects.all().delete()
with open('./access/progress.csv') as progress_csv: with open('./access/progress.csv') as progress_csv:
progress_reader = csv.DictReader(progress_csv) progress_reader = csv.DictReader(progress_csv)
for row in progress_reader: for row in progress_reader:
course = Course.objects.get(id=row['course']) course = Course.objects.get(slug=row['course'])
route = course.route.out_key
api = OutApiRoute
list_vertex = OutApiRoute.get_route_matrix(route)[0]
try: try:
user = get_user_model().objects.get(id=row['user']) teacher = get_user_model().objects.get(email=row['teacher'])
user = get_user_model().objects.get(email=row['user'])
except get_user_model().DoesNotExist: except get_user_model().DoesNotExist:
print(row['user'])
continue continue
try: try:
p = Progress.objects.get(
course=course.slug,
user=user,
teacher=get_user_model().objects.get(id=row['teacher']),
)
except Progress.DoesNotExist:
p = Progress.objects.create( p = Progress.objects.create(
course=course.slug, course_token=course.token,
user=user, user=user,
teacher=get_user_model().objects.get(id=row['teacher']), teacher=teacher,
) )
pivots = json.loads(row['success']) except IntegrityError:
for pivot in pivots: continue
try:
pv = PivotProgressVertex.objects.create(
progress=p,
vertex=Vertex.objects.get(id=pivot['id']).token,
teacher=get_user_model().objects.get(id=pivot['teacher']),
status=1,
)
if pivot['date'] and not pivot['date'] == 'None':
pv.date = datetime.strptime(pivot['date'].split('.')[0], '%Y-%m-%d %H:%M:%S')
pv.status = 2
pv.save()
except Vertex.DoesNotExist:
pass
g = Group.objects.get(name='students') g = Group.objects.get(name='students')
g.user_set.add(user) g.user_set.add(user)
pivots = json.loads(row['success'])
for pivot in pivots:
pv = ProgressLesson.objects.create(
progress=p,
lesson_token=Lesson.objects.get(old_id=pivot['id']).token,
teacher=get_user_model().objects.get(email=pivot['teacher']),
)
if pivot['date'] and not pivot['date'] == 'None':
pv.date = datetime.strptime(pivot['date'].split('.')[0], '%Y-%m-%d %H:%M:%S')
pv.status = ProgressLesson.STATUSES.done
pv.save()

@ -19,4 +19,4 @@ if __name__ == '__main__':
for row in storage_reader: for row in storage_reader:
if row['original']: if row['original']:
key = ''.join(random.choice(string.ascii_letters) for _x in range(15)) key = ''.join(random.choice(string.ascii_letters) for _x in range(15))
File.objects.create(original=row['original'], id=row['id'], key=key) File.objects.create(original=row['original'], id=row['id'])

@ -1,4 +1,4 @@
import os, sys, django, csv, json import os, sys, django, csv
sys.path.append("../") sys.path.append("../")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings")
@ -8,6 +8,7 @@ from django.contrib.auth import get_user_model
from access.models.other import Account from access.models.other import Account
if __name__ == '__main__': if __name__ == '__main__':
get_user_model().objects.all().delete()
with open('./access/users.csv') as user_csv: with open('./access/users.csv') as user_csv:
user_reader = csv.DictReader(user_csv) user_reader = csv.DictReader(user_csv)
for row in user_reader: for row in user_reader:
@ -18,6 +19,8 @@ if __name__ == '__main__':
except IndexError: except IndexError:
row['role_list'] = [] row['role_list'] = []
row.pop('id')
get_user_model().objects.get_or_create(**row) get_user_model().objects.get_or_create(**row)
except ValueError as e: except ValueError as e:
if str(e) == 'The given email must be set': if str(e) == 'The given email must be set':
@ -30,7 +33,7 @@ if __name__ == '__main__':
for row in account_reader: for row in account_reader:
row = dict(row) row = dict(row)
try: try:
acc = Account.objects.get(owner=get_user_model().objects.get(id=row['owner'])) acc = Account.objects.get(owner=get_user_model().objects.get(email=row['owner']))
acc.phone = row['phone'] if row['phone'] and len(row['phone']) < 16 else None acc.phone = row['phone'] if row['phone'] and len(row['phone']) < 16 else None
acc.city = row['city'] if row['city'] else None acc.city = row['city'] if row['city'] else None
acc.photo = row['photo'] acc.photo = row['photo']

File diff suppressed because one or more lines are too long

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-17 18:03 # Generated by Django 1.11.6 on 2018-01-22 09:27
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
@ -12,8 +12,8 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('yandex_money', '0001_initial'), ('yandex_money', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
operations = [ operations = [
@ -21,7 +21,7 @@ class Migration(migrations.Migration):
name='Bill', name='Bill',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('route', models.CharField(max_length=15, verbose_name='Токен роута')), ('course_token', models.UUIDField(editable=False, verbose_name='Токен курса')),
('comment', models.TextField(blank=True, editable=False, help_text='Будет показано пользователю', verbose_name='Комментарий продавца')), ('comment', models.TextField(blank=True, editable=False, help_text='Будет показано пользователю', verbose_name='Комментарий продавца')),
('description', models.TextField(blank=True, verbose_name='Внутренняя заметка')), ('description', models.TextField(blank=True, verbose_name='Внутренняя заметка')),
('opener', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Ответственный сотрудник')), ('opener', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Ответственный сотрудник')),
@ -37,7 +37,7 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.CharField(choices=[('W', 'Ожидание согласия'), ('P', 'На оплате'), ('F', 'Оплачен'), ('C', 'Отклонен')], default='W', max_length=1, verbose_name='Статус')), ('status', models.CharField(choices=[('W', 'Ожидание согласия'), ('P', 'На оплате'), ('F', 'Оплачен'), ('C', 'Отклонен')], default='W', max_length=1, verbose_name='Статус')),
('price', models.IntegerField(editable=False, verbose_name='Сумма')), ('price', models.IntegerField(blank=True, editable=False, null=True, verbose_name='Сумма')),
('real_price', models.IntegerField(blank=True, editable=False, help_text='Сумма, минус комиссия', null=True, verbose_name='Полученная сумма')), ('real_price', models.IntegerField(blank=True, editable=False, help_text='Сумма, минус комиссия', null=True, verbose_name='Полученная сумма')),
('method', models.CharField(choices=[('C', 'Наличные'), ('H', 'JustClick'), ('A', 'Альфа-Банк'), ('S', 'SimplePay'), ('Y', 'YandexKassa')], default='Y', max_length=2, verbose_name='Способ оплаты')), ('method', models.CharField(choices=[('C', 'Наличные'), ('H', 'JustClick'), ('A', 'Альфа-Банк'), ('S', 'SimplePay'), ('Y', 'YandexKassa')], default='Y', max_length=2, verbose_name='Способ оплаты')),
('key', models.CharField(blank=True, editable=False, max_length=255, unique=True, verbose_name='Ключ платежа')), ('key', models.CharField(blank=True, editable=False, max_length=255, unique=True, verbose_name='Ключ платежа')),
@ -54,6 +54,6 @@ class Migration(migrations.Migration):
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='bill', name='bill',
unique_together=set([('route', 'user')]), unique_together=set([('course_token', 'user')]),
), ),
] ]

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-25 11:42
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('finance', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='invoice',
name='price',
field=models.IntegerField(blank=True, editable=False, null=True, verbose_name='Сумма'),
),
]

@ -3,11 +3,11 @@ from django.conf import settings
from django.db import models from django.db import models
from yandex_money.models import Payment from yandex_money.models import Payment
from course_service.courses.models import Course, Vertex from courses.models import Course, Lesson
class Bill(models.Model): class Bill(models.Model):
route = models.CharField(max_length=15, verbose_name='Токен роута') course_token = models.UUIDField(verbose_name="Токен курса", editable=False)
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='Плательщик', related_name='bill_user') user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='Плательщик', related_name='bill_user')
opener = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='Ответственный сотрудник', null=True) opener = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='Ответственный сотрудник', null=True)
comment = models.TextField(verbose_name='Комментарий продавца', help_text='Будет показано пользователю', comment = models.TextField(verbose_name='Комментарий продавца', help_text='Будет показано пользователю',
@ -24,7 +24,7 @@ class Bill(models.Model):
verbose_name = 'Счет' verbose_name = 'Счет'
verbose_name_plural = 'Счета' verbose_name_plural = 'Счета'
unique_together = ( unique_together = (
('route', 'user',), ('course_token', 'user',),
) )

@ -1,5 +0,0 @@
# -*- coding: utf-8 -*-
from django.contrib import admin
# Register your models here.

@ -1,52 +0,0 @@
# -*- coding: utf-8 -*-
def create_user(email, name):
""" Создает пользователя в GitLab и возвращает токен пользователя для последующей работы """
user_token = '...'
return user_token
def get_user(email, name):
""" Выбирает пользователя GitLab и возвращает токен пользователя для последующей работы """
user_token = '...'
return user_token
def create_repository(user, project_name):
""" Создает проект (репу) в GitLab и возвращает токен репу """
repository_token = '...'
return repository_token
def get_repository(user, project_name):
""" Возвращает токен репы """
repository_token = '...'
return repository_token
def make_user_project_master(user, project):
""" Делает пользователя мастером в проекте """
pass
def copy_files_to_repository(base_repository, files_path, target_repository, autor):
""" Копирует файлы из базовой репы в целевую от имени автора """
pass
def approve_homework(teacher, base_repository, student, target_repository, files_path):
""" Принять домашку у студента, копировать новые файлы """
# TODO возможно это на уровне LMS надо делать, а с гитлабом вызвать copy_files_to_repository
pass
def make_save_point(teacher, students):
""" зафиксировать точку в репах с принятыми домашками """
pass
def get_last_changes(teacher, students, save_point=None):
""" показать последние изменения в репах учеников относительно save point (последнего, если None) """
pass

@ -1,7 +0,0 @@
# -*- coding: utf-8 -*-
from django.apps import AppConfig
class GitlabServiceConfig(AppConfig):
name = 'gitlab_service'

@ -1,106 +0,0 @@
# -*- coding: utf-8 -*-
import gitlab
class GitlabWrapperException(Exception):
pass
class GitlabWrapperFilesExists(GitlabWrapperException):
pass
class GitlabWrapper:
API_VERSION = '4'
def __init__(self, url, token, ssl_verify=True):
self.url = url
self.token = token
self.gl = gitlab.Gitlab(
url=self.url, private_token=self.token, api_version=self.API_VERSION, ssl_verify=ssl_verify)
def get_user(self, email):
exists_users = self.gl.users.list(search=email)
if exists_users:
return exists_users[0]
def create_user(self, email, name):
username = email[:email.find('@')].replace('.', '_')
# TODO can contain only letters, digits, '_', '-' and '.'.
# TODO Cannot start with '-' or end in '.', '.git' or '.atom'."
exists_users = self.gl.users.list(username=username)
if len(exists_users):
username = username + '_{}'.format(len(exists_users) + 1)
user_data = dict(
email=email,
username=username,
name=name,
reset_password=True,
)
new_user = self.gl.users.create(user_data)
return new_user
def get_or_create_user(self, email, name):
user = self.get_user(email)
if user is None:
user = self.create_user(email, name)
return user
def get_user_project(self, user, project_name):
try:
exists_project = self.gl.projects.get('{}/{}'.format(user.username, project_name))
return exists_project
except gitlab.GitlabGetError:
return None
def create_user_project(self, user, project_name):
user.projects.create(data={'name': project_name})
# делаем еще запрос, для получения проекта со всеми аттрибутами
new_project = self.gl.projects.get('{}/{}'.format(user.username, project_name))
return new_project
def get_or_create_user_project(self, user, project_name):
project = self.get_user_project(user, project_name)
if project is None:
project = self.create_user_project(user, project_name)
return project
def make_user_project_master(self, user, project):
try:
project.members.create(data=dict(
user_id=user.id,
access_level=40,
))
except gitlab.GitlabCreateError:
pass
def copy_files_to_repository(self, base_project, files_path, target_project, autor):
commit_actions = []
items = base_project.repository_tree(path=files_path, recursive=True)
for item in items:
# item = {'name': '__init__.py', 'path': 'module_01/lesson_01/__init__.py', 'type': 'blob',
# 'id': '633f866158ac742cf754a2c43edcb07e3a094f3c', 'mode': '100644'}
if item['type'] == 'blob':
file_sha = item['id']
file_info = base_project.repository_blob(sha=file_sha)
# file_info = {'content': 'IyAtKi0gY29kaW5nOiB1dGYtOCAtKi0KCg==', 'size': 25,
# 'sha': '633f866158ac742cf754a2c43edcb07e3a094f3c', 'encoding': 'base64'}
action = dict(
action='create',
file_path=item['path'],
content=file_info['content'],
encoding='base64',
)
commit_actions.append(action)
commit_data = {
'branch': 'master',
'commit_message': 'Add {}'.format(files_path),
'actions': commit_actions
}
try:
commit = target_project.commits.create(commit_data, sudo=autor)
return commit
except gitlab.GitlabCreateError as exc:
if exc.response_code == 400:
raise GitlabWrapperFilesExists()
raise

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
from django.db import models
from model_utils import Choices
class User(models.Model):
ROLES = Choices(
('student', 'Студент'),
('teacher', 'Преподователь'),
)
token = models.CharField(max_length=256)
role = models.CharField(max_length=32, choices=ROLES, default=ROLES.student)
class Repository(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=256)

@ -1,4 +0,0 @@
# -*- coding: utf-8 -*-
from django.test import TestCase
# Create your tests here.

@ -1,5 +0,0 @@
# -*- coding: utf-8 -*-
from django.shortcuts import render
# Create your views here.

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-17 18:03 # Generated by Django 1.11.6 on 2018-01-22 09:27
from __future__ import unicode_literals from __future__ import unicode_literals
import datetime import datetime

@ -107,8 +107,7 @@ libs = (
apps = ( apps = (
'access', 'access',
'course_service.maps', 'courses',
'course_service.courses',
'storage', 'storage',
'finance', 'finance',
'library', 'library',
@ -130,6 +129,7 @@ MIDDLEWARE_CLASSES = [
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
#'access.middleware.CheckPerm', #'access.middleware.CheckPerm',
'access.middleware.RequestToApi', 'access.middleware.RequestToApi',
# 'access.middleware.UpdateActivity',
] ]
ROOT_URLCONF = 'lms.urls' ROOT_URLCONF = 'lms.urls'
@ -137,7 +137,7 @@ ROOT_URLCONF = 'lms.urls'
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')] 'DIRS': [os.path.join(BASE_DIR, 'templates'), '/home/andrey/skill-front']
, ,
'APP_DIRS': True, 'APP_DIRS': True,
'OPTIONS': { 'OPTIONS': {

@ -1,43 +1,44 @@
amqp==2.2.2 django-model-utils
Babel==2.5.1
billiard==3.5.0.3
bson==0.5.0
celery==4.1.0 celery==4.1.0
certifi==2017.11.5
chardet==3.0.4
Django==1.11.6 Django==1.11.6
django-appconf==1.0.2
django-celery-beat==1.0.1 django-celery-beat==1.0.1
django-celery-email==2.0.0 django-celery-email==2.0.0
django-celery-results==1.0.1 django-celery-results==1.0.1
django-environ==0.4.4 django-environ==0.4.4
django-model-utils==3.0.0
django-phonenumber-field==1.3.0 django-phonenumber-field==1.3.0
django-redis==4.8.0 django-redis==4.8.0
django-redis-sessions==0.6.1 django-redis-sessions==0.6.1
django-yandex-money==1.1.2 git+https://github.com/yandex-money/yandex-money-kit-django.git
djangorestframework==3.7.0 djangorestframework==3.7.0
environ==1.0
flower==0.9.2
future==0.16.0
idna==2.6
kombu==4.1.0
lxml==4.1.1
Naked==0.1.31
olefile==0.44
phonenumberslite==8.8.8
Pillow==4.3.0 Pillow==4.3.0
pkg-resources==0.0.0
psycopg2==2.7.3.1 psycopg2==2.7.3.1
python-gitlab==1.1.0
pytz==2017.2
PyYAML==3.12
raven==6.2.1 raven==6.2.1
redis==2.10.6
requests==2.18.4 requests==2.18.4
six==1.11.0
tornado==4.5.2
Unidecode==0.4.21 Unidecode==0.4.21
urllib3==1.22
vine==1.1.4 # amqp==2.2.2
yandex-money-sdk==0.1.3 # Babel==2.5.1
# billiard==3.5.0.3
# bson==0.5.0
# certifi==2017.11.5
# chardet==3.0.4
# django-appconf==1.0.2
# django-model-utils==3.0.0
# environ==1.0
# flower==0.9.2
# future==0.16.0
# idna==2.6
# kombu==4.1.0
# lxml==4.1.1
# Naked==0.1.31
# olefile==0.44
# phonenumberslite==8.8.8
# pkg-resources==0.0.0
# python-gitlab==1.1.0
# pytz==2017.2
# PyYAML==3.12
# redis==2.10.6
# six==1.11.0
# tornado==4.5.2
# urllib3==1.22
# vine==1.1.4

@ -1,13 +1,9 @@
import random
import string
from storage.models import Comment, File from storage.models import Comment, File
def upload_file(original=None, name=None, base64=None) -> File: def upload_file(original=None, name=None, base64=None, **_kwargs) -> File:
key = ''.join(random.choice(string.ascii_letters) for _x in range(15))
if original: if original:
new_file = File.objects.create(key=key, original=original) new_file = File.objects.create(original=original)
else: else:
new_file = File.objects.upload_as_base64(base64) new_file = File.objects.upload_as_base64(base64)
@ -17,21 +13,19 @@ def upload_file(original=None, name=None, base64=None) -> File:
return new_file return new_file
def add_comment(text: str, email: str, files=None) -> Comment: def add_comment(text: str, out_key: str, files=None) -> Comment:
""" """
:param text: sting :param text: sting
:param email: string :param out_key: string
:param files: {name?: string, original?: File, base64?: string}[] одно из двух последних свойств должно быть указано :param files: {name?: string, original?: File, base64?: string}[] одно из двух последних свойств должно быть указано
:return: Comment :return: Comment
""" """
files = [] if files is None else files files = [] if files is None else files
key = ''.join(random.choice(string.ascii_letters) for _x in range(15))
comment = Comment.objects.create( comment = Comment.objects.create(
text=text, text=text,
email=email, user_key=out_key,
key=key,
) )
for file in files: for file in files:
@ -42,17 +36,17 @@ def add_comment(text: str, email: str, files=None) -> Comment:
def get_comment(key): def get_comment(key):
comment = Comment.objects.get(key=key) comment = Comment.objects.get(token=key)
return comment return comment
def update_comment(key, **kwargs): def update_comment(key, **kwargs):
comment = Comment.objects.get(key=key) comment = Comment.objects.get(token=key)
comment.__dict__.update(kwargs) comment.__dict__.update(kwargs)
comment.save() comment.save()
return comment return comment
def delete_comment(key): def delete_comment(key):
comment = Comment.objects.get(key=key).delete() comment = Comment.objects.get(token=key).delete()
return comment return comment

@ -1,8 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-17 18:03 # Generated by Django 1.11.6 on 2018-01-22 09:27
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import uuid
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -17,9 +18,9 @@ class Migration(migrations.Migration):
name='Comment', name='Comment',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('email', models.CharField(max_length=63, verbose_name='email автора')), ('user_key', models.UUIDField(editable=False, verbose_name='Ссылка на юзера')),
('text', models.TextField(default='', verbose_name='Текст комментария')), ('text', models.TextField(default='', verbose_name='Текст комментария')),
('key', models.SlugField(editable=False, unique=True, verbose_name='Получения комментария по ключу')), ('token', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='Ключ')),
('date', models.DateTimeField(auto_now_add=True, verbose_name='Дата коментария')), ('date', models.DateTimeField(auto_now_add=True, verbose_name='Дата коментария')),
], ],
options={ options={
@ -31,7 +32,7 @@ class Migration(migrations.Migration):
name='File', name='File',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('key', models.CharField(editable=False, max_length=15, unique=True, verbose_name='Внешний ключ')), ('token', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='Ключ')),
('original', models.FileField(max_length=255, unique=True, upload_to='files', verbose_name='Файл')), ('original', models.FileField(max_length=255, unique=True, upload_to='files', verbose_name='Файл')),
('name', models.CharField(blank=True, max_length=255, null=True, verbose_name='Видимое имя файла')), ('name', models.CharField(blank=True, max_length=255, null=True, verbose_name='Видимое имя файла')),
], ],

@ -1,7 +1,6 @@
# encoding=utf-8 # encoding=utf-8
import base64 import base64
import random import uuid
import string
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from django.db import models from django.db import models
@ -10,17 +9,16 @@ from django.db import models
class FileManager(models.Manager): class FileManager(models.Manager):
def upload_as_base64(self, file_base64): def upload_as_base64(self, file_base64):
key = ''.join(random.choice(string.ascii_letters) for _x in range(15))
if "data:" in file_base64: if "data:" in file_base64:
my_str = file_base64[file_base64.index("base64,") + 7:] my_str = file_base64[file_base64.index("base64,") + 7:]
ext = my_str.split('/')[-1] ext = my_str.split('/')[-1]
file = self.create(key, original=ContentFile(base64.b64decode(my_str), name='time.' + ext)) file = self.create(original=ContentFile(base64.b64decode(my_str), name='time.' + ext))
return file return file
raise ValueError() raise ValueError()
class File(models.Model): class File(models.Model):
key = models.CharField(max_length=15, verbose_name="Внешний ключ", unique=True, editable=False) token = models.UUIDField(verbose_name="Ключ", default=uuid.uuid4, editable=False)
original = models.FileField(max_length=255, verbose_name='Файл', upload_to="files", unique=True) original = models.FileField(max_length=255, verbose_name='Файл', upload_to="files", unique=True)
name = models.CharField(max_length=255, null=True, blank=True, verbose_name='Видимое имя файла') name = models.CharField(max_length=255, null=True, blank=True, verbose_name='Видимое имя файла')
@ -35,14 +33,14 @@ class File(models.Model):
class Comment(models.Model): class Comment(models.Model):
email = models.CharField(verbose_name="email автора", max_length=63) user_key = models.UUIDField(verbose_name="Ссылка на юзера", editable=False)
text = models.TextField(default="", verbose_name="Текст комментария") text = models.TextField(default="", verbose_name="Текст комментария")
files = models.ManyToManyField(to=File, blank=True, verbose_name='Файлы') files = models.ManyToManyField(to=File, blank=True, verbose_name='Файлы')
key = models.SlugField(unique=True, verbose_name="Получения комментария по ключу", editable=False) token = models.UUIDField(verbose_name="Ключ", default=uuid.uuid4, editable=False)
date = models.DateTimeField(auto_now_add=True, verbose_name="Дата коментария") date = models.DateTimeField(auto_now_add=True, verbose_name="Дата коментария")
def __str__(self): def __str__(self):
return '%s' % self.key return '%s' % self.token
class Meta: class Meta:
verbose_name = 'Коммент' verbose_name = 'Коммент'

@ -21,7 +21,7 @@ class CommentSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Comment model = Comment
exclude = ('id', 'key',) exclude = ('id', 'token',)
@staticmethod @staticmethod
def get_files(self): def get_files(self):

@ -1,4 +1,4 @@
import tempfile import uuid
from django.test import TestCase from django.test import TestCase
from storage.api import add_comment, delete_comment, update_comment, get_comment from storage.api import add_comment, delete_comment, update_comment, get_comment
@ -10,36 +10,38 @@ from storage.models import Comment
class CommentTestCase(TestCase): class CommentTestCase(TestCase):
def setUp(self): def setUp(self):
self.first_comment = add_comment("first comment", "vasia@rambler.ru") self.f_k = uuid.uuid4()
self.second_comment = add_comment(text="Привет, отличная работа", email="artem4000@gmail.com") self.s_k = uuid.uuid4()
self.first_comment = add_comment("first comment", self.f_k)
self.second_comment = add_comment(text="Привет, отличная работа", out_key=self.s_k)
def tearDown(self): def tearDown(self):
Comment.objects.all().delete() Comment.objects.all().delete()
def test_comment_get(self): def test_comment_get(self):
self.assertEqual(self.first_comment, get_comment(self.first_comment.key)) self.assertEqual(self.first_comment, get_comment(self.first_comment.token))
def test_comment_update(self): def test_comment_update(self):
new_text = "Новый текст для коммента" new_text = "Новый текст для коммента"
update_comment(key=self.first_comment.key, text=new_text) update_comment(key=self.first_comment.token, text=new_text)
self.assertEqual(get_comment(self.first_comment.key).text, new_text) self.assertEqual(get_comment(self.first_comment.token).text, new_text)
def test_comment_create(self): # def test_comment_create(self):
token = 'fskjfskj' # text = 'fskjfskj'
comment1 = add_comment(text=token, email="artem4000@gmail.com") # comment1 = add_comment(text=text, out_key=uuid.uuid4())
self.assertEqual(comment1.text, token) # self.assertEqual(comment1.text, text)
with tempfile.gettempdir() as dir_path: # with tempfile.gettempdir() as dir_path:
file_for_upload = SimpleUploadedFile(dir_path + '/1.txt', 'Я файл!'.encode('utf-8')) # file_for_upload = SimpleUploadedFile(dir_path + '/1.txt', 'Я файл!'.encode('utf-8'))
file_name = 'Клёвый файл' # file_name = 'Клёвый файл'
object_for_upload = {'original': file_for_upload, 'name': file_name} # object_for_upload = {'original': file_for_upload, 'name': file_name}
comment2 = add_comment(text=token, email="artem4000@gmail.com", files=[object_for_upload]) # comment2 = add_comment(text=token, email="artem4000@gmail.com", files=[object_for_upload])
self.assertEqual(comment2.files.count(), 1) # self.assertEqual(comment2.files.count(), 1)
self.assertEqual(comment2.files.all()[0].name, file_name) # self.assertEqual(comment2.files.all()[0].name, file_name)
def test_comment_delete(self): def test_comment_delete(self):
delete_comment(self.first_comment.key) delete_comment(self.first_comment.token)
try: try:
comment = get_comment(self.first_comment.id) comment = get_comment(self.first_comment.token)
except Comment.DoesNotExist: except Comment.DoesNotExist:
comment = None comment = None
@ -48,32 +50,32 @@ class CommentTestCase(TestCase):
class FileTestCase(TestCase): class FileTestCase(TestCase):
def setUp(self): def setUp(self):
self.first_comment = add_comment("first comment", "vasia@rambler.ru") self.first_comment = add_comment("first comment", uuid.uuid4())
self.second_comment = add_comment(text="Привет, отличная работа", email="artem4000@gmail.com") self.second_comment = add_comment(text="Привет, отличная работа", out_key=uuid.uuid4())
def test_comment_get(self): def test_comment_get(self):
self.assertEqual(self.first_comment, get_comment(self.first_comment.key)) self.assertEqual(self.first_comment, get_comment(self.first_comment.token))
def test_comment_update(self): def test_comment_update(self):
new_text = "Новый текст для коммента" new_text = "Новый текст для коммента"
update_comment(key=self.first_comment.key, text=new_text) update_comment(key=self.first_comment.token, text=new_text)
self.assertEqual(get_comment(self.first_comment.key).text, new_text) self.assertEqual(get_comment(self.first_comment.token).text, new_text)
def test_comment_create(self): def test_comment_create(self):
token = 'fskjfskj' token = 'fskjfskj'
comment1 = add_comment(text=token, email="artem4000@gmail.com") comment1 = add_comment(text=token, out_key=uuid.uuid4())
self.assertEqual(comment1.text, token) self.assertEqual(comment1.text, token)
file_for_upload = SimpleUploadedFile('1.txt', 'Я файл!'.encode('utf-8')) file_for_upload = SimpleUploadedFile('1.txt', 'Я файл!'.encode('utf-8'))
file_name = 'Клёвый файл' file_name = 'Клёвый файл'
object_for_upload = {'original': file_for_upload, 'name': file_name} object_for_upload = {'original': file_for_upload, 'name': file_name}
comment2 = add_comment(text=token, email="artem4000@gmail.com", files=[object_for_upload]) comment2 = add_comment(text=token, out_key=uuid.uuid4(), files=[object_for_upload])
self.assertEqual(comment2.files.count(), 1) self.assertEqual(comment2.files.count(), 1)
self.assertEqual(comment2.files.all()[0].name, file_name) self.assertEqual(comment2.files.all()[0].name, file_name)
def test_comment_delete(self): def test_comment_delete(self):
delete_comment(self.first_comment.key) delete_comment(self.first_comment.token)
try: try:
comment = get_comment(self.first_comment.id) comment = get_comment(self.first_comment.token)
except Comment.DoesNotExist: except Comment.DoesNotExist:
comment = None comment = None

@ -2,6 +2,7 @@ from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from storage.api import add_comment
from storage.models import File, Comment from storage.models import File, Comment
from storage.serializers import FileSerializer, CommentSerializer from storage.serializers import FileSerializer, CommentSerializer
@ -19,7 +20,7 @@ class FileView(APIView):
for token in tokens: for token in tokens:
try: try:
file_list.append(FileSerializer(File.objects.get(key=token)).data) file_list.append(FileSerializer(File.objects.get(token=token)).data)
except File.DoesNotExist: except File.DoesNotExist:
file_list.append({'upload': 'error'}) file_list.append({'upload': 'error'})
@ -29,6 +30,20 @@ class FileView(APIView):
class CommentView(APIView): class CommentView(APIView):
renderer_classes = (JSONRenderer,) renderer_classes = (JSONRenderer,)
@staticmethod
def post(request):
text = request.JSON.get('text', None)
user_key = request.JSON.get('user_token', None)
files = request.JSON.get('files', [])
if user_key:
print(user_key)
comment = add_comment(text=text, out_key=user_key, files=files)
print(comment)
return Response(CommentSerializer(comment).data, status=200)
return Response("user_token mast be set", status=400)
@staticmethod @staticmethod
def get(request): def get(request):
tokens = request.GET.getlist('tokens', None) tokens = request.GET.getlist('tokens', None)
@ -40,7 +55,7 @@ class CommentView(APIView):
for token in tokens: for token in tokens:
try: try:
comment_list.append(CommentSerializer(Comment.objects.get(key=token)).data) comment_list.append(CommentSerializer(Comment.objects.get(token=token)).data)
except Comment.DoesNotExist: except Comment.DoesNotExist:
comment_list.append({'upload': 'error'}) comment_list.append({'upload': 'error'})

Loading…
Cancel
Save