diff --git a/.dockerignore b/.dockerignore index 2adf89c1..54513197 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,2 @@ venv*/ +web/node_modules/ diff --git a/api/v1/serializers/config.py b/api/v1/serializers/config.py index 92f8aa64..e17ce57e 100644 --- a/api/v1/serializers/config.py +++ b/api/v1/serializers/config.py @@ -13,11 +13,12 @@ def _set_constance_value(key, value): class ConfigSerializer(serializers.Serializer): + SERVICE_COMMISSION = serializers.IntegerField(required=False) + SERVICE_DISCOUNT_MIN_AMOUNT = serializers.IntegerField(required=False) + SERVICE_DISCOUNT = serializers.IntegerField(required=False) INSTAGRAM_CLIENT_ACCESS_TOKEN = serializers.CharField(required=False) INSTAGRAM_CLIENT_SECRET = serializers.CharField(required=False) - INSTAGRAM_RESULTS_TAG = serializers.CharField(required=False) - INSTAGRAM_RESULTS_PATH = serializers.CharField(required=False) - SERVICE_COMMISSION = serializers.IntegerField(required=False) + INSTAGRAM_PROFILE_URL = serializers.CharField(required=False) def to_representation(self, instance): ret = OrderedDict() diff --git a/apps/course/templates/course/course.html b/apps/course/templates/course/course.html index a928f119..5f7a00c8 100644 --- a/apps/course/templates/course/course.html +++ b/apps/course/templates/course/course.html @@ -25,7 +25,7 @@
Вернуться
- {% if not paid and course.price %} + {% if course.author != request.user and not paid and course.price %}
Описание курса - {% if request.user.role == request.user.AUTHOR_ROLE or request.user.role == request.user.ADMIN_ROLE %} - УРОКИ - {% comment %} - - {% endcomment %} + {% if request.user.is_authenticated %} + {% if cource.author == request.user and request.user.role == request.user.AUTHOR_ROLE or request.user.role == request.user.ADMIN_ROLE %} + УРОКИ {% else %} - + УРОКИ + {% if not paid %} + + + + {% endif %} + + {% endif %} {% endif %}
@@ -237,8 +244,8 @@ {% with template="course/content/"|add:content.ctype|add:".html" %} {% include template %} {% endwith %} - + {% endfor %} {% if user.is_authenticated and course.lessons.exists %}
@@ -255,22 +262,13 @@
{{ lesson.title }}
- {% comment %} {% if lesson.cover %} -
- -
- {% else %} -
- -
- {% endif %} {% endcomment %}
{{ lesson.short_description | safe }}
{% if request.user.role == request.user.AUTHOR_ROLE or request.user.role == request.user.ADMIN_ROLE %} -
+ {% else %} -
+
{% endif %} {% endfor %}
@@ -391,7 +389,7 @@ {% endif %} - {% if not paid and course.price %} + {% if course.author != request.user and not paid and course.price %}
Вернуться
- + >{% if pending %}ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ ОПЛАТЫ{% else %}КУПИТЬ КУРС{% endif %} + {% endif %}
- Описание курса - УРОКИ - {% comment %} - - {% endcomment %} - + Описание курса + {% if request.user.role == request.user.AUTHOR_ROLE or request.user.role == request.user.ADMIN_ROLE %} + УРОКИ + + {% else %} + УРОКИ + {% if not paid %} + + + + {% endif %} + + {% endif %}
@@ -270,4 +285,4 @@ -{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/apps/course/views.py b/apps/course/views.py index d04f6a2d..68fd1b1a 100644 --- a/apps/course/views.py +++ b/apps/course/views.py @@ -164,6 +164,8 @@ class CourseEditView(TemplateView): self.object = drafts.last() else: self.object = Course.objects.create() + if request.user != self.object.author and request.user.role not in [User.ADMIN_ROLE, User.AUTHOR_ROLE]: + raise Http404 return super().get(request) def get_context_data(self): @@ -178,10 +180,13 @@ class CourseView(DetailView): model = Course context_object_name = 'course' template_name = 'course/course.html' + only_lessons = False def get(self, request, *args, **kwargs): response = super().get(request, *args, **kwargs) - if (not request.user.is_authenticated and self.object.status != Course.PUBLISHED): + context = self.get_context_data() + if not request.user.is_authenticated or (not request.user.is_authenticated and self.object.status != Course.PUBLISHED) or\ + (request.user.is_authenticated and request.user.role not in [User.AUTHOR_ROLE, User.ADMIN_ROLE] and self.object.author != request.user and self.only_lessons and not context['paid']): raise Http404 return response diff --git a/apps/payment/models.py b/apps/payment/models.py index e7bc6c3c..5ec8e1ac 100644 --- a/apps/payment/models.py +++ b/apps/payment/models.py @@ -144,7 +144,10 @@ class SchoolPayment(Payment): verbose_name_plural = 'Платежи за школу' def __str__(self): - days = ', '.join([dict(SchoolSchedule.WEEKDAY_CHOICES).get(weekday, '') for weekday in sorted(self.weekdays)]) + days = ', '.join([ + dict(SchoolSchedule.WEEKDAY_CHOICES).get(weekday, '') + for weekday in sorted(self.weekdays) + ]) return days def save(self, *args, **kwargs): @@ -152,9 +155,11 @@ class SchoolPayment(Payment): weekday__in=self.weekdays, ).aggregate( models.Sum('month_price'), - models.Sum('day_discount'), ) month_price_sum = aggregate.get('month_price__sum', 0) - day_discount_sum = aggregate.get('day_discount__sum', 0) if len(self.weekdays) == 7 else 0 - self.amount = month_price_sum - day_discount_sum + if len(self.weekdays) > 7: + discount = config.SERVICE_DISCOUNT + else: + discount = 0 + self.amount = month_price_sum - discount super().save(*args, **kwargs) diff --git a/apps/payment/views.py b/apps/payment/views.py index f22aefc7..52ccd274 100644 --- a/apps/payment/views.py +++ b/apps/payment/views.py @@ -4,6 +4,7 @@ import json from datetime import timedelta from django.contrib import messages +from django.contrib.auth.decorators import login_required from django.http import HttpResponse from django.shortcuts import redirect from django.views.generic import View, TemplateView @@ -22,12 +23,16 @@ from .models import AuthorBalance, CoursePayment, SchoolPayment logger = logging.getLogger('django') +@method_decorator(login_required, name='dispatch') class CourseBuyView(TemplateView): template_name = 'payment/paymentwall_widget.html' def get(self, request, pk=None, *args, **kwargs): host = request.scheme + '://' + request.get_host() course = Course.objects.get(id=pk) + if request.user == course.author: + messages.error('Вы не можете приобрести свой курс.') + return redirect(reverse_lazy('course', args=[course.id])) course_payment = CoursePayment.objects.create( user=request.user, course=course, @@ -54,6 +59,7 @@ class CourseBuyView(TemplateView): return self.render_to_response(context={'widget': widget.get_html_code()}) +@method_decorator(login_required, name='dispatch') class SchoolBuyView(TemplateView): template_name = 'payment/paymentwall_widget.html' diff --git a/apps/user/templates/user/payment-history.html b/apps/user/templates/user/payment-history.html index 4c9a34b7..af251c80 100644 --- a/apps/user/templates/user/payment-history.html +++ b/apps/user/templates/user/payment-history.html @@ -105,7 +105,7 @@ {% else %}
{% if payment.is_deliverable %} - Получено + Оплачено {% elif payment.is_under_review %} Ожидается подтверждение оплаты {% else %} @@ -132,4 +132,4 @@ -{% endblock foot %} \ No newline at end of file +{% endblock foot %} diff --git a/apps/user/views.py b/apps/user/views.py index 533d19ad..69e42bef 100644 --- a/apps/user/views.py +++ b/apps/user/views.py @@ -6,6 +6,7 @@ from os.path import splitext from datetime import timedelta from paymentwall import Pingback +from django.conf import settings from django.contrib.auth import login from django.core.exceptions import ValidationError from django.shortcuts import render, reverse, redirect @@ -102,7 +103,7 @@ class PaymentHistoryView(FormView): def post(self, request, pk=None): form = self.get_form() - if AuthorBalance.objects.filter(created_at__gte=now() - timedelta(days=30)).exists(): + if not settings.DEBUG and AuthorBalance.objects.filter(created_at__gte=now() - timedelta(days=30)).exists(): messages.error(request, 'Запрос на вывод средств можно сделать только один раз в 30 дней.') return self.form_invalid(form) if form.is_valid(): diff --git a/docker-compose.yml b/docker-compose.yml index 647430e4..ab168db4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,7 +22,7 @@ services: restart: always volumes: - .:/lilcity - command: bash -c "python manage.py migrate && python manage.py loaddata /lilcity/apps/*/fixtures/*.json && python manage.py runserver 0.0.0.0:8000 && celery worker -A project" + command: bash -c "python manage.py migrate && python manage.py loaddata /lilcity/apps/*/fixtures/*.json && gunicorn --workers=4 project.wsgi --bind=0.0.0.0:8000 --worker-class=gthread --reload" environment: - DJANGO_SETTINGS_MODULE=project.settings - DATABASE_SERVICE_HOST=db diff --git a/project/settings.py b/project/settings.py index c9b37278..6860b3d0 100644 --- a/project/settings.py +++ b/project/settings.py @@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/2.0/ref/settings/ """ import os +from celery.schedules import crontab from collections import OrderedDict from datetime import timedelta @@ -87,6 +88,7 @@ TEMPLATES = [ 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ + 'constance.context_processors.config', 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', @@ -219,7 +221,7 @@ CELERY_TASK_SERIALIZER = 'json' CELERY_BEAT_SCHEDULE = { 'retrieve_photos_from_instagram': { 'task': 'apps.content.tasks.retrieve_photos', - 'schedule': timedelta(minutes=2), + 'schedule': timedelta(minutes=2) if DEBUG else crontab(minute=0, hour=0), 'args': (), }, } @@ -230,11 +232,30 @@ CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend' CONSTANCE_CONFIG = OrderedDict(( ('INSTAGRAM_CLIENT_ACCESS_TOKEN', ('7145314808.f6fa114.ce354a5d876041fc9d3db04b0045587d', '')), ('INSTAGRAM_CLIENT_SECRET', ('2334a921425140ccb180d145dcd35b25', '')), + ('INSTAGRAM_PROFILE_URL', ('#', 'URL профиля Instagram.')), ('INSTAGRAM_RESULTS_TAG', ('#lil_акварель', 'Тэг результатов работ.')), ('INSTAGRAM_RESULTS_PATH', ('media/instagram/results/', 'Путь до результатов работ.')), - ('SERVICE_COMMISSION', (10, 'Комиссия сервиса в процентах.')) + ('SERVICE_COMMISSION', (10, 'Комиссия сервиса в процентах.')), + ('SERVICE_DISCOUNT_MIN_AMOUNT', (3500, 'Минимальная сумма платежа для школы, после которой вычитывается скидка SERVICE_DISCOUNT.')), + ('SERVICE_DISCOUNT', (1000, 'Комиссия сервиса при покупке всех дней.')), )) +CONSTANCE_CONFIG_FIELDSETS = OrderedDict({ + 'Service': ( + 'SERVICE_COMMISSION', + 'SERVICE_DISCOUNT_MIN_AMOUNT', + 'SERVICE_DISCOUNT', + ), + 'Instagram': ( + 'INSTAGRAM_CLIENT_ACCESS_TOKEN', + 'INSTAGRAM_CLIENT_SECRET', + 'INSTAGRAM_PROFILE_URL', + 'INSTAGRAM_RESULTS_TAG', + 'INSTAGRAM_RESULTS_PATH', + ), +}) + + try: from .local_settings import * except ImportError: diff --git a/project/templates/lilcity/index.html b/project/templates/lilcity/index.html index 6ec88106..2f28628d 100644 --- a/project/templates/lilcity/index.html +++ b/project/templates/lilcity/index.html @@ -440,7 +440,7 @@
{% for school_schedule in school_schedules %}
+ {% if course %} + + {% endif %} + {% block foot %}{% endblock foot %} diff --git a/project/templates/lilcity/main.html b/project/templates/lilcity/main.html index a6a812c0..817a23f6 100644 --- a/project/templates/lilcity/main.html +++ b/project/templates/lilcity/main.html @@ -190,7 +190,7 @@
Галерея

Тысячи шедевров уже созданы благодаря Lil City School. Более 10000 работ можно - увидеть в Инстаграм

+ увидеть в Инстаграм