diff --git a/access/models.py b/access/models.py index 14c590b..5c7a5c3 100755 --- a/access/models.py +++ b/access/models.py @@ -1,6 +1,12 @@ # encoding=utf-8 import random import string +import json + +from celery.result import AsyncResult +from django.contrib.contenttypes.models import ContentType +from django_celery_results.models import TaskResult + from courses.models import Vertex, Course from storage.models import Storage from django.core.mail import send_mail @@ -14,6 +20,7 @@ from django.conf import settings from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from lms.global_decorators import transaction_decorator +from journals.models import Journal, Thread, ACTION_CHOICES class Invite(models.Model): @@ -79,7 +86,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, - id=self.last().id +1 if self.last() else 0, + id=self.last().id + 1 if self.last() else 0, is_superuser=is_superuser, date_joined=date_joined, last_login=last_login, **extra_fields) if not password: @@ -99,15 +106,20 @@ class CustomUserManager(BaseUserManager): user.groups.add(Group.objects.get(name=group)) if is_send: - invite = Invite.objects.create(owner=user, hash=''.join(random.choice(string.ascii_letters) for x in range(15))) - send_mail( - subject='Спасибо за регистрацию', - message=''' - Вы были успешны зарегистрированны на портале go.skillbox.ru - для подтверждения регистрации перейдите по ссылке - https://go.skillbox.ru/api/v1/users/registration/?hash=''' + invite.hash, - from_email='robo@skillbox.ru', - recipient_list=[user.email], + invite = Invite.objects.create(owner=user, + hash=''.join(random.choice(string.ascii_letters) for x in range(15))) + body = { + "subject": 'Спасибо за регистрацию', + "message": ''' + Вы были успешны зарегистрированны на портале go.skillbox.ru + для подтверждения регистрации перейдите по ссылке + https://go.skillbox.ru/api/v1/users/registration/?hash=''' + invite.hash, + "from_email": 'robo@skillbox.ru', + "recipient_list": [user.email], + } + + tasks = send_mail( + **body ) return user @@ -132,8 +144,8 @@ class User(AbstractBaseUser, PermissionsMixin): help_text='Определяет активен ли пользователь в системе. Снимите флаг, ' 'вместо удаления пользователя.') is_blocked = models.BooleanField(verbose_name='заблочен', default=False, - help_text='Определяет заблокирован ли пользователь. Поставьте флаг, ' - 'если знаете, что это нехороший человек.') + help_text='Определяет заблокирован ли пользователь. Поставьте флаг, ' + 'если знаете, что это нехороший человек.') objects = CustomUserManager() diff --git a/courses/admin.py b/courses/admin.py index 175c446..ddbf93a 100755 --- a/courses/admin.py +++ b/courses/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin from courses.models import Course, Skills, Achievements, SkillJ,\ - CourseMap, Topic, Task, Vertex, Diploma, Tutorial + CourseMap, Topic, Task, Vertex, Diploma, Tutorial, DiplomaGen admin.site.register(CourseMap) admin.site.register(Topic) @@ -12,4 +12,5 @@ admin.site.register(Course) admin.site.register(Skills) admin.site.register(Achievements) admin.site.register(SkillJ) -admin.site.register(Diploma) \ No newline at end of file +admin.site.register(Diploma) +admin.site.register(DiplomaGen) \ No newline at end of file diff --git a/courses/migrations/0003_auto_20171023_1037.py b/courses/migrations/0003_auto_20171023_1037.py new file mode 100644 index 0000000..cf10a12 --- /dev/null +++ b/courses/migrations/0003_auto_20171023_1037.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-10-23 10:37 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('courses', '0002_auto_20171019_1149'), + ] + + operations = [ + migrations.CreateModel( + name='DiplomaGen', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('template', models.URLField(verbose_name='Путь до шаблона')), + ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Course')), + ], + options={ + 'verbose_name': 'Генератор дипломов', + 'verbose_name_plural': 'Генераторы дипловов', + }, + ), + migrations.RemoveField( + model_name='diploma', + name='course', + ), + migrations.AddField( + model_name='diploma', + name='template', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='courses.DiplomaGen', verbose_name='Использовать шаблон'), + ), + ] diff --git a/courses/models.py b/courses/models.py index 46281f3..8595ab8 100755 --- a/courses/models.py +++ b/courses/models.py @@ -111,10 +111,10 @@ class Course(models.Model): class Skills(models.Model): - title = models.CharField(verbose_name=u'Наименование', max_length=255) - color = models.CharField(verbose_name=u'Цвет', max_length=255) - icon = models.ImageField(verbose_name=u'Большая картинка', upload_to='skills', null=True, help_text='65x65') - description = models.TextField(verbose_name=u'Описание', blank=True) + title = models.CharField(verbose_name='Наименование', max_length=255) + color = models.CharField(verbose_name='Цвет', max_length=255) + icon = models.ImageField(verbose_name='Большая картинка', upload_to='skills', null=True, help_text='65x65') + description = models.TextField(verbose_name='Описание', blank=True) def __str__(self): return '%s' % self.title @@ -124,14 +124,14 @@ class Skills(models.Model): class SkillJ(models.Model): - skill = models.ForeignKey(to="Skills", verbose_name=u'Навык') - lesson = models.ForeignKey(to="Vertex", verbose_name=u'Урок') + skill = models.ForeignKey(to="Skills", verbose_name='Навык') + lesson = models.ForeignKey(to="Vertex", verbose_name='Урок') def __str__(self): return '%s' % self.skill class Meta: - verbose_name = u'Размер навыка' - verbose_name_plural = u'Размеры навыков' + verbose_name = 'Размер навыка' + verbose_name_plural = 'Размеры навыков' class Achievements(models.Model): @@ -143,17 +143,29 @@ class Achievements(models.Model): return 'Студенту %s за курс %s' % (self.user.username, self.course.title) class Meta: - verbose_name = u'Достижение' - verbose_name_plural = u'Достижения' + verbose_name = 'Достижение' + verbose_name_plural = 'Достижения' -class Diploma(models.Model): - icon = models.ImageField(verbose_name=u'Иконка', upload_to='diplomas') +class DiplomaGen(models.Model): course = models.ForeignKey(to=Course) + template = models.URLField(verbose_name="Путь до шаблона") + + def __str__(self): + return 'Шаблон можно найти по адресу: %s, диплом выдаётся за курс %s' % (self.template, self.course.title) + + class Meta: + verbose_name = 'Генератор дипломов' + verbose_name_plural = 'Генераторы дипловов' + + +class Diploma(models.Model): + icon = models.ImageField(verbose_name='Иконка', upload_to='diplomas') + template = models.ForeignKey(to=DiplomaGen, verbose_name='Использовать шаблон', blank=True, null=True) user = models.ForeignKey(to=settings.AUTH_USER_MODEL) def __str__(self): - return 'Студенту %s за курс %s' % (self.user.username, self.course.title) + return 'Студенту %s за курс %s' % (self.user.username, self.template.course.title) class Meta: verbose_name = 'Диплом' diff --git a/courses/views.py b/courses/views.py index 2cc36bf..c40e3db 100644 --- a/courses/views.py +++ b/courses/views.py @@ -6,7 +6,6 @@ from access.serializers import ExtraPrivilegeSerializer from courses.models import Course, Vertex from access.models import Progress, ExtraPrivilege from courses.serializers import CourseDetailSerializer, CourseListSerializer, VertexSerializer, CourseTreeSerializer -from finance.models import Bill class TreeView(APIView): @@ -66,7 +65,7 @@ class CourseListView(APIView): if course.public: course_serialize = CourseListSerializer(course).data course_serialize['is_mine'] = False - if request.user.is_authenticated() and Bill.objects.filter(service__course=course, user=request.user, status='F').exists(): + if request.user.is_authenticated() and Progress.objects.filter(course=course, user=request.user).exists(): course_serialize['is_mine'] = True res.append(course_serialize) diff --git a/finance/admin.py b/finance/admin.py index 1af0b47..04b13c6 100755 --- a/finance/admin.py +++ b/finance/admin.py @@ -1,7 +1,7 @@ # coding=utf-8 from django.contrib import admin -from finance.models import Bill, Price +from finance.models import Bill, Invoice admin.site.register(Bill) -admin.site.register(Price) \ No newline at end of file +admin.site.register(Invoice) \ No newline at end of file diff --git a/finance/models.py b/finance/models.py index 54253ca..7d09a77 100755 --- a/finance/models.py +++ b/finance/models.py @@ -4,33 +4,26 @@ from django.conf import settings from courses.models import Course, Vertex -class Price(models.Model): - # Цены - public = models.BooleanField(verbose_name='Опубликовать', default=False) - title = models.CharField(verbose_name='Услуга', max_length=255, help_text=u'Будет показано пользователям') - cost = models.IntegerField(verbose_name='Цена') - course = models.ForeignKey(to=Course, verbose_name='Курс', null=True, blank=True) - vertexes = models.ManyToManyField(to=Vertex, verbose_name='Список всех узлов') - description = models.TextField(verbose_name='Описание', help_text='Будет показано менеджерам') - by_time = models.IntegerField(verbose_name='Дней доступа', blank=True, null=True) +class Bill(models.Model): + course = models.ForeignKey(to=Course, verbose_name='Курс', blank=True, null=True) + user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='Плательщик', related_name=u'bill_user') + opener = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='Ответственный сотрудник', null=True) + comment = models.TextField(verbose_name=u'Комментарий продавца', help_text=u'Будет показано пользователю', + blank=True, editable=False) + description = models.TextField(verbose_name='Внутренняя заметка', default='') def __str__(self): - return '%s / %s %s' % (self.course.title, self.title, self.cost) + return '%s:%s %s' % (self.id, self.course, self.user) + + def get_full_price(self): + return sum([i.price for i in self.invoice_set.all()]) class Meta: - verbose_name = u'Услуга' - verbose_name_plural = u'Услуги' - ordering = ['-id'] + verbose_name = 'Счет' + verbose_name_plural = 'Счета' -class Bill(models.Model): - BILL_STATUSES = ( - ('W', 'Ожидание согласия'), - ('P', 'На оплате'), - ('F', 'Оплачен'), - ('C', 'Отклонен'), - ('H', 'Сгорел') - ) +class Invoice(models.Model): BILL_METHOD = ( ('C', 'Наличные'), ('H', 'JustClick'), @@ -38,26 +31,27 @@ class Bill(models.Model): ('S', 'SimplePay'), ('Y', 'YandexKassa') ) + BILL_STATUSES = ( + ('W', 'Ожидание согласия'), + ('P', 'На оплате'), + ('F', 'Оплачен'), + ('C', 'Отклонен'), + ('H', 'Сгорел') + ) status = models.CharField(verbose_name='Статус', max_length=1, default='W', choices=BILL_STATUSES) - bill_method = models.CharField(verbose_name='Способ оплаты', max_length=2, default='Y', choices=BILL_METHOD) price = models.CharField(verbose_name='Сумма', max_length=255, null=True, blank=True) real_price = models.CharField(verbose_name='Полученная сумма', max_length=255, null=True, blank=True, help_text='Сумма, минус комиссия') - service = models.ForeignKey(to=Price, verbose_name='Оплачиваемая услуга') - inside_data = models.TextField(verbose_name='Данные проверки', default='', blank=True, editable=False) - user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='Плательщик', related_name=u'bill_user') - opener = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='Ответственный сотрудник', null=True) + bill_method = models.CharField(verbose_name='Способ оплаты', max_length=2, default='Y', choices=BILL_METHOD) key = models.CharField(verbose_name='Ключ платежа', blank=True, max_length=255, default='', editable=False) out_id = models.CharField(verbose_name='ID внешнего заказа', max_length=100, blank=True, default='', editable=False) comment = models.TextField(verbose_name=u'Комментарий продавца', help_text=u'Будет показано пользователю', blank=True, editable=False) - description = models.TextField(verbose_name='Внутренняя заметка', default='') + bill = models.ForeignKey(to=Bill, verbose_name="Связный счёт") def __str__(self): - return '%s:%s %s' % (self.id, self.get_status_display(), self.user) + return '%s:%s %s' % (self.id, self.get_status_display(), self.bill.user) class Meta: - verbose_name = u'Счет' - verbose_name_plural = u'Счета' - - + verbose_name = 'Платёж' + verbose_name_plural = 'Платежи' diff --git a/journals/admin.py b/journals/admin.py old mode 100755 new mode 100644 index a82b12b..58ebf6e --- a/journals/admin.py +++ b/journals/admin.py @@ -1,9 +1,5 @@ -#! coding: utf-8 - -from django.contrib import admin - -from journals.models import Thread, Journal - - -admin.site.register(Thread) +from django.contrib import admin +from journals.models import Thread, Journal + +admin.site.register(Thread) admin.site.register(Journal) \ No newline at end of file diff --git a/journals/default_threads.py b/journals/default_threads.py index a84bf9b..9918231 100644 --- a/journals/default_threads.py +++ b/journals/default_threads.py @@ -12,19 +12,6 @@ from lms.global_decorators import transaction_decorator @transaction_decorator def main_threads(): - print("Инициализация основных тредов") - res = {} - - support_list = [ - 'kate.gazukina@skillbox.ru', - 'katerina.ragozina@skillbox.ru', - ] - - admin_list = [ - 'andrey.korolev@skillbox.ru', - 'pavel.matveev@skillbox.ru', - 'anton.kopylov@skillbox.ru', - ] admin_thread, _is_create = Thread.objects.get_or_create( key='Admin', @@ -32,61 +19,61 @@ def main_threads(): is_staff=True, ) - for i in get_user_model().objects.filter(email__in=admin_list): + for i in get_user_model().objects.filter(is_superuser=True): admin_thread.subscribers.add(i) - management_thread, _is_create = Thread.objects.get_or_create( - key='Project_management', - text='Тред для проджект-менеджеров, сюда падает статистика разного рода', - is_staff=True, - ) - - management_thread.parent.add(admin_thread) - - support_thread, _is_create = Thread.objects.get_or_create( - key='Support', - text='Тред сапортов, занимаются поддержкой клиента', - is_staff=True, - ) - - for i in get_user_model().objects.filter(email__in=support_list): - support_thread.subscribers.add(i) - - support_thread.parent.add(admin_thread) - - res['library_thread'], _is_create = Thread.objects.get_or_create( - key='Library', - text='Тред <<Библиотека>> сюда прилетает вся инфа по статьям', - is_staff=True, - ) - - res['library_thread'].parent.add(management_thread) - - course_thread, _is_create = Thread.objects.get_or_create( - key='Course_thread', - text='Тред курсов', - is_staff=True, - ) - - course_thread.parent.add(management_thread) - - start_and_end_thread, _is_create = Thread.objects.get_or_create( - key='Start_and_end', - text='Тред начала и завершения прохождения какого-либо этапа обучения', - is_staff=True, - ) - - start_and_end_thread.parent.add(course_thread) - - res['task_thread'], _is_create = Thread.objects.get_or_create( - key='Tasks', - text='Сюда пободают все переписки студентов и преподов', - is_staff=True, - ) - - res['task_thread'].parent.add(support_thread) - - return res + # management_thread, _is_create = Thread.objects.get_or_create( + # key='Project_management', + # text='Тред для проджект-менеджеров, сюда падает статистика разного рода', + # is_staff=True, + # ) + # + # management_thread.parent.add(admin_thread) + # + # support_thread, _is_create = Thread.objects.get_or_create( + # key='Support', + # text='Тред сапортов, занимаются поддержкой клиента', + # is_staff=True, + # ) + # + # for i in get_user_model().objects.filter(email__in=support_list): + # support_thread.subscribers.add(i) + # + # support_thread.parent.add(admin_thread) + # + # res['library_thread'], _is_create = Thread.objects.get_or_create( + # key='Library', + # text='Тред <<Библиотека>> сюда прилетает вся инфа по статьям', + # is_staff=True, + # ) + # + # res['library_thread'].parent.add(management_thread) + # + # course_thread, _is_create = Thread.objects.get_or_create( + # key='Course_thread', + # text='Тред курсов', + # is_staff=True, + # ) + # + # course_thread.parent.add(management_thread) + # + # start_and_end_thread, _is_create = Thread.objects.get_or_create( + # key='Start_and_end', + # text='Тред начала и завершения прохождения какого-либо этапа обучения', + # is_staff=True, + # ) + # + # start_and_end_thread.parent.add(course_thread) + # + # res['task_thread'], _is_create = Thread.objects.get_or_create( + # key='Tasks', + # text='Сюда пободают все переписки студентов и преподов', + # is_staff=True, + # ) + # + # res['task_thread'].parent.add(support_thread) + # + # return res if __name__ == '__main__': diff --git a/journals/migrations/0003_auto_20171023_1208.py b/journals/migrations/0003_auto_20171023_1208.py new file mode 100644 index 0000000..ca612c5 --- /dev/null +++ b/journals/migrations/0003_auto_20171023_1208.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-10-23 12:08 +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): + + dependencies = [ + ('journals', '0002_thread_groups'), + ] + + operations = [ + migrations.AlterField( + model_name='journal', + name='action_type', + field=models.SmallIntegerField(choices=[(0, 'try'), (1, 'yes'), (2, 'no'), (3, 'favorite'), (4, 'watch'), (5, 'start'), (6, 'end'), (7, 'create'), (8, 'update'), (9, 'delete'), (10, 'send_mail')]), + ), + migrations.AlterField( + model_name='journal', + name='object_id', + field=models.CharField(max_length=255), + ), + migrations.AlterField( + model_name='journal', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Инициатор действия или тот, на ком действие инициируется'), + ), + migrations.AlterField( + model_name='thread', + name='groups', + field=models.ManyToManyField(blank=True, to='auth.Group', verbose_name='Группы подписчиков'), + ), + ] diff --git a/journals/migrations/0004_auto_20171023_1234.py b/journals/migrations/0004_auto_20171023_1234.py new file mode 100644 index 0000000..5dea8cf --- /dev/null +++ b/journals/migrations/0004_auto_20171023_1234.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-10-23 12:34 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0003_auto_20171023_1208'), + ] + + operations = [ + migrations.AlterField( + model_name='journal', + name='object_id', + field=models.PositiveIntegerField(), + ), + ] diff --git a/journals/models.py b/journals/models.py index e0ef49c..84adbe5 100755 --- a/journals/models.py +++ b/journals/models.py @@ -27,8 +27,10 @@ ACTION_CHOICES = ( # Новое API class Journal(models.Model): - thread = models.ForeignKey(to='Thread', verbose_name=u'Тред') - user = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name=u'Инициатор действия') + thread = models.ForeignKey(to='Thread', verbose_name='Тред') + user = models.ForeignKey( + to=settings.AUTH_USER_MODEL, verbose_name='Инициатор действия или тот, на ком действие инициируется' + ) content_type = models.ForeignKey(to=ContentType) extra_data = models.TextField(null=True, blank=True) object_id = models.PositiveIntegerField() @@ -45,8 +47,8 @@ class Thread(models.Model): text = models.TextField(default='', verbose_name=u'Описание треда') is_staff = models.BooleanField(default=False, verbose_name=u'Админская ли табличка') recurse_step = models.SmallIntegerField(default=0, verbose_name=u'Поле аптимизации поиска') - subscribers = models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name=u'Подписчики', blank=True) - groups = models.ManyToManyField(to=Group, verbose_name=u'Подписчики', blank=True) + subscribers = models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name='Подписчики', blank=True) + groups = models.ManyToManyField(to=Group, verbose_name='Группы подписчиков', blank=True) check_subscribe = models.BooleanField(default=True, verbose_name='Проверять ли подписки') parent = models.ManyToManyField(to='self', blank=True, symmetrical=False) x = models.SmallIntegerField(default=300) diff --git a/lms/admin.py b/lms/admin.py new file mode 100644 index 0000000..694323f --- /dev/null +++ b/lms/admin.py @@ -0,0 +1 @@ +from django.contrib import admin