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. 4
      lms/settings.py
  54. 59
      lms/tools.py
  55. 3
      maps/tests.py
  56. 5
      storage/api.py
  57. 2
      storage/migrations/0001_initial.py
  58. 34
      storage/tests.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')]),
),
]

@ -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.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='Ссылки на комменты'),
)
comment = ArrayField(models.CharField(max_length=15, blank=True, verbose_name='Ссылки на комменты', unique=True),)

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

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

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

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

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

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

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

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

@ -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):
name = "courses"
name = "course_service.courses"
label = 'courses'
verbose_name = "Курсы"
def ready(self):
import courses.signals
pass

@ -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': 'Урок',

@ -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='Преподаватели'),
),
]

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

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

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

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

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

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

@ -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):
name = "maps"
name = "course_service.maps"
label = 'maps'
verbose_name = "Отображение курсов"

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

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

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

@ -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.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()

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

@ -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()

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

@ -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='Плательщик')),
],

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

@ -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(
'Ваш платёж прошёл успешно',

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

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

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

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

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

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

@ -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)
Loading…
Cancel
Save