From 973752bd05fc566386e9c1188fc2dea4252220e8 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 6 Nov 2017 17:02:07 +0300 Subject: [PATCH] finance update --- courses/migrations/0004_auto_20171103_1409.py | 20 ++++ courses/migrations/0005_auto_20171103_1540.py | 20 ++++ courses/migrations/0006_auto_20171103_1623.py | 30 +++++ courses/migrations/0007_auto_20171103_1627.py | 30 +++++ courses/models.py | 106 +++++++++++++----- courses/serializers.py | 2 +- courses/views.py | 4 + csv/load_bills.py | 28 +++++ finance/models.py | 2 +- lms/tools.py | 19 ++++ requirements.txt | 1 + static/css/system_messages.css | 0 static/css/tilda-grid.css | 0 13 files changed, 233 insertions(+), 29 deletions(-) create mode 100644 courses/migrations/0004_auto_20171103_1409.py create mode 100644 courses/migrations/0005_auto_20171103_1540.py create mode 100644 courses/migrations/0006_auto_20171103_1623.py create mode 100644 courses/migrations/0007_auto_20171103_1627.py create mode 100644 csv/load_bills.py create mode 100644 lms/tools.py delete mode 100755 static/css/system_messages.css delete mode 100755 static/css/tilda-grid.css diff --git a/courses/migrations/0004_auto_20171103_1409.py b/courses/migrations/0004_auto_20171103_1409.py new file mode 100644 index 0000000..eba173c --- /dev/null +++ b/courses/migrations/0004_auto_20171103_1409.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-11-03 14:09 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('courses', '0003_auto_20171023_1037'), + ] + + operations = [ + migrations.AlterField( + model_name='course', + name='slug', + field=models.SlugField(blank=True, default='', max_length=255, unique=True), + ), + ] diff --git a/courses/migrations/0005_auto_20171103_1540.py b/courses/migrations/0005_auto_20171103_1540.py new file mode 100644 index 0000000..36ed94b --- /dev/null +++ b/courses/migrations/0005_auto_20171103_1540.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-11-03 15:40 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('courses', '0004_auto_20171103_1409'), + ] + + operations = [ + migrations.AlterField( + model_name='course', + name='slug', + field=models.SlugField(blank=True, default='', editable=False, max_length=255, unique=True), + ), + ] diff --git a/courses/migrations/0006_auto_20171103_1623.py b/courses/migrations/0006_auto_20171103_1623.py new file mode 100644 index 0000000..366a684 --- /dev/null +++ b/courses/migrations/0006_auto_20171103_1623.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-11-03 16:23 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('courses', '0005_auto_20171103_1540'), + ] + + operations = [ + migrations.AlterField( + model_name='course', + name='big_image', + field=models.ImageField(blank=True, max_length=255, upload_to='course', verbose_name='Большое изображение'), + ), + migrations.AlterField( + model_name='course', + name='big_mobile_image', + field=models.ImageField(blank=True, help_text='Большая картинка для мобильной версии', max_length=255, null=True, upload_to='course', verbose_name='Под мобилку'), + ), + migrations.AlterField( + model_name='course', + name='image', + field=models.ImageField(blank=True, max_length=255, upload_to='course', verbose_name='Изображение'), + ), + ] diff --git a/courses/migrations/0007_auto_20171103_1627.py b/courses/migrations/0007_auto_20171103_1627.py new file mode 100644 index 0000000..90bd4b4 --- /dev/null +++ b/courses/migrations/0007_auto_20171103_1627.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-11-03 16:27 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('courses', '0006_auto_20171103_1623'), + ] + + operations = [ + migrations.AlterField( + model_name='course', + name='big_image', + field=models.URLField(blank=True, max_length=255, verbose_name='Большое изображение'), + ), + migrations.AlterField( + model_name='course', + name='big_mobile_image', + field=models.URLField(blank=True, help_text='Большая картинка для мобильной версии', max_length=255, null=True, verbose_name='Под мобилку'), + ), + migrations.AlterField( + model_name='course', + name='image', + field=models.URLField(blank=True, max_length=255, verbose_name='Изображение'), + ), + ] diff --git a/courses/models.py b/courses/models.py index a3238ba..99ec6ed 100755 --- a/courses/models.py +++ b/courses/models.py @@ -1,43 +1,93 @@ # -*- 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 unidecode +from django.template.defaultfilters import slugify +from lms.tools import decode_base64, get_real_name from lms.global_decorators import transaction_decorator from library.models import Tags from storage.models import Storage +COURSE_LEVEL = ( + ('B', 'Базовый'), + ('A', 'Продвинутый'), + ('E', 'Экспертный'), + ('B+A', 'Базовый + Продвинутый'), +) + +COURSE_DIRECTION = ( + (3, 'Бизнес'), + (2, 'Веб-дизайн'), + (1, 'Разработка'), + (4, 'Рисование'), +) + + +class CourseManager(models.Manager): + @transaction_decorator + def update_or_create_course(self, image=None, big_image=None, id=0, + big_mobile_image=None, mentors=None, slug=None, + teachers=None, level=None, direction=None, **kwargs): + + slug = slug if slug else slugify(unidecode.unidecode(kwargs['title'])) + + if image: + kwargs['image'] = decode_base64(image, 'course/image%s.png' % slug) + + if big_image: + kwargs['big_image'] = decode_base64(big_image, 'course/big_image%s.png' % slug) + + if big_mobile_image: + kwargs['big_mobile_image'] = decode_base64(big_mobile_image, 'course/big_mobile_image%s.png' % slug) + + if level: + kwargs['level'] = get_real_name(COURSE_LEVEL, level) + + if direction: + kwargs['direction'] = get_real_name(COURSE_DIRECTION, direction) + + try: + course = self.get(id=id) + for i in kwargs: + setattr(course, i, kwargs[i]) + course.save() + except ObjectDoesNotExist: + kwargs['slug'] = slug + 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)) + + if teachers: + for email in teachers: + course.teachers.add(get_user_model().objects.get(email=email)) + + return course + class Course(models.Model): - COURSE_LEVEL = ( - ('B', u'Базовый'), - ('A', u'Продвинутый'), - ('E', u'Экспертный'), - ('B+A', u'Базовый + Продвинутый') - ) - COURSE_DIRECTION = ( - (3, 'Бизнес'), - (2, 'Веб-дизайн'), - (1, 'Разработка'), - (4, 'Рисование') - ) - hidden = models.BooleanField(verbose_name=u'Видно только оплатившим', default=False) - level = models.CharField(verbose_name=u'Уровень', choices=COURSE_LEVEL, default='B', max_length=3) - slug = models.SlugField(max_length=255, editable=False, blank=True, default='', unique=True) - direction = models.SmallIntegerField(choices=COURSE_DIRECTION, verbose_name=u'Направление', null=True) - mentors = models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name=u'Кураторы', blank=True, + hidden = models.BooleanField(verbose_name='Видно только оплатившим', default=False) + 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) + mentors = models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name='Кураторы', blank=True, related_name='course_mentors') - public = models.BooleanField(verbose_name=u'Опубликовать', default=False) - title = models.CharField(verbose_name=u"Заголовок", max_length=255) - description = models.TextField(verbose_name=u'Описание', blank=True) - image = models.ImageField(verbose_name=u'Изображение', upload_to='course', blank=True) - big_image = models.ImageField(verbose_name=u'Большое изображение', upload_to='course', blank=True) - big_mobile_image = models.ImageField(verbose_name=u'Под мобилку', upload_to='course', blank=True, null=True, - help_text=u'Большая картинка для мобильной версии') - teachers = models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name=u'Преподаватели', + public = models.BooleanField(verbose_name='Опубликовать', default=False) + title = models.CharField(verbose_name="Заголовок", max_length=255) + description = models.TextField(verbose_name='Описание', blank=True) + image = models.URLField(verbose_name='Изображение', blank=True, max_length=255) + 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') def __str__(self): @@ -105,6 +155,8 @@ class Course(models.Model): return vertex.get_previous(vertex_model_list) + objects = CourseManager() + class Meta: verbose_name = u"Курс" verbose_name_plural = u"Курсы" @@ -235,7 +287,7 @@ class Vertex(models.Model): raise ValueError('undefined model: ' + i) vertex_id = CourseMap.objects.get(course=self.course).get_next(self.id) - vertex = Vertex.objects.get(id=int(vertex_id),) + vertex = Vertex.objects.get(id=int(vertex_id), ) if vertex.content_type.model in vertex_model_list: return vertex @@ -251,7 +303,7 @@ class Vertex(models.Model): 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),) + vertex = Vertex.objects.get(id=int(vertex_id), ) if vertex.content_type.model in vertex_model_list: return vertex diff --git a/courses/serializers.py b/courses/serializers.py index 9c55b0c..f20bfca 100644 --- a/courses/serializers.py +++ b/courses/serializers.py @@ -114,4 +114,4 @@ class CourseDetailSerializer(serializers.ModelSerializer): @staticmethod def get_teachers(self): - return [teacher.get_full_name() for teacher in self.teachers.all()] + return [teacher.email for teacher in self.teachers.all()] diff --git a/courses/views.py b/courses/views.py index 111d87d..82ac81f 100644 --- a/courses/views.py +++ b/courses/views.py @@ -63,6 +63,10 @@ class CourseListView(APIView): renderer_classes = (JSONRenderer,) status_code = 200 + def post(self, request): + course = Course.objects.update_or_create_course(**request.JSON.dict()) + return Response(CourseDetailSerializer(course).data, status=self.status_code) + def get(self, request): if request.user.is_authenticated() and request.user.is_staff: return Response([CourseListSerializer(i).data for i in Course.objects.all()], self.status_code) diff --git a/csv/load_bills.py b/csv/load_bills.py new file mode 100644 index 0000000..e9efa4b --- /dev/null +++ b/csv/load_bills.py @@ -0,0 +1,28 @@ +import os, sys, django, csv + +from django.contrib.auth import get_user_model + +sys.path.append("../") +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings") +django.setup() + +from finance.models import Bill, Invoice +from courses.models import Course + +if __name__ == '__main__': + Bill.objects.all().delete() + with open('./finance/bill.csv') as bill_csv: + bill_reader = csv.DictReader(bill_csv) + for row in bill_reader: + bill_kwarg = dict() + row = dict(row) + bill_kwarg['id'] = row.pop('id', None) + bill_kwarg['course'] = Course.objects.get(id=row.pop('course__id', None)) + opener_id = row.pop('opener__id', None) + bill_kwarg['opener'] = get_user_model().objects.get(id=opener_id) if opener_id \ + else get_user_model().objects.get(email="kate.gazukina@skillbox.ru") + bill_kwarg['user'] = get_user_model().objects.get(email=row.pop('user__email', None)) + bill_kwarg['comment'] = row.pop('comment', None) + bill_kwarg['description'] = row.pop('description', None) + bill = Bill.objects.create(**bill_kwarg) + Invoice.objects.create(bill=bill, **row) \ No newline at end of file diff --git a/finance/models.py b/finance/models.py index 628d09f..65df63e 100755 --- a/finance/models.py +++ b/finance/models.py @@ -45,7 +45,7 @@ class Invoice(models.Model): 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'Будет показано пользователю', + comment = models.TextField(verbose_name='Комментарий продавца', help_text='Будет показано пользователю', blank=True, editable=False) bill = models.ForeignKey(to=Bill, verbose_name="Связный счёт") diff --git a/lms/tools.py b/lms/tools.py new file mode 100644 index 0000000..3f920c5 --- /dev/null +++ b/lms/tools.py @@ -0,0 +1,19 @@ +import base64 +from django.conf import settings + + +def decode_base64(my_str, upload_to): + if "data:" in my_str: + my_str = my_str[my_str.index("base64,")+7:] + path = "%s/%s" % (settings.MEDIA_ROOT, upload_to) + url = "%s%s" % (settings.MEDIA_URL, upload_to) + with open(path, "wb") as fh: + fh.write(base64.b64decode(my_str)) + return url + return my_str + + +def get_real_name(array, elem): + for i, j in array: + if i and (i == elem or j == elem): + return i diff --git a/requirements.txt b/requirements.txt index cb2f4b2..773d86e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +unidecode environ amqp==2.2.2 billiard==3.5.0.3 diff --git a/static/css/system_messages.css b/static/css/system_messages.css deleted file mode 100755 index e69de29..0000000 diff --git a/static/css/tilda-grid.css b/static/css/tilda-grid.css deleted file mode 100755 index e69de29..0000000