From a9c98bd942ac21e9757b6ccaf4f7afc66877717c Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 26 Jan 2018 11:50:32 +0300 Subject: [PATCH 01/24] Add vscode config dir to .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 944152f8..5f11bf53 100644 --- a/.gitignore +++ b/.gitignore @@ -110,4 +110,5 @@ venv.bak/ # JavaScript .map node_modules -db.sqlite3 \ No newline at end of file +db.sqlite3 +.vscode \ No newline at end of file From 98fd07469e80b58d4d5fc8a06904fc717afe4dd7 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 26 Jan 2018 12:29:32 +0300 Subject: [PATCH 02/24] Add fixture for superuser --- apps/user/fixtures/superuser.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 apps/user/fixtures/superuser.json diff --git a/apps/user/fixtures/superuser.json b/apps/user/fixtures/superuser.json new file mode 100644 index 00000000..a565acf9 --- /dev/null +++ b/apps/user/fixtures/superuser.json @@ -0,0 +1,20 @@ +[ + { + "model": "auth.user", + "pk": 1, + "fields": { + "password": "pbkdf2_sha256$100000$NgQxbNX0XxNX$08G0OS9JUz0kStyfzGjhasmvj7ilUWkrm/wKMaN5H3o=", + "last_login": null, + "is_superuser": true, + "username": "admin", + "first_name": "", + "last_name": "", + "email": "admin@lil.com", + "is_staff": true, + "is_active": true, + "date_joined": "2018-01-26T09:08:15.035Z", + "groups": [], + "user_permissions": [] + } + } +] \ No newline at end of file From 8a47b077cb3806293f0cf3ef3ab0eac3eb8e465c Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 26 Jan 2018 12:30:08 +0300 Subject: [PATCH 03/24] Add autoload fixtures to docker-compose --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 69420d7b..3aef52bc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,7 @@ services: restart: always volumes: - .:/lilcity - command: bash -c "python manage.py migrate && python manage.py runserver 0.0.0.0:8000" + command: bash -c "python manage.py migrate && python manage.py loaddata /lilcity/apps/*/fixtures/*.json && python manage.py runserver 0.0.0.0:8000" environment: - DJANGO_SETTINGS_MODULE=project.settings ports: From 1bd5960fd4681b2e9f254c05da5d8cd4a324e4b5 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 26 Jan 2018 12:30:25 +0300 Subject: [PATCH 04/24] Fix migrations --- .../0002_lilcityusersettings_facebook_id.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 apps/user/migrations/0002_lilcityusersettings_facebook_id.py diff --git a/apps/user/migrations/0002_lilcityusersettings_facebook_id.py b/apps/user/migrations/0002_lilcityusersettings_facebook_id.py new file mode 100644 index 00000000..58145327 --- /dev/null +++ b/apps/user/migrations/0002_lilcityusersettings_facebook_id.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.1 on 2018-01-26 09:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('user', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='lilcityusersettings', + name='facebook_id', + field=models.PositiveIntegerField(blank=True, null=True, unique=True), + ), + ] From 61bb74445d6839385eab1181e42f3390e6a3ec0d Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 26 Jan 2018 13:42:51 +0300 Subject: [PATCH 05/24] =?UTF-8?q?LIL-112.=20=D0=98=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=B8=D1=82=D1=8C=20=D1=80=D0=B5=D0=B3=D0=B8=D1=81=D1=82?= =?UTF-8?q?=D1=80=D0=B0=D1=86=D0=B8=D1=8E=20=D0=BF=D0=BE=20=D1=81=D1=83?= =?UTF-8?q?=D1=89=D0=B5=D1=81=D1=82=D0=B2=D1=83=D1=8E=D1=89=D0=B5=D0=BC?= =?UTF-8?q?=D1=83=20e-mail?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/auth/views.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/apps/auth/views.py b/apps/auth/views.py index d1fcc322..7e30114e 100644 --- a/apps/auth/views.py +++ b/apps/auth/views.py @@ -30,7 +30,19 @@ class LearnerRegistrationView(FormView): email = form.cleaned_data['email'] password = form.cleaned_data['password'] - user = User.objects.create_user(username=email, email=email, first_name=first_name, last_name=last_name, password=password) + user, created = User.objects.get_or_create( + username=email, email=email, + first_name=first_name, last_name=last_name + ) + + if not created: + return JsonResponse({ + "success": False, + 'errors': {'__all__': [{'message': 'Возможно вы уже зарегистрированы?'}]} + }, status=400) + + user.set_password(password) + user.save() login(self.request, user) # fixme: change email text @@ -64,7 +76,8 @@ class LoginView(FormView): class VerificationEmailView(View): def get(self, request, *args, **kwargs): - is_valid_token = verification_email_token.check_token(request.user, kwargs.get('token')) + is_valid_token = verification_email_token.check_token( + request.user, kwargs.get('token')) if is_valid_token: lilcity_user_settings = request.user.lilcity_user_settings @@ -122,7 +135,8 @@ class FacebookLoginOrRegistration(View): facebook_id = data.get('id') - lilcity_user_settings = LilcityUserSettings.objects.filter(facebook_id=facebook_id) + lilcity_user_settings = LilcityUserSettings.objects.filter( + facebook_id=facebook_id) if lilcity_user_settings.count(): login(requests, user=lilcity_user_settings[0].user) return JsonResponse({"success": True}) @@ -136,7 +150,8 @@ class FacebookLoginOrRegistration(View): first_name = data.get('first_name', '') last_name = data.get('last_name', '') - user = LilcityUserProxy.objects.create_user(username=email, email=email, first_name=first_name, last_name=last_name, password=uuid4().hex) + user = LilcityUserProxy.objects.create_user( + username=email, email=email, first_name=first_name, last_name=last_name, password=uuid4().hex) user.lilcity_user_settings.is_verification_email = True user.lilcity_user_settings.facebook_id = facebook_id From 1075062f166828a08e0f8a9bac1a4fc426549610 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 26 Jan 2018 18:32:29 +0300 Subject: [PATCH 06/24] =?UTF-8?q?LIL-116.=20=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D0=B2=D1=8B=D0=BF=D0=B0?= =?UTF-8?q?=D0=B4=D0=B0=D1=8E=D1=89=D0=B8=D0=B9=20=D1=81=D0=BF=D0=B8=D1=81?= =?UTF-8?q?=D0=BE=D0=BA=20=D0=BA=D1=83=D1=80=D1=81=D0=BE=D0=B2=20=D0=B2=20?= =?UTF-8?q?=D0=B3=D0=BB=D0=B0=D0=B2=D0=BD=D0=BE=D0=BC=20=D1=88=D0=B0=D0=B1?= =?UTF-8?q?=D0=BB=D0=BE=D0=BD=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/course/inclusion/category_menu_items.html | 5 +++++ apps/course/templatetags/lilcity_category.py | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 apps/course/templates/course/inclusion/category_menu_items.html diff --git a/apps/course/templates/course/inclusion/category_menu_items.html b/apps/course/templates/course/inclusion/category_menu_items.html new file mode 100644 index 00000000..a6b5528a --- /dev/null +++ b/apps/course/templates/course/inclusion/category_menu_items.html @@ -0,0 +1,5 @@ +{% for category in category_items %} + +
{{ category.title }}
+
+{% endfor %} \ No newline at end of file diff --git a/apps/course/templatetags/lilcity_category.py b/apps/course/templatetags/lilcity_category.py index 1d8666c6..2b00a750 100644 --- a/apps/course/templatetags/lilcity_category.py +++ b/apps/course/templatetags/lilcity_category.py @@ -2,10 +2,14 @@ from django import template from ..models import Category - register = template.Library() @register.inclusion_tag('course/inclusion/category_items.html') def category_items(): - return {"category_items": Category.manager.all()} + return {"category_items": Category.objects.all()} + + +@register.inclusion_tag('course/inclusion/category_menu_items.html') +def category_menu_items(): + return {"category_items": Category.objects.all()} From f72d600498a816f8f0032518ea96eaaccf51253c Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 26 Jan 2018 18:37:06 +0300 Subject: [PATCH 07/24] =?UTF-8?q?LIL-117.=20=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D1=8C=20?= =?UTF-8?q?=D0=BA=D1=83=D1=80=D1=81=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0002_auto_20180126_1325.py | 73 +++++++++++++++++++ .../migrations/0003_auto_20180126_1347.py | 25 +++++++ apps/course/models.py | 43 ++++++++--- 3 files changed, 132 insertions(+), 9 deletions(-) create mode 100644 apps/course/migrations/0002_auto_20180126_1325.py create mode 100644 apps/course/migrations/0003_auto_20180126_1347.py diff --git a/apps/course/migrations/0002_auto_20180126_1325.py b/apps/course/migrations/0002_auto_20180126_1325.py new file mode 100644 index 00000000..14035e9e --- /dev/null +++ b/apps/course/migrations/0002_auto_20180126_1325.py @@ -0,0 +1,73 @@ +# Generated by Django 2.0.1 on 2018-01-26 13:25 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('course', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='category', + options={'verbose_name': 'Категория', 'verbose_name_plural': 'Категории'}, + ), + migrations.AlterModelManagers( + name='category', + managers=[ + ], + ), + migrations.RenameField( + model_name='course', + old_name='background', + new_name='cover', + ), + migrations.RenameField( + model_name='course', + old_name='deferred_start', + new_name='deferred_start_at', + ), + migrations.RenameField( + model_name='course', + old_name='is_highlighted', + new_name='is_featured', + ), + migrations.RemoveField( + model_name='course', + name='created_at', + ), + migrations.RemoveField( + model_name='course', + name='update_at', + ), + migrations.AddField( + model_name='course', + name='author', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='course', + name='duration', + field=models.IntegerField(default=0, verbose_name='Продолжительность курса'), + ), + migrations.AddField( + model_name='course', + name='is_infinite', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='course', + name='status', + field=models.PositiveSmallIntegerField(choices=[(0, 'Pending'), (1, 'Published'), (2, 'Archived')], default=0, verbose_name='Статус'), + ), + migrations.AddField( + model_name='course', + name='url', + field=models.URLField(default='', verbose_name='Ссылка'), + ), + ] diff --git a/apps/course/migrations/0003_auto_20180126_1347.py b/apps/course/migrations/0003_auto_20180126_1347.py new file mode 100644 index 00000000..58808dde --- /dev/null +++ b/apps/course/migrations/0003_auto_20180126_1347.py @@ -0,0 +1,25 @@ +# Generated by Django 2.0.1 on 2018-01-26 13:47 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0002_auto_20180126_1325'), + ] + + operations = [ + migrations.AddField( + model_name='course', + name='created_at', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='course', + name='update_at', + field=models.DateTimeField(auto_now=True), + ), + ] diff --git a/apps/course/models.py b/apps/course/models.py index 2c3b1102..bc2f48fc 100644 --- a/apps/course/models.py +++ b/apps/course/models.py @@ -1,32 +1,55 @@ +import arrow from django.db import models from django.utils import timezone +from django.contrib.auth import get_user_model from .manager import CategoryQuerySet +User = get_user_model() + class Course(models.Model): + STATUS_CHOICES = ( + (0, 'Pending'), + (1, 'Published'), + (2, 'Archived'), + ) + author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) title = models.CharField("Название курса", max_length=100) short_description = models.TextField("Краткое описание курса") - background = models.ImageField("Фон курса", upload_to='courses') + cover = models.ImageField("Фон курса", upload_to='courses') price = models.DecimalField("Цена курса", help_text="Если цены нету, то курс бесплатный", max_digits=10, decimal_places=2, null=True, blank=True) - is_highlighted = models.BooleanField(default=False) - deferred_start = models.DateTimeField("Отложенный запуск курса", help_text="Заполнить если курс отложенный", null=True, blank=True) - + is_infinite = models.BooleanField(default=False) + deferred_start_at = models.DateTimeField("Отложенный запуск курса", help_text="Заполнить если курс отложенный", null=True, blank=True) category = models.ForeignKey("Category", on_delete=models.PROTECT) + duration = models.IntegerField("Продолжительность курса", default=0) + is_featured = models.BooleanField(default=False) + url = models.URLField('Ссылка', default='') + status = models.PositiveSmallIntegerField('Статус', default=0, choices=STATUS_CHOICES) - created_at = models.DateTimeField(auto_created=True) + created_at = models.DateTimeField(auto_now_add=True) update_at = models.DateTimeField(auto_now=True) + @property def is_free(self): if self.price: return False return True + @property + def deferred_start_at_humanize(self): + return arrow.get(self.deferred_start_at).humanize(locale='ru') + + @property + def created_at_humanize(self): + return arrow.get(self.created_at).humanize(locale='ru') + + @property def is_deferred_start(self): - if not self.deferred_start: + if not self.deferred_start_at: return False - if timezone.now() < self.deferred_start: + if timezone.now() < self.deferred_start_at: return True return False @@ -38,7 +61,9 @@ class Course(models.Model): class Category(models.Model): title = models.CharField("Название категории", max_length=100) - manager = CategoryQuerySet.as_manager() - def __str__(self): return self.title + + class Meta: + verbose_name = "Категория" + verbose_name_plural = "Категории" From abf65db7140cd76b8472089390529d798cc30c18 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 26 Jan 2018 18:37:56 +0300 Subject: [PATCH 08/24] Fix Course admin --- apps/course/admin.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/course/admin.py b/apps/course/admin.py index 1fc02ef1..9b5cd1ae 100644 --- a/apps/course/admin.py +++ b/apps/course/admin.py @@ -5,7 +5,16 @@ from .models import Course, Category @admin.register(Course) class CourseAdmin(admin.ModelAdmin): - pass + list_display = ( + 'author', + 'title', + 'price', + 'is_infinite', + 'category', + 'status', + 'created_at', + 'update_at', + ) @admin.register(Category) From 6e572c80952d8ddd998da87011ffb80de8481a80 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 26 Jan 2018 18:38:21 +0300 Subject: [PATCH 09/24] Add arrow library --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 6b16ecce..c179d2a4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ psycopg2==2.7.3.2 facepy==1.0.9 Pillow==5.0.0 django-active-link==0.1.2 +arrow==0.12.1 From 7d9e73e5b23e3f15cc968b47d3327765f3e8f5cd Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 26 Jan 2018 18:41:14 +0300 Subject: [PATCH 10/24] =?UTF-8?q?LIL-118.=20=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D1=88=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD=20?= =?UTF-8?q?=D0=BA=D1=83=D1=80=D1=81=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/course/templates/course/_items.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/course/templates/course/_items.html b/apps/course/templates/course/_items.html index 331cd73e..0d876ccb 100644 --- a/apps/course/templates/course/_items.html +++ b/apps/course/templates/course/_items.html @@ -1,19 +1,19 @@ {% load static %} -
+
Подробнее
- {% if course.is_highlighted %} + {% if course.is_featured %}
{% endif %} {% if course.is_deferred_start %}
До запуска курса осталось:
-
16 часов 13 минут
+
{{ course.deferred_start_at_humanize }}
{% endif %}
-
АНИМАЦИЯ +
{{ course.category | upper }} {% if not course.is_free %}
{{ course.price|floatformat:"-2" }}$
{% endif %}
{{ course.title }} @@ -22,9 +22,9 @@
-
Александра Неимоверноумная
+
{{ course.author.first_name }} {{ course.author.last_name }}
-
SEPT 12, 2017
+
{{ course.created_at_humanize }}
From 94956bbf21b16caca2e5ef9ba988535d48f17d3d Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 26 Jan 2018 19:17:52 +0300 Subject: [PATCH 12/24] Add django-filter to req --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index c179d2a4..17ee0f7c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,4 @@ facepy==1.0.9 Pillow==5.0.0 django-active-link==0.1.2 arrow==0.12.1 +django-filter==2.0.0.dev1 From 9a380454a461bf299a07b860568883d3c6280c55 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 26 Jan 2018 20:17:56 +0300 Subject: [PATCH 13/24] =?UTF-8?q?LIL-116.=20=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D0=B2=D1=8B=D0=BF=D0=B0?= =?UTF-8?q?=D0=B4=D0=B0=D1=8E=D1=89=D0=B8=D0=B9=20=D1=81=D0=BF=D0=B8=D1=81?= =?UTF-8?q?=D0=BE=D0=BA=20=D0=BA=D0=B0=D1=82=D0=B5=D0=B3=D0=BE=D1=80=D0=B8?= =?UTF-8?q?=D0=B9=20=D0=BA=D1=83=D1=80=D1=81=D0=BE=D0=B2=20=D0=B2=20=D0=B3?= =?UTF-8?q?=D0=BB=D0=B0=D0=B2=D0=BD=D0=BE=D0=BC=20=D1=88=D0=B0=D0=B1=D0=BB?= =?UTF-8?q?=D0=BE=D0=BD=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/course/filters.py | 11 +++++++++++ .../course/inclusion/category_menu_items.html | 2 +- apps/course/views.py | 6 ++++++ docker-compose.yml | 18 +++++++++++++----- project/settings.py | 17 ++++++++++++++--- 5 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 apps/course/filters.py diff --git a/apps/course/filters.py b/apps/course/filters.py new file mode 100644 index 00000000..a86677be --- /dev/null +++ b/apps/course/filters.py @@ -0,0 +1,11 @@ +import django_filters + +from .models import Course + + +class CourseFilter(django_filters.FilterSet): + category__title = django_filters.CharFilter(lookup_expr='iexact') + + class Meta: + model = Course + fields = ['category'] diff --git a/apps/course/templates/course/inclusion/category_menu_items.html b/apps/course/templates/course/inclusion/category_menu_items.html index a6b5528a..80a82615 100644 --- a/apps/course/templates/course/inclusion/category_menu_items.html +++ b/apps/course/templates/course/inclusion/category_menu_items.html @@ -1,5 +1,5 @@ {% for category in category_items %} - +
{{ category.title }}
{% endfor %} \ No newline at end of file diff --git a/apps/course/views.py b/apps/course/views.py index 03beac76..bc3761f1 100644 --- a/apps/course/views.py +++ b/apps/course/views.py @@ -1,6 +1,7 @@ from django.views.generic import ListView from .models import Course +from .filters import CourseFilter class CoursesView(ListView): @@ -8,6 +9,11 @@ class CoursesView(ListView): context_object_name = "course_items" paginate_by = 6 + def get_queryset(self): + queryset = super().get_queryset() + filtered = CourseFilter(self.request.GET, queryset=queryset) + return filtered.qs + def get_template_names(self): if self.request.is_ajax(): return 'course/course_items.html' diff --git a/docker-compose.yml b/docker-compose.yml index 69420d7b..57469a32 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,10 +1,15 @@ version: '3' + services: - postgres: - image: postgres:9.6 + db: + image: postgres:9.6.3 environment: - - POSTGRES_PASSWORD=1234 - - POSTGRES_USER=postgres + - POSTGRES_DB=lilcity + - POSTGRES_USER=lilcity + - POSTGRES_PASSWORD=GPVs/E/{5&qe + - PGDATA=/var/lib/postgresql/data/pgdata + ports: + - "5432:5432" web: build: . @@ -14,7 +19,10 @@ services: command: bash -c "python manage.py migrate && python manage.py runserver 0.0.0.0:8000" environment: - DJANGO_SETTINGS_MODULE=project.settings + - DATABASE_SERVICE_HOST=db ports: - "8000:8000" depends_on: - - postgres \ No newline at end of file + - db + links: + - db \ No newline at end of file diff --git a/project/settings.py b/project/settings.py index 10946d9e..e2374617 100644 --- a/project/settings.py +++ b/project/settings.py @@ -40,6 +40,7 @@ INSTALLED_APPS = [ ] + [ 'anymail', 'active_link', + 'django_filters', ] + [ 'apps.auth.apps', 'apps.user', @@ -84,14 +85,24 @@ WSGI_APPLICATION = 'project.wsgi.application' # Database # https://docs.djangoproject.com/en/2.0/ref/settings/#databases +# DATABASES = { +# 'default': { +# 'ENGINE': 'django.db.backends.sqlite3', +# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), +# } +# } + DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': 'lilcity', + 'USER': 'lilcity', + 'PASSWORD': 'GPVs/E/{5&qe', + 'HOST': os.getenv('DATABASE_SERVICE_HOST', '127.0.0.1'), + 'PORT': 5432, } } - # Password validation # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators From edf7c7ade1efcfeb6466f59b540abd98d0231f2b Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Sat, 27 Jan 2018 10:06:18 +0300 Subject: [PATCH 14/24] Move from postgres:9.6.3 to postgres:10-alpine --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 57469a32..4402106d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: '3' services: db: - image: postgres:9.6.3 + image: postgres:10-alpine environment: - POSTGRES_DB=lilcity - POSTGRES_USER=lilcity From 0a9dbf4dd7d996d78442f4cd3e7d0cba200c2332 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Sat, 27 Jan 2018 11:35:04 +0300 Subject: [PATCH 15/24] Fix db locale --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index 4402106d..a2803d7d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,6 +4,7 @@ services: db: image: postgres:10-alpine environment: + - LANG=ru_RU.UTF-8 - POSTGRES_DB=lilcity - POSTGRES_USER=lilcity - POSTGRES_PASSWORD=GPVs/E/{5&qe From a9286281c7855f78c507a1f3ac1814d9de669749 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Sat, 27 Jan 2018 11:35:47 +0300 Subject: [PATCH 16/24] Fix Couce filterset --- apps/course/filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/course/filters.py b/apps/course/filters.py index a86677be..827079e6 100644 --- a/apps/course/filters.py +++ b/apps/course/filters.py @@ -4,7 +4,7 @@ from .models import Course class CourseFilter(django_filters.FilterSet): - category__title = django_filters.CharFilter(lookup_expr='iexact') + category = django_filters.CharFilter(field_name='category__title', lookup_expr='iexact') class Meta: model = Course From 071c9d0202568593d10770e259d8e6eb29acebf6 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Sat, 27 Jan 2018 11:36:32 +0300 Subject: [PATCH 17/24] Add courses fixture --- apps/course/fixtures/course.json | 285 +++++++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 apps/course/fixtures/course.json diff --git a/apps/course/fixtures/course.json b/apps/course/fixtures/course.json new file mode 100644 index 00000000..7daa5139 --- /dev/null +++ b/apps/course/fixtures/course.json @@ -0,0 +1,285 @@ +[ + { + "model": "course.course", + "pk": 1, + "fields": { + "author": 1, + "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", + "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", + "cover": "courses/pic-1_sTaZawQ.jpg", + "price": "50.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 2, + "duration": 1, + "is_featured": false, + "url": "https://gitlab.com/", + "status": 0, + "created_at": "2018-01-27T07:04:41.113Z", + "update_at": "2018-01-27T07:12:04.168Z" + } + }, + { + "model": "course.course", + "pk": 2, + "fields": { + "author": 1, + "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", + "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", + "cover": "courses/pic-1_sTaZawQ.jpg", + "price": "50.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 1, + "duration": 1, + "is_featured": false, + "url": "https://gitlab.com/", + "status": 0, + "created_at": "2018-01-27T07:09:03.437Z", + "update_at": "2018-01-27T07:11:55.373Z" + } + }, + { + "model": "course.course", + "pk": 3, + "fields": { + "author": 1, + "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", + "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", + "cover": "courses/pic-1_sTaZawQ.jpg", + "price": "50.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 9, + "duration": 1, + "is_featured": false, + "url": "https://gitlab.com/", + "status": 0, + "created_at": "2018-01-27T07:09:03.442Z", + "update_at": "2018-01-27T07:11:43.838Z" + } + }, + { + "model": "course.course", + "pk": 4, + "fields": { + "author": 1, + "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", + "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", + "cover": "courses/pic-1_sTaZawQ.jpg", + "price": "50.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 8, + "duration": 1, + "is_featured": false, + "url": "https://gitlab.com/", + "status": 0, + "created_at": "2018-01-27T07:09:03.445Z", + "update_at": "2018-01-27T07:11:35.342Z" + } + }, + { + "model": "course.course", + "pk": 5, + "fields": { + "author": 1, + "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", + "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", + "cover": "courses/pic-1_sTaZawQ.jpg", + "price": "50.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 7, + "duration": 1, + "is_featured": false, + "url": "https://gitlab.com/", + "status": 0, + "created_at": "2018-01-27T07:09:03.449Z", + "update_at": "2018-01-27T07:11:26.725Z" + } + }, + { + "model": "course.course", + "pk": 6, + "fields": { + "author": 1, + "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", + "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", + "cover": "courses/pic-1_sTaZawQ.jpg", + "price": "50.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 6, + "duration": 1, + "is_featured": false, + "url": "https://gitlab.com/", + "status": 0, + "created_at": "2018-01-27T07:09:03.452Z", + "update_at": "2018-01-27T07:11:15.061Z" + } + }, + { + "model": "course.course", + "pk": 7, + "fields": { + "author": 1, + "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", + "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", + "cover": "courses/pic-1_sTaZawQ.jpg", + "price": "50.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 5, + "duration": 1, + "is_featured": false, + "url": "https://gitlab.com/", + "status": 0, + "created_at": "2018-01-27T07:09:03.455Z", + "update_at": "2018-01-27T07:11:03.583Z" + } + }, + { + "model": "course.course", + "pk": 8, + "fields": { + "author": 1, + "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", + "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", + "cover": "courses/pic-1_sTaZawQ.jpg", + "price": "50.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 4, + "duration": 1, + "is_featured": false, + "url": "https://gitlab.com/", + "status": 0, + "created_at": "2018-01-27T07:09:03.458Z", + "update_at": "2018-01-27T07:10:52.322Z" + } + }, + { + "model": "course.course", + "pk": 9, + "fields": { + "author": 1, + "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", + "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", + "cover": "courses/pic-1_sTaZawQ.jpg", + "price": "50.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 3, + "duration": 1, + "is_featured": false, + "url": "https://gitlab.com/", + "status": 0, + "created_at": "2018-01-27T07:09:03.461Z", + "update_at": "2018-01-27T07:10:42.721Z" + } + }, + { + "model": "course.course", + "pk": 10, + "fields": { + "author": 1, + "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", + "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", + "cover": "courses/pic-1_sTaZawQ.jpg", + "price": "50.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 2, + "duration": 1, + "is_featured": false, + "url": "https://gitlab.com/", + "status": 0, + "created_at": "2018-01-27T07:09:03.464Z", + "update_at": "2018-01-27T07:10:33.374Z" + } + }, + { + "model": "course.course", + "pk": 11, + "fields": { + "author": 1, + "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", + "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", + "cover": "courses/pic-1_sTaZawQ.jpg", + "price": "50.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 1, + "duration": 1, + "is_featured": false, + "url": "https://gitlab.com/", + "status": 0, + "created_at": "2018-01-27T07:09:03.467Z", + "update_at": "2018-01-27T07:09:03.467Z" + } + }, + { + "model": "course.category", + "pk": 1, + "fields": { + "title": "\u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436" + } + }, + { + "model": "course.category", + "pk": 2, + "fields": { + "title": "\u0430\u043a\u0432\u0430\u0440\u0435\u043b\u044c" + } + }, + { + "model": "course.category", + "pk": 3, + "fields": { + "title": "\u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u044f" + } + }, + { + "model": "course.category", + "pk": 4, + "fields": { + "title": "\u0430\u043d\u0438\u043c\u0430\u0446\u0438\u044f" + } + }, + { + "model": "course.category", + "pk": 5, + "fields": { + "title": "\u043f\u0430\u0441\u0442\u0435\u043b\u044c" + } + }, + { + "model": "course.category", + "pk": 6, + "fields": { + "title": "\u043f\u043b\u0430\u0441\u0442\u0438\u043b\u0438\u043d" + } + }, + { + "model": "course.category", + "pk": 7, + "fields": { + "title": "\u043a\u0440\u0435\u0430\u0442\u0438\u0432\u043d\u043e\u0435 \u043c\u044b\u0448\u043b\u0435\u043d\u0438\u0435" + } + }, + { + "model": "course.category", + "pk": 8, + "fields": { + "title": "\u043c\u043e\u0442\u043e\u0440\u0438\u043a\u0430" + } + }, + { + "model": "course.category", + "pk": 9, + "fields": { + "title": "\u0436\u0438\u0432\u043e\u043f\u0438\u0441\u044c" + } + } +] \ No newline at end of file From 6173b8803c449411b4671bc4bc893cb9920b6f0a Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Sat, 27 Jan 2018 11:37:37 +0300 Subject: [PATCH 18/24] Add course picture only on dev time --- media/courses/pic-1_sTaZawQ.jpg | Bin 0 -> 16859 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 media/courses/pic-1_sTaZawQ.jpg diff --git a/media/courses/pic-1_sTaZawQ.jpg b/media/courses/pic-1_sTaZawQ.jpg new file mode 100644 index 0000000000000000000000000000000000000000..576307d4712570f0df9bf1eb43e2b11ad0358d3b GIT binary patch literal 16859 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�i{IpX`iyma{L;RRu*F29meV{v|N_b?~6zW_2aHBLYBo6zd_addC-(A zmFL?5Zj|REKicl-wlxA|?Me4i!Q=TP;UElv(jgof=cs2^y}O;UA6yClcUfNjYR>Jc zeAa2;oTEU_Ga+6v9de5~#Q`#n_o`WT{*`-#63n}}b;tjd)qbKsLE9Xn#*?a*g>f|B z683uOnPva=G7)^n9+q`od4T#7+O5Z(}s7Qc%YH9$)`JFll zH%8BRh2OIHS0t4DCRZaEoqZyB!`@*24fmCk@JU7u9lpbSSYlXGn3lfyV3ma4LZTgXh_7Xd%O!FUi_eu(h zmxG-H{>|%0Gi|bJp-3=*S?8zc{+Yu=OGMPfg0o{P1`itHFzWu;tel4^P`3FE{nZvF zukAbP7}E2xw?#J2^aETkzLSAWP%#+5r|6JBEKiYvMQcGKNR6^JKZwc9q`GgZ0=`R& zd5964zo33}@b$X~fc0B+FM$jlE_R)e0vG^+2GZIkzjdL~#WhQnYJ@}2{u?FZm?K>x z&brONs|gW=86>7pXJlWU^ze4+x+$d*xnSGxgLLBr435Um%2eJKX|JA$z{Z!dR>lv-?&mhpMF!5Uz@3WkF{0dlW zyORI0jZH*vJB{A z`R1iowNz5fbnQ(kGu;!G4h&#oy3rv6ZmXAKo}4u9H`t&vQ)EB6!JhQ+ea&HvUdWFs z!8JDan810lh$5SP>u`@Zw2{jJf}->xIzd^u0Cg0!>Y=-#Pj zhjIJb?}ex!*XhBpLEbi-2UfrS_A-tT`kUR@qKO>tFD-7WjDN6x8l@1EG93EgK?#e76RT+ zLm4T#hr9;p{rZC+uQ=0vu%9ySl8$?LqPIc@&zQ2a$G0z2bJvoddr-nkH%yZS&+dV$ zDeLNS9E8cyaCgxk2N-xr?0T38eOHp2cjU`80bpj(!!|mal3s=dOz;lL7`w13ns?lcyr6I2NuW-B$8>A+Pq>y`B!jkg%ojiN)+qptK`t?Zx1XR3&H!mZS zW?Y81n~~O+=^arrN`D@xHt8U`b6uIq4m^U5z{6r(S^jc?mh4oULGjsf_J$6H* zJl6(8Pg`@zHSK1WHCr(NQ>Rtq%N{sUlYTd9-#n!6kq%$qT`dz+;GAgzS0?1qZ`ex= zU_}MJcteK)wEEprK4S=Qay4qa^XG6?aeEHw5=Z-&$2XVRV*s+3KC@IsO17-Doa1E5 zzg4B@9EkP8#-FkNq$W>S)L=Yjgj6Z{K6uD1^X28rot{He5m&X_N27O#cXC@8K&~2E z1GZkeR?i(J%Z8p|z*?z8{>(cP1(o_{YpoQc{)iuqvRC%pP&>SXdm{A8Rp-;z`y^>6 zY3?#JN8hU}u8o&2g?_f$u000Cy9zJY+ca16;Q1mbqZJD?i=k^@PvPkyb*Z)|_BO*D zIjY=c(hKK1u_$?i6iNS&RB9IIzAJncAtkp`7r3W`B(eS0ei`k_+59U5WMUp}scODU zx5Aas&LUqQ%aHmzlDm$V=tsI*DS6F}33ic_DYhTkQo`t1KM6jlyvz5zs^d&5*Bt5* zwMrq6PfqxY7ZY{r_GJ=1d1b-cT#KyYj9vY~Zo}Ls^`_I3-4&PMMrGXh8G8c+v|fAS ztW@N!Bj7$iwxorUbavCde>INGwnQ|4=rl4(VK+I*dA$Akh1;goD$km-eN#vL!P>e5 zispL#BMi?)1Qsu*Ym96hi>>NUiLFoSnC}w(s~q#RBEF+#f&9C8B_9a+VxlV(N~K(4 z;iqL%Kf^qyaUe{(Papbnl24$E300}O7lmTD&xANY7|^po%1xy9I$yMu`D;_h>d$Gl zl5Z#hBxm1_a{O?dFW1&{Q54+95q*>Ux%J$UE?m==?qgOzZ;DN0fUV~v2{-Qp4-Gzi z>}}LCy5+^Im@-TnU_x#uwV<#d)l+bH4sE|Rr13yg#LmB5O{ZNCYNsin zu3nrb<>>7L@(FqbpLmGUI`zp$&~~Lgy;GU9qJ6ReprK^ra3ICO&N#WdbGg#{zv@Iv z`+R)8vQJL2dLu1ix4!mZi7UHk&z-ZOs$PMjpW22vNT0?d=oskc@>0WdXtIuVtBgGkS#8*BEoFSP z;r7GK991|3^)0X=gjHIZd=qjrmbU33CuxB53*q6Uwwg1s zDB+t<29%!TCZUvue*0Y4x*8c%hzx|`woap6{f-Vwy%TVLS*-;JhLguhKUv5KzyOMu zOIt#(Ulwpe*5#~QfZ-ur=3a_=+}}H-!dF|C>ZluLuZ$lM#Wg4??K{nev;LAOf7y6O2^s=Q0M3MX#e-&eR45Pdg{j=b zyoUpa?!o1Zd!L_F?@SlzhG*;qX}cqq`~~j!9*|thJj3g5*UWLPy3gmiu~Bf}nWAD- z-c%LYht=&1Ai-8b?)HXhXtadefrSR*lhFk^xl^0!3hx6w44}6BF(fu{tvi#->h$_; z9o~<1eUNrP!HamaRFTdZTA;rK(O-S_!mrngDO~g{rd0b6XdG0N3p(0-j`SN8??hsI zperBQ5!}G}PpYA>{`5YC6VsmImcfF~*Wxh%bCV92g#07Dt`XO9`KvtQx@V$?opaP5 zUXhUQG*y|TGd;?SEzH^}g*R2{;eBBa$$mzEws5qtVeT*hsU`1w1nj-$ck~fXch#PX zI###lN)cgv+N&ez`&emG?kRv2~C#J$ZaN*o7-q`lO7)Y0z>Jicg?L0XK z&4CuD`Yt-l(rs&#kn&WAfcIZ|a48 z?UY4!Y#zAbqJ*ZaMXF4WJ`K8k$oz(XzXS4q?Cs!5O;!w}D6z;kq3D^(>nOTN^xO_H zXw%wCxW6=f-e={-klx7sDzW#O)J$S=KBMW{&OcoPLzr;J?5*Jy12TRS6q?KM0Rwpd zAc(&l8LvDEZ)s|0`0#u~<2P9pi+SZY|CO)oVc=gF02$UwyCd&5Ez;Ii&~*F68QHTw ziw$je8Y4t5MSZyVD@|pL5%k+Id6?D7Vk-Y4|2lI-uX<$Sj!~-*Z0`6ex6P_1wIJIG zlS&0c&W5@#w2Wi=W1Dktsp)c08ote#(X1@Fb~EYvz4I+39LXrsWw|#~6m{hrG#$@j zSJ5!Hd{_xdloh*Y6<7XN*RKD;K>IT8&E9my(GJPLOS9T7wTacF#LmWAy9oP7@qJi+ z!M-;{W`EPca(IXx%Iqw9`4-X<6UcD?8O<8_U0v32^qncq`JsC0GO~UI=>F-VQX8j> z@omf)29Ou?5#_w*4%{KCS&up=F@4XDnERcG$aE9txn$PbYA#EtqMGv6awC=|3n`b@Uh=hR;V`Pq?dN`XHqLI zQJe%d9gQ%~RTVDbxVvoUX6Kt<21p1ZQ2X>YD0SiG%cJv|(M*o8L!di~?;!q*Dq+!v zx@meQ=g)U?@b41P7s$JHe`xi`cNLTk(P2BKn>Cd8S|uI|E5`>c*O-o3^g+ zz~s0&gSR#!T5pAlbE$Pjjh@ld*vp6NeJp+RT_~uKI1d8|B&;0ql+}*olUb?TR|M!J z$F`gpPc%1~=`Z@z$d8+Li2VX$=FsEtUZ%?mVluD2cgll9Qu@gqj}4dDT8#S6$Y=OlUEXtU0%oR59=0JuPIi@rhLp9IaOta z&sRHtVN`Qq$4MvJKCsYQ$KEE_2++?4$U}qFZAw+vX(E1~72o>ne1@QWIuuOX!alyP z#BEMxk*$5ft8XJRM&!N4JIk$UmhyDw@WSqq4tn(MjQyOq1iYc4A$B{%@dt;;AVaFJ zk2P+2DMz9^p(->Ygap#90kZ@z5e?L0NjP`=Eal`s}2p0QJ13I|DGC0wA*#m z@=l6>8xUc<9)akJb8_59EI5T|A7geq& zMfJ*ncvmUj_SfCV6ANFVgFwO`#J&Hi$UI;V&lqM5|BP4Sh1P)lpgm+quwShreGv-s z$cc4w+I?o=i=$v@L-c~{6&E;=U;l)G>> zH!)IdxOFxTd8% z9l(?5870b?iZ$_E6*L5&0__{j>F@K<1Hgvj$UDm*PB-A95i`5?%yCF>nK9Kihc6CI z?;F&U$hj~bIZMwH@!{L0*UVy6V-RB4gAhrS36i9G#V>w?tI6Z1#maI=eXX1)gV?1K z0SUjpbz#{*cYo;K3Ho8|oxL-xfwq@fp%+YXObMYynir@?ekvFFS6FauNI;F#{z_oD z9-uiB)<$!0ym1nFQTOsK8P|S8POFIlD!bLRWC#ObLni^>`~{N)+k6vDEehK;+^GuTJ(XG~-1};2JucgmkqtSxk_IGxEU~7Ww5v6!dYP1VPOh02NB$^lYJzKv|zb<1pCUro_yGx0jxTveYcM1XSehvn12e z9_Z^XLxcfTx8wC<0QI!pe?D&T9!#!?5*qxK9-F!U!hSBY1);(KT;0f#nj>0Bg?i%# zA`6=1Y|D7rHp9T&l2HqLeo(g+JfJ}b1;Gz!wqPFIw9i*da+_Yuj&D+|C;o%S5wIWw zS07b>feUF;`(D8|_@i4{%L_aPKEiU<1T6QZ(i+SIX?X|E8$Xzm6{oE!)FMmTjzsxcMInTM?3pLj=Dq*n`8tHsdL(|jx=2}4B!hw!YN|j z#UK3Uq{(X*^$@{tdfFH&94y&q_O@0+`TO^LqphHdVO0M|U3CO)e{H1q9;*2v(J+9+ zhAEDSn`LXkrr6IxZTi*xUK;a+$-Iwr*bE&MoSA=a|KV}8y7#xnqn|(4ZA3V#r7kLO zz%R}+BJb5;{t4oB*vXSnpEZJod#rg0MyMASSM2vGzO&31443JaE3x-_$TO#sKmo zb$DJ@S+>SGf3x=G&0L}SVviHbm-U?w34md{zlU@yyzmakPAwkFS%8wRu{zf0FVy0V zoySQQHm-OBS-!g}Y`@2jY_0VTR`2uemuS_-kr0EPuXNvS>`YO^&)S_GTx0!_YhqWsX&fZVQ;zO^Lp_p?<9Dj!(KQp_a@;=%@#B zoN{2!`=C`*D{xVEX?5ZR1U(~#4#XspFcF$-(J*LR?0@q5qy_e_e!pybSBbi~?|`jm zLhvvEyOXep&8FTXuu;zG{*y*^m*D4=Z{Bo|HtJ9Zg=Tq0p$syp7()HvAd~wx?BC2m zuLAKqWr8HJk$cbDr)!t0!K<3GRSW!7g3v+6K_&vOSo-w#p1$xe%W);~eHEGUVxf5+ z%hGoX=Mj+}qXp^B|FSyAI8Tp4VAG%(@}HNQytyPjuqTo^tGbyMQzlN592q{&dP}XH zO-lno5z;o$4)z*ir2w6rcJI7(L=@@}9p70bN`Q9tgO-47U`tXu_o=e7@7Jg|*SIns z*3DU3)czTlOT%m9!jD8x$6Q&5iIeJo6KaJQf-V#o93n*Sp$`#cZk-UjpR?f zzxSt#Jrd$lZm@O}-}gRmc~h&45f(0a`0o;g_m&d3?@6%iR%!&EM4u)LNUn__q+Ja@ z%G#uf9E13uZhLD9bWIQF-l-o48xF4k&+9hb@C-L9vC(nDrpU*_X~-Beoc}K11t@*T z;@NkTMd@t7uuL;_1iDm>KZ^m_PB85!o>f5be_#OiV)D0C;G$g02D-zKs*4kEY{1!A zEAN_hhVHR==>U`&``i?AWBfUH$k1NWlzqo@Ddci-6ZVMX{l_g*WnS`~cR{M3HEf;% zvPi?1L2BTyiT5SF0U<@Mo^_^gZf^5LoCoE%YHh9J1UPFwfFZ&@H zKq3aAb#=iKr2jqN#l#~xdYH!_9kFG>KXxCG72t_>L*wj|uIyK4*2;@We0N~UE-#G3 zUDzYqDv~hvwe&Jh^r@Jp^a|IrHTx*@47Y-23x0ZC?pl~A6x9lszS9@GiI*wGO%4x3 z&MA%LCfl+&&Gy9?X}h*(IO1Ry&=dN~{|u7&^fuv9zp9dA-aXw1NX@0N8Mch+ns?#y z4ooV&CsYy0&5X)r&C;VdTikWB6p$NE30v3emKzi?SKZRv2s5LQqk7Ucyay)6UXQOY zN14U0^bobHuW;cSfqE1EFX^r2Pg1SG9#U51%mg9Nu${D|-MlxM-MTRV+{z!{PIiJV zF7W21y#ATyUpdk*vy}4nZa)9${zo#UXbRFLgMKo~8S|UR3OF+{R&Ll@J!tH>i> zWW15kXBOH6hae>(b;u;uS_z_~g!qy6x(%W)U*k9aSbKc>Citg%MicfqNMq{7ru2Oo z1TpD+bN(d{-J+&dnYInlK*TEQ5*aJl(cTE1H=)Ts)FWp$$^YDSEfMv0JjDQXPY-k0 z6A#Q22k5yd7z3@JqY!k~HK~a@^qucU+QXyJk1uj@2P>V1-AZ~6Jdd`@-xQ^9egC4j zJj~HV$Fvh;ct_@^w9z>r%0xuR%2F`hzSg%^3p#9o=d!$qQ3n~Gp7&m_vY%cYd6n#w zTLE$&n#SIx5MJTisUI(O32uCKzD=Z3_qt)w-h~LSJ;ngmBvJyk@?5gQ%#vcfa{ z*_Cojg3IP_R^O4XsnI>z8boHN2~wEKB&hp~Q%&b!XWDQG_!;cyKk4wi1N!1?l(mL{Ue zG{so8Oi6PYoN{(QrF@zOhlODPbg~rP4&DfbUX%C;_$*1ZO{v@@eNabRV+0_}l^)*V zzx$ny`5|x`76@8z0#R+4o)Dn88qI$HGIP>xA(k^{Qe=o*-xdYMgTY3!XtNh$p>Bv~M@Nz*924-DVrwp9bB$5uC z4r=+^z*u8(d8T|Y?emb(pD5l#udwG*#BnWDbxbE2o`+k_n4lw#-n$Yzq(DUIAZ6~f z{9F&6W*kj4>wF1w%j-dB(@VyBhpyzK?Jwc8Fa4p1_Y0#VpJW7{UQkA)GavX1q0wCOEWB**V;tYN=b;FOBYQlUIT91EJQu|{eQ8}* zBa3f2sm0*v;wuJI6X7HNI5jhWSo?&%+tbl;TREshj0<3B?jH zpRD}$Z?C&3kXqW-Cj~2G)}M2N>U#$y4rzMgt-y{Os{*eZ-g1utv;2xeuabFe7PNdX zl@GJp0vLWGf&4{keNRD-3cK+Xk@v$*(#@TF@@X)>v;ZrxJob5gj-)0*)| zdfPb>O;&PL<7!<;N8nIc16;i{WLdR2=}Loa*yd{GMK9G&#{FsY2h$;eup z2$5J<0#tvo41iZAEp#KXH0#sB{rG8V9t1FUzBwR8_SBZbn? zWZu|C%97vIP#Bk3h*(1Yt^v8*Pa%<&D1j0Squ#58Qj@(iasdXpHrvSU1nQU=8m5>n zhm89iq%+pXE<5GS4b4k~`=oo7`Nf}zU2!%?Q6>ISrlkxmk^>3~eVtGo1oiu(sNj01 zUO|$xAFD+FDps5Rb?rK-_9=f?rc~>cl2w%?1og~U67IeM|kClG&`&44?y;b3? zL00YDhX(bcYvtRib*-uBFMbhWujsO-vmA7MGqXWHV5)0Qj_R8bdLO4z=Um?q>_jv) zL>z@9be1;>v-5}Zi`(%oBex;+#Rdyamu-s+Q{9VrPG0p? z-lN5U#^sw@D;|>W^=W!#*-PB)zc^}!(%bFh=2{jNPyOXmbY;A5b-6y|I@w0$&rN)o zrBpvBo%5{z`f>Xv(^jx8j_ULRdJMVpLY5SuY>+SV{C4DjrM<0du8$MXOVL-McovgF ztg@JL6W|errmkilc@;xB_yw#JV0c|~8qxdMkAH*q3yLe@L8jL9^E_K7t7&|LiOzNB zTiUMovscLIAko-+*Q49q?X@H73Wa@tTU)4ZMp5=vZ2nk@X{UKW&;%8-q7gpOSbM+W z!l)8bb;f@Q`l60dTewqLUu}A@$CW7yI$751g>y|N3cg2@uUd?{W6NHZCpYrd>cmLcDlq%JtJJ%a59!4YNh zGdr<^0IG#~D5P6Zp*X)Pc|-FT62%tPf=zJwevf7>X|_7Y6%1hE!?I?dEZVdjOaHR? zI@8k0c0UD9cZT;CuF8xKqa#NBayglP3ZTzW>34;Fp*ZCqx~L8`ZBXWS`Vt(&J0f?} z%I67;joamkLm|5~w%;X0bd(K-4>T%p9^SQ-R|}Fb1U88?`IHLE4|qkDvhvlip1%W` z)bLpMh3Sn7F-5T#heEK0ccZgO!xi5=2KZhuW<@pEZsp7p_y6N`8c|I;$C;^e;QKCM zpk0HU6ueE8-5XO8?By!w7|jXh^>z=q$a@%#GhBkJtY+_ZGaVN9441Jk+SknnJg$6X zSN;~{)^-*9>-+AlC&eYD7Skrp~e7)Hu>*r@5vj2grt)e*+JrFAt~9LBon{PO;>I0B?>-VtiPJr(+hfXE|SRl zVZ#%!7d3)F{rHvg*eZoC>Ca=n)2ud%!wRL~3{bw8q*@`TR;$CIZ_mMxozp|wH?{&R zlOH!>W6ql=D5`k)U<_=%1kHUP-Hx}h=+dB}-tQaveuBDqdiiZjvwwc4zbL{f^*0mj zHQ`h$tKs#1RmhYD@IDIzupxgOGM&TKkf~jBS{%z&##z;ZYdliANSV}g;-mlEliKc^ zct3=*QwReH&xW}M2O76zcC9jFQ|O!DUB2Ca-Mrnf#!peoa1O0j@^k3q;-AwjR zOa#R`RT~RK)PXfn6b0iI<9&)OSuViomfAvg-i^~VR^4LGX3;}90rgW{hYS34h3u$? zkYg*&o~v`N@jeP-%S@4|!MBkcq5u7v&J~49h(0lBo=^s`b*9Cg=#0!c1)-R*p%4Mj zr1We4b_LMaAPs9xlM%SeV?vg1SBZ*Gb2UwOoiBUg7y#c$9QHo8(Jf>dgN4VM~|8u`xm8I|u3@jTlxC!BZ6SA|u z_7IUvy^P$n(3rG;@>8Q}D%T&aE~~Yit(*8i0G~VmW-(!}H->4B=&Y~HPNOjd9)66;6oXq=|hg7Fv>_-8~q9Jipv?1t*b(Xa+mWDxB+O zjzDRcJ37YmeQB~|w1m%xYLuBQ>)PXd%FE+at>pk@s{M`(E*+#L1_Bzxi!A4Ne0var zOK^M$V?FXF*vo38*Plw|3PGKgqJx&aDZqABUa7T8I8cUHh#mZYNn>~M|hW2_Qv&`+9)(vq?ZT{ORBUWV&;o>l%! zxaCcnnaP>EkJ!*6wf6i=n#0SZ<{}kb*rtBoq~3M|iG;lcKk=;Q*(7#;R>m`vmnD&D z7Q13TqiV5aJPZpM8RqoeyfrUNb*|EsPxAH_ioe!xB%gB1?Yo``kF?+&bC&iZ=y_w2kekk3jOy z@Mpb7W*Q%q?>!TefJK3?Z}uG8q$zT;3L)gH+sSx zUy!QU6~x0DhFm!3a#1W#Dq4hf=gy_uxjMZoy)8ta!=bU$4_~-4dWet9h_f^tf=<$;uM}3tGkCXF0J3FJh*v8=ew}*AbLX+%@-Bt~g;d_A znrVUtl(F%RdiQGyefRa*>({m)4b*i6be}`nY3Kngl6o<4`f=WRXxMj88Qua`)|9%1 z4qfX|)L-<+J=KW@9f$7CFU>$~5$F)bFL!r|3YEXX&H`Ha_Em2%fMwdAxi|B^_XiKTCw-wzv%7z-%D-C;VXFn? z$lH2No25O#W4=dII8zWe#z0sr|3_0E&j99lVTXfj=i7H_gn}j0uxu!jS2XE9>F%u` zQUCL1^$y9+Ke<;`$9V9w)K32R-SdA*XLTqo*y#X_%S+X_6-9qR-aGo^xc-!mDpWGS zK&!6>-Z)eJ(zx@@Ys~=be5AHO&H!Ulb}hi?y0nwu@qGU_xasR*oZZ~e#xFkVo@uFBfDV%zYn>$sVf zITTHY(nQeq%2<(#v9QdZ2)8JBlRF6YcwH`n7ygKgPsapuKp5?5{3}MBE8I?`?MDsg z4pBBG`wT7zi>fqz8cgAAHf)3AzgxPPwV3&?p&E0~y?^Kgb8YkWe?KC}tBf*jW>no- zf{`4#(2cL^xDEB>WG!4`Jz~Okq-q0*1!Yi&5dGA&q_cPH-Dg;<&?^2i(UEF|(q)Oo zXyr%sVanU@%32eCcfBV^Fb7WE#CI_D)vI%@{CoZj&s$YUDcGb$n!hpD)ev!Z)?8*X zGUhx6H79?86uEm9GMUSGV$!?e5Pm6RYDK~^`Y>wcAsTgd7zueY7IK6hTRT;BBXdNP zx6MNw%~e*ApRN|+4K*``Ap2jw_|Fs77=yOT%`f@2M zYo!v_OMNRkcY@*$nZf|-a{GXbnVaT2y=yEct#rAf3v5~`UKIZ@+$0;G12IMu?VnF_oov5@l+*gDMo8HML+bolSVx|MWxDuel;QN%g1x~5*0bv@ZZq<`Dtj`|J690 zbcYTXrt-1s$)L(i{?x@c%K7;fd=Ev2qc_@9a)$&Vqg^bNqXggL#02UR=ad6MvKb%i z)+W)kw@UYdJPs|vZ{xqt7HGQ8l+Lx}FY#WhnMzd|WUD`8{>DmmQEfWt3tmK1b)-p| zj@W_A=&I-PjfC1i9?KjIb#j?CtYk6hfzOuobT1y|KybiqMK-9r@Rq8QzJxyt0D?wV zI@z9fIDFQbwpYE0Jw^WrJqgvH&Oc9K!T_=ie}qhyM{JC3a)~h)T+)byJaPOCX^*|j z{SKjVCCt84Kz^EPKF?TEz$P1-qEUQB85;%D^n)wfSfVNIPHoTG1<|;s8$%$LT61iL zQhJ<+Ry)wOz&B^^&0@vN@tB5Xisz7}N>+k>(C4>FU-tve2m1Vps~}3SI*SyRwDsm5Qc?n-oTT9&q>aM0H!p~N9c589vO8(vcBVXal-2Ft+k6DBI?Iiwxw?{Vz{@F|9bXieDbGyN{)Bzplfh?_c=}>Ma*pUA+%U4|qFm6j zhC4rB5$ClbXH7;`_l|glAiyb|P4f{45w?J~w_7SIA*uyvrq2&Y@nRp_16aInPns^# zO8wSYU^UL#gQ-bG>wZZY_ z>ygddNFUe_Y@LEbTYliik8lj2QjQ#?c2YzI4zfONh>l<3T-F?H`~&y8WitGMH4bO^ zC|V3ap61{QZioe$Xtr{DAR>lgk?DQs0lEAP?v+@s< z%)nvXstax7Jc1?VM}k6{-@5N>4TMS_LX=idRmT3jf+!|ggG2cbz z4tX~?Y-0H53FuoLg#@* zK@z9rz2uJ+Hpst9az;edR|NeytY3bAWT_L7z@%1@^cT+6A$x*^OgIfDRXMGU+uId< zD+G#qAQ!YLtka2bn>Wy~)9L}>B@yb1`f~_26V&Zp-(lgxw^7|(IaWLgRX%BD(lc+?m{SF^ls`(bOqPM(&|H`U=!Vte0S54k5F0`j)+|gB-Gev4 z^Y!-s2)MCH5ym=6ilWFeHAcmTYoUmRhB}u_M#!pK0KCE7(13PfTKV~UszO4}TwOV2 z$j5E)W9x0OLu=(_-@xieg8EaN`+j*09chU z0GWNKpy$ivK!0EctlmCz=irv5Qkln>vkmCzat%3RGvnM>zmF71AmN&pX=Xl{1HIR} z`3m`C0J`VMXh&b871PlXH-^|nX3~zo`n>iLrh97|#tS;U)xHFANh=d0Mq zgO9`uS#RG2&E(!~!-}EdPxu_yD?%3OC2kGR`3fMzGe|dd=N1Mq#Sfm@>4Du)9(Cq! zfbBNsgnzxLK1AL1x5jP@MDNku)J=&Wz5Sjj2^Zd&E`YT2UHEm7j||aknG_DT(2u Date: Sat, 27 Jan 2018 17:38:41 +0300 Subject: [PATCH 19/24] Add main page --- project/templates/lilcity/main.html | 575 ++++++++++++++++++++++++++++ project/urls.py | 2 +- web/build/img/sprite.svg | 0 3 files changed, 576 insertions(+), 1 deletion(-) create mode 100644 project/templates/lilcity/main.html mode change 100755 => 100644 web/build/img/sprite.svg diff --git a/project/templates/lilcity/main.html b/project/templates/lilcity/main.html new file mode 100644 index 00000000..68994872 --- /dev/null +++ b/project/templates/lilcity/main.html @@ -0,0 +1,575 @@ +{% extends "templates/lilcity/index.html" %} {% load static %} + +{% block title %}School LIL.CITY{% endblock title %} +{% block content %} +
+
+
Первая онлайн-школа креативного мышления для детей! 5+
+ КУПИТЬ ДОСУП ЗА 2000р. в мес. +
+
+
+
+
+

Каждый день, с понедельника по пятницу в 17:00 по московскому времени мы встречаемся в прямом эфире.

+

Пять предметов на каждый день: Персонаж, Развитие креативного мышления, Акварель, Пластилиновая живопись и История + искусств.

+ +
+
О школе
+
+
+ +
+
+
+

С раннего возраста мы стремимся развить в детях креативность, умение думать и анализировать, работать в смешанных + техниках и всевозможными материалами. Каждый урок интересный и уникальный.

+

Для раскрытия творческого потенциала создаем благоприятные условия которые вдохновляют к развитию. Наша школа создает + необходимую среду, в которой маленькое семечко способностей и желания рисовать вырастет в могучее дерево безграничного + потенциала. +

+

Наши ученики становяться осознанными, уверенными и творческий личностями, способные решать сложные задачи благодаря + воображению. +

+
+
- Sasha Kru, CEO и основатель + Lil.City +
+ + + + + + +
Другие видео смотрите на нашем + канале +
+
+
+
+
+ +
+
ПРЯМОЙ ЭФИР
+
Каждый день в 17.00 (по Мск)
+
Кроме выходных. Запись эфира доступна в течение 24-х часов.
+
+ + + СМОТРЕТЬ ПРИМЕР ЭФИРА
+
+
+
+
+
Преимущества
+
+

Если цените время и стремитесь быть продуктивными, то онлайн-образование это выбор прогрессивного человека для достижения + необходимых результатов.

+ +
+
+
+
+ + + +
+
Доступность
+
Учитесь, не выходя из дома. С активной жизнью в больших городах, времени для образование остается не много. + Маленьким городам, наоборот, не хватает разнообразия мегаполисов.
+
+
+
+ + + +
+
Стоимость
+
Онлайн-образование дешевле. Здесь не требуется аренда помещения, нет коммунальных платежей.
+
+
+
+ + + +
+
Свобода перемещений
+
Это важный критерий для родителей. С онлайн-образованием собирать, одевать и стоять в бесконечных пробках теперь + не нужно.
+
+
+
+ + + +
+
Выбор преподавателя
+
Личные аккаунты Инстаграм также доступны, чтобы узнать о творчестве, мыслях и интересах. Принять решение у какого + преподавателя учиться теперь просто.
+
+
+
+

И, наконец, учиться онлайн можете в пижаме, без макияжа и с огурцами на лице!

+
+
+
+
+
+
Отзывы
+
Мы получаем сотни тёплых отзывов от довольных родителей. Их можно почитать + здесь;) +
+
+
+
+ +
+
+
@redanna333
+
Мой ребёнок зарисовал!!! Хотя, я и не ставила таких глобальных целей, участвуя в этом лагере, просто занять дитё + чем-то творческим хоть иногда, но он реально рисует, сам, и не надо приглашать и уговаривать, просто берёт и + рисует) Спасибо, Саша, от всей души) Надеемся на встречу в школе)
+
+
+
+
+ +
+
+
@redanna333
+
Саша, огромное спасибо за ещё одну возможность погружения в удивительный мир Ваших уроков! С начала первого летнего + лагеря и до сегодняшнего дня, был запущен творческий круговорот разных идей, по заданиям и просто так)). На самом + деле, удивительная способность, находясь далеко, по другую сторону экрана, создавать уникальный контакт присутствия + рядом, будто в одной комнате и давно знакомы). И ещё одно важное качество- быть на стороне ребёнка в непростом + творческом процессе- моя дочь, слушая Ваше задание, расправляет "крылья", не боясь оценочного мнения , а сосредоточившись + на задаче)). P.S В школу идём без сомнений!
+
+
+
+
+ +
+
+
@redanna333
+
Саша, 👋 Вы потрясающе четко и качественно можете объяснить задание урока! Дети настолько увлекаются процессом, + что взрослые и сами включаются в эту интересную игру ТВОРЧЕСТВО! Спасибо Вам за Вдохновение, Любовь и желание + творить Чудеса! Темыч теперь легко может начать рисовать "трудные" картинки, потому что тетя Саша сказала: "все + состоит из простых форм!" Теперь это наш девиз) главное ушёл страх, что не выйдет ПРАВИЛЬНО, потому что нет ограничений + и правил в творчестве😜🙌надеемся на скорую встречу!!!
+
+
+
+
+
+
+
+
Галерея
+
+

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

