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:
- deploy
- deploy
- db
- stop
variables:
REVIEW_DOMAIN: back-review.lil.school
deploy_prod:
stage: deploy
script:
- rsync -a --stats --delete --exclude="docker/data/" --exclude="docker/.env" ./ /work/www/lil.school/
- 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:
name: prod/site
name: prod
url: https://lil.school
only:
- master
tags:
- 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__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>
</div>

@ -9,7 +9,7 @@
{% block ogtitle %}{{ course.title }} - {{ block.super }}{% endblock ogtitle %}
{% block ogurl %}{{ request.build_absolute_uri }}{% endblock ogurl %}
{% 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 %}
{% block ogdescription %}{{ course.short_description }}{% endblock ogdescription %}
@ -141,37 +141,45 @@
<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>
{% if request.user.is_authenticated %}
{% if course.author == request.user and request.user.role >= request.user.AUTHOR_ROLE %}
<a
href="{% url 'course-only-lessons' course.id %}"
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 %}
>УРОКИ
</a>
{% elif request.user.role == request.user.ADMIN_ROLE %}
<a
href="{% url 'course-only-lessons' course.id %}"
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 %}
>УРОКИ
</a>
{% if course.author == request.user and request.user.role >= request.user.AUTHOR_ROLE %}
<a
href="{% url 'course-only-lessons' course.id %}"
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 %}
>УРОКИ
</a>
{% elif request.user.role == request.user.ADMIN_ROLE %}
<a
href="{% url 'course-only-lessons' course.id %}"
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 %}
>УРОКИ
</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 %}
<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 %}
data-popup=".js-popup-auth">УРОКИ
<svg class="icon icon-lock">
<use xlink:href="{% static 'img/sprite.svg' %}#icon-lock"></use>
</svg>
{% endif %}
</a>
{% endif %}
{% endif %}
</div>
<a class="course__video video" href="#">
{% if course.cover %}

@ -9,7 +9,7 @@
{% block ogtitle %}{{ course.title }} - {{ block.super }}{% endblock ogtitle %}
{% block ogurl %}{{ request.build_absolute_uri }}{% endblock ogurl %}
{% 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 %}
{% block ogdescription %}{{ course.short_description }}{% endblock ogdescription %}
@ -184,10 +184,15 @@
<div class="lessons__row">
{% if lesson.cover %}
<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>
{% 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>
</a>

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

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

@ -299,7 +299,9 @@ class LessonView(DetailView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
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

@ -5,7 +5,7 @@
{% if school %}
<div class="done__title title">Вы успешно приобрели доступ к урокам онлайн-школы!</div>
<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>
{% else %}
<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, '')
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()
def current_live_lesson(self):
@ -98,8 +98,8 @@ class SchoolScheduleImage(models.Model):
class LiveLesson(BaseModel, DeactivatedMixin):
title = models.CharField('Название урока', max_length=100)
short_description = models.TextField('Краткое описание урока')
title = models.CharField('Название урока', max_length=100, default='', blank=True)
short_description = models.TextField('Краткое описание урока', default='', blank=True)
stream = models.URLField('Ссылка на VIMEO', default='', blank=True)
date = models.DateField(default=now, unique=True)
cover = models.ForeignKey(
@ -141,5 +141,5 @@ class LiveLesson(BaseModel, DeactivatedMixin):
return False
else:
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()

@ -6,17 +6,17 @@
{{ school_schedule }}
</div>
{% if live_lesson %}
<!--<div class="timing__date">{{ live_lesson.date }}</div>-->
<div class="timing__date">{{ live_lesson.date }}</div>
{% endif %}
</div>
<div class="timing__buy">
<div class="timing__time">{{ school_schedule.start_at }} (МСК)</div>
{% 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 %}
{% include './open_lesson.html' %}
{% endif %}
{% if live_lesson and live_lesson.title %}
{% include './open_lesson.html' %}
{% endif %}
{% else %}
{% include './day_pay_btn.html' %}
{% include './day_pay_btn.html' %}
{% endif %}
</div>
{% comment %}
@ -36,12 +36,12 @@
</div>
</div>
<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>
{% endif %}
</div>
<div class="timing__content">
{% if live_lesson %}
{% if live_lesson and live_lesson.short_description %}
{{ live_lesson.short_description }}
{% else %}
{{ school_schedule.description }}

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

@ -10,7 +10,7 @@
<div class="lesson__content">{{ livelesson.short_description }}</div>
<a class="lesson__video video" href="#">
{% 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>
</iframe>
<a href="#" onclick="location.reload();">Если видео не загрузилось обновите страницу</a>

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

@ -6,17 +6,17 @@
{{ school_schedule }}
</div>
{% if live_lesson %}
<!--<div class="timing__date">{{ live_lesson.date }}</div>-->
<div class="timing__date">{{ live_lesson.date }}</div>
{% endif %}
</div>
<div class="timing__buy">
<div class="timing__time">{{ school_schedule.start_at }} (МСК)</div>
{% 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 %}
{% include './open_lesson.html' %}
{% endif %}
{% if live_lesson and live_lesson.title %}
{% include './open_lesson.html' %}
{% endif %}
{% else %}
{% include './day_pay_btn.html' %}
{% include './day_pay_btn.html' %}
{% endif %}
</div>
{% comment %}
@ -36,12 +36,12 @@
</div>
</div>
<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>
{% endif %}
</div>
<div class="timing__content">
{% if live_lesson %}
{% if live_lesson and live_lesson.short_description %}
{{ live_lesson.short_description }}
{% else %}
{{ school_schedule.description }}

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

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

@ -67,18 +67,18 @@
<div class="section__center center">
<div class="tabs js-tabs">
<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">ПРИОБРЕТЕННЫЕ
<span class="mobile-hide">КУРСЫ</span>
</button>
{% if not simple_user %}
<button class="tabs__btn js-tabs-btn active">ОПУБЛИКОВАННЫЕ
<button class="tabs__btn js-tabs-btn">ОПУБЛИКОВАННЫЕ
<span class="mobile-hide">КУРСЫ</span>
</button>
{% endif %}
</div>
<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 %}
<div class="center center_xs">
<div class="done">
@ -127,7 +127,7 @@
</div>
</div>
{% 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__list">
{% 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/
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/
EXPOSE 80
ENTRYPOINT ["/app/docker/entrypoint_app.sh"]

@ -10,6 +10,11 @@ services:
- .env
volumes:
- ./data/postgres:/var/lib/postgresql/data
logging: &logging
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
redis:
image: redis:4.0.9-alpine
@ -18,6 +23,7 @@ services:
- "127.0.0.1:6379:6379"
volumes:
- ./data/redis:/data
logging: *logging
app:
build:
@ -36,3 +42,4 @@ services:
links:
- db
- 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
cd /app
chown www-data:www-data /app/media
python manage.py migrate
#python manage.py loaddata /app/apps/*/fixtures/*.json
python2.7 /usr/bin/supervisord -n

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

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

@ -1,7 +1,7 @@
{% load static %} {% load thumbnail %}
{% if request.user.is_authenticated %}
<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 %}
<img class="ava__pic" src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}" />
{% empty %}

@ -33,7 +33,7 @@ class IndexView(TemplateView):
end_at = datetime.combine(now_time.today(), school_schedule.start_at)
online = (
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()
)
online_coming_soon = (

@ -26,3 +26,7 @@ twilio==6.14.6
git+https://github.com/ivlevdenis/paymentwall-python.git
# python-instagram==1.3.2
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 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 }">
<div class="field__label">ДАТА</div>
<div class="field__wrap">
<lil-select :value.sync="course.date" :options="scheduleOptions" placeholder="Выберите дату"/>
</div>
</div>
</div> -->
<div v-if="!live" class="info__field field">
<div class="field__label field__label_gray">ДОСТУП</div>
@ -656,11 +656,11 @@
this.course = api.convertCourseJson(response.data);
this.course.live = this.live;
if (this.live && this.course.date) {
/* if (this.live && this.course.date) {
this.course.date = _.find(this.scheduleOptions, (item) => {
return item.value == this.course.date;
})
}
} */
this.$nextTick(() => {
this.courseLoading = false;
@ -841,11 +841,11 @@
this.course.id = courseData.id;
}
if(this.live && courseData.date) {
/*if(this.live && courseData.date) {
this.course.date = _.find(this.scheduleOptions, function(item){
return item.value == courseData.date;
});
}
}*/
this.$nextTick(() => {
this.courseSyncHook = false;
});

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

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

Loading…
Cancel
Save