From 2b9113a6f64a48e14140c27841f059660fdfd354 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 14 Nov 2017 10:45:22 +0300 Subject: [PATCH] =?UTF-8?q?=D0=AF=D0=BD=D0=B4=D0=B5=D0=BA=D1=81=20=D0=B4?= =?UTF-8?q?=D0=B5=D0=BD=D1=8C=D0=B3=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- courses/models.py | 6 +- csv/load_student_teachers_threads.py | 53 ++++++----- finance/migrations/0012_auto_20171110_1302.py | 20 +++++ finance/models.py | 1 - finance/serializers.py | 2 +- journals/default_threads.py | 15 ++-- .../migrations/0009_auto_20171110_1302.py | 20 +++++ .../migrations/0010_auto_20171110_1646.py | 25 ++++++ journals/models.py | 88 ++++++++++--------- journals/serilizers.py | 22 +++-- journals/urls.py | 3 +- journals/views.py | 22 ++--- requirements.txt | 21 ++++- 13 files changed, 205 insertions(+), 93 deletions(-) create mode 100644 finance/migrations/0012_auto_20171110_1302.py create mode 100644 journals/migrations/0009_auto_20171110_1302.py create mode 100644 journals/migrations/0010_auto_20171110_1646.py diff --git a/courses/models.py b/courses/models.py index 99ec6ed..b2f6136 100755 --- a/courses/models.py +++ b/courses/models.py @@ -264,9 +264,9 @@ class Vertex(models.Model): """ course = models.ForeignKey(to=Course) - title = models.CharField(verbose_name=u'Название', max_length=255) - free = models.BooleanField(default=True, verbose_name=u'Привилегии для узла не будут проверяться') - description = models.TextField(verbose_name=u'Описание', default='', blank=True, null=True) + 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) children = models.ManyToManyField(to='Vertex', blank=True) content_type = models.ForeignKey(to=ContentType) object_id = models.PositiveIntegerField() diff --git a/csv/load_student_teachers_threads.py b/csv/load_student_teachers_threads.py index e7a674c..d9c8acc 100644 --- a/csv/load_student_teachers_threads.py +++ b/csv/load_student_teachers_threads.py @@ -19,27 +19,44 @@ if __name__ == '__main__': comment_reader = csv.DictReader(comment_csv) for row in comment_reader: if row['type'] == 'task': + teacher_action = row['status'] == "Одобрено" or row['status'] == "Отклонено" owner = get_user_model().objects.get(email=row['owner__email']) sub = get_user_model().objects.get(email=row['sub__email']) + teacher = owner if teacher_action else sub + user = owner if not teacher_action else sub vertex = Vertex.objects.get(object_id=row['object_id'], content_type__model='task') - action = 0 - if row['status'] == "Одобрено": - action = 1 - child_thread, is_create = Thread.objects.get_or_create( - key="""user_%s__staff_%s""" % (sub.id, owner.id,) - ) - elif row['status'] == "Отклонено": - action = 2 - child_thread, is_create = Thread.objects.get_or_create( - key="""user_%s__staff_%s""" % (sub.id, owner.id,) - ) - else: - child_thread, is_create = Thread.objects.get_or_create( - key="""user_%s__staff_%s""" % (owner.id, sub.id,) + action = '' + int(not teacher_action)*"try" +\ + int(row['status'] == "Одобрено")*"yes" +\ + int(row['status'] == "Отклонено")*"no" + + try: + child_thread = Thread.objects.filter(subscribers=teacher).filter(subscribers=user)[0] + except IndexError: + child_thread = Thread.objects.create( + key="""user_%s__user_%s""" % (teacher.id, user.id,), + text="""Приватный диалог %s и %s""" % (teacher.email, user.email,), + is_recurse=True, ) + child_thread.subscribers.add(user) + child_thread.subscribers.add(teacher) + child_thread.parent.add(Thread.objects.get(key="""user_%s""" % user.id)) + child_thread.parent.add(Thread.objects.get(key="""user_%s""" % teacher.id)) + + child_child_thread, is_create = Thread.objects.get_or_create( + key="""user_%s__vertex_%s""" % (user.id, vertex.id,), + text="""Домашняя работа по курсу %s и теме %s для студента %s""" % + (vertex.course.title, vertex.vertex_set.all()[0].title, user.get_full_name()), + ) + + if is_create: + child_child_thread.parent.add(thread) + child_child_thread.parent.add(Thread.objects.get(key="""user_%s""" % user.id)) + child_child_thread.parent.add(child_thread) + child_child_thread.subscribers.add(user) + journal = Journal.objects.create( - thread=child_thread, + thread=child_child_thread, user=owner, content_type=ct, object_id=vertex.id, @@ -48,7 +65,7 @@ if __name__ == '__main__': ) journal_comment = Journal.objects.create( - thread=child_thread, + thread=child_child_thread, user=owner, content_type=ct, object_id=vertex.id, @@ -61,7 +78,3 @@ if __name__ == '__main__': if file_id: journal.files.add(Storage.objects.get(id=file_id)) - if is_create: - child_thread.parent.add(thread) - child_thread.subscribers.add(owner) - child_thread.subscribers.add(sub) diff --git a/finance/migrations/0012_auto_20171110_1302.py b/finance/migrations/0012_auto_20171110_1302.py new file mode 100644 index 0000000..5451f04 --- /dev/null +++ b/finance/migrations/0012_auto_20171110_1302.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-11-10 13:02 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('finance', '0011_invoice_yandex_pay'), + ] + + operations = [ + migrations.AlterField( + model_name='invoice', + name='status', + field=models.CharField(choices=[('W', 'Ожидание согласия'), ('P', 'На оплате'), ('F', 'Оплачен'), ('C', 'Отклонен')], default='W', max_length=1, verbose_name='Статус'), + ), + ] diff --git a/finance/models.py b/finance/models.py index aac8e86..7b8cc9c 100755 --- a/finance/models.py +++ b/finance/models.py @@ -41,7 +41,6 @@ class Invoice(models.Model): ('P', 'На оплате'), ('F', 'Оплачен'), ('C', 'Отклонен'), - ('H', 'Сгорел') ) status = models.CharField(verbose_name='Статус', max_length=1, default='W', choices=BILL_STATUSES) price = models.IntegerField(verbose_name='Сумма', null=True, blank=True) diff --git a/finance/serializers.py b/finance/serializers.py index 7c041bb..71c468a 100644 --- a/finance/serializers.py +++ b/finance/serializers.py @@ -9,7 +9,7 @@ class BillSerializer(serializers.ModelSerializer): class Meta: model = Bill - fields = '__all__' + exclude = ('yandex_pay',) @staticmethod def get_user(self): diff --git a/journals/default_threads.py b/journals/default_threads.py index 493d81d..a64b924 100644 --- a/journals/default_threads.py +++ b/journals/default_threads.py @@ -1,5 +1,7 @@ import os, sys, django +from django.contrib.auth import get_user_model + sys.path.append("../") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings") django.setup() @@ -16,9 +18,8 @@ def main_threads(): admin_thread = Thread.objects.create( key='Admin', - text='Тред для админов, сюда падают все журналируемые сообщения в системе', + text='Тред для админов, сюда падают все журналируемые сообщения в системе, кроме личных переписок', is_staff=True, - is_recurse=True, x=500, y=75, ) @@ -29,7 +30,6 @@ def main_threads(): key='Project_management', text='Тред для проджект-менеджеров, сюда падает статистика разного рода', is_staff=True, - is_recurse=True, ) management_thread.groups.add(Group.objects.get(name='project_managers')) @@ -39,7 +39,6 @@ def main_threads(): key='Support', text='Тред сапортов, занимаются поддержкой клиента', is_staff=True, - is_recurse=True, y=500, ) @@ -50,7 +49,6 @@ def main_threads(): key='Sale_lead', text='Тред лидов, сейлзов', is_staff=True, - is_recurse=True, x=700, ) @@ -79,6 +77,13 @@ def main_threads(): st_tch.parent.add(support_thread) + for i in get_user_model().objects.all(): + Thread.objects.create( + key="""user_%s""" % i.id, + text="""Приватный тред пользователя %s""" % i.email, + is_recurse=True, + ) + if __name__ == '__main__': main_threads() diff --git a/journals/migrations/0009_auto_20171110_1302.py b/journals/migrations/0009_auto_20171110_1302.py new file mode 100644 index 0000000..73074ce --- /dev/null +++ b/journals/migrations/0009_auto_20171110_1302.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-11-10 13:02 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0008_remove_journal_children'), + ] + + operations = [ + migrations.AlterField( + model_name='thread', + name='key', + field=models.CharField(max_length=200, unique=True), + ), + ] diff --git a/journals/migrations/0010_auto_20171110_1646.py b/journals/migrations/0010_auto_20171110_1646.py new file mode 100644 index 0000000..8ee3d53 --- /dev/null +++ b/journals/migrations/0010_auto_20171110_1646.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-11-10 16:46 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0009_auto_20171110_1302'), + ] + + operations = [ + migrations.AlterField( + model_name='journal', + name='action_type', + field=models.CharField(choices=[('try', 'попытался сдать'), ('yes', 'одобрил'), ('no', 'отклонил'), ('favorite', 'добавил в избранное'), ('watch', 'просмотрел'), ('like', 'лайкнул'), ('dislike', 'дизлайкнул'), ('comment', 'оставил комментарий'), ('start', 'начал прохождение'), ('end', 'закончил прохождение'), ('create', 'создал'), ('update', 'обновил'), ('delete', 'удалил')], max_length=31), + ), + migrations.AlterField( + model_name='thread', + name='key', + field=models.CharField(editable=False, max_length=200, unique=True), + ), + ] diff --git a/journals/models.py b/journals/models.py index ce07451..a577903 100755 --- a/journals/models.py +++ b/journals/models.py @@ -6,25 +6,26 @@ from django.contrib.auth.models import Group from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.db import models +from django.db import connection from courses.models import Achievements, Course, CourseMap, Diploma from finance.models import Bill from storage.models import Storage ACTION_CHOICES = ( - (0, 'try'), - (1, 'yes'), - (2, 'no'), - (3, 'favorite'), - (4, 'watch'), - (5, 'like'), - (6, 'dislike'), - (7, 'comment'), - (8, 'start'), - (9, 'end'), - (10, 'create'), - (11, 'update'), - (12, 'delete'), + ('try', 'попытался сдать'), + ('yes', 'одобрил'), + ('no', 'отклонил'), + ('favorite', 'добавил в избранное'), + ('watch', 'просмотрел'), + ('like', 'лайкнул'), + ('dislike', 'дизлайкнул'), + ('comment', 'оставил комментарий'), + ('start', 'начал прохождение'), + ('end', 'закончил прохождение'), + ('create', 'создал'), + ('update', 'обновил'), + ('delete', 'удалил'), ) @@ -37,7 +38,7 @@ class Journal(models.Model): extra_data = models.TextField(null=True, blank=True) object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') - action_type = models.SmallIntegerField(choices=ACTION_CHOICES) + action_type = models.CharField(max_length=31, choices=ACTION_CHOICES) date = models.DateTimeField(auto_now=True) files = models.ManyToManyField(to=Storage, blank=True) @@ -46,7 +47,7 @@ class Journal(models.Model): class Thread(models.Model): - key = models.CharField(max_length=200) + key = models.CharField(max_length=200, unique=True, editable=False) text = models.TextField(default='', verbose_name='Описание треда') is_staff = models.BooleanField(default=False, verbose_name='Админская ли табличка') is_recurse = models.BooleanField(default=False, verbose_name='Поле аптимизации поиска') @@ -57,38 +58,43 @@ class Thread(models.Model): x = models.SmallIntegerField(default=300) y = models.SmallIntegerField(default=300) - def get_journals(self, **filter_extra): - threads = [i for i in self.thread_set.all()].append(self) - return Journal.objects.filter(thread_in=threads, **filter_extra).order_by('-date') + def check_status(self): + # Определяет статус треда если такой есть. + # Возможно, костыль. + res = None + for i in self.journal_set.all(): + if i.action_type in ['try', 'yes', 'no']: + res = i.action_type + return res def check_perm(self, user): - return (user in self.subscribers.all()) or bool(sum([int(i.check_perm(user)) for i in self.parent.all()])) + res = user in self.subscribers.all() + for i in self.groups.all(): + res = res or i in user.groups + return res or sum([int(i.check_perm(user)) for i in self.parent.all()]) def child_thread_count(self): - - if self.is_recurse: - return self.thread_set.count() - - return sum([i.child_thread_count() for i in self.thread_set.all()]) + # cursor = connection.cursor() + # if self.is_recurse: + # cursor.execute(""" + # WITH RECURSIVE temp1 (to_thread_id, from_thread_id) AS ( + # SELECT T1.to_thread_id, T1.from_thread_id + # FROM journals_thread_parent T1 + # WHERE to_thread_id = %s + # UNION + # SELECT T2.to_thread_id, T2.from_thread_id + # FROM journals_thread_parent T2 + # INNER JOIN temp1 ON(T2.from_thread_id = temp1.to_thread_id) + # ) + # SELECT COUNT(*) FROM temp1 + # """, [self.id]) + # count = cursor.fetchone() + # return int(count[0]) + + return self.thread_set.count() def journals_count(self): - children = list(self.get_children()) - children.append(self) - return Journal.objects.filter(thread__in=children).count() - - def get_children(self): - children = self.thread_set.filter(is_staff=False) - - if self.is_recurse: - list(children).append(self) - return children - - res = [self] - - for child in children: - res += child.get_children() - - return res + return self.journal_set.count() def __str__(self): return self.key diff --git a/journals/serilizers.py b/journals/serilizers.py index 65a18fa..1793010 100644 --- a/journals/serilizers.py +++ b/journals/serilizers.py @@ -10,17 +10,26 @@ class JournalSerializer(serializers.ModelSerializer): exclude = ('content_type', 'object_id') -class ThreadSerializer(serializers.ModelSerializer): +class ThreadDetailSerializer(serializers.ModelSerializer): + journals = serializers.SerializerMethodField() class Meta: model = Thread - exclude = ('is_staff', ) + fields = ('journals', 'id', 'text') + @staticmethod + def get_journals(self): + return [JournalSerializer(i).data for i in self.journal_set.all()] -class ThreadAdminSerializer(ThreadSerializer): + +class ThreadAdminSerializer(serializers.ModelSerializer): count_children = serializers.SerializerMethodField() count_journals = serializers.SerializerMethodField() + class Meta: + model = Thread + exclude = ('is_staff', ) + @staticmethod def get_count_children(self): return self.child_thread_count() @@ -31,12 +40,7 @@ class ThreadAdminSerializer(ThreadSerializer): class ThreadUserSerializer(serializers.ModelSerializer): - journals = serializers.SerializerMethodField() class Meta: model = Thread - fields = ('journals', 'key') - - @staticmethod - def get_journals(self): - return [JournalSerializer(i).data for i in self.journal_set.all()] \ No newline at end of file + fields = ('id', 'text') \ No newline at end of file diff --git a/journals/urls.py b/journals/urls.py index 9bf787f..62a17d7 100644 --- a/journals/urls.py +++ b/journals/urls.py @@ -2,6 +2,7 @@ from django.conf.urls import url from journals import views as views urlpatterns = [ - url(r'thread/$', views.ThreadListView.as_view()), + url(r'thread/$', views.ThreadAdminListView.as_view()), + url(r'thread/teacher/$', views.TeacherThreadListView.as_view()), url(r'thread/(?P[-\w]+)/$', views.ThreadDetailView.as_view()), ] \ No newline at end of file diff --git a/journals/views.py b/journals/views.py index 26d5ceb..b4687d2 100644 --- a/journals/views.py +++ b/journals/views.py @@ -1,12 +1,14 @@ 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 lms.global_decorators import transaction_decorator from journals.models import Thread from journals.serilizers import ThreadUserSerializer, ThreadAdminSerializer -class ThreadListView(APIView): +class ThreadAdminListView(APIView): renderer_classes = (JSONRenderer,) status_code = 200 @@ -40,12 +42,12 @@ class ThreadDetailView(APIView): return Response("Thread not found", status=404) -# class FindThreadView(APIView): -# renderer_classes = (JSONRenderer,) -# status_code = 200 -# -# def get(self, request): -# is_full = request.GET.get('full', True) -# if is_full: -# key = request.GET['key'] -# return Response(ThreadSerializer(Thread.objects.get(key=key)).data, self.status_code) \ No newline at end of file +class TeacherThreadListView(APIView): + renderer_classes = (JSONRenderer,) + + @transaction_decorator + def get(self, request): + if not request.user.is_authenticated and not 'teachers' in request.user.groups: + return Response("Permission denied", status=403) + threads = Thread.objects.filter(Q(subscribers=request.user) | Q(groups__user=request.user)) + return Response([ThreadUserSerializer(i).data for i in threads]) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 773d86e..d7a5ad0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,19 +1,36 @@ -unidecode -environ amqp==2.2.2 +Babel==2.5.1 billiard==3.5.0.3 celery==4.1.0 +certifi==2017.11.5 +chardet==3.0.4 Django==1.11.6 django-appconf==1.0.2 django-celery-beat==1.0.1 django-celery-email==2.0.0 django-celery-results==1.0.1 +django-environ==0.4.4 +django-redis==4.8.0 +django-redis-sessions==0.6.1 +django-yandex-money==1.1.2 djangorestframework==3.7.0 +environ==1.0 +flower==0.9.2 +future==0.16.0 +idna==2.6 kombu==4.1.0 +lxml==4.1.1 olefile==0.44 Pillow==4.3.0 pkg-resources==0.0.0 psycopg2==2.7.3.1 pytz==2017.2 raven==6.2.1 +redis==2.10.6 +requests==2.18.4 +six==1.11.0 +tornado==4.5.2 +Unidecode==0.4.21 +urllib3==1.22 vine==1.1.4 +yandex-money-sdk==0.1.3