From d356aed11bfefde7c84a2940e748da0e5bbc9fd8 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 14 Dec 2017 15:39:05 +0300 Subject: [PATCH] fix --- access/migrations/0001_initial.py | 35 ++++++++- access/migrations/0002_auto_20171213_1230.py | 76 ------------------- access/models/other.py | 28 +++---- access/models/user.py | 4 +- access/serializers.py | 3 +- access/views.py | 10 +-- achievements/migrations/0001_initial.py | 2 +- ...213_1230.py => 0002_auto_20171213_2300.py} | 4 +- achievements/models.py | 4 +- api_v1/urls.py | 2 +- config_app/settings/test.env | 2 +- .../migrations => course_service}/__init__.py | 0 course_service/courses/__init__.py | 1 + course_service/courses/admin.py | 7 ++ course_service/courses/api.py | 21 +++++ {courses => course_service/courses}/apps.py | 5 +- .../courses}/migrations/0001_initial.py | 13 ++-- .../courses/migrations/0002_course_route.py | 11 +-- .../migrations/0003_auto_20171214_1513.py | 25 ++++++ .../courses/migrations}/__init__.py | 0 {courses => course_service/courses}/models.py | 71 ++++++++--------- .../courses}/serializers.py | 2 +- .../courses}/signals.py | 2 +- course_service/courses/tsets.py | 51 +++++++++++++ {courses => course_service/courses}/urls.py | 3 +- {courses => course_service/courses}/views.py | 8 +- .../maps}/__init__.py | 0 {maps => course_service/maps}/admin.py | 2 +- course_service/maps/api.py | 7 ++ {maps => course_service/maps}/apps.py | 3 +- {maps => course_service/maps}/exeptions.py | 0 .../maps}/migrations/0001_initial.py | 7 +- course_service/maps/migrations/__init__.py | 0 {maps => course_service/maps}/models.py | 6 +- {maps => course_service/maps}/serializers.py | 2 +- course_service/maps/tests.py | 23 ++++++ {maps => course_service/maps}/views.py | 0 courses/__init__.py | 1 - courses/admin.py | 7 -- csv/load_bills.py | 7 +- csv/load_courses.py | 56 +++++++++----- csv/load_perm.py | 7 +- csv/load_storage.py | 18 +++++ csv/load_student_teachers_threads.py | 7 +- finance/migrations/0001_initial.py | 5 +- finance/migrations/0002_auto_20171214_1405.py | 29 +++++++ finance/migrations/0003_auto_20171214_1406.py | 26 +++++++ finance/models.py | 10 +-- finance/signals.py | 3 +- finance/tests.py | 0 finance/views.py | 20 +++-- library/migrations/0001_initial.py | 2 +- lms/global_decorators.py | 2 +- lms/settings.py | 4 +- lms/tools.py | 59 +++++++++++++- maps/tests.py | 3 - storage/api.py | 5 +- storage/migrations/0001_initial.py | 2 +- storage/tests.py | 34 +++++++++ 59 files changed, 502 insertions(+), 245 deletions(-) delete mode 100644 access/migrations/0002_auto_20171213_1230.py rename achievements/migrations/{0002_auto_20171213_1230.py => 0002_auto_20171213_2300.py} (97%) rename {courses/migrations => course_service}/__init__.py (100%) mode change 100755 => 100644 create mode 100755 course_service/courses/__init__.py create mode 100755 course_service/courses/admin.py create mode 100644 course_service/courses/api.py rename {courses => course_service/courses}/apps.py (68%) rename {courses => course_service/courses}/migrations/0001_initial.py (83%) rename courses/migrations/0002_auto_20171213_1230.py => course_service/courses/migrations/0002_course_route.py (61%) create mode 100644 course_service/courses/migrations/0003_auto_20171214_1513.py rename {maps => course_service/courses/migrations}/__init__.py (100%) mode change 100644 => 100755 rename {courses => course_service/courses}/models.py (83%) rename {courses => course_service/courses}/serializers.py (97%) rename {courses => course_service/courses}/signals.py (86%) create mode 100644 course_service/courses/tsets.py rename {courses => course_service/courses}/urls.py (84%) rename {courses => course_service/courses}/views.py (93%) rename {maps/migrations => course_service/maps}/__init__.py (100%) rename {maps => course_service/maps}/admin.py (64%) create mode 100644 course_service/maps/api.py rename {maps => course_service/maps}/apps.py (70%) rename {maps => course_service/maps}/exeptions.py (100%) rename {maps => course_service/maps}/migrations/0001_initial.py (91%) create mode 100644 course_service/maps/migrations/__init__.py rename {maps => course_service/maps}/models.py (95%) rename {maps => course_service/maps}/serializers.py (90%) create mode 100644 course_service/maps/tests.py rename {maps => course_service/maps}/views.py (100%) delete mode 100755 courses/__init__.py delete mode 100755 courses/admin.py create mode 100644 csv/load_storage.py create mode 100644 finance/migrations/0002_auto_20171214_1405.py create mode 100644 finance/migrations/0003_auto_20171214_1406.py create mode 100644 finance/tests.py delete mode 100644 maps/tests.py diff --git a/access/migrations/0001_initial.py b/access/migrations/0001_initial.py index bcfe877..40712e7 100644 --- a/access/migrations/0001_initial.py +++ b/access/migrations/0001_initial.py @@ -1,8 +1,9 @@ # -*- 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 import access.models.user +from django.conf import settings import django.contrib.postgres.fields from django.db import migrations, models import django.db.models.deletion @@ -13,6 +14,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ + ('auth', '0008_alter_user_username_max_length'), ] operations = [ @@ -23,13 +25,16 @@ class Migration(migrations.Migration): ('password', models.CharField(max_length=128, verbose_name='password')), ('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')), + ('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')), - ('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')), ('date_joined', models.DateTimeField(verbose_name='date joined')), ('is_staff', 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='заблочен')), + ('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={ 'verbose_name': 'Пользователь', @@ -48,6 +53,7 @@ class Migration(migrations.Migration): ('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/')), ('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={ 'verbose_name': 'Дополнительная информация о пользователе', @@ -70,14 +76,18 @@ class Migration(migrations.Migration): name='PivotProgressVertex', fields=[ ('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)), - ('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( name='Progress', fields=[ ('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={ 'verbose_name': 'Прогресс пользователя', @@ -96,4 +106,23 @@ class Migration(migrations.Migration): }, 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')]), + ), ] diff --git a/access/migrations/0002_auto_20171213_1230.py b/access/migrations/0002_auto_20171213_1230.py deleted file mode 100644 index 3492030..0000000 --- a/access/migrations/0002_auto_20171213_1230.py +++ /dev/null @@ -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')]), - ), - ] diff --git a/access/models/other.py b/access/models/other.py index 380963c..715093f 100644 --- a/access/models/other.py +++ b/access/models/other.py @@ -1,13 +1,11 @@ from django.conf import settings from django.contrib.postgres.fields import ArrayField 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 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): @@ -53,16 +51,10 @@ class Progress(models.Model): teacher = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name="Преподователь по умолчанию", related_name='teacher_progress') user = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name='Студент', null=True) - course = models.ForeignKey(to=Course, verbose_name='Курс', null=True) - template = models.OneToOneField(to=CourseRoute, blank=True, null=True, verbose_name='Шаблон для прохождения если ' - 'не указан явно смотри ' - 'функцию get_template()') + template = models.CharField(max_length=15, verbose_name='Токен прохождения') def __str__(self): - return '%s %s' % ( - self.user.email, - self.course.title, - ) + return '%s' % (self.user.email,) @transaction_decorator def add_vertex(self, vertex): @@ -94,7 +86,7 @@ class Progress(models.Model): class Meta: verbose_name = 'Прогресс пользователя' verbose_name_plural = 'Прогресс пользователя' - unique_together = (("user", "course"),) + unique_together = (("user", "template"),) class PivotProgressVertex(models.Model): @@ -103,9 +95,9 @@ class PivotProgressVertex(models.Model): (1, 'Ожидание'), (0, 'Не выполненно'), ) + teacher = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name="Преподователь по умолчанию", + blank=True, null=True) 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) - comment = ArrayField( - models.CharField(max_length=15, blank=True, verbose_name='Ссылки на комменты'), - ) \ No newline at end of file + comment = ArrayField(models.CharField(max_length=15, blank=True, verbose_name='Ссылки на комменты', unique=True),) \ No newline at end of file diff --git a/access/models/user.py b/access/models/user.py index 43d90b4..c68ea18 100644 --- a/access/models/user.py +++ b/access/models/user.py @@ -44,6 +44,7 @@ class CustomUserManager(BaseUserManager): email = self.normalize_email(email) 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) if not password: @@ -92,8 +93,9 @@ class CustomUserManager(BaseUserManager): class User(AbstractBaseUser, PermissionsMixin): + out_key = models.CharField(max_length=15, unique=True, verbose_name="Ключ для внешних сервисов", editable=False) 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) date_joined = models.DateTimeField(_('date joined')) is_staff = models.BooleanField(verbose_name='флаг персонала', default=False, diff --git a/access/serializers.py b/access/serializers.py index 756c3b1..5750f43 100644 --- a/access/serializers.py +++ b/access/serializers.py @@ -1,8 +1,9 @@ from django.contrib.auth import get_user_model from rest_framework import serializers + from access.models.other import Account, Progress from achievements.serialers import DiplomaSerializer, AchievementsSerializer -from courses.serializers import MiniVertexSerializer, CourseInitSerializer +from course_service.courses.serializers import MiniVertexSerializer, CourseInitSerializer class ProgressSerializer(serializers.ModelSerializer): diff --git a/access/views.py b/access/views.py index 0cfd9fb..169c613 100644 --- a/access/views.py +++ b/access/views.py @@ -1,20 +1,20 @@ +import datetime import random import string -import datetime -from django.contrib.auth import get_user_model from django.contrib import auth +from django.contrib.auth import get_user_model from django.core.mail import send_mail +from django.db.models import Q from django.shortcuts import redirect from rest_framework.permissions import IsAuthenticated -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 rest_framework.views import APIView from access.models.other import Invite, Progress, ResetPassword from access.serializers import UserSelfSerializer, UserSearchSerializer -from courses.models import Vertex +from course_service.courses.models import Vertex class TeacherListView(APIView): diff --git a/achievements/migrations/0001_initial.py b/achievements/migrations/0001_initial.py index 9c5a33f..d644437 100644 --- a/achievements/migrations/0001_initial.py +++ b/achievements/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- 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 django.db import migrations, models diff --git a/achievements/migrations/0002_auto_20171213_1230.py b/achievements/migrations/0002_auto_20171213_2300.py similarity index 97% rename from achievements/migrations/0002_auto_20171213_1230.py rename to achievements/migrations/0002_auto_20171213_2300.py index 3c81e1f..01a6a58 100644 --- a/achievements/migrations/0002_auto_20171213_1230.py +++ b/achievements/migrations/0002_auto_20171213_2300.py @@ -1,5 +1,5 @@ # -*- 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 django.conf import settings @@ -12,8 +12,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('courses', '0001_initial'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('courses', '0001_initial'), ('achievements', '0001_initial'), ] diff --git a/achievements/models.py b/achievements/models.py index 7707add..e92b8a0 100644 --- a/achievements/models.py +++ b/achievements/models.py @@ -1,7 +1,7 @@ -from django.db import models 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): diff --git a/api_v1/urls.py b/api_v1/urls.py index d4775ad..eb9c384 100644 --- a/api_v1/urls.py +++ b/api_v1/urls.py @@ -1,7 +1,7 @@ from django.conf.urls import url, include urlpatterns = [ - url(r'courses/', include('courses.urls')), + url(r'courses/', include('course_service.courses.urls')), url(r'users/', include('access.urls')), url(r'library/', include('library.urls')), url(r'finance/', include('finance.urls')), diff --git a/config_app/settings/test.env b/config_app/settings/test.env index 3a1aac2..ece0044 100644 --- a/config_app/settings/test.env +++ b/config_app/settings/test.env @@ -1,5 +1,5 @@ DEBUG=True 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' CACHE_URL=rediscache://127.0.0.1:6379/1?client_class=django_redis.client.DefaultClient \ No newline at end of file diff --git a/courses/migrations/__init__.py b/course_service/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from courses/migrations/__init__.py rename to course_service/__init__.py diff --git a/course_service/courses/__init__.py b/course_service/courses/__init__.py new file mode 100755 index 0000000..a85b23a --- /dev/null +++ b/course_service/courses/__init__.py @@ -0,0 +1 @@ +default_app_config = "course_service.courses.apps.CoursesAppConfig" diff --git a/course_service/courses/admin.py b/course_service/courses/admin.py new file mode 100755 index 0000000..d35da92 --- /dev/null +++ b/course_service/courses/admin.py @@ -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) \ No newline at end of file diff --git a/course_service/courses/api.py b/course_service/courses/api.py new file mode 100644 index 0000000..ba95edd --- /dev/null +++ b/course_service/courses/api.py @@ -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 diff --git a/courses/apps.py b/course_service/courses/apps.py similarity index 68% rename from courses/apps.py rename to course_service/courses/apps.py index 879596c..db22b5b 100644 --- a/courses/apps.py +++ b/course_service/courses/apps.py @@ -3,8 +3,9 @@ from django.apps import AppConfig class CoursesAppConfig(AppConfig): - name = "courses" + name = "course_service.courses" + label = 'courses' verbose_name = "Курсы" def ready(self): - import courses.signals \ No newline at end of file + pass \ No newline at end of file diff --git a/courses/migrations/0001_initial.py b/course_service/courses/migrations/0001_initial.py similarity index 83% rename from courses/migrations/0001_initial.py rename to course_service/courses/migrations/0001_initial.py index 69ec748..c3a461e 100644 --- a/courses/migrations/0001_initial.py +++ b/course_service/courses/migrations/0001_initial.py @@ -1,7 +1,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 +import django.contrib.postgres.fields from django.db import migrations, models import django.db.models.deletion @@ -11,7 +12,6 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('storage', '0001_initial'), ] 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='Уровень')), ('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='Направление')), - ('sort', models.SmallIntegerField(null=True, verbose_name='Порядок сортировки')), ('public', models.BooleanField(default=False, verbose_name='Опубликовать')), ('title', models.CharField(max_length=255, verbose_name='Заголовок')), ('description', models.TextField(blank=True, verbose_name='Описание')), ('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='Под мобилку')), + ('teachers', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=15, verbose_name='Ссылки на преподов'), default=[], size=None)), ], options={ 'verbose_name': 'Курс', @@ -42,7 +42,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('title', models.CharField(max_length=255, 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={ 'verbose_name': 'Тема', @@ -57,9 +57,10 @@ class Migration(migrations.Migration): ('free', models.BooleanField(default=True, verbose_name='Привилегии для узла не будут проверяться')), ('description', 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)), - ('materials', models.ManyToManyField(blank=True, to='storage.File', verbose_name='Материалы урока')), - ('topic', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Topic')), + ('token', models.CharField(max_length=15, unique=True, verbose_name='Ключ доступа к узлу')), + ('topic', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Topic', verbose_name='Тема')), ], options={ 'verbose_name': 'Урок', diff --git a/courses/migrations/0002_auto_20171213_1230.py b/course_service/courses/migrations/0002_course_route.py similarity index 61% rename from courses/migrations/0002_auto_20171213_1230.py rename to course_service/courses/migrations/0002_course_route.py index bd82c9e..dcaee66 100644 --- a/courses/migrations/0002_auto_20171213_1230.py +++ b/course_service/courses/migrations/0002_course_route.py @@ -1,8 +1,7 @@ # -*- 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 django.conf import settings from django.db import migrations, models import django.db.models.deletion @@ -12,9 +11,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('maps', '0001_initial'), ('courses', '0001_initial'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('maps', '0001_initial'), ] operations = [ @@ -23,9 +21,4 @@ class Migration(migrations.Migration): name='route', 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='Преподаватели'), - ), ] diff --git a/course_service/courses/migrations/0003_auto_20171214_1513.py b/course_service/courses/migrations/0003_auto_20171214_1513.py new file mode 100644 index 0000000..63a8c03 --- /dev/null +++ b/course_service/courses/migrations/0003_auto_20171214_1513.py @@ -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='Описание'), + ), + ] diff --git a/maps/__init__.py b/course_service/courses/migrations/__init__.py old mode 100644 new mode 100755 similarity index 100% rename from maps/__init__.py rename to course_service/courses/migrations/__init__.py diff --git a/courses/models.py b/course_service/courses/models.py similarity index 83% rename from courses/models.py rename to course_service/courses/models.py index 9c3ccd9..23134be 100755 --- a/courses/models.py +++ b/course_service/courses/models.py @@ -1,20 +1,16 @@ # -*- coding: utf-8 -*- -from django.conf import settings -from django.contrib.auth import get_user_model -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 random +import string + 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 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 library.models import Tags -from storage.models import File -import random +from lms.tools import decode_base64, get_real_name COURSE_LEVEL = ( ('B', 'Базовый'), @@ -32,10 +28,10 @@ COURSE_DIRECTION = ( class CourseManager(models.Manager): - @transaction_decorator - def update_or_create_course(self, image=None, big_image=None, id=0, route=None, - big_mobile_image=None, mentors=None, slug=None, - teachers=None, level=None, direction=None, **kwargs): + + def update_or_create_course(self, image=None, big_image=None, id=0, + big_mobile_image=None, slug=None, + level=None, direction=None, **kwargs): slug = slug if slug else slugify(unidecode.unidecode(kwargs['title'])) @@ -54,29 +50,30 @@ class CourseManager(models.Manager): if direction: kwargs['direction'] = get_real_name(COURSE_DIRECTION, direction) - if route: - kwargs['route'] = CourseRoute.objects.get(id=route) - try: course = self.get(id=id) for i in kwargs: if kwargs[i]: setattr(course, i, kwargs[i]) course.save() + except ObjectDoesNotExist: 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) - CourseMap.objects.create(course=course) - - if mentors: - for email in mentors: - course.mentors.add(get_user_model().objects.get(email=email)) + course.save() - if teachers: - for email in teachers: - course.teachers.add(get_user_model().objects.get(email=email)) + 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 @@ -85,7 +82,6 @@ class Course(models.Model): 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) direction = models.SmallIntegerField(choices=COURSE_DIRECTION, verbose_name='Направление', null=True) - sort = models.SmallIntegerField(null=True, verbose_name="Порядок сортировки") public = models.BooleanField(verbose_name='Опубликовать', default=False) title = models.CharField(verbose_name="Заголовок", max_length=255) 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_mobile_image = models.URLField(verbose_name='Под мобилку', blank=True, null=True, help_text='Большая картинка для мобильной версии', max_length=255) - teachers = models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name='Преподаватели', - related_name='course_teachers') + teachers = ArrayField( + models.CharField(max_length=15, blank=True, verbose_name='Ссылки на преподов'), default=[]) route = models.OneToOneField(to=CourseRoute, verbose_name="Порядок прохождения по умолчанию", blank=True, null=True) def __str__(self): return self.title - def get_teacher(self): - return random.choice(self.teachers.all()) - def get_maps(self, user): return user.progress_set.get(course=self).get_template().get_maps() @@ -166,7 +159,8 @@ class Course(models.Model): class Topic(models.Model): title = models.CharField(verbose_name='Название', max_length=255) 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: verbose_name = "Тема" @@ -180,13 +174,14 @@ class Vertex(models.Model): (1, 'Ручная валидация'), (0, 'Без валидации'), ) - topic = models.ForeignKey(to=Topic) + topic = models.ForeignKey(to=Topic, verbose_name='Тема') title = models.CharField(verbose_name='Название', max_length=255) 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) - 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) + token = models.CharField(max_length=15, verbose_name="Ключ доступа к узлу", unique=True) def __str__(self): return self.title diff --git a/courses/serializers.py b/course_service/courses/serializers.py similarity index 97% rename from courses/serializers.py rename to course_service/courses/serializers.py index 9afb06c..c3145af 100644 --- a/courses/serializers.py +++ b/course_service/courses/serializers.py @@ -1,6 +1,6 @@ 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): diff --git a/courses/signals.py b/course_service/courses/signals.py similarity index 86% rename from courses/signals.py rename to course_service/courses/signals.py index 086c944..5e86e42 100644 --- a/courses/signals.py +++ b/course_service/courses/signals.py @@ -1,7 +1,7 @@ from django.db.models.signals import pre_delete from django.dispatch import receiver -from courses.models import Vertex +from course_service.courses.models import Vertex @receiver(pre_delete, sender=Vertex) diff --git a/course_service/courses/tsets.py b/course_service/courses/tsets.py new file mode 100644 index 0000000..50be802 --- /dev/null +++ b/course_service/courses/tsets.py @@ -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) + + diff --git a/courses/urls.py b/course_service/courses/urls.py similarity index 84% rename from courses/urls.py rename to course_service/courses/urls.py index 8bb834a..f7d4ee0 100644 --- a/courses/urls.py +++ b/course_service/courses/urls.py @@ -1,5 +1,6 @@ from django.conf.urls import url -from courses import views as views + +from course_service.courses import views as views urlpatterns = [ url(r'detail/([0-9]{1,99})/$', views.CourseDetailView.as_view()), diff --git a/courses/views.py b/course_service/courses/views.py similarity index 93% rename from courses/views.py rename to course_service/courses/views.py index 50c42a8..4c1ae12 100644 --- a/courses/views.py +++ b/course_service/courses/views.py @@ -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.response import Response +from rest_framework.views import APIView from access.models import Progress -from maps.serializers import CourseMapSerializer -from courses.models import Course, Vertex -from courses.serializers import CourseDetailSerializer, CourseListSerializer, VertexSerializer, MiniVertexSerializer +from course_service.courses.serializers import CourseDetailSerializer, CourseListSerializer, VertexSerializer +from course_service.maps.serializers import CourseMapSerializer class TreeView(APIView): diff --git a/maps/migrations/__init__.py b/course_service/maps/__init__.py similarity index 100% rename from maps/migrations/__init__.py rename to course_service/maps/__init__.py diff --git a/maps/admin.py b/course_service/maps/admin.py similarity index 64% rename from maps/admin.py rename to course_service/maps/admin.py index 96e6b9b..36efe2a 100644 --- a/maps/admin.py +++ b/course_service/maps/admin.py @@ -1,6 +1,6 @@ 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(CourseRoute) diff --git a/course_service/maps/api.py b/course_service/maps/api.py new file mode 100644 index 0000000..7e67ba0 --- /dev/null +++ b/course_service/maps/api.py @@ -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 diff --git a/maps/apps.py b/course_service/maps/apps.py similarity index 70% rename from maps/apps.py rename to course_service/maps/apps.py index 2e9c896..76f0dbf 100644 --- a/maps/apps.py +++ b/course_service/maps/apps.py @@ -2,5 +2,6 @@ from django.apps import AppConfig class MapsConfig(AppConfig): - name = "maps" + name = "course_service.maps" + label = 'maps' verbose_name = "Отображение курсов" diff --git a/maps/exeptions.py b/course_service/maps/exeptions.py similarity index 100% rename from maps/exeptions.py rename to course_service/maps/exeptions.py diff --git a/maps/migrations/0001_initial.py b/course_service/maps/migrations/0001_initial.py similarity index 91% rename from maps/migrations/0001_initial.py rename to course_service/maps/migrations/0001_initial.py index 67a3360..72aca92 100644 --- a/maps/migrations/0001_initial.py +++ b/course_service/maps/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- 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 django.db import migrations, models @@ -31,6 +31,7 @@ class Migration(migrations.Migration): 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(blank=True, max_length=255, null=True, verbose_name='Имя шаблона')), ('is_template', models.BooleanField(default=True, verbose_name='Может ли быть использован как шаблон')), ], @@ -69,10 +70,10 @@ class Migration(migrations.Migration): ), migrations.AlterUniqueTogether( name='pivotvertex', - unique_together=set([('map_course', 'vertex'), ('sort', 'map_course')]), + unique_together=set([('sort', 'map_course'), ('map_course', 'vertex')]), ), migrations.AlterUniqueTogether( name='pivotcoursemap', - unique_together=set([('sort', 'route'), ('map_course', 'route')]), + unique_together=set([('map_course', 'route'), ('sort', 'route')]), ), ] diff --git a/course_service/maps/migrations/__init__.py b/course_service/maps/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/maps/models.py b/course_service/maps/models.py similarity index 95% rename from maps/models.py rename to course_service/maps/models.py index 6a90a4e..57c7cb6 100644 --- a/maps/models.py +++ b/course_service/maps/models.py @@ -1,8 +1,7 @@ from django.db import models +from course_service.maps.exeptions import MapTypeError from lms.global_decorators import transaction_decorator -from maps.exeptions import MapTypeError -from django.db.models import Q 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) is_template = models.BooleanField(default=True, verbose_name='Может ли быть использован как шаблон') @@ -72,7 +72,7 @@ class CourseMap(models.Model): return full_list 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 def helper(v_list): diff --git a/maps/serializers.py b/course_service/maps/serializers.py similarity index 90% rename from maps/serializers.py rename to course_service/maps/serializers.py index d620793..04334b9 100644 --- a/maps/serializers.py +++ b/course_service/maps/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from maps.models import CourseRoute, CourseMap +from course_service.maps.models import CourseRoute, CourseMap class CourseRouteSerializer(serializers.ModelSerializer): diff --git a/course_service/maps/tests.py b/course_service/maps/tests.py new file mode 100644 index 0000000..ac84f6d --- /dev/null +++ b/course_service/maps/tests.py @@ -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']) diff --git a/maps/views.py b/course_service/maps/views.py similarity index 100% rename from maps/views.py rename to course_service/maps/views.py diff --git a/courses/__init__.py b/courses/__init__.py deleted file mode 100755 index 354e387..0000000 --- a/courses/__init__.py +++ /dev/null @@ -1 +0,0 @@ -default_app_config = "courses.apps.CoursesAppConfig" diff --git a/courses/admin.py b/courses/admin.py deleted file mode 100755 index 71dc813..0000000 --- a/courses/admin.py +++ /dev/null @@ -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) \ No newline at end of file diff --git a/csv/load_bills.py b/csv/load_bills.py index 341f480..7520d0b 100644 --- a/csv/load_bills.py +++ b/csv/load_bills.py @@ -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.db import IntegrityError @@ -9,7 +12,7 @@ django.setup() from yandex_money.models import Payment from finance.models import Bill, Invoice -from courses.models import Course +from course_service.courses.models import Course if __name__ == '__main__': Bill.objects.all().delete() diff --git a/csv/load_courses.py b/csv/load_courses.py index b957be6..42f8adc 100644 --- a/csv/load_courses.py +++ b/csv/load_courses.py @@ -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("../") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings") django.setup() -from courses.models import Course, Vertex -from maps.models import CourseRoute, CourseMap, PivotVertex, PivotCourseMap -from storage.models import File +from course_service.courses.api import InApiTeacher +from django.contrib.auth import get_user_model +from course_service.courses.models import Course, Vertex, Topic +from course_service.maps.models import CourseRoute, CourseMap, PivotVertex, PivotCourseMap if __name__ == '__main__': CourseMap.objects.all().delete() @@ -17,6 +20,7 @@ if __name__ == '__main__': PivotCourseMap.objects.all().delete() Vertex.objects.all().delete() Course.objects.all().delete() + with open('./course/course.csv') as user_csv: user_reader = csv.DictReader(user_csv) for row in user_reader: @@ -28,24 +32,40 @@ if __name__ == '__main__': try: for teacher in teachers: if teacher: - course.teachers.add(teacher) - - except IntegrityError: - pass + teacher = get_user_model().objects.get(id=teacher) + InApiTeacher.add_teacher(course.slug, teacher) - with open('./course/storage.csv') as storage_csv: - storage_reader = csv.DictReader(storage_csv) - for row in storage_reader: - if row['original']: - File.objects.get_or_create(**row) + except get_user_model().DoesNotExist: + print('Плохо') with open('./course/vertex.csv') as vertex_csv: vertex_reader = csv.DictReader(vertex_csv) for row in vertex_reader: row = dict(row) - model = row.pop('type', None) - parent = row.pop('parent', None) - course = Course.objects.get(id=row.pop('course', None)) + model_type = row.pop('type', None) + description = row.pop('description', 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 [] 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) course.route = route_obj 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) sort += 1 diff --git a/csv/load_perm.py b/csv/load_perm.py index a711f53..0c8498b 100644 --- a/csv/load_perm.py +++ b/csv/load_perm.py @@ -1,4 +1,7 @@ -import os, sys, django, csv +import csv +import django +import os +import sys sys.path.append("../") 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 import get_user_model from access.models.other import Progress -from courses.models import Vertex, Course +from course_service.courses.models import Vertex, Course if __name__ == '__main__': Progress.objects.all().delete() diff --git a/csv/load_storage.py b/csv/load_storage.py new file mode 100644 index 0000000..baf1116 --- /dev/null +++ b/csv/load_storage.py @@ -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']) \ No newline at end of file diff --git a/csv/load_student_teachers_threads.py b/csv/load_student_teachers_threads.py index 7ce2dc4..123a6c8 100644 --- a/csv/load_student_teachers_threads.py +++ b/csv/load_student_teachers_threads.py @@ -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 @@ -6,7 +9,7 @@ sys.path.append("../") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings") django.setup() -from courses.models import Vertex +from course_service.courses.models import Vertex from django.contrib.contenttypes.models import ContentType from journals.models import Thread, Journal from storage.models import File diff --git a/finance/migrations/0001_initial.py b/finance/migrations/0001_initial.py index 5ef26b6..4234496 100644 --- a/finance/migrations/0001_initial.py +++ b/finance/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- 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 django.conf import settings @@ -12,7 +12,6 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('courses', '0002_auto_20171213_1230'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ('yandex_money', '0002_auto_20171128_1150'), ] @@ -22,9 +21,9 @@ class Migration(migrations.Migration): name='Bill', fields=[ ('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='Комментарий продавца')), ('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='Ответственный сотрудник')), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bill_user', to=settings.AUTH_USER_MODEL, verbose_name='Плательщик')), ], diff --git a/finance/migrations/0002_auto_20171214_1405.py b/finance/migrations/0002_auto_20171214_1405.py new file mode 100644 index 0000000..67892e9 --- /dev/null +++ b/finance/migrations/0002_auto_20171214_1405.py @@ -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', + ), + ] diff --git a/finance/migrations/0003_auto_20171214_1406.py b/finance/migrations/0003_auto_20171214_1406.py new file mode 100644 index 0000000..7f87f0f --- /dev/null +++ b/finance/migrations/0003_auto_20171214_1406.py @@ -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')]), + ), + ] diff --git a/finance/models.py b/finance/models.py index 4ccff33..df0177f 100755 --- a/finance/models.py +++ b/finance/models.py @@ -1,13 +1,13 @@ # coding=utf-8 -from django.db import models from django.conf import settings +from django.db import models from yandex_money.models import Payment -from courses.models import Course, Vertex +from course_service.courses.models import Course, Vertex 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') opener = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='Ответственный сотрудник', null=True) comment = models.TextField(verbose_name='Комментарий продавца', help_text='Будет показано пользователю', @@ -15,7 +15,7 @@ class Bill(models.Model): description = models.TextField(verbose_name='Внутренняя заметка', default='') 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): return sum([i.price for i in self.invoice_set.all()]) @@ -24,7 +24,7 @@ class Bill(models.Model): verbose_name = 'Счет' verbose_name_plural = 'Счета' unique_together = ( - ('course', 'opener', 'user',), + ('route', 'opener', 'user',), ) diff --git a/finance/signals.py b/finance/signals.py index c56503d..68b5693 100644 --- a/finance/signals.py +++ b/finance/signals.py @@ -25,9 +25,8 @@ def invoice_signal(instance, **kwargs): if instance.status == 'F': if instance.is_open: Progress.objects.get_or_create( - course=instance.bill.course, + template=instance.bill.route, user=instance.bill.user, - active_obj=instance.bill.course.get_first(['tutorial', 'task',]) ) msg = EmailMessage( 'Ваш платёж прошёл успешно', diff --git a/finance/tests.py b/finance/tests.py new file mode 100644 index 0000000..e69de29 diff --git a/finance/views.py b/finance/views.py index 25f1d58..13dbc80 100644 --- a/finance/views.py +++ b/finance/views.py @@ -1,20 +1,19 @@ -from django.contrib.auth import get_user_model +import csv -from courses.models import Course -from lms.global_decorators import transaction_decorator -from rest_framework.views import APIView -from rest_framework.renderers import JSONRenderer -from rest_framework.response import Response +import requests +from django.contrib.auth import get_user_model from django.db.models import Q -from django.shortcuts import redirect -import csv 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 finance.models import Bill, Invoice from finance.serializers import BillSerializer, InvoiceSerializer +from lms.global_decorators import transaction_decorator from lms.tools import get_real_name -import requests class BillListView(APIView): @@ -39,7 +38,6 @@ class BillListView(APIView): if bill: bill['user'] = get_user_model().objects.get(email=bill['user']) bill['opener'] = get_user_model().objects.get(email=bill['opener']) - bill['course'] = Course.objects.get(title=bill['course']) bill.pop('invoices', None) bill_obj, is_create = Bill.objects.update_or_create(**bill) diff --git a/library/migrations/0001_initial.py b/library/migrations/0001_initial.py index c57fc97..e60a4d3 100644 --- a/library/migrations/0001_initial.py +++ b/library/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- 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 import datetime diff --git a/lms/global_decorators.py b/lms/global_decorators.py index 7b60603..d5b0fff 100644 --- a/lms/global_decorators.py +++ b/lms/global_decorators.py @@ -15,4 +15,4 @@ def transaction_decorator(function_to_decorate): finally: transaction.set_autocommit(True) - return wrap \ No newline at end of file + return wrap diff --git a/lms/settings.py b/lms/settings.py index 38d5090..b1858ba 100644 --- a/lms/settings.py +++ b/lms/settings.py @@ -106,12 +106,12 @@ libs = ( apps = ( 'access', - 'courses', + 'course_service.maps', + 'course_service.courses', 'storage', 'finance', 'library', 'achievements', - 'maps', 'config_app', ) diff --git a/lms/tools.py b/lms/tools.py index 32f0002..4a8d259 100644 --- a/lms/tools.py +++ b/lms/tools.py @@ -5,7 +5,7 @@ from django.core.files.base import ContentFile def decode_base64(my_str, upload_to=None): 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: ext = my_str.split('/')[-1] return ContentFile(base64.b64decode(my_str), name='temp.' + ext) @@ -22,3 +22,60 @@ def get_real_name(array, elem): for i, j in array: if i and (i == elem or j == elem): return i + + +EXAMPLE_BASE64 = "data:image/png;base64,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" diff --git a/maps/tests.py b/maps/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/maps/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/storage/api.py b/storage/api.py index 81ea7fa..d0e0f60 100644 --- a/storage/api.py +++ b/storage/api.py @@ -17,13 +17,16 @@ def upload_file(original=None, name=None, base64=None) -> 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 email: string :param files: {name?: string, original?: File, base64?: string}[] одно из двух последних свойств должно быть указано :return: Comment """ + + files = [] if files is None else files + key = ''.join(random.choice(string.ascii_letters) for _x in range(15)) comment = Comment.objects.create( text=text, diff --git a/storage/migrations/0001_initial.py b/storage/migrations/0001_initial.py index c24c80f..482c3a3 100644 --- a/storage/migrations/0001_initial.py +++ b/storage/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- 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 django.db import migrations, models diff --git a/storage/tests.py b/storage/tests.py index 6b886ea..38e4609 100644 --- a/storage/tests.py +++ b/storage/tests.py @@ -37,3 +37,37 @@ class CommentTestCase(TestCase): comment = None 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) \ No newline at end of file