feature/fix_generate_pass
Andrey 8 years ago
parent c1d79b5bb3
commit d356aed11b
  1. 35
      access/migrations/0001_initial.py
  2. 76
      access/migrations/0002_auto_20171213_1230.py
  3. 28
      access/models/other.py
  4. 4
      access/models/user.py
  5. 3
      access/serializers.py
  6. 10
      access/views.py
  7. 2
      achievements/migrations/0001_initial.py
  8. 4
      achievements/migrations/0002_auto_20171213_2300.py
  9. 4
      achievements/models.py
  10. 2
      api_v1/urls.py
  11. 2
      config_app/settings/test.env
  12. 0
      course_service/__init__.py
  13. 1
      course_service/courses/__init__.py
  14. 7
      course_service/courses/admin.py
  15. 21
      course_service/courses/api.py
  16. 5
      course_service/courses/apps.py
  17. 13
      course_service/courses/migrations/0001_initial.py
  18. 11
      course_service/courses/migrations/0002_course_route.py
  19. 25
      course_service/courses/migrations/0003_auto_20171214_1513.py
  20. 0
      course_service/courses/migrations/__init__.py
  21. 71
      course_service/courses/models.py
  22. 2
      course_service/courses/serializers.py
  23. 2
      course_service/courses/signals.py
  24. 51
      course_service/courses/tsets.py
  25. 3
      course_service/courses/urls.py
  26. 8
      course_service/courses/views.py
  27. 0
      course_service/maps/__init__.py
  28. 2
      course_service/maps/admin.py
  29. 7
      course_service/maps/api.py
  30. 3
      course_service/maps/apps.py
  31. 0
      course_service/maps/exeptions.py
  32. 7
      course_service/maps/migrations/0001_initial.py
  33. 0
      course_service/maps/migrations/__init__.py
  34. 6
      course_service/maps/models.py
  35. 2
      course_service/maps/serializers.py
  36. 23
      course_service/maps/tests.py
  37. 0
      course_service/maps/views.py
  38. 1
      courses/__init__.py
  39. 7
      courses/admin.py
  40. 7
      csv/load_bills.py
  41. 56
      csv/load_courses.py
  42. 7
      csv/load_perm.py
  43. 18
      csv/load_storage.py
  44. 7
      csv/load_student_teachers_threads.py
  45. 5
      finance/migrations/0001_initial.py
  46. 29
      finance/migrations/0002_auto_20171214_1405.py
  47. 26
      finance/migrations/0003_auto_20171214_1406.py
  48. 10
      finance/models.py
  49. 3
      finance/signals.py
  50. 0
      finance/tests.py
  51. 20
      finance/views.py
  52. 2
      library/migrations/0001_initial.py
  53. 2
      lms/global_decorators.py
  54. 4
      lms/settings.py
  55. 59
      lms/tools.py
  56. 3
      maps/tests.py
  57. 5
      storage/api.py
  58. 2
      storage/migrations/0001_initial.py
  59. 34
      storage/tests.py

@ -1,8 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-13 12:30 # Generated by Django 1.11.6 on 2017-12-13 23:00
from __future__ import unicode_literals from __future__ import unicode_literals
import access.models.user import access.models.user
from django.conf import settings
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
@ -13,6 +14,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('auth', '0008_alter_user_username_max_length'),
] ]
operations = [ operations = [
@ -23,13 +25,16 @@ 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='Ключ для внешних сервисов')),
('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='Guest', 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')),
('date_joined', models.DateTimeField(verbose_name='date joined')), ('date_joined', models.DateTimeField(verbose_name='date joined')),
('is_staff', models.BooleanField(default=False, help_text='Определяет разрешение пользователя на вход в административную часть.', verbose_name='флаг персонала')), ('is_staff', models.BooleanField(default=False, help_text='Определяет разрешение пользователя на вход в административную часть.', verbose_name='флаг персонала')),
('is_active', models.BooleanField(default=False, help_text='Определяет активен ли пользователь в системе. Снимите флаг, вместо удаления пользователя.', verbose_name='активен')), ('is_active', models.BooleanField(default=False, help_text='Определяет активен ли пользователь в системе. Снимите флаг, вместо удаления пользователя.', verbose_name='активен')),
('is_blocked', models.BooleanField(default=False, help_text='Определяет заблокирован ли пользователь. Поставьте флаг, если знаете, что это нехороший человек.', verbose_name='заблочен')), ('is_blocked', models.BooleanField(default=False, help_text='Определяет заблокирован ли пользователь. Поставьте флаг, если знаете, что это нехороший человек.', verbose_name='заблочен')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
], ],
options={ options={
'verbose_name': 'Пользователь', 'verbose_name': 'Пользователь',
@ -48,6 +53,7 @@ class Migration(migrations.Migration):
('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(blank=True, default='/static/default/access/default.png', null=True, upload_to='user/photo/')), ('photo', models.ImageField(blank=True, default='/static/default/access/default.png', null=True, upload_to='user/photo/')),
('phone', models.CharField(blank=True, max_length=15, null=True)), ('phone', models.CharField(blank=True, max_length=15, null=True)),
('owner', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
], ],
options={ options={
'verbose_name': 'Дополнительная информация о пользователе', 'verbose_name': 'Дополнительная информация о пользователе',
@ -70,14 +76,18 @@ class Migration(migrations.Migration):
name='PivotProgressVertex', name='PivotProgressVertex',
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')),
('vertex', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=15, unique=True, verbose_name='Ссылки на узлы'), size=None)),
('status', models.SmallIntegerField(choices=[(2, 'Выполненно'), (1, 'Ожидание'), (0, 'Не выполненно')], default=0)), ('status', models.SmallIntegerField(choices=[(2, 'Выполненно'), (1, 'Ожидание'), (0, 'Не выполненно')], default=0)),
('comment', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=15, verbose_name='Ссылки на комменты'), size=None)), ('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')),
('template', models.CharField(max_length=15, 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(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Студент')),
], ],
options={ options={
'verbose_name': 'Прогресс пользователя', 'verbose_name': 'Прогресс пользователя',
@ -96,4 +106,23 @@ 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(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Преподователь по умолчанию'),
),
migrations.AddField(
model_name='invite',
name='owner',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Кому приглошение'),
),
migrations.AlterUniqueTogether(
name='progress',
unique_together=set([('user', 'template')]),
),
] ]

@ -1,76 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-13 12:30
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('access', '0001_initial'),
('maps', '0001_initial'),
('auth', '0008_alter_user_username_max_length'),
('courses', '0002_auto_20171213_1230'),
]
operations = [
migrations.AddField(
model_name='progress',
name='course',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='courses.Course', verbose_name='Курс'),
),
migrations.AddField(
model_name='progress',
name='teacher',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='teacher_progress', to=settings.AUTH_USER_MODEL, verbose_name='Преподователь по умолчанию'),
),
migrations.AddField(
model_name='progress',
name='template',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='maps.CourseRoute', verbose_name='Шаблон для прохождения если не указан явно смотри функцию get_template()'),
),
migrations.AddField(
model_name='progress',
name='user',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Студент'),
),
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='vertex',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Vertex'),
),
migrations.AddField(
model_name='invite',
name='owner',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Кому приглошение'),
),
migrations.AddField(
model_name='account',
name='owner',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='user',
name='groups',
field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups'),
),
migrations.AddField(
model_name='user',
name='user_permissions',
field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'),
),
migrations.AlterUniqueTogether(
name='progress',
unique_together=set([('user', 'course')]),
),
]