+ +
+ +
+
+
+
+
+
ПРИЛОЖЕНИЯ LIL CITY
+
Лучшие работы попадают в приложение Lil World
+
Lil World – это арт-фото редактор многократно отмеченный AppStore по всему миру как лучшее приложение с огромной коллекцией + иллюстраций стикеров. Украшайте ваши фото и превращайте их в волшебные миры.
+ +
+
+
+ +
+
+
+
+
+
+
Преподаватели
+
+

Преподаватели Lil City School имеют большой опыт, поэтому с первых минут детям будет интересно с нами.

+ +
+
+
+
+ +
+
+
Саша Крю, + #lil_персонаж +
+
@sashakru
+
+

Закончила ПХУ им К.А.Савицкого художник театра и кино. Работала с крупнейшими российскими и зарубежными + издательствами.

+

Участник и победитель международных выставок.

+

Основатель компании "Lil City".

+
+
+
+
+
+ +
+
+
Саша Крю, + #lil_персонаж +
+
@sashakru
+
+

Закончила ПХУ им К.А.Савицкого художник театра и кино. Работала с крупнейшими российскими и зарубежными + издательствами.

+

Участник и победитель международных выставок.

+

Основатель компании "Lil City".

+
+
+
+
+
+ +
+
+
Саша Крю, + #lil_персонаж +
+
@sashakru
+
+

