From fae11c2d4ffc5136044548ff255fc2c13879182f Mon Sep 17 00:00:00 2001 From: gzbender Date: Tue, 17 Jul 2018 15:12:38 +0500 Subject: [PATCH 001/137] =?UTF-8?q?LIL-560=20=D0=9A=D0=BD=D0=BE=D0=BF?= =?UTF-8?q?=D0=BA=D1=83=20=C2=AB=D0=A1=D0=BC=D0=BE=D1=82=D1=80=D0=B5=D1=82?= =?UTF-8?q?=D1=8C=20=D1=83=D1=80=D0=BE=D0=BA=C2=BB=20=D0=BF=D0=BE=D0=B4?= =?UTF-8?q?=D0=BD=D1=8F=D1=82=D1=8C=20=D0=B2=D1=8B=D1=88=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../summer/_schedule_purchased_item.html | 90 ++++++++++--------- apps/school/templates/summer/open_lesson.html | 2 +- web/src/sass/_common.sass | 29 ++---- 3 files changed, 56 insertions(+), 65 deletions(-) diff --git a/apps/school/templates/summer/_schedule_purchased_item.html b/apps/school/templates/summer/_schedule_purchased_item.html index 4620367c..0e86ccba 100644 --- a/apps/school/templates/summer/_schedule_purchased_item.html +++ b/apps/school/templates/summer/_schedule_purchased_item.html @@ -1,6 +1,6 @@ {% load static %} {% load thumbnail %}
-
+
{{ school_schedule }} @@ -26,53 +26,55 @@ {% endif %}
-
-
- {% thumbnail live_lesson.cover.image "70x70" crop="center" as im %} - - {% empty %} - - {% endthumbnail %} +
+
+
+ {% thumbnail live_lesson.cover.image "70x70" crop="center" as im %} + + {% empty %} + + {% endthumbnail %} +
-
-
-
{{ school_schedule.title }}{% if live_lesson and live_lesson.title %}, - {{ live_lesson.title }} +
+
{{ school_schedule.title }}{% if live_lesson and live_lesson.title %}, + {{ live_lesson.title }} + {% endif %} +
+
+ {% if live_lesson and live_lesson.short_description %} + {{ live_lesson.short_description }} + {% else %} + {{ school_schedule.description }} {% endif %} -
-
- {% if live_lesson and live_lesson.short_description %} - {{ live_lesson.short_description }} - {% else %} - {{ school_schedule.description }} - {% endif %} -
-
-
Материалы
-
-
- {{ school_schedule.materials }} -
- {% if school_schedule.schoolschedule_images.exists %} -
Результаты прошлых уроков
- -
-
- +
+ +
diff --git a/apps/school/templates/summer/open_lesson.html b/apps/school/templates/summer/open_lesson.html index 46999c7f..abb8a9a2 100644 --- a/apps/school/templates/summer/open_lesson.html +++ b/apps/school/templates/summer/open_lesson.html @@ -1,4 +1,4 @@ смотреть урок +>подробнее diff --git a/web/src/sass/_common.sass b/web/src/sass/_common.sass index 033ce477..01efd236 100755 --- a/web/src/sass/_common.sass +++ b/web/src/sass/_common.sass @@ -3731,6 +3731,8 @@ a.grey-link box-shadow: -40px 0 0 0 $pink-light, 40px 0 0 0 $pink-light +nf border-color: transparent + +m + display: block &__item.open padding-bottom: 40px &__item.open &__toggle @@ -3750,14 +3752,8 @@ a.grey-link &__item.disable &__title, &__item.disable &__content opacity: .4 - &__item.disable &__cell - &:nth-child(1) - justify-content: center - &:nth-child(3) - +m - padding: 0 0 50px &__cell - &:nth-child(1) + &--info display: flex padding-right: 20px flex-direction: column @@ -3766,15 +3762,15 @@ a.grey-link +m padding: 0 flex: 0 0 0 - &:nth-child(2) + margin-bottom: 15px + &--preview padding-right: 20px flex: 0 0 90px - &:nth-child(3) + &--content flex: 0 0 calc(100% - 254px) +m - padding-top: 20px flex: 0 0 calc(100% - 114px) - &:nth-child(4) + &--toggle flex: 0 0 34px &__unlock +m @@ -3785,11 +3781,6 @@ a.grey-link margin-bottom: auto +m display: flex - position: absolute - top: 20px - left: 90px - right: 0 - padding: 0 flex-direction: row &__day position: relative @@ -3816,10 +3807,8 @@ a.grey-link margin: -3px 0 0 auto &__buy +m - position: absolute - left: 0 - bottom: 40px - align-items: center + display: flex + margin-left: 20px &__time margin: 15px 0 opacity: .5 From c36f68300a919f9e526387b65fc6cc872148b353 Mon Sep 17 00:00:00 2001 From: gzbender Date: Tue, 17 Jul 2018 22:05:22 +0500 Subject: [PATCH 002/137] =?UTF-8?q?LIL-572=20=D0=9D=D0=B5=D0=B7=D0=B0?= =?UTF-8?q?=D1=80=D0=B5=D0=B3=D0=B8=D1=81=D1=82=D1=80=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D0=BD=D1=8B=D0=B9=20=D0=BF=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=20=D0=BD=D0=B5?= =?UTF-8?q?=20=D0=B2=D0=B8=D0=B4=D0=B8=D1=82=20=D0=BA=D1=83=D1=80=D1=81?= =?UTF-8?q?=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/user/templates/user/profile.html | 14 ++++++-------- apps/user/views.py | 1 + 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/user/templates/user/profile.html b/apps/user/templates/user/profile.html index caddc204..6b0f0b07 100644 --- a/apps/user/templates/user/profile.html +++ b/apps/user/templates/user/profile.html @@ -62,22 +62,22 @@
-{% if not guest %}
+ {% if owner %} - {% if not simple_user %} - - {% endif %}
+ {% if owner %}
{% if is_purchased_future %}
@@ -126,8 +126,8 @@
- {% if not simple_user %} -
+ {% endif %} +
{% if published.exists %} @@ -145,10 +145,8 @@
- {% endif %}
-{% endif %} {% endblock content %} diff --git a/apps/user/views.py b/apps/user/views.py index 14ecef7b..500d8404 100644 --- a/apps/user/views.py +++ b/apps/user/views.py @@ -56,6 +56,7 @@ class UserView(DetailView): else: context['simple_user'] = True context['guest'] = True + context['owner'] = self.request.user.id == self.object.id if context['guest'] and self.object.role <= User.USER_ROLE: raise Http404() From e5e5a065836c5203b2cd4a0168d41fef966d657e Mon Sep 17 00:00:00 2001 From: gzbender Date: Tue, 17 Jul 2018 22:47:45 +0500 Subject: [PATCH 003/137] =?UTF-8?q?LIL-574=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=B8=D1=82=D1=8C=20=D1=81=D1=83=D0=BC=D0=BC=D0=B0/?= =?UTF-8?q?=D0=BC=D0=B5=D1=81=D1=8F=D1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- project/templates/blocks/popup_buy.html | 4 ++-- web/src/sass/_common.sass | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/project/templates/blocks/popup_buy.html b/project/templates/blocks/popup_buy.html index f35c3474..96b3b29d 100644 --- a/project/templates/blocks/popup_buy.html +++ b/project/templates/blocks/popup_buy.html @@ -37,7 +37,7 @@ {% comment %} dont delete {% endcomment %} {{ school_schedule.title }} - {{school_schedule.month_price}}р + {{school_schedule.month_price}}р в мес. {% endfor %} @@ -58,7 +58,7 @@ {% comment %} dont delete {% endcomment %} {{ school_schedule.title }} - {{school_schedule.month_price}}р + {{school_schedule.month_price}}р в мес. {% endfor %} diff --git a/web/src/sass/_common.sass b/web/src/sass/_common.sass index 033ce477..8b994f06 100755 --- a/web/src/sass/_common.sass +++ b/web/src/sass/_common.sass @@ -1832,17 +1832,17 @@ a.grey-link padding: 0 text-align: right flex: 0 0 calc(100% - 110px) - &:nth-child(2), - &:last-child + &:nth-child(2) flex: 0 0 60px &:nth-child(3) - flex: 0 0 calc(100% - 230px) + flex: 0 0 calc(100% - 290px) +t padding: 0 - flex: 0 0 calc(100% - 60px) + flex: 0 0 calc(100% - 120px) &:last-child padding: 0 text-align: right + flex: 0 0 120px &_blue &__content &:after background: #4A90E2 From cb809cb8fa154831359c72e80251f64ea97ddbd6 Mon Sep 17 00:00:00 2001 From: gzbender Date: Thu, 19 Jul 2018 14:55:42 +0500 Subject: [PATCH 004/137] =?UTF-8?q?LIL-560=20=D0=9A=D0=BD=D0=BE=D0=BF?= =?UTF-8?q?=D0=BA=D1=83=20=C2=AB=D0=A1=D0=BC=D0=BE=D1=82=D1=80=D0=B5=D1=82?= =?UTF-8?q?=D1=8C=20=D1=83=D1=80=D0=BE=D0=BA=C2=BB=20=D0=BF=D0=BE=D0=B4?= =?UTF-8?q?=D0=BD=D1=8F=D1=82=D1=8C=20=D0=B2=D1=8B=D1=88=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blocks/_schedule_purchased_item.html | 78 ------------------- apps/school/templates/blocks/schedule.html | 69 +--------------- .../schedule_item.html} | 38 +++++---- .../templates/blocks/schedule_purchased.html | 4 +- .../templates/school/schedules_print.html | 62 +-------------- .../templates/summer/schedule_purchased.html | 4 +- web/src/sass/_common.sass | 11 ++- 7 files changed, 41 insertions(+), 225 deletions(-) delete mode 100644 apps/school/templates/blocks/_schedule_purchased_item.html rename apps/school/templates/{summer/_schedule_purchased_item.html => blocks/schedule_item.html} (70%) diff --git a/apps/school/templates/blocks/_schedule_purchased_item.html b/apps/school/templates/blocks/_schedule_purchased_item.html deleted file mode 100644 index b9d1db1f..00000000 --- a/apps/school/templates/blocks/_schedule_purchased_item.html +++ /dev/null @@ -1,78 +0,0 @@ -{% load static %} {% load thumbnail %} -
-
-
-
- {{ school_schedule }} -
- {% if live_lesson %} -
{{ live_lesson.date }}
- {% endif %} -
-
-
{{ school_schedule.start_at }} (МСК)
- {% if school_schedule.weekday in school_schedules_purchased %} - {% if live_lesson and live_lesson.title %} - {% include './open_lesson.html' %} - {% endif %} - {% else %} - {% include './day_pay_btn.html' %} - {% endif %} -
- {% comment %} - - {% endcomment %} - {% if school_schedule.weekday in school_schedules_purchased %} - - {% endif %} -
-
-
- {% thumbnail live_lesson.cover.image "70x70" crop="center" as im %} - - {% empty %} - - {% endthumbnail %} -
-
-
-
{{ school_schedule.title }}{% if live_lesson and live_lesson.title %}, - {{ live_lesson.title }} - {% endif %} -
-
- {% if live_lesson and live_lesson.short_description %} - {{ live_lesson.short_description }} - {% else %} - {{ school_schedule.description }} - {% endif %} -
-
-
Материалы
-
-
- {{ school_schedule.materials }} -
-
- {% if school_schedule.schoolschedule_images.exists %} -
Результаты прошлых уроков
- - {% endif %} -
-
-
- -
-
diff --git a/apps/school/templates/blocks/schedule.html b/apps/school/templates/blocks/schedule.html index 2e4c6457..5f088511 100644 --- a/apps/school/templates/blocks/schedule.html +++ b/apps/school/templates/blocks/schedule.html @@ -6,74 +6,7 @@
{% for school_schedule in school_schedules %} - {% with current_live_lesson=school_schedule.current_live_lesson %} -
-
-
-
- {{ school_schedule }} -
- {% if current_live_lesson %} - - {% endif %} -
-
-
{{ school_schedule.start_at }} (МСК)
- -
-
-
-
- {% thumbnail current_live_lesson.cover.image "70x70" crop="center" as im %} - - {% empty %} - - {% endthumbnail %} -
-
-
-
{{ school_schedule.title }} - {% if current_live_lesson and current_live_lesson.title %} - , {{ current_live_lesson.title }} - {% endif %} -
-
- {% if live_lesson %} - {{ live_lesson.short_description }} - {% else %} - {{ school_schedule.description }} - {% endif %} -
-
-
Материалы
-
-
- {{ school_schedule.materials }} -
-
- {% if school_schedule.schoolschedule_images.exists %} -
Результаты прошлых уроков
- - {% endif %} -
-
-
- -
-
- {% endwith %} + {% include 'blocks/schedule_item.html' with school_schedule=school_schedule live_lesson=school_schedule.current_live_lesson purchased=True %} {% endfor %}
diff --git a/apps/school/templates/summer/_schedule_purchased_item.html b/apps/school/templates/blocks/schedule_item.html similarity index 70% rename from apps/school/templates/summer/_schedule_purchased_item.html rename to apps/school/templates/blocks/schedule_item.html index 0e86ccba..0cafb9f3 100644 --- a/apps/school/templates/summer/_schedule_purchased_item.html +++ b/apps/school/templates/blocks/schedule_item.html @@ -1,21 +1,23 @@ {% load static %} {% load thumbnail %} -
+
{{ school_schedule }}
- {% if live_lesson %} + {% if purchased and live_lesson %}
{{ live_lesson.date }}
{% endif %} +
{{ school_schedule.start_at }} (МСК)
-
{{ school_schedule.start_at }} (МСК)
- {% if school_schedule.weekday in school_schedules_purchased %} - {% if live_lesson and live_lesson.title %} - {% include './open_lesson.html' %} + {% if purchased %} + {% if school_schedule.weekday in school_schedules_purchased %} + {% if live_lesson and live_lesson.title %} + {% include './open_lesson.html' %} + {% endif %} + {% else %} + {% include './day_pay_btn.html' %} {% endif %} - {% else %} - {% include './day_pay_btn.html' %} {% endif %}
@@ -36,7 +38,7 @@ {% endthumbnail %}
-
+
{{ school_schedule.title }}{% if live_lesson and live_lesson.title %}, {{ live_lesson.title }} {% endif %} @@ -51,11 +53,19 @@
Материалы
-
- {{ school_schedule.materials }} -
+ {% if print %} +
+
+ {{ school_schedule.materials }} +
+
+ {% else %} +
+ {{ school_schedule.materials }} +
+ {% endif %}
- {% if school_schedule.schoolschedule_images.exists %} + {% if not print and school_schedule.schoolschedule_images.exists %}
Результаты прошлых уроков
+ {% if not print %}
+ {% endif %}
diff --git a/apps/school/templates/blocks/schedule_purchased.html b/apps/school/templates/blocks/schedule_purchased.html index 7f8f035d..5d1a5827 100644 --- a/apps/school/templates/blocks/schedule_purchased.html +++ b/apps/school/templates/blocks/schedule_purchased.html @@ -41,10 +41,10 @@ {% for school_schedule in school_schedules %} {% if is_previous %} {% if school_schedule.previous_live_lesson in live_lessons %} - {% include './_schedule_purchased_item.html' with school_schedule=school_schedule live_lesson=school_schedule.previous_live_lesson %} + {% include 'blocks/schedule_item.html' with school_schedule=school_schedule live_lesson=school_schedule.previous_live_lesson purchased=True %} {% endif %} {% else %} - {% include './_schedule_purchased_item.html' with school_schedule=school_schedule live_lesson=school_schedule.current_live_lesson %} + {% include 'blocks/schedule_item.html' with school_schedule=school_schedule live_lesson=school_schedule.current_live_lesson purchased=True %} {% endif %} {% endfor %} {% endif %} diff --git a/apps/school/templates/school/schedules_print.html b/apps/school/templates/school/schedules_print.html index 25aca06e..66c936cb 100644 --- a/apps/school/templates/school/schedules_print.html +++ b/apps/school/templates/school/schedules_print.html @@ -11,70 +11,16 @@ - +
Расписание
- {% for school_schedule in school_schedules %} {% with current_live_lesson=school_schedule.current_live_lesson %} -
-
-
-
- {{ school_schedule }} -
- {% if current_live_lesson %} - - {% endif %} -
-
-
{{ school_schedule.start_at }} (МСК)
-
-
-
-
- {% thumbnail current_live_lesson.cover.image "70x70" crop="center" as im %} - {% empty %} - {% endthumbnail %} -
-
-
-
{{ school_schedule.title }}{% if current_live_lesson %}, - {{ current_live_lesson.title }} - {% endif %} -
-
- {% if live_lesson %} - {{ live_lesson.short_description }} - {% else %} - {{ school_schedule.description }} - {% endif %} -
-
-
Материалы
-
-
-
- {{ school_schedule.materials }} -
-
-
- - - - - - - - - - -
-
-
- {% endwith %} {% endfor %} + {% for school_schedule in school_schedules %} + {% include 'blocks/schedule_item.html' with school_schedule=school_schedule live_lesson=school_schedule.current_live_lesson print=True %} + {% endfor %}
diff --git a/apps/school/templates/summer/schedule_purchased.html b/apps/school/templates/summer/schedule_purchased.html index 63e963fd..e327fc96 100644 --- a/apps/school/templates/summer/schedule_purchased.html +++ b/apps/school/templates/summer/schedule_purchased.html @@ -41,10 +41,10 @@ {% for school_schedule in school_schedules %} {% if is_previous %} {% if school_schedule.previous_live_lesson in live_lessons %} - {% include './_schedule_purchased_item.html' with school_schedule=school_schedule live_lesson=school_schedule.previous_live_lesson %} + {% include 'blocks/schedule_item.html' with school_schedule=school_schedule live_lesson=school_schedule.previous_live_lesson purchased=True %} {% endif %} {% else %} - {% include './_schedule_purchased_item.html' with school_schedule=school_schedule live_lesson=school_schedule.current_live_lesson %} + {% include 'blocks/schedule_item.html' with school_schedule=school_schedule live_lesson=school_schedule.current_live_lesson purchased=True %} {% endif %} {% endfor %} {% endif %} diff --git a/web/src/sass/_common.sass b/web/src/sass/_common.sass index 8386a037..f9cee7ff 100755 --- a/web/src/sass/_common.sass +++ b/web/src/sass/_common.sass @@ -3782,6 +3782,7 @@ a.grey-link +m display: flex flex-direction: row + width: 100% &__day position: relative margin: 10px 0 5px @@ -3807,20 +3808,22 @@ a.grey-link margin: -3px 0 0 auto &__buy +m - display: flex - margin-left: 20px + flex: 1 0 0 + text-align: right &__time margin: 15px 0 opacity: .5 +m - width: 90px - margin: 0 + margin: -1px 15px + font-size: 12px; &__btn margin-right: -60px padding-left: 20px padding-right: 20px white-space: nowrap letter-spacing: 1px + +m + margin-right: 0 &__pic display: block width: 100% From 1cbb6fd39dcaed345f03d905b6ebb25b1e6d3f04 Mon Sep 17 00:00:00 2001 From: gzbender Date: Thu, 19 Jul 2018 23:38:54 +0500 Subject: [PATCH 005/137] =?UTF-8?q?LIL-576=20=D0=9F=D0=BE=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D1=82=D1=8C=20=D0=B2=D0=B5=D1=80=D1=81=D1=82?= =?UTF-8?q?=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/course/course_only_lessons.html | 15 +++++------ apps/course/templates/course/lesson.html | 24 ++++++++--------- web/src/sass/_common.sass | 27 +++++++++++++++---- 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/apps/course/templates/course/course_only_lessons.html b/apps/course/templates/course/course_only_lessons.html index f0217bcf..0f8c0483 100644 --- a/apps/course/templates/course/course_only_lessons.html +++ b/apps/course/templates/course/course_only_lessons.html @@ -180,19 +180,18 @@ {% endif %} diff --git a/apps/course/templates/course/lesson.html b/apps/course/templates/course/lesson.html index 662a4dd5..48d64ef8 100644 --- a/apps/course/templates/course/lesson.html +++ b/apps/course/templates/course/lesson.html @@ -27,18 +27,18 @@ {% endif %}
-
{{ lesson.title }}
-
{{ lesson.short_description }}
- {% comment %} - {% if lesson.cover %} - - {% else %} - - {% endif %} - - - - {% endcomment %} +
+
+
+ +
+
+
+
{{ lesson.title }}
+
{{ lesson.short_description }}
+
+
diff --git a/web/src/sass/_common.sass b/web/src/sass/_common.sass index 8386a037..fe137963 100755 --- a/web/src/sass/_common.sass +++ b/web/src/sass/_common.sass @@ -175,7 +175,7 @@ button top: 1px left: 1px right: 1px - bottom: 1px + bottom: 2px background: white border-radius: 2px transition: opacity .2s @@ -2654,10 +2654,9 @@ a.grey-link border-radius: 50%; overflow: hidden; &__pic - top: 50%; - position: relative; - transform: translateY(-50%); + object-fit: cover width: 100% + height: 100% &__content flex: 0 0 calc(100% - 165px) &__actions @@ -2700,7 +2699,7 @@ a.grey-link margin-bottom: 10px color: #191919 &__content - margin-bottom: 30px + margin-bottom: 15px color: #191919 &__video_frame width: 100% @@ -2708,6 +2707,24 @@ a.grey-link &__chat_frame width: 100% height: 600px + &__row + display: flex + +m + display: block + &__preview + margin-right: 25px + flex: 0 0 140px + +m + display: none + &__pic-wrapper + width: 130px; + height: 130px; + border-radius: 50%; + overflow: hidden; + &__pic + object-fit: cover + width: 100% + height: 100% .lessons From b110eaad87d984d4243d3104ddd756c60dddd9cb Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Thu, 19 Jul 2018 15:38:04 +0300 Subject: [PATCH 006/137] LIL-596. Fix non paid schedule template (cherry picked from commit 6760feb) --- apps/school/templates/blocks/schedule.html | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/school/templates/blocks/schedule.html b/apps/school/templates/blocks/schedule.html index 2e4c6457..89ce9222 100644 --- a/apps/school/templates/blocks/schedule.html +++ b/apps/school/templates/blocks/schedule.html @@ -8,21 +8,25 @@ {% for school_schedule in school_schedules %} {% with current_live_lesson=school_schedule.current_live_lesson %}
-
+
{{ school_schedule }}
+ {% comment %} {% if current_live_lesson %} - +
{{ current_live_lesson.date }}
{% endif %} + {% endcomment %}
{{ school_schedule.start_at }} (МСК)
- + {% comment %} + {% include './pay_btn.html' %} + {% endcomment %}
-
+
{% thumbnail current_live_lesson.cover.image "70x70" crop="center" as im %} @@ -31,7 +35,7 @@ {% endthumbnail %}
-
+
{{ school_schedule.title }} {% if current_live_lesson and current_live_lesson.title %} , {{ current_live_lesson.title }} From bd2da70c3bdb98f15fcbb48ec634eb8511cd26b2 Mon Sep 17 00:00:00 2001 From: gzbender Date: Sat, 21 Jul 2018 01:05:09 +0500 Subject: [PATCH 007/137] =?UTF-8?q?LIL-580=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=B8=D1=82=D1=8C=20=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=B6=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B5=D0=BF=D0=BE?= =?UTF-8?q?=D0=B4=D0=B0=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8F=20=D0=BD=D0=B0?= =?UTF-8?q?=20=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D0=B5=20=D1=83?= =?UTF-8?q?=D1=80=D0=BE=D0=BA=D0=B0=20=D1=88=D0=BA=D0=BE=D0=BB=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/course/templates/course/lesson.html | 19 +++++++++++++++++ .../templates/school/livelesson_detail.html | 21 +++++++++++++++++++ web/src/sass/_common.sass | 2 ++ 3 files changed, 42 insertions(+) diff --git a/apps/course/templates/course/lesson.html b/apps/course/templates/course/lesson.html index 662a4dd5..dd3c178d 100644 --- a/apps/course/templates/course/lesson.html +++ b/apps/course/templates/course/lesson.html @@ -29,6 +29,25 @@
{{ lesson.title }}
{{ lesson.short_description }}
+ +
+ {% if lesson.author.photo %} +
+ +
+ {% else %} +
+ +
+ {% endif %} +
+
{{ lesson.author.get_full_name }}
+
+
{{ lesson.created_at_humanize }}
+
+
+
+
{% comment %} {% if lesson.cover %} diff --git a/apps/school/templates/school/livelesson_detail.html b/apps/school/templates/school/livelesson_detail.html index 1bcb3227..2637da2c 100644 --- a/apps/school/templates/school/livelesson_detail.html +++ b/apps/school/templates/school/livelesson_detail.html @@ -8,6 +8,27 @@
{{ livelesson.title }}
{{ livelesson.short_description }}
+ {% comment %} +
+
+ {% if livelesson.author.photo %} +
+ +
+ {% else %} +
+ +
+ {% endif %} +
+
{{ livelesson.author.get_full_name }}
+
+
{{ livelesson.created_at_humanize }}
+
+
+
+
+ {% endcomment %} {% if livelesson.stream_index %} - Если видео не загрузилось обновите страницу + Если видео не загрузилось - уменьшите качество видео или обновите страницу {% else %} {% if livelesson.cover %} @@ -22,7 +22,7 @@ {% endif %} {% endif %} - +
From a4c9fc608cd14e75458df9127f5bd08c65e080a7 Mon Sep 17 00:00:00 2001 From: gzbender Date: Wed, 25 Jul 2018 16:00:35 +0500 Subject: [PATCH 016/137] =?UTF-8?q?fix=20in=20LIL-597=20=D0=9F=D0=BE=D0=BA?= =?UTF-8?q?=D0=B0=D0=B7=D1=8B=D0=B2=D0=B0=D1=82=D1=8C=20=D0=B7=D0=B0=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=B8=20=D1=83=D1=80=D0=BE=D0=BA=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=B7=D0=B0=20=D0=B2=D1=81=D1=91=20=D0=B2=D1=80=D0=B5=D0=BC?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/school/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/school/views.py b/apps/school/views.py index 3c1b478a..982a2430 100644 --- a/apps/school/views.py +++ b/apps/school/views.py @@ -192,6 +192,7 @@ class SummerSchoolView(TemplateView): school_schedules = SchoolSchedule.objects.all() school_schedules_dict = {ss.weekday: ss for ss in school_schedules} school_schedules_dict[0] = school_schedules_dict.get(7) + all_schedules_purchased = [] if self.request.user.is_authenticated: all_schedules_purchased = SchoolPayment.objects.filter( user=self.request.user, From 1c20b3c328a25b9f5e694124da89e93cf6958755 Mon Sep 17 00:00:00 2001 From: gzbender Date: Wed, 25 Jul 2018 16:26:43 +0500 Subject: [PATCH 017/137] Fix LIL-560 --- apps/school/templates/blocks/schedule_item.html | 2 +- apps/school/templates/summer/schedule_purchased.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/school/templates/blocks/schedule_item.html b/apps/school/templates/blocks/schedule_item.html index 0cafb9f3..3895f575 100644 --- a/apps/school/templates/blocks/schedule_item.html +++ b/apps/school/templates/blocks/schedule_item.html @@ -28,7 +28,7 @@ {% endif %}
-
+
{% thumbnail live_lesson.cover.image "70x70" crop="center" as im %} diff --git a/apps/school/templates/summer/schedule_purchased.html b/apps/school/templates/summer/schedule_purchased.html index e320bb14..78faff49 100644 --- a/apps/school/templates/summer/schedule_purchased.html +++ b/apps/school/templates/summer/schedule_purchased.html @@ -41,7 +41,7 @@ {% if is_previous %} {% for live_lesson in live_lessons %} {% if live_lesson.school_schedule %} - {% include 'blocks/schedule_item.html' with school_schedule=school_schedule live_lesson=school_schedule.previous_live_lesson purchased=True %} + {% include 'blocks/schedule_item.html' with school_schedule=live_lesson.school_schedule live_lesson=live_lesson purchased=True %} {% endif %} {% endfor %} {% else %} From 5844d2877a08bde84568d58132fe14b0d3bf4dfa Mon Sep 17 00:00:00 2001 From: nikita Date: Fri, 27 Jul 2018 11:13:19 +0300 Subject: [PATCH 018/137] LIL-589 linebreak --- apps/course/templates/course/course_only_lessons.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/course/templates/course/course_only_lessons.html b/apps/course/templates/course/course_only_lessons.html index f0217bcf..fd0d489c 100644 --- a/apps/course/templates/course/course_only_lessons.html +++ b/apps/course/templates/course/course_only_lessons.html @@ -189,7 +189,7 @@
{% endif %} -
{{ lesson.short_description | truncatechars_html:800 | safe }}
+
{{ lesson.short_description | truncatechars_html:800 | safe | linebreaks }}
Перейти к уроку From e43e8901147cb562401fdfbafe0df6d529f24462 Mon Sep 17 00:00:00 2001 From: nikita Date: Fri, 27 Jul 2018 11:25:33 +0300 Subject: [PATCH 019/137] LIL-589 linebreaks --- apps/course/templates/course/content/imagetext.html | 2 +- apps/course/templates/course/course.html | 4 ++-- apps/school/templates/school/livelessons_list.html | 2 +- web/src/components/CourseRedactor.vue | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/course/templates/course/content/imagetext.html b/apps/course/templates/course/content/imagetext.html index 6385391e..80c6797a 100644 --- a/apps/course/templates/course/content/imagetext.html +++ b/apps/course/templates/course/content/imagetext.html @@ -9,7 +9,7 @@
-
{{ content.txt | safe }}
+
{{ content.txt | safe | linebreaks }}
diff --git a/apps/course/templates/course/course.html b/apps/course/templates/course/course.html index f5ba90af..15a54c12 100644 --- a/apps/course/templates/course/course.html +++ b/apps/course/templates/course/course.html @@ -70,7 +70,7 @@
{{ course.title }}
-
{{ course.short_description }}
+
{{ course.short_description | linebreaks }}
{{ course.title }}
-
{{ course.short_description }}
+
{{ course.short_description | linebreaks }}
{% if course.author.photo %} diff --git a/apps/school/templates/school/livelessons_list.html b/apps/school/templates/school/livelessons_list.html index b02e4f8d..b2099cbe 100644 --- a/apps/school/templates/school/livelessons_list.html +++ b/apps/school/templates/school/livelessons_list.html @@ -24,7 +24,7 @@
{{ livelesson.date }} // {{ livelesson.title }}
-
{{ livelesson.short_description }}
+
{{ livelesson.short_description | linebreaks }}
{% endfor %} diff --git a/web/src/components/CourseRedactor.vue b/web/src/components/CourseRedactor.vue index f4291156..28e10243 100644 --- a/web/src/components/CourseRedactor.vue +++ b/web/src/components/CourseRedactor.vue @@ -236,7 +236,7 @@
{{ lesson.title }}
-
{{ lesson.short_description }}
+
{{ lesson.short_description | linebreaks }}
From 74dec82c1f4c535577f143cda601d69512b0cf22 Mon Sep 17 00:00:00 2001 From: gzbender Date: Sun, 29 Jul 2018 06:22:59 +0500 Subject: [PATCH 020/137] =?UTF-8?q?LIL-593=20=D0=94=D0=BE=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=B0=D1=82=D1=8C=20=D0=BB=D0=BE=D0=B3=D0=B8?= =?UTF-8?q?=D0=BA=D1=83=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D0=BF?= =?UTF-8?q?=D0=BB=D0=B0=D1=82=D0=B5=D0=B6=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/v1/views.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/api/v1/views.py b/api/v1/views.py index 41cd881a..bb5eb6ed 100644 --- a/api/v1/views.py +++ b/api/v1/views.py @@ -419,13 +419,24 @@ class AuthorRequestViewSet(ExtendedModelViewSet): class PaymentViewSet(ExtendedModelViewSet): - queryset = Payment.objects.filter(status__isnull=False).order_by('-created_at') + queryset = Payment.objects.all() serializer_class = PaymentSerializer permission_classes = (IsAdmin,) - filter_fields = ('status',) + filter_fields = ('status', 'user',) ordering_fields = ( 'id', 'user__email', 'user__first_name', 'user__last_name', 'amount', 'created_at', ) search_fields = ('user__email', 'user__first_name', 'user__last_name',) + + def get_queryset(self): + queryset = self.queryset + course = self.request.query_params.get('course') + weekdays = self.request.query_params.getlist('weekdays[]') + if course: + queryset = CoursePayment.objects.filter(course=course) + if weekdays: + queryset = SchoolPayment.objects.filter(weekdays__overlap=weekdays) + + return queryset.filter(status__isnull=False).order_by('-created_at') From 0c71c73172ad74ada544eafdad5c97db67dd3908 Mon Sep 17 00:00:00 2001 From: gzbender Date: Tue, 31 Jul 2018 22:12:55 +0500 Subject: [PATCH 021/137] =?UTF-8?q?LIL-603=20=D0=97=D0=B0=D0=BA=D1=80?= =?UTF-8?q?=D1=8B=D1=82=D1=8C=20=D0=B2=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6=D0=BD?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D1=8C=20=D0=BA=D1=83=D0=BF=D0=B8=D1=82=D1=8C?= =?UTF-8?q?=20=D0=BB=D0=B0=D0=B3=D0=B5=D1=80=D1=8C=20=D1=81=2031.07?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/summer/schedule_purchased.html | 3 +++ apps/school/views.py | 8 ++++++- project/context_processors.py | 21 +++++++++++++++++++ project/settings.py | 1 + project/templates/blocks/about.html | 2 ++ project/templates/blocks/header.html | 4 +++- project/templates/blocks/promo.html | 8 +++++-- 7 files changed, 43 insertions(+), 4 deletions(-) diff --git a/apps/school/templates/summer/schedule_purchased.html b/apps/school/templates/summer/schedule_purchased.html index 78faff49..4bd18774 100644 --- a/apps/school/templates/summer/schedule_purchased.html +++ b/apps/school/templates/summer/schedule_purchased.html @@ -25,12 +25,15 @@ {% else %}
Новые уроки
{% endif %} + + {% comment %}
запись уроков новые уроки + {% endcomment %}
{% endif %}
diff --git a/apps/school/views.py b/apps/school/views.py index 982a2430..ec7f9233 100644 --- a/apps/school/views.py +++ b/apps/school/views.py @@ -6,7 +6,7 @@ from django.contrib.auth.decorators import login_required, user_passes_test from django.db.utils import IntegrityError from django.db.models import Min, F, Func, Q, Value from django.http import Http404 -from django.shortcuts import get_object_or_404 +from django.shortcuts import get_object_or_404, redirect from django.utils.decorators import method_decorator from django.utils.timezone import now from django.views.generic import ListView, UpdateView, TemplateView, DetailView @@ -170,6 +170,12 @@ class SchoolView(TemplateView): class SummerSchoolView(TemplateView): template_name = 'school/summer_school.html' + def get(self, request, *args, **kwargs): + context = self.get_context_data(**kwargs) + if not context.get('is_purchased'): + return redirect('/') + return self.render_to_response(context) + def get_context_data(self): context = super().get_context_data() is_previous = 'is_previous' in self.request.GET diff --git a/project/context_processors.py b/project/context_processors.py index 0e7e9d32..e4788dc4 100644 --- a/project/context_processors.py +++ b/project/context_processors.py @@ -1,5 +1,9 @@ +from django.utils.timezone import now +from paymentwall.pingback import Pingback + from apps.config.models import Config from apps.content.models import Baner +from apps.payment.models import SchoolPayment def config(request): @@ -8,3 +12,20 @@ def config(request): def baner(request): return {'baner': Baner.objects.filter(use=True).first()} + + +def is_summer_school_purchased(request): + if request.user.is_authenticated: + n = now().date() + school_payment = SchoolPayment.objects.filter( + user=request.user, + status__in=[ + Pingback.PINGBACK_TYPE_REGULAR, + Pingback.PINGBACK_TYPE_GOODWILL, + Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED, + ], + date_start__lte=n, + date_end__gte=n + ) + return {'is_summer_school_purchased': school_payment.exists()} + return {'is_summer_school_purchased': False} diff --git a/project/settings.py b/project/settings.py index 9323745c..1aecdb8c 100644 --- a/project/settings.py +++ b/project/settings.py @@ -93,6 +93,7 @@ TEMPLATES = [ 'context_processors': [ 'project.context_processors.config', 'project.context_processors.baner', + 'project.context_processors.is_summer_school_purchased', 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', diff --git a/project/templates/blocks/about.html b/project/templates/blocks/about.html index 8deec906..5fefa4ca 100644 --- a/project/templates/blocks/about.html +++ b/project/templates/blocks/about.html @@ -44,6 +44,7 @@
+ {% comment %} {% if not is_purchased and not is_purchased_future %} {% endif %} + {% endcomment %}
diff --git a/project/templates/blocks/header.html b/project/templates/blocks/header.html index ae4107d1..5be001d8 100644 --- a/project/templates/blocks/header.html +++ b/project/templates/blocks/header.html @@ -25,13 +25,15 @@
From 20c041abc3a1474202accc6bb47dfc636d08c6c2 Mon Sep 17 00:00:00 2001 From: gzbender Date: Tue, 31 Jul 2018 23:43:39 +0500 Subject: [PATCH 022/137] Fix LIL-603 --- apps/user/templates/user/profile.html | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/user/templates/user/profile.html b/apps/user/templates/user/profile.html index 6b0f0b07..e1deb25b 100644 --- a/apps/user/templates/user/profile.html +++ b/apps/user/templates/user/profile.html @@ -67,8 +67,8 @@
{% if owner %} - - #} + {% endif %} @@ -78,6 +78,7 @@
{% if owner %} + {% comment %}
{% if is_purchased_future %}
@@ -108,7 +109,8 @@ {% endif %} {% endif %}
-
+ {% endcomment %} +
{% if paid.exists %} From 8e714a9eb7b736c3f84d23f9a8269578d8a5a147 Mon Sep 17 00:00:00 2001 From: gzbender Date: Wed, 1 Aug 2018 02:57:23 +0500 Subject: [PATCH 023/137] =?UTF-8?q?LIL-606=20=D0=9E=D0=B1=D1=8B=D1=87?= =?UTF-8?q?=D0=BD=D1=8B=D0=B9=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=20=D0=B2=D0=B8=D0=B4=D0=B8=D1=82?= =?UTF-8?q?=20=D0=B2=D0=BA=D0=BB=D0=B0=D0=B4=D0=BA=D1=83=20=D0=9E=D0=BF?= =?UTF-8?q?=D1=83=D0=B1=D0=BB=D0=B8=D0=BA=D0=BE=D0=B2=D0=B0=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D0=B5=20=D0=BA=D1=83=D1=80=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/user/templates/user/profile.html | 24 ++++++++-------- apps/user/views.py | 37 +++++++++++++++---------- project/templates/blocks/user_menu.html | 2 +- project/urls.py | 5 ++-- 4 files changed, 38 insertions(+), 30 deletions(-) diff --git a/apps/user/templates/user/profile.html b/apps/user/templates/user/profile.html index 6b0f0b07..fd279635 100644 --- a/apps/user/templates/user/profile.html +++ b/apps/user/templates/user/profile.html @@ -2,9 +2,7 @@
- {% if not guest %} - Редактировать - {% endif %} + Редактировать
{% thumbnail user.photo "120x120" crop="center" as im %} @@ -56,7 +54,7 @@ {% endif %}
@@ -66,18 +64,18 @@
- {% if owner %} - + {# #} - {% endif %} - + {% endif %}
- {% if owner %} + {% comment %}
{% if is_purchased_future %}
@@ -108,7 +106,8 @@ {% endif %} {% endif %}
-
+ {% endcomment %} +
{% if paid.exists %} @@ -126,8 +125,8 @@
- {% endif %} -
+ {% if is_author %} +
{% if published.exists %} @@ -145,6 +144,7 @@
+ {% endif %}
diff --git a/apps/user/views.py b/apps/user/views.py index 500d8404..abbd2e4d 100644 --- a/apps/user/views.py +++ b/apps/user/views.py @@ -44,22 +44,20 @@ def resend_email_verify(request): return redirect('user-edit-profile', request.user.id) -class UserView(DetailView): +@method_decorator(login_required, name='dispatch') +class ProfileView(TemplateView): model = User template_name = 'user/profile.html' + def get(self, request, *args, **kwargs): + self.object = self.request.user + context = self.get_context_data(object=self.object) + return self.render_to_response(context) + def get_context_data(self, object): context = super().get_context_data() - if not self.request.user.is_anonymous: - context['simple_user'] = self.request.user.role == User.USER_ROLE - context['guest'] = self.request.user.id != self.object.id and self.request.user.role <= User.USER_ROLE - else: - context['simple_user'] = True - context['guest'] = True - context['owner'] = self.request.user.id == self.object.id - - if context['guest'] and self.object.role <= User.USER_ROLE: - raise Http404() + context['user'] = self.request.user + context['is_author'] = self.request.user.role == User.AUTHOR_ROLE context['published'] = Course.objects.filter( author=self.object, @@ -110,6 +108,18 @@ class UserView(DetailView): return context +class UserView(DetailView): + model = User + template_name = 'user/author_profile.html' + + def get_context_data(self, object): + context = super().get_context_data() + context['published'] = Course.objects.filter( + author=self.object, + ) + return context + + class SubscribeView(View): def post(self, request, pk=None, **kwargs): @@ -195,11 +205,8 @@ class UserEditView(UpdateView): template_name = 'user/profile-settings.html' form_class = UserEditForm - @method_decorator(login_required) def dispatch(self, request, *args, **kwargs): - self.object = self.get_object() - if request.user != self.object: - raise Http404() + self.object = request.user return super().dispatch(request, *args, **kwargs) def post(self, request, *args, **kwargs): diff --git a/project/templates/blocks/user_menu.html b/project/templates/blocks/user_menu.html index 13f4fbdf..e1bc7d57 100644 --- a/project/templates/blocks/user_menu.html +++ b/project/templates/blocks/user_menu.html @@ -26,7 +26,7 @@ {% endif %} {% endif %} {% endif %} - +
ПРОФИЛЬ
diff --git a/project/urls.py b/project/urls.py index a3b1a5ad..d24d6487 100644 --- a/project/urls.py +++ b/project/urls.py @@ -29,7 +29,7 @@ from apps.user.views import ( AuthorRequestView, UserView, UserEditView, NotificationEditView, PaymentHistoryView, resend_email_verify, - SubscribeView, + SubscribeView, ProfileView, ) from apps.payment.views import ( CourseBuySuccessView, CourseBuyView, @@ -67,8 +67,9 @@ urlpatterns = [ path('payments/error', TemplateView.as_view(template_name='payment/payment_error.html'), name='payment-error'), path('school/checkout', SchoolBuyView.as_view(), name='school-checkout'), path('search/', SearchView.as_view(), name='search'), + path('user/profile/', ProfileView.as_view(), name='user-profile'), + path('user/profile/edit', UserEditView.as_view(), name='user-edit-profile'), path('user//', UserView.as_view(), name='user'), - path('user//edit', UserEditView.as_view(), name='user-edit-profile'), path('user//notifications', NotificationEditView.as_view(), name='user-edit-notifications'), path('user//payments', PaymentHistoryView.as_view(), name='user-edit-payments'), path('user/resend-email-verify', resend_email_verify, name='resend-email-verify'), From f9b4308bf53f88fc1dded45f3e9ebf92430ebcb4 Mon Sep 17 00:00:00 2001 From: gzbender Date: Wed, 1 Aug 2018 03:37:35 +0500 Subject: [PATCH 024/137] Fix LIL-603 --- apps/school/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/school/views.py b/apps/school/views.py index ec7f9233..3f269067 100644 --- a/apps/school/views.py +++ b/apps/school/views.py @@ -181,7 +181,8 @@ class SummerSchoolView(TemplateView): is_previous = 'is_previous' in self.request.GET date_now = now().date() yesterday = date_now - timedelta(days=1) - month_start = date_now.replace(day=1) + # month_start = date_now.replace(day=1) + month_start = datetime(2018, 7, 1).date() now_time = now() try: school_schedule = SchoolSchedule.objects.get(weekday=now_time.isoweekday()) @@ -211,7 +212,7 @@ class SummerSchoolView(TemplateView): ).annotate( joined_weekdays=Func(F('weekdays'), function='unnest',) ).distinct().values_list('joined_weekdays', flat=True) - all_schedules_purchased = map(lambda x: 1 if x == 7 else x+1, all_schedules_purchased) + all_schedules_purchased = list(map(lambda x: 1 if x == 7 else x+1, all_schedules_purchased)) school_payment = SchoolPayment.objects.filter( user=self.request.user, From f90bc9d8eda5ac1d87ca351a84d4fedffd4622a9 Mon Sep 17 00:00:00 2001 From: nikita Date: Wed, 1 Aug 2018 08:37:15 +0300 Subject: [PATCH 025/137] Promo off --- project/templates/blocks/promo.html | 66 ++++++++++++++--------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/project/templates/blocks/promo.html b/project/templates/blocks/promo.html index 43ce8075..e05a1e88 100644 --- a/project/templates/blocks/promo.html +++ b/project/templates/blocks/promo.html @@ -11,39 +11,39 @@
Lil School — первая образовательная онлайн-платформа креативного мышления для детей
- {% if user.is_authenticated and online %} -
- Сейчас идёт прямой эфир урока «{{ school_schedule.title }}, {{ school_schedule.current_live_lesson.title }}» -
-
- {% elif user.is_authenticated and online_coming_soon and school_schedule and school_schedule.start_at_humanize %} -
- Урок «{{ school_schedule.title }}, {{ school_schedule.current_live_lesson.title }}» начнётся -
-
- {{ school_schedule.start_at_humanize }} -
- + {% if False and user.is_authenticated and online %} +
+ Сейчас идёт прямой эфир урока «{{ school_schedule.title }}, {{ school_schedule.current_live_lesson.title }}» +
+ + {% elif False and user.is_authenticated and online_coming_soon and school_schedule and school_schedule.start_at_humanize %} +
+ Урок «{{ school_schedule.title }}, {{ school_schedule.current_live_lesson.title }}» начнётся +
+
+ {{ school_schedule.start_at_humanize }} +
+ {% else %}
{# Присоединяйтесь в Рисовальный лагерь #} From 83919037b9f4d4a318d2d7f089edfb5f1928d8f2 Mon Sep 17 00:00:00 2001 From: gzbender Date: Wed, 1 Aug 2018 19:39:11 +0500 Subject: [PATCH 026/137] =?UTF-8?q?LIL-606=20=D0=9E=D0=B1=D1=8B=D1=87?= =?UTF-8?q?=D0=BD=D1=8B=D0=B9=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=20=D0=B2=D0=B8=D0=B4=D0=B8=D1=82?= =?UTF-8?q?=20=D0=B2=D0=BA=D0=BB=D0=B0=D0=B4=D0=BA=D1=83=20=D0=9E=D0=BF?= =?UTF-8?q?=D1=83=D0=B1=D0=BB=D0=B8=D0=BA=D0=BE=D0=B2=D0=B0=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D0=B5=20=D0=BA=D1=83=D1=80=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/user/templates/user/notification-settings.html | 2 +- apps/user/templates/user/payment-history.html | 2 +- apps/user/templates/user/profile-settings.html | 2 +- apps/user/views.py | 11 +++++++---- project/templates/blocks/user_menu.html | 2 +- project/urls.py | 4 ++-- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/apps/user/templates/user/notification-settings.html b/apps/user/templates/user/notification-settings.html index f2688060..40c679e5 100644 --- a/apps/user/templates/user/notification-settings.html +++ b/apps/user/templates/user/notification-settings.html @@ -2,7 +2,7 @@
diff --git a/apps/user/templates/user/payment-history.html b/apps/user/templates/user/payment-history.html index 5643dfa5..c27a3faa 100644 --- a/apps/user/templates/user/payment-history.html +++ b/apps/user/templates/user/payment-history.html @@ -2,7 +2,7 @@
diff --git a/apps/user/templates/user/profile-settings.html b/apps/user/templates/user/profile-settings.html index f590c7f9..5502c21d 100644 --- a/apps/user/templates/user/profile-settings.html +++ b/apps/user/templates/user/profile-settings.html @@ -2,7 +2,7 @@
diff --git a/apps/user/views.py b/apps/user/views.py index abbd2e4d..da1245ae 100644 --- a/apps/user/views.py +++ b/apps/user/views.py @@ -41,7 +41,7 @@ def resend_email_verify(request): url = request.scheme + '://' + request.get_host() + str(reverse_lazy('lilcity:verification-email', args=[token, request.user.id])) send_email('Вы успешно прошли регистрацию', request.user.email, "notification/email/verification_email.html", url=url) messages.info(request, 'Письмо подтверждения отправлено.') - return redirect('user-edit-profile', request.user.id) + return redirect('user-edit-profile') @method_decorator(login_required, name='dispatch') @@ -200,13 +200,16 @@ class PaymentHistoryView(FormView): @method_decorator(login_required, name='dispatch') -class UserEditView(UpdateView): +class ProfileEditView(UpdateView): model = User template_name = 'user/profile-settings.html' form_class = UserEditForm + def get_object(self, queryset=None): + return self.request.user + def dispatch(self, request, *args, **kwargs): - self.object = request.user + self.object = self.get_object() return super().dispatch(request, *args, **kwargs) def post(self, request, *args, **kwargs): @@ -252,7 +255,7 @@ class UserEditView(UpdateView): return super().post(request, *args, **kwargs) def get_success_url(self): - return reverse('user-edit-profile', args=[self.object.id]) + return reverse('user-edit-profile') class AuthorRequestView(FormView): diff --git a/project/templates/blocks/user_menu.html b/project/templates/blocks/user_menu.html index e1bc7d57..95e2b7d3 100644 --- a/project/templates/blocks/user_menu.html +++ b/project/templates/blocks/user_menu.html @@ -1,7 +1,7 @@ {% load static %} {% load thumbnail %} {% if request.user.is_authenticated %}
Вернуться
- {% if not paid and course.price %} + {% if not paid and course.price and not has_full_access %} Date: Thu, 2 Aug 2018 13:12:29 +0500 Subject: [PATCH 031/137] =?UTF-8?q?LIL-608=20=D0=92=20=D0=BB=D0=B0=D0=B3?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=20=D0=BD=D0=B0=201=20=D0=B0=D0=B2=D0=B3?= =?UTF-8?q?=D1=83=D1=81=D1=82=D0=B0=20=D0=B5=D1=81=D1=82=D1=8C=20=D0=BB?= =?UTF-8?q?=D0=B8=D1=88=D0=BD=D0=B8=D0=B9=20=D1=83=D1=80=D0=BE=D0=BA,=20?= =?UTF-8?q?=D0=BD=D0=B0=D0=B4=D0=BE=20=D0=B5=D0=B3=D0=BE=20=D1=83=D0=B4?= =?UTF-8?q?=D0=B0=D0=BB=D0=B8=D1=82=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/school/templates/summer/schedule_purchased.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/school/templates/summer/schedule_purchased.html b/apps/school/templates/summer/schedule_purchased.html index 4bd18774..b6e183a1 100644 --- a/apps/school/templates/summer/schedule_purchased.html +++ b/apps/school/templates/summer/schedule_purchased.html @@ -43,7 +43,7 @@ {% else %} {% if is_previous %} {% for live_lesson in live_lessons %} - {% if live_lesson.school_schedule %} + {% if live_lesson.school_schedule and live_lesson.title %} {% include 'blocks/schedule_item.html' with school_schedule=live_lesson.school_schedule live_lesson=live_lesson purchased=True %} {% endif %} {% endfor %} From e6d138a374a4d449ccadb75e8783efe1ae5c5b54 Mon Sep 17 00:00:00 2001 From: gzbender Date: Fri, 3 Aug 2018 14:13:48 +0500 Subject: [PATCH 032/137] =?UTF-8?q?LIL-609=20=D0=9D=D0=B5=D0=BA=D0=BE?= =?UTF-8?q?=D1=80=D1=80=D0=B5=D0=BA=D1=82=D0=BD=D0=BE=D0=B5=20=D0=B2=D1=80?= =?UTF-8?q?=D0=B5=D0=BC=D1=8F=20=D0=B2=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0=D1=80=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/course/models.py | 7 ++++--- apps/payment/models.py | 3 ++- apps/payment/views.py | 7 ++++--- apps/school/templates/blocks/open_lesson.html | 2 +- apps/user/views.py | 2 -- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/apps/course/models.py b/apps/course/models.py index 942a0593..72cfad97 100644 --- a/apps/course/models.py +++ b/apps/course/models.py @@ -7,6 +7,7 @@ from django.utils.text import slugify from django.utils.timezone import now from django.contrib.auth import get_user_model from django.urls import reverse_lazy +from django.conf import settings from polymorphic_tree.models import PolymorphicMPTTModel, PolymorphicTreeForeignKey from project.mixins import BaseModel, DeactivatedMixin @@ -129,11 +130,11 @@ class Course(BaseModel, DeactivatedMixin): @property def deferred_start_at_humanize(self): - return arrow.get(self.deferred_start_at).humanize(locale='ru') + return arrow.get(self.deferred_start_at, settings.TIME_ZONE).humanize(locale='ru') @property def created_at_humanize(self): - return arrow.get(self.created_at).humanize(locale='ru') + return arrow.get(self.created_at, settings.TIME_ZONE).humanize(locale='ru') @property def is_deferred_start(self): @@ -234,7 +235,7 @@ class Comment(PolymorphicMPTTModel, DeactivatedMixin): @property def created_at_humanize(self): - return arrow.get(self.created_at).humanize(locale='ru') + return arrow.get(self.created_at, settings.TIME_ZONE).humanize(locale='ru') def __str__(self): return self.content diff --git a/apps/payment/models.py b/apps/payment/models.py index 32d7a0e9..415ad98e 100644 --- a/apps/payment/models.py +++ b/apps/payment/models.py @@ -9,6 +9,7 @@ from django.contrib.auth import get_user_model from django.contrib.postgres.fields import ArrayField, JSONField from django.core.validators import RegexValidator from django.utils.timezone import now +from django.conf import settings from project.utils import weekday_in_date_range @@ -198,4 +199,4 @@ class SchoolPayment(Payment): @property def date_end_humanize(self): - return arrow.get(self.date_end).humanize(locale='ru') + return arrow.get(self.date_end, settings.TIME_ZONE).humanize(locale='ru') diff --git a/apps/payment/views.py b/apps/payment/views.py index 2f70df36..40fccca0 100644 --- a/apps/payment/views.py +++ b/apps/payment/views.py @@ -17,6 +17,7 @@ from django.views.decorators.csrf import csrf_exempt from django.urls import reverse_lazy from django.utils.decorators import method_decorator from django.utils.timezone import now +from django.conf import settings from paymentwall import Pingback, Product, Widget @@ -219,14 +220,14 @@ class PaymentwallCallbackView(View): date_start = self.add_months(sourcedate=now().replace(hour=0, minute=0, day=1), months=1) date_end = school_payment.date_end else: - date_start = arrow.get(school_payment.date_end).shift(days=1).datetime - date_end = arrow.get(date_start).shift(months=1).datetime + date_start = arrow.get(school_payment.date_end, settings.TIME_ZONE).shift(days=1).datetime + date_end = arrow.get(date_start, settings.TIME_ZONE).shift(months=1).datetime else: #month = 0 if now().day >= 1 and now().day <= 10 else 1 # Логика июльского лагеря: до конца июля приобретаем только на текущий месяц month = 0 date_start = self.add_months(sourcedate=now().replace(hour=0, minute=0, day=1), months=month) - date_end = arrow.get(date_start).shift(months=1, minutes=-1).datetime + date_end = arrow.get(date_start, settings.TIME_ZONE).shift(months=1, minutes=-1).datetime payment.date_start = date_start payment.date_end = date_end if product_type_name == 'course': diff --git a/apps/school/templates/blocks/open_lesson.html b/apps/school/templates/blocks/open_lesson.html index 46999c7f..abb8a9a2 100644 --- a/apps/school/templates/blocks/open_lesson.html +++ b/apps/school/templates/blocks/open_lesson.html @@ -1,4 +1,4 @@ смотреть урок +>подробнее diff --git a/apps/user/views.py b/apps/user/views.py index da1245ae..309fb0e0 100644 --- a/apps/user/views.py +++ b/apps/user/views.py @@ -1,5 +1,3 @@ -import arrow - from io import BytesIO from PIL import Image from uuid import uuid4 From 30e1784b294daeb426d885e9d4156d337cd801dc Mon Sep 17 00:00:00 2001 From: gzbender Date: Fri, 3 Aug 2018 22:16:36 +0500 Subject: [PATCH 033/137] =?UTF-8?q?LIL-600=20=D0=98=D0=B3=D0=BD=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=B3=D0=BB=D0=B0=D0=B2=D0=BD=D1=8B=D0=B5=20=D0=B1=D1=83=D0=BA?= =?UTF-8?q?=D0=B2=D1=8B=20=D0=B2=20email?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/auth/forms.py | 8 +++++--- apps/auth/views.py | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/auth/forms.py b/apps/auth/forms.py index 0baea362..6977a3c2 100644 --- a/apps/auth/forms.py +++ b/apps/auth/forms.py @@ -1,4 +1,5 @@ from django import forms +from django.contrib.auth.forms import AuthenticationForm class LearnerRegistrationForm(forms.Form): @@ -8,6 +9,7 @@ class LearnerRegistrationForm(forms.Form): password = forms.CharField() -class LoginForm(forms.Form): - email = forms.CharField() - password = forms.CharField() +class AuthenticationForm(AuthenticationForm): + + def clean_username(self): + return self.cleaned_data.get('username', '').lower() diff --git a/apps/auth/views.py b/apps/auth/views.py index f6105e75..acb7712d 100644 --- a/apps/auth/views.py +++ b/apps/auth/views.py @@ -7,7 +7,6 @@ from facepy import GraphAPI from facepy.exceptions import FacepyError from django.contrib.auth import get_user_model, logout, login, views -from django.contrib.auth.forms import AuthenticationForm from django.core.files.base import ContentFile from django.http import JsonResponse from django.urls import reverse_lazy @@ -20,7 +19,7 @@ from django.shortcuts import redirect from apps.notification.utils import send_email from apps.config.models import Config -from .forms import LearnerRegistrationForm +from .forms import LearnerRegistrationForm, AuthenticationForm from .tokens import verification_email_token User = get_user_model() @@ -33,7 +32,7 @@ class LearnerRegistrationView(FormView): def form_valid(self, form): first_name = form.cleaned_data['first_name'] last_name = form.cleaned_data['last_name'] - email = form.cleaned_data['email'] + email = form.cleaned_data['email'].lower() password = form.cleaned_data['password'] user, created = User.objects.get_or_create( @@ -170,6 +169,7 @@ class FacebookLoginOrRegistration(View): "errors": {"email": 'is field required'} }) else: + email = email.lower() try: user = User.objects.get(email=email) except User.DoesNotExist: From b1439eeaeccfb327101c56a4ec25b78cf56205ea Mon Sep 17 00:00:00 2001 From: gzbender Date: Sat, 4 Aug 2018 12:57:44 +0500 Subject: [PATCH 034/137] =?UTF-8?q?LIL-610=20=D0=A4=D1=83=D0=BD=D0=BA?= =?UTF-8?q?=D1=86=D0=B8=D1=8F=20=D0=9E=D1=82=D0=B2=D0=B5=D1=82=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=D0=BD=D0=B0=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=20=D0=BD=D0=B5=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0?= =?UTF-8?q?=D0=B5=D1=82=20=D0=B2=20=D1=83=D1=80=D0=BE=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/course/templates/course/lesson.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/course/templates/course/lesson.html b/apps/course/templates/course/lesson.html index 662a4dd5..90e511d5 100644 --- a/apps/course/templates/course/lesson.html +++ b/apps/course/templates/course/lesson.html @@ -98,6 +98,7 @@
{% if request.user.is_authenticated %}
+
+
From 1b93afb991b7bfba6ef82b4f908904f3f43aab65 Mon Sep 17 00:00:00 2001 From: gzbender Date: Thu, 9 Aug 2018 03:47:03 +0500 Subject: [PATCH 035/137] =?UTF-8?q?LIL-585=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=B8=D1=82=D1=8C=20=D1=82=D0=B5=D0=BA=D1=81=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/school/livelesson_detail.html | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/apps/school/templates/school/livelesson_detail.html b/apps/school/templates/school/livelesson_detail.html index 1bcb3227..01663932 100644 --- a/apps/school/templates/school/livelesson_detail.html +++ b/apps/school/templates/school/livelesson_detail.html @@ -8,21 +8,20 @@
{{ livelesson.title }}
{{ livelesson.short_description }}
- {% if livelesson.stream_index %} - - Если видео не загрузилось обновите страницу + + + + Если видео не загрузилось, уменьшите качество видео или обновите страницу {% else %} - {% if livelesson.cover %} - - {% else %} - + {% if livelesson.cover %} + + {% else %} {% endif %} {% endif %} -
From 7775208dbe04ec1e403c26f7c979e6bbe5d46f5e Mon Sep 17 00:00:00 2001 From: gzbender Date: Thu, 9 Aug 2018 04:10:05 +0500 Subject: [PATCH 036/137] =?UTF-8?q?LIL-615=20=D0=9F=D0=BE=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=8F=D1=82=D1=8C=20=D1=81=D1=81=D1=8B=D0=BB=D0=BA=D0=B8?= =?UTF-8?q?,=20=D0=BA=D0=BE=D1=82=D0=BE=D1=80=D1=8B=D0=B5=20=D1=81=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5=D1=80=D0=B6=D0=B0=D1=82=20=D0=BA=D0=B8=D1=80=D0=B8?= =?UTF-8?q?=D0=BB=D0=BB=D0=B8=D1=86=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/course/filters.py | 2 +- apps/course/templates/course/courses.html | 4 ++-- apps/course/templates/course/inclusion/category_items.html | 3 ++- apps/course/templatetags/lilcity_category.py | 2 +- web/src/js/modules/courses.js | 6 +++++- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/course/filters.py b/apps/course/filters.py index 827079e6..6e4e873e 100644 --- a/apps/course/filters.py +++ b/apps/course/filters.py @@ -4,7 +4,7 @@ from .models import Course class CourseFilter(django_filters.FilterSet): - category = django_filters.CharFilter(field_name='category__title', lookup_expr='iexact') + category = django_filters.CharFilter(field_name='category') class Meta: model = Course diff --git a/apps/course/templates/course/courses.html b/apps/course/templates/course/courses.html index a3277174..8f6f57d2 100644 --- a/apps/course/templates/course/courses.html +++ b/apps/course/templates/course/courses.html @@ -20,8 +20,8 @@
-
-
{% if category %}{{ category.0 }}{% else %}Категории{% endif %}
+
+
Категории
diff --git a/apps/course/templates/course/inclusion/category_items.html b/apps/course/templates/course/inclusion/category_items.html index 5e6aeef5..8dd8a0e9 100644 --- a/apps/course/templates/course/inclusion/category_items.html +++ b/apps/course/templates/course/inclusion/category_items.html @@ -1,5 +1,6 @@ {% for cat in category_items %} -
+
{{ cat.title }}
{% endfor %} \ No newline at end of file diff --git a/apps/course/templatetags/lilcity_category.py b/apps/course/templatetags/lilcity_category.py index 957e50c2..f4596935 100644 --- a/apps/course/templatetags/lilcity_category.py +++ b/apps/course/templatetags/lilcity_category.py @@ -9,7 +9,7 @@ register = template.Library() def category_items(category=None): return { 'category_items': Category.objects.filter(courses__status=Course.PUBLISHED).exclude(courses=None).distinct(), - 'category': category, + 'category': int(category[0]) if category and category[0] else None, } diff --git a/web/src/js/modules/courses.js b/web/src/js/modules/courses.js index 2581df46..1353ae43 100644 --- a/web/src/js/modules/courses.js +++ b/web/src/js/modules/courses.js @@ -12,6 +12,10 @@ moment.locale('ru'); const history = createHistory(); $(document).ready(function () { + const currentCategory = $('div.js-select-option.active[data-category-option]').attr('data-category-name'); + if(currentCategory) { + $('.js-select[data-category-select] .js-select-head').text(currentCategory); + } // Обработчик отложенных курсов setInterval(() => { $('div[data-future-course]').each((_, element) => { @@ -129,4 +133,4 @@ function load_courses(coursesUrl, fromStart) { buttonElement.removeClass('loading'); } }); -} \ No newline at end of file +} From 4e1ef7982fc1eee137c47e0fe5cfd253b5f129e8 Mon Sep 17 00:00:00 2001 From: gzbender Date: Thu, 9 Aug 2018 04:29:53 +0500 Subject: [PATCH 037/137] =?UTF-8?q?LIL-614=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=B8=D1=82=D1=8C=20=D0=BB=D0=BE=D0=B3=D0=BE=D1=82=D0=B8?= =?UTF-8?q?=D0=BF=20=D0=BF=D0=B0=D1=80=D1=82=D0=BD=D0=B5=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- project/templates/blocks/partners.html | 3 +++ web/src/img/pinkbus.jpg | Bin 0 -> 34159 bytes 2 files changed, 3 insertions(+) create mode 100644 web/src/img/pinkbus.jpg diff --git a/project/templates/blocks/partners.html b/project/templates/blocks/partners.html index ee16423d..194daf54 100644 --- a/project/templates/blocks/partners.html +++ b/project/templates/blocks/partners.html @@ -11,6 +11,9 @@ + + +
diff --git a/web/src/img/pinkbus.jpg b/web/src/img/pinkbus.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6661d8495387dc509ee086025050d77e9784b452 GIT binary patch literal 34159 zcmbq)bzB=k*KR1ZxE3iCfMKXa{&+0!RUn{y(>Wbn^e% z1rpM84*(wxaD*g|j6?%?fscfYkMukM00ICnUc5j?`p3=wdqYP>dxe4Y;w1_);O|UJUF+p&A|AMTS`XG+{z=Oen(o* z!qPf4rLYLp*FS(5mjW@9DF54K0sb-U3sf{Dbi`p3e87u;T-7V&S2!4WFA>xI&&$FG zydt1Rea#`EZbnFxM5N*Rwcs3$n1q&7QuDKUa%%156c@LawoXb~eecX0I(i;n3rjb5 z53lsPhK&m;&!Wa@>5#C#*=HyK3mFOVjF9mGVt}eN_VRA~=yc_&*{R9vifm}Lhsu(Z?WY}UwQ@nXZ!GG`u91uz4Hb{(r?95s zO2y`)2_5PSCGTml}_`tYH1oWv^0gVEUzWq$XMC*qfuL z%&0#=O}BfstxDn-1cr)!84$tJB2PY{gh@;~l ze9CuQ7_d2FX}klPd*TW?3Hr< z#k{9#q|||-%b5d=j86@fNupnq{WTU}Gg$|nQ=4{TlMpGDOzTmbc!gVqYX;#yr<{ya zU1q1CXnbi0@mQ3VlGC8YpVRx6@!_ZVSBuT}dhgT(Qi26?^_{?O4$0q`hcZE3O=V`s zftnK>raZS*f`^VKl18rB?_RR}q;pid8p(Kkt!^`Qra$hjYQk}s}u zj*9~8BllzpHUYG!*Z;#QaF0QwuFj-b`TH zPASXSKMD&>zd5rHiapHUD0-SF5M2#C>kAyBc`$9P0V`WZlgaTzsul!r22UXq8?(Tx zy2>vsv(R#zz35-=a_!hYX=RjEV?e8*J z-2a?JsoB~7BzC897l=H3@89TN@g)bZvG%JjKA%aWIa`$aBb%RN<3Tb1TjweEIiiL5 z9h@DMiU#jNjy2sXJpw?ybSFWRz?KyO-N?GPI3i<)>UAHl2K_x z=-P4~E_voN0F8V%6MPg>b3c{n(ID7Fd86QTbCk{T41i%cXEbjKw-*#e6ne$0h~lFM zY5(*NExh6v{;A4pXK3qT+IVJIZZG28W)cZpP2cTQ{w&8{3~?(eCUsZo8Ty=i`CVYM z7kiA%`WY}ucyI&%z43%0_fS;?r^yw42Iwz~^~yg3(i-Bq?R1yz#QL82A5HTDu^mm1 z1NWZ+5A6?Nl56(7KrDy$2xn2s+>;`qD%o$vn`iK6K+(rT*p@+%QWc3hZ5x4w;Mb=L zN15(l6MkaZ-arCTvHzKw^8RVn>(@Gu%Qg3l%bn)gUz5@1Z2!2rK)9ni?#?SdS!`uN z0vp&w2Wk~OSGXMyI+csuR~)&$%haBtWtb~P>^((0I_M%@2sw5M&2#_Xvu8d7Uh@k& zxEi_ae+HDTt~>)MAIki@k+uW64B82W8e_I~{v6n+I5DWa2ZhpLI@DpZ^1~l#zh0-t z52f{<2v>aFm=H`!uHKkfriDG3vQx`><^Pq01Z^-r170c3Zps62L_!xIq~sL~-XP`I zqUOh|?$0%?@*1#EjOL|BRv#7@#(NzV6Y%LxtCIGjWtzO1M@B_u6h0S17%qiF&_9gV zzirsRiho%yp5HCH!H>1~2ov|-Pz ze^l5X_cgjsZ@fyGha+us8FN7*(&MBUX^=`4@Ce%NfAS1SpANdb zi{lE++8GO!_{Ysr=5~B~$#pv;x~gE;X;*3{t6N1usU*mv|CJ`R zm_TT#GboQd^j^&GW_RqO%EO&{SjwK3s&lW_^mcb_AqcM1(dps-=s0mg^n^a82Si@r zuA7?L4zkyTB|N-&=(^D-(PY**f!*0_q&Kb~Gua!&q&G6_x}}2Nr7Hj9AcL%X28!)v~c?gE4i)!YP) zM76;^ugv~)*CX_4gz;|esiG7k;Vceez&Ud6T09U-T<-|`pX*y+7WeLI;2B^<6twkj z>d)a4ytvu#dhG4(i=2<$s1J9WLDd-D7pb;3o{o>03n0@4+`VsdwJ`;r4vXLVe+T=H#3zt*l%nd$x^*(MpB`;7xrQABPMPV{!$S$0;ZQ!PoR8(Ke}U%TfBll`h{z-k(?apinA zM($BduHE3~k~m83{2TgjW#x&A-dH|c%!C(a5<`Z6PjnARo&or#BSnp-#ikE3K@8nu z`w`kt#auxc>rYaXk9g4TnHc8{BivzM$mHQO!2KC;I0%ROoSGt3aS!SH0(Ao zpiXq=C3YVKT`~A?{a1PnKSc*}JZ zW4I3jiYLxe{+}9+?64aja=#rEk?S?D?Sp%g^hD-iGuPZIYoFbmYH?D;HI5f?X%vno z$yxce=}(x^Z$&&Pz6D+wH9DFk!3(uSg2dEdo{fhGt7m{!YsQ9^%$=$!um0W9Xpqqz z<@xfXH(47qwf42{vBWVD@bH+M4Ud+SkhEn|C<4(<_bL4KZvSYb3bngC*;PvJp6t5R z?@YE()t6cJ_spDozh>BKMjGB8{jwhDoVvFgX{{bE#*PXJD+D=RL8SPIjaB{2Rxt{PuK2 zbAO)L{mHNEBB63WCIh!X4|(h})AEaB!BaZyx_9) zJyi#mLkSvSaJQ1jbz2dTk;mQolIpY*?kTJN#mVP;%SX9c$~wo4+wB!IgK&>m`tkDc zt4q`j8&OK(T=x^U!n~5bw(XJ|pE)P%4fWGTb2d z!%YpkVz)!MZ*Nm>nqNP->op_id6VAk6^844D*9>RFG`8qaA)SrBHyghFC*j9 zsyWs3Mhas_cJ%AEt=_9-RQD{3r8#q$G!_;TkFFF1+SATq&B^2%?{o=zJDhehBAt*s zd?0VumO*I)`741^HkBd5qz1f-_pgzlJ+c*RU3&&_sXxhdv<{6sdSLpK!b`85ZfGox zt_@}jCmW^Xq6V`UEWtnv&OI=otfx>b!CM?C82Oaf^`pB^FLM1GWx^31s5ImKcPvl> zpBs8Fh8v2-7jKyRv?5zs`zb?R`Mk1yRgo_|O))2K)7P$*lvG2d>764Hemn^lVI?^F zMJ68^JukyjO~gAe9DftVB9qGX~;2rYhQg#cgPEW+KxqCt%P-{Qg=;O?y2it%ub z?;-VROp(cnD5-8*AS#rL^JHjHFa?c4sv;@FB)t{7wXflT$?J8BuaG#8GJE6X;6 z<=DPwfWYv|a)D3sxb!`>slc#Hryva^Ld1Z*Tx4H;|8wIg<~RC)QN}eJP^!B2$b5y{ zw+aRh$Pq`3#8NK|oagu07Q8e++L_;7gY2qbppJA zhulX}>JkuwlOo!}7%#fGwet*6MLs!bd}O7d3Nl`C4i2h2x|K(M1|$Y_D#iKb8H)&q z@h9Y;&K9sJ=ot2%7)VBnO%?U7$IJEj<3`OSwNJ6zWLnpAZ1taYJo#VzwD)y_60erd z|2egj;Bcy#VnZjd2Zj6dmih*CQg)gq#;@7?m_n~+26xu|II& z0JZtcHS=;%Pvy7Ez?^Ai@^35=+PLamrC!=j#Qw1%KI9aBlXKg3^U(M6gf*a&n4NY$ zwxK*`XtteQlCq(z&pKz{mF1hF*w~+_{GVTWQrOIV`wLuIo2hVp2E3?aAAyU8F15qc zoW$03WbVXNe21S7D7R`3t{$FHb zsRIt~3e^#;yt%qPWH9&R0*vS~WZWmo2nXWLy%pPH=*om7fy>)8;{#w~Tt_uohpn&Q zY^A4tP^m?Sl7?aRq73ojWl(2QCDtIhm5-Briz=@Lgwy%@^2}89Tx=b0h2BH*w{8}x3=?Cp-)K^af{eJ}IpMdcptgwKGa{$r#`h_ify#Q z&w0F5f0|zDUSS8Q7CwR%o~lr<6e{YQ)@HOjgX&E9o&lZx*P4wEyvrApD%VdVnG4T= zYnNw0Mr3?>VAkM0y)L}>$%;bFtNR8L@C;~bG`+`J{RfZ7RuPEc@_L2+yG+>|a1hUT z>3=gqzr4oa3FTrIOY9dTZGDY6YjqP(SqQJwfM)4F$P3YtU*ywgk$>RLvVec0PQ6zT zfuVZy$Mv>PQZlper&-z-jVAoOx`;%HcWCG*ow(cehb{*Xcd_*KQuWy2?Qv4sU}Ic7 z9cW^3`s0m{=#9GR6n0m)@h8f#`XFlCYqpR0kY2jdSc+(@1>vQPkyjnA@ z{)yYC-<{a7H*G8CA#>Zmw|4h{Q)#NTn(zz=pA#F?+*=iC1CjA~i6%P2L^WIx8l$1U zo7zU?#aZ|K8-!eMJOik0{z6q(@B=^G-8=;$*NB#NQX=TH%Aj;eWp^Rv?;v%`Wi0at ztQ3vkoys8#SXbshe;t@OoC?<_|CzF8#3?$>kV;e&P<`C0$!MWx6m5RGT%K1Owwxm*;mLMNRVu4tuYK7fpK)*@wz5AH*_7 z{XK00bFj{izYyM5i@mdfJ8*T^E^BVKJQS377ReP!-0<85ss4lZ6b0w7+=|HD&Wf5w zUf!w3SFb`nanpBO2XQzol@esaZMFwQAE>R5l%r-ZK|dNyvya5f9OyBaB|A6MdN9_% zMI`l5I^eD`LIc|L-lD_bo2{M0@_U*DZO|_%T>@c6hm*8PlJC^i$gyA-A11`8cz*cm z)IpEGz{w1i3AMN{4XE?v3pj~vv+J686R+d~5DfDM{r^PeQ!UHT z;>pujx<;DN2q!u*^0w-osOiMzg>|;y3LCBHD9F1Qa^$WRe$$rG8O^7??j6h}us$K< z5jBMbHdZ~}Vj>*E;H}HoqY3~zTv6GQNLM4rdy;CpKbqZkaDAmY4VFUr%!Z#(0}GN> zM+m^S0fcr!8=M5Y{^n$|x8WxhGcwAB`Kscm=yKqV`cx#VBN#?hudR$kU;o~I*}K>a z68}dl%c+*Av5mYT!csns^m`gI`N$`Z;ZG@f0dOdZIcBUnmX5#wafxCHw>jIV|iBv$H;!%Do8rQ zZ$_|m=2~&F2Hstj% z6oY^3Sr)6+{TK+P3LWDIl`d=|d-4SpWQP56M&bR0S^80f&Q2 zd<_x=yGkP{bSvCiYYq>YX0(M!rD+6V5#1}hOm zhx9dTSdM#^h5P>d18B9I17!c$N`uw6(Ow@hi0#f8l*GdccgzeWE;0kYk8|LsfAbgwt=%S z(xzDJ6RA{vWEj)@L?L+z8J>z5QFLruTjLCxCzFJ*ZA|MGB5s$wfvOpg$0 z%2Iiz8Do5wG>igpx>mVeLJSb@UN?{P)kIQ4g=%Jd{_xc;&$_QwcQpc7QizR}n7hv4 zh>>lsa>6ie=$J*SzI)0CW((#gmoUoPYEbHq_bE-Jk{Ht^gNX|j_HupJK8gxG1Ag6D zJzdH@WK4n?)%PIpse4QqSqhW@qrb3deXQHC{|4op;w_;y;4L5tmT zQ|O4Dp_9BJiI;aeQ;h3=us$x@TU*n0BlgQqP_EL}!n>tNfifX9S_NH8L&fjBXbhSB zrzgl+M>Ok@y}2TNO`+0*#6GeOxCaSnXh!*^to-rxn117nB`B5vK+ar5N_NAA69coy zD@yXdPT!nx5}MTK$Z9V%8}yw%uz1J6^J3}+Mtc+!y|`CA`nI%~cw8KO+9L?VwfnBV zqR}r+;WsZZ|9uvA6hY1Aq+T};SG)*ND1N~!9qPKCq4O2WuK{u0Tkte!$W~R8avi{Mg$jycoNSaxl4jDr0A+jHl#mGO1`VW6s4m70#8wii8%}AP!;%k7hxUQ)uxQb3ZLOpl&xTxgHP-)r z1rrvd0`%l{QP|#UDbcyINfimXM9}>tum5ggeeCb2FdGh9AyFu;O8+(O*{q{GJE%%z z(Wp5qhqmrx)PPMz;7Ugi81DTfvHhxA#kxU_cg*7RdZwlT#WMfhH{Mc$Y;911(e+1P zjnwRYR`BeStAZjSZGvnXN=uzQLxca(4sx&FZ`qVhZ*A;SVK+J9H=i!fgZ#!R*a}*g zGJ3aM>(~9|BCL}cH`Z}v@ZUv^X4vIdERl+14j>zj7e`fPpt7rBUM(!#v=gxXY-^&On)NC`M?)!*>wDfaAVnj^(YU{^ zigRXerPR5R=q5e8utd+!7MxzVj;X-1v7;d+sX+B&**KK^F+bNo|Ja`9e$f85B;h~m z#cO%O-;P6W&IS{KX13^m)KdbzFWKub*Sd+tV)yU1hP#H@10VDdp`6N~m$3ZFnt<8%fWDR6 zYq5&+hY9BX8FCiE&ip}WF3xD}9-qeK0N;Dzwi_cGGyG&DMH4AZ*XqBiHz;Ky2Ws#i zvSo55K}XS)ZNO)MnC?<}bbTdn5ssI~K}xRd?hxG%Q=wNn)uGre&c zwa7q4NTI@yw(NpEmEx~?2yFZ93A&t1AvDp9Qcq{kvV0mo;4gzjl#Hv@f+a`gbA{UC zHh*xC58sqKjqIM7{O{WQUXkhtVbqDI{>6$ljQd#_ZoUi5?*6Z?wYhGrCzI_9hs-&m zb{}m)lmcM~@Os?y746~c$MPU=J2GvZ^0G~BVKzKcH6Kro@9wAuA#Y_$ zHaeS+=AkEz6GT=k@~ce|>19QlO^yTQ+!yld3cK3!C|9(aNNbNrRH3c<)F^lo#y%*N zIA|ZxsNyA1lOUOb2Y-E5O)2j`uiZVR*Ia(NJfxef5o~$dqugZQzkGON)GTs1sa3FeN|Slry{E7RD#_Fe6S z-9XiKt#<0`NTaEW2>|bub>VOYeNl2hxm$&&_x`ByRlg7Gsr}+I%J8?D7$)-wGtZdl z{Ku-+K?)LzkxL4+r#|*TB7Kr@ZMTH)fY$3GWPW6^Yf-`A7DLDMrJedeY_R(vW9u&T z0G#CPl&Zs{rZwF;PkhLnr@w0NU>=ZxC7w2OPqy1xC*``bW_jWnz*jU|{AkZ|!-X&z z2Oksgubu%dumcF0-_C;>-1ByS{^W2furci(;fn13^=-(nvJBh}y6Yqb((XM2Tu&y4 z^6rA(1m-FU{olAQ9oYW?&S#ISu{A6y1q&;45DZH=V2irY_(LEdj5I=?jG8qqB(aU# zfZE_sIVvhD*E{m9>M<>7GT{~xBUEYqR#J$w*SsG3*dO{c6akQzg?W|`NYA=Tl$7Y0!{Rk}T-7fZSQx_`iso%+TcSOv zO!z=kLYP{?BV768O*!p#ktex59UCjB9bEqW5fP?);RC~mtXwcoK!TN6_mM@HBOTeA z9*JJpcujF}vQjj;nw8%2)Q@QX8cLwPTBH#b|EXANP9&c|SH2PfjcpzMvN+ZT&`v*Y zIOT8#CfHVvH8ruJBF}qYl?4Fv$%rMm_O6pi$3G6#S9laMTR zIJLhXU^ds(&}q^buzHcYHNlFjm&2(z66kSfk2KJ2Wc#tvGeG#Kr%k81 zJkoW9U=63lgdQ#x62uvwS==e=Ko``R?+4|8vZY?E9&qJm#Jqh1M4msVH4OmCjF6L+2EQ(h_k+!kcEqn4~L z*AK+J)bq?((N0arFjYJDXzkP4AZuSK+PUGsMSHB>fg}Ry&cy*P{5QkT04$0R$s58A zPq7c=$iXiNBl^t4RdCVCBcXd#?sKUf9^lrRb{&x-lk_$B0z&-anZGrvy8E#8!tAVZ zcQ}hQmRju~bE}Qo1#AbyB0o%Yx?hU)reWLrx`FdVZ5Ugbn@8IF0{;gZkUlUvev?KSXB=81Ma+UXYcZx&D z8-DhEuqXMX-Q&7I()`ny?N@y|w3ECIe&BfC(m0u(21#KMoTfn}G+?(a>pTNy@iu_^ z#nN;>GWoxa1SLq?KM`MDIbavv7>)=_}AQHp+U>-sa=q zmb)O0ypv9co&iylQ!6&e6L2W$Ibth{FIm6+(!(F#Lisul`{V_4o7*keuW~$76@EMI zI`{=Vo_g)7itr6Pup;#Kw~s(QI-U^FUU<|N`R{7A8$rP!H59#51r#^j31x2-Z#;>+ zuba4$r~`Pa32cKR@dhCC}e6gB*3=}qL_QFBk(u*a5TNY z(pP3cic=%;q{g7w#mO?Ir^cSd}D|o@T6Rht8Mh$^_Hi8^W2?)dKLbr-b z(re<(v#pbPsbasEZ5;0`%e^;cnibq%p)&X)Q#jGeN7Q$1U6p6Mzn5%nr&t08XKri+ z(qd~1cvwf(hi~b`E8s=US|RwVj@j;iy45qZGkg0AQGuhxYugq9YAdGi!D5mhrC@4J z&j4E5!-zw-P#A*#`Th*ZH>@cNI=2nF5~_LF&PEVb;Owr0M87oiGRTw zkf?$q#Pd2gVlnJe1-dB=P!QuZSVw};U5c-}j|iV5q2}R+*?!TSQW2q7JE`y*5-tYL z)7BHUqh{+}Nmb3$=sTJZgDja9&Z+Q+Ds=vyoviwqab}R`Fjt_P&Hh|+{qDxy5ye}W z!aFiUC`{EMVWiko+_2Sz!l$5YZ&%X#x3s)6OXyF&d0y#QnM0xv zk|pz2o&a5iw8te8HtW=T*TV>^*xO_m+;rS#ozBk5xy|ga@85!bOiywh?Ad=ztuPbV zS-4=pEsZ};Nf9phcoU=ICli$%@fXSRBz%5ku~aQ#d&#?~r=*emHY)03>z7M1|6jFT z--Zl%VQ=2>M1P#hc4b~D9+UtPt5n2G{Q*e*fdWEqaX-^|AucM68|*qNll#ikA%%F9 zeDt95ya?zox1s0|;%9_4>jDjc>IT$sA z6=uic=VADT@YR8Rw^1`8q524aL7SF{wnoor6>9@m7RP$s2R!JdIWH5AO|{X9@iIbib(eEho_bAcoG2{ch08=Uq1NmyDXy6_Ic* zr3)ZwMjc0lXB5C2VrLE8@DAa>8*PF9G5q3JC#2Z#MAs;tA=|QvZXFXVV%=q!A8pQC zEo4RP-7QgT&1oBeeF~H~O(cJ=Y#Vh?SQ;AV#ylO$TFT88Sl-#%S?CQmP4yD(MK7vx zalJ%&XZ87&0)q+sQ}g__$uec1K=Za$b@>4@VULjuupR7Hz+N z$UgbUF3;?zME*xk)!uDFfvbCyiPt;lM@a}v^m{CN*v^+Ij~xs6P|nTmCx8Ra z$Zg36%{StRNVthm&VOKV1_)*K>@BW=c>d;}v0uk(o~JwmBKF~`%^64jn&*j6%F~tz zPm9yJ1IBmc6BzWm@~Osf@6?NZ8<7NA? zGH^A*2Jt_nGjWu4H^r|z4wc>B%@G$l($D0cc zQuJV%4`r6^g;E+gZtg-MRXF(A*VoMY3J{mrp=#DjWyxq@h zOS#&9n!xOm#zrv?G1c^){;5nX2^_^O zYVuS<6jS+1#FFS3R??mgSMO2EFwf`5B6d9W^Z2xrr-vPHb&?GH!b*u`Qx;HKRKJcm zX$c?=QQC?#F*~;(5{9_xk}GO}m=qM%l0i(;C?IO#GDVFRbVWIlQN~85?b7&HMOoil ztr!yo==P}g+WRS1DWJRexqVk`hvjWs>N**zl}ROOdGh79<)0Jd(S|>r`wG>ds1IqI zX=}wYmy)Tp=Z4ofyZarh32sOIG_iz_ybpuR9fIqkU34zwk(?W*#%-C!(G4$_P3ngktA(%UPy=j)&~#S~(Q<-sK+FZqF6JYdS0b%lh%7R&jvRN2)7Ga81rZ zy6&!DZX$H1Et)848Ydg8WP)wnsa&k0lMQso7P(U{5x>69-)HDbRq*L>*CRud=GCGI zuOwmWszrMJ)=M0MmRf^ook#+Hcv?4(IO?IQHC3L(Ozg3Yumd6m1OpiOP*asRZeh(2 z;aKOH%GupARG5UVb{Nq{JO5Fz%EX8|@7qv!%4Tl#PA$;2p)2?KY=W1}gn|Qj#qyV6 zLaa(N;QAR*z7I#Y&YvsmLZmIudb)eqCYU^79*cnu#16l;h@5m^+KN@$Wd)$rPaJ8j z1q24Xozn zbydIiOch@{W9e;n$@0&dTQ-L=#;jczzawr(#eT9~d79UDKEOew3sn8g?;8x1pYB-q zLmj@le-is5`+d;0H2eisLKuNh86#uXy;%i)e#yz6c_AUU`eN)0+dcR-)})urgV^sI-tP!c7O_SGq3yKic0P7Vj{ zYOl>SX(0BxY+-37DO9EPsQ@|#+7KE0>UX$6GBR)A;A`p8mUq1>Q`yJrPGG_iVux+pk`C4y!+;m z6`N8!j?tFXzdV!W_S8-3TapnAmc>Xt%%;xWK08UvS7%Jl9UGeQEg;s!QkW8Zc$kxk zkf+V2Ub(@}jJ~}31_urFr#X5xrN0yhUFKPr+|18N-Aw}796wd=BgZ!ApDm{X9Ntah zw1NSCp~MC@VUUfUnx?&C3~@3cQ7=lFkzpL+UT7q3Gr2xOh^fpsMXhyye?NR5cx+%> zY@7ba@zIkrq%Y54L!DEbYYXFQ5Vx=tJB85gMTdyH>!-JA&4h205=Ki}V&|*UjkINc zIs)6(#Trd&XoTZ2yF#Z@qg@rh5$Lfv+Atp*ynYyV?#z62nh)8QR(`X_^lifV_QKs5A@TGsUPo{C6{?*NSyVfiGMite>h<8&kx zo&)qudwH*kszOd2+1T>sZO4R2VJcJvQc5{Y2lfv5JxDc53tD1|XymUe_mw`2yZ2#i z^#FY!gRu_=dHW272lVup=mype(_^x>f_+2pD(GI5l^#Uz9LepU3EPk84eUsyW|(DR zn<#Y|GYyx3KCF$pRjOdS+tsFjmJJ-uJAC`vIE}UG&VCvLysGg?aGz`HY;%gjZvF=m z0Zvw}FcTGn>(z{q*&AZ2)1=tCcylhXiS8&Ke|Izwr!pE(vx6$CMm>xgpznpCpNPFG zVKbv}XeiO!z&|H7jwDF-OZ^lgU?%5PmP)~B+C&t%Q-6}$_||AC{Y0Mxq%k4{up+EQ z%N8uvl4LqCTQ_;V>K*mzg%n(>`Tod(@x0bFnWuMOC@2aM0{@p-i?Lhfp2o49HtL3% zMgO3tcy;Yu!+pjJr?Yz?Mw6$67#`7Qt>Mj+|ed%oTK-9Pv?XcHOw( z#Vp14*?m=sBc~p)eRDXOC#$_7vu)hEy_fe72%>OE`%kaszre`9G0A@vJhvTTEoszd z%#u?Y#Z}bu8mb{7$vzkg7))wZm&uyFDCdszm2rsVcQP`Sh3X!$2`<{n4L?p~N2LXp}gKj{fa5YCAmmGv(9<@b#bC4_Pf7t>v8U$Vf}kmZIsXDF=ZMo z;g|xkT5@lRU5=r*g3Cr%;@r8ZB?aD zZ--Ig;O1*i$wGS)Bai8~=gTDfI&~~lZPay;wn3A+IOG`wHT1aps{^b6;y<) z8@_$CBJ%gz`k4k4TGr6q-1_Z;na~F6*tjs5QP_e`3+!>>kA>?BvwVlalKq4DKAkbD zI_}t6+nEjJ57G?nF{IIJXM6tEf)K8e2h2K;qclyeSK39HM{SbyJ;hTs!C|9&={ZuS01eDu!E2d%=w z71%V&Eu@#E;h%)hmF>D;8KQs%@oCafIC1d4TyQF7x)eMh;%iJisjy^>T%U-ai8`W9 zOoQ`WEgbn@PFJN0PTu@#Ndxd0oxtUnl+ZB)+f)=J)2b9XJ}p49@TpBi;uxmLN3cP{ z=c&=m$S1e1RB7sM;klf0K5C=>=RToZ!wI4%9?C_a&h{@&GR|wnpgivo*WWf&Eo$1r z_I=C-+2SVYps&J8SQ?N4<09{*Fea%h?4e)od-^8&iQ|oSf{%1I;v57`Tw#P6q}5>& zpFyY7OGg-vBNQFQ{76)LOVssqhG~9e4!1QNVY7--P17@JyeruYdV0k*`dibTRKd{` z+*-@;!vAbOS`Ha%cSGPBQ23WghTitdtf2`Pt6e|G9%d9TxV)!>gwg>erW?6uBaY;s zAKtUEp{6)ZEOU1baK({NQI+$nI@OeMoSJ?A`w7himvh7X>=7X%v+uGoLrGZ8K zTKhmM4k3VS1c_M{k{PB1Z!2P7Gr@rI1)rUCw&$0zVu;tRCTpbnW*cb&@m~x z&V%_g+9zfUw)vNSr1FOswb&_UFR@8WY8<_BIDdi87O&4w3-})FlV_Yc)J9VD3@F%g zan5Ze-cG-gF$Yx>aUqz$vlBI4738PIO$V3{wxO> zMOh-u?Whv=axOAaQgG@%$7vas3)^XS#xMSYX)LGCA3dHp zOvas!zV22!@lsj1R!ed`YL`hf4*xb0@u;VKL;j^2>_dJCs|Lf)yWs4D6_?(B@rdi3mZHeu}2qBm8dqSOkP)@8AVPhkQrW_rJXjwYpoh?89S}>5v zJlOjE-@q)fHHT&}Cy#&w9}6o0|+Ed5ZF~KmT_A>l5Lt$`tIw zjMDK?-o68Rr!LuTL!*9@>f|wX8w2*C9G@55>4YVRhU$(m3QrKYQ#c%m$on9$n&d~x zqV7S-b6ya*|K)-3b0oX2OG133zu?RqmTe%6?}Rs<907>~vW=PwQV$&#EUD`2U;A?0 zujE&KX-{MwqU`J$B8^1VpZ`mfRtwy@1e*T!zO$LE_W0ML-b%XM_V3Q)@J3Gpsl&AC({Wz9AE*dNb}UX^3ANew;@+{A7tHB z*oVWp`a-4(WiuoLrmAMBC<}I1o&mRa?1=mfle>T#HlwF5J$8?!)KYrpza%Z@+ZD~J zDBG@hT6z9{-P|V>9W3E2A*h0q{lUV*jY5tfaNhTh+KQiP8Fb&*nelMYDE8Wa9yN+a zuEsapHKU{x&s12{I6-K{efU{$g8lRI={@M zy%WO~#C0jUk|?#_<^4Y1HS7{FiWS01kt3etVQt?dA7&T{Me7e|Ce%%g{H(jsRz^Q^ z?92V;K*E8)@KBO~1m?6l;N&#?G5oDqp%a~we6oYk=lsH6XNmXf`lxnsh| zQm)Ks*4n!|GS{DtpvB_mi)(sX0-|9RUL3h7qzF~k%PWy=D3Qn;4}AKy0|R@u7|i2_ z%@QGll~a!l0cuYxg6W~=cc2_}@?@fKvE{VGqxgD}QVpH94kd@^885IJZ6q*rD4oCjgdA)(apRE89N!6U_-{7w5O$M_M6C zy~4;7J>Fa6_~#cJySP2xOtfG7LHoD?)i4V$vgVk@< z!3wU5ZXdImelgI{cYuUX{7c_?+Pm=;y9u@}|D?_)SjLR!{*10B4=AH3qI-eMv6L*t zP5H4CFzIkFB6|$=pZmbt08;tgI#umarnzPTIKh%T-xX*D6ni!qWz=dFQ8(= zhOh#<+pvUb?=O3Rg%%IZs{@_Mt!>Q`@k$=~xCg5>-<$Sv%C z-IGYnSVZYUy3d;SNY9DtMLd4!_y#}#5mz5Zy!2D=^msDGwmg5b6#jm{ZDv5%5yU2? zJ1z0DiKGZbct)m5J7^n1nkR*2Wr3|ZTiA`3!vjHRED;Ud{ZNlXe^J|ApctIULF~yO z9oAe{Rk|{Kr?qUbI>e3jo=}NAqMx?HkD}%)j&(Khnw#;rs88I`Ps47K(dd>9Aw_4& zJl`~CXwtncwBL*{i<45}+EV#5s_O#dn|BK14dbS5oX9ZVU@7q!Ygbplp|#zO&m-~y ze)usaJSp?$w}}G27D10)pPNY0*5oiOA=Wl<_h~=6y9ifeF};4TSJptx(0V!<7H z56`?a^J#v;+H&PwvaNGJ_I0hzz(uoFifw2{gA5}b=y&#f{w(A7yrC}h zr%zdX=6!e&$0_r;&kwpA11Dt|E%8c~ReJ_)lcd}U?pG~!zA<4mXj0M_1#OSt`33he zh09)fy<-$-`V^rS*cyJGij+|7^dD!6*ZLxnHGlq?(Gvr znCzW%(MDfx98`q}=PNvoY<2X3#vy5Jz9;XFQIr3Tx?vLQe7NDA%)Rf$gggH98UxTs zA(aj4Ac&PG7vMwXx%BJ@P?e!sZvY7vZ^|nAg0`!IrgYaSzRnOkzZwA6N_TS&q2nFk z`zYc;r!}}^lW4G^-0fU-++T^1XtcmMjC_XhmKkx>SRtv*=`mI!QbJK&@1_Ld%x<4O z?tU-U`LU~oux}=zw3mz~5;t?p{BJ>GYc?q}D(dXIvbyXp>{Z%%&YE?|gL?_hF6L!W zqXrijfaWDL8-S}YMi+3r#XYQ5fWH1rcyc^-kTw8rBkbQEvg}sI{SmvRNiB(Q@?x~Y zFG0EGUC=HpvDG#`1S$Bwb>=n@Tafh z&FbAuC%8{TXIA|mDa0zPme@d2$_7mV(Hh#n6DwSn%XI7$87xB_GCnYFg62zgEXuPB z8)Dw4lZy5CGa~2U@dQPBN-?&d?EB6wKbHsVENZy~X>TwVmKjyG5)pdG5NfatXXSSm zXCt#EYUraUPkw^17W0hI=o$X6D4p^+$TYljoXTp!a4xlxYL5|`mNI1Tk3CaDg^_<| zNn0am_SpyVBL6@GM$(F~D;+V|K2o&_&M#!XYg)}YNMpK|Gd(;c?c>;vuG29dh!D9aO>EFfySTp+Apnp(^&HtDl<%d zn8l;oBbXbA`r((`c>W?c{MX|e-hSW32qp@tEZBt*52ttT{zCwyEa-AkzQ?L3xWvUz zNx4ikQayUx>A(D#+C2LsF|7f-K2R7E%@3A17ukCT8CO2#pKZb8lfV7j0gb~P9qzNW zx{X5a7FIPJwB6WI68-7vr7rw0DT3)PZwA6X&5n20F-xV|NuWAg?#7bc=3uzUBpvAy z6i4>({^ekzmqUktQEsPR6Guvdvz|$qg_<*8F3l7F)i3sup>>`xkW=S52uenj!ZJuT zg(C2H9hNancN%sKe1)#%A}&HGigK`kJY$EDD4mzmBpj)iF*7=$o8H>+ zL)pm4`JQr%tU-l#6GalzR29f8V@|Dh4)AFM*g^-&$n}1=ppJ$qMH}IXh&G*aAar#> z{eWw6^}rW9vvy4QE1d-S=M1#J^7=MO%+~`6ccQAbuXyT5TMvSn_qO%*5$y|#D7x2G$=KCCrHWct z!Dt^XmG#BC0S73X@n5sih-~ku^OPAI8aUKJw5|AKp&!_6m>m+3=(2!>tMB8mWE1zI z-VhN~N9>lm`SQ1Q9&N!9v#wyK-2TTd30C-Bx@*5X+FS7Y`xNhwTpW%g4GWy4wmiLc zP(^lUqN0OSt(R)`k(TD7M~#OL`hsQ^O+IsF7EIRg36y0SS96>S-wTpYRA|%jk@zd9 z9>M&j<+tJ&Oo|=7O<8|W(Oy7+B(8+W^GV?C(bMKQxQa5v#MWqepe8z#>qIYyr^cog zhs$GxvlTpX4yN!hyKV7o9+VGL&_QIta2GE4a#VezJFDt=&M>-x9$hmvX;+J=D{uJg zR|hJ^n0T1JhI}6y1-i6RSJ#*H)WM*%Ako-%4aWB8X#`<7U4Xxb@<;fO8~E(HgRfz% z-drMIOD@A!T5W6Pd!_qL3nkjM9T=>%B+}4pLH^1e78jv3>J<-b781G~xR^(cb98l? zJ~CGUvw}|kbQY-G3&aNY$=@WlRh)FDxz^Vm+a5nOjx6-@4*K|YApibRqT>ykgVU);i+L1t~4$UtHw*8=itZ#<0$VMYqKvDzAA?5?Wk7hD-)W+_e(ZY?@ zG4>FLGN#p}6;KAz@yaBE1(V{$#FXVd;$h*|4m|E+-|z` zmCGx61FkiXt1Ljz?-{knWg_`}S|EYCG^4=6S}pQ(?{XK5F=GQba;~|8Gz~AKq*9W= z5hij5tczt(3s0d}1LV+p*>7yG&rB+(CN{(^3m3oR>bTgoBuv>vgr3jmul+p?f;IFCQ#dIs9{j@zZanpb1*ih=cfbG>|K`r{u??bpCz9teI;V-4LDL?o=lExV#5_F zvXP}5kK8BzJSQiUs5im;RfWCuRlnH6iRn_sNxypmvL=4JTDxK4`c*jHbiT~+X7*^Y zob`N%_O)qWbGeuwNG2^Fn;c0Qw=pR5PZp>(|5>OsVj*aaRI`AX6d5Km;eO-Ndsl<4 z8@hULM8*d(G@>!;B#EI$WOw;6KPzjHOsK?@!HDpoBB-uRW`p-|TvPIw^6-`-Ku&4= z=nql{+UE0>h8tS41VCCUtUvf$-__+q@N^;=`6Trz%kSf}v;Iq6>0)1bKc%{nq?6>6 z?>|>|U-Lh8N`K+UqLJtAFj$L0VtskX+>_Ya^;6Cs)sVqvT~sHfl@gT&3I<%^vC<|R z_UT7Nr9{JCJEv0m*B5UBlhXu^6Z^DdgwN!_NDA6TymY3J(v8zT8&AUyTS68I=ienc zQYIbWyO@3t=P*#T0UUX(M>J|_{<0A=A5u8e&Vm;8*F}S0M>fRneO>30U2#-+sT*xT zjTjuDU+K3g8HKH*mDQ1JT+}?R;k8@()*_F}MD&OAX=#=8y^EE_@MLUV9?@*$v!<@ z>gsA5B1PVp-{!>7>maDmD6Nu3^44-YK2BX@gd%YJ%6KYYYu+F}=^KN08%Y#tp-J); z{#3z|a*rf$Lr!St8d}NJ?<+&Bwf#GM9epC69J?7Q(D!2?@tF@Vj>w!_CC2So6XPU? zjGMsf``jZ+(NRSnWu&Tm7@omx0oux$XOKg=xatXGiMNx#Qv|#@$poCm`rin1BLI^hu_z*^?-ddjP3N0(4*K7 z*O{$V+eEb7oys~=a72S6IHHLsQkRwvBBE*KVg9=wPA{eWjfVA)T`}5gv1w`f=!o$~ z>C|EUbjfuZ5+*ns{`p5aaq7%1zkiBSOz;R2iz;|%8$8ocFi`~x`2_#CHKTZRj_C z0QR?5andO0VzE25uPE;033+NYXP>Fg}O zs?ubT$8`kJFm%Nu;_7h%0HXu@)0u1ltEx`L^*_#MSpn`jXP232$V6B@ds-xVVPLRoa^$30#yZPVaY9mT8zs&BE14KA(&AG!2~c$1gh%DDCm zy9kuC&J{zz=GyW1be*dN(u;&r)WLvh`^VsJ@7K=#k?Jd)TrUTp;pYQOSFL@14ZU5u!grV}O8zzi6YIp#%CnJrZ24>vhYl@k+E-B=q-6O37z!3a zK-fmY=Np+>x*mEL2TmUyF5{EGa3A8%W#)W^G6$NU8CAkZ!z25Ody!52++%b#O%NI! zoeHdv>+tm;5%GoQH9NY_PORPR!ErvKnkS}54v|6nVrOrrG=aTiovAo3aB?LQ1yOy6 z=FE`!=AAaJt)NZBJ)tnTW1ML1Tz6pfR2r7(fAQR@n1?S!dK~1ER+^3)-;w;GpSmZ? ztdQB{fv_B?2y0KOlrZw`IzEYaE`#?bBCV}a91Y)>r`=<@4kyBeP=emRjhNRoz99X3 zQ&MCKrw@WTXspxIn0>^!p&ef3&o1IrYUG}QiW#J?^Fg+qm48rAs`ak*T|Wq*G2A$k z>G(ow2ZrA)E>0pd`(QdJybF zT!jh!As=7uq`y(2xn8o=8dch*UYRyu6#c}l&XZR*YEp5sjSQ!F)082X-y?7h>MWeYH?5Tv#m9)a%vHXu zU;N9N{PFfjA^Z^getkhfpd@zTzgIn6QxKl7oW2_rGe6x&KIzk&6J?cS1o7-dx5(~x zjfV0V6&1&V*egQwm|bvHQfVNE%VRS31Pl^d;q)0KpPfHS2YIw*EgnrZ{;v6?(lN9v z-!;ND=KRmfha0y-GWa+b@YBnGJlsxGM>Ln0UZFX8wRi}F%>iZ{LMvKnM_ZE`Zk-Bc zLE=5GlcLK-9v(_r%wJv$O)cDvbNv|CL${!Bq0VXiQ-tm&X0O^6-M&_H3@?Mfyzm1Z z!F&b~NNQbQelgThbq-G$zbwLe=93IuA9ks$Uv=I7qwoZb``?%sA$^<(knP9lnqu zu{2Dy&bI{?+XjGq89Ld;$CzKaw1UN6w2JDUwjaykZ@!vYkxE}DT~UD^?AmJP!`5mQ zfgcj6($yH)5v)UryJ-(Uw#B5j{ZAH##^tS|iSQ668ZALgcOd4Ew^f>&Vsvyk$}wLM zaFi(gg9Xh`7nbfF4JfdC>c90HBG3U>S-=I>m#=F;koF$Lq%xOpY;e zMViGS2D|arnLK-R$*8WD!|RM+yJlAPS`% zLrd9IV8ysX@%sR2OAN)YnRQ)qB~sOQX7hy!)q&&{K*TfRq;vv$zj)#KEvZW}1 zt?l9A59x>-qr^yxl;!yfv|&){heTY!gL?o#V*0x$>5LXcwaHy&F_Sg*JHiecGD1PH z(&7F_t?tZ6dEd}P`+ChaPMC*^Uqwk^)krKlZH&ET-q&)2!m1jC$m+_Jtg>B;Pn_t- z4|(O!c|rV+8=%EoQ&~@XqvO)u2S;-kwYnLt(uS@z=uR+Gf9x;k`Jt2nSDj2}8OPID zY%SGPl)^*1(GC0w21siW zo#*I#5}gABqT5G!<}v7ZnD)@e4-xfAwDT<*JcFd^l7po7%?j@RofRKp7PxPX&@FaN zsuC`e{sk!f%ud|AWHGKH)=4y|wlb8{*^B&HW)uS|ORgHjmS|9YBck}<l!y-(D*3yv`o|FR9N| z?1OxK$J*XtZ&v^zArJ!v3)pD&c77S5ezU+`((W{N402y+KXyMl^}^v+tsCYAi+@>j zVQ8n!`)=RwJmHLFD+@}P8YrIW%!>L2V$6<8+es}NDQLr~+x%6ptKz8pZ(^|!)U!DX zKR=JZF1GyrV)@&mLYRB^h#S+fv+*L0UP_ zcqwB_j7hk>(_)?h^Yp)-7Ld=aB;LFvD0u|ic zdiJqfdPPJ2QU{b9wetbnV1*vzTNZCVoP9TN${2F8u!N)&By1F%9$MQ);dAW3!tCh7 zU6TwR+vDeG846GXqQz?MrlmrX5DPaR!yy7Qv{X?AgM+;tw)c_*miI%eRB_1uH z(BBHL{rW7z@ezy^Nj&_uW+(7anQp@kxKGNs&tc78XLg0M(QhMVLXi@EHM1FdEQPp_ zGo*l|R;`0NS;$0^1PJ&2ySE$F(Ftbv8f5}Dp7)P9^7XNMk;M(F{?otHUa~kZ*8OKb z$)|A0RADkXfP85nn0WPbqJx~^CCRv59dO8uNB=6a$ibSd(4p_V* zgyDVyFbzVwdvFUbX8yq%CAhw)uqjW&N0+`M9h}}|8YhBP)^Qrjdq&9Kh8C;1S2UWo zOYu3|D7n%HK^8@f+8w7lvjP!N)IX}U+w0Yyidhzr%xdXy4`uR#~wAN)p)HbIXXzV$sCv6BKyM@2!L_ zk|s7$EB)BatlFDa!JLr>PC`zSaTTATT!e@l0k&sn;Y3a$(Y`@UB%A8w%AbQ?T|cPL z{`R5W!n@Ux6KM$AqXx?GFP~knx|}39c>NRw5X+bCbfs%955yRzH~s~dS&G|c1Wk4m zt0Zz0Eb?XjnKflYv+CZ>npe}gZ5(1z!%RNuX9&+n9F}sW_8ekON4bbk`R?;Og#KJT z^VFEv!${CsWYGrI4RNT7}xl&375DWW2D5 zyCfRzR^~GBUf;pzMHBoYQEA6o_6E>W5u`C)`CpwK4m%(`T!emrJ%dk&v=`n12+6J0azsZpomPj{u2c0F23oh)I_xpwy=ytX6t<9Tn;yKOR-6ViOp+3BgnD^$u^j?_HEpQ;&zAujA@=v{`=P=Tu zp~eerT_E+agTn{^q?gg1M1njksKGGPA4U=jTbI*@7lv+EdLKc2J0$DK?sP4tbOHJ<^CA z29iwQ87P()+#BsM`tRQqXj8-CPzf~3_CpYV83b-w5tw+LgcIuUH5^AlEHLAXFf8By z^7`r$majpk9rZt2uK9j6JEkcU-`LLMa`g zvpG=6%Q3cFUop0dw?gOTUD+t4xzUfZFn?CGSCZYNt}z$ed#HCnj`^@>73Zg$xD{mE^x<^( z;nqHo-GlvB5KQlgfti{D1_n@0coB3d6NsVHD_;P%!oTyKKK%&hZVc+*1r|$08diti zd=K!W${yF~6?0D)9($I$giWPPD{?aDgM723#qKwkn|Bo{#1l6JbnnIkP;jFX#o7^9 zZyBz#j{ZaNTUS6em+8);Ug{zK4h{rp5- z%sEG;u+e8$xYl@=csA!rh5pRFcIjkX8loKV%JOZ1iW3)i?NBRm%+g1A6TVGibK=^e znYtD0gfKp#0g6cnW2pQVk|>t7o1M?Vz*O=Hr*~jtRW7~|LI^^j!kdT9iN-orm}VOZ z9UeZVH;*g#oD9lvxC>pgw-@3{poRTg3ghQy#j3$Tk>A5##l;u^1dMh*d#i~HEU%~Y z&2|&u*eO#_x?HyPEJ)Asw-o{`g_4eb9S5{+nWBfP1 zBzmAfD&wZa&|lUh4%#ATmDrQZEop}ZY!RG}z(7CGi-Y@*MBmLPFN8E$bPS;I)D%l} z)>M%mbmgFe#xlde*~(tbO$R+LW98hmehh3DOv?42gosLL$y&kE|K@+TtDkaLPc(d_9tee>f)2!X33V};}mt#7iRGTCd zzEvWg;xY8?kH6q5Db__aap6;+aX&S7LOBUE5V5-rv7?jblZaWRYn3ZQe+jq{0EU#mr7 z#xXHw$E0vC6}F;R`m*OKt%SCHn6fR_dc3n$ASU>;u)Y9G2S@n&al01wU4n`VNBADH zvlPV?Cg7O5o38fja}@9g@DdXxXcj0Y{SbiS?@GWBeQKk=@Ni?~ ztaJbyB(?Nd{^<()IMrguwo$Niu>-2{`>hVV?vVzTZH2Iy)1zo{^|ciY`14(IOh>pl zyx4ko5h<&}9Ma5|KQoJ6rORenUSlf9CNVKF(E+9R5-itI(n@_Jn8J(1?kY_l=-ErU zI?N<)nHA~sk)-z!24ll#MH|yPY%x z$e_2}pZG?c6;+o;!^cIf8j(biXt~K0=;$*_XxR7)aOM%jq%Q4zDiY#7XmFq}Za>jY zuu!4RTrFsdY}}>$e1@KEgustTflx&btCv( z6T$Lkmn3|^NDf(7@g*_~<>858;Odg5aXNDlR|4S?e;Wa2s7~p*D41m_{NX=@6GCA< zVekJC5I!8=`r+9rZBtcA)D7hWq`n7hWLWeVTR63l=cMfrdu%w0y(i_Vpl5HT$x*en zg7+v`5h-+V@Krdq@z zmtYyX0R*0`I(A|YI7Whjx>aB^do6#4gdc_#CxuuwwbQ7@nQw@E6Nxo`ZP~v0yll}o zhQL7LUQ2X)dUkiK7iq1`?-9Od@veC)Ib2LZ;Ko|U_Qj#J8+k;JVjFlE4| zk&sS8Y&6Mg!;?E^2a~B~Wj zG&!ci|1bN}y_+5DLK+KOjh??a)@XUVBA*c`PoS%z{YeQ&>+jXLJcT=^8;bvbl0)0cny&y)lP5KtPvh0ZgKJ(dWAk?@pYMs?u7 zHg^Dh@%Y}M6Y2n(%;eo^#(wwk)cfEUu{*qDpunzmo}_hpm*JP&jb0gJL*ih29)gYf zsnXH4(1&?0WwrQ6t}nkny;qTek7TiXgw5iu`x%>lQKBa!AmoLDV*07RHucCNTZ@?!2mZCeT65b<@9_MKUo1&-=D?f?yH+Qn2NN&Z4=5v*UD^!MR&F6;La zZ67e>B+lJjE#Z`G47tEFmBtbIufJ`R&Ak46KKHCUauJ9ih}NP}4Njz8YTpHJR`4dM z)n$X%U3GoZt$(J2OZzZKJFQq~q!slGGv8^ySE4NCd6xCP$QPPF*I3Z_(&7!Dk=_W- z@|hYj;fteRTvtyV%x1eJSY5=LFqlxWj?9ytg|Y0oqzQ%BtJJc~2?OGUxP5PV_tWf9 zE}DMI2c&!#zDouA_!a)02ssV>r6Aw6iUD3zZ2~WBI`G?I;VbSc$)S$MVmMkRsxx>i z+gB)A0s@Y+vJ$>&tWi*frQH>*p`ap3;7L(mq3<(lnN1xocG+~2nAv=}hWge$Au&QM z2Dc_zlxDMI{J#8$z}V<)px=IC!+{jJ7kwr6k+PM69}xVe5(g@2b!f%xRm#2v71K&~ zK}(i-%8mHGu{U|hLmMCS zW>UY(`cNxlML9Q!_mdp0vI2tkFb)EBy!$mR-JO-#SpUW1`o_XykneKeqU*bG!;)6L z+7Ua}0e-OtF?QvmXjn!Kl({fQ!;o(lLin|-g0sX!;1GX<&DM-2M~`ERDu)591209u z8I}F%2qPUowAt(?fC*b7yavP(UZ1!SfxPPU_pepiw+6KT5HPR$U|=fFIK6ZEDrUZ! zM3ViKkHUjpOw%0xC|Fm3$>5rnbLl&1b`Jb-{q){ zcPzaF<|WVQcOao9!dl|5J12)Gej9%VmRs@N6Px+0xSp;?jfzQQ97TKi#sUT*4h~)v zA#xnXhVV*et*-J{GXAhGsDIwd8iGxX)sJN!C9zgYpYDzGR{8I^56&uped2@BM=R~zOi`y{u zjpccLQC-9vQ&YW28gX_n6Q>!y$=ErNCow3~R5=$Gjzl0TYajUWsNhyI_&Hsx8V8L#-+Zpx^xgC3Vy6)M-Nr_T%MIVSR(4|I zy=NKd2kFYV3<^Ipp`&Tmc=30`I4in5xgg_a@_=S{_G*E<1aEivA+MG7T!wE(XugSm z@dx#Ym2n_U8vjpPpg`Ix2IW4zLi>v`s8Q6e`yd(4A+dirbzlE7b_#B*#_x3utjt!d z$s`m%3Q-mW0r_!U`$?CXY?;Pb}y?&t0L?@pmU${y-s-m=}oM~YDf|fdM3PZe_hPccKwUT9` zEKXF!42eY=e&nG)fWJeiwd~_BDbG2I4*U;Rs2^G_*ZOl45gaZ*O-x^?D8$W>>CF!>@>(qj1;(F^sQ6-wE|2bvsJ z`$z}3>iZyNahR3mv``QOU*XWqP#IsBV2PMGR#fWpwTcGIVWJk0QLGuiWb(2)SA{OR z@luYf(Guz|ERl6qbxLE67SpI{iq*3rF|U**Rr_qG z$Hzs|9T$gudgXonF8QFJ{!f3YHNo3&?v5sTc3R%cLMsei5e&fkYp9(?YA`Y$h`G+e$y8Jno=b8@rO}O$^_P~7q z!*HP#*<{@zO+^27Tjs!CFn}3F{ihN#=aZXPs?}EzCb(^#{7?bWA@m8=#OCrpgzU-+ ziyNupZ}L=| ztWPY~O_^pcZOL;}MP9ZE*0=lwNcDX2<*okF+Iu=*@%Q5$&Q3~A@7pU6N_t!;_Ctu| zQEqtjCn>Q8Os#b@Vu@qZ7p!G{^rEXeSrIUQBJK&_Ih91KL~9WWD+5a_JQZk^m}yha zP+q4c!%S}+u(h5=`86d{LbG1Jog)5+K%jA0&-$Ptxe`L9*!R{n_=@|FX#ZRKt?P}$ zH|5IL8Nti?{rgQBeYdxSWx-JG*1t>B?!M8NsAgSdM%f;oVP^e5qjVrTAl7;qO9n6i zPI*>$#$LmCETu(=se#IvFWVUFoH(#)4fKNy`M?M?5V@L2n? zsY65R532*wo7}~Wh6^bIC`S@Cvd~nmZ;GBp;QRM$h)I;88|6zQ%j`+ z&p3yjYZd-^z(nEUv>CprSoxor5!oWXz7t(ijm_M-_AdxO5U(U#$K zM{{y~CPQN8DcgRRfYX>ooWQaT$5_Y`vT6~w;KuSQ-gksnGX5lU$q$mIOump07$n@t z4FoufltT@E4#jQ>hm@mD=fo%L+@>5D6h3X}ZbX;53vw9jSiBk(C$vKtjFsVSx_J9! zO#4;gbrGGmS~{t1=WnnN3zc|)eyk3r4(`nsGz@ZnAuQ6`x-e69QlvZdc`eGEfQ=-f zc8Su6R>q?Jl-{*l@1$BqO@Q#EibJSSua$@Z6iCJon_&Azpa@6%>Sv3bOwpt+j95V7 zkO%X2RU2%ef0GFW^w6^^&X4(Y=;_yrD&yPO>s2|(s(l*$F^XDJmWjzy0C}M*g}senC>dm=)sQg-lg~Y3<>Q(LDzO`eQU*`j(t2g&Vv~# zDcpNa$B1jI7xT<{MXOFzgQEP}kdvt(STERnu*FH}xat#v0$*%NqkoL%1v3;dsj~be zzZK+)0&0SHEnw4G;cM19r)tWIeJZKs)s`3gEZcEDRRQ3Kd!O2fWq?c#>`2Xvjr1%G zP33Fq{82r>JnQiPp>K}}AY=$~V4}m57Bdaj#u>SARQz>7BWIYXIf%AU&0;A?z(QPz zoW#zac)!=v&pDplX5Cw~em(VJs>zh>N;#%%pRI1&3n6t7Hwo7^__8b~DhPhIOlt~h zcg?gf4sBv_tSaYAQTo|q?d@@)1!2TsU8JMAP~065c}pRc`sn>X5wJJlFW5@=eVsbU z{7ACw<=ns8v1r5Er>eYYxY{{9DeFy7!k?7#F=rI{wdKVJ zs&dBc+*MicT>?6*UvaJVD{%K6^16%jV!O22Thn}xS6$LET6xV9K#DEqTgS@Ex*2Et zN*M5o?-y7A-x2;QkKN+rUoTG2kl?KLMp-AB5Fv;`vK4+%VIc3np3(iQ>;xZI2^w~> zHc)cgkeC>`+^iU>WE0vM48JGKR>=jb>x)kgqzh*dd0#>rv++g5$;?s>1wM;^xSgHo ziM{QVIToxYlW1#WEdi8!1*`aMfCk>#DP>!=Z}xNQUPg4 zQ_Zq`w;4V_9dw;pe>mRo?k`}e z3nHL4Y$Wzp!aKE4j(*l?$fvNbY!#Z#F>2lYTg}=&5U75y>-mJJQ=J>cId)n-6QHCI z)NX&)ePjtC%EMlFNV~hp`@FawO{_^Llf0U?Hp$GGplXQuUWE>a}L>3P6C zOC&(HD>!?!4s}gvL6)4@TF#lDV74*3k+vCN0-5my-XCG%`k4XL&*2fB*ED`IK^*OSY=3cGCw+^9L2}S*wak(XXO0{KK zKI&Y;Y>fCAYlby4gY24EATrNM7Vvs1n{VY2FmsuJl0=0+ofJh|aDGJRt=pSt2uCyQ zT5IacoDad-b7@mgIV6w6RI434uD-`w!63Y4@uRI!A>ek<0rP0u@rVToxJ+hByQl+h z1ppaUMSYa6K61_V)+;s&owT!chkFlqdS%q7fUU3Tcc-hc#I3+obV2qdAAB^qG?PsJ;*KgWFgKCDliV8-?ALW$zPdhA#&eN;0vVesMi zNMcblF~rgZ({C;oN{Tx-uU@U6GuaI0n;gg4=fceA%GZsyXyV9?oN}5#Y$d5vo z7kmzfo)QFT1zCW(t2a+2<^PKCwuXJH)1~*hE1-uIOuJAv=-m^y4fc(90Zbnn^qBT) zc79oYuq=5L=Rgr5m#&ah|M=5(b>DL^ohdnI~{tQ{>qf)mt7Fez(_|m zdJ?yd3p4@Qy}NNVRv&67%(|;9<(k;`v}DFMit9t#io5q~Kb4DPinYamJt<3YM*Cm( z!+tMF0aB{nm1Wo245k!tJ=YQrJv^J@_j;%PF$=b)8)NZsC(mVq<2~WO0%BjS^YCFm zdf0Oc)*Jq9`|tIhcGjF!Yt1;f$izM<^Fm@?!Smno_X@6rj`&= znr6TFC-?td8fNuR%xgA57nBo!Gm^^78!M)7nPbL6PFXq1emH4MGEA5bIdStXn?5nX zsbwKHHnDls3(B!sy9lyn8#azMq4M*$*o$@0+q1SFW*_kmA&E=aT357cPeu1A*YWAs zIVI0F+PrtB_F+g>#%hgru?BO7dMj-bq8IdXjXzHVFpNd`9M+-9>$Z6T3%=k9SCJ+H zozG;S*9{RR=)wyh5XU5thZCeWM12HXOtmfbNx_qw%jU3oL)M49v}%e_7{Di|eZi*< zS6)`7qLEH)_3PSzyOK*Qm)>sndk%qJt&#?KsmRO8$B@o=bJeV=rLRG94FMG0j%^<% z8|{5l?h*f9bDYpDtgi9D^MUbRfiKsTu4*hbYHA_0D|<8Y#=QNWwJWqNWb?Rb@Q}LLN%Y$# zpmAO2?w>W6mR03k4+w?UTi%|T#c1&}L9!kQrSypYxirtlZ4q+*Z~E-r$Mzd>(X$MP z*t)y(p!2)^w~OtG2*cH(&<1H)V^juIGN)s?aHDAD4(UF*(xsFy<5~yo^Bjf?&eL9! zKFv<9WxkUBC2Adk{G%|JM4>O6x{ddfTDx1K{5;2MAt4MNJpVT4QWbN|c2AfzS+ZJL z7H(Gf?B`Zvc-a2_o`|83(XMzEX(OH%SQxZHIL-G+C|2R$lo@<-OX4+kM+O}qv;n=Y>*HYhQvrHg_|R+_dNrCFA1a$Zih zOLDd&Cuq4~OJpw7|1EsDDO7-t#a>g>^vpu6=(VM$@IjzMYc6Sp7YU%s_CLrgXiq9z*+hZbwX+AQ|Y@1M6cHL#ed6iAMo{8?V- zfA3yZ+(XFGq8W#43A zBPBmhsfZKE;#1mNm`nchHAvi>36g5L6$k%%H41N$(LQ=~(eD53YnBq3N5RV0B&V&Rk;-zau@>>LR25=bTUbue_6XD|V`u-p6OombqCJ47ZUc zi*mp73S5b}CNqIHP%peJRixVxEi2ttc+bzuoK4oYRW-#i@l~@_8-6kNrojFB(Bdv0 zw)+_LX67iRq_7COmYv$(6MbyX6FFUIU4LR~FXL~x{epL*lRi@}YFPumqs0&a0-Dn% zTH_h4eh=Fa=#W}0Wtf^t99=*Ir;hDZsgcTdk_iiUmYO9k15J9z2@wSZljAV`HHvn`Nj+9LouswImO@GbE%Nm*ZUqRVVA5*|^=WZtnPl=r(NH1i^Sk63;lw8|#_$H2KVQSnUJ|EWkNaBH0}T2{DI zN0Abki*N>YQzTlS*kW>9HdNZ`#L_*)zPz|^3dcnSYJam%X%|1N2`L`g`EA^5tsEB| zsA1lDkjo!Xckx?mq8@dcG2LnuK5pDdv>lKCYFJ4FoZeV>to6gxKE92KH}ijHqSI@v zx9~!rQb66o?5Wvr@4orP8M^?w+?Dz+<)luhx!Gd~bxS`GkzW84wx zIeZ^!#?qP2vZ7oOtj!8 zqgM>IWpWWnYH}kgw|9+ZsqCY_lqtXO)$Oxg;iU_S;X{{b*S{|doCjuaU_-p9g-17U zMyJ$TyHtP??Pqcck=iMH4C1u}<_Z`I*A@FCu$~x{@kygx!op|uchB%R7dTCasc2V{ zZ7WMhxtlwe3W}^+daEJ9aziRno9?H4pI}faw}6VHjay|Va(DY`TI4tzL;vaM(y9D1 ztes=lVxirP;}fsq(ExeAL_#mYnZc5 z2+mVKep40zaHmV zS2lz^moFoVB7PkwOZob<0Y}B^^EcB!7r&;OkDYi1PAoUwGx~F8`UpSxEOv<5!=*~= z>yzB*?_Exf6Ig;-Vqxr=?aq`u;Nx#7=Nu%3xDYN?GbbNs+b|>a=JVDbz7^cHxpz+4Kajm+MgD3C zc;KTncQA7haYX#0{(88mc_dNtz@7}(k0`F~bmx%O|0 zuK_pB^Y32jL0-*z&w}$3s{2=0?8D8;bS~A4dV1Gct$vh|6&V@&Ldj6}-QgtjYrPVN zpRuiKwkTzrDtejggGb+dJGECSCkFg{I<*4=dOoh$s2+=gU4OL~rNr;f&%mwO!G4Z%i2$k6|7MeJtH+@THE6e;V*E&?CQ@{4^xT@%*3 z4iRoT{D;5;uk@l*Y;l|#r-+#N`2&CT&?!V-RzcT>_%mH)3^yjQME_1mwE(vz8x7m0T5NyQfzsHW>d|sbv@og;hJu)TizzuRkJI#T*~DmMR%QFtY5O<{>B;WMKZ@C6B{YjI+PkyX*t}+eoS$Vrs|Jo6k zPBrV^R2zUy*TfsrBV`_e!UHdz%2< z(zCR$``dixqmvWOl}~^Bw5sd4-L9?u-g>q=YvQlmn$}Z#Twh$Q Date: Thu, 9 Aug 2018 15:54:42 +0500 Subject: [PATCH 038/137] lil-600, case insensitive auth backend --- apps/auth/backend.py | 21 +++++++++++++++++++++ project/settings.py | 1 + 2 files changed, 22 insertions(+) create mode 100644 apps/auth/backend.py diff --git a/apps/auth/backend.py b/apps/auth/backend.py new file mode 100644 index 00000000..125f411a --- /dev/null +++ b/apps/auth/backend.py @@ -0,0 +1,21 @@ +from django.contrib.auth.backends import ModelBackend +from django.contrib.auth import get_user_model + +User = get_user_model() + + +class CaseInsensitiveModelBackend(ModelBackend): + + def authenticate(self, username=None, password=None): + try: + user = User.objects.get(**{f'{User.USERNAME_FIELD}__iexact': username}) + if user.check_password(password): + return user + except User.DoesNotExist: + return None + + def get_user(self, user_id): + try: + return User.objects.get(pk=user_id) + except User.DoesNotExist: + return None diff --git a/project/settings.py b/project/settings.py index 1aecdb8c..646c94ed 100644 --- a/project/settings.py +++ b/project/settings.py @@ -142,6 +142,7 @@ AUTH_PASSWORD_VALIDATORS = [ AUTH_USER_MODEL = 'user.User' +AUTHENTICATION_BACKENDS = ['apps.auth.backend.CaseInsensitiveModelBackend'] # Internationalization # https://docs.djangoproject.com/en/2.0/topics/i18n/ From 19739c1dce758d060d3a60daf6c11417e0e65893 Mon Sep 17 00:00:00 2001 From: gzbender Date: Thu, 9 Aug 2018 16:08:36 +0500 Subject: [PATCH 039/137] =?UTF-8?q?LIL-600=20=D0=98=D0=B3=D0=BD=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=B3=D0=BB=D0=B0=D0=B2=D0=BD=D1=8B=D0=B5=20=D0=B1=D1=83=D0=BA?= =?UTF-8?q?=D0=B2=D1=8B=20=D0=B2=20email;=20refactoring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/auth/forms.py | 7 ------- apps/auth/views.py | 5 +++-- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/apps/auth/forms.py b/apps/auth/forms.py index 6977a3c2..e369a8f9 100644 --- a/apps/auth/forms.py +++ b/apps/auth/forms.py @@ -1,5 +1,4 @@ from django import forms -from django.contrib.auth.forms import AuthenticationForm class LearnerRegistrationForm(forms.Form): @@ -7,9 +6,3 @@ class LearnerRegistrationForm(forms.Form): last_name = forms.CharField() email = forms.EmailField() password = forms.CharField() - - -class AuthenticationForm(AuthenticationForm): - - def clean_username(self): - return self.cleaned_data.get('username', '').lower() diff --git a/apps/auth/views.py b/apps/auth/views.py index acb7712d..379ab0a5 100644 --- a/apps/auth/views.py +++ b/apps/auth/views.py @@ -7,6 +7,7 @@ from facepy import GraphAPI from facepy.exceptions import FacepyError from django.contrib.auth import get_user_model, logout, login, views +from django.contrib.auth.forms import AuthenticationForm from django.core.files.base import ContentFile from django.http import JsonResponse from django.urls import reverse_lazy @@ -19,7 +20,7 @@ from django.shortcuts import redirect from apps.notification.utils import send_email from apps.config.models import Config -from .forms import LearnerRegistrationForm, AuthenticationForm +from .forms import LearnerRegistrationForm from .tokens import verification_email_token User = get_user_model() @@ -171,7 +172,7 @@ class FacebookLoginOrRegistration(View): else: email = email.lower() try: - user = User.objects.get(email=email) + user = User.objects.get(email__iexact=email) except User.DoesNotExist: first_name = data.get('first_name', '') last_name = data.get('last_name', '') From 9c047e2a4d74733e074c90f36f4065e012206933 Mon Sep 17 00:00:00 2001 From: gzbender Date: Thu, 9 Aug 2018 17:58:17 +0500 Subject: [PATCH 040/137] LIL-615 fix --- apps/course/templates/course/inclusion/category_items.html | 2 +- apps/course/templates/course/inclusion/category_menu_items.html | 2 +- apps/course/templatetags/lilcity_category.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/course/templates/course/inclusion/category_items.html b/apps/course/templates/course/inclusion/category_items.html index 8dd8a0e9..5549ffa4 100644 --- a/apps/course/templates/course/inclusion/category_items.html +++ b/apps/course/templates/course/inclusion/category_items.html @@ -1,5 +1,5 @@ {% for cat in category_items %} -
{{ cat.title }}
diff --git a/apps/course/templates/course/inclusion/category_menu_items.html b/apps/course/templates/course/inclusion/category_menu_items.html index a01342a9..ecb83298 100644 --- a/apps/course/templates/course/inclusion/category_menu_items.html +++ b/apps/course/templates/course/inclusion/category_menu_items.html @@ -2,7 +2,7 @@
Все курсы
{% for cat in category_items %} - +
{{ cat.title }}
{% endfor %} diff --git a/apps/course/templatetags/lilcity_category.py b/apps/course/templatetags/lilcity_category.py index f4596935..956bbab4 100644 --- a/apps/course/templatetags/lilcity_category.py +++ b/apps/course/templatetags/lilcity_category.py @@ -17,5 +17,5 @@ def category_items(category=None): def category_menu_items(category=None): return { 'category_items': Category.objects.filter(courses__status=Course.PUBLISHED).exclude(courses=None).distinct(), - 'category': category, + 'category': int(category[0]) if category and category[0] else None, } From 2984d33073754bb69372c7cdc2747339426118cb Mon Sep 17 00:00:00 2001 From: gzbender Date: Thu, 9 Aug 2018 20:12:30 +0500 Subject: [PATCH 041/137] lil-600 fix --- apps/auth/backend.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/apps/auth/backend.py b/apps/auth/backend.py index 125f411a..41568b52 100644 --- a/apps/auth/backend.py +++ b/apps/auth/backend.py @@ -6,16 +6,12 @@ User = get_user_model() class CaseInsensitiveModelBackend(ModelBackend): - def authenticate(self, username=None, password=None): + def authenticate(self, request, username=None, password=None, **kwargs): + if username is None: + username = kwargs.get(User.USERNAME_FIELD) try: user = User.objects.get(**{f'{User.USERNAME_FIELD}__iexact': username}) - if user.check_password(password): + if user.check_password(password) and self.user_can_authenticate(user): return user except User.DoesNotExist: return None - - def get_user(self, user_id): - try: - return User.objects.get(pk=user_id) - except User.DoesNotExist: - return None From 1d1f516f0e25d3edb449584c4c057a70b7a5462c Mon Sep 17 00:00:00 2001 From: nikita Date: Fri, 10 Aug 2018 23:56:09 +0300 Subject: [PATCH 042/137] category fix --- apps/course/templates/course/_items.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/course/templates/course/_items.html b/apps/course/templates/course/_items.html index 42757ddc..b4356f2b 100644 --- a/apps/course/templates/course/_items.html +++ b/apps/course/templates/course/_items.html @@ -48,7 +48,7 @@
{{ course.category | upper }} + href="{% url 'courses' %}?category={{ course.category.id }}">{{ course.category | upper }} {% if not course.is_free %}
{{ course.price|floatformat:"-2" }}₽
{% endif %} From 1258a695c8f8c3d36ec47b020d21f6d0fc0e7425 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Thu, 19 Jul 2018 15:12:14 +0300 Subject: [PATCH 043/137] LIL-575. Effective amount from payment provider (cherry picked from commit 5393d61) --- apps/payment/models.py | 2 +- apps/payment/views.py | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/payment/models.py b/apps/payment/models.py index 415ad98e..25e98c82 100644 --- a/apps/payment/models.py +++ b/apps/payment/models.py @@ -190,7 +190,7 @@ class SchoolPayment(Payment): month_price_sum = aggregate.get('month_price__sum', 0) * weekday_count // all_weekday_count else: month_price_sum = aggregate.get('month_price__sum', 0) - if month_price_sum >= config.SERVICE_DISCOUNT_MIN_AMOUNT: + if self.id is None and month_price_sum >= config.SERVICE_DISCOUNT_MIN_AMOUNT: discount = config.SERVICE_DISCOUNT else: discount = 0 diff --git a/apps/payment/views.py b/apps/payment/views.py index 40fccca0..afe2b8ac 100644 --- a/apps/payment/views.py +++ b/apps/payment/views.py @@ -197,12 +197,18 @@ class PaymentwallCallbackView(View): payment.status = pingback.get_type() payment.data = payment_raw_data if pingback.is_deliverable(): + effective_amount = pingback.get_parameter('effective_price_amount') + + if effective_amount: + payment.amount = effective_amount + transaction_to_mixpanel.delay( payment.user.id, payment.amount, now().strftime('%Y-%m-%dT%H:%M:%S'), product_type_name, ) + if product_type_name == 'school': school_payment = SchoolPayment.objects.filter( user=payment.user, @@ -252,6 +258,7 @@ class PaymentwallCallbackView(View): 'update_at': payment.update_at, } payment.save() + product_payment_to_mixpanel.delay( payment.user.id, f'{product_type_name.title()} payment', @@ -269,6 +276,7 @@ class PaymentwallCallbackView(View): product_type_name, payment.roistat_visit, ) + author_balance = getattr(payment, 'author_balance', None) if author_balance and author_balance.type == AuthorBalance.IN: if pingback.is_deliverable(): @@ -277,7 +285,6 @@ class PaymentwallCallbackView(View): payment.author_balance.status = AuthorBalance.PENDING else: payment.author_balance.status = AuthorBalance.DECLINED - payment.author_balance.save() return HttpResponse('OK') else: From 0ad60238a91898e72e7cf47e94d4049e583c0c6a Mon Sep 17 00:00:00 2001 From: gzbender Date: Mon, 13 Aug 2018 19:14:38 +0500 Subject: [PATCH 044/137] =?UTF-8?q?LIL-582=20=D0=9A=D0=BE=D0=BD=D0=BA?= =?UTF-8?q?=D1=83=D1=80=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __init__.py | 0 api/v1/serializers/content.py | 5 +- api/v1/serializers/contest.py | 45 +++ api/v1/serializers/mixins.py | 4 +- api/v1/urls.py | 5 +- api/v1/views.py | 21 +- .../migrations/0021_auto_20180813_1306.py | 48 +++ apps/content/models.py | 48 +++ .../content/blocks/contest_work.html | 3 + .../templates/content/blocks}/gallery.html | 64 ++-- .../templates/content/blocks}/image.html | 20 +- .../templates/content/blocks}/imagetext.html | 0 .../templates/content/blocks}/text.html | 18 +- .../templates/content/blocks}/video.html | 84 ++-- apps/content/templates/content/contest.html | 44 +++ .../templates/content/contest_edit.html | 18 + apps/content/views.py | 24 +- apps/course/migrations/0040_course_age.py | 18 + .../migrations/0041_auto_20180813_1306.py | 36 ++ apps/course/models.py | 6 +- apps/course/templates/course/course.html | 2 +- apps/course/templates/course/lesson.html | 2 +- project/templates/blocks/lil_store_js.html | 8 + project/templates/lilcity/edit_index.html | 1 + project/templates/lilcity/index.html | 1 + project/urls.py | 4 +- web/src/components/ContestRedactor.vue | 361 ++++++++++++++++++ web/src/components/ContestWorks.vue | 30 ++ web/src/components/blocks/ContestWork | 38 ++ web/src/js/contest-redactor.js | 19 + 30 files changed, 874 insertions(+), 103 deletions(-) create mode 100644 __init__.py create mode 100644 api/v1/serializers/contest.py create mode 100644 apps/content/migrations/0021_auto_20180813_1306.py create mode 100644 apps/content/templates/content/blocks/contest_work.html rename apps/{course/templates/course/content => content/templates/content/blocks}/gallery.html (97%) rename apps/{course/templates/course/content => content/templates/content/blocks}/image.html (96%) rename apps/{course/templates/course/content => content/templates/content/blocks}/imagetext.html (100%) rename apps/{course/templates/course/content => content/templates/content/blocks}/text.html (94%) rename apps/{course/templates/course/content => content/templates/content/blocks}/video.html (95%) create mode 100644 apps/content/templates/content/contest.html create mode 100644 apps/content/templates/content/contest_edit.html create mode 100644 apps/course/migrations/0040_course_age.py create mode 100644 apps/course/migrations/0041_auto_20180813_1306.py create mode 100644 project/templates/blocks/lil_store_js.html create mode 100644 web/src/components/ContestRedactor.vue create mode 100644 web/src/components/ContestWorks.vue create mode 100644 web/src/components/blocks/ContestWork create mode 100644 web/src/js/contest-redactor.js diff --git a/__init__.py b/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/api/v1/serializers/content.py b/api/v1/serializers/content.py index 729368e2..aff24dd5 100644 --- a/api/v1/serializers/content.py +++ b/api/v1/serializers/content.py @@ -3,8 +3,7 @@ from rest_framework import serializers from apps.content.models import ( Baner, Content, Image, Text, ImageText, Video, - Gallery, GalleryImage, ImageObject, -) + Gallery, GalleryImage, ImageObject,) from . import Base64ImageField @@ -14,6 +13,7 @@ BASE_CONTENT_FIELDS = ( 'uuid', 'course', 'lesson', + 'contest', 'live_lesson', 'title', 'position', @@ -254,3 +254,4 @@ class ContentSerializer(serializers.ModelSerializer): elif isinstance(obj, Gallery): return GallerySerializer(obj, context=self.context).to_representation(obj) return super(ContentSerializer, self).to_representation(obj) + diff --git a/api/v1/serializers/contest.py b/api/v1/serializers/contest.py new file mode 100644 index 00000000..3d76244d --- /dev/null +++ b/api/v1/serializers/contest.py @@ -0,0 +1,45 @@ +from rest_framework import serializers + +from api.v1.serializers.content import ContentSerializer, ContentCreateSerializer, ImageObjectSerializer +from api.v1.serializers.mixins import DispatchContentMixin + +from apps.content.models import (Contest, ContestWork) + + +class ContestSerializer(serializers.ModelSerializer): + cover = ImageObjectSerializer() + content = ContentSerializer(many=True) + class Meta: + model = Contest + fields = '__all__' + + +class ContestCreateSerializer(DispatchContentMixin, serializers.ModelSerializer): + content = serializers.ListSerializer( + child=ContentCreateSerializer(), + required=False, + ) + class Meta: + model = Contest + fields = '__all__' + + def create(self, validated_data): + content = validated_data.pop('content', []) + contest = super().create(validated_data) + self.dispatch_content(contest, content) + return contest + + def update(self, instance, validated_data): + content = validated_data.pop('content', []) + contest = super().update(instance, validated_data) + self.dispatch_content(contest, content) + return contest + + def to_representation(self, instance): + return ContestSerializer(instance=instance, context=self.context).to_representation(instance) + + +class ContestWorkSerializer(serializers.ModelSerializer): + class Meta: + model = ContestWork + fields = '__all__' diff --git a/api/v1/serializers/mixins.py b/api/v1/serializers/mixins.py index 0f055f00..40584503 100644 --- a/api/v1/serializers/mixins.py +++ b/api/v1/serializers/mixins.py @@ -4,7 +4,7 @@ from apps.school.models import LiveLesson from apps.content.models import ( Content, Image, Text, ImageText, Video, Gallery, GalleryImage, ImageObject, -) + Contest) from .content import ( TextCreateSerializer, ImageCreateSerializer, ImageTextCreateSerializer, VideoCreateSerializer, @@ -25,6 +25,8 @@ class DispatchContentMixin(object): obj_type = 'lesson' elif isinstance(obj, LiveLesson): obj_type = 'live_lesson' + elif isinstance(obj, Contest): + obj_type = 'contest' cdata[obj_type] = obj.id if ctype == 'text': if 'id' in cdata and cdata['id']: diff --git a/api/v1/urls.py b/api/v1/urls.py index 409ed79f..0e8cab3d 100644 --- a/api/v1/urls.py +++ b/api/v1/urls.py @@ -18,7 +18,7 @@ from .views import ( UserViewSet, LessonViewSet, ImageObjectViewSet, SchoolScheduleViewSet, LiveLessonViewSet, PaymentViewSet, -) + ContestViewSet, ContestWorkViewSet) router = DefaultRouter() router.register(r'author-requests', AuthorRequestViewSet, base_name='author-requests') @@ -44,6 +44,9 @@ router.register(r'school-schedules', SchoolScheduleViewSet, base_name='school-sc router.register(r'users', UserViewSet, base_name='users') +router.register(r'contests', ContestViewSet, base_name='contests') +router.register(r'contest_works', ContestWorkViewSet, base_name='contest_works') + # router.register(r'configs', ConfigViewSet, base_name='configs') diff --git a/api/v1/views.py b/api/v1/views.py index 41cd881a..ac9d70fe 100644 --- a/api/v1/views.py +++ b/api/v1/views.py @@ -39,6 +39,7 @@ from .serializers.user import ( AuthorRequestSerializer, UserSerializer, UserPhotoSerializer, ) +from .serializers.contest import ContestCreateSerializer, ContestSerializer, ContestWorkSerializer from .permissions import ( IsAdmin, IsAdminOrIsSelf, @@ -56,7 +57,7 @@ from apps.config.models import Config from apps.content.models import ( Baner, Image, Text, ImageText, Video, Gallery, GalleryImage, ImageObject, -) + Contest, ContestWork) from apps.payment.models import ( AuthorBalance, Payment, CoursePayment, SchoolPayment, @@ -429,3 +430,21 @@ class PaymentViewSet(ExtendedModelViewSet): 'amount', 'created_at', ) search_fields = ('user__email', 'user__first_name', 'user__last_name',) + + +class ContestViewSet(ExtendedModelViewSet): + queryset = Contest.objects.all() + serializer_class = ContestCreateSerializer + serializer_class_map = { + 'list': ContestSerializer, + 'retrieve': ContestSerializer, + } + filter_fields = ('active',) + search_fields = ('description', 'title', 'slug',) + ordering_fields = ('id', 'title', 'active', 'date_start', 'date_end',) + permission_classes = (IsAdmin,) + + +class ContestWorkViewSet(ExtendedModelViewSet): + queryset = ContestWork.objects.all() + serializer_class = ContestWorkSerializer diff --git a/apps/content/migrations/0021_auto_20180813_1306.py b/apps/content/migrations/0021_auto_20180813_1306.py new file mode 100644 index 00000000..66033641 --- /dev/null +++ b/apps/content/migrations/0021_auto_20180813_1306.py @@ -0,0 +1,48 @@ +# Generated by Django 2.0.6 on 2018-08-13 13:06 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0040_course_age'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('content', '0020_auto_20180424_1607'), + ] + + operations = [ + migrations.CreateModel( + name='Contest', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=255)), + ('description', models.TextField(blank=True, default='', max_length=1000)), + ('slug', models.SlugField(allow_unicode=True, blank=True, max_length=100, null=True, unique=True)), + ('date_start', models.DateField(blank=True, null=True, verbose_name='Дата начала')), + ('date_end', models.DateField(blank=True, null=True, verbose_name='Дата окончания')), + ('active', models.BooleanField(default=True)), + ('cover', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='contest_covers', to='content.ImageObject', verbose_name='Фоновая картинка')), + ], + ), + migrations.CreateModel( + name='ContestWork', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('child_full_name', models.CharField(max_length=255)), + ('age', models.SmallIntegerField()), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('contest', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='content.Contest')), + ('image', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contest_work_images', to='content.ImageObject', verbose_name='Работа участника')), + ('likes', models.ManyToManyField(blank=True, to='course.Like')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.AddField( + model_name='content', + name='contest', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='content', to='content.Contest', verbose_name='Конкурс'), + ), + ] diff --git a/apps/content/models.py b/apps/content/models.py index 468058eb..3f4992ea 100644 --- a/apps/content/models.py +++ b/apps/content/models.py @@ -1,9 +1,13 @@ from urllib.parse import urlparse from django.db import models +from django.contrib.auth import get_user_model from polymorphic.models import PolymorphicModel +User = get_user_model() + + class ImageObject(models.Model): image = models.ImageField('Изображение', upload_to='content/imageobject') @@ -36,6 +40,12 @@ class Content(PolymorphicModel): verbose_name='Урок онлайн школы', related_name='content', ) + contest = models.ForeignKey( + 'Contest', on_delete=models.CASCADE, + null=True, blank=True, + verbose_name='Конкурс', + related_name='content', + ) title = models.CharField('Заголовок', max_length=100, default='') position = models.PositiveSmallIntegerField( 'Положение на странице', @@ -131,3 +141,41 @@ class Baner(models.Model): if self.use: Baner.objects.filter(use=True).update(use=False) return super().save(*args, **kwargs) + + +class Contest(models.Model): + title = models.CharField(max_length=255) + description = models.TextField(max_length=1000, blank=True, default='') + slug = models.SlugField( + allow_unicode=True, null=True, blank=True, + max_length=100, unique=True, db_index=True, + ) + cover = models.ForeignKey( + ImageObject, related_name='contest_covers', + verbose_name='Фоновая картинка', on_delete=models.CASCADE, + null=True, blank=True, + ) + date_start = models.DateField('Дата начала', null=True, blank=True) + date_end = models.DateField('Дата окончания', null=True, blank=True) + active = models.BooleanField(default=True) + # TODO? baner + + def save(self, *args, **kwargs): + if self.active: + Contest.objects.filter(active=True).update(active=False) + return super().save(*args, **kwargs) + + +class ContestWork(models.Model): + user = models.ForeignKey( + User, on_delete=models.CASCADE + ) + contest = models.ForeignKey(Contest, on_delete=models.CASCADE) + image = models.ForeignKey( + ImageObject, related_name='contest_work_images', + verbose_name='Работа участника', on_delete=models.CASCADE, + ) + child_full_name = models.CharField(max_length=255) + age = models.SmallIntegerField() + created_at = models.DateTimeField(auto_now_add=True) + likes = models.ManyToManyField('course.Like', blank=True) diff --git a/apps/content/templates/content/blocks/contest_work.html b/apps/content/templates/content/blocks/contest_work.html new file mode 100644 index 00000000..b80e6e06 --- /dev/null +++ b/apps/content/templates/content/blocks/contest_work.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/apps/course/templates/course/content/gallery.html b/apps/content/templates/content/blocks/gallery.html similarity index 97% rename from apps/course/templates/course/content/gallery.html rename to apps/content/templates/content/blocks/gallery.html index 1116322a..69ad0386 100644 --- a/apps/course/templates/course/content/gallery.html +++ b/apps/content/templates/content/blocks/gallery.html @@ -1,32 +1,32 @@ -{% load thumbnail %} -{% if results %} -
Галерея итогов обучения
- -{% else %} -
-
-
{{ content.title }}
- -
-
-{% endif %} +{% load thumbnail %} +{% if results %} +
Галерея итогов обучения
+ +{% else %} +
+
+
{{ content.title }}
+ +
+
+{% endif %} diff --git a/apps/course/templates/course/content/image.html b/apps/content/templates/content/blocks/image.html similarity index 96% rename from apps/course/templates/course/content/image.html rename to apps/content/templates/content/blocks/image.html index 4ab24601..5d515430 100644 --- a/apps/course/templates/course/content/image.html +++ b/apps/content/templates/content/blocks/image.html @@ -1,10 +1,10 @@ -
-
-
- {{ content.title }} -
-
- -
-
-
+
+
+
+ {{ content.title }} +
+
+ +
+
+
diff --git a/apps/course/templates/course/content/imagetext.html b/apps/content/templates/content/blocks/imagetext.html similarity index 100% rename from apps/course/templates/course/content/imagetext.html rename to apps/content/templates/content/blocks/imagetext.html diff --git a/apps/course/templates/course/content/text.html b/apps/content/templates/content/blocks/text.html similarity index 94% rename from apps/course/templates/course/content/text.html rename to apps/content/templates/content/blocks/text.html index 18ce2235..59e46ded 100644 --- a/apps/course/templates/course/content/text.html +++ b/apps/content/templates/content/blocks/text.html @@ -1,10 +1,10 @@ -
-
-
- {{ content.title }} -
-
- {{ content.txt | safe }} -
-
+
+
+
+ {{ content.title }} +
+
+ {{ content.txt | safe }} +
+
\ No newline at end of file diff --git a/apps/course/templates/course/content/video.html b/apps/content/templates/content/blocks/video.html similarity index 95% rename from apps/course/templates/course/content/video.html rename to apps/content/templates/content/blocks/video.html index 3527507f..fe741465 100644 --- a/apps/course/templates/course/content/video.html +++ b/apps/content/templates/content/blocks/video.html @@ -1,43 +1,43 @@ -
-
-
- {{ content.title }} -
-
- {% if 'youtube.com' in content.url or 'youtu.be' in content.url %} - - {% elif 'vimeo.com' in content.url %} - - {% endif %} -
-
-
- - \ No newline at end of file diff --git a/apps/content/templates/content/contest.html b/apps/content/templates/content/contest.html new file mode 100644 index 00000000..2ca0a7d6 --- /dev/null +++ b/apps/content/templates/content/contest.html @@ -0,0 +1,44 @@ +{% extends "templates/lilcity/index.html" %} + + +{% block content %} +
+
+
+ Lil School — первая образовательная онлайн-платформа креативного мышления для детей +
+
+ Приглашаем вас на месяц открытых дверей в Lil School +
+ +
+
+{% for content in contest.content.all %} + + {% with template="content/blocks/"|add:content.ctype|add:".html" %} + {% include template %} + {% endwith %} + +{% endfor %} +
+
+ +
Галерея
+
+
+

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

+ +
+ +
+
+{% endblock content %} diff --git a/apps/content/templates/content/contest_edit.html b/apps/content/templates/content/contest_edit.html new file mode 100644 index 00000000..d1c26b62 --- /dev/null +++ b/apps/content/templates/content/contest_edit.html @@ -0,0 +1,18 @@ +{% extends "templates/lilcity/edit_index.html" %} +{% load static %} + +{% block title %} + {% if object %} + Редактирование конкурса {{ object.title }} + {% else %} + Создание конкурса + {% endif %} +{% endblock title %} + +{% block content %} + +{% endblock content %} +{% block foot %} + + +{% endblock foot %} diff --git a/apps/content/views.py b/apps/content/views.py index 28002783..f4335009 100644 --- a/apps/content/views.py +++ b/apps/content/views.py @@ -1,2 +1,24 @@ -from django.shortcuts import render +from django.contrib.auth.decorators import login_required +from django.shortcuts import get_object_or_404 +from django.utils.decorators import method_decorator +from django.views.generic import TemplateView +from apps.content.models import Contest + + +@method_decorator(login_required, name='dispatch') +class ContestEditView(TemplateView): + template_name = 'content/contest_edit.html' + + def get(self, request, pk=None, lesson=None): + if pk: + self.object = get_object_or_404(Contest, pk=pk) + else: + self.object = Contest() + + return super().get(request) + + def get_context_data(self): + context = super().get_context_data() + context['object'] = self.object + return context diff --git a/apps/course/migrations/0040_course_age.py b/apps/course/migrations/0040_course_age.py new file mode 100644 index 00000000..f8426fd7 --- /dev/null +++ b/apps/course/migrations/0040_course_age.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.6 on 2018-08-08 01:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0039_lesson_position'), + ] + + operations = [ + migrations.AddField( + model_name='course', + name='age', + field=models.SmallIntegerField(choices=[(0, ''), (1, 'до 5'), (2, '5-7'), (3, '7-9'), (4, '9-12'), (5, '12-15'), (6, '15-18'), (7, 'от 18')], default=0), + ), + ] diff --git a/apps/course/migrations/0041_auto_20180813_1306.py b/apps/course/migrations/0041_auto_20180813_1306.py new file mode 100644 index 00000000..d2596544 --- /dev/null +++ b/apps/course/migrations/0041_auto_20180813_1306.py @@ -0,0 +1,36 @@ +# Generated by Django 2.0.6 on 2018-08-13 13:06 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('content', '0021_auto_20180813_1306'), + ('course', '0040_course_age'), + ] + + operations = [ + migrations.CreateModel( + name='ContestWorkComment', + fields=[ + ('comment_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='course.Comment')), + ('contest_work', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='content.ContestWork')), + ], + options={ + 'ordering': ('tree_id', 'lft'), + 'abstract': False, + 'base_manager_name': 'objects', + }, + bases=('course.comment',), + ), + migrations.AlterModelOptions( + name='course', + options={'ordering': ['-is_featured'], 'verbose_name': 'Курс', 'verbose_name_plural': 'Курсы'}, + ), + migrations.RemoveField( + model_name='course', + name='age', + ), + ] diff --git a/apps/course/models.py b/apps/course/models.py index d4966b3c..6492b999 100644 --- a/apps/course/models.py +++ b/apps/course/models.py @@ -15,7 +15,7 @@ from project.mixins import BaseModel, DeactivatedMixin from .manager import CategoryQuerySet -from apps.content.models import ImageObject, Gallery, Video +from apps.content.models import ImageObject, Gallery, Video, ContestWork User = get_user_model() @@ -284,3 +284,7 @@ class LessonComment(Comment): class Meta(Comment.Meta): verbose_name = 'Комментарий урока' verbose_name_plural = 'Комментарии уроков' + + +class ContestWorkComment(Comment): + contest_work = models.ForeignKey(ContestWork, on_delete=models.CASCADE, related_name='comments') diff --git a/apps/course/templates/course/course.html b/apps/course/templates/course/course.html index 53a56abc..c3a35c4f 100644 --- a/apps/course/templates/course/course.html +++ b/apps/course/templates/course/course.html @@ -227,7 +227,7 @@
{% for content in course.content.all %} - {% with template="course/content/"|add:content.ctype|add:".html" %} + {% with template="content/blocks/"|add:content.ctype|add:".html" %} {% include template %} {% endwith %} diff --git a/apps/course/templates/course/lesson.html b/apps/course/templates/course/lesson.html index 1152d092..cf40b447 100644 --- a/apps/course/templates/course/lesson.html +++ b/apps/course/templates/course/lesson.html @@ -44,7 +44,7 @@
{% for content in lesson.content.all %} - {% with template="course/content/"|add:content.ctype|add:".html" %} + {% with template="content/blocks/"|add:content.ctype|add:".html" %} {% include template %} {% endwith %} diff --git a/project/templates/blocks/lil_store_js.html b/project/templates/blocks/lil_store_js.html new file mode 100644 index 00000000..d7d6bb9e --- /dev/null +++ b/project/templates/blocks/lil_store_js.html @@ -0,0 +1,8 @@ + diff --git a/project/templates/lilcity/edit_index.html b/project/templates/lilcity/edit_index.html index f6029dbb..18be67aa 100644 --- a/project/templates/lilcity/edit_index.html +++ b/project/templates/lilcity/edit_index.html @@ -315,6 +315,7 @@
+{% include 'templates/blocks/lil_store_js.html' %} + + diff --git a/web/src/components/ContestWorks.vue b/web/src/components/ContestWorks.vue new file mode 100644 index 00000000..40f35f21 --- /dev/null +++ b/web/src/components/ContestWorks.vue @@ -0,0 +1,30 @@ + + + + + diff --git a/web/src/components/blocks/ContestWork b/web/src/components/blocks/ContestWork new file mode 100644 index 00000000..a59389f9 --- /dev/null +++ b/web/src/components/blocks/ContestWork @@ -0,0 +1,38 @@ + + + + + diff --git a/web/src/js/contest-redactor.js b/web/src/js/contest-redactor.js new file mode 100644 index 00000000..a485cac6 --- /dev/null +++ b/web/src/js/contest-redactor.js @@ -0,0 +1,19 @@ +import Vue from 'vue' +import Vuelidate from 'vuelidate' +import VueAutosize from '../components/directives/autosize' +import ContestRedactor from '../components/ContestRedactor.vue' + +if (process.env.NODE_ENV === 'development') { + // Enable vue-devtools + Vue.config.devtools = true; +} + +Vue.use(VueAutosize); +Vue.use(Vuelidate); + +let app = new Vue({ + el: '#lilcity-vue-app', + components: { + 'contest-redactor': ContestRedactor, + } +}); From 876cefab66ad6b37c9056b95c2d6a7f55c90f09f Mon Sep 17 00:00:00 2001 From: gzbender Date: Tue, 14 Aug 2018 02:49:40 +0500 Subject: [PATCH 045/137] LIL-582 --- apps/content/templates/content/contest.html | 21 +- apps/content/views.py | 8 +- project/templates/blocks/lil_store_js.html | 2 + project/templates/blocks/popup_auth.html | 2 +- project/urls.py | 3 +- web/src/components/UploadContestWork.vue | 183 ++++++++++++++++++ .../blocks/{ContestWork => ContestWork.vue} | 0 web/src/components/blocks/Image.vue | 4 +- web/src/js/app.js | 23 +++ web/webpack.config.js | 5 + 10 files changed, 237 insertions(+), 14 deletions(-) create mode 100644 web/src/components/UploadContestWork.vue rename web/src/components/blocks/{ContestWork => ContestWork.vue} (100%) diff --git a/apps/content/templates/content/contest.html b/apps/content/templates/content/contest.html index 2ca0a7d6..3fe3b7b5 100644 --- a/apps/content/templates/content/contest.html +++ b/apps/content/templates/content/contest.html @@ -2,19 +2,21 @@ {% block content %} -
+ +
- Lil School — первая образовательная онлайн-платформа креативного мышления для детей + {{ contest.title }}
- Приглашаем вас на месяц открытых дверей в Lil School + {{ contest.description }}
+
{% for content in contest.content.all %} {% with template="content/blocks/"|add:content.ctype|add:".html" %} @@ -25,20 +27,21 @@
-
Галерея
+
Галлерея
-

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

-
+
{% endblock content %} + +{% block foot %} +{% endblock foot %} diff --git a/apps/content/views.py b/apps/content/views.py index f4335009..52c3d966 100644 --- a/apps/content/views.py +++ b/apps/content/views.py @@ -1,7 +1,7 @@ from django.contrib.auth.decorators import login_required from django.shortcuts import get_object_or_404 from django.utils.decorators import method_decorator -from django.views.generic import TemplateView +from django.views.generic import TemplateView, DetailView from apps.content.models import Contest @@ -22,3 +22,9 @@ class ContestEditView(TemplateView): context = super().get_context_data() context['object'] = self.object return context + + +class ContestView(DetailView): + model = Contest + context_object_name = 'contest' + template_name = 'content/contest.html' diff --git a/project/templates/blocks/lil_store_js.html b/project/templates/blocks/lil_store_js.html index d7d6bb9e..e944a567 100644 --- a/project/templates/blocks/lil_store_js.html +++ b/project/templates/blocks/lil_store_js.html @@ -1,5 +1,7 @@ +{% load static %} + + diff --git a/web/src/components/blocks/ContestWork b/web/src/components/blocks/ContestWork.vue similarity index 100% rename from web/src/components/blocks/ContestWork rename to web/src/components/blocks/ContestWork.vue diff --git a/web/src/components/blocks/Image.vue b/web/src/components/blocks/Image.vue index 9006500d..789d8a69 100644 --- a/web/src/components/blocks/Image.vue +++ b/web/src/components/blocks/Image.vue @@ -1,5 +1,5 @@