# 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.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.Serializer): |
||||
class ConfigSerializer(serializers.ModelSerializer): |
||||
SERVICE_COMMISSION = serializers.IntegerField(required=False) |
||||
SERVICE_DISCOUNT_MIN_AMOUNT = serializers.IntegerField(required=False) |
||||
SERVICE_DISCOUNT = serializers.IntegerField(required=False) |
||||
INSTAGRAM_CLIENT_ACCESS_TOKEN = serializers.CharField(required=False) |
||||
INSTAGRAM_CLIENT_SECRET = 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): |
||||
ret = OrderedDict() |
||||
fields = self._readable_fields |
||||
for field in fields: |
||||
attribute = instance.get(field.field_name) |
||||
ret[field.field_name] = field.to_representation(attribute) |
||||
return ret |
||||
|
||||
def to_internal_value(self, data): |
||||
ret = OrderedDict(get_values()) |
||||
for k, v in data.items(): |
||||
ret[k] = v |
||||
return ret |
||||
|
||||
def update(self, instance, validated_data): |
||||
for k, v in validated_data.items(): |
||||
_set_constance_value(k, v) |
||||
class Meta: |
||||
model = Config |
||||
fields = ( |
||||
'SERVICE_COMMISSION', |
||||
'SERVICE_DISCOUNT_MIN_AMOUNT', |
||||
'SERVICE_DISCOUNT', |
||||
'INSTAGRAM_CLIENT_ACCESS_TOKEN', |
||||
'INSTAGRAM_CLIENT_SECRET', |
||||
'INSTAGRAM_PROFILE_URL', |
||||
'SCHOOL_LOGO_IMAGE', |
||||
'MAIN_PAGE_TOP_IMAGE', |
||||
) |
||||
|
||||
@ -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 }}. Перейдите по ссылке ниже: |
||||
{{ 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 %} |
||||
|
||||
{% recursetree object.comments.all %} |
||||
{% if not node.deactivated_at %} |
||||
{% include './comment.html' %} |
||||
{{ children }} {% endrecursetree %} |
||||
{% if not node.is_leaf_node %} |
||||
{{ children }} |
||||
{% endif %} |
||||
{% endif %} |
||||
{% endrecursetree %} |
||||
|
||||
@ -1,35 +1,32 @@ |
||||
{% load thumbnail %} |
||||
{% if results %} |
||||
<div class="title">Галерея итогов обучения</div> |
||||
<div class="examples"> |
||||
{% for image in course.gallery.gallery_images.all %} |
||||
<div class="examples__item"> |
||||
<img class="examples__pic" src="{{ image.img.image.url }}"> |
||||
<div class="title">Галерея итогов обучения</div> |
||||
<div class="examples gallery"> |
||||
{% for image in course.gallery.gallery_images.all %} |
||||
<div class="examples__item"> |
||||
<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> |
||||
{% endfor %} |
||||
</div> |
||||
{% else %} |
||||
<div class="section section_gradient"> |
||||
<div class="section__center center center_sm"> |
||||
<div class="title">{{ content.title }}</div> |
||||
<div class="examples"> |
||||
{% for image in content.gallery_images.all %} |
||||
<div class="section section_gradient"> |
||||
<div class="section__center center center_sm"> |
||||
<div class="title">{{ content.title }}</div> |
||||
<div class="examples"> |
||||
{% for image in content.gallery_images.all %} |
||||
<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 %} |
||||
{% endfor %} |
||||
</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>--> |
||||
{% endif %} |
||||
|
||||
@ -1,13 +1,19 @@ |
||||
{% extends "templates/lilcity/edit_index.html" %} |
||||
{% 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 %} |
||||
<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 }}" |
||||
access-token="{{ request.user.auth_token }}" |
||||
{% if course and course.id %}:course-id="{{ course.id }}"{% endif %}></course-redactor> |
||||
{% endblock content %} |
||||
{% block foot %} |
||||
<script type="text/javascript" src={% static "courseRedactor.js" %}></script> |
||||
<link rel="stylesheet" href={% static "courseRedactor.css" %}/> |
||||
{% endblock foot %} |
||||
<script type="text/javascript" src="{% static "courseRedactor.js" %}"></script> |
||||
<link rel="stylesheet" href="{% static "courseRedactor.css" %}" /> |
||||
{% 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 |
||||
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 |
||||
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-mptt==0.9.0 |
||||
django-silk==2.0.0 |
||||
django-phonenumber-field==2.0.0 |
||||
django-polymorphic-tree==1.5 |
||||
celery[redis]==4.1.0 |
||||
djangorestframework==3.7.7 |
||||
drf-yasg[validation]==1.4.0 |
||||
django-silk==2.0.0 |
||||
django-cors-headers==2.1.0 |
||||
django-constance[database]==2.1.0 |
||||
drf-yasg[validation]==1.5.0 |
||||
facepy==1.0.9 |
||||
gunicorn==19.7.1 |
||||
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 |
||||
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