Закончила ПХУ им К.А.Савицкого художник театра и кино. Работала с крупнейшими российскими и зарубежными + издательствами.

+

Участник и победитель международных выставок.

+

Основатель компании "Lil City".

+
+
+
+
+
+ +
+
+
Саша Крю, + #lil_персонаж +
+
@sashakru
+
+

Закончила ПХУ им К.А.Савицкого художник театра и кино. Работала с крупнейшими российскими и зарубежными + издательствами.

+

Участник и победитель международных выставок.

+

Основатель компании "Lil City".

+
+
+
+
+
Если хотите к нам в команду, то ждем ваше резюме + на почту +
+
+
+
+
+
Расписание
+
+
+
Понедельник
+
+
Персонаж.
+
Учимся создавать персонажей из простых форм. Изучаем характеры и эмоции.
+
+ +
Cамое главное - иметь альбом или блокнот с пустыми страницами (без линий и клеток) плотной гладкой бумагой, формат + А4. Рисовать будем цветными карандашами, а также простым, мягкостью B2. Иногда пригодятся вырезки из журналов + и клей-карандаш.
+
+
+
+
+
Вторник
+
+
Персонаж.
+
Учимся создавать персонажей из простых форм. Изучаем характеры и эмоции.
+
+ +
Cамое главное - иметь альбом или блокнот с пустыми страницами (без линий и клеток) плотной гладкой бумагой, формат + А4. Рисовать будем цветными карандашами, а также простым, мягкостью B2. Иногда пригодятся вырезки из журналов + и клей-карандаш.
+
+
+
+
+
Среда
+
+
Персонаж.
+
Учимся создавать персонажей из простых форм. Изучаем характеры и эмоции.
+
+ +
Cамое главное - иметь альбом или блокнот с пустыми страницами (без линий и клеток) плотной гладкой бумагой, формат + А4. Рисовать будем цветными карандашами, а также простым, мягкостью B2. Иногда пригодятся вырезки из журналов + и клей-карандаш.
+
+
+
+
+
Четверг
+
+
Персонаж.
+
Учимся создавать персонажей из простых форм. Изучаем характеры и эмоции.
+
+ +
Cамое главное - иметь альбом или блокнот с пустыми страницами (без линий и клеток) плотной гладкой бумагой, формат + А4. Рисовать будем цветными карандашами, а также простым, мягкостью B2. Иногда пригодятся вырезки из журналов + и клей-карандаш.
+
+
+
+
+
Пятница
+
+
Персонаж.
+
Учимся создавать персонажей из простых форм. Изучаем характеры и эмоции.
+
+ +
Cамое главное - иметь альбом или блокнот с пустыми страницами (без линий и клеток) плотной гладкой бумагой, формат + А4. Рисовать будем цветными карандашами, а также простым, мягкостью B2. Иногда пригодятся вырезки из журналов + и клей-карандаш.
+
+
+
+
+
+ Распечатать расписание чтобы не забыть
+
+
+
+
+
Онлайн-курсы
+
Помимо онлайн-школы Lil City у нас есть отдельные + курсы в записи. Учитесь и развивайте креативное мышление когда вам удобно.
+
+
+
+ + +
Подробнее
+
+
+
+ АНИМАЦИЯ +
30$
+
+ Базовый курс для детей по основам иллюстрации +
Этот курс поможет детям узнать о том как из простых форм создавать веселый и харизматичных персонажей.
+
+
+ +
+
+
Александра Неимоверноумная
+
+
SEPT 12, 2017
+ +
+
+
+
+
+ + +
Подробнее
+
+
До запуска курса осталось:
+
16 часов 13 минут
+
+
+
+
+ АНИМАЦИЯ +
30$
+
+ Базовый курс для детей по основам иллюстрации +
Этот курс поможет детям узнать о том как из простых форм создавать веселый и харизматичных персонажей.
+
+
+ +
+
+
Александра Неимоверноумная
+
+
SEPT 12, 2017
+ +
+
+
+
+
+ + +
Подробнее
+
+
+ АНИМАЦИЯ +
30$
+
+ Базовый курс для детей по основам иллюстрации +
Этот курс поможет детям узнать о том как из простых форм создавать веселый и харизматичных персонажей.
+
+
+ +
+
+
Александра Неимоверноумная
+
+
SEPT 12, 2017
+ +
+
+
+
+
+ +
+
+
+
+
+
+
LILCITY
+
Приложения, развивающие игры и интерактивные книги от Lil City.
+ УЗНАТЬ БОЛЬШЕ +
+
+
+ +
+
+
+
+
Наши партнеры
+ +
+
+{% endblock content %} \ No newline at end of file diff --git a/project/urls.py b/project/urls.py index af884c32..ed5cd1dc 100644 --- a/project/urls.py +++ b/project/urls.py @@ -24,7 +24,7 @@ urlpatterns = [ path('admin/', admin.site.urls), path('auth/', include(('apps.auth.urls', 'lilcity'))), path('courses/', CoursesView.as_view(), name='courses'), - path('', TemplateView.as_view(template_name="templates/lilcity/index.html"), name='index'), + path('', TemplateView.as_view(template_name="templates/lilcity/main.html"), name='index'), ] diff --git a/web/build/img/sprite.svg b/web/build/img/sprite.svg old mode 100755 new mode 100644 From b9ef367c6d35f75d7ec3650684839d3b135a3edb Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Sat, 27 Jan 2018 19:09:51 +0300 Subject: [PATCH 20/24] Migrate to new user model --- apps/auth/views.py | 14 +++---- apps/user/admin.py | 7 ---- apps/user/migrations/0001_initial.py | 57 ++++++++++++++++---------- apps/user/models.py | 60 ++++++++++++++++------------ project/settings.py | 3 +- 5 files changed, 78 insertions(+), 63 deletions(-) diff --git a/apps/auth/views.py b/apps/auth/views.py index d1fcc322..f2ebcf18 100644 --- a/apps/auth/views.py +++ b/apps/auth/views.py @@ -13,7 +13,6 @@ from django.views.generic import FormView, View from django.views.generic.edit import BaseFormView from apps.notification.utils import send_email -from apps.user.models import LilcityUserSettings, LilcityUserProxy from .forms import LearnerRegistrationForm from .tokens import verification_email_token @@ -120,9 +119,9 @@ class FacebookLoginOrRegistration(View): except FacepyError: return JsonResponse({"success": False}) - facebook_id = data.get('id') + fb_id = data.get('id') - lilcity_user_settings = LilcityUserSettings.objects.filter(facebook_id=facebook_id) + lilcity_user_settings = User.objects.filter(fb_id=fb_id) if lilcity_user_settings.count(): login(requests, user=lilcity_user_settings[0].user) return JsonResponse({"success": True}) @@ -136,9 +135,8 @@ class FacebookLoginOrRegistration(View): first_name = data.get('first_name', '') last_name = data.get('last_name', '') - user = LilcityUserProxy.objects.create_user(username=email, email=email, first_name=first_name, last_name=last_name, password=uuid4().hex) - - user.lilcity_user_settings.is_verification_email = True - user.lilcity_user_settings.facebook_id = facebook_id - user.lilcity_user_settings.save() + user = User.objects.create_user(username=email, email=email, first_name=first_name, last_name=last_name, password=uuid4().hex) + user.is_email_proved = True + user.fb_id = fb_id + user.save() return JsonResponse({"success": True}) diff --git a/apps/user/admin.py b/apps/user/admin.py index 92876bde..694323fa 100644 --- a/apps/user/admin.py +++ b/apps/user/admin.py @@ -1,8 +1 @@ from django.contrib import admin - -from .models import LilcityUserSettings - - -@admin.register(LilcityUserSettings) -class LilcityUserSettingsAdmin(admin.ModelAdmin): - pass diff --git a/apps/user/migrations/0001_initial.py b/apps/user/migrations/0001_initial.py index 54aa1ab8..a2ed6c65 100644 --- a/apps/user/migrations/0001_initial.py +++ b/apps/user/migrations/0001_initial.py @@ -1,9 +1,10 @@ -# Generated by Django 2.0.1 on 2018-01-15 08:33 +# Generated by Django 2.0.1 on 2018-01-27 16:03 -import apps.user.models -from django.conf import settings +import django.contrib.auth.models +import django.contrib.auth.validators +import django.contrib.postgres.fields.jsonb from django.db import migrations, models -import django.db.models.deletion +import django.utils.timezone class Migration(migrations.Migration): @@ -12,33 +13,47 @@ class Migration(migrations.Migration): dependencies = [ ('auth', '0009_alter_user_last_name_max_length'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( - name='LilcityUserSettings', + name='User', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('is_verification_email', models.BooleanField(default=False)), - ], - ), - migrations.CreateModel( - name='LilcityUserProxy', - fields=[ + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), + ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('email', models.EmailField(max_length=254, unique=True, verbose_name='email address')), + ('role', models.PositiveSmallIntegerField(choices=[(0, 'пользователь'), (1, 'автор'), (2, 'администратор')], default=0)), + ('gender', models.CharField(choices=[('n', 'не указан'), ('m', 'Мужчина'), ('f', 'Женщина')], max_length=1)), + ('country', models.CharField(default='', max_length=50)), + ('city', models.CharField(default='', max_length=85)), + ('about', models.CharField(default='', max_length=1000)), + ('instagram', models.URLField(default='')), + ('facebook', models.URLField(default='')), + ('twitter', models.URLField(default='')), + ('pinterest', models.URLField(default='')), + ('youtube', models.URLField(default='')), + ('vkontakte', models.URLField(default='')), + ('fb_id', models.PositiveIntegerField(blank=True, null=True, unique=True)), + ('fb_data', django.contrib.postgres.fields.jsonb.JSONField(default={})), + ('is_email_proved', models.BooleanField(default=False)), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), ], options={ - 'proxy': True, - 'indexes': [], + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'abstract': False, }, - bases=('auth.user',), managers=[ - ('objects', apps.user.models.LilcityUserManager()), + ('objects', django.contrib.auth.models.UserManager()), ], ), - migrations.AddField( - model_name='lilcityusersettings', - name='user', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='lilcity_user_settings', to=settings.AUTH_USER_MODEL), - ), ] diff --git a/apps/user/models.py b/apps/user/models.py index 82ec3c6d..e568b7d9 100644 --- a/apps/user/models.py +++ b/apps/user/models.py @@ -1,27 +1,35 @@ from django.db import models -from django.contrib.auth import get_user_model -from django.contrib.auth.models import AbstractUser, UserManager - - -User = get_user_model() - - -class LilcityUserManager(UserManager): - def create_user(self, username, email=None, password=None, **extra_fields): - user = super().create_user(username, email, password, **extra_fields) - LilcityUserSettings.objects.create(user=user) - return user - - -class LilcityUserProxy(User): - objects = LilcityUserManager() - - class Meta: - proxy = True - - -class LilcityUserSettings(models.Model): - user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='lilcity_user_settings') - is_verification_email = models.BooleanField(default=False) - facebook_id = models.PositiveIntegerField(null=True, blank=True, unique=True) - +from django.contrib.auth.models import AbstractUser +from django.contrib.postgres import fields as pgfields +from django.utils.translation import gettext_lazy as _ + + +class User(AbstractUser): + ROLE_CHOICES = ( + (0, 'пользователь'), + (1, 'автор'), + (2, 'администратор'), + ) + GENDER_CHOICES = ( + ('n', 'не указан'), + ('m', 'Мужчина'), + ('f', 'Женщина'), + ) + email = models.EmailField(_('email address'), unique=True) + role = models.PositiveSmallIntegerField(default=0, choices=ROLE_CHOICES) + gender = models.CharField(max_length=1, choices=GENDER_CHOICES) + country = models.CharField(max_length=50, default='') + city = models.CharField(max_length=85, default='') + about = models.CharField(max_length=1000, default='') + instagram = models.URLField(default='') + facebook = models.URLField(default='') + twitter = models.URLField(default='') + pinterest = models.URLField(default='') + youtube = models.URLField(default='') + vkontakte = models.URLField(default='') + fb_id = models.PositiveIntegerField(null=True, blank=True, unique=True) + fb_data = pgfields.JSONField(default={}) + is_email_proved = models.BooleanField(default=False) + + USERNAME_FIELD = 'email' + REQUIRED_FIELDS = [] diff --git a/project/settings.py b/project/settings.py index 10946d9e..8ad324da 100644 --- a/project/settings.py +++ b/project/settings.py @@ -27,7 +27,6 @@ DEBUG = True ALLOWED_HOSTS = ["*"] # fixme: production mode - # Application definition INSTALLED_APPS = [ @@ -110,6 +109,8 @@ AUTH_PASSWORD_VALIDATORS = [ }, ] +AUTH_USER_MODEL = 'user.User' + # Internationalization # https://docs.djangoproject.com/en/2.0/topics/i18n/ From bb390d608393a7f9a46a9053cb443306fb0d26b5 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Sat, 27 Jan 2018 19:16:47 +0300 Subject: [PATCH 21/24] Add verbose names for User fields --- apps/user/models.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/user/models.py b/apps/user/models.py index e568b7d9..9d129e61 100644 --- a/apps/user/models.py +++ b/apps/user/models.py @@ -16,20 +16,20 @@ class User(AbstractUser): ('f', 'Женщина'), ) email = models.EmailField(_('email address'), unique=True) - role = models.PositiveSmallIntegerField(default=0, choices=ROLE_CHOICES) - gender = models.CharField(max_length=1, choices=GENDER_CHOICES) - country = models.CharField(max_length=50, default='') - city = models.CharField(max_length=85, default='') - about = models.CharField(max_length=1000, default='') + role = models.PositiveSmallIntegerField('Роль', default=0, choices=ROLE_CHOICES) + gender = models.CharField('Пол', max_length=1, choices=GENDER_CHOICES) + country = models.CharField('Страна', max_length=50, default='') + city = models.CharField('Город', max_length=85, default='') + about = models.CharField('О себе', max_length=1000, default='') instagram = models.URLField(default='') facebook = models.URLField(default='') twitter = models.URLField(default='') pinterest = models.URLField(default='') youtube = models.URLField(default='') - vkontakte = models.URLField(default='') + vkontakte = models.URLField('ВКонтакте', default='') fb_id = models.PositiveIntegerField(null=True, blank=True, unique=True) fb_data = pgfields.JSONField(default={}) - is_email_proved = models.BooleanField(default=False) + is_email_proved = models.BooleanField('Верифицирован по email', default=False) USERNAME_FIELD = 'email' REQUIRED_FIELDS = [] From e0c3862e8cfd533c8c416a8f4c5395df583e6405 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Sun, 28 Jan 2018 09:47:17 +0300 Subject: [PATCH 22/24] Fix User REQUIRED_FIELDS --- apps/user/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/user/models.py b/apps/user/models.py index 9d129e61..c0aac8ca 100644 --- a/apps/user/models.py +++ b/apps/user/models.py @@ -1,5 +1,5 @@ from django.db import models -from django.contrib.auth.models import AbstractUser +from django.contrib.auth.models import AbstractUser, UserManager from django.contrib.postgres import fields as pgfields from django.utils.translation import gettext_lazy as _ @@ -32,4 +32,4 @@ class User(AbstractUser): is_email_proved = models.BooleanField('Верифицирован по email', default=False) USERNAME_FIELD = 'email' - REQUIRED_FIELDS = [] + REQUIRED_FIELDS = ['username'] From 65eae9a55227aa1226e25f0874c7743dcc0424b3 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Sun, 28 Jan 2018 11:16:09 +0300 Subject: [PATCH 23/24] Remove not needed migration --- .../0002_lilcityusersettings_facebook_id.py | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 apps/user/migrations/0002_lilcityusersettings_facebook_id.py diff --git a/apps/user/migrations/0002_lilcityusersettings_facebook_id.py b/apps/user/migrations/0002_lilcityusersettings_facebook_id.py deleted file mode 100644 index 58145327..00000000 --- a/apps/user/migrations/0002_lilcityusersettings_facebook_id.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.0.1 on 2018-01-26 09:13 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('user', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='lilcityusersettings', - name='facebook_id', - field=models.PositiveIntegerField(blank=True, null=True, unique=True), - ), - ] From 277abce53c8225f6426854eb075dba17a7206e89 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Sun, 28 Jan 2018 13:42:21 +0300 Subject: [PATCH 24/24] Add new superuser mixin for dev time --- apps/user/fixtures/superuser.json | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/apps/user/fixtures/superuser.json b/apps/user/fixtures/superuser.json index a565acf9..f9fc5b21 100644 --- a/apps/user/fixtures/superuser.json +++ b/apps/user/fixtures/superuser.json @@ -1,18 +1,32 @@ [ { - "model": "auth.user", + "model": "user.user", "pk": 1, "fields": { - "password": "pbkdf2_sha256$100000$NgQxbNX0XxNX$08G0OS9JUz0kStyfzGjhasmvj7ilUWkrm/wKMaN5H3o=", - "last_login": null, + "password": "pbkdf2_sha256$100000$HoGxjmAQy4yo$3u+CrUiFtooddpg16OEUaSSjqUlwgFUBP1gt75hEoJs=", + "last_login": "2018-01-28T08:41:44.702Z", "is_superuser": true, - "username": "admin", + "username": "admin@lil.city", "first_name": "", "last_name": "", - "email": "admin@lil.com", "is_staff": true, "is_active": true, - "date_joined": "2018-01-26T09:08:15.035Z", + "date_joined": "2018-01-28T08:41:19.690Z", + "email": "admin@lil.city", + "role": 0, + "gender": "", + "country": "", + "city": "", + "about": "", + "instagram": "", + "facebook": "", + "twitter": "", + "pinterest": "", + "youtube": "", + "vkontakte": "", + "fb_id": null, + "fb_data": {}, + "is_email_proved": false, "groups": [], "user_permissions": [] }