@ -1,13 +1,11 @@
from django.conf import settings from django.conf import settings
from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.fields import ArrayField
from django.db import models from django.db import models
from courses.models import Course, Vertex
from lms.global_decorators import transaction_decorator
from maps.models import CourseRoute
from django.db.models import Q from django.db.models import Q
from storage.models import Comment from course_service.courses.models import Course, Vertex
from course_service.maps.models import CourseRoute
from lms.global_decorators import transaction_decorator
class Invite(models.Model): class Invite(models.Model):
@ -53,16 +51,10 @@ class Progress(models.Model):
teacher = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name="Преподователь по умолчанию", teacher = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name="Преподователь по умолчанию",
related_name='teacher_progress') related_name='teacher_progress')
user = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name='Студент', null=True) user = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name='Студент', null=True)
course = models.ForeignKey(to=Course, verbose_name='Курс', null=True) template = models.CharField(max_length=15, verbose_name='Токен прохождения')
template = models.OneToOneField(to=CourseRoute, blank=True, null=True, verbose_name='Шаблон для прохождения если '
'не указан явно смотри '
'функцию get_template()')
def __str__(self): def __str__(self):
return '%s %s' % ( return '%s' % (self.user.email,)
self.user.email,
self.course.title,
)
@transaction_decorator @transaction_decorator
def add_vertex(self, vertex): def add_vertex(self, vertex):
@ -94,7 +86,7 @@ class Progress(models.Model):
class Meta: class Meta:
verbose_name = 'Прогресс пользователя' verbose_name = 'Прогресс пользователя'
verbose_name_plural = 'Прогресс пользователя' verbose_name_plural = 'Прогресс пользователя'
unique_together = (("user", "course"),) unique_together = (("user", "template"),)
class PivotProgressVertex(models.Model): class PivotProgressVertex(models.Model):
@ -103,9 +95,9 @@ class PivotProgressVertex(models.Model):
(1, 'Ожидание'), (1, 'Ожидание'),
(0, 'Не выполненно'), (0, 'Не выполненно'),
) )
teacher = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name="Преподователь по умолчанию",
blank=True, null=True)
progress = models.ForeignKey(to=Progress) progress = models.ForeignKey(to=Progress)
vertex = models.ForeignKey(to=Vertex) vertex = ArrayField(models.CharField(max_length=15, blank=True, verbose_name='Ссылки на узлы', unique=True),)
status = models.SmallIntegerField(choices=VERTEX_STATUS, default=0) status = models.SmallIntegerField(choices=VERTEX_STATUS, default=0)
comment = ArrayField( comment = ArrayField(models.CharField(max_length=15, blank=True, verbose_name='Ссылки на комменты', unique=True),)
models.CharField(max_length=15, blank=True, verbose_name='Ссылки на комменты'),
)

@ -44,6 +44,7 @@ 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:
@ -92,8 +93,9 @@ class CustomUserManager(BaseUserManager):
class User(AbstractBaseUser, PermissionsMixin): class User(AbstractBaseUser, PermissionsMixin):
out_key = models.CharField(max_length=15, unique=True, verbose_name="Ключ для внешних сервисов", 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='Guest') 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)
date_joined = models.DateTimeField(_('date joined')) date_joined = models.DateTimeField(_('date joined'))
is_staff = models.BooleanField(verbose_name='флаг персонала', default=False, is_staff = models.BooleanField(verbose_name='флаг персонала', default=False,

@ -1,8 +1,9 @@
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from rest_framework import serializers from rest_framework import serializers
from access.models.other import Account, Progress from access.models.other import Account, Progress
from achievements.serialers import DiplomaSerializer, AchievementsSerializer from achievements.serialers import DiplomaSerializer, AchievementsSerializer
from courses.serializers import MiniVertexSerializer, CourseInitSerializer from course_service.courses.serializers import MiniVertexSerializer, CourseInitSerializer
class ProgressSerializer(serializers.ModelSerializer): class ProgressSerializer(serializers.ModelSerializer):

@ -1,20 +1,20 @@
import datetime
import random import random
import string import string
import datetime
from django.contrib.auth import get_user_model
from django.contrib import auth from django.contrib import auth
from django.contrib.auth import get_user_model
from django.core.mail import send_mail from django.core.mail import send_mail
from django.db.models import Q
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.views import APIView
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 django.db.models import Q from rest_framework.views import APIView
from access.models.other import Invite, Progress, ResetPassword from access.models.other import Invite, Progress, ResetPassword
from access.serializers import UserSelfSerializer, UserSearchSerializer from access.serializers import UserSelfSerializer, UserSearchSerializer
from courses.models import Vertex from course_service.courses.models import Vertex
class TeacherListView(APIView): class TeacherListView(APIView):

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-13 12:30 # Generated by Django 1.11.6 on 2017-12-13 23:00
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-13 12:30 # Generated by Django 1.11.6 on 2017-12-13 23:00
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 = [
('courses', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('courses', '0001_initial'),
('achievements', '0001_initial'), ('achievements', '0001_initial'),
] ]

@ -1,7 +1,7 @@
from django.db import models
from django.conf import settings from django.conf import settings
from django.db import models
from courses.models import Course, Vertex from course_service.courses.models import Course, Vertex
class Achievements(models.Model): class Achievements(models.Model):

@ -1,7 +1,7 @@
from django.conf.urls import url, include from django.conf.urls import url, include
urlpatterns = [ urlpatterns = [
url(r'courses/', include('courses.urls')), url(r'courses/', include('course_service.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,5 +1,5 @@
DEBUG=True DEBUG=True
SECRET_KEY='!eiquy7_+2#vn3z%zfp51$m-=tmvtcv*cj*@x$!v(_9btq0w=$' SECRET_KEY='!eiquy7_+2#vn3z%zfp51$m-=tmvtcv*cj*@x$!v(_9btq0w=$'
DATABASE_URL='psql://team:nu5Xefise@127.0.0.1:5432/new_lms' DATABASE_URL='sqlite:///None'
EMAIL_URL='smtp+tls://9ae31a1a770138:a7d79ee373a14c@smtp.mailtrap.io:2525' EMAIL_URL='smtp+tls://9ae31a1a770138:a7d79ee373a14c@smtp.mailtrap.io:2525'
CACHE_URL=rediscache://127.0.0.1:6379/1?client_class=django_redis.client.DefaultClient CACHE_URL=rediscache://127.0.0.1:6379/1?client_class=django_redis.client.DefaultClient

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

@ -0,0 +1,7 @@
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)

@ -0,0 +1,21 @@
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

@ -3,8 +3,9 @@ from django.apps import AppConfig
class CoursesAppConfig(AppConfig): class CoursesAppConfig(AppConfig):
name = "courses" name = "course_service.courses"
label = 'courses'
verbose_name = "Курсы" verbose_name = "Курсы"
def ready(self): def ready(self):
import courses.signals pass

@ -1,7 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-13 12:30 # Generated by Django 1.11.6 on 2017-12-13 23:00
from __future__ import unicode_literals from __future__ import unicode_literals
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
@ -11,7 +12,6 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('storage', '0001_initial'),
] ]
operations = [ operations = [
@ -23,13 +23,13 @@ class Migration(migrations.Migration):
('level', models.CharField(choices=[('B', 'Базовый'), ('A', 'Продвинутый'), ('E', 'Экспертный'), ('B+A', 'Базовый + Продвинутый')], default='B', max_length=3, verbose_name='Уровень')), ('level', models.CharField(choices=[('B', 'Базовый'), ('A', 'Продвинутый'), ('E', 'Экспертный'), ('B+A', 'Базовый + Продвинутый')], default='B', max_length=3, verbose_name='Уровень')),
('slug', models.SlugField(blank=True, default='', editable=False, max_length=255, unique=True)), ('slug', models.SlugField(blank=True, default='', editable=False, max_length=255, unique=True)),
('direction', models.SmallIntegerField(choices=[(3, 'Бизнес'), (2, 'Веб-дизайн'), (1, 'Разработка'), (4, 'Рисование')], null=True, verbose_name='Направление')), ('direction', models.SmallIntegerField(choices=[(3, 'Бизнес'), (2, 'Веб-дизайн'), (1, 'Разработка'), (4, 'Рисование')], null=True, verbose_name='Направление')),
('sort', models.SmallIntegerField(null=True, verbose_name='Порядок сортировки')),
('public', models.BooleanField(default=False, verbose_name='Опубликовать')), ('public', models.BooleanField(default=False, verbose_name='Опубликовать')),
('title', models.CharField(max_length=255, verbose_name='Заголовок')), ('title', models.CharField(max_length=255, verbose_name='Заголовок')),
('description', models.TextField(blank=True, verbose_name='Описание')), ('description', models.TextField(blank=True, 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, null=True, verbose_name='Под мобилку')), ('big_mobile_image', models.URLField(blank=True, help_text='Большая картинка для мобильной версии', max_length=255, null=True, verbose_name='Под мобилку')),
('teachers', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=15, verbose_name='Ссылки на преподов'), default=[], size=None)),
], ],
options={ options={
'verbose_name': 'Курс', 'verbose_name': 'Курс',
@ -42,7 +42,7 @@ class Migration(migrations.Migration):
('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='Название')),
('icon', models.ImageField(blank=True, null=True, upload_to='', verbose_name='Иконка темы')), ('icon', models.ImageField(blank=True, null=True, upload_to='', verbose_name='Иконка темы')),
('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Course')), ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Course', verbose_name='курс')),
], ],
options={ options={
'verbose_name': 'Тема', 'verbose_name': 'Тема',
@ -57,9 +57,10 @@ class Migration(migrations.Migration):
('free', models.BooleanField(default=True, verbose_name='Привилегии для узла не будут проверяться')), ('free', models.BooleanField(default=True, verbose_name='Привилегии для узла не будут проверяться')),
('description', models.TextField(blank=True, default='', null=True, verbose_name='Описание')), ('description', models.TextField(blank=True, default='', null=True, verbose_name='Описание')),
('video', models.TextField(blank=True, default='', null=True, verbose_name='Код видео')), ('video', models.TextField(blank=True, default='', null=True, verbose_name='Код видео')),
('materials', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=15, verbose_name='Материалы урока'), size=None)),
('valid_type', models.SmallIntegerField(choices=[(3, 'Автаматическая валидация'), (2, 'Полуавтаматическая валидация'), (1, 'Ручная валидация'), (0, 'Без валидации')], default=0)), ('valid_type', models.SmallIntegerField(choices=[(3, 'Автаматическая валидация'), (2, 'Полуавтаматическая валидация'), (1, 'Ручная валидация'), (0, 'Без валидации')], default=0)),
('materials', models.ManyToManyField(blank=True, to='storage.File', verbose_name='Материалы урока')), ('token', models.CharField(max_length=15, unique=True, verbose_name='Ключ доступа к узлу')),
('topic', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Topic')), ('topic', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Topic', verbose_name='Тема')),
], ],
options={ options={
'verbose_name': 'Урок', 'verbose_name': 'Урок',

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-13 12:30 # Generated by Django 1.11.6 on 2017-12-13 23:00
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 import django.db.models.deletion
@ -12,9 +11,8 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('maps', '0001_initial'),
('courses', '0001_initial'), ('courses', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL), ('maps', '0001_initial'),
] ]
operations = [ operations = [
@ -23,9 +21,4 @@ class Migration(migrations.Migration):
name='route', name='route',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='maps.CourseRoute', verbose_name='Порядок прохождения по умолчанию'), field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='maps.CourseRoute', verbose_name='Порядок прохождения по умолчанию'),
), ),
migrations.AddField(
model_name='course',
name='teachers',
field=models.ManyToManyField(related_name='course_teachers', to=settings.AUTH_USER_MODEL, verbose_name='Преподаватели'),
),
] ]

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-14 15:13
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('courses', '0002_course_route'),
]
operations = [
migrations.AddField(
model_name='topic',
name='description',
field=models.TextField(blank=True, null=True, verbose_name='Описание'),
),
migrations.AlterField(
model_name='vertex',
name='description',
field=models.TextField(blank=True, null=True, verbose_name='Описание'),
),
]

@ -1,20 +1,16 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.conf import settings import random
from django.contrib.auth import get_user_model import string
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.core.exceptions import ObjectDoesNotExist
import json
import unidecode import unidecode
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.template.defaultfilters import slugify from django.template.defaultfilters import slugify
from maps.models import CourseRoute, CourseMap
from lms.tools import decode_base64, get_real_name from course_service.maps.models import CourseRoute, CourseMap
from lms.global_decorators import transaction_decorator from lms.global_decorators import transaction_decorator
from library.models import Tags from lms.tools import decode_base64, get_real_name
from storage.models import File
import random
COURSE_LEVEL = ( COURSE_LEVEL = (
('B', 'Базовый'), ('B', 'Базовый'),
@ -32,10 +28,10 @@ COURSE_DIRECTION = (
class CourseManager(models.Manager): class CourseManager(models.Manager):
@transaction_decorator
def update_or_create_course(self, image=None, big_image=None, id=0, route=None, def update_or_create_course(self, image=None, big_image=None, id=0,
big_mobile_image=None, mentors=None, slug=None, big_mobile_image=None, slug=None,
teachers=None, level=None, direction=None, **kwargs): level=None, direction=None, **kwargs):
slug = slug if slug else slugify(unidecode.unidecode(kwargs['title'])) slug = slug if slug else slugify(unidecode.unidecode(kwargs['title']))
@ -54,29 +50,30 @@ class CourseManager(models.Manager):
if direction: if direction:
kwargs['direction'] = get_real_name(COURSE_DIRECTION, direction) kwargs['direction'] = get_real_name(COURSE_DIRECTION, direction)
if route:
kwargs['route'] = CourseRoute.objects.get(id=route)
try: try:
course = self.get(id=id) course = self.get(id=id)
for i in kwargs: for i in kwargs:
if kwargs[i]: if kwargs[i]:
setattr(course, i, kwargs[i]) setattr(course, i, kwargs[i])
course.save() course.save()
except ObjectDoesNotExist: except ObjectDoesNotExist:
kwargs['slug'] = slug kwargs['slug'] = slug
kwargs['route'] = CourseRoute.objects.create(name="Основной шаблон") kwargs['route'] = CourseRoute.objects.create(
name=kwargs['title'],
is_template=True,
out_key=''.join(random.choice(string.ascii_letters) for x in range(15)),
)
course = self.create(**kwargs) course = self.create(**kwargs)
CourseMap.objects.create(course=course) course.save()
if mentors:
for email in mentors:
course.mentors.add(get_user_model().objects.get(email=email))
if teachers: return course
for email in teachers:
course.teachers.add(get_user_model().objects.get(email=email))
def change_route(self, pk, route):
route = CourseRoute.objects.get(id=route)
course = self.get(id=pk)
course.route = route
course.save()
return course return course
@ -85,7 +82,6 @@ class Course(models.Model):
level = models.CharField(verbose_name='Уровень', choices=COURSE_LEVEL, default='B', max_length=3) level = models.CharField(verbose_name='Уровень', choices=COURSE_LEVEL, default='B', max_length=3)
slug = models.SlugField(max_length=255, blank=True, default='', unique=True, editable=False) slug = models.SlugField(max_length=255, blank=True, default='', unique=True, editable=False)
direction = models.SmallIntegerField(choices=COURSE_DIRECTION, verbose_name='Направление', null=True) direction = models.SmallIntegerField(choices=COURSE_DIRECTION, verbose_name='Направление', null=True)
sort = models.SmallIntegerField(null=True, verbose_name="Порядок сортировки")
public = models.BooleanField(verbose_name='Опубликовать', default=False) public = models.BooleanField(verbose_name='Опубликовать', default=False)
title = models.CharField(verbose_name="Заголовок", max_length=255) title = models.CharField(verbose_name="Заголовок", max_length=255)
description = models.TextField(verbose_name='Описание', blank=True) description = models.TextField(verbose_name='Описание', blank=True)
@ -93,16 +89,13 @@ class Course(models.Model):
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, null=True, big_mobile_image = models.URLField(verbose_name='Под мобилку', blank=True, null=True,
help_text='Большая картинка для мобильной версии', max_length=255) help_text='Большая картинка для мобильной версии', max_length=255)
teachers = models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name='Преподаватели', teachers = ArrayField(
related_name='course_teachers') models.CharField(max_length=15, blank=True, verbose_name='Ссылки на преподов'), default=[])
route = models.OneToOneField(to=CourseRoute, verbose_name="Порядок прохождения по умолчанию", blank=True, null=True) 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_teacher(self):
return random.choice(self.teachers.all())
def get_maps(self, user): def get_maps(self, user):
return user.progress_set.get(course=self).get_template().get_maps() return user.progress_set.get(course=self).get_template().get_maps()
@ -166,7 +159,8 @@ class Course(models.Model):
class Topic(models.Model): class Topic(models.Model):
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) icon = models.ImageField(verbose_name='Иконка темы', null=True, blank=True)
course = models.ForeignKey(to=Course) description = models.TextField(verbose_name='Описание', blank=True, null=True)
course = models.ForeignKey(to=Course, verbose_name='курс')
class Meta: class Meta:
verbose_name = "Тема" verbose_name = "Тема"
@ -180,13 +174,14 @@ class Vertex(models.Model):
(1, 'Ручная валидация'), (1, 'Ручная валидация'),
(0, 'Без валидации'), (0, 'Без валидации'),
) )
topic = models.ForeignKey(to=Topic) 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=True, verbose_name='Привилегии для узла не будут проверяться') free = models.BooleanField(default=True, verbose_name='Привилегии для узла не будут проверяться')
description = models.TextField(verbose_name='Описание', default='', blank=True, null=True) description = models.TextField(verbose_name='Описание', blank=True, null=True)
video = models.TextField(verbose_name='Код видео', default='', blank=True, null=True) video = models.TextField(verbose_name='Код видео', default='', blank=True, null=True)
materials = models.ManyToManyField(File, verbose_name='Материалы урока', blank=True) materials = ArrayField(models.CharField(max_length=15, blank=True, verbose_name='Материалы урока'))
valid_type = models.SmallIntegerField(choices=VALID_TYPE, default=0) valid_type = models.SmallIntegerField(choices=VALID_TYPE, default=0)
token = models.CharField(max_length=15, verbose_name="Ключ доступа к узлу", unique=True)
def __str__(self): def __str__(self):
return self.title return self.title

