├── .gitignore ├── README.md ├── lesson1 ├── Lesson1.pdf └── homework.md ├── lesson10 ├── homework.md └── lecture_10.pdf ├── lesson11 ├── es.py ├── homework.md └── lesson11.pdf ├── lesson12 ├── docker │ ├── app1 │ │ ├── Dockerfile │ │ └── run.sh │ └── app2 │ │ ├── Dockerfile │ │ ├── db │ │ └── 1.txt │ │ ├── requirements.txt │ │ └── sample │ │ ├── db.sqlite3 │ │ ├── manage.py │ │ └── sample │ │ ├── __init__.py │ │ ├── asgi.py │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py ├── homework.md └── lesson-12.pdf ├── lesson13 └── lesson-13.pdf ├── lesson2 ├── homework.md └── lecture_2.pdf ├── lesson3 ├── homework.md └── lesson-3.pdf ├── lesson4 ├── homework.md ├── lesson-4.pdf └── snippets │ ├── .DS_Store │ └── project_files.py ├── lesson5 ├── homework.md └── lesson-5.pdf ├── lesson6 ├── lesson-6.pdf └── quiz.md ├── lesson7 ├── homework.md └── lesson-7.pdf ├── lesson8 ├── homework.md ├── lesson-8.pdf └── templates_and_styles │ ├── .DS_Store │ ├── base.html │ ├── home.html │ ├── index.css │ └── login.html └── lesson9 ├── async_url.py ├── block_socket.py ├── counter.py ├── generator_socket.py ├── homework.md ├── lecture_9.pdf ├── select_socket.py └── selectors_socket.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/* 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # technosphere_python_2021_spring 2 | Штучки (презентации и материал) для курса «Back-end разработка на Python». 3 | -------------------------------------------------------------------------------- /lesson1/Lesson1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_python_2021_spring/5ad10ce20d55181a434f8f16515822d246821bb0/lesson1/Lesson1.pdf -------------------------------------------------------------------------------- /lesson1/homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание к лекции №1 2 | 3 | ### 1. Написать консольную игру крестики-нолики в виде класса. 4 | 5 | Привожу пример того, как схематично можно изобразить класс игры. 6 | 7 | ```py 8 | class TicTacGame: 9 | 10 | def show_board(): 11 | pass 12 | 13 | def validate_input(): 14 | pass 15 | 16 | def start_game(): 17 | pass 18 | 19 | def check_winner(): 20 | pass 21 | 22 | if __name__ == '__main__': 23 | game = TicTac() 24 | game.start_game() 25 | 26 | ``` 27 | 28 | Пользовательский ввод осуществляется с помощью input, не забудьте валидировать пользовательский ввод и выводить человеческое описание ошибки. 29 | Чтобы вам было комфортнее писать тесты, в методе валидации вы можете возбуждать какое-то исключение в случае неверного ввода, а в методе, где вы выводите ошибку пользователю, обрабатывать это исключение. 30 | 31 | Схема класса не обязательно должна быть, как у меня, можете добавлять и менять методы, как считаете нужным, не забывайте про грамотную организацию кода, ненужное дублирование и код-лапшу. 32 | 33 | Если хотите, можете написать вспомогательную функцию, запустив которую, компьютер сыграет сам с собой без участия человека, либо сделать возможным игру между человеком и компьютером 34 | 35 | 36 | ### 2. Написать тесты (unittest) для игры, покрыв тестами основные методы 37 | 38 | ### 3. Проверить корректность и стиль кода с помощью pylint или flake8 39 | 40 | -------------------------------------------------------------------------------- /lesson10/homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание к лекции №10 2 | 3 | ### 1. Написать таск, который отправляет письмо админу при создании объекта в бд 4 | 5 | ### 2. Написать периодический таск на какое-либо действие (например считать количество пользователей в системе каждые 5 мин и записывать в файл) 6 | 7 | ### 3. Использовать flower для мониторинга задач 8 | 9 | ### 4. Установить и поднять centrifugo, прикрутить к проекту, продемонстрировать отправку какого-то сообщения с помощью websocket 10 | 11 | #### * При создании объекта в бд, отображать новый объект в списке объектов 12 | -------------------------------------------------------------------------------- /lesson10/lecture_10.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_python_2021_spring/5ad10ce20d55181a434f8f16515822d246821bb0/lesson10/lecture_10.pdf -------------------------------------------------------------------------------- /lesson11/es.py: -------------------------------------------------------------------------------- 1 | import elasticsearch 2 | from faker import Faker 3 | 4 | 5 | def main(): 6 | es = elasticsearch.Elasticsearch() 7 | index_name = "test_sphere_python" 8 | 9 | fake = Faker(locale="Ru_ru") 10 | for i in range(10): 11 | doc = { 12 | "title": fake.address(), 13 | "descr": fake.sentence(nb_words=5) 14 | } 15 | es.index(index=index_name, id=10 + i, body=doc) 16 | 17 | res = es.search(index=index_name, body={"query": {"match_all": {}}}) 18 | print(res) 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /lesson11/homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание #11 2 | 3 | ### Расстояние Левенштейна 4 | Написать функцию, которая будет считать расстояние Левенштейна между двумя словами. Написать тесты для неё, проверяющие корректность работы функции. 5 | 6 | 7 | ### Развернуть и наполнить тестовыми данными Elasticsearch; 8 | Данные должны соответствовать модели для Джанго-приложения из прошлых домашек. 9 | 10 | ### Реализовать поиск по пользователям, продуктам (сущностям); 11 | 12 | ### Реализовать метод API для поиска по указанным сущностям и отображения результатов. 13 | 14 | -------------------------------------------------------------------------------- /lesson11/lesson11.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_python_2021_spring/5ad10ce20d55181a434f8f16515822d246821bb0/lesson11/lesson11.pdf -------------------------------------------------------------------------------- /lesson12/docker/app1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | RUN apt update && apt -y install cowsay fortune 3 | RUN mkdir /app 4 | ADD ./run.sh /app/ 5 | WORKDIR /app/ 6 | 7 | ENTRYPOINT ["/app/run.sh"] 8 | -------------------------------------------------------------------------------- /lesson12/docker/app1/run.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | function main() 4 | { 5 | local message=$(/usr/games/fortune) 6 | if [ $# -ne 0 ]; then 7 | message="$@" 8 | fi 9 | echo "${message}" | /usr/games/cowsay -f tux 10 | } 11 | 12 | main $@ 13 | -------------------------------------------------------------------------------- /lesson12/docker/app2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-alpine 2 | RUN mkdir /app/ 3 | ADD . /app/ 4 | WORKDIR /app/ 5 | 6 | RUN pip install -r /app/requirements.txt 7 | 8 | ENTRYPOINT ["python", "/app/sample/manage.py", "runserver", "0.0.0.0:8000"] 9 | -------------------------------------------------------------------------------- /lesson12/docker/app2/db/1.txt: -------------------------------------------------------------------------------- 1 | hello 2 | -------------------------------------------------------------------------------- /lesson12/docker/app2/requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.3.4 2 | Django==3.2 3 | djangorestframework==3.12.4 4 | pytz==2021.1 5 | sqlparse==0.4.1 6 | -------------------------------------------------------------------------------- /lesson12/docker/app2/sample/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_python_2021_spring/5ad10ce20d55181a434f8f16515822d246821bb0/lesson12/docker/app2/sample/db.sqlite3 -------------------------------------------------------------------------------- /lesson12/docker/app2/sample/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sample.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /lesson12/docker/app2/sample/sample/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_python_2021_spring/5ad10ce20d55181a434f8f16515822d246821bb0/lesson12/docker/app2/sample/sample/__init__.py -------------------------------------------------------------------------------- /lesson12/docker/app2/sample/sample/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for sample project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sample.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /lesson12/docker/app2/sample/sample/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for sample project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.2. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.2/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | 15 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 16 | BASE_DIR = Path(__file__).resolve().parent.parent 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'django-insecure-2*iy-@zijk-*pqgvutq*m0bl3sw=q#6_dygyri)4+i00*ij#xy' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | ] 41 | 42 | MIDDLEWARE = [ 43 | 'django.middleware.security.SecurityMiddleware', 44 | 'django.contrib.sessions.middleware.SessionMiddleware', 45 | 'django.middleware.common.CommonMiddleware', 46 | 'django.middleware.csrf.CsrfViewMiddleware', 47 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 48 | 'django.contrib.messages.middleware.MessageMiddleware', 49 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 50 | ] 51 | 52 | ROOT_URLCONF = 'sample.urls' 53 | 54 | TEMPLATES = [ 55 | { 56 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 57 | 'DIRS': [], 58 | 'APP_DIRS': True, 59 | 'OPTIONS': { 60 | 'context_processors': [ 61 | 'django.template.context_processors.debug', 62 | 'django.template.context_processors.request', 63 | 'django.contrib.auth.context_processors.auth', 64 | 'django.contrib.messages.context_processors.messages', 65 | ], 66 | }, 67 | }, 68 | ] 69 | 70 | WSGI_APPLICATION = 'sample.wsgi.application' 71 | 72 | 73 | # Database 74 | # https://docs.djangoproject.com/en/3.2/ref/settings/#databases 75 | 76 | DATABASES = { 77 | 'default': { 78 | 'ENGINE': 'django.db.backends.sqlite3', 79 | 'NAME': BASE_DIR / 'db.sqlite3', 80 | } 81 | } 82 | 83 | 84 | # Password validation 85 | # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators 86 | 87 | AUTH_PASSWORD_VALIDATORS = [ 88 | { 89 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 90 | }, 91 | { 92 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 93 | }, 94 | { 95 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 96 | }, 97 | { 98 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 99 | }, 100 | ] 101 | 102 | 103 | # Internationalization 104 | # https://docs.djangoproject.com/en/3.2/topics/i18n/ 105 | 106 | LANGUAGE_CODE = 'en-us' 107 | 108 | TIME_ZONE = 'UTC' 109 | 110 | USE_I18N = True 111 | 112 | USE_L10N = True 113 | 114 | USE_TZ = True 115 | 116 | 117 | # Static files (CSS, JavaScript, Images) 118 | # https://docs.djangoproject.com/en/3.2/howto/static-files/ 119 | 120 | STATIC_URL = '/static/' 121 | 122 | # Default primary key field type 123 | # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field 124 | 125 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 126 | -------------------------------------------------------------------------------- /lesson12/docker/app2/sample/sample/urls.py: -------------------------------------------------------------------------------- 1 | """sample URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.2/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path 18 | 19 | urlpatterns = [ 20 | path('admin/', admin.site.urls), 21 | ] 22 | -------------------------------------------------------------------------------- /lesson12/docker/app2/sample/sample/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for sample project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sample.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /lesson12/homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание #12 2 | ### Установить docker и docker-compose 3 | ### Создание Dockerfile для Django-приложения 4 | ### Создание docker-compose для проекта: 5 | - nginx, 6 | - база данных, 7 | - elasticsearch, 8 | - Django-приложение. 9 | ### Создание Makefile для проекта; 10 | Преподаватель должен иметь возможность, имея установленными только git, docker и docker-compose склонировать проект, выполнить команды `make migrate` и увидеть успешную миграцию. 11 | -------------------------------------------------------------------------------- /lesson12/lesson-12.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_python_2021_spring/5ad10ce20d55181a434f8f16515822d246821bb0/lesson12/lesson-12.pdf -------------------------------------------------------------------------------- /lesson13/lesson-13.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_python_2021_spring/5ad10ce20d55181a434f8f16515822d246821bb0/lesson13/lesson-13.pdf -------------------------------------------------------------------------------- /lesson2/homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание к лекции №2 (объектная модель, метапрограммирование, память) 2 | 3 | ## 1. Реализовать класс, отнаследованный от списка 4 | При этом один список: 5 | - Можно вычитать из другого [5, 1, 3, 7] - [1, 2, 7] = [4, -1, -4, 7]; 6 | - Можно складывать с другим [5, 1, 3, 7] + [1, 2, 7] = [6, 3, 10, 7]; 7 | - Результатом сложения/вычитания должен быть новый кастомный список; 8 | - Сложение/вычитание также должно работать с обычными списками:
9 | [1, 2] +- CustomList([3, 4]) -> CustomList(...)
10 | CustomList([3, 4]) +- [1, 2] -> CustomList(...) 11 | - При неравной длине, дополнять меньший список нулями только на время выполнения операции. Исходные списки не должны изменяться; 12 | - При сравнении списков должна сравниваться сумма элементов списков; 13 | - Списки можно считать всегда числовыми; 14 | - На все должны быть тесты в отдельном модуле. 15 | 16 | ## 2. Написать метакласс, который в начале названий всех атрибутов и методов (кроме магических) добавляет префикс "custom_" (+тесты). 17 | class CustomMeta(): 18 | pass 19 | 20 | class CustomClass(metaclass=CustomMeta): 21 | x = 50 22 | 23 | def line(self): 24 | return 100 25 | 26 | inst = CustomClass() 27 | inst.custom_x 28 | inst.custom_line() 29 | 30 | inst.x # ошибка 31 | inst.line() # ошибка 32 | -------------------------------------------------------------------------------- /lesson2/lecture_2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_python_2021_spring/5ad10ce20d55181a434f8f16515822d246821bb0/lesson2/lecture_2.pdf -------------------------------------------------------------------------------- /lesson3/homework.md: -------------------------------------------------------------------------------- 1 | ## Домашнее задание 3 2 | 3 | ### Настроить nginx для отдачи статический файлов из public/ 4 | Для выполнения ДЗ необходимо установить gunicorn и nginx. 5 | Nginx должен быть установлен как системный пакет, а gunicorn как python библиотека в virtualenv. 6 | 7 | В nginx нужно создать виртуальный хост и настроить его на отдачу файлов из директории /home/username/projectname/public. 8 | Настройки nginx (точнее фрагмент содержащий вирт.хост) нужно положить в директорию проекта, что бы преподаватели могли его проверить. 9 | 10 | ### Создать простейшее WSGI приложение и запустить его с помощью gunicorn 11 | Далее нужно создать простое WSGI приложение (без использования Flask или других фреймворков). 12 | Приложение должно возвращать JSON документ с правильным MIME типом и содержимым вида {"time": <текущее время>, "url": }. 13 | Запустить [gunicorn](https://docs.gunicorn.org/en/stable/run.html) и проверить работу приложения из браузера. 14 | 15 | ### Настроить проксирование запросов на nginx 16 | Далее настроить Nginx для проксирования запросов к Gunicorn. 17 | Отличать запросы которые нужно проксировать от тех которые нужно отдавать с диска можно по префиксу URL. 18 | Все URL начинающиеся с API нужно проксировать на Gunicorn. 19 | 20 | ### Измерить производительность Nginx и Gunicorn c помощью ab или wrk. Добиться отказа системы 21 | Протестировать производительность Nginx и Gunicorn c помощью утили ab (пакет apache2-utils) или wrk. 22 | При тестировании нужно использовать минимум 4 потока и 5000 запросов. 23 | Протестировать: 24 | - отдачу статики Nginx; 25 | - отдачу динамических документов Gunicorn; 26 | - отдачу динамических документов Gunicorn при проксировании через Nginx; 27 | - подобрать параметры, которые приведут к отказу системы; 28 | 29 | -------------------------------------------------------------------------------- /lesson3/lesson-3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_python_2021_spring/5ad10ce20d55181a434f8f16515822d246821bb0/lesson3/lesson-3.pdf -------------------------------------------------------------------------------- /lesson4/homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание к лекции №4 2 | 3 | ### 1. Создать и запустить Django проект 4 | 5 | Изменить структуру проекта, чтобы дерево директорий было подобно: 6 | 7 | ```py 8 | messenger 9 | |---- users 10 | |---- chats 11 | |---- messages 12 | … 13 | |---- templates 14 | |---- static 15 | |---- manage.py 16 | |---- application 17 | |---- settings.py 18 | |---- urls.py 19 | |---- asgi.py 20 | 21 | ``` 22 | 23 | Для этого необходимо будет заменить в некотрых файлах пути до asgi и до settings 24 | Не забыть выполнить миграции и подлючить созданные приложения в installed apps. 25 | 26 | ### 2. Реализовать "заглушки" для методов API, используя JsonResponse 27 | 28 | (для проверки API можем использовать Postman и другие инструменты) 29 | 30 | 1. Создание объекта 31 | 2. Список объектов 32 | 3. Детальная информация об объекте 33 | 4. Функция, которая возвращает отрендеренный html (например, главная страница приложения) 34 | 35 | 36 | 37 | ### 3. Внутри контроллера обрабатывать только нужные методы (GET/POST) 38 | 39 | -------------------------------------------------------------------------------- /lesson4/lesson-4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_python_2021_spring/5ad10ce20d55181a434f8f16515822d246821bb0/lesson4/lesson-4.pdf -------------------------------------------------------------------------------- /lesson4/snippets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_python_2021_spring/5ad10ce20d55181a434f8f16515822d246821bb0/lesson4/snippets/.DS_Store -------------------------------------------------------------------------------- /lesson4/snippets/project_files.py: -------------------------------------------------------------------------------- 1 | # messenger/chats/urls.py 2 | 3 | 4 | from django.conf.urls import include 5 | from django.contrib import admin 6 | from django.http import JsonResponse 7 | from django.urls import path 8 | from chats.views import main 9 | 10 | # return {'chats': [{'id': chat.id, 'url': f'/detail/{chat.id}'}]} 11 | 12 | urlpatterns = [ 13 | path('single//', main, name='main'), 14 | ] 15 | 16 | 17 | # messenger/chats/views.py 18 | 19 | from django.http import JsonResponse 20 | from django.shortcuts import render 21 | from django.urls import reverse 22 | 23 | 24 | def main(request, chat_id): 25 | print('id:', chat_id) 26 | print(reverse('main', kwargs={'chat_id': 123})) 27 | return JsonResponse({'status': 'ok'}) 28 | 29 | 30 | #messenger/messenger/urls.py 31 | 32 | from django.conf.urls import include 33 | from django.contrib import admin 34 | from django.http import JsonResponse 35 | from django.urls import path 36 | 37 | urlpatterns = [ 38 | path('admin/', admin.site.urls), 39 | path('chats/', include('chats.urls')), 40 | ] 41 | 42 | 43 | # messenger/messenger/local_settings.py 44 | 45 | DEBUG = True 46 | 47 | 48 | # messenger/messenger/settings.py - что меняли 49 | 50 | ALLOWED_HOSTS = ['*'] 51 | 52 | INSTALLED_APPS = [ 53 | 'django.contrib.admin', 54 | 'django.contrib.auth', 55 | 'django.contrib.contenttypes', 56 | 'django.contrib.sessions', 57 | 'django.contrib.messages', 58 | 'django.contrib.staticfiles', 59 | 'chats', 60 | ] 61 | 62 | LANGUAGE_CODE = 'ru-Ru' 63 | 64 | try: 65 | from .local_settings import * 66 | except ImportError: 67 | pass -------------------------------------------------------------------------------- /lesson5/homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание к лекции №5 2 | 3 | ### Установить Postgres, создать нового пользователя и БД и настроить доступ 4 | Необходимо создать нового пользователя, создать базу данных, принадлежащему этому пользователю. 5 | 6 | ### Спроектировать базу данных проекта, подготовить модели и мигрировать их в БД 7 | На основе домашнего задания №4 необходимо подключить к Django-проекту созданную базу данных, сделать миграции и применить 8 | эти миграции. 9 | 10 | Необходимо реализовать одно из отношений: 11 | - Один-ко-многим; 12 | - Многие-ко-многим; 13 | - Один-к-одному. 14 | 15 | Затем закоммитить новые файлы (модели, миграции и т.д., но не логин/пароль от базы данных!). 16 | -------------------------------------------------------------------------------- /lesson5/lesson-5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_python_2021_spring/5ad10ce20d55181a434f8f16515822d246821bb0/lesson5/lesson-5.pdf -------------------------------------------------------------------------------- /lesson6/lesson-6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_python_2021_spring/5ad10ce20d55181a434f8f16515822d246821bb0/lesson6/lesson-6.pdf -------------------------------------------------------------------------------- /lesson6/quiz.md: -------------------------------------------------------------------------------- 1 | # Квиз по ORM 2 | 3 | 1. В миграциях всегда указываются зависимости 4 | - **Да** 5 | - Нет 6 | 7 | 2. python manage.py makemigrations --empty yourappname 8 | - Заменит последнюю миграцию на новую 9 | - Отменит все примененные миграции 10 | - **Создаст новую миграцию** 11 | - Создаст фейковую миграцию 12 | 13 | 3. Для чего нужно apps.get_model('some_app', 'SomeModel') 14 | - Для того, чтобы брать модели из кеша 15 | - **Для того, чтобы избежать циклических импортов или импортировать модели тогда, когда они могут быть еще не доступны** 16 | - Вообще так лучше делать всегда, когда хотим импортировать модель 17 | 18 | 4. Как сделать так, чтобы модель была доступна в админке 19 | - **admin.site.register(Chat, ChatAdmin)** 20 | - admin.site.bind(Chat) 21 | - register(ChatAdmin, admin=True) 22 | - @admin.site.register 23 | 24 | 5. Какой метод отвечает за строковое представление модели в python3 25 | - \_\_unicode\_\_ 26 | - \_\_repr\_\_ 27 | - **\_\_str\_\_** 28 | 29 | 6. class Meta отвечает за 30 | - Конфигуцию приложения 31 | - Конфигурацию полей модели 32 | - **Конфигурацию модели** 33 | 34 | 7. Что такое "objects" в Tag.objects.create(slug=’some_tag’)? 35 | - Model 36 | - Queryset 37 | - **ModelManager** 38 | - Related Model Manager 39 | - Object 40 | - Метод экземпляра 41 | 42 | 8. Нужен ли save после post.tags.add(tag) 43 | - Да 44 | - **Нет** 45 | 46 | 9. Какое исключение, если объект не будет найден Post.objects.get(id=5) * 47 | - **Post.DoesNotExist** 48 | - DoesNotExist 49 | - Posts.DoesNotExists 50 | - NoObjectsReturned 51 | 52 | 10. Какой sql будет построен по запросу Post.objects.filter( title__contains='hello') * 53 | - **SELECT "blog_post"."id", "blog_post"."title", "blog_post"."category_id", "blog_post"."author_id" FROM "blog_post" WHERE "blog_post"."title" LIKE %hello%** 54 | - SELECT "blog_post"."id", "blog_post"."title", "blog_post"."category_id", "blog_post"."author_id" FROM "blog_post" WHERE "blog_post"."title" = hello 55 | - SELECT "blog_post"."id", "blog_post"."title", "blog_post"."category_id", "blog_post"."author_id" FROM "blog_post" WHERE "blog_post"."title" IN (h, e, l, o) 56 | - SELECT "blog_post"."id", "blog_post"."title", "blog_post"."category_id", "blog_post"."author_id" FROM "blog_post" INNER JOIN "blog_category" ON ("blog_post"."category_id" = "blog_category"."id") WHERE "blog_category"."title" LIKE %hello% 57 | 58 | 11. Какой объект вернется Post.objects.filter(category_id=1) 59 | - **Queryset** 60 | - List 61 | - ModelManager 62 | 63 | 12. Что возвращает метод values_list? 64 | - Список словарей с данными и описание колонок 65 | - **Список кортежей с данными или только список полей** 66 | - Список колонок 67 | 68 | 13. Post.objects.annotate(Count('tags')) 69 | - Посчитает суммарное количество всех тегов 70 | - **Посчитает количество тегов в каждом посте** 71 | - Посчитает максимальное количество тегов в посте -------------------------------------------------------------------------------- /lesson7/homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание к лекции №7 2 | 3 | Это домашнее задание должно быть выполненно на основе домашнего задания #4. 4 | 5 | ### Добавить в проект djangorestframework 6 | Установить, подключить --- всё просто. 7 | 8 | ### Переписать заглушки всех предыдущих методов 9 | Для этого понадобиться определить для каждой модели свой собственный Serializer (serializers.py), классы ModelViewSet, добавить маршруты для DRF. 10 | 11 | ### Написать один или несколько форм для валидации форм 12 | Например написать форму для регистрации/авторизации пользователя. 13 | -------------------------------------------------------------------------------- /lesson7/lesson-7.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_python_2021_spring/5ad10ce20d55181a434f8f16515822d246821bb0/lesson7/lesson-7.pdf -------------------------------------------------------------------------------- /lesson8/homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание к лекции №8 2 | 3 | ### Реализовать OAuth2-авторизацию 4 | Через любую социальную сеть, используя библиотеку social-auth-app-django. Должна быть готова страница с кнопкой, по нажатию на которую произойдет механизм авторизации. Также для авторизованного пользователя должна отображаться кнопка логаута. 5 | 6 | ### Написать декоратор, проверяющий авторизацию при вызовах API 7 | На все функции view, требующие чтобы пользователь был залогинен в системе, навесить САМОПИСНЫЙ декоратор, проверяющий есть ли пользователь в объекте HttpRequest, иначе - редирект на страницу логина 8 | 9 | ### Изменить запросы и код API 10 | 11 | Изменить вьюхи так, чтобы в них учитывался текущий пользователь. 12 | Допустим, у вас функция, возвращающая заказы пользователя. Тогда ваш код был: 13 | 14 | ```python 15 | def get_orders(request): 16 | orders = Order.objects.all() 17 | return JsonResponse({'orders': [{'id': order.id, 'date': order.date} for order in orders]}) 18 | ``` 19 | 20 | Станет: 21 | 22 | ```python 23 | @login_required 24 | def get_orders(request): 25 | # берем закаты текущего пользователя из запроса 26 | orders = Order.objects.filter(user=request.user) 27 | return JsonResponse({'orders': [{'id': order.id, 'date': order.date} for order in orders]}) 28 | ``` -------------------------------------------------------------------------------- /lesson8/lesson-8.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_python_2021_spring/5ad10ce20d55181a434f8f16515822d246821bb0/lesson8/lesson-8.pdf -------------------------------------------------------------------------------- /lesson8/templates_and_styles/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_python_2021_spring/5ad10ce20d55181a434f8f16515822d246821bb0/lesson8/templates_and_styles/.DS_Store -------------------------------------------------------------------------------- /lesson8/templates_and_styles/base.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | Social Auth with Django 12 | 13 | 14 |
15 |
16 |

{% block title %}{% endblock %}

17 |
18 | {% block content %} 19 | {% endblock %} 20 |
21 |
22 |
23 | 24 | -------------------------------------------------------------------------------- /lesson8/templates_and_styles/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block title %} Home {% endblock %} 3 | {% block content %} 4 |
5 |
6 |

Welcome {% if user.first_name %}{{ user.first_name }}{% else %}{{ user.username }}{% endif %}

7 |
8 |
9 | {% endblock %} -------------------------------------------------------------------------------- /lesson8/templates_and_styles/index.css: -------------------------------------------------------------------------------- 1 | img { 2 | border: 3px solid #282c34; 3 | } 4 | 5 | .row { 6 | width: 100%; 7 | margin: 0; 8 | } 9 | 10 | .container-fluid { 11 | height: 100vh; 12 | background-color: #282c34; 13 | display: flex; 14 | justify-content: center; 15 | align-items: center; 16 | } 17 | 18 | .container-fluid > div { 19 | width: 85%; 20 | min-width: 300px; 21 | max-width: 500px; 22 | } 23 | 24 | .card { 25 | width: 100%; 26 | } 27 | 28 | .social-container { 29 | display: flex; 30 | flex-direction: column; 31 | justify-content: center; 32 | } 33 | 34 | .btn a, .btn a:hover { 35 | color: white; 36 | text-decoration: none ; 37 | } -------------------------------------------------------------------------------- /lesson8/templates_and_styles/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block title %} Sign in {% endblock %} 3 | {% block content %} 4 |
5 |
6 | 17 |
18 |
19 | {% endblock %} -------------------------------------------------------------------------------- /lesson9/async_url.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import aiohttp 3 | import time 4 | 5 | 6 | URL = "https://loremflickr.com/320/240" 7 | 8 | 9 | async def download(url, session): 10 | async with session.get(url, allow_redirects=True) as resp: 11 | data = await resp.read() 12 | with open(f"photo_{time.time()}", "wb") as f: 13 | f.write(data) 14 | 15 | 16 | async def run(): 17 | t1 = time.time() 18 | async with aiohttp.ClientSession() as session: 19 | for i in range(10): 20 | await download(URL, session) 21 | 22 | t2 = time.time() 23 | print("single", t2 - t1) 24 | 25 | tasks = [] 26 | t1 = time.time() 27 | async with aiohttp.ClientSession() as session: 28 | for i in range(10): 29 | tasks.append(asyncio.create_task(download(URL, session))) 30 | await asyncio.gather(*tasks) 31 | 32 | t2 = time.time() 33 | print("gather", t2 - t1) 34 | 35 | 36 | if __name__ == "__main__": 37 | asyncio.run(run()) 38 | -------------------------------------------------------------------------------- /lesson9/block_socket.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 4 | server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 5 | server_sock.bind(('localhost', 15000)) 6 | server_sock.listen() 7 | 8 | while True: 9 | print('before accept') 10 | client_sock, addr = server_sock.accept() 11 | print('connect from', addr) 12 | 13 | while True: 14 | data = client_sock.recv(4096) 15 | 16 | if not data: 17 | break 18 | else: 19 | client_sock.send(data.decode().upper().encode()) 20 | 21 | client_sock.close() 22 | -------------------------------------------------------------------------------- /lesson9/counter.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import time 3 | 4 | N = 100_000_000 5 | 6 | 7 | def count(n): 8 | while n > 0: 9 | n -= 1 10 | 11 | 12 | def run(): 13 | t1 = time.time() 14 | count(N) 15 | t2 = time.time() 16 | print("singe time", t2 - t1) 17 | 18 | 19 | n_threads = 10 20 | 21 | threads = [ 22 | threading.Thread(target=count, args=(N // n_threads,)) 23 | for i in range(n_threads) 24 | ] 25 | t1 = time.time() 26 | for th in threads: 27 | th.start() 28 | for th in threads: 29 | th.join() 30 | 31 | t2 = time.time() 32 | print("thread time", t2 - t1) 33 | 34 | 35 | if __name__ == "__main__": 36 | run() 37 | -------------------------------------------------------------------------------- /lesson9/generator_socket.py: -------------------------------------------------------------------------------- 1 | # David Beazley algo 2 | import socket 3 | from select import select 4 | 5 | 6 | tasks = [] 7 | # sock: gen 8 | to_read = {} 9 | to_write = {} 10 | 11 | 12 | def server(): 13 | server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 14 | server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 15 | server_sock.bind(('localhost', 15000)) 16 | server_sock.listen() 17 | 18 | while True: 19 | yield 'read', server_sock 20 | client_sock, addr = server_sock.accept() # read 21 | print('connect from', addr) 22 | 23 | tasks.append(client(client_sock)) 24 | 25 | 26 | def client(client_sock): 27 | while True: 28 | yield 'read', client_sock 29 | data = client_sock.recv(4096) # read 30 | 31 | if not data: 32 | break 33 | else: 34 | yield 'write', client_sock 35 | client_sock.send(data.decode().upper().encode()) # write 36 | 37 | client_sock.close() 38 | 39 | 40 | def event_loop(): 41 | while any([tasks, to_read, to_write]): 42 | 43 | while not tasks: 44 | ready_to_read, ready_to_write, _ = select(to_read, to_write, []) 45 | 46 | for sock in ready_to_read: 47 | tasks.append(to_read.pop(sock)) 48 | 49 | for sock in ready_to_write: 50 | tasks.append(to_write.pop(sock)) 51 | 52 | try: 53 | task = tasks.pop(0) 54 | op_type, sock = next(task) 55 | 56 | if op_type == 'read': 57 | to_read[sock] = task 58 | elif op_type == 'write': 59 | to_write[sock] = task 60 | 61 | except StopIteration: 62 | pass 63 | 64 | 65 | if __name__ == '__main__': 66 | tasks.append(server()) 67 | event_loop() 68 | -------------------------------------------------------------------------------- /lesson9/homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание к лекции №9 2 | 3 | ## 1. Клиент-серверное приложение для обкачки урлов 4 | ### Написание сервера 5 | Написать master-worker cервер (количество воркеров задаётся при запуске) для обработки запросов от клиента. 6 | 7 | Алгоритм должен быть следующим: 8 | 9 | - Мастер и воркеры это разные потоки в едином приложении; 10 | - Мастер слушает порт, на который клиент будет по TCP отправлять урлы для обкачки; 11 | - Мастер принимает запрос, читает url от клиента и передаёт этот url одному из воркеров; 12 | - Воркер обкачивает url по https и возвращает клиенту топ K самых частых слов и их количества в формате json; 13 | - После каждого обработанного урла сервер должен вывести статистику: сколько урлов было обработано на данный момент суммарно всеми воркерами; 14 | 15 | `python server.py -w 10 -k 7` (сервер использует 10 воркеров для обкачки и отправляет клиенту топ-7 частых слов) 16 | 17 | 18 | ### Написание клиента 19 | Утилита, отправляющая по TCP запросы с урлами серверу. 20 | Нужно сделать следующее: 21 | 22 | - Подготовить файл с запросами (порядка 100 URL разных); 23 | - На вход клиенту передаётся два аргумента --- файл с URL'ами и M (количество потоков); 24 | - Клиент отправляет параллельно M запросов на сервер и печатает ответ сервера в стандартый вывод, то есть, например: `xxx.com: {'word1': 100, 'word2': 50}`. 25 | 26 | `python client.py 10 urls.txt` 27 | 28 | 29 | Все действия должны быть выделены в функции. 30 | 31 | 32 | ## 2. Скрипт для асинхронной обкачки урлов 33 | Написать скрипт для обкачки списка урлов с возможностью задавать количество одновременных запросов, используя асинхронное программирование. 34 | Клиент можно использовать любой, например, из aiohttp. Так, 10 одновременных запросов могут задаваться командой: 35 | `python fetcher.py -c 10 urls.txt` 36 | или 37 | `python fetcher.py 10 urls.txt` 38 | -------------------------------------------------------------------------------- /lesson9/lecture_9.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_python_2021_spring/5ad10ce20d55181a434f8f16515822d246821bb0/lesson9/lecture_9.pdf -------------------------------------------------------------------------------- /lesson9/select_socket.py: -------------------------------------------------------------------------------- 1 | import socket 2 | from select import select 3 | 4 | to_monitor = [] 5 | 6 | server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 7 | server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 8 | server_sock.bind(('localhost', 15000)) 9 | server_sock.listen() 10 | 11 | 12 | def accept_conn(server_sock): 13 | client_sock, addr = server_sock.accept() 14 | print('Connect', addr) 15 | to_monitor.append(client_sock) 16 | 17 | 18 | def respond(client_sock): 19 | data = client_sock.recv(4096) 20 | 21 | if data: 22 | client_sock.send(data.decode().upper().encode()) 23 | else: 24 | client_sock.close() 25 | to_monitor.remove(client_sock) 26 | 27 | 28 | def event_loop(): 29 | while True: 30 | ready_to_read, _, _ = select(to_monitor, [], []) # read, write, err 31 | for sock in ready_to_read: 32 | if sock is server_sock: 33 | accept_conn(sock) 34 | else: 35 | respond(sock) 36 | 37 | 38 | if __name__ == '__main__': 39 | to_monitor.append(server_sock) 40 | event_loop() 41 | -------------------------------------------------------------------------------- /lesson9/selectors_socket.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import selectors 3 | 4 | selector = selectors.DefaultSelector() 5 | print('selector', selector) 6 | 7 | 8 | def server(): 9 | server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 10 | server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 11 | server_sock.bind(('localhost', 15000)) 12 | server_sock.listen() 13 | 14 | selector.register(server_sock, selectors.EVENT_READ, accept_conn) 15 | 16 | 17 | def accept_conn(server_sock): 18 | client_sock, addr = server_sock.accept() 19 | print('Connect', addr) 20 | selector.register(client_sock, selectors.EVENT_READ, respond) 21 | 22 | 23 | def respond(client_sock): 24 | data = client_sock.recv(4096) 25 | 26 | if data: 27 | client_sock.send(data.decode().upper().encode()) 28 | else: 29 | selector.unregister(client_sock) 30 | client_sock.close() 31 | 32 | 33 | def event_loop(): 34 | while True: 35 | events = selector.select() # (key, events_mask) 36 | 37 | for key, _ in events: 38 | # key: NamedTuple(fileobj, events, data) 39 | callback = key.data 40 | callback(key.fileobj) 41 | 42 | 43 | if __name__ == '__main__': 44 | server() 45 | event_loop() 46 | --------------------------------------------------------------------------------