# 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); |
||||||
|
} |
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue