# Conflicts: # apps/course/templates/course/content/gallery.html # apps/course/templates/course/content/image.html # apps/course/templates/course/content/imagetext.html # project/templates/lilcity/main.htmlremotes/origin/hasaccess
commit
d10062ba5a
109 changed files with 8128 additions and 670 deletions
@ -1,39 +1,27 @@ |
|||||||
from constance import config |
|
||||||
from constance.admin import get_values, ConstanceForm |
|
||||||
from rest_framework import serializers |
from rest_framework import serializers |
||||||
from rest_framework.fields import SkipField |
|
||||||
from collections import OrderedDict |
|
||||||
|
|
||||||
|
from apps.config.models import Config |
||||||
|
|
||||||
def _set_constance_value(key, value): |
|
||||||
form = ConstanceForm(initial=get_values()) |
|
||||||
field = form.fields[key] |
|
||||||
clean_value = field.clean(field.to_python(value)) |
|
||||||
setattr(config, key, clean_value) |
|
||||||
|
|
||||||
|
class ConfigSerializer(serializers.ModelSerializer): |
||||||
class ConfigSerializer(serializers.Serializer): |
|
||||||
SERVICE_COMMISSION = serializers.IntegerField(required=False) |
SERVICE_COMMISSION = serializers.IntegerField(required=False) |
||||||
SERVICE_DISCOUNT_MIN_AMOUNT = serializers.IntegerField(required=False) |
SERVICE_DISCOUNT_MIN_AMOUNT = serializers.IntegerField(required=False) |
||||||
SERVICE_DISCOUNT = serializers.IntegerField(required=False) |
SERVICE_DISCOUNT = serializers.IntegerField(required=False) |
||||||
INSTAGRAM_CLIENT_ACCESS_TOKEN = serializers.CharField(required=False) |
INSTAGRAM_CLIENT_ACCESS_TOKEN = serializers.CharField(required=False) |
||||||
INSTAGRAM_CLIENT_SECRET = serializers.CharField(required=False) |
INSTAGRAM_CLIENT_SECRET = serializers.CharField(required=False) |
||||||
INSTAGRAM_PROFILE_URL = serializers.CharField(required=False) |
INSTAGRAM_PROFILE_URL = serializers.CharField(required=False) |
||||||
|
SCHOOL_LOGO_IMAGE = serializers.ImageField(required=False, allow_null=True) |
||||||
|
MAIN_PAGE_TOP_IMAGE = serializers.ImageField(required=False, allow_null=True) |
||||||
|
|
||||||
def to_representation(self, instance): |
class Meta: |
||||||
ret = OrderedDict() |
model = Config |
||||||
fields = self._readable_fields |
fields = ( |
||||||
for field in fields: |
'SERVICE_COMMISSION', |
||||||
attribute = instance.get(field.field_name) |
'SERVICE_DISCOUNT_MIN_AMOUNT', |
||||||
ret[field.field_name] = field.to_representation(attribute) |
'SERVICE_DISCOUNT', |
||||||
return ret |
'INSTAGRAM_CLIENT_ACCESS_TOKEN', |
||||||
|
'INSTAGRAM_CLIENT_SECRET', |
||||||
def to_internal_value(self, data): |
'INSTAGRAM_PROFILE_URL', |
||||||
ret = OrderedDict(get_values()) |
'SCHOOL_LOGO_IMAGE', |
||||||
for k, v in data.items(): |
'MAIN_PAGE_TOP_IMAGE', |
||||||
ret[k] = v |
) |
||||||
return ret |
|
||||||
|
|
||||||
def update(self, instance, validated_data): |
|
||||||
for k, v in validated_data.items(): |
|
||||||
_set_constance_value(k, v) |
|
||||||
|
|||||||
@ -0,0 +1,18 @@ |
|||||||
|
from django.contrib.auth import login |
||||||
|
from django.utils.deprecation import MiddlewareMixin |
||||||
|
|
||||||
|
from rest_framework.authtoken.models import Token |
||||||
|
|
||||||
|
|
||||||
|
class TokenAuthLoginMiddleware(MiddlewareMixin): |
||||||
|
|
||||||
|
def process_request(self, request): |
||||||
|
if 'token' in request.GET: |
||||||
|
token = request.GET.get('token') |
||||||
|
if token: |
||||||
|
try: |
||||||
|
token = Token.objects.get(key=token) |
||||||
|
user = token.user |
||||||
|
login(request, user) |
||||||
|
except Token.DoesNotExist: |
||||||
|
pass |
||||||
@ -1,2 +1,2 @@ |
|||||||
Восстановление пароля для {{ email }}. Перейдите по ссылке ниже: |
Восстановление пароля для {{ email }}. Перейдите по ссылке ниже: |
||||||
{{ protocol}}://{{ domain }}{% url 'lilcity:password_reset_confirm' uidb64=uid token=token %} |
{{ domain }}{% url 'lilcity:password_reset_confirm' uidb64=uid token=token %} |
||||||
|
|||||||
@ -0,0 +1,5 @@ |
|||||||
|
from django.contrib import admin |
||||||
|
|
||||||
|
from .models import Config |
||||||
|
|
||||||
|
admin.site.register(Config) |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
from django.apps import AppConfig |
||||||
|
|
||||||
|
|
||||||
|
class ConfigConfig(AppConfig): |
||||||
|
name = 'config' |
||||||
@ -0,0 +1,27 @@ |
|||||||
|
# Generated by Django 2.0.3 on 2018-03-26 10:25 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
initial = True |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.CreateModel( |
||||||
|
name='Config', |
||||||
|
fields=[ |
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||||
|
('INSTAGRAM_CLIENT_ACCESS_TOKEN', models.CharField(default='7145314808.f6fa114.6b737a5355534e0eb5cf7c40cb4998f6', max_length=51)), |
||||||
|
('INSTAGRAM_CLIENT_SECRET', models.CharField(default='2334a921425140ccb180d145dcd35b25', max_length=32)), |
||||||
|
('INSTAGRAM_PROFILE_URL', models.CharField(default='#', max_length=126)), |
||||||
|
('SERVICE_COMMISSION', models.IntegerField(default=10)), |
||||||
|
('SERVICE_DISCOUNT_MIN_AMOUNT', models.IntegerField(default=3500)), |
||||||
|
('SERVICE_DISCOUNT', models.ImageField(default=1000, upload_to='')), |
||||||
|
('SCHOOL_LOGO_IMAGE', models.ImageField(null=True, upload_to='')), |
||||||
|
], |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
# Generated by Django 2.0.3 on 2018-03-26 10:26 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('config', '0001_initial'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AlterField( |
||||||
|
model_name='config', |
||||||
|
name='SCHOOL_LOGO_IMAGE', |
||||||
|
field=models.FileField(null=True, upload_to=''), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,23 @@ |
|||||||
|
# Generated by Django 2.0.3 on 2018-03-26 10:27 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('config', '0002_auto_20180326_1026'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AlterField( |
||||||
|
model_name='config', |
||||||
|
name='SCHOOL_LOGO_IMAGE', |
||||||
|
field=models.ImageField(null=True, upload_to=''), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='config', |
||||||
|
name='SERVICE_DISCOUNT', |
||||||
|
field=models.IntegerField(default=1000), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
# Generated by Django 2.0.3 on 2018-03-26 11:20 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('config', '0003_auto_20180326_1027'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AddField( |
||||||
|
model_name='config', |
||||||
|
name='MAIN_PAGE_TOP_IMAGE', |
||||||
|
field=models.ImageField(null=True, upload_to=''), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,23 @@ |
|||||||
|
# Generated by Django 2.0.3 on 2018-03-26 13:14 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('config', '0004_config_main_page_top_image'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AlterField( |
||||||
|
model_name='config', |
||||||
|
name='MAIN_PAGE_TOP_IMAGE', |
||||||
|
field=models.ImageField(blank=True, null=True, upload_to=''), |
||||||
|
), |
||||||
|
migrations.AlterField( |
||||||
|
model_name='config', |
||||||
|
name='SCHOOL_LOGO_IMAGE', |
||||||
|
field=models.ImageField(blank=True, null=True, upload_to=''), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,39 @@ |
|||||||
|
from django.db import models |
||||||
|
|
||||||
|
|
||||||
|
class Config(models.Model): |
||||||
|
INSTAGRAM_CLIENT_ACCESS_TOKEN = models.CharField( |
||||||
|
max_length=51, default='7145314808.f6fa114.6b737a5355534e0eb5cf7c40cb4998f6' |
||||||
|
) |
||||||
|
INSTAGRAM_CLIENT_SECRET = models.CharField(max_length=32, default='2334a921425140ccb180d145dcd35b25') |
||||||
|
INSTAGRAM_PROFILE_URL = models.CharField(max_length=126, default='#') |
||||||
|
SERVICE_COMMISSION = models.IntegerField(default=10) |
||||||
|
SERVICE_DISCOUNT_MIN_AMOUNT = models.IntegerField(default=3500) |
||||||
|
SERVICE_DISCOUNT = models.IntegerField(default=1000) |
||||||
|
SCHOOL_LOGO_IMAGE = models.ImageField(null=True, blank=True) |
||||||
|
MAIN_PAGE_TOP_IMAGE = models.ImageField(null=True, blank=True) |
||||||
|
|
||||||
|
def save(self, *args, **kwargs): |
||||||
|
self.pk = 1 |
||||||
|
super().save(*args, **kwargs) |
||||||
|
|
||||||
|
def delete(self, *args, **kwargs): |
||||||
|
pass |
||||||
|
|
||||||
|
@classmethod |
||||||
|
def load(cls): |
||||||
|
try: |
||||||
|
obj, created = cls.objects.get_or_create(pk=1) |
||||||
|
except: |
||||||
|
# This magic for migrate |
||||||
|
obj = { |
||||||
|
'INSTAGRAM_CLIENT_ACCESS_TOKEN': '', |
||||||
|
'INSTAGRAM_CLIENT_SECRET': '', |
||||||
|
'INSTAGRAM_PROFILE_URL': '', |
||||||
|
'SERVICE_COMMISSION': '', |
||||||
|
'SERVICE_DISCOUNT_MIN_AMOUNT': '', |
||||||
|
'SERVICE_DISCOUNT': '', |
||||||
|
'SCHOOL_LOGO_IMAGE': '', |
||||||
|
'MAIN_PAGE_TOP_IMAGE': '', |
||||||
|
} |
||||||
|
return obj |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
from django.test import TestCase |
||||||
|
|
||||||
|
# Create your tests here. |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
from django.shortcuts import render |
||||||
|
|
||||||
|
# Create your views here. |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
# Generated by Django 2.0.3 on 2018-03-16 11:05 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('content', '0014_auto_20180215_1503'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AddField( |
||||||
|
model_name='content', |
||||||
|
name='uuid', |
||||||
|
field=models.UUIDField(blank=True, null=True), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
# Generated by Django 2.0.2 on 2018-03-12 10:05 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('course', '0034_auto_20180215_1503'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AddField( |
||||||
|
model_name='comment', |
||||||
|
name='deactivated_at', |
||||||
|
field=models.DateTimeField(blank=True, default=None, null=True), |
||||||
|
), |
||||||
|
] |
||||||
@ -1,5 +1,10 @@ |
|||||||
{% load mptt_tags %} |
{% load mptt_tags %} |
||||||
|
|
||||||
{% recursetree object.comments.all %} |
{% recursetree object.comments.all %} |
||||||
|
{% if not node.deactivated_at %} |
||||||
{% include './comment.html' %} |
{% include './comment.html' %} |
||||||
{{ children }} {% endrecursetree %} |
{% if not node.is_leaf_node %} |
||||||
|
{{ children }} |
||||||
|
{% endif %} |
||||||
|
{% endif %} |
||||||
|
{% endrecursetree %} |
||||||
|
|||||||
@ -1,35 +1,32 @@ |
|||||||
|
{% load thumbnail %} |
||||||
{% if results %} |
{% if results %} |
||||||
<div class="title">Галерея итогов обучения</div> |
<div class="title">Галерея итогов обучения</div> |
||||||
<div class="examples"> |
<div class="examples gallery"> |
||||||
{% for image in course.gallery.gallery_images.all %} |
{% for image in course.gallery.gallery_images.all %} |
||||||
<div class="examples__item"> |
<div class="examples__item"> |
||||||
<img class="examples__pic" src="{{ image.img.image.url }}"> |
<a href="{{ image.img.image.url }}"> |
||||||
|
{% thumbnail image.img.image "140x140" crop="center" as im %} |
||||||
|
<img class="examples__pic" src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}"> |
||||||
|
{% endthumbnail %} |
||||||
|
</a> |
||||||
|
</div> |
||||||
|
{% endfor %} |
||||||
</div> |
</div> |
||||||
{% endfor %} |
|
||||||
</div> |
|
||||||
{% else %} |
{% else %} |
||||||
<div class="section section_gradient"> |
<div class="section section_gradient"> |
||||||
<div class="section__center center center_sm"> |
<div class="section__center center center_sm"> |
||||||
<div class="title">{{ content.title }}</div> |
<div class="title">{{ content.title }}</div> |
||||||
<div class="examples"> |
<div class="examples"> |
||||||
{% for image in content.gallery_images.all %} |
{% for image in content.gallery_images.all %} |
||||||
<div class="examples__item"> |
<div class="examples__item"> |
||||||
<img class="examples__pic" src="{{ image.img.image.url }}"> |
<a href="{{ image.img.image.url }}"> |
||||||
|
{% thumbnail image.img.image "140x140" crop="center" as im %} |
||||||
|
<img class="examples__pic" src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}"> |
||||||
|
{% endthumbnail %} |
||||||
|
</a> |
||||||
</div> |
</div> |
||||||
{% endfor %} |
{% endfor %} |
||||||
</div> |
</div> |
||||||
</div> |
|
||||||
</div> |
</div> |
||||||
{% endif %} |
|
||||||
|
|
||||||
|
|
||||||
<!--<div class="section section_gradient"> |
|
||||||
<div class="section__center center center_sm"> |
|
||||||
|
|
||||||
<div class="title">Галерея итогов обучения</div> |
|
||||||
<div class="examples"> |
|
||||||
|
|
||||||
</div> |
|
||||||
|
|
||||||
</div> |
</div> |
||||||
</div>--> |
{% endif %} |
||||||
|
|||||||
@ -1,13 +1,19 @@ |
|||||||
{% extends "templates/lilcity/edit_index.html" %} |
{% extends "templates/lilcity/edit_index.html" %} |
||||||
{% load static %} |
{% load static %} |
||||||
{% block title %}{% if course %}Редактирование курса {{ course.title }}{% else %}Создание курса{% endif %}{% endblock title%} |
{% block title %} |
||||||
|
{% if course %} |
||||||
|
Редактирование {% if not live %}курса{% else %}стрима{% endif %} {{ course.title }} |
||||||
|
{% else %} |
||||||
|
Создание {% if not live %}курса{% else %}стрима{% endif %} |
||||||
|
{% endif %} |
||||||
|
{% endblock title%} |
||||||
{% block content %} |
{% block content %} |
||||||
<course-redactor author-picture="{% if request.user.photo %}{{ request.user.photo.url }}{% else %}{% static 'img/user.jpg' %}{% endif %}" |
<course-redactor :live="{{ live }}" author-picture="{% if request.user.photo %}{{ request.user.photo.url }}{% else %}{% static 'img/user.jpg' %}{% endif %}" |
||||||
author-name="{{ request.user.first_name }} {{ request.user.last_name }}" |
author-name="{{ request.user.first_name }} {{ request.user.last_name }}" |
||||||
access-token="{{ request.user.auth_token }}" |
access-token="{{ request.user.auth_token }}" |
||||||
{% if course and course.id %}:course-id="{{ course.id }}"{% endif %}></course-redactor> |
{% if course and course.id %}:course-id="{{ course.id }}"{% endif %}></course-redactor> |
||||||
{% endblock content %} |
{% endblock content %} |
||||||
{% block foot %} |
{% block foot %} |
||||||
<script type="text/javascript" src={% static "courseRedactor.js" %}></script> |
<script type="text/javascript" src="{% static "courseRedactor.js" %}"></script> |
||||||
<link rel="stylesheet" href={% static "courseRedactor.css" %}/> |
<link rel="stylesheet" href="{% static "courseRedactor.css" %}" /> |
||||||
{% endblock foot %} |
{% endblock foot %} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
{% extends "notification/email/_base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
<p style="margin: 0 0 20px">Поздравляем! Вам одобрено назначение преподавателем!</p> |
||||||
|
<div style="margin-bottom: 10px;"> |
||||||
|
<p>Теперь вы можете публиковать курсы.</p> |
||||||
|
{% if password and email %} |
||||||
|
<p><strong>Параметры входа:</strong></p> |
||||||
|
<p><strong>email:</strong> {{ email }}</p> |
||||||
|
<p><strong>пароль:</strong> {{ password }}</p> |
||||||
|
{% endif %} |
||||||
|
</div> |
||||||
|
{% endblock content %} |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
{% extends "notification/email/_base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
<p style="margin: 0 0 20px">К сожалению вам отказано в назначении преподавателем!</p> |
||||||
|
<div style="margin-bottom: 10px;"> |
||||||
|
<p>{{ cause }}</p> |
||||||
|
</div> |
||||||
|
{% endblock content %} |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
from mixpanel import Mixpanel |
||||||
|
|
||||||
|
from django.conf import settings |
||||||
|
|
||||||
|
from project.celery import app |
||||||
|
|
||||||
|
|
||||||
|
@app.task |
||||||
|
def transaction_to_mixpanel(user_id, amount, time, product_type): |
||||||
|
mix = Mixpanel(settings.MIX_TOKEN) |
||||||
|
mix.people_track_charge( |
||||||
|
user_id, |
||||||
|
amount, |
||||||
|
{ |
||||||
|
'$time': time, |
||||||
|
'product_type': product_type, |
||||||
|
} |
||||||
|
) |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
{% extends "templates/lilcity/index.html" %} {% load static %} {% block content %} |
||||||
|
<div class="section"> |
||||||
|
<div class="section__center center center_xs"> |
||||||
|
<div class="done"> |
||||||
|
<div class="done__title title">Вы успешно приобрели курс!</div> |
||||||
|
<div class="done__foot"> |
||||||
|
<a class="done__btn btn btn_md btn_stroke" href="{% url 'course' course.id %}">ПЕРЕЙТИ К КУРСУ</a> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
{% endblock content %} |
||||||
@ -0,0 +1,58 @@ |
|||||||
|
[ |
||||||
|
{ |
||||||
|
"model": "user.subscriptioncategory", |
||||||
|
"pk": 1, |
||||||
|
"fields": { |
||||||
|
"title": "Новости школы", |
||||||
|
"auto_add": false |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "user.subscriptioncategory", |
||||||
|
"pk": 2, |
||||||
|
"fields": { |
||||||
|
"title": "Новые курсы", |
||||||
|
"auto_add": true |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "user.subscriptioncategory", |
||||||
|
"pk": 3, |
||||||
|
"fields": { |
||||||
|
"title": "Бонусы от партнёров", |
||||||
|
"auto_add": false |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "user.subscriptioncategory", |
||||||
|
"pk": 4, |
||||||
|
"fields": { |
||||||
|
"title": "Акции", |
||||||
|
"auto_add": true |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "user.subscriptioncategory", |
||||||
|
"pk": 5, |
||||||
|
"fields": { |
||||||
|
"title": "Партнёрские акции", |
||||||
|
"auto_add": false |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "user.subscriptioncategory", |
||||||
|
"pk": 6, |
||||||
|
"fields": { |
||||||
|
"title": "Новости компании", |
||||||
|
"auto_add": true |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "user.subscriptioncategory", |
||||||
|
"pk": 7, |
||||||
|
"fields": { |
||||||
|
"title": "Комментарии в которых участвуете", |
||||||
|
"auto_add": false |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
@ -0,0 +1,27 @@ |
|||||||
|
# Generated by Django 2.0.2 on 2018-03-12 14:01 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('user', '0008_auto_20180212_0750'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.CreateModel( |
||||||
|
name='AuthorRequest', |
||||||
|
fields=[ |
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||||
|
('first_name', models.CharField(max_length=30, verbose_name='first name')), |
||||||
|
('last_name', models.CharField(max_length=150, verbose_name='last name')), |
||||||
|
('email', models.EmailField(max_length=254, verbose_name='email address')), |
||||||
|
('about', models.CharField(blank=True, max_length=1000, null=True, verbose_name='О себе')), |
||||||
|
('facebook', models.URLField(blank=True, default='', null=True)), |
||||||
|
('status', models.PositiveSmallIntegerField(choices=[(0, 'pending'), (1, 'accepted'), (2, 'declined')], default=0)), |
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)), |
||||||
|
('update_at', models.DateTimeField(auto_now=True)), |
||||||
|
], |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
# Generated by Django 2.0.2 on 2018-03-12 16:10 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('user', '0009_authorrequest'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AlterField( |
||||||
|
model_name='authorrequest', |
||||||
|
name='email', |
||||||
|
field=models.EmailField(max_length=254, unique=True, verbose_name='email address'), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,17 @@ |
|||||||
|
# Generated by Django 2.0.3 on 2018-03-13 07:44 |
||||||
|
|
||||||
|
from django.db import migrations |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('user', '0010_auto_20180312_1610'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AlterModelOptions( |
||||||
|
name='authorrequest', |
||||||
|
options={'ordering': ('-created_at',), 'verbose_name': 'Заявка не преподавателя', 'verbose_name_plural': 'Заявки не преподавателя'}, |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
# Generated by Django 2.0.3 on 2018-03-13 07:49 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('user', '0011_auto_20180313_0744'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AddField( |
||||||
|
model_name='authorrequest', |
||||||
|
name='cause', |
||||||
|
field=models.TextField(blank=True, null=True, verbose_name='Причина отказа'), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
# Generated by Django 2.0.3 on 2018-03-13 10:10 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('user', '0012_authorrequest_cause'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AddField( |
||||||
|
model_name='authorrequest', |
||||||
|
name='declined_send_at', |
||||||
|
field=models.DateTimeField(blank=True, null=True), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
# Generated by Django 2.0.3 on 2018-03-13 10:55 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('user', '0013_authorrequest_declined_send_at'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AddField( |
||||||
|
model_name='authorrequest', |
||||||
|
name='accepted_send_at', |
||||||
|
field=models.DateTimeField(blank=True, null=True), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,45 @@ |
|||||||
|
# Generated by Django 2.0.3 on 2018-03-15 05:47 |
||||||
|
|
||||||
|
from django.conf import settings |
||||||
|
from django.db import migrations, models |
||||||
|
import django.db.models.deletion |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('user', '0014_authorrequest_accepted_send_at'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.CreateModel( |
||||||
|
name='EmailSubscription', |
||||||
|
fields=[ |
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||||
|
('email', models.EmailField(max_length=254, unique=True, verbose_name='email address')), |
||||||
|
('mailchimp_status', models.PositiveSmallIntegerField(choices=[(0, 'error'), (1, 'sent')], default=0)), |
||||||
|
], |
||||||
|
), |
||||||
|
migrations.CreateModel( |
||||||
|
name='SubscriptionCategory', |
||||||
|
fields=[ |
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||||
|
('title', models.CharField(max_length=100)), |
||||||
|
], |
||||||
|
options={ |
||||||
|
'verbose_name': 'Категория подписки', |
||||||
|
'verbose_name_plural': 'Категории подписки', |
||||||
|
'ordering': ('title',), |
||||||
|
}, |
||||||
|
), |
||||||
|
migrations.AddField( |
||||||
|
model_name='emailsubscription', |
||||||
|
name='categories', |
||||||
|
field=models.ManyToManyField(to='user.SubscriptionCategory'), |
||||||
|
), |
||||||
|
migrations.AddField( |
||||||
|
model_name='emailsubscription', |
||||||
|
name='user', |
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,20 @@ |
|||||||
|
# Generated by Django 2.0.3 on 2018-03-15 06:03 |
||||||
|
|
||||||
|
from django.conf import settings |
||||||
|
from django.db import migrations, models |
||||||
|
import django.db.models.deletion |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('user', '0015_auto_20180315_0547'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AlterField( |
||||||
|
model_name='emailsubscription', |
||||||
|
name='user', |
||||||
|
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='email_subscription', to=settings.AUTH_USER_MODEL), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
# Generated by Django 2.0.3 on 2018-03-15 06:18 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('user', '0016_auto_20180315_0603'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AddField( |
||||||
|
model_name='subscriptioncategory', |
||||||
|
name='auto_add', |
||||||
|
field=models.BooleanField(default=False), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
# Generated by Django 2.0.3 on 2018-03-15 17:19 |
||||||
|
|
||||||
|
from django.db import migrations |
||||||
|
import phonenumber_field.modelfields |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('user', '0017_subscriptioncategory_auto_add'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AddField( |
||||||
|
model_name='user', |
||||||
|
name='phone', |
||||||
|
field=phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True, unique=True), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
# Generated by Django 2.0.3 on 2018-03-20 08:40 |
||||||
|
|
||||||
|
from django.db import migrations, models |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
('user', '0018_user_phone'), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.AddField( |
||||||
|
model_name='user', |
||||||
|
name='show_in_mainpage', |
||||||
|
field=models.BooleanField(default=False, verbose_name='Показывать на главной странице'), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
from mixpanel import Mixpanel |
||||||
|
|
||||||
|
from django.conf import settings |
||||||
|
|
||||||
|
from project.celery import app |
||||||
|
|
||||||
|
|
||||||
|
@app.task |
||||||
|
def user_to_mixpanel(user_id, email, phone, first_name, last_name, date_joined, role, subscriptions): |
||||||
|
mix = Mixpanel(settings.MIX_TOKEN) |
||||||
|
mix.people_set( |
||||||
|
user_id, |
||||||
|
{ |
||||||
|
'$email': email, |
||||||
|
'$phone': phone, |
||||||
|
'$first_name': first_name, |
||||||
|
'$last_name': last_name, |
||||||
|
'$created': date_joined, |
||||||
|
'role': role, |
||||||
|
'subscriptions': subscriptions, |
||||||
|
} |
||||||
|
) |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
{% extends "templates/lilcity/index.html" %} {% load static %} {% block content %} |
||||||
|
<div class="section"> |
||||||
|
<div class="section__center center center_xs"> |
||||||
|
<div class="done"> |
||||||
|
<div class="done__title title">Ваша заявка отправлена!</div> |
||||||
|
<div class="done__foot"> |
||||||
|
<a class="done__btn btn btn_md btn_stroke" href="/">ПЕРЕЙТИ К ГЛАВНОЙ</a> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
{% endblock content %} |
||||||
@ -0,0 +1,69 @@ |
|||||||
|
{% extends "templates/lilcity/index.html" %} {% load static %} {% block content %} {% if messages %} |
||||||
|
<div class="section section_menu"> |
||||||
|
<div class="section__center center center_xs"> |
||||||
|
{% for message in messages %} |
||||||
|
<div class="message message_{{ message.tags }}">{{ message }}</div> |
||||||
|
{% endfor %} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
{% endif %} |
||||||
|
<div class="section"> |
||||||
|
<div class="section__center center center_xs"> |
||||||
|
<form class="form" method="POST">{% csrf_token %} |
||||||
|
<div class="form__group"> |
||||||
|
<div class="form__title">Стать автором</div> |
||||||
|
<div class="form__fieldset"> |
||||||
|
<div class="form__field field{% if form.first_name.errors %} error{% endif %}"> |
||||||
|
<div class="field__label">ИМЯ</div> |
||||||
|
<div class="field__wrap"> |
||||||
|
<input name='first_name' class="field__input" type="text" placeholder="Имя" value="{{ form.first_name.value }}"> |
||||||
|
</div> |
||||||
|
{% if form.first_name.errors %} |
||||||
|
<div class="field__error">Укажите корректно свои данные</div> |
||||||
|
{% endif %} |
||||||
|
</div> |
||||||
|
<div class="form__field field{% if form.last_name.errors %} error{% endif %}"> |
||||||
|
<div class="field__label">ФАМИЛИЯ</div> |
||||||
|
<div class="field__wrap"> |
||||||
|
<input name='last_name' class="field__input" type="text" placeholder="Фамилия" value="{{ form.last_name.value }}"> |
||||||
|
</div> |
||||||
|
{% if form.last_name.errors %} |
||||||
|
<div class="field__error">Укажите корректно свои данные</div> |
||||||
|
{% endif %} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="form__field field{% if form.email.errors %} error{% endif %}"> |
||||||
|
<div class="field__label">Почта</div> |
||||||
|
<div class="field__wrap"> |
||||||
|
<input name='email' class="field__input" type="email" placeholder="Почта" value="{{ form.email.value }}"> |
||||||
|
</div> |
||||||
|
{% if form.email.errors %} |
||||||
|
<div class="field__error">Укажите корректно свои данные</div> |
||||||
|
{% endif %} |
||||||
|
</div> |
||||||
|
<div class="form__field field{% if form.about.errors %} error{% endif %}"> |
||||||
|
<div class="field__label">О себе</div> |
||||||
|
<div class="field__wrap"> |
||||||
|
<textarea name='about' class="field__textarea" placeholder="Расскажите о себе и своем опыте">{% if form.about.value %}{{ form.about.value }}{% endif %}</textarea> |
||||||
|
</div> |
||||||
|
{% if form.about.errors %} |
||||||
|
<div class="field__error">Укажите корректно свои данные</div> |
||||||
|
{% endif %} |
||||||
|
</div> |
||||||
|
<div class="form__field field{% if form.facebook.errors %} error{% endif %}"> |
||||||
|
<div class="field__label">FACEBOOK</div> |
||||||
|
<div class="field__wrap"> |
||||||
|
<input name='facebook' class="field__input" type="text" placeholder="https://facebook.com/lilcitycompany" value="{% if form.facebook.value %}{{ form.facebook.value }}{% endif %}"> |
||||||
|
</div> |
||||||
|
{% if form.facebook.errors %} |
||||||
|
<div class="field__error">Укажите корректно свои данные</div> |
||||||
|
{% endif %} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="form__foot"> |
||||||
|
<button class="form__btn btn btn_md">СОХРАНИТЬ</button> |
||||||
|
</div> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
{% endblock content %} |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
from apps.config.models import Config |
||||||
|
|
||||||
|
|
||||||
|
def config(request): |
||||||
|
return {"config": Config.load()} |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
from django.forms import ImageField as BaseImageField |
||||||
|
|
||||||
|
|
||||||
|
class ImageField(BaseImageField): |
||||||
|
|
||||||
|
def to_internal_value(self, data): |
||||||
|
# if data is None image field was not uploaded |
||||||
|
if data: |
||||||
|
file_object = super(ImageField, self).to_internal_value(data) |
||||||
|
django_field = self._DjangoImageField() |
||||||
|
django_field.error_messages = self.error_messages |
||||||
|
django_field.to_python(file_object) |
||||||
|
return file_object |
||||||
|
return data |
||||||
@ -0,0 +1,52 @@ |
|||||||
|
{% load static %} |
||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="ru"> |
||||||
|
|
||||||
|
<head> |
||||||
|
<meta charset="UTF-8"> |
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge"> |
||||||
|
<link rel="stylesheet" media="all" href={% static "app.css" %}> |
||||||
|
</head> |
||||||
|
|
||||||
|
<body> |
||||||
|
<div class="section section_gray"> |
||||||
|
<div class="section__center center center_md"> |
||||||
|
<a id="schedule" name="schedule"> |
||||||
|
<div class="title title_center">Расписание</div> |
||||||
|
</a> |
||||||
|
<div class="schedule"> |
||||||
|
{% for school_schedule in school_schedules %} |
||||||
|
<div class="schedule__item"> |
||||||
|
<div class="schedule__day">{{ school_schedule }}</div> |
||||||
|
<div class="schedule__wrap"> |
||||||
|
<div class="schedule__title">{{ school_schedule.title }}</div> |
||||||
|
<div class="schedule__content">{{ school_schedule.description }}</div> |
||||||
|
<div class="schedule__toggle toggle"> |
||||||
|
<button class="toggle__head js-toggle-head active">Материалы |
||||||
|
<svg class="icon icon-arrow-down"> |
||||||
|
<use xlink:href="{% static 'img/sprite.svg' %}#icon-arrow-down"></use> |
||||||
|
</svg> |
||||||
|
</button> |
||||||
|
<div class="toggle__body">{{ school_schedule.materials }}</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
{% endfor %} |
||||||
|
</div> |
||||||
|
{% comment %} <div class="text text_mb0"> |
||||||
|
<a href='#'>Распечатать расписание</a> чтобы не забыть |
||||||
|
</div> {% endcomment %} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<script type="text/javascript" src={% static "app.js" %}></script> |
||||||
|
<script type="text/javascript"> |
||||||
|
var toggle__body = Array.from(document.getElementsByClassName("toggle__body")); |
||||||
|
toggle__body.forEach(function (item, i, toggle__body) { |
||||||
|
item.style.display = "block" |
||||||
|
}); |
||||||
|
window.print(); |
||||||
|
window.close(); |
||||||
|
</script> |
||||||
|
</body> |
||||||
|
</html> |
||||||
@ -1,24 +1,27 @@ |
|||||||
# Python-3.6 |
# Python-3.6 |
||||||
gunicorn==19.7.1 |
|
||||||
requests==2.18.4 |
|
||||||
Django==2.0.2 |
|
||||||
django-anymail[mailgun]==1.2 |
|
||||||
# paymentwall-python==1.0.7 |
|
||||||
git+https://github.com/ivlevdenis/paymentwall-python.git |
|
||||||
twilio==6.10.0 |
|
||||||
psycopg2==2.7.3.2 |
|
||||||
facepy==1.0.9 |
|
||||||
Pillow==5.0.0 |
|
||||||
django-active-link==0.1.2 |
|
||||||
arrow==0.12.1 |
arrow==0.12.1 |
||||||
|
celery[redis]==4.1.0 |
||||||
|
Django==2.0.3 |
||||||
|
django-active-link==0.1.2 |
||||||
|
django-anymail[mailgun]==2.0 |
||||||
|
django-cors-headers==2.2.0 |
||||||
django-filter==2.0.0.dev1 |
django-filter==2.0.0.dev1 |
||||||
django-mptt==0.9.0 |
django-mptt==0.9.0 |
||||||
|
django-silk==2.0.0 |
||||||
|
django-phonenumber-field==2.0.0 |
||||||
django-polymorphic-tree==1.5 |
django-polymorphic-tree==1.5 |
||||||
celery[redis]==4.1.0 |
|
||||||
djangorestframework==3.7.7 |
djangorestframework==3.7.7 |
||||||
drf-yasg[validation]==1.4.0 |
drf-yasg[validation]==1.5.0 |
||||||
django-silk==2.0.0 |
facepy==1.0.9 |
||||||
django-cors-headers==2.1.0 |
gunicorn==19.7.1 |
||||||
django-constance[database]==2.1.0 |
mixpanel==4.3.2 |
||||||
|
psycopg2-binary==2.7.4 |
||||||
|
Pillow==5.0.0 |
||||||
|
raven==6.6.0 |
||||||
|
requests==2.18.4 |
||||||
|
sorl-thumbnail==12.4.1 |
||||||
|
twilio==6.10.5 |
||||||
|
# paymentwall-python==1.0.7 |
||||||
|
git+https://github.com/ivlevdenis/paymentwall-python.git |
||||||
# python-instagram==1.3.2 |
# python-instagram==1.3.2 |
||||||
git+https://github.com/ivlevdenis/python-instagram.git |
git+https://github.com/ivlevdenis/python-instagram.git |
||||||
|
|||||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 597 B |
@ -0,0 +1,36 @@ |
|||||||
|
import $ from 'jquery'; |
||||||
|
|
||||||
|
$(document).ready(function (e) { |
||||||
|
if (typeof mixpanel != 'undefined') { |
||||||
|
mixpanel.identify(USER_ID); |
||||||
|
let body = $('body'), |
||||||
|
cource = $('.course'); |
||||||
|
|
||||||
|
if (cource.length) { |
||||||
|
mixpanel.track( |
||||||
|
'Open course', |
||||||
|
{ 'course_id': COURSE_ID } |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
body.on('click', '[data-popup]', function (e) { |
||||||
|
let data = $(this).data('popup'); |
||||||
|
if (data === '.js-popup-buy') { |
||||||
|
mixpanel.track( |
||||||
|
'Open school buy popup' |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
||||||
|
body.on('click', '[data-course-buy]', function (e) { |
||||||
|
e.preventDefault(); |
||||||
|
let href = $(this).attr('href'); |
||||||
|
let t = mixpanel.track( |
||||||
|
'Click course buy button', |
||||||
|
{ 'course_id': COURSE_ID }, |
||||||
|
function () { |
||||||
|
window.location = href; |
||||||
|
} |
||||||
|
); |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
import $ from 'jquery'; |
||||||
|
import '../../sass/components/notification.scss'; |
||||||
|
|
||||||
|
export function showNotification(style, text) { |
||||||
|
let htmlNode = document.createElement('div'); |
||||||
|
let htmlElement = $(htmlNode).addClass('notification').addClass(`notification--${style}`).text(text).appendTo($('body')); |
||||||
|
|
||||||
|
setTimeout(() => { |
||||||
|
htmlElement.fadeOut(400, () => { |
||||||
|
htmlElement.remove(); |
||||||
|
}) |
||||||
|
}, 3500); |
||||||
|
} |
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue