diff --git a/access/migrations/0002_auto_20171214_2034.py b/access/migrations/0002_auto_20171214_2034.py new file mode 100644 index 0000000..386156a --- /dev/null +++ b/access/migrations/0002_auto_20171214_2034.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-12-14 20:34 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0001_initial'), + ] + + operations = [ + migrations.RenameField( + model_name='progress', + old_name='template', + new_name='route', + ), + migrations.AlterUniqueTogether( + name='progress', + unique_together=set([('user', 'route')]), + ), + ] diff --git a/access/migrations/0003_auto_20171214_2049.py b/access/migrations/0003_auto_20171214_2049.py new file mode 100644 index 0000000..b9dc237 --- /dev/null +++ b/access/migrations/0003_auto_20171214_2049.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-12-14 20:49 +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 = [ + ('access', '0002_auto_20171214_2034'), + ] + + operations = [ + migrations.AlterField( + model_name='progress', + name='teacher', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teacher_progress', to=settings.AUTH_USER_MODEL, verbose_name='Преподователь по умолчанию'), + ), + migrations.AlterField( + model_name='progress', + name='user', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Студент'), + preserve_default=False, + ), + ] diff --git a/access/models/other.py b/access/models/other.py index 715093f..5e9037c 100644 --- a/access/models/other.py +++ b/access/models/other.py @@ -49,44 +49,17 @@ class Account(models.Model): 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) - template = models.CharField(max_length=15, verbose_name='Токен прохождения') + related_name='teacher_progress', null=True, blank=True) + user = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name='Студент') + route = models.CharField(max_length=15, verbose_name='Токен прохождения') def __str__(self): return '%s' % (self.user.email,) - @transaction_decorator - def add_vertex(self, vertex): - PivotProgressVertex.objects.create( - progress=self, - vertex=vertex, - - ) - self.progress_list.add(vertex) - parent = vertex.vertex_set.first() if vertex.vertex_set.all().exists() else None - if parent: - children_ids = self.progress_list.values_list('id', flat=True) - _flt = ~Q(id__in=children_ids) - if parent.children.filter(_flt).exists(): - self.progress_list.add(parent) - - def get_template(self): - return self.template if self.template else self.course.route - - def is_finish(self): - return self.get_template().is_finish(self.user) - - def get_objects_in_progress(self): - return self.get_template().get_active_objects(self.user) - - def is_access(self, vertex): - return vertex in self.progress_list.all() or vertex in self.get_objects_in_progress() - class Meta: verbose_name = 'Прогресс пользователя' verbose_name_plural = 'Прогресс пользователя' - unique_together = (("user", "template"),) + unique_together = (("user", "route"),) class PivotProgressVertex(models.Model): diff --git a/access/models/user.py b/access/models/user.py index c68ea18..71e993d 100644 --- a/access/models/user.py +++ b/access/models/user.py @@ -119,9 +119,6 @@ class User(AbstractBaseUser, PermissionsMixin): def get_short_name(self): return self.first_name - def get_thread(self): - Thread.objects.get(key="""user_%s""" % self.id,) - class Meta: verbose_name = _('Пользователь') verbose_name_plural = _('Пользователи') \ No newline at end of file diff --git a/achievements/models.py b/achievements/models.py index e92b8a0..5308e25 100644 --- a/achievements/models.py +++ b/achievements/models.py @@ -56,8 +56,11 @@ class Diploma(models.Model): user = models.ForeignKey(to=settings.AUTH_USER_MODEL) def __str__(self): - return 'Студенту %s за курс %s' % (self.user.username, self.template.course.title) + return 'Студенту %s за курс %s' % ( + self.user.get_full_name(), + self.template.course.title if self.template else "не связан с курсом", + ) class Meta: verbose_name = 'Диплом' - verbose_name_plural = 'Дипломы' \ No newline at end of file + verbose_name_plural = 'Дипломы' diff --git a/course_service/courses/models.py b/course_service/courses/models.py index 6025597..66413f0 100755 --- a/course_service/courses/models.py +++ b/course_service/courses/models.py @@ -90,59 +90,6 @@ class Course(models.Model): def __str__(self): return self.title - def get_maps(self, user): - return user.progress_set.get(course=self).get_template().get_maps() - - def get_vertexes(self, vertex_type=None): - - if vertex_type: - return self.vertex_set.filter(content_type__model=vertex_type, course=self) - return self.vertex_set.filter(course=self) - - def get_statistic(self): - """ - Минималистичная статистика по уроку, - количество тем, уроков, домашек. - """ - - return { - "topic_count": self.get_vertexes('topic').count(), - "tutorial_count": self.get_vertexes('tutorial').count(), - "task_count": self.get_vertexes('task').count(), - } - - def get_first(self, vertex_model_list=None): - if vertex_model_list is None: - vertex_model_list = ['topic', 'tutorial', 'task'] - - else: - for i in vertex_model_list: - if i not in ['topic', 'tutorial', 'task']: - raise ValueError('undefined model: ' + i) - - vertex = Vertex.objects.get(id=self.coursemap_set.get(sort=0).get_first()) - - if vertex.content_type.model in vertex_model_list: - return vertex - - return vertex.get_next(vertex_model_list) - - def get_last(self, vertex_model_list=None): - if vertex_model_list is None: - vertex_model_list = ['topic', 'tutorial', 'task'] - - else: - for i in vertex_model_list: - if i not in ['topic', 'tutorial', 'task']: - raise ValueError('undefined model: ' + i) - - vertex = Vertex.objects.get(id=self.coursemap_set.get(sort=0).get_last()) - - if vertex.content_type.model in vertex_model_list: - return vertex - - return vertex.get_previous(vertex_model_list) - objects = CourseManager() class Meta: @@ -180,61 +127,6 @@ class Vertex(models.Model): def __str__(self): return self.title - def get_next(self, route): - return route.get_first().get_next(self) - - def get_previous(self, vertex_model_list=None): - if vertex_model_list is None: - vertex_model_list = ['topic', 'tutorial', 'task'] - - else: - for i in vertex_model_list: - if i not in ['topic', 'tutorial', 'task']: - raise ValueError('undefined model: ' + i) - - vertex_id = CourseMap.objects.get(course=self.course).get_previous(self.id) - vertex = Vertex.objects.get(id=int(vertex_id), ) - - if vertex.content_type.model in vertex_model_list: - return vertex - return vertex.get_previous(vertex_model_list) - - def is_more(self, vertex) -> bool: - if not self.course == vertex.course: - raise ValueError('Vertexes of different course') - course_map = CourseMap.objects.get(course=self.course) - return course_map.map_to_list().index(self.id) > course_map.map_to_list().index(vertex.id) - - def check_vertex(self, user) -> bool: - if self.free: - return True - - if not user.is_authenticated: - return False - - try: - progress = self.course.progress_set.get(user=user) - except ObjectDoesNotExist: - return False - - return progress.is_access(self) - - def get_number(self, vertex_type=None): - """ - Возврощает порядковый номер узла с определённым типом. - Пример мы хотим определит какой по счёту теме принадлежит конкретно взятый урок. - """ - vertex_list = list(self.course.get_vertexes(vertex_type)) - try: - res = vertex_list.index(self) - except ValueError: - parents = self.vertex_set.all() - if parents.count() == 1: - res = vertex_list.index(parents[0]) - else: - res = None - return res - class Meta: verbose_name = "Урок" verbose_name_plural = "Уроки" diff --git a/course_service/maps/api.py b/course_service/maps/api.py index 7e67ba0..ef4edf0 100644 --- a/course_service/maps/api.py +++ b/course_service/maps/api.py @@ -5,3 +5,12 @@ class OutApiRoute: @staticmethod def change_id(id: int) -> str: return CourseRoute.objects.get(id=id).out_key + + @staticmethod + def get_route_matrix(out_key): + map_list = [i.map_course for i in CourseRoute.objects.get(out_key=out_key).pivotcoursemap_set.all()] + return [[j.vertex.token for j in i.pivotvertex_set.all()] for i in map_list] + + @staticmethod + def get_route(out_key): + return CourseRoute.objects.get(out_key=out_key) \ No newline at end of file diff --git a/course_service/maps/migrations/0002_auto_20171215_1329.py b/course_service/maps/migrations/0002_auto_20171215_1329.py new file mode 100644 index 0000000..f77c101 --- /dev/null +++ b/course_service/maps/migrations/0002_auto_20171215_1329.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-12-15 13:29 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('courses', '0003_auto_20171214_1513'), + ('maps', '0001_initial'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='coursemap', + unique_together=set([('course', 'name')]), + ), + ] diff --git a/course_service/maps/models.py b/course_service/maps/models.py index 57c7cb6..851a33b 100644 --- a/course_service/maps/models.py +++ b/course_service/maps/models.py @@ -13,19 +13,6 @@ class CourseRoute(models.Model): name = models.CharField(max_length=255, verbose_name='Имя шаблона', blank=True, null=True) is_template = models.BooleanField(default=True, verbose_name='Может ли быть использован как шаблон') - def is_finish(self, user): - return len([1 for i in self.pivotcoursemap_set.all() if i.map_course.is_finish(user)]) > 0 - - def get_active_objects(self, user): - return (i.map_course.get_active_object(user) for i in self.pivotcoursemap_set.all()) \ - if not self.is_finish(user) else [] - - def get_maps(self): - return (i.map_course for i in self.pivotcoursemap_set.all()) - - def get_first(self): - return self.pivotcoursemap_set.first().map_course - def __str__(self): return self.name @@ -41,74 +28,13 @@ class CourseMap(models.Model): course = models.ForeignKey(to='courses.Course', verbose_name='К какому курсу привязан') name = models.CharField(max_length=255, verbose_name="Имя прохождения", default="Линейное прохождение") - def get_next(self, vertex): - pivot = self.pivotvertex_set.get(vertex=vertex) - pivots = self.pivotvertex_set.filter(sort__gt=pivot.sort).exclude(vertex__content_type__model='topic') - return pivots.first().vertex if pivots.exists() else None - - @transaction_decorator - def add_vertex(self, vertex, sort): - if sort > self.pivotvertex_set.count() + 1: - raise ValueError("list index out of range") - for i in self.pivotvertex_set.filter(sort__gte=sort): - i.sort += 1 - i.save() - - pivot = PivotVertex.objects.create(vertex=vertex, sort=sort, map_course=self) - pivot.save() - return pivot - - def get_objects(self, vertex=None): - if vertex and not self.course == vertex.course: - raise MapTypeError('''Переданный узел принадлежит курсу "%s", а должен принадлежать курсу "%s"''' - % (self.course.title, vertex.course.title)) - - full_list = [i.vertex for i in self.pivotvertex_set.all()] - - if vertex: - idx = full_list.index(vertex) + 1 - return full_list[:idx] - - return full_list - - def get_tree(self, serializer=None): - from course_service.courses.serializers import MiniVertexSerializer - serializer = serializer if serializer is not None else MiniVertexSerializer - - def helper(v_list): - new_list = [] - for i in v_list: - val = serializer(i).data - new_list.append(val) - if i.children.all().count(): - val['children'] = helper(i.children.all()) - return new_list - - return helper([i.vertex for i in self.pivotvertex_set.filter(vertex__vertex__isnull=True)]) - - def get_difference(self, user) -> list: - success_list = user.progress_set.get(course=self.course).progress_list.all() - all_vertex = [i.vertex for i in self.pivotvertex_set.all()] - res = list(set(all_vertex).difference(set(success_list))) - return res - - def is_finish(self, user) -> bool: - return self.get_difference(user) == [] - - def get_active_object(self, user): - try: - return self.pivotvertex_set.exclude( - vertex__content_type__model='topic' - ).filter(vertex__in=self.get_difference(user)).first().vertex - except AttributeError: - return None - def __str__(self): return '''Линейное прохождение по курсу "%s"''' % self.course.title class Meta: verbose_name = 'Карта линейного прохождения курсов' verbose_name_plural = 'Карты линейного прохождения курсов' + unique_together =('course', 'name',) class PivotCourseMap(models.Model): diff --git a/csv/load_bills.py b/csv/load_bills.py index c08eeef..e0201ab 100644 --- a/csv/load_bills.py +++ b/csv/load_bills.py @@ -12,10 +12,13 @@ django.setup() from yandex_money.models import Payment from finance.models import Bill, Invoice +from access.models.other import Progress from course_service.courses.models import Course if __name__ == '__main__': + Payment.objects.all().delete() Bill.objects.all().delete() + Progress.objects.all().delete() with open('./finance/bill.csv') as bill_csv: bill_reader = csv.DictReader(bill_csv) for row in bill_reader: @@ -54,20 +57,19 @@ if __name__ == '__main__': real_price = None inv = Invoice.objects.create(bill=bill, method=method, price=price, real_price=real_price, **row) + inv.date = row['date'] + inv.save() - if method == 'Y': - yandex_pay, _is_create = Payment.objects.get_or_create( + if method == 'Y' and not row['status'] == 'W': + row['yandex_pay'], _is_create = Payment.objects.get_or_create( order_amount=price, order_number=inv.id, customer_number=bill.user.id, user=bill.user, cps_email=bill.user.email, - shop_amount=inv.real_price, + shop_amount=real_price, + status='Processed' if 'P' else ('Success' if 'F' else 'Fail') ) - inv.yandex_pay = yandex_pay - - inv.date = row['date'] - inv.save() except IntegrityError: - pass + pass \ No newline at end of file diff --git a/csv/load_student_teachers_threads.py b/csv/load_comments.py similarity index 92% rename from csv/load_student_teachers_threads.py rename to csv/load_comments.py index 123a6c8..067c343 100644 --- a/csv/load_student_teachers_threads.py +++ b/csv/load_comments.py @@ -10,13 +10,11 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings") django.setup() 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 +from storage.models import Comment +from access.models.other import PivotProgressVertex if __name__ == '__main__': csv.field_size_limit(500 * 1024 * 1024) - ct = ContentType.objects.get(app_label='courses', model='vertex') with open('./management/comment.csv') as comment_csv: comment_reader = csv.DictReader(comment_csv) for row in comment_reader: diff --git a/csv/load_courses.py b/csv/load_courses.py index ce093b2..5f3c605 100644 --- a/csv/load_courses.py +++ b/csv/load_courses.py @@ -52,7 +52,6 @@ if __name__ == '__main__': with open('./course/vertex.csv') as vertex_csv: vertex_reader = csv.DictReader(vertex_csv) - sort = 0 for row in vertex_reader: row = dict(row) model_type = row.pop('type', None) @@ -69,6 +68,7 @@ if __name__ == '__main__': if model_type == 'topic': course = Course.objects.get(id=row.pop('course', None)) + map_obj = CourseMap.objects.get(course=course) Topic.objects.create( id=pk, icon=row.pop('icon', None), @@ -79,6 +79,7 @@ if __name__ == '__main__': try: topic_id = row.pop('topic', None) + last_pivot = PivotVertex.objects.filter(map_course=map_obj).last() if model_type == 'tutorial': topic = Topic.objects.get(id=topic_id) small_vertex = Vertex.objects.create( @@ -90,8 +91,11 @@ if __name__ == '__main__': title=title, token=''.join(random.choice(string.ascii_letters) for x in range(15)) ) - PivotVertex.objects.create(map_course=map_obj, vertex=small_vertex, sort=sort) - sort += 1 + PivotVertex.objects.create( + map_course=map_obj, + vertex=small_vertex, + sort=last_pivot.sort+1 if last_pivot else 1, + ) if model_type == 'task': topic = Topic.objects.get(id=topic_id) @@ -104,7 +108,10 @@ if __name__ == '__main__': valid_type=1, token=''.join(random.choice(string.ascii_letters) for x in range(15)) ) - PivotVertex.objects.create(map_course=map_obj, vertex=small_vertex, sort=sort) - sort += 1 + PivotVertex.objects.create( + map_course=map_obj, + vertex=small_vertex, + sort=last_pivot.sort+1 if last_pivot else 1, + ) except Topic.DoesNotExist: pass \ No newline at end of file diff --git a/csv/load_perm.py b/csv/load_perm.py index 39637c2..4ff64fd 100644 --- a/csv/load_perm.py +++ b/csv/load_perm.py @@ -9,53 +9,60 @@ django.setup() from django.contrib.auth.models import Group from django.contrib.auth import get_user_model -from access.models.other import Progress +from access.models.other import Progress, PivotProgressVertex from course_service.courses.models import Vertex, Course +from course_service.maps.api import OutApiRoute if __name__ == '__main__': Progress.objects.all().delete() with open('./access/progress.csv') as progress_csv: progress_reader = csv.DictReader(progress_csv) for row in progress_reader: - course = Course.objects.get(id=row['course']) + route = Course.objects.get(id=row['course']).route.out_key vertex = None try: + pk=None if row['type'] == 'lesson': - get_id = int(row['last_success_obj']) + 500 - vertex = Vertex.objects.get(content_type__model='tutorial', object_id=row['last_success_obj'], - course=course) + pk = int(row['last_success_obj']) + 600 elif row['type'] == 'homework': - get_id = int(row['last_success_obj']) +50 - vertex = Vertex.objects.get(content_type__model='task', object_id=row['last_success_obj'], - course=course) + pk = int(row['last_success_obj']) + 50 elif row['type'] == 'exam': - get_id = int(row['last_success_obj']) - vertex = Vertex.objects.get(content_type__model='task', object_id=str(get_id), course=course) + pk = int(row['last_success_obj']) + + vertex = Vertex.objects.get(id=pk).token except Vertex.DoesNotExist: pass + api = OutApiRoute + + list_vertex = OutApiRoute.get_route_matrix(route)[0] + try: + user = get_user_model().objects.get(id=row['user']) if vertex is not None: - user = get_user_model().objects.get(id=row['user']) p, c = Progress.objects.get_or_create( user=user, - course=course, + route=route, ) - obj_list = list(course.route.get_maps())[0].get_objects(vertex) - g = Group.objects.get(name='students') - g.user_set.add(user) - if c: - p.progress_list.add(*obj_list) + for i in list_vertex: + PivotProgressVertex( + progress=p, + vertex=i, + status=2, + ) else: p, c = Progress.objects.get_or_create( - user=get_user_model().objects.get(id=row['user']), - course=course, + user=user, + route=route, ) + g = Group.objects.get(name='students') + g.user_set.add(user) + except get_user_model().DoesNotExist: print(row['user']) diff --git a/finance/signals.py b/finance/signals.py index 384c34d..2010d64 100644 --- a/finance/signals.py +++ b/finance/signals.py @@ -5,6 +5,9 @@ from yandex_money.models import Payment from finance.models import Invoice from access.models.other import Progress +from course_service.maps.api import OutApiRoute + +api = OutApiRoute @receiver(pre_save, sender=Invoice) @@ -25,16 +28,16 @@ def invoice_signal(instance, **kwargs): if instance.status == 'F': if instance.is_open: Progress.objects.get_or_create( - template=instance.bill.route, + route=instance.bill.route, user=instance.bill.user, ) msg = EmailMessage( 'Ваш платёж прошёл успешно', '''Вам открыт доступ к курсу "%s", вы можете перейти по ссылке и ознакомиться с материалами https://go.skillbox.ru/course/%s''' - % (instance.bill.course.title, instance.bill.course.slug), + % (api.get_route(instance.bill.route).name, api.get_route(instance.bill.route).course.slug), 'robo@skillbox.ru', - [instance.yandex_pay.cps_email], + [instance.bill.user.email], cc=[instance.bill.opener.email], reply_to=[instance.bill.opener.email], ) @@ -57,7 +60,7 @@ def invoice_signal(instance, **kwargs): решить проблему самостоятельно, ответьте на это письмо, постарайтесь подробно описать последовательность действий, которая привела к ошибке""" - % (instance.bill.user.get_full_name(), instance.bill.route.title, instance.id), + % (instance.bill.user.get_full_name(), api.get_route(instance.bill.route).course.title, instance.id), instance.bill.opener.email, reply_to=["it@skillbox.ru"] ) diff --git a/storage/migrations/0003_comment_date.py b/storage/migrations/0003_comment_date.py new file mode 100644 index 0000000..dea589c --- /dev/null +++ b/storage/migrations/0003_comment_date.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-12-15 15:32 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('storage', '0002_auto_20171214_1908'), + ] + + operations = [ + migrations.AddField( + model_name='comment', + name='date', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='Дата коментария'), + preserve_default=False, + ), + ] diff --git a/storage/models.py b/storage/models.py index ad2478b..fc1b088 100755 --- a/storage/models.py +++ b/storage/models.py @@ -39,6 +39,7 @@ class Comment(models.Model): text = models.TextField(default="", verbose_name="Текст комментария") files = models.ManyToManyField(to=File, blank=True, verbose_name='Файлы') key = models.SlugField(unique=True, verbose_name="Получения комментария по ключу") + date = models.DateTimeField(auto_now_add=True, verbose_name="Дата коментария") def __str__(self): return '%s' % self.key