diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 00000000..f86269c7 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,18 @@ +name: CI + +on: + pull_request: + branches: [ master ] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + with: + python-version: "3.11" + - name: Install requirements + run: pip install -r requirements-dev.txt + - name: Run lint + run: make check diff --git a/Dockerfile b/Dockerfile index a489ed4f..8562f92f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8-slim-buster as base +FROM python:3.11-slim-buster as base ENV PYTHONUNBUFFERED 1 ENV DEBIAN_FRONTEND=noninteractive diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..09bc7fb1 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +isort: + isort . +style: + flake8 . +types: + mypy . +test: + cd landing_page; pytest . +check: + make isort style types test +run: + cd landing_page; DEBUG=1 python manage.py runserver +shell: + cd landing_page; DEBUG=1 python manage.py shell +migrate: + cd landing_page; DEBUG=1 python manage.py migrate diff --git a/landing_page/landing_page/settings.py b/landing_page/landing_page/settings.py index a6206e02..8c759562 100644 --- a/landing_page/landing_page/settings.py +++ b/landing_page/landing_page/settings.py @@ -1,41 +1,29 @@ -""" -Django settings for landing_page project. - -Generated by 'django-admin startproject' using Django 2.0.5. - -For more information on this file, see -https://docs.djangoproject.com/en/2.0/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/2.0/ref/settings/ -""" - import os -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '3=dgm+l+a*q98^7(frp1^q6fm_a-b-!pa758g$e34boojl3j0b' +SECRET_KEY = os.environ.get('SECRET_KEY', '3=dgm+l+a*q98^7(frp1^q6fm_a-b-!pa758g$e34boojl3j0b') -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = bool(os.getenv('DEBUG', False)) +DEBUG = bool(int(os.getenv('DEBUG', False))) ALLOWED_HOSTS = ['*'] -# Application definition +CSRF_TRUSTED_ORIGINS = ['https://learn.python.ru'] +X_FRAME_OPTIONS = "SAMEORIGIN" INSTALLED_APPS = [ 'mainpage.apps.MainpageConfig', + 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'django.contrib.sitemaps', + + 'memoize', + 'waffle', ] MIDDLEWARE = [ @@ -46,6 +34,7 @@ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'waffle.middleware.WaffleMiddleware', ] ROOT_URLCONF = 'landing_page.urls' @@ -68,9 +57,6 @@ WSGI_APPLICATION = 'landing_page.wsgi.application' -# Database -# https://docs.djangoproject.com/en/2.0/ref/settings/#databases - DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', @@ -78,9 +64,6 @@ } } -# Password validation -# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators - AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', @@ -96,17 +79,12 @@ }, ] -# Internationalization -# https://docs.djangoproject.com/en/2.0/topics/i18n/ - LANGUAGE_CODE = 'ru-RU' TIME_ZONE = 'Europe/Moscow' USE_I18N = True -USE_L10N = True - USE_THOUSAND_SEPARATOR = True THOUSAND_SEPARATOR = ' ' @@ -115,9 +93,6 @@ USE_TZ = True -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/2.0/howto/static-files/ - STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, '../staticfiles') @@ -130,4 +105,4 @@ MEDIA_URL = '/media/' -WEBHOOK_KEY = '0001VypAArY7lFDgMdyC5kwutDGQdDc6rXljuIcI5iBttpPe200' +DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' diff --git a/landing_page/landing_page/sitemap.py b/landing_page/landing_page/sitemap.py new file mode 100644 index 00000000..83a36418 --- /dev/null +++ b/landing_page/landing_page/sitemap.py @@ -0,0 +1,19 @@ +import datetime + +from django.contrib.sitemaps import Sitemap +from django.urls import reverse +from mainpage.utils.github import fetch_last_commit_date_from_github_repo + + +class LearnSitemap(Sitemap): + changefreq = "weekly" + priority = 0.5 + + def items(self) -> list[str]: + return ["index", "index_advanced"] + + def location(self, item: str) -> str: + return reverse(item) + + def lastmod(self, obj: str) -> datetime.datetime | None: + return fetch_last_commit_date_from_github_repo('moscowpython', 'learnpython') diff --git a/landing_page/landing_page/urls.py b/landing_page/landing_page/urls.py index 0f50e6ca..e05589bd 100644 --- a/landing_page/landing_page/urls.py +++ b/landing_page/landing_page/urls.py @@ -1,22 +1,18 @@ -"""landing_page URL Configuration - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/2.0/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: path('', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.urls import include, path - 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) -""" from django.contrib import admin +from django.contrib.sitemaps.views import sitemap from django.urls import include, path +from landing_page.sitemap import LearnSitemap + urlpatterns = [ path('', include('mainpage.urls')), + path('admin/', admin.site.urls), + + path( + "sitemap.xml", + sitemap, + {"sitemaps": {"static": LearnSitemap}}, + name="django.contrib.sitemaps.views.sitemap", + ), ] diff --git a/landing_page/landing_page/wsgi.py b/landing_page/landing_page/wsgi.py index e3b2e0ca..2f859d21 100644 --- a/landing_page/landing_page/wsgi.py +++ b/landing_page/landing_page/wsgi.py @@ -1,12 +1,3 @@ -""" -WSGI config for landing_page project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/ -""" - import os from django.core.wsgi import get_wsgi_application diff --git a/landing_page/mainpage/admin.py b/landing_page/mainpage/admin.py index 0514c74a..3e6dd497 100644 --- a/landing_page/mainpage/admin.py +++ b/landing_page/mainpage/admin.py @@ -1,16 +1,8 @@ from django.contrib import admin -from .models import ( - MoscowPythonMeetup, LearnPythonCourse, Curators, Feedback, GraduateProjects, GraduateStories, - LearnPythonCoursePrices, GraduateProjectsVideos, Podcasts -) +from .models import CourseReview, Curators, Enrollment, GraduateProjects -admin.site.register(MoscowPythonMeetup) -admin.site.register(LearnPythonCourse) admin.site.register(Curators) -admin.site.register(Feedback) admin.site.register(GraduateProjects) -admin.site.register(GraduateStories) -admin.site.register(LearnPythonCoursePrices) -admin.site.register(GraduateProjectsVideos) -admin.site.register(Podcasts) +admin.site.register(Enrollment) +admin.site.register(CourseReview) diff --git a/landing_page/mainpage/js/main.js b/landing_page/mainpage/js/main.js index 6c51618b..5fb0ccdc 100644 --- a/landing_page/mainpage/js/main.js +++ b/landing_page/mainpage/js/main.js @@ -1,3 +1,4 @@ + class LearnPython { constructor() { diff --git a/landing_page/mainpage/migrations/0037_auto_20210803_1252.py b/landing_page/mainpage/migrations/0037_auto_20210803_1252.py new file mode 100644 index 00000000..f589527e --- /dev/null +++ b/landing_page/mainpage/migrations/0037_auto_20210803_1252.py @@ -0,0 +1,36 @@ +# Generated by Django 2.2.20 on 2021-08-03 09:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mainpage', '0036_auto_20200523_1308'), + ] + + operations = [ + migrations.CreateModel( + name='LearnPythonMultiCityCourses', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('cityname', models.CharField(help_text='Какой город указывать в списке', max_length=50, verbose_name='Название города')), + ('long', models.DecimalField(decimal_places=6, max_digits=9)), + ('lat', models.DecimalField(decimal_places=6, max_digits=9)), + ('early_date', models.DateField(verbose_name='Дата окончания ранней регистрации')), + ('early_price', models.IntegerField(verbose_name='Стоимость курса в раннюю регистрацию')), + ('early_installment_price', models.IntegerField(verbose_name='Стоимость рассрочки')), + ('basic_date', models.DateField(verbose_name='Дата начала основной регистрации')), + ('basic_price', models.IntegerField(verbose_name='Стоимость курса в обычную регистрацию')), + ('basic_installment_price', models.IntegerField(verbose_name='Стоимость рассрочки')), + ], + options={ + 'verbose_name_plural': 'LearnPython Цены на курсы в разных городах', + }, + ), + migrations.AlterField( + model_name='graduatestories', + name='story_section', + field=models.TextField(choices=[('Есть опыт, хочу освоить новый язык', 'Есть опыт, хочу освоить новый язык'), ('Хочу новый навык или работу', 'Хочу новый навык или работу'), ('Никогда не программировал', 'Никогда не программировал')], default='Никогда не программировал', help_text='В какую из секций историй', verbose_name='Раздел истории'), + ), + ] diff --git a/landing_page/mainpage/migrations/0038_auto_20210803_1257.py b/landing_page/mainpage/migrations/0038_auto_20210803_1257.py new file mode 100644 index 00000000..7f6af965 --- /dev/null +++ b/landing_page/mainpage/migrations/0038_auto_20210803_1257.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.20 on 2021-08-03 09:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mainpage', '0037_auto_20210803_1252'), + ] + + operations = [ + migrations.RenameField( + model_name='learnpythonmulticitycourses', + old_name='cityname', + new_name='city_name', + ), + migrations.AlterField( + model_name='graduatestories', + name='story_section', + field=models.TextField(choices=[('Хочу новый навык или работу', 'Хочу новый навык или работу'), ('Есть опыт, хочу освоить новый язык', 'Есть опыт, хочу освоить новый язык'), ('Никогда не программировал', 'Никогда не программировал')], default='Никогда не программировал', help_text='В какую из секций историй', verbose_name='Раздел истории'), + ), + ] diff --git a/landing_page/mainpage/migrations/0039_alter_graduatestories_story_section.py b/landing_page/mainpage/migrations/0039_alter_graduatestories_story_section.py new file mode 100644 index 00000000..c3d122a4 --- /dev/null +++ b/landing_page/mainpage/migrations/0039_alter_graduatestories_story_section.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.4 on 2023-08-17 07:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mainpage', '0038_auto_20210803_1257'), + ] + + operations = [ + migrations.AlterField( + model_name='graduatestories', + name='story_section', + field=models.TextField(choices=[('Есть опыт, хочу освоить новый язык', 'Есть опыт, хочу освоить новый язык'), ('Хочу новый навык или работу', 'Хочу новый навык или работу'), ('Никогда не программировал', 'Никогда не программировал')], default='Никогда не программировал', help_text='В какую из секций историй', verbose_name='Раздел истории'), + ), + ] diff --git a/landing_page/mainpage/migrations/0040_delete_feedback_delete_graduateprojectsvideos_and_more.py b/landing_page/mainpage/migrations/0040_delete_feedback_delete_graduateprojectsvideos_and_more.py new file mode 100644 index 00000000..a90895a8 --- /dev/null +++ b/landing_page/mainpage/migrations/0040_delete_feedback_delete_graduateprojectsvideos_and_more.py @@ -0,0 +1,62 @@ +# Generated by Django 4.2.4 on 2023-08-24 14:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mainpage', '0039_alter_graduatestories_story_section'), + ] + + operations = [ + migrations.DeleteModel( + name='Feedback', + ), + migrations.DeleteModel( + name='GraduateProjectsVideos', + ), + migrations.DeleteModel( + name='GraduateStories', + ), + migrations.DeleteModel( + name='LearnPythonCourse', + ), + migrations.DeleteModel( + name='LearnPythonCoursePrices', + ), + migrations.DeleteModel( + name='LearnPythonMultiCityCourses', + ), + migrations.DeleteModel( + name='MoscowPythonMeetup', + ), + migrations.DeleteModel( + name='Podcasts', + ), + migrations.AlterModelOptions( + name='curators', + options={'verbose_name_plural': 'Кураторы'}, + ), + migrations.RemoveField( + model_name='curators', + name='curator_github', + ), + migrations.RemoveField( + model_name='curators', + name='curator_motto', + ), + migrations.RemoveField( + model_name='curators', + name='curator_social_network', + ), + migrations.RemoveField( + model_name='curators', + name='curator_status', + ), + migrations.AddField( + model_name='curators', + name='is_visible', + field=models.BooleanField(default=True, verbose_name='Показывать на главной'), + ), + ] diff --git a/landing_page/mainpage/migrations/0041_enrollment.py b/landing_page/mainpage/migrations/0041_enrollment.py new file mode 100644 index 00000000..d368bc7d --- /dev/null +++ b/landing_page/mainpage/migrations/0041_enrollment.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2.4 on 2023-08-24 14:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mainpage', '0040_delete_feedback_delete_graduateprojectsvideos_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='Enrollment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('timepad_event_id', models.CharField(blank=True, max_length=64, null=True)), + ('start_date', models.DateField()), + ('end_date', models.DateField()), + ('end_registration_date', models.DateField()), + ('early_price_rub', models.IntegerField()), + ('late_price_rub', models.IntegerField()), + ('early_price_date_to', models.DateField()), + ('late_price_date_from', models.DateField()), + ], + ), + ] diff --git a/landing_page/mainpage/migrations/0042_enrollment_platim_url.py b/landing_page/mainpage/migrations/0042_enrollment_platim_url.py new file mode 100644 index 00000000..bb1c64c0 --- /dev/null +++ b/landing_page/mainpage/migrations/0042_enrollment_platim_url.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.4 on 2023-08-25 15:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mainpage', '0041_enrollment'), + ] + + operations = [ + migrations.AddField( + model_name='enrollment', + name='platim_url', + field=models.CharField(blank=True, max_length=254, null=True), + ), + ] diff --git a/landing_page/mainpage/migrations/0043_enrollment_type.py b/landing_page/mainpage/migrations/0043_enrollment_type.py new file mode 100644 index 00000000..cd5ad17f --- /dev/null +++ b/landing_page/mainpage/migrations/0043_enrollment_type.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.4 on 2023-09-08 13:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mainpage', '0042_enrollment_platim_url'), + ] + + operations = [ + migrations.AddField( + model_name='enrollment', + name='type', + field=models.CharField(blank=True, choices=[('BASE', 'BASE'), ('ADVANCED', 'ADVANCED')], max_length=10, null=True), + ), + ] diff --git a/landing_page/mainpage/migrations/0044_coursereview.py b/landing_page/mainpage/migrations/0044_coursereview.py new file mode 100644 index 00000000..ad76f100 --- /dev/null +++ b/landing_page/mainpage/migrations/0044_coursereview.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.4 on 2023-11-19 11:21 + +import mainpage.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mainpage', '0043_enrollment_type'), + ] + + operations = [ + migrations.CreateModel( + name='CourseReview', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=254)), + ('text', models.TextField()), + ('review_for', models.CharField(blank=True, choices=[(mainpage.models.EnrollmentType['BASE'], mainpage.models.EnrollmentType['BASE']), (mainpage.models.EnrollmentType['ADVANCED'], mainpage.models.EnrollmentType['ADVANCED'])], max_length=10, null=True)), + ('reviewer_name', models.CharField(blank=True, max_length=254, null=True)), + ('reviewed_at', models.DateField()), + ], + ), + ] diff --git a/landing_page/mainpage/models.py b/landing_page/mainpage/models.py index c4f38d21..b34a9eeb 100644 --- a/landing_page/mainpage/models.py +++ b/landing_page/mainpage/models.py @@ -1,233 +1,14 @@ -from django.db import models -from datetime import date, datetime, timedelta - - -class MoscowPythonMeetup(models.Model): - class Meta: - verbose_name_plural = "MoscowPython Митапы" - - def __str__(self): - return f'MoscowPython Meetup № {self.meetup_number}' - - meetup_number = models.IntegerField( - verbose_name='Номер митапа', - help_text='Введите номер митапа' - ) - - meetup_day = models.DateTimeField( - verbose_name='Дата и время митапа', - help_text='Где и во сколько?' - ) - - meetup_link = models.URLField( - verbose_name='Ссылка на митап', - default='http://www.moscowpython.ru', - help_text='Ссылка по стандарту, можно менять' - ) - - -class LearnPythonCourse(models.Model): - class Meta: - verbose_name_plural = "LearnPython Наборы" - - def __str__(self): - return f'LearnPython Набор № {self.course_index}' - - course_index = models.IntegerField( - verbose_name='Набор №', - help_text='Порядковый номер набора' - ) - - timepad_event_id = models.CharField( - verbose_name='Номер эвента в Timepad', - help_text='/dashboard/event/909971 <- вот этот', - max_length=20, - null=True - ) - - course_start_date = models.DateField( - verbose_name='Дата начала курса', - help_text='Дата первого занятия' - ) - - course_end_date = models.DateField( - verbose_name='Дата окончания курса', - help_text='Дата последнего занятия', - ) - - end_registration_date = models.DateTimeField( - verbose_name='Дата закрытия регистрации', - help_text='Во сколько закрывается регистрация?', - default=None, - - ) - - course_day_2 = models.DateField( - verbose_name='Второй день занятий', - help_text='Дата второго занятия', - default=None, - blank=True, - null=True - ) - - course_day_3 = models.DateField( - verbose_name='Третий день занятий', - help_text='Дата третьего занятия', - default=None, - blank=True, - null=True - ) - - course_day_4 = models.DateField( - verbose_name='Четвертый день занятий', - help_text='Дата четвертого занятия', - default=None, - blank=True, - null=True - ) - - course_day_5 = models.DateField( - verbose_name='Пятый день занятий', - help_text='Дата пятого занятия', - default=None, - blank=True, - null=True - ) - - course_day_6 = models.DateField( - verbose_name='Шестой день занятий', - help_text='Дата шестого занятия', - default=None, - blank=True, - null=True - ) - - course_day_7 = models.DateField( - verbose_name='Седьмой день занятий', - help_text='Дата седьмого занятия', - default=None, - blank=True, - null=True - ) - - course_day_8 = models.DateField( - verbose_name='Восьмой день занятий', - help_text='Дата восьмого занятия', - default=None, - blank=True, - null=True - ) - - course_day_9 = models.DateField( - verbose_name='Девятый день занятий', - help_text='Дата девятого занятия', - default=None, - blank=True, - null=True - ) - - offline_time_lessons_start = models.TimeField( - verbose_name='Время начала занятия', - help_text='Во сколько занятие начинается', - default='11:00:00', - blank=True, - null=True - ) - - offline_time_lessons_ends = models.TimeField( - verbose_name='Время окончания занятия', - help_text='Во сколько занятие заканчивается', - default='14:00:00', - blank=True, - null=True - ) - - online_time_of_call = models.TimeField( - verbose_name='Время начала звонка', - help_text='Во сколько созваниваемся?', - default='11:30:00', - blank=True, - null=True - ) - - offline_session_closed = models.BooleanField( - verbose_name='Закрыть офлайн набор?', - help_text='Поставь галочку, чтобы закрыть набор', - default=False, - ) - - online_session_closed = models.BooleanField( - verbose_name='Закрыть онлайн набор?', - help_text='Поставь галочку, чтобы закрыть набор', - default=False, - ) - - def get_date_after_first_lesson(self): - return self.course_start_date + timedelta(days=1) - - def get_day_before_last_lesson(self): - return self.course_end_date - timedelta(days=1) - - -class LearnPythonCoursePrices(models.Model): - class Meta: - verbose_name_plural = 'LearnPython Цены на курсы' - - def __str__(self): - return f'Интервал {self.price_range} на {self.course_type}' - - price_range = models.IntegerField( - verbose_name='Название временного отрезка', - help_text='Мустанг / Гепард / Панда и вот это все', - default=None - ) - - course_type = [ - ('Online', 'Online'), - ('Offline', 'Offline'), - ('OfflinePenza', 'OfflinePenza'), - ('OfflineSpb', 'OfflineSpb') - ] - - course_type = models.TextField( - choices=course_type, - verbose_name='Тип курса', - help_text='Выберите тип курса' - ) - - price_range_start_date = models.DateField( - verbose_name='Дата начала интервала', - help_text='Когда начинается данное предложение', - blank=True, - null=True - ) +import enum - price_range_end_date = models.DateField( - verbose_name='Дата окончания интервала', - help_text='Когда истекает данное предложение', - blank=True, - null=True - ) - - price_range_price = models.IntegerField( - verbose_name='Цена', - help_text='Сколько стоит курс в этот период' - ) - - @property - def within_price_range(self): - return self.price_range_start_date <= date.today() <= self.price_range_end_date - - @property - def past_due_date(self): - return date.today() > self.price_range_end_date +from django.db import models +from django.utils.timezone import now class Curators(models.Model): class Meta: - verbose_name_plural = 'LearnPython Кураторы' + verbose_name_plural = 'Кураторы' - def __str__(self): + def __str__(self) -> str: return f'Куратор {self.curator_name}' curator_name = models.CharField( @@ -241,14 +22,6 @@ def __str__(self): help_text='Где и кем работает, что программирует' ) - curator_motto = models.CharField( - max_length=150, - verbose_name='Девиз куратора', - help_text='Через тернии к звездам или что-нибудь такое', - blank=True, - null=True - ) - curator_photo = models.ImageField( help_text='Фотография куратора', verbose_name='Фото куратора', @@ -256,143 +29,9 @@ def __str__(self): upload_to='courators/' ) - curator_status = models.BooleanField( - verbose_name="Куратор для ближайшего набора?", - help_text='Куратор работает в текущем наборе?', - default=True - ) - - curator_github = models.URLField( - blank=True, - help_text='Ссылка на гит-хаб куратора', - verbose_name='GitHub куратора' - ) - - curator_social_network = models.URLField( - blank=True, - help_text='Если есть, ссылка на соцсеточку', - verbose_name='Соцсеточка куратора' - ) - - -class Feedback(models.Model): - class Meta: - verbose_name_plural = "LearnPython Отзывы" - - def __str__(self): - return f'Отзыв участника {self.feedback_author}' - - feedback_author = models.CharField( - verbose_name='Автор отзыва', - help_text='Кто автор отзыва?', - max_length=50, - null=True - ) - - feedback_author_link = models.URLField( - verbose_name='Ссылка на автора', - help_text='Есть ли ссылка на соцсеточки автора?', - blank=True, - null=True - ) - - feedback_author_position = models.CharField( - verbose_name='Должность выпускника', - help_text='Кем работает автор?', - max_length=50, - null=True - ) - - feedback_author_photo = models.ImageField( - verbose_name='Фотография автора', - help_text='Прикрепите фото автора отзыва', - blank=True, - upload_to='feedbacks/' - ) - - feedback_title = models.CharField( - verbose_name='Заголовок отзыва', - help_text='Большие буквы заголовка', - max_length=140, - default=None - ) - - feedback_text = models.TextField( - verbose_name='Текст отзыва', - help_text='А сюда пишем отзыв автора', - null=True - ) - - -class GraduateStories(models.Model): - class Meta: - verbose_name_plural = 'Learn Python Истории учеников' - - def __str__(self): - return f'История участника {self.story_author}' - - story_author = models.CharField( - verbose_name='Автор истории', - help_text='Как зовут автора истории?', - max_length=50, - null=True - ) - - story_author_photo = models.ImageField( - verbose_name='Фотография автора', - help_text='Прикрепите фото автора истории', - null=True, - upload_to='stories/' - ) - - story_author_position = models.CharField( - verbose_name='Должность выпускника', - help_text='Кто теперь выпускник', - max_length=50, - null=True - ) - - story_author_background = models.CharField( - verbose_name='В прошлом автор был:', - help_text='Прим. "В прошлом: терапевт"', - max_length=150 - ) - - story_section_choices = { - ('Никогда не программировал', - 'Никогда не программировал' - ), - ('Хочу новый навык или работу', - 'Хочу новый навык или работу' - ), - ('Есть опыт, хочу освоить новый язык', - 'Есть опыт, хочу освоить новый язык' - ) - } - - story_section = models.TextField( - choices=story_section_choices, - default='Никогда не программировал', - verbose_name='Раздел истории', - help_text='В какую из секций историй' - ) - - story_text_before_course = models.TextField( - verbose_name='История пользователя', - help_text='До курса', - default=None - ) - - story_text_on_course = models.TextField( - verbose_name='История пользователя', - help_text='На курсе', - default=None - ) - - story_text_after_course = models.TextField( - verbose_name='История пользователя', - help_text='После курса', - default=None + is_visible = models.BooleanField( + verbose_name="Показывать на главной", + default=True, ) @@ -400,7 +39,7 @@ class GraduateProjects(models.Model): class Meta: verbose_name_plural = 'LearnPython Проекты Учеников' - def __str__(self): + def __str__(self) -> str: return f'Проект "{self.project_name}"' project_name = models.CharField( @@ -418,59 +57,45 @@ def __str__(self): ) -class GraduateProjectsVideos(models.Model): - class Meta: - verbose_name_plural = 'LearnPython Видео проектов Учеников' +class EnrollmentType(enum.StrEnum): + BASE = "BASE" + ADVANCED = "ADVANCED" - def __str__(self): - return f'Проект "{self.project_name}"' + @classmethod + def get_choices(cls) -> list[tuple[str, str]]: + return [(v, v) for v in cls] - project_name = models.CharField( - verbose_name='Название проекта', - max_length=150, - help_text='Название проекта', - default=None - ) - project_url = models.CharField( - verbose_name='Ссылка', - max_length=250, - help_text='Ссылка на Youtube', - default=None - ) +class Enrollment(models.Model): + timepad_event_id = models.CharField(max_length=64, null=True, blank=True) + platim_url = models.CharField(max_length=254, null=True, blank=True) - project_course = models.CharField( - verbose_name='Номер набора', - max_length=5, - help_text='Номер Выпуска', - default=None - ) - - project_description = models.TextField( - verbose_name='Описание проект', - help_text='Краткое описание проекта', - default=None - ) + type = models.CharField(max_length=10, choices=EnrollmentType.get_choices(), null=True, blank=True) + start_date = models.DateField() + end_date = models.DateField() + end_registration_date = models.DateField() + early_price_rub = models.IntegerField() + late_price_rub = models.IntegerField() + early_price_date_to = models.DateField() + late_price_date_from = models.DateField() + @staticmethod + def get_enrollment_with_active_registration(enrollment_type: EnrollmentType) -> "Enrollment | None": + return Enrollment.objects.filter( + type=enrollment_type, + end_registration_date__gte=now(), + ).first() -class Podcasts(models.Model): - - class Meta: - verbose_name_plural = 'LearnPython Подкаст с учеником' + def __str__(self) -> str: + return f"{self.type.capitalize() if self.type else 'NO TYPE'} enrollment ({self.start_date} - {self.end_date})" - def __str__(self): - return f'Проект "{self.podcast_name}"' - podcast_name = models.CharField( - verbose_name='Название подкаста', - max_length=150, - help_text='Название подкаста', - default=None - ) +class CourseReview(models.Model): + title = models.CharField(max_length=254) + text = models.TextField() + review_for = models.CharField(max_length=10, choices=EnrollmentType.get_choices(), null=True, blank=True) + reviewer_name = models.CharField(max_length=254, null=True, blank=True) + reviewed_at = models.DateField() - podcast_url = models.CharField( - verbose_name='Ссылка', - max_length=250, - help_text='Ссылка на Youtube', - default=None - ) + def __str__(self) -> str: + return f'{self.title} ({self.review_for})' diff --git a/landing_page/mainpage/static/css/style.css b/landing_page/mainpage/static/css/style.css index 29353bc2..0334c792 100644 --- a/landing_page/mainpage/static/css/style.css +++ b/landing_page/mainpage/static/css/style.css @@ -402,6 +402,10 @@ h5 { color: #f2c94c; } +.color-purple { + color: #be24f2; +} + .brackets { text-transform: uppercase; color: #333333; @@ -1878,7 +1882,7 @@ ul.who-we-icon { } .how-to-pay-cont { - padding-bottom: 40px; + padding-bottom: 60px; } .how-to-pay p, .how-to-pay ul { @@ -3427,7 +3431,7 @@ ul.installment-plan li:before { } .counter { - padding: 9px 0; + padding: 9px 0 30px; } .price-second-container { @@ -3542,7 +3546,7 @@ ul.installment-plan li:before { } .counter .price-second { - padding: 10px 15px 30px; + padding: 30px 15px 30px; border-radius: 0; margin-right: -15px; margin-left: -15px; @@ -4010,6 +4014,13 @@ ul.installment-plan li:before { } } +#swich_block_online .price-second .price-list { + display: flex; + flex-direction: row; + justify-content: center; + margin: 0 -25px; +} + .price-list { display: flex; flex-direction: row; @@ -4101,11 +4112,23 @@ ul.installment-plan li:before { padding-top: 0; } +#swich_block_online .price-list .price-item { + padding: 20px; + margin: 15px 100px; +} + .price-second .price-list .price-item { padding: 20px; margin: 15px; } +@media (max-width: 1300px){ + .price-second .price-list .price-item { + padding: 20px; + margin: 15px; + } +} + .price-second .price-item.payment-after { border: none; background: #ffffff; @@ -4125,6 +4148,573 @@ ul.installment-plan li:before { border-box: none; background: #ffffff; } +.offline-map{ + padding: 100px 0; +} + +.offline-map.upper{ + padding: 100px 0 0; +} + +.offline-map .pc-section-header{ + padding-bottom: 67px; +} + +.offline-map-wrapper{ + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: stretch; +} + +.offline-map-city-list{ + width: 737px; + height: 826px; + flex-shrink: 0; + background-color: #333333; + display: flex; + flex-direction: row; + justify-content: flex-end; + padding: 44px 25px 0 0; +} + +.city-list{ + list-style: none; + margin: 0; + padding: 0 370px 0 0; +} + +.city-list ::-webkit-scrollbar-thumb { + background: red; + border-radius: 10px; +} + +.simplebar-scrollbar::before { + background-color: rgba(45, 156, 219, 0.6); + border-radius: 12px; + height: 472px; +} + +.city-list li a{ + display: block; + text-decoration: none; + font-family: 'Ubuntu', sans-serif; + font-style: normal; + font-weight: 700; + font-size: 36px; + line-height: 1.2; + text-transform: uppercase; + color: #FFFFFF; + padding: 16px 0; +} + +.city-list li a.active{ + color: #F2C94C; +} + +.city-list li a:hover{ + color: #F2C94C; +} + +@media (max-width: 1600px){ + .offline-map-city-list{ + width: 400px; + } + + .city-list{ + padding: 0 280px 0 0; + } + + .city-list li a{ + font-size: 27px; + padding: 13px 0; + } +} + +@media (max-width: 1160px){ + .offline-map-wrapper{ + display: block; + } + + .offline-map-city-list{ + width: 100%; + height: auto; + flex-shrink: unset; + background-color: #333333; + display: flex; + flex-direction: row; + justify-content: flex-start; + padding: 30px 25px; + } + + .city-list{ + padding: 0; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: flex-start; + } + + .city-list li a{ + width: max-content; + padding: 13px; + } + + + #swich_block_online .price-list .price-item { + margin: 15px; + } +} + +@media (max-width: 992px) { + .counter .pc-section-header { + padding-bottom: 0px; + } + + .counter .pc-section-header h2 { + text-align: left; + text-transform: uppercase; + font-size: 1.4rem; + color: #333333; + } + + .offline-map { + padding: 0px; + } + + .offline-map.upper { + padding: 0px; + } + + .offline-map .pc-section-header h2 { + text-align: left; + text-transform: uppercase; + font-size: 1.4rem; + color: #333333; + margin-bottom: 32px; + } + + .offline-map .pc-section-header h2 br{ + display: none; + } + + .offline-map .pc-section-header { + padding-bottom: 0; + } + + .city-list li a { + font-size: 23px; + } +} + +@media (max-width: 900px){ + #swich_block_online .price-second .price-list{ + flex-direction: column; + } +} + +@media (max-width: 600px){ + .offline-map-city-list{ + padding: 15px; + } + + .city-list{ + justify-content: center; + } + + .city-list li a{ + padding: 10px; + font-size: 16px; + } +} + +.offline_city_content_wrapper h3{ + font-family: 'Ubuntu', sans-serif; + font-style: normal; + font-weight: 700; + font-size: 32px; + line-height: 1; + text-transform: uppercase; + color: #333333; + margin: 0 0 34px; +} + +.offline_city_content_list{ + display: flex; + flex-direction: row; + justify-content: space-between; + margin: 0 -15px; +} + +.offline_city_content_item{ + width: calc(100%/2 - 30px); + margin: 0 15px; +} + +.offline_city_content_item button{ + width: 100%; + height: 46px; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + background-color: #2D9CDB; + font-family: 'Ubuntu', sans-serif; + font-style: normal; + font-weight: 700; + font-size: 16px; + line-height: 1; + text-align: center; + color: #FFFFFF; + border-radius: 54px; + border: none; + outline: none; + cursor: pointer; + transition: box-shadow 0.2s; +} + +.offline_city_content_item button:hover{ + box-shadow: 0 3px 0 #2e729e; +} + +.offline_city_content_item button:disabled{ + background-color: #D3D3D3; + color: #7B7B7B; + cursor: auto; +} + +.offline_city_content_item button:disabled:hover{ + box-shadow: none; +} + +.offline_city_content_item .title{ + font-family: 'Ubuntu', sans-serif; + font-style: normal; + font-weight: 700; + font-size: 16px; + line-height: 18px; + color: #333333; + margin: 0 0 5px; +} + +.offline_city_content_item .offline_date{ + font-family: 'Ubuntu', sans-serif; + font-style: normal; + font-weight: 400; + font-size: 16px; + line-height: 18px; + color: #333333; + margin: 0 0 10px; +} + +.offline_city_content_item .offline_price{ + font-family: 'Ubuntu', sans-serif; + font-style: normal; + font-weight: 700; + font-size: 28px; + line-height: 32px; + color: #333333; + margin: 0 0 6px; +} + +.offline_city_content_item .decr-price{ + text-decoration: none; + display: block; + font-family: 'Ubuntu', sans-serif; + font-style: normal; + font-weight: 400; + font-size: 16px; + line-height: 18px; + color: #333333; + margin: 0 0 33px; +} + +#offline-map { + width: 100%; + height: 826px; +} + +#upper-offline-map { + width: 100%; + height: 826px; +} + +@media (max-width: 992px) { + + #offline-map{ + height: 560px; + margin-bottom: 30px; + } + + #upper-offline-map{ + height: 560px; + margin-bottom: 30px; + } +} + +@media (max-width: 760px){ + #offline-map{ + height: 400px; + } + + #upper-offline-map{ + height: 400px; + } + + .offline_city_content_wrapper h3{ + font-size: 20px; + margin: 0 0 24px; + } + + .offline_city_content_list{ + margin: 0 -10px; + } + + .offline_city_content_item{ + width: calc(100%/2 - 20px); + margin: 0 10px; + } + + .offline_city_content_item button{ + width: 100%; + height: 40px; + font-size: 12px; + border-radius: 54px; + } + + .offline_city_content_item .title{ + font-size: 14px; + line-height: 1.2; + margin: 0 0 5px; + } + + .offline_city_content_item .offline_date{ + font-size: 14px; + line-height: 1.2; + } + + .offline_city_content_item .offline_price{ + font-size: 20px; + line-height: 1.2; + margin: 0 0 25px; + } + + .offline_city_content_item .decr-price{ + font-size: 14px; + line-height: 1.2; + margin: 0 0 25px; + } + + +} + +@media (max-width: 500px){ + #offline-map{ + height: 400px; + } + + #upper-offline-map{ + height: 400px; + } + + .offline_city_content_wrapper h3{ + font-size: 18px; + margin: 0 0 24px; + } + + .offline_city_content_list{ + margin: 0 -5px; + } + + .offline_city_content_item{ + width: calc(100%/2 - 10px); + margin: 0 5px; + } + + .offline_city_content_item button{ + width: 100%; + height: 34px; + font-size: 11px; + border-radius: 54px; + } + + .offline_city_content_item .title{ + font-size: 13px; + line-height: 1.2; + margin: 0 0 5px; + } + + .offline_city_content_item .offline_date{ + font-size: 14px; + line-height: 1.2; + } + + .offline_city_content_item .offline_price{ + font-size: 18px; + line-height: 1.2; + margin: 0 0 25px; + } + + .offline_city_content_item .decr-price{ + font-size: 14px; + line-height: 1.2; + margin: 0 0 25px; + } + + +} + +@media (max-width: 400px){ + #offline-map{ + height: 550px; + } + + #upper-offline-map{ + height: 550px; + } + + .offline_city_content_wrapper h3{ + font-size: 16px; + margin: 0 0 25px; + } + + .offline_city_content_list{ + flex-direction: column; + margin: 0; + } + + .offline_city_content_item{ + width: 100%; + } + + .offline_city_content_item + .offline_city_content_item{ + margin-top: 20px; + } + + .offline_city_content_item button{ + width: 100%; + height: 34px; + font-size: 11px; + border-radius: 54px; + } + + .offline_city_content_item .title{ + font-size: 13px; + line-height: 1.2; + margin: 0 0 5px; + } + + .offline_city_content_item .offline_date{ + font-size: 14px; + line-height: 1.2; + } + + .offline_city_content_item .offline_price{ + font-size: 18px; + line-height: 1.2; + margin: 0 0 15px; + } + + .offline_city_content_item .decr-price{ + font-size: 14px; + line-height: 1.2; + margin: 0 0 25px; + } + + +} + +#offline-map .ymaps-2-1-78-balloon, +#offline-map .ymaps-2-1-78-balloon *, +#offline-map .ymaps-2-1-78-balloon *:before, +#offline-map .ymaps-2-1-78-balloon *:after { + box-sizing: border-box !important; +} + +#upper-offline-map .ymaps-2-1-78-balloon, +#upper-offline-map .ymaps-2-1-78-balloon *, +#upper-offline-map .ymaps-2-1-78-balloon *:before, +#upper-offline-map .ymaps-2-1-78-balloon *:after { + box-sizing: border-box !important; +} + +#offline-map .ymaps-2-1-78-balloon__tail:after{ + display: none; +} + +#upper-offline-map .ymaps-2-1-78-balloon__tail:after{ + display: none; +} + +#offline-map .ymaps-2-1-78-balloon__close+.ymaps-2-1-78-balloon__content{ + padding: 0 !important; +} + +#upper-offline-map .ymaps-2-1-78-balloon__close+.ymaps-2-1-78-balloon__content{ + padding: 0 !important; +} + +#offline-map .ymaps-2-1-78-balloon__content { + padding: 0 !important; +} + +#upper-offline-map .ymaps-2-1-78-balloon__content { + padding: 0 !important; +} + +#offline-map .ymaps-2-1-78-balloon__content > ymaps[id] { + /*width: 100% !important;*/ + /*height: 100% !important;*/ + /*overflow: visible !important;*/ + padding: 30px; +} + +#upper-offline-map .ymaps-2-1-78-balloon__content > ymaps[id] { + /*width: 100% !important;*/ + /*height: 100% !important;*/ + /*overflow: visible !important;*/ + padding: 30px; +} + +#offline-map .ymaps-2-1-78-balloon__layout{ + width: 100%; + height: 100%; +} + +#upper-offline-map .ymaps-2-1-78-balloon__layout{ + width: 100%; + height: 100%; +} + +@media (max-width: 760px){ + #offline-map .ymaps-2-1-78-balloon__content > ymaps[id] { + padding: 20px; + } + + #upper-offline-map .ymaps-2-1-78-balloon__content > ymaps[id] { + padding: 20px; + } +} + +@media (max-width: 500px){ + #offline-map .ymaps-2-1-78-balloon__content > ymaps[id] { + padding: 12px; + } + + #upper-offline-map .ymaps-2-1-78-balloon__content > ymaps[id] { + padding: 12px; + } +} + +@media (max-width: 400px){ + #offline-map .ymaps-2-1-78-balloon__content > ymaps[id] { + padding: 17px; + } + + #upper-offline-map .ymaps-2-1-78-balloon__content > ymaps[id] { + padding: 17px; + } +} @media (max-width: 992px) { .labor-program .schedule-item h4 { @@ -4172,3 +4762,8 @@ ul.installment-plan li:before { text-transform: none; } +.advanced-promo { + background: #252526; + color: #ffffff; + padding: 15px; +} diff --git a/landing_page/mainpage/static/images/advanced/technologies.png b/landing_page/mainpage/static/images/advanced/technologies.png new file mode 100644 index 00000000..623de434 Binary files /dev/null and b/landing_page/mainpage/static/images/advanced/technologies.png differ diff --git a/landing_page/mainpage/static/images/blue-mark.svg b/landing_page/mainpage/static/images/blue-mark.svg new file mode 100644 index 00000000..b483a9e3 --- /dev/null +++ b/landing_page/mainpage/static/images/blue-mark.svg @@ -0,0 +1,3 @@ + + + diff --git a/landing_page/mainpage/static/images/favicon_lp_120.png b/landing_page/mainpage/static/images/favicon_lp_120.png new file mode 100644 index 00000000..98e5ab00 Binary files /dev/null and b/landing_page/mainpage/static/images/favicon_lp_120.png differ diff --git a/landing_page/mainpage/static/images/favicon_lp_16.png b/landing_page/mainpage/static/images/favicon_lp_16.png new file mode 100644 index 00000000..eb69d5b4 Binary files /dev/null and b/landing_page/mainpage/static/images/favicon_lp_16.png differ diff --git a/landing_page/mainpage/static/images/favicon_lpa_120.png b/landing_page/mainpage/static/images/favicon_lpa_120.png new file mode 100644 index 00000000..e1f001fc Binary files /dev/null and b/landing_page/mainpage/static/images/favicon_lpa_120.png differ diff --git a/landing_page/mainpage/static/images/favicon_lpa_16.png b/landing_page/mainpage/static/images/favicon_lpa_16.png new file mode 100644 index 00000000..23765864 Binary files /dev/null and b/landing_page/mainpage/static/images/favicon_lpa_16.png differ diff --git a/landing_page/mainpage/static/images/mentor/teacher/lebedev.jpg b/landing_page/mainpage/static/images/mentor/teacher/lebedev.jpg new file mode 100644 index 00000000..8d3d15a4 Binary files /dev/null and b/landing_page/mainpage/static/images/mentor/teacher/lebedev.jpg differ diff --git a/landing_page/mainpage/static/images/yellow-mark.svg b/landing_page/mainpage/static/images/yellow-mark.svg new file mode 100644 index 00000000..260794a2 --- /dev/null +++ b/landing_page/mainpage/static/images/yellow-mark.svg @@ -0,0 +1,3 @@ + + + diff --git a/landing_page/mainpage/static/js/main.js b/landing_page/mainpage/static/js/main.js index b72b445f..e849e5b7 100644 --- a/landing_page/mainpage/static/js/main.js +++ b/landing_page/mainpage/static/js/main.js @@ -15,6 +15,7 @@ class LearnPython { this.initScrollupbut(); this.fixHeader(); this.initSlider(); + // this.initSimpleBar(); this.initOnlineSlider(); this.mobiTimetableInit(); this.initTeacherSlider(); @@ -80,6 +81,16 @@ class LearnPython { }); } + initSimpleBar() { + if (window.matchMedia('(min-width: 1160px)').matches){ + new SimpleBar(document.getElementById('city-simplebar')); + } + + if (window.matchMedia('(min-width: 1160px)').matches){ + new SimpleBar(document.getElementById('upper-city-simplebar')); + } + } + initOnlineSlider() { if (!this.timetableSlider) { this.timetableSlider = $('.online-timetable, .offline-timetable').slick({ diff --git a/landing_page/mainpage/static/js/widgets/advance_widget.js b/landing_page/mainpage/static/js/widgets/advance_widget.js new file mode 100644 index 00000000..bb35fffe --- /dev/null +++ b/landing_page/mainpage/static/js/widgets/advance_widget.js @@ -0,0 +1,69 @@ +document.addEventListener("DOMContentLoaded", function() { + var script = document.getElementById('f5be3bcced9b8d99a4cc095aadcc152acc1c0aa02'); + + var par = script.parentNode; + script.parentNode.style.overflow = 'hidden'; + + var iframe = document.createElement('iframe'); + iframe.src = 'https://learnpythonru.getcourse.ru/pl/lite/widget/widget' + + "?" + window.location.search.substring(1) + + "&id=1151238" + + "&ref=" + encodeURIComponent(document.referrer) + + "&loc=" + encodeURIComponent(document.location.href); + iframe.style.width = '100%'; + iframe.style.height = '0px'; + iframe.style.border = 'none'; + iframe.style.overflow = 'hidden'; + iframe.setAttribute('allowfullscreen', 'allowfullscreen'); + iframe.className = '511'; + iframe.id = 'c6b45c08ef83f13b64a41560165b2147de61cf2a' + '_' + iframe.className; + // name можно получить изнутри iframe + iframe.name = iframe.className; + + var iframeId = iframe.id; + + var gcEmbedOnMessage = function(e) { + var insertedIframe = document.getElementById(iframeId); + if (!insertedIframe) { + return; + } + + if (e.data.uniqName == 'f5be3bcced9b8d99a4cc095aadcc152acc1c0aa0') { + if (e.data.height) { + if (e.data.iframeName) { + if (e.data.iframeName == iframe.name) { + par.style.height = ( e.data.height ) + "px"; + insertedIframe.style.height = (e.data.height) + "px"; + } + } else { + par.style.height = ( e.data.height ) + "px"; + insertedIframe.style.height = (e.data.height) + "px"; + } + } + } + }; + + if (window.addEventListener) { + window.addEventListener("message", gcEmbedOnMessage, false); + } else if (window.attachEvent) { + window.attachEvent('onmessage', gcEmbedOnMessage) + } else { + window['onmessage'] = gcEmbedOnMessage + } + + script.parentNode.insertBefore(iframe, script); + par.removeChild( script ) +}); + +var getLocation = function(href) { + var l = document.createElement("a"); + l.href = href; + return l; +}; + +var currentScript = document.currentScript || (function() { + var scripts = document.getElementsByTagName('script'); + return scripts[scripts.length - 1]; +})(); + +var domain = ( (getLocation( currentScript.src )).hostname ); \ No newline at end of file diff --git a/landing_page/mainpage/static/js/widgets/advance_widget_bottom.js b/landing_page/mainpage/static/js/widgets/advance_widget_bottom.js new file mode 100644 index 00000000..d40b05d8 --- /dev/null +++ b/landing_page/mainpage/static/js/widgets/advance_widget_bottom.js @@ -0,0 +1,69 @@ +document.addEventListener("DOMContentLoaded", function() { + var script = document.getElementById('f5be3bcced9b8d99a4cc095aadcc152acc1c0aa0'); + + var par = script.parentNode; + script.parentNode.style.overflow = 'hidden'; + + var iframe = document.createElement('iframe'); + iframe.src = 'https://learnpythonru.getcourse.ru/pl/lite/widget/widget' + + "?" + window.location.search.substring(1) + + "&id=1151238" + + "&ref=" + encodeURIComponent(document.referrer) + + "&loc=" + encodeURIComponent(document.location.href); + iframe.style.width = '100%'; + iframe.style.height = '0px'; + iframe.style.border = 'none'; + iframe.style.overflow = 'hidden'; + iframe.setAttribute('allowfullscreen', 'allowfullscreen'); + iframe.className = '51'; + iframe.id = 'c6b45c08ef83f13b64a41560165b2147de61cf2a' + '_' + iframe.className; + // name можно получить изнутри iframe + iframe.name = iframe.className; + + var iframeId = iframe.id; + + var gcEmbedOnMessage = function(e) { + var insertedIframe = document.getElementById(iframeId); + if (!insertedIframe) { + return; + } + + if (e.data.uniqName == 'f5be3bcced9b8d99a4cc095aadcc152acc1c0aa0') { + if (e.data.height) { + if (e.data.iframeName) { + if (e.data.iframeName == iframe.name) { + par.style.height = ( e.data.height ) + "px"; + insertedIframe.style.height = (e.data.height) + "px"; + } + } else { + par.style.height = ( e.data.height ) + "px"; + insertedIframe.style.height = (e.data.height) + "px"; + } + } + } + }; + + if (window.addEventListener) { + window.addEventListener("message", gcEmbedOnMessage, false); + } else if (window.attachEvent) { + window.attachEvent('onmessage', gcEmbedOnMessage) + } else { + window['onmessage'] = gcEmbedOnMessage + } + + script.parentNode.insertBefore(iframe, script); + par.removeChild( script ) +}); + +var getLocation = function(href) { + var l = document.createElement("a"); + l.href = href; + return l; +}; + +var currentScript = document.currentScript || (function() { + var scripts = document.getElementsByTagName('script'); + return scripts[scripts.length - 1]; +})(); + +var domain = ( (getLocation( currentScript.src )).hostname ); \ No newline at end of file diff --git a/landing_page/mainpage/static/js/widgets/widget.js b/landing_page/mainpage/static/js/widgets/widget.js new file mode 100644 index 00000000..cbe5de5f --- /dev/null +++ b/landing_page/mainpage/static/js/widgets/widget.js @@ -0,0 +1,71 @@ +console.log('in file') + +document.addEventListener("DOMContentLoaded", function() { + var script = document.getElementById('3e6321862d90cf3a72c6198927067b35e82b0b822'); + // var script = document.currentScript; + var par = script.parentNode; + script.parentNode.style.overflow = 'hidden'; + + var iframe = document.createElement('iframe'); + iframe.src = 'https://learnpythonru.getcourse.ru/pl/lite/widget/widget' + + "?" + window.location.search.substring(1) + + "&id=1139675" + + "&ref=" + encodeURIComponent(document.referrer) + + "&loc=" + encodeURIComponent(document.location.href); + iframe.style.width = '100%'; + iframe.style.height = '0px'; + iframe.style.border = 'none'; + iframe.style.overflow = 'hidden'; + iframe.setAttribute('allowfullscreen', 'allowfullscreen'); + iframe.className = '941'; + iframe.id = 'cc9d55a8a649fc463bf0a580d1c0567f6542b153' + '_' + iframe.className; + // name можно получить изнутри iframe + iframe.name = iframe.className; + + var iframeId = iframe.id; + + var gcEmbedOnMessage = function(e) { + var insertedIframe = document.getElementById(iframeId); + if (!insertedIframe) { + return; + } + + if (e.data.uniqName == '3e6321862d90cf3a72c6198927067b35e82b0b82') { + if (e.data.height) { + if (e.data.iframeName) { + if (e.data.iframeName == iframe.name) { + par.style.height = ( e.data.height ) + "px"; + insertedIframe.style.height = (e.data.height) + "px"; + } + } else { + par.style.height = ( e.data.height ) + "px"; + insertedIframe.style.height = (e.data.height) + "px"; + } + } + } + }; + + if (window.addEventListener) { + window.addEventListener("message", gcEmbedOnMessage, false); + } else if (window.attachEvent) { + window.attachEvent('onmessage', gcEmbedOnMessage) + } else { + window['onmessage'] = gcEmbedOnMessage + } + + script.parentNode.insertBefore(iframe, script); + par.removeChild( script ) +}); + +var getLocation = function(href) { + var l = document.createElement("a"); + l.href = href; + return l; +}; + +var currentScript = document.currentScript || (function() { + var scripts = document.getElementsByTagName('script'); + return scripts[scripts.length - 1]; +})(); + +var domain = ( (getLocation( currentScript.src )).hostname ); \ No newline at end of file diff --git a/landing_page/mainpage/static/js/widgets/widget_bottom.js b/landing_page/mainpage/static/js/widgets/widget_bottom.js new file mode 100644 index 00000000..49df1eb5 --- /dev/null +++ b/landing_page/mainpage/static/js/widgets/widget_bottom.js @@ -0,0 +1,71 @@ +console.log('in file') + +document.addEventListener("DOMContentLoaded", function() { + var script = document.getElementById('3e6321862d90cf3a72c6198927067b35e82b0b82'); + // var script = document.currentScript; + var par = script.parentNode; + script.parentNode.style.overflow = 'hidden'; + + var iframe = document.createElement('iframe'); + iframe.src = 'https://learnpythonru.getcourse.ru/pl/lite/widget/widget' + + "?" + window.location.search.substring(1) + + "&id=1139675" + + "&ref=" + encodeURIComponent(document.referrer) + + "&loc=" + encodeURIComponent(document.location.href); + iframe.style.width = '100%'; + iframe.style.height = '0px'; + iframe.style.border = 'none'; + iframe.style.overflow = 'hidden'; + iframe.setAttribute('allowfullscreen', 'allowfullscreen'); + iframe.className = '940'; + iframe.id = 'cc9d55a8a649fc463bf0a580d1c0567f6542b153' + '_' + iframe.className; + // name можно получить изнутри iframe + iframe.name = iframe.className; + + var iframeId = iframe.id; + + var gcEmbedOnMessage = function(e) { + var insertedIframe = document.getElementById(iframeId); + if (!insertedIframe) { + return; + } + + if (e.data.uniqName == '3e6321862d90cf3a72c6198927067b35e82b0b82') { + if (e.data.height) { + if (e.data.iframeName) { + if (e.data.iframeName == iframe.name) { + par.style.height = ( e.data.height ) + "px"; + insertedIframe.style.height = (e.data.height) + "px"; + } + } else { + par.style.height = ( e.data.height ) + "px"; + insertedIframe.style.height = (e.data.height) + "px"; + } + } + } + }; + + if (window.addEventListener) { + window.addEventListener("message", gcEmbedOnMessage, false); + } else if (window.attachEvent) { + window.attachEvent('onmessage', gcEmbedOnMessage) + } else { + window['onmessage'] = gcEmbedOnMessage + } + + script.parentNode.insertBefore(iframe, script); + par.removeChild( script ) +}); + +var getLocation = function(href) { + var l = document.createElement("a"); + l.href = href; + return l; +}; + +var currentScript = document.currentScript || (function() { + var scripts = document.getElementsByTagName('script'); + return scripts[scripts.length - 1]; +})(); + +var domain = ( (getLocation( currentScript.src )).hostname ); \ No newline at end of file diff --git a/landing_page/mainpage/static/js/yandex_map.js b/landing_page/mainpage/static/js/yandex_map.js index 20b4c0d2..b81eb15a 100644 --- a/landing_page/mainpage/static/js/yandex_map.js +++ b/landing_page/mainpage/static/js/yandex_map.js @@ -1,42 +1,226 @@ -ymaps.ready(function () { - var myMap = new ymaps.Map('map', { - center: [55.781774, 37.669752], - zoom: 17 - }, { - searchControlProvider: 'yandex#search' - }), - - // Создаём макет содержимого. - - myPlacemarkWithContent = new ymaps.Placemark([55.781774, 37.669752], { - hintContent: 'Москва, Русаковская ул., 1', - balloonContent: ' ' + - '
' + - 'LEARN ' + - 'PYTHON' + - ' ' + - '

Москва, Русаковская ул., 1 станция метро «Красносельская»

' + - '
', - - }, { - // Опции. - // Необходимо указать данный тип макета. - iconLayout: 'default#imageWithContent', - // Своё изображение иконки метки. - iconImageHref: 'static/images/icon-svg/pin.svg', - hideIconOnBalloonOpen: false, - // Размеры метки. - iconImageSize: [60, 84], - // Смещение левого верхнего угла иконки относительно - // её "ножки" (точки привязки). - iconImageOffset: [-30, -84], - }); - - myMap.geoObjects - - .add(myPlacemarkWithContent); +jQuery(function() { + // use strict + + luxon.Settings.defaultLocale = 'ru-RU'; + + let today = new Date(); + + ymaps.ready(function() { + // Offline map + (() => { + + function InitOfflinemap (id) { + let $offline_cities = jQuery('#offline-cities-json'), + offline_cities = [], + balloon_width = 570, + balloon_height = 317, + balloon_offset = [30, 340], + icon_inactive_sizes = [28, 40], + icon_active_sizes = [42, 60], + panMargin = 150; + + if (window.matchMedia('(max-width: 760px)').matches){ + balloon_width = 400; + balloon_height = 220; + balloon_offset = [30, 240]; + icon_inactive_sizes = [20, 32]; + icon_active_sizes = [30, 48]; + panMargin = [110, 70]; + } + + if (window.matchMedia('(max-width: 500px)').matches){ + balloon_width = 350; + balloon_height = 198; + balloon_offset = [30, 220]; + icon_inactive_sizes = [20, 32]; + icon_active_sizes = [30, 48]; + panMargin = [110, 50]; + } + + if (window.matchMedia('(max-width: 400px)').matches){ + balloon_width = 250; + balloon_height = 327; + balloon_offset = [30, 348]; + icon_inactive_sizes = [20, 32]; + icon_active_sizes = [30, 48]; + panMargin = [70, 50]; + } + + + let icon_inactive_offset = [ + -1 * icon_inactive_sizes[0] / 2, + -1 * icon_inactive_sizes[1] + ], + icon_active_offset = [ + -1 * icon_active_sizes[0] / 2, + -1 * icon_active_sizes[1] + ]; + + try { + offline_cities = JSON.parse($offline_cities.html() || '[]'); + } catch (err) { + console.error(err); + } + + if (Array.isArray(offline_cities) === false || offline_cities.length === 0) { + return; + } + let map = new ymaps.Map(id, { + center: [ + offline_cities[0].coords[0] - 2, + offline_cities[0].coords[1] + 5 + ], + zoom: 6 + }, { + searchControlProvider: 'yandex#search' + }); + for (let city of offline_cities) { + let early_date = parse_date(city.early_date), + basic_date = parse_date(city.basic_date), + placemark = new ymaps.Placemark(city.coords, { + balloonContent: ` +
+

${city.name}

+
+
+

Ранняя регистрация

+

До ${format_date(city.early_date)}

+

${format_price(city.early_price)} ₽

+ В рассрочку от ${format_price(city.early_installment_price)} р/мес + +
+
+

Обычная регистрация

+

С ${format_date(city.basic_date)}

+

${format_price(city.basic_price)} ₽

+ В рассрочку от ${format_price(city.basic_installment_price)} р/мес + +
+
+
` + }, { + // Опции. + // Необходимо указать данный тип макета. + iconLayout: 'default#imageWithContent', + // Своё изображение иконки метки. + iconImageHref: 'static/images/yellow-mark.svg', + hideIconOnBalloonOpen: false, + // Размеры метки. + iconImageSize: icon_inactive_sizes, + // Смещение левого верхнего угла иконки относительно + // её "ножки" (точки привязки). + iconImageOffset: icon_inactive_offset, + + balloonOffset: balloon_offset, + balloonMaxWidth: balloon_width, + balloonMinWidth: balloon_width, + balloonMaxHeight: balloon_height, + balloonMinHeight: balloon_height, + balloonCloseButton: false, + balloonAutoPanMargin: panMargin + }); + + city.placemark = placemark; + + placemark.events.add('balloonopen', function() { + activateCityByName(city.name); + + placemark.options.set('iconImageHref', 'static/images/blue-mark.svg'); + placemark.options.set('iconImageSize', icon_active_sizes); + placemark.options.set('iconImageOffset', icon_active_offset); + }); + + placemark.events.add('balloonclose', function() { + placemark.options.set('iconImageHref', 'static/images/yellow-mark.svg'); + placemark.options.set('iconImageSize', icon_inactive_sizes); + placemark.options.set('iconImageOffset', icon_inactive_offset); + }); + + map.geoObjects.add(placemark); + } + + activateCityByName(offline_cities[0].name); + + jQuery(document).on('click', '[data-offline-city-name]', function(e) { + e.preventDefault(); + + let $this = jQuery(this); + + activateCityByName($this.data('offline-city-name')); + }); + + function activateCityByName(city_name) { + jQuery('[data-offline-city-name]').removeClass('active'); + jQuery(`[data-offline-city-name="${city_name}"]`).addClass('active'); + + let city = offline_cities.find(({name}) => name === city_name); + + city?.placemark?.balloon.open(); + } + } + + //InitOfflinemap('offline-map'); + //InitOfflinemap('upper-offline-map'); + })(); + + // Footer map + (() => { + let map = new ymaps.Map('map', { + center: [55.755988, 37.643448], + zoom: 16 + }, { + searchControlProvider: 'yandex#search' + }); + + map.geoObjects.add(new ymaps.Placemark([55.755988, 37.643448], { + hintContent: 'Хохловский пер., 7-9с2, подъезд 3, этаж 1', + balloonContent: ' ' + + '
' + + 'LEARN ' + + 'PYTHON' + + ' ' + + '

Хохловский пер., 7-9с2, подъезд 3, этаж 1, станция метро «Китай-город»

' + + '
' + + }, { + // Опции. + // Необходимо указать данный тип макета. + iconLayout: 'default#imageWithContent', + // Своё изображение иконки метки. + iconImageHref: 'static/images/icon-svg/pin.svg', + hideIconOnBalloonOpen: false, + // Размеры метки. + iconImageSize: [60, 84], + // Смещение левого верхнего угла иконки относительно + // её "ножки" (точки привязки). + iconImageOffset: [-30, -84] + })); + })(); + }); + + function format_price(price) { + return new Intl.NumberFormat('ru-RU').format(price); + } + + function parse_date(date) { + return luxon.DateTime.fromISO(date); + } + + function format_date(date) { + return parse_date(date).toLocaleString({month: 'long', day: 'numeric'}); + } + + function format_date_with_year(date) { + return parse_date(date).toLocaleString(luxon.DateTime.DATE_FULL); + } }); diff --git a/landing_page/mainpage/static/robots.txt b/landing_page/mainpage/static/robots.txt index 6d286774..a68ba7bc 100644 --- a/landing_page/mainpage/static/robots.txt +++ b/landing_page/mainpage/static/robots.txt @@ -1,3 +1,6 @@ User-agent: * -Disallow: *lessons* -Host: https://learn.python.ru/ \ No newline at end of file +Disallow: /admin/ +Disallow: /success/ +Disallow: /lessons/ +Sitemap: http://learn.python.ru/sitemap.xml +Clean-param: utm&ref&source&trk&&5030bbc2&calltouch_tm&etext diff --git a/landing_page/mainpage/templates/mainpage/advanced.html b/landing_page/mainpage/templates/mainpage/advanced.html new file mode 100644 index 00000000..68d0a6c7 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/advanced.html @@ -0,0 +1,191 @@ +{% load static %} +{% load l10n %} + + + + + + + + + + + + + + + + + + + + Learn Python Advanced - продвинутые курсы Python онлайн + + {% include 'mainpage/include/common/counters.html' %} + + + +
+ {% include 'mainpage/include/advanced/index_menu.html' %} +
+ +
+ {% include 'mainpage/include/advanced/index_hero.html' with enrollment=enrollment registration_closes_date_formatted=registration_closes_date_formatted %} +
+ {% include 'mainpage/include/advanced/index_about_video.html' %} + +
+
+
+

Что дает этот курс

+
+
+ +
+ {% include 'mainpage/include/advanced/index_course_details_mobile.html' %} + {% include 'mainpage/include/advanced/index_course_details_desktop.html' %} +
+
+ +
+
+
+
+
+

После курса у вас останутся

+

+ Новые навыки, связи и полезные материалы. +

+
+
+
+
+
+
+ Доступ ко всем видеолекциям и презентациям +
+
+

Доступ ко всем видеолекциям и презентациям.

+
+
+
+
+ Сертификат о прохождении курса +
+
+

Сертификат о прохождении курса.

+
+
+
+
+ Чат со всеми преподавателями, кураторами и однокурсниками +
+
+

Чат со всеми преподавателями, кураторами и однокурсниками.

+
+
+
+
+ Портфолио выполненных проектов на GitHub +
+
+

Портфолио выполненных проектов на GitHub.

+
+
+
+
+ Опыт работы с Django, DRF, pytest, docker, mypy, github Actions, API популярных сервисов, другими технологиями +
+
+

+ Опыт работы с Django, DRF, pytest, docker, mypy, github Actions, + API популярных сервисов, другими технологиями. +

+
+
+
+
+
+ +
+
+ + {% include 'mainpage/include/advanced/index_modules.html' %} + + {% include 'mainpage/include/advanced/index_authors.html' with curators_list=curators_list%} + +
+
+
+

Стоимость

+
+
+
+
+

Записаться

+
+
+
+
+
+
+

онлайн курс

+
+
+ +
+
+
+
+
+
+
+ + {% if reviews %} + {% include 'mainpage/include/common/index_reviews.html' with reviews=reviews %} + {% endif %} + + {% if enrollment.platim_url %} + {% include 'mainpage/include/index_how_to_pay_by_platim.html' %} + {% else %} + {% include 'mainpage/include/advanced/index_how_to_pay.html' %} + {% endif %} + + {% include 'mainpage/include/advanced/index_footer.html' %} + +
+ + {% include 'mainpage/include/advanced/index_register_popup.html' with enrollment=enrollment %} + + + + + + + + + + + diff --git a/landing_page/mainpage/templates/mainpage/include/advanced/index_about_video.html b/landing_page/mainpage/templates/mainpage/include/advanced/index_about_video.html new file mode 100644 index 00000000..4bf49abd --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/advanced/index_about_video.html @@ -0,0 +1,7 @@ +
+
+
+

О курсе

+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/advanced/index_authors.html b/landing_page/mainpage/templates/mainpage/include/advanced/index_authors.html new file mode 100644 index 00000000..432a3891 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/advanced/index_authors.html @@ -0,0 +1,122 @@ +{% load static %} + +
+
+
+

Курс ведут

+
+
+
+
+
+

Кто делает курс

+
+
+
+
+
+

Кто мы

+
+
+
+
+
+
+
+

Курс организует сообщество MoscowPython

+

+ C 2012 года проводим митапы Python-разработчиков на площадках Яндекса, ЦИАН, Мегафона, + Mail.ru Group и других компаний. Записываем видео и подкаст для питонистов. + Объединили свыше 5К человек в единое комьюнити. +

+
    +
  • +
    + 3 year +
    +
    +

    5+ лет

    + основному курсу +
    +
  • +
  • +
    + 11 sets +
    +
    +

    30+ наборов

    + прошли обучение +
    +
  • +
  • +
    + 400 graduates +
    +
    +

    1500+

    + выпускников +
    +
  • +
+
+
+
+
+
+ +
+
+
+

Основные преподаватели и организаторы

+
+
+
+
+

Основные преподаватели и организаторы

+

+ Практики с большим опытом в разработке и управлении проектами. Разработали сам курс. + Ведут занятия. Записывают видеолекции. Помогают со сложными задачами. Отвечают за + организационные и административные вопросы. +

+
+
+
    +
  • +
    +
    + Валентин Домбровский
    +
    +

    Валентин Домбровский

    +

    + Евангелист и сооснователь MoscowPython сообщества. +

    +

    + Сооснователь IT-рекрутингового агентства Geekfactor.io. +

    +
    +
    + Евангелист и сооснователь MoscowPython сообщества. Сооснователь IT-рекрутингового агентства Geekfactor.io. +
    +
    +
  • +
  • +
    +
    + Семен Осипов +
    +
    +

    Илья Лебедев

    +

    + Евангелист MoscowPython, разработчик в ANNA Money, до этого – технический директор + в Zipsale и BestDoctor. +

    +
    +
    + Программирует и преподаёт Python больше 10 лет. +
    +
    +
  • +
+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/advanced/index_course_details_desktop.html b/landing_page/mainpage/templates/mainpage/include/advanced/index_course_details_desktop.html new file mode 100644 index 00000000..12e5609c --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/advanced/index_course_details_desktop.html @@ -0,0 +1,158 @@ +{% load static %} + +
+
+
+

Кому подойдет

+

+ Мы учли интересы всех: и новичков, и продолжающих. +

+

В результате вы можете создавать:

+
+
+
+
+
+
+
+
    +
  • + + Вы прошли основной курс Learn Python + и хотите продолжить обучение. + +
  • +
  • + + Вы хотите автоматизировать свою работу + в управлении проектами, тестировании, маркетинге, SEO, системном администрировании и т.д. + +
  • +
  • + + Вы уже учили основы Python + до этого, но где-то «застряли». + +
  • +
  • + + Вы хотите сменить работу + или попасть в ИТ. + +
  • +
  • + + Вы учите Python как второй язык + программирования, ведь это лучший второй ЯП для любых задач. + +
  • +
  • + + У вас получается писать код, + но выглядит он так, что стыдно показать другим. + +
  • +
+

+ Курс подстроится под вас. + Поэтому мы собираем небольшие группы, а куратор следит за вашим прогрессом в течение всей недели. + Если вы забуксуете, он объяснит тему дополнительно. А если будете опережать других, вам + придумают задачек — скучно не будет! +

+
+
+
+
+
+ +
+
+
+

Что вас ждет

+

+ Вы сразу получите результат и поймете, куда расти. И так каждую неделю. +

+
+
+
+
+
+
+
+ 80+ часов практики +
+
+

Практика с первого дня

+

от 80 часов с преподавателем и самостоятельно

+
+
+
+
+
+
+ 30 часов видеоуроков +
+
+

30 часов видеоуроков

+

и презентации в еженедельных рассылках

+
+
+
+
+
+
+ Куратор, группа 5-7 человек +
+
+

Десятки интересных заданий

+

создание сайтов, ботов, работа с
данными и т.д.

+
+
+
+
+
+
+ Интересные задания +
+
+

Куратор, группа 5-7 человек

+

и опытные преподаватели

+
+
+
+
+
+
+ Код-ревью +
+
+

Проверка задач: код-ревью

+

и рекомендации куратора

+
+
+
+
+
+
+ Готовое портфолио +
+
+

Живое общение и Telegram-чат

+

с преподавателями и однокурсниками

+
+
+
+
+
+
+ Знакомства в отрасли +
+
+

Поддержка в течение 2 месяцев

+

после окончания курса

+
+
+
+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/advanced/index_course_details_mobile.html b/landing_page/mainpage/templates/mainpage/include/advanced/index_course_details_mobile.html new file mode 100644 index 00000000..084c3df9 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/advanced/index_course_details_mobile.html @@ -0,0 +1,57 @@ +{% load static %} + +
+ +
diff --git a/landing_page/mainpage/templates/mainpage/include/advanced/index_faq.html b/landing_page/mainpage/templates/mainpage/include/advanced/index_faq.html new file mode 100644 index 00000000..bc226ea3 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/advanced/index_faq.html @@ -0,0 +1,272 @@ +
+
+
+

FAQ

+
+
+
+
+
+
+
+
+
+ + Зачем мне вообще Python? + +
+
+
+
+ С одной стороны, умение программировать может быть подспорьем для вас в основной работе + — вы сможете писать полезные программы для сбора данных, их анализа и вывода в удобном + формате (будь то веб сайт или бот в мессенджере). Это может быть полезно, если вы + работаете маркетологом, аналитиком данных, системным администратором, seo-специалистом + и так далее. С другой стороны, программист — востребованная профессия и наши курсы + могут помочь сделать первый шаг в её освоении. +
+
+
+
+
+
+ +
+
+
+
+ В течение всего курса вы находитесь в плотном взаимодействии со своим куратором, + имея возможность получать ответы на свои вопросы и отзывы по поводу своего кода. + Также работая над своим реальным проектом в течение курса, вы учитесь командному + взаимодействию, которое является важной частью работы разработчика. В целом курс + выстроен так, что вы не попадёте в ситуацию, когда вам необходимо построить + синхрофазотрон, научившись закручивать гайки — программа выстроена с учётом + постепенного вхождения в программирование в правильной логической последовательности. +
+
+
+
+
+
+ +
+
+
+
+ Создавая курс, мы старались учесть интересы тех, кто не знает о программировании + вообще ничего, так что можно смело сказать, что он рассчитан на самых новичков. + При этом, однако, формат курса и фокус на практике предполагает возможность «повышения + квалификации» для тех, кто учит Python как второй язык программирования или же уже + выучил основы до этого — вы сможете заниматься в том темпе, в каком вам будет комфортно. +
+
+
+
+
+
+ +
+
+
+
+ Да, вы сможете постоянно возвращаться к той или иной лекции по необходимости. +
+
+
+
+
+
+ +
+
+
+
+ Для начала работы программистом даже на начальной позиции требуются опыт и портфолио. + Начать формировать и то, и другое вы сможете уже в ходе занятий на курсе. После этого, + если вы захотите работать программистом, мы рекомендуем продолжить формирование + портфолио и профессиональное развитие — все необходимые для этого ресурсы будут вам + предоставлены. Где-то через полгода — год самостоятельного развития вы сможете + попробовать себя на позицию junior-разработчика. +
+
+
+
+
+
+ +
+
+
+
+ Нет, не критично, поскольку материалы лекций будут доступны онлайн, а практиковаться вы + сможете самостоятельно, поддерживая контакт с куратором. +
+
+
+
+
+
+ +
+
+
+
+ Во-первых, мы — не школа программирования, мы — сообщество разработчиков-практиков + MoscowPython. У нас нет профессиональных преподавателей — в течение курса вы общаетесь + с теми, кто имел реальный опыт участия в «боевых» проектах. Во-вторых, наш курс + сфокусирован на практике и не требует знаний программирования для того, чтобы можно + было начать учиться на нём. С другой стороны, опять же в силу фокуса на практике, курс + подходит и тем, кто имеет больше опыта в программировании. +
+
+
+ +
+
+
+ +
+
+
+
+ Да, посмотрите эту историю нашей выпускницы + https://youtu.be/p3BLRyDsvyk +
+
+
+
+
+
+ +
+
+
+
+ На курсе мы не требуем этих знаний. Вы можете начать заниматься и так: часто программисту + важнее разобраться в другой отрасли работы (например, том, как устроена бухгалтерия), + чтобы написать хороший сервис. +
+
+
+
+
+
+ +
+
+
+
+ Да, обязательно иметь свой ноутбук или компьютер +
+
+
+
+
+
+ +
+
+
+
+ За неделю до начала занятий вы начнете получать сообщения, что и как установить на ноутбук. + Также мы будем делиться полезными материалами во время курса. А пока рекомендуем + наш подкаст - Moscow Python Podcast. +
+
+
+
+
+
+ +
+
+
+
+ Да. Если ко второму занятию вы поймете, что мы не оправдали ваших надежд - напишите нам + и мы вернем деньги в полном объеме. Также, в случае форс-мажорных обстоятельств за + небольшую доплату мы можем вас перевести на следующий набор. +
+
+
+
+
+
+ +
+
+
+
+ Мы всегда рады и рассматриваем новых кураторов для наших наборов. Если вы заинтересованы + - пишите нам на team@python.ru +
+
+
+
+
+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/advanced/index_footer.html b/landing_page/mainpage/templates/mainpage/include/advanced/index_footer.html new file mode 100644 index 00000000..f4ab433e --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/advanced/index_footer.html @@ -0,0 +1,51 @@ + + +
+ +
diff --git a/landing_page/mainpage/templates/mainpage/include/advanced/index_hero.html b/landing_page/mainpage/templates/mainpage/include/advanced/index_hero.html new file mode 100644 index 00000000..7933c273 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/advanced/index_hero.html @@ -0,0 +1,63 @@ +{% load static %} + +
+
+
+ +

+ Курс программирования на + Python 3.12 + для тех, кто уже знаком с веб-разработкой на Python +

+

+ Вы научитесь писать код, за который не стыдно: аннотированный, покрытый тестами, с правильными + структурами данных, с настроенными линтерами и CI. Опытные питонисты, имеющие опыт «боевой» + разработки, всю дорогу будут проверять ваш код и давать рекомендации. +

+ {% if enrollment %} +

+ С {{ enrollment.start_date | date:"j E" }} по {{ enrollment.end_date | date:"j E" }} +

+

+ Регистрация закроется {{ enrollment.end_registration_date | date:"j E" }} +

+

+ Всего + 25 + мест в наборе – успейте присоединиться! +

+ +
+
00дней
+ : +
00часов
+ : +
00минут
+ : +
00секунд
+
+ {% else %} +

+ Сейчас регистрация закрыта, но мы скоро регистрацию на новый набор +

+ {% endif %} +
+
+
+
+
+

Учитесь онлайн

+
+ +
+
+
+
+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/advanced/index_how_to_pay.html b/landing_page/mainpage/templates/mainpage/include/advanced/index_how_to_pay.html new file mode 100644 index 00000000..749dd9df --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/advanced/index_how_to_pay.html @@ -0,0 +1,78 @@ +{% load static %} + +
+
+
+

Как оплатить

+
+
+
+
+
+

Как оплатить

+
+
+
+
+
+

Частным лицам

+

(оплата сразу)

+
+
+
+

+ Начните регистрацию, чтобы забронировать место на курсе, а затем оплатите курс в течение 4 дней + одним из способов: +

+
    +
  • +

    + Картой Visa или Mastercard. + +

    +
  • +
  • +

    + Электронными деньгами: Яндекс.Деньги или WebMoney. + yandex money + yandex money +

    +
  • +
  • +

    + Наличными через салоны «Связной». + Связной +

    +
  • +
  • +

    + Для оплаты зарубежной картой свяжитесь с нами + team@python.ru. +

    +
  • +
+
+
+
+
+
+

Юридическим лицам

+

(безналичная оплата)

+
+
+
+
    +
  • +

    Начните регистрацию и выберите опцию «Выставить счет».

    +
  • +
  • +

    + Свяжитесь с нами по любым вопросам, написав на + team@python.ru. +

    +
  • +
+
+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/advanced/index_how_to_pay_by_platim.html b/landing_page/mainpage/templates/mainpage/include/advanced/index_how_to_pay_by_platim.html new file mode 100644 index 00000000..b8d44bf8 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/advanced/index_how_to_pay_by_platim.html @@ -0,0 +1,65 @@ +{% load static %} + +
+
+
+

Как оплатить

+
+
+
+
+
+

Как оплатить

+
+
+
+
+
+

Частным лицам

+

(оплата сразу)

+
+
+
+

+ Начните регистрацию, чтобы забронировать место на курсе, а затем оплатите курс в течение 4 дней + одним из способов: +

+
    +
  • +

    + Картой Visa или Mastercard банков РФ. +

    +
  • +
  • +

    + Картой Visa или Mastercard банков многих стран вне РФ. +

    +
  • +
  • +

    + QR-кодом (если вы пользуетесь Тинькофф). +

    +
  • +
+

+ В процессе оплаты вы можете выбрать вариант рассрочки от 3 до 12 месяцев. +

+ +
+
+
+
+
+

Юридическим лицам

+

(безналичная оплата)

+
+
+
+

+ Свяжитесь с нами, написав на + team@python.ru. +

+
+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/advanced/index_menu.html b/landing_page/mainpage/templates/mainpage/include/advanced/index_menu.html new file mode 100644 index 00000000..bcf61d8f --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/advanced/index_menu.html @@ -0,0 +1,63 @@ +
+
+ + + +
+ {% include 'mainpage/include/common/buttons/menu_order_button.html' %} +
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/advanced/index_modules.html b/landing_page/mainpage/templates/mainpage/include/advanced/index_modules.html new file mode 100644 index 00000000..4feb100d --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/advanced/index_modules.html @@ -0,0 +1,97 @@ +{% load static %} + + +
+
+
+

Как проходит курс

+
+
+
+
+
+
+

Что вас ждет

+

+ Вы будете много работать самостоятельно по вечерам и выходным, а преподаватели будут рядом, + чтобы подсказать. +

+
+
+
+
+
+

+ Все группы занимаются по одной программе + и получают одинаковое внимание преподавателей и кураторов. +

+

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

+

+ Всю неделю + вы выполняете задания и получаете ревью от куратора. +

+ +
+
+
+
+
+
+ + +
+
+
+

Программа

+
+
+
+
+
+
+

Программа

+

На курсе есть 4 трека на выбор.

+

Вне зависимости от вашего выбора, у вас будут материалы со всех треков.

+
    +
  • Практика программирования
  • +
  • Инфраструктура кода
  • +
  • Юнит-тесты
  • +
  • Django
  • +
+

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

+
+
+
+
+ +
+
+ {% include "mainpage/include/advanced/track_programming_practice.html" %} +
+
+ {% include "mainpage/include/advanced/track_code_infrastructure.html" %} +
+
+ {% include "mainpage/include/advanced/track_unittests.html" %} +
+
+ {% include "mainpage/include/advanced/track_django.html" %} +
+
+
+
+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/advanced/index_register_popup.html b/landing_page/mainpage/templates/mainpage/include/advanced/index_register_popup.html new file mode 100644 index 00000000..bc7ad462 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/advanced/index_register_popup.html @@ -0,0 +1,62 @@ +{% load static %} + + + diff --git a/landing_page/mainpage/templates/mainpage/include/advanced/index_reviews.html b/landing_page/mainpage/templates/mainpage/include/advanced/index_reviews.html new file mode 100644 index 00000000..d40ca62a --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/advanced/index_reviews.html @@ -0,0 +1,74 @@ +{% load static %} + + +
+
+
+

Отзывы

+
+
+
+
+

Отзывы наших выпускников

+
+
+ +
+ +
+
+
+ +
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/advanced/index_you_will_learn.html b/landing_page/mainpage/templates/mainpage/include/advanced/index_you_will_learn.html new file mode 100644 index 00000000..6eba2485 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/advanced/index_you_will_learn.html @@ -0,0 +1,101 @@ +{% load static %} + +
+
+
+

Вы научитесь

+

Вы научитесь

+

Программировать, а не кодить.

+
+
+
+
+
+
+
+
+ +
+

Делать сайты и веб-приложения

+
+
+

+ и сможете написать «клон» Avito: доску объявлений для одной товарной категории с собственной + базой данных +

+
+
+
+
+
+
+
+ +
+

Работать с данными

+
+
+

+ и сможете написать первый data science проект: сервис, который будет анализировать успешность + постов в VK по реакциям читателей +

+
+
+
+
+
+
+
+ +
+

Создавать чат-ботов

+
+
+

и сможете написать Telegram-бота, который информирует друзей о курсе акций и криптовалют

+
+
+
+ +
+
+
+
+ +
+

Писать свои проекты

+
+
+

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

+

+ Каждую неделю вы будете добавлять функциональность, а на финальном занятии презентуете + рабочую версию проекта. +

+

+ После 3-го занятия вы можете предложить свою идею однокурсникам или присоединиться к чужому + проекту. +

+

+ Вот какие проекты делали студенты 10-го набора (май-июль 2018): +

+
+
+
+ +
+
+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/advanced/track_code_infrastructure.html b/landing_page/mainpage/templates/mainpage/include/advanced/track_code_infrastructure.html new file mode 100644 index 00000000..28901046 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/advanced/track_code_infrastructure.html @@ -0,0 +1,52 @@ +

Трек «Инфраструктура кода»

+ +

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

+ +

Аннотации типов

+ + +

Структуры данных

+ + +

Стиль и структура кода

+ + +

Docker

+ + +

Тулинг

+ + +

CI/CD

+ + +{% include 'mainpage/include/common/buttons/track_order_button.html' %} \ No newline at end of file diff --git a/landing_page/mainpage/templates/mainpage/include/advanced/track_django.html b/landing_page/mainpage/templates/mainpage/include/advanced/track_django.html new file mode 100644 index 00000000..5c94860d --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/advanced/track_django.html @@ -0,0 +1,33 @@ +

Трек «Django»

+ +

+ В этом треке все необходимое для знакомства Django и тем, как она используется в реальных коммерческих проектов. +

+ +

Составные части Django

+ + +

Смежные темы

+ + +{% include 'mainpage/include/common/buttons/track_order_button.html' %} \ No newline at end of file diff --git a/landing_page/mainpage/templates/mainpage/include/advanced/track_programming_practice.html b/landing_page/mainpage/templates/mainpage/include/advanced/track_programming_practice.html new file mode 100644 index 00000000..80a6756c --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/advanced/track_programming_practice.html @@ -0,0 +1,24 @@ +

Трек «Практика программирования»

+ +

Мы считаем, что если вы уже знаете азы языка, то главное, что вам нужно – это много практики и ревью.

+ +

+ Этот трек как раз про это. + На нём нет лекций, зато каждую неделю вы получаете небольшой проект, который вам нужно реализовать. +

+ +

+ Общей тематики у проектов нет – это нужно, чтобы кроме большого количества практики вы расширяли свой кругозор. +

+ +

Примеры проектов

+ + + +{% include 'mainpage/include/common/buttons/track_order_button.html' %} \ No newline at end of file diff --git a/landing_page/mainpage/templates/mainpage/include/advanced/track_unittests.html b/landing_page/mainpage/templates/mainpage/include/advanced/track_unittests.html new file mode 100644 index 00000000..ea1383bf --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/advanced/track_unittests.html @@ -0,0 +1,37 @@ +

Трек «Юниттесты»

+ +

+ В этом треке вы научитесь всему, что нужно для настройки и написания + юнит-тестов в современном комерческом проекте. +

+ +

Юнит-тесты

+ + +

Стиль кода тестов

+ + +

Инструменты pytest

+ + +{% include 'mainpage/include/common/buttons/track_order_button.html' %} diff --git a/landing_page/mainpage/templates/mainpage/include/common/buttons/block_order_button.html b/landing_page/mainpage/templates/mainpage/include/common/buttons/block_order_button.html new file mode 100644 index 00000000..3cd7a6da --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/common/buttons/block_order_button.html @@ -0,0 +1,16 @@ + +{% if enrollment.platim_url %} + + Записаться на курс + +{% else %} + +{% endif %} \ No newline at end of file diff --git a/landing_page/mainpage/templates/mainpage/include/common/buttons/menu_order_button.html b/landing_page/mainpage/templates/mainpage/include/common/buttons/menu_order_button.html new file mode 100644 index 00000000..e8ba82e2 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/common/buttons/menu_order_button.html @@ -0,0 +1,3 @@ + + Записаться + diff --git a/landing_page/mainpage/templates/mainpage/include/common/buttons/track_order_button.html b/landing_page/mainpage/templates/mainpage/include/common/buttons/track_order_button.html new file mode 100644 index 00000000..b30e9150 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/common/buttons/track_order_button.html @@ -0,0 +1,8 @@ +
+ \ No newline at end of file diff --git a/landing_page/mainpage/templates/mainpage/include/common/counters.html b/landing_page/mainpage/templates/mainpage/include/common/counters.html new file mode 100644 index 00000000..e29a695d --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/common/counters.html @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/landing_page/mainpage/templates/mainpage/include/common/index_reviews.html b/landing_page/mainpage/templates/mainpage/include/common/index_reviews.html new file mode 100644 index 00000000..80818364 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/common/index_reviews.html @@ -0,0 +1,65 @@ +{% load static %} + + +
+
+
+

Отзывы

+
+
+
+
+

Отзывы наших выпускников

+
+
+ +
+ +
+
+
+ +
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/index_about_video.html b/landing_page/mainpage/templates/mainpage/include/index_about_video.html new file mode 100644 index 00000000..c85f2924 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/index_about_video.html @@ -0,0 +1,31 @@ +
+
+
+

О курсе

+
+
+
+
+
+
+

Видеоответ на все вопросы

+

+ Почему мы учим Python’у лучше всех, как мы это делаем, + чему вы научитесь и почему наши выпускники - уже практически junior программисты. +

+
+
+
+
+ +
+
+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/index_authors.html b/landing_page/mainpage/templates/mainpage/include/index_authors.html new file mode 100644 index 00000000..319e4f90 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/index_authors.html @@ -0,0 +1,175 @@ +{% load static %} + +
+
+
+

Курс ведут

+
+
+
+
+
+

Кто делает курс

+
+
+
+
+
+

Кто мы

+
+
+
+
+
+
+
+

Курс организует сообщество MoscowPython

+

+ C 2012 года проводим митапы Python-разработчиков на площадках Яндекса, ЦИАН, Мегафона, + Mail.ru Group и других компаний. Записываем видео и подкаст для питонистов. + Объединили свыше 5К человек в единое комьюнити. +

+
    +
  • +
    + 3 year +
    +
    +

    5 лет

    + курсу +
    +
  • +
  • +
    + 11 sets +
    +
    +

    29 наборов

    + прошли обучение +
    +
  • +
  • +
    + 400 graduates +
    +
    +

    1500+

    + выпускников +
    +
  • +
+
+
+
+
+
+ +
+
+
+

Основные преподаватели и организаторы

+
+
+
+
+

Основные преподаватели и организаторы

+

+ Практики с большим опытом в разработке и управлении проектами. Разработали сам курс. + Ведут занятия. Записывают видеолекции. Помогают со сложными задачами. Отвечают за + организационные и административные вопросы. +

+
+
+
    +
  • +
    +
    + Валентин Домбровский
    +
    +

    Валентин Домбровский

    +

    + Евангелист и сооснователь MoscowPython сообщества. +

    +

    + Сооснователь IT-рекрутингового агентства Geekfactor.io. +

    +
    +
    + Евангелист и сооснователь MoscowPython сообщества. Сооснователь IT-рекрутингового агентства Geekfactor.io. +
    +
    +
  • +
  • +
    +
    + Семен Осипов +
    +
    +

    Илья Лебедев

    +

    + Евангелист MoscowPython, разработчик в ANNA Money, до этого – технический директор + в Zipsale и BestDoctor. +

    +
    +
    + Программирует и преподаёт Python больше 10 лет. +
    +
    +
  • +
  • +
    +
    + Михаил Корнеев +
    +
    +

    Михаил Корнеев

    +

    + В коммерческой разработке с 2001 года, с 2009 — работает с Python. +

    +

    + Cооснователь MoscowPython. Тимлид в международном IT-стартапе. +

    +
    +
    + Сооснователь MoscowPython, Тимлид в BestDoctor. В коммерческой разработке с 2001 года, + с 2009 - работает с Python. +
    +
    +
  • +
+
+
+
+
+

Кураторы групп

+
+
+
+
+

Кураторы групп

+

+ Первыми придут на помощь на занятиях и между ними. Помогут спланировать и + распределить задачи в вашем выпускном проекте. +

+
+
+
    + {% for curator in curators_list %} +
  • +
    +
    + {% if curator.curator_photo %} + {{ curator.curator_name }} + {% endif %} +

    {{ curator.curator_name }}

    +
    +
    +

    {{ curator.curator_bio }}

    +
    +
    +
  • + {% endfor %} +
+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/index_course_details_desktop.html b/landing_page/mainpage/templates/mainpage/include/index_course_details_desktop.html new file mode 100644 index 00000000..84716016 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/index_course_details_desktop.html @@ -0,0 +1,163 @@ +{% load static %} + +
+
+
+

Кому подойдет

+

+ Мы учли интересы всех: и новичков, и продолжающих. +

+

В результате вы можете создавать:

+
+
+
+
+
+
+
+
    +
  • + + Вы никогда не программировали + или делали это только в школе. + +
  • +
  • + + Вы хотите автоматизировать свою работу + в управлении проектами, тестировании, маркетинге, SEO, системном администрировании и т.д. + +
  • +
  • + + Вы уже учили основы Python + до этого, но где-то «застряли». + +
  • +
  • + + Вы хотите сменить работу + или попасть в ИТ. + +
  • +
  • + + Вы учите Python как второй язык + программирования, ведь это лучший второй ЯП для любых задач. + +
  • +
+

+ Курс подстроится под вас. + Поэтому мы собираем небольшие группы, а куратор следит за вашим прогрессом в течение всей недели. + Если вы забуксуете, он объяснит тему дополнительно. А если будете опережать других, вам + придумают задачек — скучно не будет! +

+
+
+
+
+
+ +
+
+
+

Что вас ждет

+

+ Вы сразу получите результат и поймете, куда расти. И так каждую неделю. +

+
+
+
+
+
+
+
+ 80+ часов практики +
+
+

Практика с первого дня:

+

от 80 часов с преподавателем и самостоятельно

+
+
+
+
+
+
+ 30 часов видеоуроков +
+
+

30 часов видеоуроков

+

и презентации в еженедельных рассылках

+
+
+
+
+
+
+ Куратор, группа 5-7 человек +
+
+

Десятки интересных заданий:

+

создание сайтов, ботов, работа с
данными и т.д.

+
+
+
+
+
+
+ Интересные задания +
+
+

Куратор, группа 5-7 человек

+

и опытные преподаватели

+
+
+
+
+
+
+ Код-ревью +
+
+

Проверка задач: код-ревью

+

и рекомендации куратора

+
+
+
+
+
+
+ 2 месяца поддержки +
+
+

Свой проект

+

во второй половине курса

+
+
+
+
+
+
+ Готовое портфолио +
+
+

Живое общение и Telegram-чат

+

с преподавателями и однокурсниками

+
+
+
+
+
+
+ Знакомства в отрасли +
+
+

Поддержка в течение 2 месяцев

+

после окончания курса

+
+
+
+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/index_course_details_mobile.html b/landing_page/mainpage/templates/mainpage/include/index_course_details_mobile.html new file mode 100644 index 00000000..0c6d9759 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/index_course_details_mobile.html @@ -0,0 +1,57 @@ +{% load static %} + +
+ +
diff --git a/landing_page/mainpage/templates/mainpage/include/index_faq.html b/landing_page/mainpage/templates/mainpage/include/index_faq.html new file mode 100644 index 00000000..f260f8e0 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/index_faq.html @@ -0,0 +1,293 @@ +
+
+
+

FAQ

+
+
+
+
+
+
+
+
+
+ + Зачем мне вообще Python? + +
+
+
+
+ С одной стороны, умение программировать может быть подспорьем для вас в основной работе + — вы сможете писать полезные программы для сбора данных, их анализа и вывода в удобном + формате (будь то веб сайт или бот в мессенджере). Это может быть полезно, если вы + работаете маркетологом, аналитиком данных, системным администратором, seo-специалистом + и так далее. С другой стороны, программист — востребованная профессия и наши курсы + могут помочь сделать первый шаг в её освоении. +
+
+
+
+
+
+ +
+
+
+
+ В течение всего курса вы находитесь в плотном взаимодействии со своим куратором, + имея возможность получать ответы на свои вопросы и отзывы по поводу своего кода. + Также работая над своим реальным проектом в течение курса, вы учитесь командному + взаимодействию, которое является важной частью работы разработчика. В целом курс + выстроен так, что вы не попадёте в ситуацию, когда вам необходимо построить + синхрофазотрон, научившись закручивать гайки — программа выстроена с учётом + постепенного вхождения в программирование в правильной логической последовательности. +
+
+
+
+
+
+ +
+
+
+
+ Создавая курс, мы старались учесть интересы тех, кто не знает о программировании + вообще ничего, так что можно смело сказать, что он рассчитан на самых новичков. + При этом, однако, формат курса и фокус на практике предполагает возможность «повышения + квалификации» для тех, кто учит Python как второй язык программирования или же уже + выучил основы до этого — вы сможете заниматься в том темпе, в каком вам будет комфортно. +
+
+
+
+
+
+ +
+
+
+
+ Да, вы сможете постоянно возвращаться к той или иной лекции по необходимости. +
+
+
+
+
+
+ +
+
+
+
+ Для начала работы программистом даже на начальной позиции требуются опыт и портфолио. + Начать формировать и то, и другое вы сможете уже в ходе занятий на курсе. После этого, + если вы захотите работать программистом, мы рекомендуем продолжить формирование + портфолио и профессиональное развитие — все необходимые для этого ресурсы будут вам + предоставлены. Где-то через полгода — год самостоятельного развития вы сможете + попробовать себя на позицию junior-разработчика. +
+
+
+
+
+
+ +
+
+
+
+ Нет, не критично, поскольку материалы лекций будут доступны онлайн, а практиковаться вы + сможете самостоятельно, поддерживая контакт с куратором. +
+
+
+
+
+
+ +
+
+
+
+ Во-первых, мы — не школа программирования, мы — сообщество разработчиков-практиков + MoscowPython. У нас нет профессиональных преподавателей — в течение курса вы общаетесь + с теми, кто имел реальный опыт участия в «боевых» проектах. Во-вторых, наш курс + сфокусирован на практике и не требует знаний программирования для того, чтобы можно + было начать учиться на нём. С другой стороны, опять же в силу фокуса на практике, курс + подходит и тем, кто имеет больше опыта в программировании. +
+
+
+ +
+
+
+ +
+
+
+
+ Да, посмотрите эту историю нашей выпускницы + https://youtu.be/p3BLRyDsvyk +
+
+
+
+
+
+ +
+
+
+
+ На курсе мы не требуем этих знаний. Вы можете начать заниматься и так: часто программисту + важнее разобраться в другой отрасли работы (например, том, как устроена бухгалтерия), + чтобы написать хороший сервис. +
+
+
+
+
+
+ +
+
+
+
+ Да, обязательно иметь свой ноутбук или компьютер +
+
+
+
+
+
+ +
+
+
+
+ За неделю до начала занятий вы начнете получать сообщения, что и как установить на ноутбук. + Также мы будем делиться полезными материалами во время курса. А пока рекомендуем + наш подкаст - Moscow Python Podcast. +
+
+
+
+
+
+ +
+
+
+
+ Да. Если ко второму занятию вы поймете, что мы не оправдали ваших надежд - напишите нам + и мы вернем деньги в полном объеме. Также, в случае форс-мажорных обстоятельств за + небольшую доплату мы можем вас перевести на следующий набор. +
+
+
+
+
+
+ +
+
+
+
+ Мы всегда рады и рассматриваем новых кураторов для наших наборов. Если вы заинтересованы + - пишите нам на team@python.ru +
+
+
+ + +
+
+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/index_footer.html b/landing_page/mainpage/templates/mainpage/include/index_footer.html new file mode 100644 index 00000000..f4ab433e --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/index_footer.html @@ -0,0 +1,51 @@ + + +
+ +
diff --git a/landing_page/mainpage/templates/mainpage/include/index_hero.html b/landing_page/mainpage/templates/mainpage/include/index_hero.html new file mode 100644 index 00000000..c909e000 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/index_hero.html @@ -0,0 +1,63 @@ +{% load static %} + +
+
+
+ +

+ Курс программирования на + Python 3.12 + для любого уровня c упором на практику +

+

Ближайший набор пройдет только Онлайн

+

+ Вы создадите Telegram-бота в первую же неделю. Познакомитесь с основами data science и веб-разработки. + Напишете собственный проект. Опытные питонисты, имеющие опыт «боевой» разработки, всю дорогу будут + проверять ваш код и давать рекомендации. +

+ {% if enrollment %} +

+ С {{ enrollment.start_date | date:"j E" }} по {{ enrollment.end_date | date:"j E" }} +

+

+ Регистрация закроется {{ enrollment.end_registration_date | date:"j E" }} +

+

+ Всего + 50 + мест в наборе – успейте присоединиться! +

+
+
00дней
+ : +
00часов
+ : +
00минут
+ : +
00секунд
+
+ {% else %} +

+ Сейчас регистрация закрыта, но мы скоро регистрацию на новый набор +

+ {% endif %} +
+
+
+
+
+

Учитесь онлайн

+
+ +
+
+
+
+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/index_how_to_pay.html b/landing_page/mainpage/templates/mainpage/include/index_how_to_pay.html new file mode 100644 index 00000000..749dd9df --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/index_how_to_pay.html @@ -0,0 +1,78 @@ +{% load static %} + +
+
+
+

Как оплатить

+
+
+
+
+
+

Как оплатить

+
+
+
+
+
+

Частным лицам

+

(оплата сразу)

+
+
+
+

+ Начните регистрацию, чтобы забронировать место на курсе, а затем оплатите курс в течение 4 дней + одним из способов: +

+
    +
  • +

    + Картой Visa или Mastercard. + +

    +
  • +
  • +

    + Электронными деньгами: Яндекс.Деньги или WebMoney. + yandex money + yandex money +

    +
  • +
  • +

    + Наличными через салоны «Связной». + Связной +

    +
  • +
  • +

    + Для оплаты зарубежной картой свяжитесь с нами + team@python.ru. +

    +
  • +
+
+
+
+
+
+

Юридическим лицам

+

(безналичная оплата)

+
+
+
+
    +
  • +

    Начните регистрацию и выберите опцию «Выставить счет».

    +
  • +
  • +

    + Свяжитесь с нами по любым вопросам, написав на + team@python.ru. +

    +
  • +
+
+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/index_how_to_pay_by_platim.html b/landing_page/mainpage/templates/mainpage/include/index_how_to_pay_by_platim.html new file mode 100644 index 00000000..c7d076b4 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/index_how_to_pay_by_platim.html @@ -0,0 +1,70 @@ +{% load static %} + +
+
+
+

Как оплатить

+
+
+
+
+
+

Как оплатить

+
+
+
+
+
+

Частным лицам

+

(оплата сразу)

+
+
+
+

+ Начните регистрацию, чтобы забронировать место на курсе, а затем оплатите курс в течение 4 дней + одним из способов: +

+
    +
  • +

    + Картой Visa или Mastercard банков РФ. +

    +
  • +
  • +

    + Картой Visa или Mastercard банков многих стран вне РФ. +

    +
  • +
  • +

    + QR-кодом (если вы пользуетесь Тинькофф). +

    +
  • +
  • +

    + Yandex.Pay +

    +
  • +
+

+ В процессе оплаты вы можете выбрать вариант рассрочки от 3 до 12 месяцев. +

+ +
+
+
+
+
+

Юридическим лицам

+

(безналичная оплата)

+
+
+
+

+ Свяжитесь с нами, написав на + team@python.ru. +

+
+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/index_labor.html b/landing_page/mainpage/templates/mainpage/include/index_labor.html new file mode 100644 index 00000000..066b2f54 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/index_labor.html @@ -0,0 +1,87 @@ +
+
+
+
+
+

Поддержка трудоустройства

+
+
+
+
+
+

Поддержка трудоустройства

+
+
+
+
+
+
+
+

Что это такое?

+

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

+
+
+
+
+
+

+ Все участники получают нашу поддержку по трудоустройству. +

+

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

+
+
+
    +
  • +
    +
    + Шаг 1 +

    Посещаем вебинар по поиску работы

    +
    +

    + Через неделю после окончания курса мы проводим вебинар по поиску работы. Мы + говорим о том, почему надо ходить на собеседования и как их не бояться, + рассказываем "секреты" HR и о том, как составлять резюме, чтобы на него обращали + внимание, причесываем GitHub. +

    +
    +
  • +
  • +
    +
    + Шаг 2 +

    Корректируем курс

    +
    +

    + Желающие могут заполнить анкету с резюме и ссылкой на свой проект, а мы дадим + персональные комментарии по составлению резюме и вашему GitHub. +

    +
    +
  • +
  • +
    +
    + Шаг 3 +

    Работа с партнерами

    +
    +

    + Ваши исправленные резюме мы показываем нашим партнерам, которые проявили + заинтересованность в найме наших выпускников. +

    +
    +
  • +
+
+
+
+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/index_menu.html b/landing_page/mainpage/templates/mainpage/include/index_menu.html new file mode 100644 index 00000000..f51b3088 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/index_menu.html @@ -0,0 +1,69 @@ +
+
+ + + +
+ {% include 'mainpage/include/common/buttons/menu_order_button.html' %} +
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/index_modules.html b/landing_page/mainpage/templates/mainpage/include/index_modules.html new file mode 100644 index 00000000..eff53c90 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/index_modules.html @@ -0,0 +1,156 @@ +{% load static %} + + +
+
+
+
+

Как проходит обучение

+
+
+
+
+
+

Как проходит курс

+
+
+
+
+
+
+

Что вас ждет

+

+ Вы будете много работать самостоятельно по вечерам и выходным, а преподаватели будут рядом, + чтобы подсказать. +

+
+
+
+
+
+

+ Все группы занимаются по одной программе + и получают одинаковое внимание преподавателей и кураторов. +

+

+ Каждую субботу в 12 часов + по московскому времени мы проводим созвон всего набора для обсуждения + достигнутых результатов и возникших вопросов, а также презентаций проектов + участников курса. Также регулярно проходят созвоны с кураторами групп по + индивидуальному расписанию каждой группы (об удобном времени вы договариваетесь + с куратором своей группы в начале обучения). В течение недели вы изучаете теорию + по предоставленным материалам и выполняете практические домашние задания + (первые 3 недели - это учебные задачи, а в дальнейшем вы пишете свой проект) в + удобном для вас графике. Вы всегда можете обратиться с вопросами к своему + куратору или в общий чат и оперативно получить ответ и рекомендации. + Такой формат курса позволяет вам эффективно проходить обучение и при этом совмещать + его с своей регулярной деятельностью - работой или учебой в вузе. +

+
+
+
    +
  • +
    +
    + 1-3 неделя +

    Изучаем и подтягиваем основы Python

    +
    + +

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

    +
    +
  • +
  • +
    +
    + 4-9 неделя +

    Пишем свой проект

    +
    +

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

    +
    +
  • +
  • +
    +
    + 10 неделя +

    Финишная прямая

    +
    +

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

    +
    +
  • +
+
+
+
+
+
+
+ + +
+
+
+

Программа

+
+
+
+
+
+
+

Программа

+

На курсе мы учим программированию на Python в целом - "Основной" трек.

+

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

+
    +
  • Веб-разработка
  • +
  • Анализ данных
  • +
  • Чат-боты
  • +
+

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

+
+
+
+
+ +
+
+ {% include "mainpage/include/track_main.html" %} +
+
+ {% include "mainpage/include/track_web.html" %} +
+
+ {% include "mainpage/include/track_ds.html" %} +
+
+ {% include "mainpage/include/track_bots.html" %} +
+
+
+

+ Ещё у нас есть + продвинутый курс + для тех, кто уже освоил эту программу. +

+
+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/index_register_popup.html b/landing_page/mainpage/templates/mainpage/include/index_register_popup.html new file mode 100644 index 00000000..bc7ad462 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/index_register_popup.html @@ -0,0 +1,62 @@ +{% load static %} + + + diff --git a/landing_page/mainpage/templates/mainpage/include/index_you_will_learn.html b/landing_page/mainpage/templates/mainpage/include/index_you_will_learn.html new file mode 100644 index 00000000..6eba2485 --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/include/index_you_will_learn.html @@ -0,0 +1,101 @@ +{% load static %} + +
+
+
+

Вы научитесь

+

Вы научитесь

+

Программировать, а не кодить.

+
+
+
+
+
+
+
+
+ +
+

Делать сайты и веб-приложения

+
+
+

+ и сможете написать «клон» Avito: доску объявлений для одной товарной категории с собственной + базой данных +

+
+
+
+
+
+
+
+ +
+

Работать с данными

+
+
+

+ и сможете написать первый data science проект: сервис, который будет анализировать успешность + постов в VK по реакциям читателей +

+
+
+
+
+
+
+
+ +
+

Создавать чат-ботов

+
+
+

и сможете написать Telegram-бота, который информирует друзей о курсе акций и криптовалют

+
+
+
+ +
+
+
+
+ +
+

Писать свои проекты

+
+
+

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

+

+ Каждую неделю вы будете добавлять функциональность, а на финальном занятии презентуете + рабочую версию проекта. +

+

+ После 3-го занятия вы можете предложить свою идею однокурсникам или присоединиться к чужому + проекту. +

+

+ Вот какие проекты делали студенты 10-го набора (май-июль 2018): +

+
+
+
+ +
+
+
+
+
diff --git a/landing_page/mainpage/templates/mainpage/include/track_bots.html b/landing_page/mainpage/templates/mainpage/include/track_bots.html index 45ddfbf1..950c9ade 100644 --- a/landing_page/mainpage/templates/mainpage/include/track_bots.html +++ b/landing_page/mainpage/templates/mainpage/include/track_bots.html @@ -69,3 +69,5 @@

Тестирование чат-ботов

  • Мокапы: тестируем функции, работающие с MongoDB
  • Тестируем колбеки
  • + +{% include 'mainpage/include/common/buttons/track_order_button.html' %} \ No newline at end of file diff --git a/landing_page/mainpage/templates/mainpage/include/track_ds.html b/landing_page/mainpage/templates/mainpage/include/track_ds.html index c168df3c..9d660278 100644 --- a/landing_page/mainpage/templates/mainpage/include/track_ds.html +++ b/landing_page/mainpage/templates/mainpage/include/track_ds.html @@ -44,3 +44,5 @@

    Делаем предсказания

  • Знакомство с scikit-learn
  • Делаем предсказания
  • + +{% include 'mainpage/include/common/buttons/track_order_button.html' %} \ No newline at end of file diff --git a/landing_page/mainpage/templates/mainpage/include/track_main.html b/landing_page/mainpage/templates/mainpage/include/track_main.html index 6484d17e..856c4e62 100644 --- a/landing_page/mainpage/templates/mainpage/include/track_main.html +++ b/landing_page/mainpage/templates/mainpage/include/track_main.html @@ -53,3 +53,5 @@

    Размещение проекта в Интернете

  • Администрирование и настройка сервера
  • Запуск проекта на сервере
  • + +{% include 'mainpage/include/common/buttons/track_order_button.html' %} \ No newline at end of file diff --git a/landing_page/mainpage/templates/mainpage/include/track_web.html b/landing_page/mainpage/templates/mainpage/include/track_web.html index 4d8c9701..f91d745c 100644 --- a/landing_page/mainpage/templates/mainpage/include/track_web.html +++ b/landing_page/mainpage/templates/mainpage/include/track_web.html @@ -61,4 +61,6 @@

    Добавляем комментарии на сайт

  • Делаем запросы из связанных таблиц
  • Отображение комментариев на сайте
  • Добавляем возможность комментировать новости
  • - \ No newline at end of file + + +{% include 'mainpage/include/common/buttons/track_order_button.html' %} \ No newline at end of file diff --git a/landing_page/mainpage/templates/mainpage/index.html b/landing_page/mainpage/templates/mainpage/index.html index 8ea4e0dc..cdc4acc4 100644 --- a/landing_page/mainpage/templates/mainpage/index.html +++ b/landing_page/mainpage/templates/mainpage/index.html @@ -1,2370 +1,218 @@ - - {% load static %} {% load l10n %} - - - - - - - - - - - - - Learn Python - Курсы Python в Москве и онлайн - - - - - - - - - - - - - -
    -
    - -
    -
    - -
    -
    -
    -
    - -

    Курс программирования на Python 3.8 - для любого уровня c упором на практику

    -

    Онлайн | В Москве

    -

    - Вы создадите Telegram-бота на первом же уроке. Познакомитесь с основами data science и - веб-разработки. - Напишите собственный проект. Опытные питонисты, имеющие опыт «боевой» разработки, всю дорогу - будут - проверять - ваш код и давать рекомендации.

    -

    - С {{course.course_start_date | date:"j E" }} по {{course.course_end_date | date:"j E" }} -

    -

    - Регистрация закроется {{course.end_registration_date | date:"j E" }} -

    -
    -
    00дней -
    - : -
    00часов -
    - : -
    00минут -
    - : -
    00секунд -
    -
    -
    -
    -
    -
    -
    -

    Учитесь онлайн

    -

    - Версия курса для тех, кто много работает, живет не в Москве или за рубежом. -

    -
      -
    • Те же преподаватели и задачи, что и у оффлайн-группы
    • -
    • Индивидуальные созвоны с куратором в субботу. Telegram-чат в другие - дни -
    • -
    -
    -
    -
    -
    -
    Ранняя регистрация
    -
    До - {{ online_price_ranges.0.price_range_end_date | date:"j E" }}
    - {{ online_price_ranges.0.price_range_price}} - р. -
    - -
    - {% if today <= online_price_ranges.0.price_range_end_date %} - - {%else%} - - {%endif%} -
    - -
    -
    -
    -
    Обычная регистрация
    -
    С - {{ online_price_ranges.1.price_range_start_date | date:"j E" }}
    - {{ online_price_ranges.1.price_range_price}} - р. -
    - -
    - {% if today >= online_price_ranges.1.price_range_start_date %} - - {%else%} - - {%endif%} -
    -
    - -
    -
    -
    -
    -
    -
    -

    Учитесь оффлайн в Москве

    -
    - -
    - -
    -

    - Приходите на занятия по субботам, решайте задачи и - смотрите видео из - дома в остальные дни. -

    -
      -
    • Учимся в Deworkacy Русаковская - рядом с метро Красносельская
    • -
    • Группа из 5-7 человек вашего уровня плюс куратор на месте. - Telegram-чат в другие дни -
    • -
    - {% if is_offline_closed%} -
    -
    -

    Регистрация закрыта ввиду эпидемиологической ситуации

    -
    -
    - {% else %} -
    -
    -
    -
    -
    Ранняя регистрация
    -
    До - {{ offline_price_ranges.0.price_range_end_date | date:"j E" }}
    - {{ offline_price_ranges.0.price_range_price}} - р. -
    - -
    - {% if today <= offline_price_ranges.0.price_range_end_date %} - - {%else%} - - {%endif%} -
    - -
    -
    -
    -
    Обычная регистрация
    -
    С - {{ offline_price_ranges.1.price_range_start_date | date:"j E" }}
    - {{ offline_price_ranges.1.price_range_price}} - р. -
    - -
    - {% if today >= offline_price_ranges.1.price_range_start_date %} - - {%else%} - - {%endif%} -
    -
    - {% endif %} -
    - - -
    -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -

    О курсе

    -
    -
    -
    -
    -
    -
    -

    Видеоответ на все вопросы

    -

    - Почему мы учим Python’у лучше всех, как мы это делаем, - чему вы научитесь и почему наши выпускники - уже практически junior программисты. -

    -
    -
    -
    -
    - -
    -
    -
    -
    -
    - -
    -
    -
    -

    Что дает этот курс

    -
    -
    - {% if 0 %} -
    -
    -

    О курсе

    -
    -
    - {% endif %} -
    -
    -
      -
    • -
      - Знание основ Python и навыки работы с Flask -
      -
      -

      Знание основ Python и навыки работы с Flask, SQLAlchemy, Jupyter Notebook, Mongo DB, - HTML, JSON, API популярных сервисов, другими технологиями.

      -
      -
    • -
    • -
      - 80+ часов практики -
      -
      -

      Много практики: от 80 часов с преподавателями и самостоятельно.

      -
      -
    • -
    • -
      - Видеоуроки, которые остаются у вас -
      -
      -

      Видеоуроки, которые остаются у вас и после курса: свыше 30 часов.

      -
      -
    • -
    • -
      - Свой проект и портфолио на GitHub. -
      -
      -

      Свой проект и портфолио на GitHub.

      -
      -
    • -
    • -
      - Сертификат -
      -
      -

      Сертификат.

      -
      -
    • -
    • -
      - 2 месяца поддержки после курса: чат, митапы -
      -
      -

      2 месяца поддержки после курса: чат, митапы.

      -
      -
    • -
    -
    -
    -
    -
    -

    Кому подойдет

    -

    - Мы учли интересы всех: и новичков, и продолжающих. -

    -

    В результате вы можете создавать:

    -
    -
    -
    -
    -
    -
    -
    -
    -
      -
    • - Вы никогда не программировали или делали это только в - школе. -
    • -
    • Вы хотите автоматизировать свою работу в управлении - проектами, тестировании, маркетинге, SEO, системном администрировании и - т.д. -
    • -
    • - Вы уже учили основы Python до этого, но где-то - «застряли». -
    • -
    • Вы хотите сменить работу или попасть в ИТ.
    • -
    • Вы учите Python как второй язык программирования, ведь это - лучший второй ЯП для любых задач. -
    • -
    -

    - Курс подстроится под вас. Поэтому мы собираем - небольшие - группы, а куратор следит за - вашим прогрессом в течение всей недели. Если вы забуксуете, он объяснит тему - дополнительно. - А если будете опережать других, вам придумают задачек — скучно не будет! -

    -
    -
    -
    -
    -
    -
    -
    -
    -

    Что вас ждет

    -

    - Вы сразу получите результат и поймете, куда расти. И так каждую неделю. -

    -
    -
    -
    -
    -
    -
    -
    - 80+ часов практики -
    -
    -

    Практика с первого дня:

    -

    от 80 часов с преподавателем и самостоятельно

    -
    -
    -
    -
    -
    -
    - 30 часов видеоуроков -
    -
    -

    30 часов видеоуроков

    -

    и презентации в еженедельных рассылках

    -
    -
    -
    -
    -
    -
    - Куратор, группа 5-7 человек -
    -
    -

    Десятки интересных заданий:

    -

    создание сайтов, ботов, работа с
    данными и т.д. -

    -
    -
    -
    -
    -
    -
    - Интересные задания -
    -
    -

    Куратор, группа 5-7 человек

    -

    и опытные преподаватели

    -
    -
    -
    -
    -
    -
    - Код-ревью -
    -
    -

    Проверка задач: код-ревью

    -

    и рекомендации куратора

    -
    -
    -
    -
    -
    -
    - 2 месяца поддержки -
    -
    -

    Свой проект

    -

    во второй половине курса

    -
    -
    -
    -
    -
    -
    - Готовое портфолио -
    -
    -

    Живое общение и Telegram-чат

    -

    с преподавателями и однокурсниками

    -
    -
    -
    -
    -
    -
    - Знакомства в отрасли -
    -
    -

    Поддержка в течение 2 месяцев

    -

    после окончания курса

    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Вы научитесь

    -

    Вы научитесь

    -

    Программировать, а не кодить.

    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -

    Делать сайты и веб-приложения

    -
    -
    -

    и сможете написать «клон» Avito: доску объявлений для одной товарной - категории с - собственной базой данных

    -
    -
    -
    -
    -
    -
    -
    - -
    -

    Работать с данными

    -
    -
    -

    и сможете написать первый data science проект: сервис, который будет - анализировать успешность постов в VK по реакциям читателей

    -
    -
    -
    -
    -
    -
    -
    - -
    -

    Создавать чат-ботов

    -
    -
    -

    и сможете написать Telegram-бота, который информирует друзей о курсе акций и - криптовалют

    -
    -
    -
    - -
    -
    -
    -
    - -
    -

    Писать свои проекты

    -
    -
    -

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

    -

    Каждую неделю вы будете добавлять функциональность, а - финальном занятии презентуете рабочую версию проекта.

    -

    После 3-го занятия вы можете предложить свою идею - однокурсникам - или присоединиться к чужому проекту.

    -

    Вот какие проекты делали студенты 10-го набора - (май-июль - 2018):

    -
    -
    -
    - -
    -
    -
    -
    -
    - -
    -
    - -
    -
    -

    - Проекты, созданные во время обучения -

    -
    -

    - Смотреть все проекты -

    -
    -
    - {% if projects %} - {% for project in projects %} -
    - slide -

    {{ project.project_name }}

    -
    - {% endfor %} - {% endif %} -
    -
    -
    - -
    -
    -
    -
    -
    -

    После курса у вас останутся

    -

    - Новые навыки, связи и полезные материалы. -

    -
    -
    -
    -
    -
    -
    - Доступ ко всем видеолекциям и презентациям -
    -
    -

    Доступ ко всем видеолекциям и презентациям.

    -
    -
    -
    -
    - Сертификат о прохождении курса -
    -
    -

    Сертификат о прохождении курса.

    -
    -
    -
    -
    - Чат со всеми преподавателями, кураторами и однокурсниками -
    -
    -

    Чат со всеми преподавателями, кураторами и однокурсниками.

    -
    -
    -
    -
    - Портфолио выполненных проектов на GitHub -
    -
    -

    Портфолио выполненных проектов на GitHub.

    -
    -
    -
    -
    - Опыт работы с Flask, SQLAlchemy, Jupyter Notebook, Mongo DB, Pandas, Bootstrap
-										(HTML/CSS), JSON -
    -
    -

    Опыт работы с Flask, SQLAlchemy, Jupyter Notebook, - Mongo DB, Pandas, Bootstrap - (HTML/CSS), JSON, API популярных сервисов, другими технологиями.

    -
    -
    -
    -
    -
    - -
    -
    - -
    -
    -
    -
    -

    Как проходит обучение

    -
    -
    -
    -
    -
    -

    Как проходит курс

    -
    -
    -
    -
    -
    -
    -

    Что вас ждет

    -

    - Вы будете много работать самостоятельно по вечерам и выходным, а преподаватели будут - рядом, чтобы подсказать. -

    -
    -
    -
    -
    -
    -

    - Онлайн- и оффлайн-группы занимаются по одной программе и получают - одинаковое - внимание - преподавателей и кураторов. -

    -

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

    -
    -
    -
      -
    • -
      -
      - 1-3 неделя -

      Изучаем и подтягиваем основы Python

      -
      - -

      - Каждую неделю вы на практике проходите блок базовых знаний: слушаете - мини-лекций, - пишете код по примерам, затем выполняете проекты и задачи по теме и - выкладываете на проверку в вашем репозитории. Кураторы дают фидбек, как - улучшить код. -

      -
      -
    • -
    • -
      -
      - 4-9 неделя -

      Пишем свой проект

      -
      -

      - Вы продолжаете изучать материалы и выполнять задания, а параллельно - начинаете дипломный проект: выбираете идею и напарника, каждую неделю - добавляете функциональность. Куратор помогает спланировать этапы разработки, - подобрать технологии и решить сложности, которые возникают в процессе. -

      -
      -
    • -
    • -
      -
      - 10 неделя -

      Финишная прямая

      -
      -

      - Вы доводите проект до рабочего прототипа, который можно показать - коллегам и друзьям, и презентуете его перед аудиторией (онлайн-группа - делает это по видеосвязи). Если вы презентуете свой проект - вы - получаете сертификат - об успешном окончании курса. -

      -
      -
    • -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -

    Программа

    -
    -
    -
    -
    -
    -
    -

    Программа

    -

    - На курсе мы учим программированию на Python в целом - "Основной" трек.

    -

    - Параллельно с основным курсом вы по выбору можете изучать один из тематических треков:

    -
      -
    • Веб-разработка
    • -
    • Анализ данных
    • -
    • Чат-боты
    • -
    -

    - Используйте переключатели, чтобы посмотреть содержание каждого трека. -

    -

    -
    -
    -
    -
    - -
    -
    - {% include "mainpage/include/track_main.html" %} -
    -
    - {% include "mainpage/include/track_web.html" %} -
    -
    - {% include "mainpage/include/track_ds.html" %} -
    -
    - {% include "mainpage/include/track_bots.html" %} -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -

    Курс ведут

    -
    -
    -
    -
    -
    -

    Кто делает курс

    -
    -
    -
    -
    -
    -

    Кто мы

    -
    -
    -
    -
    -
    -
    -
    -
    -

    Курс организует сообщество MoscowPython

    -

    C 2012 года проводим митапы Python-разработчиков на площадках Яндекса, ЦИАН, - Мегафона, - Mail.ru Group и других компаний. Записываем видео и подкаст для питонистов. - Объединили - свыше 5К человек в единое комьюнити.

    -
      -
    • -
      - 3 year -
      -
      -

      4 года

      - курсу -
      -
    • -
    • -
      - 11 sets -
      -
      -

      19 наборов

      - прошли обучение -
      -
    • -
    • -
      - 400 graduates -
      -
      -

      900+

      - выпускников -
      -
    • -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Основные преподаватели и организаторы

    -
    -
    -
    -
    -

    Основные преподаватели и организаторы

    -

    - Практики с большим опытом в разработке и управлении проектами. Разработали сам курс. - Ведут занятия. Записывают видеолекции. Помогают со сложными задачами. Отвечают за - организационные и административные вопросы. -

    -
    -
    -
      -
    • -
      -
      - Михаил Корнеев -
      -
      -

      Михаил Корнеев

      -

      - Уровень: технический директор. В коммерческой разработке с 2001 года, с 2009 — - работает с Python. -

      -

      - Сооснователь Ingenix AI, компании, занимающейся заказной разработкой в сфере - машинного обучения и - блокчейн. Сооснователь MoscowPython. -

      -
      -
      - Сооснователь MoscowPython, технический директор и сооснователь Ingenix AI: компании, - занимающейся - заказной разработкой в сфере машинного обучения и блокчейн. В коммерческой - разработке с - 2001 года. -
      -
      -
    • -
    • -
      -
      - Илья Лебедев
      -
      -

      Илья Лебедев

      -

      - Делал курсы разной сложности для Бауманки, НИУ ВШЭ. Автор сайта - прикладных задач для питонистов Devman. -

      -

      - Опыт в разработке — 10 лет. Помогает с разработкой в BestDoctor. -

      -
      -
      - Ведет онлайн-версию курса. Опыт в разработке - 10 лет, сотрудничает с BestDoctor. - Делал курсы программирования для Бауманки, НИУ ВШЭ. Создал сайт прикладных задач для - питонистов Devman. -
      -
      -
    • -
    • -
      -
      - Владимир Филонов -
      -
      -

      Владимир Филонов

      -

      - Основатель студии заказной разработки itcanfly, в прошлом — менеджер проектов и - наемный разработчик. - Начинал с C++ и PHP, пришел к Python и Erlang. -

      -

      Евангелист MoscowPython и ChechPython.

      -
      -
      - Основатель студии заказной разработки itcanfly, в прошлом - менеджер проектов и - наемный - разработчик. - Начинал с C++ и PHP, пришел к Python и Erlang. Евангелист MoscowPython и - ChechPython. -
      -
      -
    • -
    • -
      -
      - Илья Лебедев
      -
      -

      Валентин Домбровский

      -

      - Евангелист и сооснователь MoscowPython сообщества. -

      -

      - Сооснователь Drylabs, компании, создающей open-source проекты. -

      -
      -
      - Евангелист и сооснователь MoscowPython сообщества. - Сооснователь Drylabs, компании, создающей open-source проекты. -
      -
      -
    • -
    • -
      -
      - Михаил Корнеев -
      -
      -

      Семён Осипов

      -

      - Активист MoscowPython сообщества, Big Data Engineer в X5 Retail Group. -

      -

      - Выпускник курсов, отвечает за административные вопросы курса. -

      -
      -
      - Активист MoscowPython сообщества, Big Data Engineer в X5 Retail Group. - Выпускник курсов, отвечает за административные вопросы. -
      -
      -
    • -
    -
    -
    -
    -
    -

    Кураторы групп

    -
    -
    -
    -
    -

    Кураторы групп

    -

    - Первыми придут на помощь на занятиях и между ними. Помогут спланировать и - распределить задачи в вашем выпускном проекте. -

    -
    -
    -
      - {% for curator in curators_list %} -
    • -
      -
      - {% if curator.curator_photo %} - {{curator.curator_name}} - {%endif%} -

      {{curator.curator_name}}

      -
      -
      -

      - {{ curator.curator_bio }} -

      -
      -
      -
    • - {% endfor %} -
    -
    - -
    -
    - -
    -
    -
    -
    -
    -

    Поддержка трудоустройства

    -
    -
    -
    -
    -
    -

    Поддержка трудоустройства

    -
    -
    -
    -
    -
    -
    -
    -

    Что это такое?

    -

    - После окончания курсов мы помогаем нашим выпускникам с трудоустройством: рассказываем, - как - проходить собеседования, как исправить резюме и причесать GitHub. -

    -
    -
    -
    -
    -
    -

    - Все участники получают нашу поддержку по трудоустройству. -

    -

    - После окончания курса мы проводим бесплатный вебинар, на котором - рассказываем про особенности составления - резюме, прохождения собеседований, поиску работы. Мы также приглашаем в наш закрытый - чат, где мы делимся вакансиями - "из первых рук" для начинающих. -

    -
    -
    -
      -
    • -
      -
      - Шаг 1 -

      Посещаем вебинар по поиску работы

      -
      - -

      - Через неделю после окончания курса мы проводим вебинар по поиску работы. - Мы говорим о том, почему надо ходить - на собеседования и как их не бояться, рассказываем "секреты" HR и о том, - как составлять резюме, чтобы на него - обращали внимание, причесываем GitHub. -

      -
      -
    • -
    • -
      -
      - Шаг 2 -

      Корректируем курс

      -
      -

      - Желающие могут заполнить анкету с резюме и ссылкой на свой проект, - а мы дадим персональные комментарии по составлению резюме и вашему - GitHub. -

      -
      -
    • -
    • -
      -
      - Шаг 3 -

      Работа с партнерами

      -
      -

      - Ваши исправленные резюме мы показываем нашим партнерам, которые проявили - заинтересованность в найме наших - выпускников. -

      -
      -
    • -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -

    Стоимость

    -
    -
    -
    -
    -

    Записаться

    -
    -
    -
    -
    - -
    -
    -

    онлайн курс

    -
    -
    -
    -
    -
    Ранняя регистрация
    -
    До - {{ online_price_ranges.0.price_range_end_date | date:"j E" }}
    - {{ online_price_ranges.0.price_range_price}} - р. -
    - -
    - {% if today <= online_price_ranges.0.price_range_end_date %} - - {%else%} - - {%endif%} -
    - -
    -
    -
    -
    Обычная регистрация
    -
    С - {{ online_price_ranges.1.price_range_start_date | date:"j E" }}
    - {{ online_price_ranges.1.price_range_price}} - р. -
    - -
    - {% if today >= online_price_ranges.1.price_range_start_date %} - - {%else%} - - {%endif%} -
    -
    -
    -
    -
    -
    -

    оффлайн курс

    -
    -
    -
    -
    -
    Ранняя регистрация
    -
    До - {{ offline_price_ranges.0.price_range_end_date | date:"j E" }}
    - {{ offline_price_ranges.0.price_range_price}} - р. -
    - -
    - {% if today <= offline_price_ranges.0.price_range_end_date %} - - {%else%} - - {%endif%} -
    - -
    -
    -
    -
    Обычная регистрация
    -
    С - {{ offline_price_ranges.1.price_range_start_date | date:"j E" }}
    - {{ offline_price_ranges.1.price_range_price}} - р. -
    - -
    - {% if today >= offline_price_ranges.1.price_range_start_date %} - - {%else%} - - {%endif%} -
    -
    -
    -
    -
    - -
    -
    - -
    -
    -
    -

    Как оплатить

    -
    -
    -
    -
    -
    -

    Как оплатить

    -
    -
    -
    -
    -
    -

    Рассрочка на 1, 6 или 12 месяцев

    -
    -
    -
    -

    Рассрочка - вид кредита, который предоставляет «Яндекс.Касса» и её банки-партнеры. - Услуга доступна гражданам РФ с постоянным доходом в возрасте от 18 до 65 лет.

    -
      -
    • -

      Начните регистрацию и выберите пункт «Оплата в кредит».

      -
    • -
    • -

      Откройте «Яндекс.Кошелек».

      -
    • -
    • -

      Оформите заявку на кредит и дождитесь одобрения.

      -
    • -
    • -

      Погасите полную стоимость обучения в первый месяц, чтобы избежать переплат. - Либо оплачивайте обучение частями в течение 6 или 12 месяцев с переплатой 3,9% в - месяц.

      -
    • -
    -
    -
    -
    -
    -
    -

    Частным лицам

    -

    (оплата сразу)

    -
    -
    -
    -

    Начните регистрацию, чтобы забронировать место на курсе, а затем оплатите курс в течение 4 - дней - одним из способов:

    -
      -
    • -

      Картой Visa или Mastercard. -

      -
    • -
    • -

      Электронными деньгами: Яндекс.Деньги или WebMoney. - yandex money - yandex money -

      -
    • -
    • -

      Наличными через салоны «Связной». Связной

      -
    • -
    -
    -
    -
    -
    -
    -

    Юридическим лицам

    -

    (безналичная оплата)

    -
    -
    -
    -
      -
    • -

      Начните регистрацию и выберите опцию «Выставить счет».

      -
    • -
    • -

      Свяжитесь с нами по любым вопросам, написав на learn@python.ru. -

      -
    • -
    -
    -
    - -
    -
    - -
    -
    -
    -

    Отзывы

    -
    -
    -
    -
    -

    Отзывы наших выпускников

    -
    -
    - -
    -
      -
    • -
      - 3 year -
      -
      -

      4 года

      - курсу -
      -
    • -
    • -
      - 11 sets -
      -
      -

      900+

      - выпускников -
      -
    • - -
    -
    - - {% if 0 %} -
    -
    - -
    -
    - {% endif %} - -
    -
    - -
    -
    -
    - -
    -
    -
    -

    FAQ

    -
    -
    -
    -
    -
    -
    -
    -
    -
    - - Зачем мне вообще Python? - -
    -
    -
    -
    - С одной стороны, умение программировать может быть подспорьем для вас в основной - работе — вы сможете - писать полезные программы для сбора данных, их анализа и вывода в удобном - формате - (будь то веб сайт - или бот в мессенджере). Это может быть полезно, если вы работаете маркетологом, - аналитиком данных, - системным администратором, seo-специалистом и так далее. С другой стороны, - программист — - востребованная профессия и наши курсы могут помочь сделать первый шаг в её - освоении. -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - В течение всего курса вы находитесь в плотном взаимодействии со своим куратором, - имея возможность - получать ответы на свои вопросы и отзывы по поводу своего кода. Также работая - над - своим реальным - проектом в течение курса, вы учитесь командному взаимодействию, которое является - важной частью - работы разработчика. В целом курс выстроен так, что вы не попадёте в ситуацию, - когда - вам необходимо - построить синхрофазотрон, научившись закручивать гайки — программа выстроена с - учётом постепенного - вхождения в программирование в правильной логической последовательности. -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - Создавая курс, мы старались учесть интересы тех, кто не знает о программировании - вообще ничего, так - что можно смело сказать, что он рассчитан на самых новичков. При этом, однако, - формат курса и фокус - на практике предполагает возможность «повышения квалификации» для тех, кто учит - Python как второй - язык программирования или же уже выучил основы до этого — вы сможете заниматься - в - том темпе, в каком - вам будет комфортно. -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - В каждом наборе курса участвует порядка 70-и человек. Все они делятся на группы - по 5-6 - человек - согласно уровню знаний. Первое занятие — интенсив, который продлится с 10 до 19 - часов и в ходе - которого вы напишите своего первого бота для мессенджера Telegram. В дальнейшем - мы - будем предлагать - вам лекции для изучения в онлайн-режиме, а практические занятия будут полностью - посвящены практике и - взаимодействию с куратором. Каждое такое занятие будет проходить по субботам с - 15 до - 18 часов. Также - в течение курса вы сможете общаться с «сокурсниками» и куратором в - онлайн-режиме. - Первые 4 занятия - курса вам даётся домашнее задание. Вы его делаете и по готовности показываете - куратору. После 4-й - недели вы планируете свой проект, разбиваете задачи с напарником (куратор - помогает - это сделать) и - каждую неделю добавляете функциональность в свой проект. Здесь вы сами ставите себе - задачи - (куратор - поможет, если случился затык, но не будет ставить задачи за вас). Презентации - проектов будет - посвящена часть последнего — «выпускного» — занятия. -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - Да, вы сможете постоянно возвращаться к той или иной лекции по необходимости. -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - Для начала работы программистом даже на начальной позиции требуются опыт и - портфолио. Начать - формировать и то, и другое вы сможете уже в ходе занятий на курсе. После этого, - если - вы захотите - работать программистом, мы рекомендуем продолжить формирование портфолио и - профессиональное развитие - — все необходимые для этого ресурсы будут вам предоставлены. Где-то через - полгода — - год - самостоятельного развития вы сможете попробовать себя на позицию - junior-разработчика. -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - Нет, не критично, поскольку материалы лекций будут доступны онлайн, а - практиковаться - вы сможете - самостоятельно, поддерживая контакт с куратором также онлайн. -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - Да, такая возможность есть. В этом случае вы пропускаете оффлайн-занятия, однако - вы - сможете общаться - с куратором по Скайпу. -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - Во-первых, мы — не школа программирования, мы — сообщество - разработчиков-практиков - MoscowPython. У - нас нет профессиональных преподавателей — в течение курса вы общаетесь с теми, - кто - имел реальный - опыт участия в «боевых» проектах. Во-вторых, наш курс сфокусирован на практике и - не - требует знаний - программирования для того, чтобы можно было начать учиться на нём. С другой - стороны, - опять же в силу - фокуса на практике, курс подходит и тем, кто имеет больше опыта в - программировании. -
    -
    -
    - -
    -
    -
    - -
    -
    -
    -
    - Да, посмотрите эту историю нашей выпускницы https://youtu.be/p3BLRyDsvyk -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - На курсе мы не требуем этих знаний. Вы можете начать заниматься и так: часто - программисту - важнее разобраться в другой отрасли работы (например, том, как устроена - бухгалтерия), - чтобы написать хороший сервис. -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - Да, обязательно иметь свой ноутбук или компьютер. Если вы учитесь оффлайн: - приносите - и - уносите ноутбук с собой, а мы обеспечим розетки, стол, стул, чай и печеньки. -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - За неделю до начала занятий вы начнете получать сообщения, что и как установить - на ноутбук. - Также мы будем делиться полезными материалами во время курса. - А пока рекомендуем наш подкаст - Moscow - Python Podcast. -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - Да. Если ко второму занятию вы поймете, что мы не оправдали ваших надежд - - напишите нам и мы вернем деньги в полном объеме. - Также, в случае форс-мажорных обстоятельств за небольшую доплату мы можем вас - перевести на следующий набор. -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - Можно перейти с оффлайна на онлайн-обучение. Сделать наоборот не получится - - места - в оффлайн-группе ограничены. -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - Мы всегда рады и рассматриваем новых кураторов для наших наборов. - Если вы заинтересованы - пишите нам на learn@python.ru -
    -
    -
    -
    -
    -
    -
    -
    - - - -
    - -
    -
    - - - -
    - - -
    - - - - - - - - - + + + + + + + + + + + + + + + + + + + Learn Python - курсы Python онлайн + + {% include 'mainpage/include/common/counters.html' %} + + + +
    + {% include 'mainpage/include/index_menu.html' %} +
    + +
    + {% include 'mainpage/include/index_hero.html' with enrollment=enrollment registration_closes_date_formatted=registration_closes_date_formatted %} +
    + {% include 'mainpage/include/index_about_video.html' %} + +
    +
    +
    +

    Что дает этот курс

    +
    +
    + +
    + {% include 'mainpage/include/index_course_details_mobile.html' %} + {% include 'mainpage/include/index_course_details_desktop.html' %} + {% include 'mainpage/include/index_you_will_learn.html' %} +
    +
    + +
    +
    +

    + Проекты, созданные во время обучения +

    +
    + {% if projects %} + {% for project in projects %} +
    + slide +

    {{ project.project_name }}

    +
    + {% endfor %} + {% endif %} +
    +
    +
    + +
    +
    +
    +
    +
    +

    После курса у вас останутся

    +

    + Новые навыки, связи и полезные материалы. +

    +
    +
    +
    +
    +
    +
    + Доступ ко всем видеолекциям и презентациям +
    +
    +

    Доступ ко всем видеолекциям и презентациям.

    +
    +
    +
    +
    + Сертификат о прохождении курса +
    +
    +

    Сертификат о прохождении курса.

    +
    +
    +
    +
    + Чат со всеми преподавателями, кураторами и однокурсниками +
    +
    +

    Чат со всеми преподавателями, кураторами и однокурсниками.

    +
    +
    +
    +
    + Портфолио выполненных проектов на GitHub +
    +
    +

    Портфолио выполненных проектов на GitHub.

    +
    +
    +
    +
    + Опыт работы с Flask, SQLAlchemy, Jupyter Notebook, Mongo DB, Pandas, Bootstrap
+                                                (HTML/CSS), JSON +
    +
    +

    Опыт работы с Flask, SQLAlchemy, Jupyter Notebook, + Mongo DB, Pandas, Bootstrap + (HTML/CSS), JSON, API популярных сервисов, другими технологиями.

    +
    +
    +
    +
    +
    + +
    +
    + + {% include 'mainpage/include/index_modules.html' %} + + {% include 'mainpage/include/index_authors.html' with curators_list=curators_list%} + + {% include 'mainpage/include/index_labor.html' %} + +
    +
    +
    +

    Стоимость

    +
    +
    +
    +
    +

    Записаться

    +
    +
    +
    +
    +
    +
    +

    онлайн курс

    +
    +
    + +
    +
    +
    +
    +
    +
    +
    + + {% if enrollment.platim_url %} + {% include 'mainpage/include/index_how_to_pay_by_platim.html' %} + {% else %} + {% include 'mainpage/include/index_how_to_pay.html' %} + {% endif %} + + {% if reviews %} + {% include 'mainpage/include/common/index_reviews.html' with reviews=reviews %} + {% endif %} + + {% include 'mainpage/include/index_faq.html' %} + + {% if should_show_chat %} +
    + {% endif %} + {% include 'mainpage/include/index_footer.html' %} +
    + + {% include 'mainpage/include/index_register_popup.html' with enrollment=enrollment %} + + + + + + + + + + + diff --git a/landing_page/mainpage/templates/mainpage/projects.html b/landing_page/mainpage/templates/mainpage/projects.html deleted file mode 100644 index 2a393697..00000000 --- a/landing_page/mainpage/templates/mainpage/projects.html +++ /dev/null @@ -1,210 +0,0 @@ - - - {% load static %} - {% load l10n %} - - - - - - - - - - - - Learn Python - учимся программировать на Python за 2 месяца - - - - - - - - - - - - - - -
    -
    - -
    -
    - -
    -
    -
    -

    - Проекты, созданные во время обучения -

    -
    -
    - -
    - {% if student_projects %} - {% for project in student_projects %} -
    -
    -
    -
    -

    {{project.project_name}}

    -

    - {{project.project_description}} -

    -
    -
    -
      -
    • -
      - -
      -
    • -
    -
    -
    - {% endfor %} - {% endif %} -
    -
    - - -
    - -
    - - - - - - - diff --git a/landing_page/mainpage/templates/mainpage/success.html b/landing_page/mainpage/templates/mainpage/success.html new file mode 100644 index 00000000..fc44c1ef --- /dev/null +++ b/landing_page/mainpage/templates/mainpage/success.html @@ -0,0 +1,73 @@ +{% load static %} +{% load l10n %} + + + + + + + + + + + + + + + + + + + Learn Python - Курсы Python онлайн + + {% include 'mainpage/include/common/counters.html' %} + + + +
    +
    +
    +

    + Спасибо за регистрацию на курс + LEARN PYTHON + {% if enrollment.type == 'ADVANCED' %} + ADVANCED + {% endif %} +

    +

    + Занятия начнутся {{ enrollment.start_date | date:"j E" }}. + За несколько дней до этого мы пришлём вам письмо со всеми деталями. + До этого ничего делать не нужно. До встречи! +

    +
    +
    +
    + + {% include 'mainpage/include/index_footer.html' %} + + + + + + + + + + + + diff --git a/landing_page/mainpage/tests.py b/landing_page/mainpage/tests.py deleted file mode 100644 index 7ce503c2..00000000 --- a/landing_page/mainpage/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/landing_page/mainpage/urls.py b/landing_page/mainpage/urls.py index d5712b47..5d2d0066 100644 --- a/landing_page/mainpage/urls.py +++ b/landing_page/mainpage/urls.py @@ -4,5 +4,9 @@ urlpatterns = [ path('', views.index, name='index'), - path('projects', views.projects, name='projects') + path('success/', views.success_handle, name='success'), + + path('advanced/', views.advanced_handle, name='index_advanced'), + path('advanced/success/', views.success_handle_advanced, name='success_advanced'), + ] diff --git a/landing_page/mainpage/utils/__init__.py b/landing_page/mainpage/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/landing_page/mainpage/utils/github.py b/landing_page/mainpage/utils/github.py new file mode 100644 index 00000000..75af065a --- /dev/null +++ b/landing_page/mainpage/utils/github.py @@ -0,0 +1,29 @@ +import datetime + +import requests +from memoize import memoize + + +@memoize(timeout=24 * 60 * 60) +def fetch_last_commit_date_from_github_repo( + owner: str, + repo_name: str, + timeout_msec: int = 500, +) -> datetime.datetime | None: + response = requests.get( + f"https://api.github.com/repos/{owner}/{repo_name}/commits", + timeout=timeout_msec / 1000, + ) + if not response: + return None + all_commits = response.json() + if not isinstance(all_commits, list) or not all_commits: + return None + last_commit = all_commits[0] + raw_commit_date = last_commit.get("commit", {}).get("author", {}).get("date") + try: + last_commit_date = datetime.datetime.fromisoformat(raw_commit_date) if raw_commit_date else None + except ValueError: + last_commit_date = None + + return last_commit_date diff --git a/landing_page/mainpage/utils/typing.py b/landing_page/mainpage/utils/typing.py new file mode 100644 index 00000000..bebb5f57 --- /dev/null +++ b/landing_page/mainpage/utils/typing.py @@ -0,0 +1,8 @@ +from typing import Optional, TypeVar + +_T = TypeVar("_T") + + +def ensured(value: Optional[_T]) -> _T: + assert value + return value diff --git a/landing_page/mainpage/utils/utm_parser.py b/landing_page/mainpage/utils/utm_parser.py new file mode 100644 index 00000000..26df2242 --- /dev/null +++ b/landing_page/mainpage/utils/utm_parser.py @@ -0,0 +1,6 @@ +from typing import Iterator + + +def get_utm_params(params: Iterator[tuple[str, ...]]) -> str: + utm_string = "&".join([f"{k}={v.encode('utf-8').decode('utf-8')}" for k, v in params if v and "utm_" in k]) + return '?' + utm_string if utm_string else '' diff --git a/landing_page/mainpage/views.py b/landing_page/mainpage/views.py index eca8a3c5..8ec3e837 100644 --- a/landing_page/mainpage/views.py +++ b/landing_page/mainpage/views.py @@ -1,109 +1,66 @@ -from django.shortcuts import render -from django.conf import settings -from django.http import ( - HttpResponse, HttpResponseForbidden, - HttpResponseServerError) -from django.template import loader -from .models import (LearnPythonCourse, GraduateProjects, - LearnPythonCoursePrices, - Feedback, Curators, GraduateStories, GraduateProjectsVideos, - Podcasts,) from datetime import date +from django.http import HttpRequest, HttpResponse +from django.shortcuts import render +from waffle import switch_is_active -def index(request): - '''Docstring testc''' - template = loader.get_template('mainpage/index.html') - - # Course data - # Fixes #3 LearnPythonCourse matching query does not exist. - try: - current_course = LearnPythonCourse.objects.latest('course_index') - except LearnPythonCourse.DoesNotExist: - current_course = LearnPythonCourse() - - online_prices = LearnPythonCoursePrices.objects.filter( - course_type='Online').order_by('price_range_price') - offline_prices = LearnPythonCoursePrices.objects.filter( - course_type='Offline').order_by('price_range_price') - offline_prices_penza = LearnPythonCoursePrices.objects.filter( - course_type='OfflinePenza').order_by('price_range_price') - offline_prices_spb = LearnPythonCoursePrices.objects.filter( - course_type='OfflineSpb').order_by('price_range_price') - - # Student projects data - student_projects = list(GraduateProjects.objects.all()) - - # User stories - graduate_stories_list = list(GraduateStories.objects.all()) - - # User podcasts - podcasts_list = list(Podcasts.objects.all()) - - # Curators data - curators_list = Curators.objects.filter(curator_status=True) - - # Feedback data - student_feedback = list(Feedback.objects.all()) - - # Closed sessions - is_online_closed = current_course.online_session_closed +from .models import CourseReview, Curators, Enrollment, EnrollmentType, GraduateProjects +from .utils.utm_parser import get_utm_params - is_offline_closed = current_course.offline_session_closed +def index(request: HttpRequest) -> HttpResponse: + enrollment = Enrollment.get_enrollment_with_active_registration(enrollment_type=EnrollmentType.BASE) context = { - 'course': current_course, - 'projects': student_projects, - 'online_price_ranges': online_prices, - 'offline_price_ranges': offline_prices, - 'offline_price_penza_ranges': offline_prices_penza, - 'offline_price_spb_ranges': offline_prices_spb, - 'registration_closes_date': current_course.end_registration_date - .strftime( - '%b %d, %Y %H:%M:%S' - ), - 'student_feedback': student_feedback, - 'student_videos': [ - { - 'title': 'Как войти в разработку за считанные месяцы', - 'youtube_id': 'DkHWpgctTuA' - }, - { - 'title': 'Личный опыт джуниора: удачи, фейлы, рецепты', - 'youtube_id': 'vKKqsJ8IvAg' - }, - { - 'title': 'Python для врача и медицина для программиста.', - 'youtube_id': 's_ZNqjIW3ZA' - } - ], - 'curators_list': curators_list, - 'graduate_stories': graduate_stories_list, - 'podcasts_list': podcasts_list, + 'enrollment': enrollment, + 'projects': GraduateProjects.objects.all(), + 'registration_closes_date_formatted': ( + enrollment.end_registration_date.strftime('%b %d, %Y %H:%M:%S') + if enrollment else "" + ), + 'curators_list': Curators.objects.filter(is_visible=True), 'today': date.today(), - 'is_online_closed': is_online_closed, - 'is_offline_closed': is_offline_closed, - - } - return HttpResponse(template.render(context, request)) - - -def projects(request): - '''Docstring testc''' - template = loader.get_template('mainpage/projects.html') - - try: - current_course = LearnPythonCourse.objects.latest('course_index') - except LearnPythonCourse.DoesNotExist: - current_course = LearnPythonCourse() - - # Student projects data - student_projects_videos = list(GraduateProjectsVideos.objects.all().order_by('-project_course')) - - context = { - 'course': current_course, - 'student_projects': student_projects_videos, - 'today': date.today() - + 'reviews': CourseReview.objects.filter(review_for=EnrollmentType.BASE), + 'should_show_chat': switch_is_active('show_tg_chat_widget'), + 'utm_string': get_utm_params(request.GET.items()) } - return HttpResponse(template.render(context, request)) + return render(request, 'mainpage/index.html', context) + + +def advanced_handle(request: HttpRequest) -> HttpResponse: + enrollment = Enrollment.get_enrollment_with_active_registration(enrollment_type=EnrollmentType.ADVANCED) + return render( + request, + 'mainpage/advanced.html', + context={ + 'today': date.today(), + 'enrollment': enrollment, + 'reviews': CourseReview.objects.filter(review_for=EnrollmentType.ADVANCED), + 'utm_string': get_utm_params(request.GET.items()), + 'registration_closes_date_formatted': ( + enrollment.end_registration_date.strftime('%b %d, %Y %H:%M:%S') + if enrollment else "" + ), + }, + ) + + +def success_handle(request: HttpRequest) -> HttpResponse: + enrollment = Enrollment.get_enrollment_with_active_registration(enrollment_type=EnrollmentType.BASE) + return render( + request, + 'mainpage/success.html', + context={ + 'enrollment': enrollment, + }, + ) + + +def success_handle_advanced(request: HttpRequest) -> HttpResponse: + enrollment = Enrollment.get_enrollment_with_active_registration(enrollment_type=EnrollmentType.ADVANCED) + return render( + request, + 'mainpage/success.html', + context={ + 'enrollment': enrollment, + }, + ) diff --git a/landing_page/tests/__init__.py b/landing_page/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/landing_page/tests/conftest.py b/landing_page/tests/conftest.py new file mode 100644 index 00000000..170bc68a --- /dev/null +++ b/landing_page/tests/conftest.py @@ -0,0 +1,19 @@ +import datetime + +import pytest +from mainpage.models import Enrollment + + +@pytest.fixture +def active_enrollment() -> Enrollment: + now = datetime.datetime.now() + return Enrollment.objects.create( + timepad_event_id="123", + start_date=now + datetime.timedelta(days=3), + end_date=now + datetime.timedelta(days=10), + end_registration_date=now + datetime.timedelta(days=1), + early_price_rub=100, + late_price_rub=200, + early_price_date_to=now, + late_price_date_from=now, + ) diff --git a/landing_page/tests/test_index.py b/landing_page/tests/test_index.py new file mode 100644 index 00000000..4e2a29fd --- /dev/null +++ b/landing_page/tests/test_index.py @@ -0,0 +1,9 @@ +import pytest +from django.test import Client +from mainpage.models import Enrollment + + +@pytest.mark.django_db +def test__index__shows_up(client: Client, active_enrollment: Enrollment) -> None: + response = client.get('/') + assert response.status_code == 200 diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 00000000..5a1cb6e8 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,7 @@ +-r requirements.txt +flake8==7.0.0 +mypy==1.9.0 +isort==5.13.2 +pytest==8.1.1 +pytest-django==4.8.0 +types-requests==2.31.0.20240311 diff --git a/requirements.txt b/requirements.txt index 11cae60b..c2e09f6c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ -Django==2.2.17 -Pillow==7.2.0 -pytz==2020.1 +Django==5.0.3 +Pillow==10.2.0 +requests==2.31.0 +django-memoize==2.3.1 +django-waffle==4.1.0 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..ee0db5d7 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,28 @@ +[flake8] +exclude = migrations +max-line-length = 120 + + +[mypy] +ignore_missing_imports = True +disallow_incomplete_defs = True +no_implicit_optional = True +disallow_untyped_calls = True +warn_redundant_casts = True +warn_unused_ignores = True +disallow_untyped_defs = True +check_untyped_defs = True +exclude = migrations + +[isort] +combine_as_imports = true +default_section = THIRDPARTY +include_trailing_comma = true +use_parentheses = true +known_first_party = learn_bot +line_length = 120 +multi_line_output = 3 + + +[tool:pytest] +DJANGO_SETTINGS_MODULE = landing_page.settings