Merge remote-tracking branch 'origin/master' into dev2

# Conflicts:
#	project/templates/blocks/promo.html
#	project/templates/lilcity/index.html
remotes/origin/hasaccess^2
nikita 8 years ago
commit cb98082328
  1. 56
      .gitlab-ci.yml
  2. 6
      apps/course/templates/course/content/imagetext.html
  3. 56
      apps/course/templates/course/course.html
  4. 11
      apps/course/templates/course/course_only_lessons.html
  5. 1
      apps/course/templates/course/courses.html
  6. 11
      apps/course/templates/course/lesson.html
  7. 4
      apps/course/views.py
  8. 2
      apps/payment/templates/payment/payment_success.html
  9. 18
      apps/school/migrations/0017_auto_20180628_2000.py
  10. 18
      apps/school/migrations/0018_auto_20180629_1501.py
  11. 8
      apps/school/models.py
  12. 14
      apps/school/templates/blocks/_schedule_purchased_item.html
  13. 1
      apps/school/templates/blocks/schedule_purchased.html
  14. 2
      apps/school/templates/school/livelesson_detail.html
  15. 1
      apps/school/templates/school/summer_school.html
  16. 14
      apps/school/templates/summer/_schedule_purchased_item.html
  17. 1
      apps/school/templates/summer/schedule_purchased.html
  18. 44
      apps/school/views.py
  19. 8
      apps/user/templates/user/profile.html
  20. 22
      docker/.env.review
  21. 1
      docker/Dockerfile
  22. 7
      docker/docker-compose-prod.yml
  23. 58
      docker/docker-compose-review.yml
  24. 1
      docker/entrypoint_app.sh
  25. 2
      project/settings.py
  26. 14
      project/templates/blocks/about.html
  27. 2
      project/templates/blocks/user_menu.html
  28. 2
      project/views.py
  29. 4
      requirements.txt
  30. 12
      web/src/components/CourseRedactor.vue
  31. 38
      web/src/components/LessonRedactor.vue
  32. BIN
      web/src/img/og_blog.jpg
  33. BIN
      web/src/img/og_courses.jpg
  34. BIN
      web/src/img/og_main.jpg
  35. BIN
      web/src/img/og_summer_school.jpg
  36. 3
      web/src/js/modules/api.js
  37. 14
      web/src/sass/_common.sass

@ -1,16 +1,66 @@
stages: stages:
- deploy - deploy
- db
- stop
variables:
REVIEW_DOMAIN: back-review.lil.school
deploy_prod: deploy_prod:
stage: deploy stage: deploy
script: script:
- rsync -a --stats --delete --exclude="docker/data/" --exclude="docker/.env" ./ /work/www/lil.school/ - rsync -a --stats --delete --exclude="docker/data/" --exclude="docker/.env" ./ /work/www/lil.school/
- cd /work/www/lil.school/docker/ - cd /work/www/lil.school/docker/
- docker-compose -f docker-compose-prod.yml up --build -d - docker-compose -f docker-compose-prod.yml -p back up --build -d
environment: environment:
name: prod/site name: prod
url: https://lil.school url: https://lil.school
only: only:
- master - master
tags: tags:
- prod - prod
deploy_review:
stage: deploy
script:
- export REVIEW_HOST=$CI_COMMIT_REF_SLUG-$REVIEW_DOMAIN
- cd docker
- cp .env.review .env
- docker-compose -f docker-compose-review.yml -p back$CI_COMMIT_REF_NAME up --build -d
environment:
name: review/$CI_COMMIT_REF_SLUG
url: https://$CI_COMMIT_REF_SLUG-$REVIEW_DOMAIN
on_stop: stop-review
tags:
- review
only:
- branches
stop-review:
stage: stop
environment:
name: review/$CI_COMMIT_REF_SLUG
action: stop
script:
- export REVIEW_HOST=$CI_COMMIT_REF_SLUG-$REVIEW_DOMAIN
- cd docker
- docker-compose -f docker-compose-review.yml -p back$CI_COMMIT_REF_NAME down
- rm -rf /work/data/back_${CI_COMMIT_REF_NAME}/
when: manual
only:
- branches
tags:
- review
prod-db:
stage: db
script:
- export REVIEW_HOST=$CI_COMMIT_REF_SLUG-$REVIEW_DOMAIN
- cd docker
- docker-compose -f docker-compose-review.yml -p back$CI_COMMIT_REF_NAME restart db
- echo 'DROP DATABASE IF EXISTS lilcity; CREATE DATABASE lilcity' | docker-compose -f docker-compose-review.yml -p back$CI_COMMIT_REF_NAME exec -T -u postgres postgres psql postgres
when: manual
only:
- branches
tags:
- review

@ -4,7 +4,11 @@
<div class="lessons__item"> <div class="lessons__item">
<div class="lessons__row"> <div class="lessons__row">
<div class="lessons__preview"><img class="lessons__pic" style="display: block;border-radius:50%;width:130px;height: 130px" src="{{ content.img.image.url }}"></div> <div class="lessons__preview">
<div class="lessons__pic-wrapper">
<img class="lessons__pic" src="{{ content.img.image.url }}">
</div>
</div>
<div class="lessons__content">{{ content.txt | safe }}</div> <div class="lessons__content">{{ content.txt | safe }}</div>
</div> </div>
</div> </div>