@ -1,6 +1,6 @@
from rest_framework import serializers from rest_framework import serializers
from courses.models import Course, Vertex, Topic from course_service.courses.models import Course, Vertex, Topic
class TopicSerializer(serializers.ModelSerializer): class TopicSerializer(serializers.ModelSerializer):

@ -1,7 +1,7 @@
from django.db.models.signals import pre_delete from django.db.models.signals import pre_delete
from django.dispatch import receiver from django.dispatch import receiver
from courses.models import Vertex from course_service.courses.models import Vertex
@receiver(pre_delete, sender=Vertex) @receiver(pre_delete, sender=Vertex)

@ -0,0 +1,51 @@
from django.test import TestCase
from course_service.courses.models import Course
from lms.tools import EXAMPLE_BASE64
from course_service.courses.api import InApiTeacher
class CourseInApiTestCase(TestCase):
def setUp(self):
self.first_course = Course.objects.create(
title='Первый курс',
slug='perviy-kuourse',
)
self.CDTeacher = InApiTeacher()
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'])
class CourseCRUDTest(TestCase):
def setUp(self):
self.first_course = Course.objects.update_or_create_course(
title='Новый курс',
level="Базовый",
direction="Бизнес",
)
def test_slug(self):
self.assertIsNotNone(self.first_course.slug)
def test_upload_img(self):
Course.objects.update_or_create_course(
id=self.first_course.id,
title='Новый курс',
image=EXAMPLE_BASE64,
big_image=EXAMPLE_BASE64,
level="Базовый",
direction="Бизнес",
)
self.assertIsNone(self.first_course.big_mobile_image)
self.assertIsNotNone(self.first_course.image)