@ -9,7 +9,7 @@
{% block ogtitle %}{{ course.title }} - {{ block.super }}{% endblock ogtitle %} {% block ogtitle %}{{ course.title }} - {{ block.super }}{% endblock ogtitle %}
{% block ogurl %}{{ request.build_absolute_uri }}{% endblock ogurl %} {% block ogurl %}{{ request.build_absolute_uri }}{% endblock ogurl %}
{% if course.cover and course.cover.image %} {% if course.cover and course.cover.image %}
{% block ogimage %}http://{{request.META.HTTP_HOST}}{{ course.cover.image.url }}{% endblock ogimage %} {% block ogimage %}http://{{request.META.HTTP_HOST}}{% if course.cover %}{{ course.cover.image.url }}{% else %}{% static 'img/og_courses.jpg' %}{% endif %}{% endblock ogimage %}
{% endif %} {% endif %}
{% block ogdescription %}{{ course.short_description }}{% endblock ogdescription %} {% block ogdescription %}{{ course.short_description }}{% endblock ogdescription %}
@ -141,37 +141,45 @@
<div class="course__actions"> <div class="course__actions">
<a href="{% url 'course' course.id %}" class="course__action btn btn_lg{% if not only_lessons %} btn_stroke{% else %} btn_gray{% endif %}">Описание курса</a> <a href="{% url 'course' course.id %}" class="course__action btn btn_lg{% if not only_lessons %} btn_stroke{% else %} btn_gray{% endif %}">Описание курса</a>
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
{% if course.author == request.user and request.user.role >= request.user.AUTHOR_ROLE %} {% if course.author == request.user and request.user.role >= request.user.AUTHOR_ROLE %}
<a <a
href="{% url 'course-only-lessons' course.id %}" href="{% url 'course-only-lessons' course.id %}"
class="course__action btn btn_lg{% if only_lessons %} btn_stroke{% else %} btn_gray{% endif %}" class="course__action btn btn_lg{% if only_lessons %} btn_stroke{% else %} btn_gray{% endif %}"
{% if not user.is_authenticated %}data-popup=".js-popup-auth"{% endif %} {% if not user.is_authenticated %}data-popup=".js-popup-auth"{% endif %}
>УРОКИ >УРОКИ
</a> </a>
{% elif request.user.role == request.user.ADMIN_ROLE %} {% elif request.user.role == request.user.ADMIN_ROLE %}
<a <a
href="{% url 'course-only-lessons' course.id %}" href="{% url 'course-only-lessons' course.id %}"
class="course__action btn btn_lg{% if only_lessons %} btn_stroke{% else %} btn_gray{% endif %}" class="course__action btn btn_lg{% if only_lessons %} btn_stroke{% else %} btn_gray{% endif %}"
{% if not user.is_authenticated %}data-popup=".js-popup-auth"{% endif %} {% if not user.is_authenticated %}data-popup=".js-popup-auth"{% endif %}
>УРОКИ >УРОКИ
</a> </a>
{% else %}
<a
class="course__action btn btn_lg{% if only_lessons %} btn_stroke{% else %} btn_gray{% endif %}"
{% if paid %}
href="{% url 'course-only-lessons' course.id %}"
{% else %}
data-popup=".js-popup-course-lock"
{% endif %}
>УРОКИ
{% if not paid %}
<svg class="icon icon-lock">
<use xlink:href="{% static 'img/sprite.svg' %}#icon-lock"></use>
</svg>
{% endif %}
</a>
{% endif %}
{% else %} {% else %}
<a <a
class="course__action btn btn_lg{% if only_lessons %} btn_stroke{% else %} btn_gray{% endif %}" class="course__action btn btn_lg{% if only_lessons %} btn_stroke{% else %} btn_gray{% endif %}"
{% if paid %} data-popup=".js-popup-auth">УРОКИ
href="{% url 'course-only-lessons' course.id %}"
{% else %}
data-popup=".js-popup-course-lock"
{% endif %}
>УРОКИ
{% if not paid %}
<svg class="icon icon-lock"> <svg class="icon icon-lock">
<use xlink:href="{% static 'img/sprite.svg' %}#icon-lock"></use> <use xlink:href="{% static 'img/sprite.svg' %}#icon-lock"></use>
</svg> </svg>
{% endif %}
</a> </a>
{% endif %} {% endif %}
{% endif %}
</div> </div>
<a class="course__video video" href="#"> <a class="course__video video" href="#">
{% if course.cover %} {% if course.cover %}

@ -9,7 +9,7 @@
{% block ogtitle %}{{ course.title }} - {{ block.super }}{% endblock ogtitle %} {% block ogtitle %}{{ course.title }} - {{ block.super }}{% endblock ogtitle %}
{% block ogurl %}{{ request.build_absolute_uri }}{% endblock ogurl %} {% block ogurl %}{{ request.build_absolute_uri }}{% endblock ogurl %}
{% if course.cover %} {% if course.cover %}
{% block ogimage %}{{ request.build_absolute_uri }}{{ course.cover.url }}{% endblock ogimage %} {% block ogimage %}http://{{request.META.HTTP_HOST}}{% if course.cover %}{{ course.cover.image.url }}{% else %}{% static 'img/og_courses.jpg' %}{% endif %}{% endblock ogimage %}
{% endif %} {% endif %}
{% block ogdescription %}{{ course.short_description }}{% endblock ogdescription %} {% block ogdescription %}{{ course.short_description }}{% endblock ogdescription %}
@ -184,10 +184,15 @@
<div class="lessons__row"> <div class="lessons__row">
{% if lesson.cover %} {% if lesson.cover %}
<div class="lessons__preview"> <div class="lessons__preview">
<img class="lessons__pic" src="{{ lesson.cover.url }}"> <div class="lessons__pic-wrapper">
<img class="lessons__pic" src="{{ lesson.cover.image.url }}">
</div>
</div> </div>
{% endif %} {% endif %}
<div class="lessons__content">{{ lesson.short_description | safe }}</div> <div class="lessons__content">{{ lesson.short_description | truncatechars_html:800 | safe }}</div>
</div>
<div class="lessons__row">
<a href="{% url 'lesson' pk=lesson.id %}" class="btn btn_stroke">Перейти к уроку</a>
</div> </div>
</div> </div>
</a> </a>

@ -2,6 +2,7 @@
{% load static %} {% load static %}
{% load category_items from lilcity_category %} {% load category_items from lilcity_category %}
{% block ogimage %}http://{{request.META.HTTP_HOST}}{% static 'img/og_courses.jpg' %}{% endblock ogimage %}
{% block content %} {% block content %}
<div class="main" style="background-image: url({% static 'img/bg-1.jpg' %});"> <div class="main" style="background-image: url({% static 'img/bg-1.jpg' %});">
<div class="main__center center"> <div class="main__center center">

@ -2,11 +2,12 @@
{% load static %} {% load static %}
{% block title %}{{ lesson.title }} - {{ block.super }}{% endblock title %} {% block title %}{{ lesson.title }} - {{ block.super }}{% endblock title %}
{% block ogimage %}http://{{request.META.HTTP_HOST}}{% if lesson.course.cover %}{{ lesson.course.cover.image.url }}{% else %}{% static 'img/og_courses.jpg' %}{% endif %}{% endblock ogimage %}
{% block content %} {% block content %}
<div class="section" style="margin-bottom:0;padding-bottom:0"> <div class="section" style="margin-bottom:0;padding-bottom:0">
<div class="section__center center center_sm"> <div class="section__center center center_sm">
<div class="go"> <div class="go">
<a class="go__item" href="{% if next %}{{next}}{% else %}{% url 'course' lesson.course.id %}{% endif %}"> <a class="go__item" href="{% if next %}{{next}}{% else %}{% url 'course-only-lessons' lesson.course.id %}{% endif %}">
<div class="go__arrow"> <div class="go__arrow">
<svg class="icon icon-arrow-left"> <svg class="icon icon-arrow-left">
<use xlink:href="{% static 'img/sprite.svg' %}#icon-arrow-left"></use> <use xlink:href="{% static 'img/sprite.svg' %}#icon-arrow-left"></use>
@ -14,8 +15,8 @@
</div> </div>
<div class="go__title">Вернуться к&nbsp;списку уроков</div> <div class="go__title">Вернуться к&nbsp;списку уроков</div>
</a> </a>
{% comment %} {% if next_lesson %} {% if next_lesson %}
<a class="go__item" href="{{ next_lesson }}"> <a class="go__item" href="{% url 'lesson' pk=next_lesson %}">
<div class="go__title">Перейти к&nbsp;следующему уроку</div> <div class="go__title">Перейти к&nbsp;следующему уроку</div>
<div class="go__arrow"> <div class="go__arrow">
<svg class="icon icon-arrow-right"> <svg class="icon icon-arrow-right">
@ -23,7 +24,7 @@
</svg> </svg>
</div> </div>
</a> </a>
{% endif %} {% endcomment %} {% endif %}
</div> </div>
<div class="lesson"> <div class="lesson">
<div class="lesson__subtitle subtitle">{{ lesson.title }}</div> <div class="lesson__subtitle subtitle">{{ lesson.title }}</div>
@ -32,7 +33,7 @@
{% if lesson.cover %} {% if lesson.cover %}
<img class="video__pic" src="{{ lesson.cover.image.url }}"/> <img class="video__pic" src="{{ lesson.cover.image.url }}"/>
{% else %} {% else %}
{% endif %} {% endif %}
<svg class="icon icon-play"> <svg class="icon icon-play">
<use xlink:href="{% static 'img/sprite.svg' %}#icon-play"></use> <use xlink:href="{% static 'img/sprite.svg' %}#icon-play"></use>

@ -299,7 +299,9 @@ class LessonView(DetailView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['next'] = self.request.GET.get('next', None) context['next'] = self.request.GET.get('next', None)
context['next_lesson'] = self.request.GET.get('next_lesson', None) lessons = list(self.object.course.lessons.values_list('id', flat=True))
index = lessons.index(self.object.id)
context['next_lesson'] = lessons[index + 1] if index < len(lessons) - 1 else None
return context return context

@ -5,7 +5,7 @@
{% if school %} {% if school %}
<div class="done__title title">Вы успешно приобрели доступ к урокам онлайн-школы!</div> <div class="done__title title">Вы успешно приобрели доступ к урокам онлайн-школы!</div>
<div class="done__foot"> <div class="done__foot">
<a class="done__btn btn btn_md btn_stroke" href="{% url 'school:summer-school' %}">ПЕРЕЙТИ К ШКОЛЕ</a> <a class="done__btn btn btn_md btn_stroke" href="{% url 'user' request.user.id %}">ПЕРЕЙТИ К ШКОЛЕ</a>
</div> </div>
{% else %} {% else %}
<div class="done__title title">Вы успешно приобрели курс!</div> <div class="done__title title">Вы успешно приобрели курс!</div>

@ -0,0 +1,18 @@
# Generated by Django 2.0.5 on 2018-06-28 20:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('school', '0016_auto_20180429_0818'),
]
operations = [
migrations.AlterField(
model_name='livelesson',
name='short_description',
field=models.TextField(blank=True, default='', verbose_name='Краткое описание урока'),
),
]

@ -0,0 +1,18 @@
# Generated by Django 2.0.6 on 2018-06-29 15:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('school', '0017_auto_20180628_2000'),
]
operations = [
migrations.AlterField(
model_name='livelesson',
name='title',
field=models.CharField(blank=True, default='', max_length=100, verbose_name='Название урока'),
),
]

@ -46,7 +46,7 @@ class SchoolSchedule(models.Model):
return dict(self.WEEKDAY_CHOICES).get(self.weekday, '') return dict(self.WEEKDAY_CHOICES).get(self.weekday, '')
def is_online(self): def is_online(self):
end_at = datetime.combine(now().today(), self.start_at) + timedelta(hours=2) end_at = datetime.combine(now().today(), self.start_at) + timedelta(hours=1)
return self.start_at <= now().time() and end_at.time() >= now().time() and self.weekday == now().isoweekday() return self.start_at <= now().time() and end_at.time() >= now().time() and self.weekday == now().isoweekday()
def current_live_lesson(self): def current_live_lesson(self):
@ -98,8 +98,8 @@ class SchoolScheduleImage(models.Model):
class LiveLesson(BaseModel, DeactivatedMixin): class LiveLesson(BaseModel, DeactivatedMixin):
title = models.CharField('Название урока', max_length=100) title = models.CharField('Название урока', max_length=100, default='', blank=True)
short_description = models.TextField('Краткое описание урока') short_description = models.TextField('Краткое описание урока', default='', blank=True)
stream = models.URLField('Ссылка на VIMEO', default='', blank=True) stream = models.URLField('Ссылка на VIMEO', default='', blank=True)
date = models.DateField(default=now, unique=True) date = models.DateField(default=now, unique=True)
cover = models.ForeignKey( cover = models.ForeignKey(
@ -141,5 +141,5 @@ class LiveLesson(BaseModel, DeactivatedMixin):
return False return False
else: else:
start_at = school_schedule.start_at start_at = school_schedule.start_at
end_at = datetime.combine(now().today(), start_at) + timedelta(hours=2) end_at = datetime.combine(now().today(), start_at) + timedelta(hours=1)
return start_at <= now().time() and end_at.time() >= now().time() return start_at <= now().time() and end_at.time() >= now().time()

@ -6,17 +6,17 @@
{{ school_schedule }} {{ school_schedule }}
</div> </div>
{% if live_lesson %} {% if live_lesson %}
<!--<div class="timing__date">{{ live_lesson.date }}</div>--> <div class="timing__date">{{ live_lesson.date }}</div>
{% endif %} {% endif %}
</div> </div>
<div class="timing__buy"> <div class="timing__buy">
<div class="timing__time">{{ school_schedule.start_at }} (МСК)</div> <div class="timing__time">{{ school_schedule.start_at }} (МСК)</div>
{% if school_schedule.weekday in school_schedules_purchased %} {% if school_schedule.weekday in school_schedules_purchased %}
{% if live_lesson and school_schedule.is_online or live_lesson and is_previous and live_lesson in live_lessons %} {% if live_lesson and live_lesson.title %}
{% include './open_lesson.html' %} {% include './open_lesson.html' %}
{% endif %} {% endif %}
{% else %} {% else %}
{% include './day_pay_btn.html' %} {% include './day_pay_btn.html' %}
{% endif %} {% endif %}
</div> </div>
{% comment %} {% comment %}
@ -36,12 +36,12 @@
</div> </div>
</div> </div>
<div class="timing__cell"> <div class="timing__cell">
<div class="timing__title">{{ school_schedule.title }}{% if live_lesson %}, <div class="timing__title">{{ school_schedule.title }}{% if live_lesson and live_lesson.title %},
<span class="bold">{{ live_lesson.title }}</span> <span class="bold">{{ live_lesson.title }}</span>
{% endif %} {% endif %}
</div> </div>
<div class="timing__content"> <div class="timing__content">
{% if live_lesson %} {% if live_lesson and live_lesson.short_description %}
{{ live_lesson.short_description }} {{ live_lesson.short_description }}
{% else %} {% else %}
{{ school_schedule.description }} {{ school_schedule.description }}

@ -9,7 +9,6 @@
<div class="casing__msg">Подписка истекает <div class="casing__msg">Подписка истекает
<span class="bold">{{ subscription_ends }}</span> <span class="bold">{{ subscription_ends }}</span>
</div> </div>
{% include './prolong_btn.html' %}
{% else %} {% else %}
<div class="casing__msg">Подписка <div class="casing__msg">Подписка
<span class="bold">истекла</span> <span class="bold">истекла</span>

@ -10,7 +10,7 @@
<div class="lesson__content">{{ livelesson.short_description }}</div> <div class="lesson__content">{{ livelesson.short_description }}</div>
<a class="lesson__video video" href="#"> <a class="lesson__video video" href="#">
{% if livelesson.stream_index %} {% if livelesson.stream_index %}
<iframe class="lesson__video_frame" src="https://player.vimeo.com/video/{{ livelesson.stream_index }}" frameborder="0" webkitallowfullscreen <iframe class="lesson__video_frame" src="https://player.vimeo.com/video/{{ livelesson.stream_index }}?autoplay=1" frameborder="0" webkitallowfullscreen
mozallowfullscreen allowfullscreen> mozallowfullscreen allowfullscreen>
</iframe> </iframe>
<a href="#" onclick="location.reload();">Если видео не загрузилось обновите страницу</a> <a href="#" onclick="location.reload();">Если видео не загрузилось обновите страницу</a>

@ -1,5 +1,6 @@
{% extends "templates/lilcity/index.html" %} {% load static %} {% extends "templates/lilcity/index.html" %} {% load static %}
{% block title %}Онлайн-школа LilCity{% endblock title%} {% block title %}Онлайн-школа LilCity{% endblock title%}
{% block ogimage %}http://{{request.META.HTTP_HOST}}{% static 'img/og_summer_school.jpg' %}{% endblock %}
{% block content %} {% block content %}
{% if not is_purchased %} {% if not is_purchased %}
{% include "../summer/promo.html" %} {% include "../summer/promo.html" %}

@ -6,17 +6,17 @@
{{ school_schedule }} {{ school_schedule }}
</div> </div>
{% if live_lesson %} {% if live_lesson %}
<!--<div class="timing__date">{{ live_lesson.date }}</div>--> <div class="timing__date">{{ live_lesson.date }}</div>
{% endif %} {% endif %}
</div> </div>
<div class="timing__buy"> <div class="timing__buy">
<div class="timing__time">{{ school_schedule.start_at }} (МСК)</div> <div class="timing__time">{{ school_schedule.start_at }} (МСК)</div>
{% if school_schedule.weekday in school_schedules_purchased %} {% if school_schedule.weekday in school_schedules_purchased %}
{% if live_lesson and school_schedule.is_online or live_lesson and is_previous and live_lesson in live_lessons %} {% if live_lesson and live_lesson.title %}
{% include './open_lesson.html' %} {% include './open_lesson.html' %}
{% endif %} {% endif %}
{% else %} {% else %}
{% include './day_pay_btn.html' %} {% include './day_pay_btn.html' %}
{% endif %} {% endif %}
</div> </div>
{% comment %} {% comment %}
@ -36,12 +36,12 @@
</div> </div>
</div> </div>
<div class="timing__cell"> <div class="timing__cell">
<div class="timing__title">{{ school_schedule.title }}{% if live_lesson %}, <div class="timing__title">{{ school_schedule.title }}{% if live_lesson and live_lesson.title %},
<span class="bold">{{ live_lesson.title }}</span> <span class="bold">{{ live_lesson.title }}</span>
{% endif %} {% endif %}
</div> </div>
<div class="timing__content"> <div class="timing__content">
{% if live_lesson %} {% if live_lesson and live_lesson.short_description %}
{{ live_lesson.short_description }} {{ live_lesson.short_description }}
{% else %} {% else %}
{{ school_schedule.description }} {{ school_schedule.description }}

@ -9,7 +9,6 @@
<div class="casing__msg">Подписка истекает <div class="casing__msg">Подписка истекает
<span class="bold">{{ subscription_ends }}</span> <span class="bold">{{ subscription_ends }}</span>
</div> </div>
{% include './prolong_btn.html' %}
{% else %} {% else %}
<div class="casing__msg">Подписка <div class="casing__msg">Подписка
<span class="bold">истекла</span> <span class="bold">истекла</span>

@ -61,26 +61,26 @@ class LiveLessonsDetailView(DetailView):
def get(self, request, pk=None): def get(self, request, pk=None):
response = super().get(request, pk=pk) response = super().get(request, pk=pk)
try: #try:
school_payment = SchoolPayment.objects.get( # school_payment = SchoolPayment.objects.get(
user=request.user, # user=request.user,
add_days=False, # add_days=False,
date_start__lte=now(), # date_start__lte=now(),
date_end__gte=now(), # date_end__gte=now(),
status__in=[ # status__in=[
Pingback.PINGBACK_TYPE_REGULAR, # Pingback.PINGBACK_TYPE_REGULAR,
Pingback.PINGBACK_TYPE_GOODWILL, # Pingback.PINGBACK_TYPE_GOODWILL,
Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED, # Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED,
], # ],
) # )
except SchoolPayment.DoesNotExist: #except SchoolPayment.DoesNotExist:
school_payment = None # school_payment = None
if request.user.role not in [User.ADMIN_ROLE, User.TEACHER_ROLE] and not ( #if request.user.role not in [User.ADMIN_ROLE, User.TEACHER_ROLE] and not (
request.user.role == User.USER_ROLE and # request.user.role == User.USER_ROLE and
school_payment and # school_payment and
school_payment.is_deliverable() # school_payment.is_deliverable()
): #):
raise Http404 # raise Http404
return response return response
@ -122,7 +122,7 @@ class SchoolView(TemplateView):
end_at = datetime.combine(now_time.today(), school_schedule.start_at) end_at = datetime.combine(now_time.today(), school_schedule.start_at)
online = ( online = (
school_schedule.start_at <= now_time.time() and school_schedule.start_at <= now_time.time() and
(end_at + timedelta(hours=2)).time() >= now_time.time() and (end_at + timedelta(hours=1)).time() >= now_time.time() and
school_schedule.current_live_lesson() school_schedule.current_live_lesson()
) )
if self.request.user.is_authenticated: if self.request.user.is_authenticated:
@ -183,7 +183,7 @@ class SummerSchoolView(TemplateView):
end_at = datetime.combine(now_time.today(), school_schedule.start_at) end_at = datetime.combine(now_time.today(), school_schedule.start_at)
online = ( online = (
school_schedule.start_at <= now_time.time() and school_schedule.start_at <= now_time.time() and
(end_at + timedelta(hours=2)).time() >= now_time.time() and (end_at + timedelta(hours=1)).time() >= now_time.time() and
school_schedule.current_live_lesson() school_schedule.current_live_lesson()
) )
if self.request.user.is_authenticated: if self.request.user.is_authenticated:

@ -67,18 +67,18 @@
<div class="section__center center"> <div class="section__center center">
<div class="tabs js-tabs"> <div class="tabs js-tabs">
<div class="tabs__nav"> <div class="tabs__nav">
<button class="tabs__btn js-tabs-btn">ЛАГЕРЬ</button> <button class="tabs__btn js-tabs-btn active">ЛАГЕРЬ</button>
<button class="tabs__btn js-tabs-btn">ПРИОБРЕТЕННЫЕ <button class="tabs__btn js-tabs-btn">ПРИОБРЕТЕННЫЕ
<span class="mobile-hide">КУРСЫ</span> <span class="mobile-hide">КУРСЫ</span>
</button> </button>
{% if not simple_user %} {% if not simple_user %}
<button class="tabs__btn js-tabs-btn active">ОПУБЛИКОВАННЫЕ <button class="tabs__btn js-tabs-btn">ОПУБЛИКОВАННЫЕ
<span class="mobile-hide">КУРСЫ</span> <span class="mobile-hide">КУРСЫ</span>
</button> </button>
{% endif %} {% endif %}
</div> </div>
<div class="tabs__container"> <div class="tabs__container">
<div class="tabs__item js-tabs-item"> <div class="tabs__item js-tabs-item" style="display: block;">
{% if is_purchased_future %} {% if is_purchased_future %}
<div class="center center_xs"> <div class="center center_xs">
<div class="done"> <div class="done">
@ -127,7 +127,7 @@
</div> </div>
</div> </div>
{% if not simple_user %} {% if not simple_user %}
<div class="tabs__item js-tabs-item" style="display: block;"> <div class="tabs__item js-tabs-item">
<div class="courses courses_scroll"> <div class="courses courses_scroll">
<div class="courses__list"> <div class="courses__list">
{% if published.exists %} {% if published.exists %}

@ -0,0 +1,22 @@
# DEBUG=True
ALLOWED_HOSTS=*
PORT=8000
CORS_ORIGIN_WHITELIST=lilcity.9ev.ru:8080
LANG=ru_RU.UTF-8
POSTGRES_DB=lilcity
POSTGRES_USER=lilcity
POSTGRES_PASSWORD=GPVs/E/{5&qe
DJANGO_SETTINGS_MODULE=project.settings
DATABASE_SERVICE_HOST=db
SECRET_KEY=jelm*91lj(_-o20+6^a+bgv!4s6e_efry^#+f#=1ak&s1xr-2j
MAILGUN_API_KEY=key-ec6af2d43d031d59bff6b1c8fb9390cb
MAILGUN_SENDER_DOMAIN=mail.9ev.ru
DEFAULT_FROM_EMAIL=postmaster@mail.9ev.ru
TWILIO_ACCOUNT=ACdf4a96b776cc764bc3ec0f0e136ba550
TWILIO_TOKEN=559a6b1fce121759c9af2dcbb3f755ea
TWILIO_FROM_PHONE=+37128914409
PAYMENTWALL_APP_KEY=d6f02b90cf6b16220932f4037578aff7
PAYMENTWALL_SECRET_KEY=4ea515bf94e34cf28646c2e12a7b8707
MIXPANEL_TOKEN=79bd6bfd98667ed977737e6810b8abcd
RAVEN_DSN=https://b545dac0ae0545a1bcfc443326fe5850:6f9c900cef7f4c11b63561030b37d15c@sentry.io/1197254
ROISTAT_COUNTER_ID=09db30c750035ae3d70a41d5f10d59ec

@ -18,4 +18,5 @@ ADD . /app/
COPY --from=front /web/build/ /app/web/build/ COPY --from=front /web/build/ /app/web/build/
RUN python manage.py collectstatic --no-input RUN python manage.py collectstatic --no-input
RUN rm -rf /etc/nginx/ && cp -r docker/conf/nginx /etc/ && cp -r docker/conf/supervisor/* /etc/supervisor/conf.d/ && chown -R www-data:www-data /app/ RUN rm -rf /etc/nginx/ && cp -r docker/conf/nginx /etc/ && cp -r docker/conf/supervisor/* /etc/supervisor/conf.d/ && chown -R www-data:www-data /app/
EXPOSE 80
ENTRYPOINT ["/app/docker/entrypoint_app.sh"] ENTRYPOINT ["/app/docker/entrypoint_app.sh"]

@ -10,6 +10,11 @@ services:
- .env - .env
volumes: volumes:
- ./data/postgres:/var/lib/postgresql/data - ./data/postgres:/var/lib/postgresql/data
logging: &logging
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
redis: redis:
image: redis:4.0.9-alpine image: redis:4.0.9-alpine
@ -18,6 +23,7 @@ services:
- "127.0.0.1:6379:6379" - "127.0.0.1:6379:6379"
volumes: volumes:
- ./data/redis:/data - ./data/redis:/data
logging: *logging
app: app:
build: build:
@ -36,3 +42,4 @@ services:
links: links:
- db - db
- redis - redis
logging: *logging

@ -0,0 +1,58 @@
version: '3'
services:
db:
image: postgres:10.3-alpine
restart: always
env_file:
- .env
volumes:
- /work/data/back_${CI_COMMIT_REF_NAME}/postgres:/var/lib/postgresql/data
logging: &logging
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
networks:
- internal
- review
labels:
- traefik.enable=false
redis:
image: redis:4.0.9-alpine
restart: always
volumes:
- /work/data/back_${CI_COMMIT_REF_NAME}/redis:/data
logging: *logging
networks:
- internal
- review
labels:
- traefik.enable=false
app:
build:
context: ../
dockerfile: docker/Dockerfile
restart: always
env_file:
- .env
volumes:
- /work/data/back_${CI_COMMIT_REF_NAME}/media:/app/media
depends_on:
- db
- redis
logging: *logging
networks:
- internal
- review
labels:
- traefik.frontend.rule=Host:${REVIEW_HOST}
- traefik.docker.network=review
networks:
internal:
review:
external:
name: review

@ -1,5 +1,6 @@
#!/bin/sh #!/bin/sh
cd /app cd /app
chown www-data:www-data /app/media
python manage.py migrate python manage.py migrate
#python manage.py loaddata /app/apps/*/fixtures/*.json #python manage.py loaddata /app/apps/*/fixtures/*.json
python2.7 /usr/bin/supervisord -n python2.7 /usr/bin/supervisord -n

@ -55,6 +55,7 @@ INSTALLED_APPS = [
'corsheaders', 'corsheaders',
'sorl.thumbnail', 'sorl.thumbnail',
'raven.contrib.django.raven_compat', 'raven.contrib.django.raven_compat',
'django_user_agents',
] + [ ] + [
'apps.auth.apps', 'apps.auth.apps',
'apps.user', 'apps.user',
@ -68,6 +69,7 @@ INSTALLED_APPS = [
MIDDLEWARE = [ MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', 'corsheaders.middleware.CorsMiddleware',
'django_user_agents.middleware.UserAgentMiddleware',
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',

@ -44,21 +44,19 @@
</div> </div>
</div> </div>
<div class="letsgo"> <div class="letsgo">
<a {% if not is_purchased and not is_purchased_future %}
{% if not is_purchased_future %} <a
{% if not user.is_authenticated %} {% if not user.is_authenticated %}
data-popup=".js-popup-auth" data-popup=".js-popup-auth"
{% else %} {% else %}
data-popup=".js-popup-buy" data-popup=".js-popup-buy"
{% endif %} {% endif %}
{% endif %}
class="main__btn btn" class="main__btn btn"
href="#" href="#"
> >
{% if not is_purchased and not is_purchased_future %}купить доступ от {{ min_school_price }} руб./месяц{% endif %} купить доступ от {{ min_school_price }} руб./месяц
{% if is_purchased_future and not is_purchased %}ваша подписка начинается {{school_purchased_future.date_start}}{% endif %} </a>
{% if is_purchased %}ваша подписка истекает {{ subscription_ends_humanize }}<br/>перейти к оплате{% endif %} {% endif %}
</a>
</div> </div>
</div> </div>
</div> </div>

@ -1,7 +1,7 @@
{% load static %} {% load thumbnail %} {% load static %} {% load thumbnail %}
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<div class="header__login"> <div class="header__login">
<a class="header__ava ava" href="{% url 'user' request.user.id %}"> <a class="header__ava ava" href="{% if request.user_agent.is_touch_capable %}#{% else %}{% url 'user' request.user.id %}{% endif %}">
{% thumbnail request.user.photo "48x48" crop="center" as im %} {% thumbnail request.user.photo "48x48" crop="center" as im %}
<img class="ava__pic" src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}" /> <img class="ava__pic" src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}" />
{% empty %} {% empty %}

@ -33,7 +33,7 @@ class IndexView(TemplateView):
end_at = datetime.combine(now_time.today(), school_schedule.start_at) end_at = datetime.combine(now_time.today(), school_schedule.start_at)
online = ( online = (
school_schedule.start_at <= now_time.time() and school_schedule.start_at <= now_time.time() and
(end_at + timedelta(hours=2)).time() >= now_time.time() and (end_at + timedelta(hours=1)).time() >= now_time.time() and
school_schedule.current_live_lesson() school_schedule.current_live_lesson()
) )
online_coming_soon = ( online_coming_soon = (

@ -26,3 +26,7 @@ twilio==6.14.6
git+https://github.com/ivlevdenis/paymentwall-python.git git+https://github.com/ivlevdenis/paymentwall-python.git
# python-instagram==1.3.2 # python-instagram==1.3.2
git+https://github.com/ivlevdenis/python-instagram.git git+https://github.com/ivlevdenis/python-instagram.git
django-user-agents==0.3.2
user-agents==1.1.0
ua-parser==0.8.0

@ -84,13 +84,13 @@
</div> </div>
</div> </div>
<div v-if="live" class="info__field field" <!-- <div v-if="live" class="info__field field"
v-bind:class="{ error: ($v.course.date.$dirty || showErrors) && $v.course.date.$invalid }"> v-bind:class="{ error: ($v.course.date.$dirty || showErrors) && $v.course.date.$invalid }">
<div class="field__label">ДАТА</div> <div class="field__label">ДАТА</div>
<div class="field__wrap"> <div class="field__wrap">
<lil-select :value.sync="course.date" :options="scheduleOptions" placeholder="Выберите дату"/> <lil-select :value.sync="course.date" :options="scheduleOptions" placeholder="Выберите дату"/>
</div> </div>
</div> </div> -->
<div v-if="!live" class="info__field field"> <div v-if="!live" class="info__field field">
<div class="field__label field__label_gray">ДОСТУП</div> <div class="field__label field__label_gray">ДОСТУП</div>
@ -656,11 +656,11 @@
this.course = api.convertCourseJson(response.data); this.course = api.convertCourseJson(response.data);
this.course.live = this.live; this.course.live = this.live;
if (this.live && this.course.date) { /* if (this.live && this.course.date) {
this.course.date = _.find(this.scheduleOptions, (item) => { this.course.date = _.find(this.scheduleOptions, (item) => {
return item.value == this.course.date; return item.value == this.course.date;
}) })
} } */
this.$nextTick(() => { this.$nextTick(() => {
this.courseLoading = false; this.courseLoading = false;
@ -841,11 +841,11 @@
this.course.id = courseData.id; this.course.id = courseData.id;
} }
if(this.live && courseData.date) { /*if(this.live && courseData.date) {
this.course.date = _.find(this.scheduleOptions, function(item){ this.course.date = _.find(this.scheduleOptions, function(item){
return item.value == courseData.date; return item.value == courseData.date;
}); });
} }*/
this.$nextTick(() => { this.$nextTick(() => {
this.courseSyncHook = false; this.courseSyncHook = false;
}); });

@ -14,16 +14,22 @@
</div> </div>
<div class="kit__title title">{{ title }}</div> <div class="kit__title title">{{ title }}</div>
<div class="kit__section"> <div class="kit__section">
<div class="kit__field field" <div class="kit__row">
v-bind:class="{ error: $v.currentLesson.title.$invalid }"> <lil-image :image-id.sync="lesson.coverImageId" :image-url.sync="lesson.coverImage"
<div class="field__wrap"> v-on:update:imageUrl="onUpdateCoverUrl" v-on:update:imageId="onUpdateCoverId" :access-token="accessToken"/>
<input type="text" class="field__input" placeholder="Название урока" v-model="lesson.title"> <div class="kit__fieldset">
</div> <div class="kit__field field"
</div> v-bind:class="{ error: $v.currentLesson.title.$invalid }">
<div class="kit__field field" <div class="field__wrap">
v-bind:class="{ error: $v.currentLesson.short_description.$invalid }"> <input type="text" class="field__input" placeholder="Название урока" v-model="lesson.title">
<div class="field__wrap"> </div>
<textarea class="field__input" v-autosize="lesson.short_description" placeholder="Описание урока" v-model="lesson.short_description"></textarea> </div>
<div class="kit__field field"
v-bind:class="{ error: $v.currentLesson.short_description.$invalid }">
<div class="field__wrap">
<textarea class="field__input" v-autosize="lesson.short_description" placeholder="Описание урока" v-model="lesson.short_description"></textarea>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -81,6 +87,7 @@
import BlockImages from './blocks/BlockImages' import BlockImages from './blocks/BlockImages'
import BlockImageText from './blocks/BlockImageText' import BlockImageText from './blocks/BlockImageText'
import BlockVideo from './blocks/BlockVideo' import BlockVideo from './blocks/BlockVideo'
import LilImage from "./blocks/Image"
import {api} from "../js/modules/api"; import {api} from "../js/modules/api";
import Draggable from 'vuedraggable'; import Draggable from 'vuedraggable';
import _ from 'lodash' import _ from 'lodash'
@ -105,7 +112,13 @@
if (blockToRemove.data.id) { if (blockToRemove.data.id) {
api.removeContentBlock(blockToRemove, this.accessToken); api.removeContentBlock(blockToRemove, this.accessToken);
} }
} },
onUpdateCoverUrl(newValue) {
this.lesson.coverImage = newValue;
},
onUpdateCoverId(newValue) {
this.lesson.coverImageId = newValue;
},
}, },
computed: { computed: {
title() { title() {
@ -120,10 +133,11 @@
'block-images': BlockImages, 'block-images': BlockImages,
'block-video': BlockVideo, 'block-video': BlockVideo,
'vue-draggable': Draggable, 'vue-draggable': Draggable,
'lil-image': LilImage,
} }
} }
</script> </script>
<style scoped> <style scoped>
</style> </style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 KiB

@ -204,6 +204,7 @@ export const api = {
const isAdding = (!lessonObject.hasOwnProperty('id') || !lessonObject.hasOwnProperty('id')); const isAdding = (!lessonObject.hasOwnProperty('id') || !lessonObject.hasOwnProperty('id'));
const lessonJson = { const lessonJson = {
cover: lessonObject.coverImageId ? lessonObject.coverImageId : null,
title: lessonObject.title, title: lessonObject.title,
short_description: lessonObject.short_description, short_description: lessonObject.short_description,
course: lessonObject.course_id, course: lessonObject.course_id,
@ -284,6 +285,8 @@ export const api = {
id: lessonJSON.id, id: lessonJSON.id,
title: lessonJSON.title, title: lessonJSON.title,
short_description: lessonJSON.short_description, short_description: lessonJSON.short_description,
coverImageId: lessonJSON.cover && lessonJSON.cover.id ? lessonJSON.cover.id : null,
coverImage: lessonJSON.cover && lessonJSON.cover.image ? lessonJSON.cover.image : null,
content: api.convertContentResponse(lessonJSON.content) content: api.convertContentResponse(lessonJSON.content)
} }
}, },

@ -2648,8 +2648,15 @@ a.grey-link
flex: 0 0 140px flex: 0 0 140px
+m +m
display: none display: none
&__pic-wrapper
width: 130px;
height: 130px;
border-radius: 50%;
overflow: hidden;
&__pic &__pic
display: block top: 50%;
position: relative;
transform: translateY(-50%);
width: 100% width: 100%
&__content &__content
flex: 0 0 calc(100% - 165px) flex: 0 0 calc(100% - 165px)
@ -3061,8 +3068,6 @@ a.grey-link
border-bottom: 1px solid $border border-bottom: 1px solid $border
align-items: center align-items: center
justify-content: center justify-content: center
+m
margin: 0 -15px 30px
&__btn &__btn
height: 56px height: 56px
border-bottom: 1px solid $border border-bottom: 1px solid $border
@ -3072,7 +3077,8 @@ a.grey-link
letter-spacing: 1px letter-spacing: 1px
transition: border-color .2s, color .2s transition: border-color .2s, color .2s
+m +m
flex: 0 0 50% flex: 0 0 35%
font-size: 10px
&:not(:last-child) &:not(:last-child)
margin-right: 40px margin-right: 40px
+m +m

Loading…
Cancel
Save