@ -1,5 +1,6 @@
from django.conf.urls import url from django.conf.urls import url
from courses import views as views
from course_service.courses import views as views
urlpatterns = [ urlpatterns = [
url(r'detail/([0-9]{1,99})/$', views.CourseDetailView.as_view()), url(r'detail/([0-9]{1,99})/$', views.CourseDetailView.as_view()),

@ -1,11 +1,11 @@
from rest_framework.views import APIView from course_service.courses.models import Course, Vertex
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 access.models import Progress from access.models import Progress
from maps.serializers import CourseMapSerializer from course_service.courses.serializers import CourseDetailSerializer, CourseListSerializer, VertexSerializer
from courses.models import Course, Vertex from course_service.maps.serializers import CourseMapSerializer
from courses.serializers import CourseDetailSerializer, CourseListSerializer, VertexSerializer, MiniVertexSerializer
class TreeView(APIView): class TreeView(APIView):

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

@ -0,0 +1,7 @@
from course_service.maps.models import CourseRoute
class OutApiRoute:
@staticmethod
def change_id(id: int) -> str:
return CourseRoute.objects.get(id=id).out_key

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

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-13 12:30 # Generated by Django 1.11.6 on 2017-12-13 23:00
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
@ -31,6 +31,7 @@ class Migration(migrations.Migration):
name='CourseRoute', name='CourseRoute',
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')),
('out_key', models.CharField(editable=False, max_length=15, unique=True, 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='Имя шаблона')),
('is_template', models.BooleanField(default=True, verbose_name='Может ли быть использован как шаблон')), ('is_template', models.BooleanField(default=True, verbose_name='Может ли быть использован как шаблон')),
], ],
@ -69,10 +70,10 @@ class Migration(migrations.Migration):
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='pivotvertex', name='pivotvertex',
unique_together=set([('map_course', 'vertex'), ('sort', 'map_course')]), unique_together=set([('sort', 'map_course'), ('map_course', 'vertex')]),
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='pivotcoursemap', name='pivotcoursemap',
unique_together=set([('sort', 'route'), ('map_course', 'route')]), unique_together=set([('map_course', 'route'), ('sort', 'route')]),
), ),
] ]

@ -1,8 +1,7 @@
from django.db import models from django.db import models
from course_service.maps.exeptions import MapTypeError
from lms.global_decorators import transaction_decorator from lms.global_decorators import transaction_decorator
from maps.exeptions import MapTypeError
from django.db.models import Q
class CourseRoute(models.Model): class CourseRoute(models.Model):
@ -10,6 +9,7 @@ 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='Имя шаблона', blank=True, null=True) name = models.CharField(max_length=255, verbose_name='Имя шаблона', blank=True, null=True)
is_template = models.BooleanField(default=True, verbose_name='Может ли быть использован как шаблон') is_template = models.BooleanField(default=True, verbose_name='Может ли быть использован как шаблон')
@ -72,7 +72,7 @@ class CourseMap(models.Model):
return full_list return full_list
def get_tree(self, serializer=None): def get_tree(self, serializer=None):
from courses.serializers import MiniVertexSerializer from course_service.courses.serializers import MiniVertexSerializer
serializer = serializer if serializer is not None else MiniVertexSerializer serializer = serializer if serializer is not None else MiniVertexSerializer
def helper(v_list): def helper(v_list):

@ -1,6 +1,6 @@
from rest_framework import serializers from rest_framework import serializers
from maps.models import CourseRoute, CourseMap from course_service.maps.models import CourseRoute, CourseMap
class CourseRouteSerializer(serializers.ModelSerializer): class CourseRouteSerializer(serializers.ModelSerializer):

@ -0,0 +1,23 @@
# 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 +0,0 @@
default_app_config = "courses.apps.CoursesAppConfig"

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

@ -1,4 +1,7 @@
import os, sys, django, csv import csv
import django
import os
import sys
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.db import IntegrityError from django.db import IntegrityError
@ -9,7 +12,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 courses.models import Course from course_service.courses.models import Course
if __name__ == '__main__': if __name__ == '__main__':
Bill.objects.all().delete() Bill.objects.all().delete()

@ -1,14 +1,17 @@
import os, sys, django, csv import csv
import os
import sys
from django.db import IntegrityError import django
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 courses.models import Course, Vertex from course_service.courses.api import InApiTeacher
from maps.models import CourseRoute, CourseMap, PivotVertex, PivotCourseMap from django.contrib.auth import get_user_model
from storage.models import File from course_service.courses.models import Course, Vertex, Topic
from course_service.maps.models import CourseRoute, CourseMap, PivotVertex, PivotCourseMap
if __name__ == '__main__': if __name__ == '__main__':
CourseMap.objects.all().delete() CourseMap.objects.all().delete()
@ -17,6 +20,7 @@ if __name__ == '__main__':
PivotCourseMap.objects.all().delete() PivotCourseMap.objects.all().delete()
Vertex.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:
user_reader = csv.DictReader(user_csv) user_reader = csv.DictReader(user_csv)
for row in user_reader: for row in user_reader:
@ -28,24 +32,40 @@ if __name__ == '__main__':
try: try:
for teacher in teachers: for teacher in teachers:
if teacher: if teacher:
course.teachers.add(teacher) teacher = get_user_model().objects.get(id=teacher)
InApiTeacher.add_teacher(course.slug, teacher)
except IntegrityError:
pass
with open('./course/storage.csv') as storage_csv: except get_user_model().DoesNotExist:
storage_reader = csv.DictReader(storage_csv) print('Плохо')
for row in storage_reader:
if row['original']:
File.objects.get_or_create(**row)
with open('./course/vertex.csv') as vertex_csv: with open('./course/vertex.csv') as vertex_csv:
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)
model = row.pop('type', None) model_type = row.pop('type', None)
parent = row.pop('parent', None) description = row.pop('description', None)
course = Course.objects.get(id=row.pop('course', None)) title = row.pop('title', None)
extra_data = row.pop('extra_data', None)
if model_type == 'topic':
course = Course.objects.get(id=row.pop('course', None))
Topic.objects.create(
id=extra_data['id'],
icon=extra_data['icon'],
course=course,
description=description,
title=title,
)
if model_type == 'task':
topic = Course.objects.get(id=row.pop('parent', None))
Vertex.objects.create(
id=extra_data['id'],
video=extra_data['video'],
materials=extra_data['materials'],
topic=topic,
description=description,
title=title,
)
topic = row.pop('parent', None)
parents = [Vertex.objects.get(object_id=parent, content_type__model='topic')] if parent else [] parents = [Vertex.objects.get(object_id=parent, content_type__model='topic')] if parent else []
Vertex.objects.create_with_dependencies(model=model, course=course, parents=parents, **row) Vertex.objects.create_with_dependencies(model=model, course=course, parents=parents, **row)
@ -54,7 +74,7 @@ if __name__ == '__main__':
route_obj = CourseRoute.objects.create(name='''Основной шаблон по курсу "%s"''' % course.title) route_obj = CourseRoute.objects.create(name='''Основной шаблон по курсу "%s"''' % course.title)
course.route = route_obj course.route = route_obj
sort = 0 sort = 0
for vertex in Vertex.objects.filter(course=course, content_type__model='topic').order_by("object_id"): for vertex in Vertex.objects.filter(course=course).order_by("object_id"):
PivotVertex.objects.create(map_course=map_obj, vertex=vertex, sort=sort) PivotVertex.objects.create(map_course=map_obj, vertex=vertex, sort=sort)
sort += 1 sort += 1

@ -1,4 +1,7 @@
import os, sys, django, csv import csv
import django
import os
import sys
sys.path.append("../") sys.path.append("../")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings")
@ -7,7 +10,7 @@ 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 access.models.other import Progress from access.models.other import Progress
from courses.models import Vertex, Course from course_service.courses.models import Vertex, Course
if __name__ == '__main__': if __name__ == '__main__':
Progress.objects.all().delete() Progress.objects.all().delete()

@ -0,0 +1,18 @@
import csv
import os
import sys
import django
sys.path.append("../")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings")
django.setup()
from storage.api import upload_file
if __name__ == '__main__':
with open('./course/storage.csv') as storage_csv:
storage_reader = csv.DictReader(storage_csv)
for row in storage_reader:
if row['original']:
upload_file(original=row['original'])

@ -1,4 +1,7 @@
import os, sys, django, csv import csv
import django
import os
import sys
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
@ -6,7 +9,7 @@ sys.path.append("../")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings")
django.setup() django.setup()
from courses.models import Vertex from course_service.courses.models import Vertex
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from journals.models import Thread, Journal from journals.models import Thread, Journal
from storage.models import File from storage.models import File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-13 12:30 # Generated by Django 1.11.6 on 2017-12-13 23:00
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
@ -12,7 +12,6 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('courses', '0002_auto_20171213_1230'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('yandex_money', '0002_auto_20171128_1150'), ('yandex_money', '0002_auto_20171128_1150'),
] ]
@ -22,9 +21,9 @@ 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')),
('course', models.CharField(max_length=255, 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(default='', verbose_name='Внутренняя заметка')), ('description', models.TextField(default='', verbose_name='Внутренняя заметка')),
('course', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='courses.Course', 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='Ответственный сотрудник')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bill_user', to=settings.AUTH_USER_MODEL, verbose_name='Плательщик')), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bill_user', to=settings.AUTH_USER_MODEL, verbose_name='Плательщик')),
], ],

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-14 14:05
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('finance', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='bill',
name='route',
field=models.CharField(default='', max_length=15, unique=True, verbose_name='Токен роута'),
preserve_default=False,
),
migrations.AlterUniqueTogether(
name='bill',
unique_together=set([]),
),
migrations.RemoveField(
model_name='bill',
name='course',
),
]

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-14 14:06
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('finance', '0002_auto_20171214_1405'),
]
operations = [
migrations.AlterField(
model_name='bill',
name='route',
field=models.CharField(max_length=15, verbose_name='Токен роута'),
),
migrations.AlterUniqueTogether(
name='bill',
unique_together=set([('route', 'opener', 'user')]),
),
]

@ -1,13 +1,13 @@
# coding=utf-8 # coding=utf-8
from django.db import models
from django.conf import settings from django.conf import settings
from django.db import models
from yandex_money.models import Payment from yandex_money.models import Payment
from courses.models import Course, Vertex from course_service.courses.models import Course, Vertex
class Bill(models.Model): class Bill(models.Model):
course = models.ForeignKey(to=Course, verbose_name='Курс', blank=True, null=True) route = models.CharField(max_length=15, verbose_name='Токен роута')
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='Будет показано пользователю',
@ -15,7 +15,7 @@ class Bill(models.Model):
description = models.TextField(verbose_name='Внутренняя заметка', default='') description = models.TextField(verbose_name='Внутренняя заметка', default='')
def __str__(self): def __str__(self):
return '%s:%s %s' % (self.id, self.course, self.user) return '%s: %s' % (self.id, self.user)
def get_full_price(self): def get_full_price(self):
return sum([i.price for i in self.invoice_set.all()]) return sum([i.price for i in self.invoice_set.all()])
@ -24,7 +24,7 @@ class Bill(models.Model):
verbose_name = 'Счет' verbose_name = 'Счет'
verbose_name_plural = 'Счета' verbose_name_plural = 'Счета'
unique_together = ( unique_together = (
('course', 'opener', 'user',), ('route', 'opener', 'user',),
) )

@ -25,9 +25,8 @@ def invoice_signal(instance, **kwargs):
if instance.status == 'F': if instance.status == 'F':
if instance.is_open: if instance.is_open:
Progress.objects.get_or_create( Progress.objects.get_or_create(
course=instance.bill.course, template=instance.bill.route,
user=instance.bill.user, user=instance.bill.user,
active_obj=instance.bill.course.get_first(['tutorial', 'task',])
) )
msg = EmailMessage( msg = EmailMessage(
'Ваш платёж прошёл успешно', 'Ваш платёж прошёл успешно',

@ -1,20 +1,19 @@
from django.contrib.auth import get_user_model import csv
from courses.models import Course import requests
from lms.global_decorators import transaction_decorator from django.contrib.auth import get_user_model
from rest_framework.views import APIView
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
from django.db.models import Q from django.db.models import Q
from django.shortcuts import redirect
import csv
from django.http import HttpResponse, HttpResponseForbidden from django.http import HttpResponse, HttpResponseForbidden
from django.shortcuts import redirect
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
from rest_framework.views import APIView
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 finance.serializers import BillSerializer, InvoiceSerializer from finance.serializers import BillSerializer, InvoiceSerializer
from lms.global_decorators import transaction_decorator
from lms.tools import get_real_name from lms.tools import get_real_name
import requests
class BillListView(APIView): class BillListView(APIView):
@ -39,7 +38,6 @@ class BillListView(APIView):
if bill: if bill:
bill['user'] = get_user_model().objects.get(email=bill['user']) bill['user'] = get_user_model().objects.get(email=bill['user'])
bill['opener'] = get_user_model().objects.get(email=bill['opener']) bill['opener'] = get_user_model().objects.get(email=bill['opener'])
bill['course'] = Course.objects.get(title=bill['course'])
bill.pop('invoices', None) bill.pop('invoices', None)
bill_obj, is_create = Bill.objects.update_or_create(**bill) bill_obj, is_create = Bill.objects.update_or_create(**bill)

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-13 12:30 # Generated by Django 1.11.6 on 2017-12-13 23:00
from __future__ import unicode_literals from __future__ import unicode_literals
import datetime import datetime

@ -15,4 +15,4 @@ def transaction_decorator(function_to_decorate):
finally: finally:
transaction.set_autocommit(True) transaction.set_autocommit(True)
return wrap return wrap

@ -106,12 +106,12 @@ libs = (
apps = ( apps = (
'access', 'access',
'courses', 'course_service.maps',
'course_service.courses',
'storage', 'storage',
'finance', 'finance',
'library', 'library',
'achievements', 'achievements',
'maps',
'config_app', 'config_app',
) )

@ -5,7 +5,7 @@ from django.core.files.base import ContentFile
def decode_base64(my_str, upload_to=None): def decode_base64(my_str, upload_to=None):
if "data:" in my_str: if "data:" in my_str:
my_str = my_str[my_str.index("base64,")+7:] my_str = my_str[my_str.index("base64,") + 7:]
if not upload_to: if not upload_to:
ext = my_str.split('/')[-1] ext = my_str.split('/')[-1]
return ContentFile(base64.b64decode(my_str), name='temp.' + ext) return ContentFile(base64.b64decode(my_str), name='temp.' + ext)
@ -22,3 +22,60 @@ def get_real_name(array, elem):
for i, j in array: for i, j in array:
if i and (i == elem or j == elem): if i and (i == elem or j == elem):
return i return i
EXAMPLE_BASE64 = "data:image/png;base64,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"

@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.

@ -17,13 +17,16 @@ def upload_file(original=None, name=None, base64=None) -> File:
return new_file return new_file
def add_comment(text: str, email: str, files=[]) -> Comment: def add_comment(text: str, email: str, files=None) -> Comment:
""" """
:param text: sting :param text: sting
:param email: string :param email: 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
key = ''.join(random.choice(string.ascii_letters) for _x in range(15)) key = ''.join(random.choice(string.ascii_letters) for _x in range(15))
comment = Comment.objects.create( comment = Comment.objects.create(
text=text, text=text,

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-13 12:30 # Generated by Django 1.11.6 on 2017-12-13 23:00
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models

@ -37,3 +37,37 @@ class CommentTestCase(TestCase):
comment = None comment = None
self.assertIsNone(comment) self.assertIsNone(comment)
class FileTestCase(TestCase):
def setUp(self):
self.first_comment = add_comment("first comment", "vasia@rambler.ru")
self.second_comment = add_comment(text="Привет, отличная работа", email="artem4000@gmail.com")
def test_comment_get(self):
self.assertEqual(self.first_comment, get_comment(self.first_comment.key))
def test_comment_update(self):
new_text = "Новый текст для коммента"
update_comment(key=self.first_comment.key, text=new_text)
self.assertEqual(get_comment(self.first_comment.key).text, new_text)
def test_comment_create(self):
token = 'fskjfskj'
comment1 = add_comment(text=token, email="artem4000@gmail.com")
self.assertEqual(comment1.text, token)
file_for_upload = SimpleUploadedFile('1.txt', 'Я файл!'.encode('utf-8'))
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])
self.assertEqual(comment2.files.count(), 1)
self.assertEqual(comment2.files.all()[0].name, file_name)
def test_comment_delete(self):
delete_comment(self.first_comment.key)
try:
comment = get_comment(self.first_comment.id)
except Comment.DoesNotExist:
comment = None
self.assertIsNone(comment)
Loading…
Cancel
Save