1
0
Fork 0
mirror of https://github.com/EDeev/yandex_lyceum.git synced 2026-10-08 04:59:28 +03:00

Compare commits

...

4 commits

Author SHA1 Message Date
8e0dca0207 README на русском и английском, скриншот 2026-10-02 21:18:33 +00:00
e892a11052 CI: проверка синтаксиса (ruff) и импорт обоих Flask-приложений 2026-10-02 21:18:33 +00:00
968588eea1 Ключ API Яндекс.Карт и SECRET_KEY из окружения, зависимости только используемые
requirements.txt сокращён до Flask, Flask-WTF, WTForms, requests и Pillow; Werkzeug
закреплён на 2.3 — с Werkzeug 3 Flask-WTF 1.1 не импортируется.
2026-10-02 21:18:33 +00:00
37d30ee29f Порядок: дубль удалён, папки названы по содержанию
«WEB. HTML 2» повторял «WEB. HTML flask-wtf» (отличались только два маршрута) — оставлена
более поздняя версия. Папки: 01-flask-html, 02-flask-wtf-forms, 03-yandex-maps-search.
Переводы строк приведены к LF.
2026-10-02 21:18:33 +00:00
45 changed files with 726 additions and 997 deletions

3
.gitattributes vendored Normal file
View file

@ -0,0 +1,3 @@
* text=auto eol=lf
*.png binary
*.jpg binary

22
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,22 @@
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install ruff==0.14.0 -r requirements.txt
- name: Ruff (синтаксис и ошибки)
run: ruff check --select E9,F .
- name: Приложения импортируются
run: |
cd 01-flask-html && python -c "import server" && cd ..
cd 02-flask-wtf-forms && python -c "import server"

View file

@ -1,273 +1,273 @@
from flask import Flask, url_for, request from flask import Flask, url_for, request
app = Flask(__name__) app = Flask(__name__)
@app.route('/') @app.route('/')
def index(): def index():
return "Миссия Колонизация Марса" return "Миссия Колонизация Марса"
@app.route('/index') @app.route('/index')
def gimn(): def gimn():
return 'И на Марсе будут яблони цвести!' return 'И на Марсе будут яблони цвести!'
@app.route('/promotion') @app.route('/promotion')
def promo(): def promo():
return '</br>'.join(['Человечество вырастает из детства.', return '</br>'.join(['Человечество вырастает из детства.',
'Человечеству мала одна планета.', 'Человечеству мала одна планета.',
'Мы сделаем обитаемыми безжизненные пока планеты.', 'Мы сделаем обитаемыми безжизненные пока планеты.',
'И начнем с Марса!', 'Присоединяйся!']) 'И начнем с Марса!', 'Присоединяйся!'])
@app.route('/image_mars') @app.route('/image_mars')
def img(): def img():
return f"""<!doctype html> return f"""<!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Привет, Марс!</title> <title>Привет, Марс!</title>
</head> </head>
<body> <body>
<h1>Жди нас, Марс!</h1> <h1>Жди нас, Марс!</h1>
<img src="{url_for('static', filename='img/planet.jpg')}"></br> <img src="{url_for('static', filename='img/planet.jpg')}"></br>
<a>Вот она какая, красная планета.</a> <a>Вот она какая, красная планета.</a>
</body> </body>
</html>""" </html>"""
@app.route('/promotion_image') @app.route('/promotion_image')
def promo_img(): def promo_img():
return f"""<!doctype html> return f"""<!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="{url_for('static', filename='css/style.css')}" /> <link rel="stylesheet" type="text/css" href="{url_for('static', filename='css/style.css')}" />
<title>Колонизация</title> <title>Колонизация</title>
</head> </head>
<body> <body>
<h1 class="red" >Жди нас, Марс!</h1> <h1 class="red" >Жди нас, Марс!</h1>
<img src="{url_for('static', filename='img/planet.jpg')}"></br> <img src="{url_for('static', filename='img/planet.jpg')}"></br>
<div class="font black"><b>Человечество вырастает из детства.</b></div></br> <div class="font black"><b>Человечество вырастает из детства.</b></div></br>
<div class="font green"><b>Человечеству мала одна планета.</b></div></br> <div class="font green"><b>Человечеству мала одна планета.</b></div></br>
<div class="font gray"><b>Мы сделаем обитаемыми безжизненные пока планеты.</b></div></br> <div class="font gray"><b>Мы сделаем обитаемыми безжизненные пока планеты.</b></div></br>
<div class="font yellow"><b>И начнем с Марса!</b></div></br> <div class="font yellow"><b>И начнем с Марса!</b></div></br>
<div class="font red"><b>Присоединяйся!</b></div> <div class="font red"><b>Присоединяйся!</b></div>
</body> </body>
</html>""" </html>"""
@app.route('/astronaut_selection', methods=['POST', 'GET']) @app.route('/astronaut_selection', methods=['POST', 'GET'])
def form_sample(): def form_sample():
if request.method == 'GET': if request.method == 'GET':
return f'''<!doctype html> return f'''<!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" <link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css"
integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1"
crossorigin="anonymous"> crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="{url_for('static', filename='css/style.css')}" /> <link rel="stylesheet" type="text/css" href="{url_for('static', filename='css/style.css')}" />
<title>Отбор астронавтов</title> <title>Отбор астронавтов</title>
</head> </head>
<body> <body>
<h1 style="text-align:center">Анкета претендента</h1> <h1 style="text-align:center">Анкета претендента</h1>
<h2 style="text-align:center">на участие в миссии</h2> <h2 style="text-align:center">на участие в миссии</h2>
<div> <div>
<form class="login_form" method="post"> <form class="login_form" method="post">
<input type="text" class="form-control" id="text" placeholder="Введите фамилию" name="text1"> <input type="text" class="form-control" id="text" placeholder="Введите фамилию" name="text1">
<input type="text" class="form-control" id="text" placeholder="Введите имя" name="text2"></br> <input type="text" class="form-control" id="text" placeholder="Введите имя" name="text2"></br>
<input type="email" class="form-control" id="email" aria-describedby="emailHelp" placeholder="Введите адрес почты" name="email"> <input type="email" class="form-control" id="email" aria-describedby="emailHelp" placeholder="Введите адрес почты" name="email">
<div class="form-group"> <div class="form-group">
<label for="classSelect">Какое у Вас образование?</label> <label for="classSelect">Какое у Вас образование?</label>
<select class="form-control" id="classSelect" name="class"> <select class="form-control" id="classSelect" name="class">
<option>Начальное</option> <option>Начальное</option>
<option>Среднее оконченное</option> <option>Среднее оконченное</option>
<option>Среднее неоконченное</option> <option>Среднее неоконченное</option>
<option>Высшее оконченное</option> <option>Высшее оконченное</option>
<option>Высшее неоконченное</option> <option>Высшее неоконченное</option>
</select> </select>
</div></br> </div></br>
<label for="classSelect">Какие у Вас есть профессии?</label> <label for="classSelect">Какие у Вас есть профессии?</label>
<div class="form-group form-check"> <div class="form-group form-check">
<input type="checkbox" class="form-check-input" id="acceptRules" name="accept1"> <input type="checkbox" class="form-check-input" id="acceptRules" name="accept1">
<label class="form-check-label" for="acceptRules">Инженер-исследователь</label></br> <label class="form-check-label" for="acceptRules">Инженер-исследователь</label></br>
<input type="checkbox" class="form-check-input" id="acceptRules" name="accept2"> <input type="checkbox" class="form-check-input" id="acceptRules" name="accept2">
<label class="form-check-label" for="acceptRules">Инженер-строитель</label></br> <label class="form-check-label" for="acceptRules">Инженер-строитель</label></br>
<input type="checkbox" class="form-check-input" id="acceptRules" name="accept3"> <input type="checkbox" class="form-check-input" id="acceptRules" name="accept3">
<label class="form-check-label" for="acceptRules">Пилот</label></br> <label class="form-check-label" for="acceptRules">Пилот</label></br>
<input type="checkbox" class="form-check-input" id="acceptRules" name="accept4"> <input type="checkbox" class="form-check-input" id="acceptRules" name="accept4">
<label class="form-check-label" for="acceptRules">Метеоролог</label></br> <label class="form-check-label" for="acceptRules">Метеоролог</label></br>
<input type="checkbox" class="form-check-input" id="acceptRules" name="accept5"> <input type="checkbox" class="form-check-input" id="acceptRules" name="accept5">
<label class="form-check-label" for="acceptRules">Инженер по жизнеобеспечению</label></br> <label class="form-check-label" for="acceptRules">Инженер по жизнеобеспечению</label></br>
<input type="checkbox" class="form-check-input" id="acceptRules" name="accept6"> <input type="checkbox" class="form-check-input" id="acceptRules" name="accept6">
<label class="form-check-label" for="acceptRules">Инженер по радиационной защите</label></br> <label class="form-check-label" for="acceptRules">Инженер по радиационной защите</label></br>
<input type="checkbox" class="form-check-input" id="acceptRules" name="accept7"> <input type="checkbox" class="form-check-input" id="acceptRules" name="accept7">
<label class="form-check-label" for="acceptRules">Врач</label></br> <label class="form-check-label" for="acceptRules">Врач</label></br>
<input type="checkbox" class="form-check-input" id="acceptRules" name="accept8"> <input type="checkbox" class="form-check-input" id="acceptRules" name="accept8">
<label class="form-check-label" for="acceptRules">Экзобиолог</label></br> <label class="form-check-label" for="acceptRules">Экзобиолог</label></br>
</div></br> </div></br>
<div class="form-group"> <div class="form-group">
<label for="form-check">Укажите пол</label> <label for="form-check">Укажите пол</label>
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="radio" name="sex" id="male" value="male"> <input class="form-check-input" type="radio" name="sex" id="male" value="male">
<label class="form-check-label" for="male"> <label class="form-check-label" for="male">
Мужской Мужской
</label> </label>
</div> </div>
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="radio" name="sex" id="female" value="female"> <input class="form-check-input" type="radio" name="sex" id="female" value="female">
<label class="form-check-label" for="female"> <label class="form-check-label" for="female">
Женский Женский
</label> </label>
</div> </div>
</div></br> </div></br>
<div class="form-group"> <div class="form-group">
<label for="about">Почему Вы хотите принять участие в миссии?</label> <label for="about">Почему Вы хотите принять участие в миссии?</label>
<textarea class="form-control" id="about" rows="3" name="about"></textarea> <textarea class="form-control" id="about" rows="3" name="about"></textarea>
</div></br> </div></br>
<div class="form-group"> <div class="form-group">
<label for="photo">Приложите фотографию</label></br> <label for="photo">Приложите фотографию</label></br>
<input type="file" class="form-control-file" id="photo" name="file"> <input type="file" class="form-control-file" id="photo" name="file">
</div></br> </div></br>
<div class="form-group form-check"> <div class="form-group form-check">
<input type="checkbox" class="form-check-input" id="acceptRules" name="accept"> <input type="checkbox" class="form-check-input" id="acceptRules" name="accept">
<label class="form-check-label" for="acceptRules">Готовы остаться на Марсе?</label> <label class="form-check-label" for="acceptRules">Готовы остаться на Марсе?</label>
</div></br> </div></br>
<button type="submit" class="btn btn-primary">Отправить</button> <button type="submit" class="btn btn-primary">Отправить</button>
</form> </form>
</div> </div>
</body> </body>
</html>''' </html>'''
elif request.method == 'POST': elif request.method == 'POST':
print("Фамилия -", request.form.get('text1')) print("Фамилия -", request.form.get('text1'))
print("Имя -", request.form.get('text2')) print("Имя -", request.form.get('text2'))
print("Почта -", request.form.get('email')) print("Почта -", request.form.get('email'))
print("Образование -", request.form.get('class')) print("Образование -", request.form.get('class'))
prof = [] prof = []
typis = {'accept1': "Инженер-исследователь", 'accept2': "Инженер-строитель", 'accept3': "Пилот", typis = {'accept1': "Инженер-исследователь", 'accept2': "Инженер-строитель", 'accept3': "Пилот",
'accept4': "Метеоролог", 'accept5': 'Инженер по жизнеобеспечению', 'accept4': "Метеоролог", 'accept5': 'Инженер по жизнеобеспечению',
'accept6': 'Инженер по радиационной защите', 'accept7': "Врач", 'accept8': "Экзобиолог"} 'accept6': 'Инженер по радиационной защите', 'accept7': "Врач", 'accept8': "Экзобиолог"}
for i in typis: for i in typis:
a = request.form.get(i) a = request.form.get(i)
if a: if a:
prof.append(typis[i]) prof.append(typis[i])
print("Профкссия(-и):", ", ".join(prof)) print("Профкссия(-и):", ", ".join(prof))
print("Пол -", request.form.get('sex')) print("Пол -", request.form.get('sex'))
print("Причины участия -", request.form.get('about')) print("Причины участия -", request.form.get('about'))
print("Фото -", request.form.get('file')) print("Фото -", request.form.get('file'))
if request.form.get('accept'): if request.form.get('accept'):
print("Подписан на программу") print("Подписан на программу")
else: else:
print("Отказался от программы") print("Отказался от программы")
return "Форма отправлена" return "Форма отправлена"
@app.route('/choice/<planet_name>') @app.route('/choice/<planet_name>')
def choice(planet_name): def choice(planet_name):
return f"""<!doctype html> return f"""<!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="{url_for('static', filename='css/style.css')}" /> <link rel="stylesheet" type="text/css" href="{url_for('static', filename='css/style.css')}" />
<title>Колонизация</title> <title>Колонизация</title>
</head> </head>
<body> <body>
<h1 class="font">Моё предложение: {planet_name}</h1> <h1 class="font">Моё предложение: {planet_name}</h1>
<h1>Эта планета близка к Земле;</h1> <h1>Эта планета близка к Земле;</h1>
<div class="font green"><b>На ней много необходимых ресурсов;</b></div></br> <div class="font green"><b>На ней много необходимых ресурсов;</b></div></br>
<div class="font gray"><b>На ней есть вода и атмосфера;</b></div></br> <div class="font gray"><b>На ней есть вода и атмосфера;</b></div></br>
<div class="font yellow"><b>На ней есть небольшое магнитное поле;</b></div></br> <div class="font yellow"><b>На ней есть небольшое магнитное поле;</b></div></br>
<div class="font red"><b>Наконец, она просто красива!</b></div></br> <div class="font red"><b>Наконец, она просто красива!</b></div></br>
</body> </body>
</html>""" </html>"""
@app.route('/results/<nickname>/<int:level>/<float:rating>') @app.route('/results/<nickname>/<int:level>/<float:rating>')
def result(nickname, level, rating): def result(nickname, level, rating):
return f"""<!doctype html> return f"""<!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="{url_for('static', filename='css/style.css')}" /> <link rel="stylesheet" type="text/css" href="{url_for('static', filename='css/style.css')}" />
<title>Колонизация</title> <title>Колонизация</title>
</head> </head>
<body> <body>
<h1 class="font">Результаты отбора</h1> <h1 class="font">Результаты отбора</h1>
<h1>Претендента на участие в миссии {nickname}:</h1> <h1>Претендента на участие в миссии {nickname}:</h1>
<div class="font green"><b>Поздравляем! Ваш рейтинг после {level} этапа отбора</b></div> <div class="font green"><b>Поздравляем! Ваш рейтинг после {level} этапа отбора</b></div>
<h1>составляет {rating}!</h1> <h1>составляет {rating}!</h1>
<div class="font yellow"><b>Желаем удачи!</b></div></br> <div class="font yellow"><b>Желаем удачи!</b></div></br>
</body> </body>
</html>""" </html>"""
@app.route('/load_photo', methods=['POST', 'GET']) @app.route('/load_photo', methods=['POST', 'GET'])
def photo(): def photo():
if request.method == 'GET': if request.method == 'GET':
return f'''<!doctype html> return f'''<!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" <link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css"
integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1"
crossorigin="anonymous"> crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="{url_for('static', filename='css/style.css')}" /> <link rel="stylesheet" type="text/css" href="{url_for('static', filename='css/style.css')}" />
<title>Отбор астронавтов</title> <title>Отбор астронавтов</title>
</head> </head>
<body> <body>
<h1 style="text-align:center">Загрузка фотографии</h1> <h1 style="text-align:center">Загрузка фотографии</h1>
<h2 style="text-align:center">для участия в миссии</h2> <h2 style="text-align:center">для участия в миссии</h2>
<div> <div>
<form class="login_form" method="post" enctype="multipart/form-data"> <form class="login_form" method="post" enctype="multipart/form-data">
<div class="form-group"> <div class="form-group">
<label for="photo">Приложите фотографию</label></br> <label for="photo">Приложите фотографию</label></br>
<input type="file" class="form-control-file" id="photo" name="file"> <input type="file" class="form-control-file" id="photo" name="file">
</div></br> </div></br>
<img class="width" src="{url_for('static', filename='img/img.png')}"></br> <img class="width" src="{url_for('static', filename='img/img.png')}"></br>
<button type="submit" class="btn btn-primary">Отправить</button> <button type="submit" class="btn btn-primary">Отправить</button>
</form> </form>
</div> </div>
</body> </body>
</html>''' </html>'''
elif request.method == 'POST': elif request.method == 'POST':
f = request.files['file'] f = request.files['file']
if f: if f:
with open("static/img/img.png", "wb") as file: with open("static/img/img.png", "wb") as file:
file.write(f.read()) file.write(f.read())
return f'''<!doctype html> return f'''<!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" <link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css"
integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1"
crossorigin="anonymous"> crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="{url_for('static', filename='css/style.css')}" /> <link rel="stylesheet" type="text/css" href="{url_for('static', filename='css/style.css')}" />
<title>Отбор астронавтов</title> <title>Отбор астронавтов</title>
</head> </head>
<body> <body>
<h1 style="text-align:center">Загрузка фотографии</h1> <h1 style="text-align:center">Загрузка фотографии</h1>
<h2 style="text-align:center">для участия в миссии</h2> <h2 style="text-align:center">для участия в миссии</h2>
<div> <div>
<form class="login_form" method="post" enctype="multipart/form-data"> <form class="login_form" method="post" enctype="multipart/form-data">
<div class="form-group"> <div class="form-group">
<label for="photo">Приложите фотографию</label></br> <label for="photo">Приложите фотографию</label></br>
<input type="file" class="form-control-file" id="photo" name="file"> <input type="file" class="form-control-file" id="photo" name="file">
</div></br> </div></br>
<img class="width" src="{url_for('static', filename='img/img.png')}"></br> <img class="width" src="{url_for('static', filename='img/img.png')}"></br>
<button type="submit" class="btn btn-primary">Отправить</button> <button type="submit" class="btn btn-primary">Отправить</button>
</form> </form>
</div> </div>
</body> </body>
</html>''' </html>'''
if __name__ == '__main__': if __name__ == '__main__':
app.run(port=8080, host='127.0.0.1') app.run(port=8080, host='127.0.0.1')

View file

@ -1,54 +1,54 @@
h1.red { h1.red {
color: #d22e3a; color: #d22e3a;
} }
h1.font { h1.font {
font-size: 36px; font-size: 36px;
} }
img.width { img.width {
width: 430px; width: 430px;
} }
form.login_form { form.login_form {
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
max-width: 450px; max-width: 450px;
background-color: #ffdcbb; background-color: #ffdcbb;
border: 1px solid gray; border: 1px solid gray;
border-radius: 5px; border-radius: 5px;
padding: 10px; padding: 10px;
} }
div.font { div.font {
font-size: 20px; font-size: 20px;
width: 600px; width: 600px;
height: 40px; height: 40px;
text-align: left; text-align: left;
line-height: 50px; line-height: 50px;
} }
div.red { div.red {
color: #6b1c29; color: #6b1c29;
background-color: #f7d7dc; background-color: #f7d7dc;
} }
div.black { div.black {
color: #000000; color: #000000;
background-color: #d6d9d9; background-color: #d6d9d9;
} }
div.green { div.green {
color: #185921; color: #185921;
background-color: #d3eedc; background-color: #d3eedc;
} }
div.gray { div.gray {
color: #393c42; color: #393c42;
background-color: #e1e3e4; background-color: #e1e3e4;
} }
div.yellow { div.yellow {
color: #845900; color: #845900;
background-color: #fff3ce; background-color: #fff3ce;
} }

View file

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View file

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View file

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View file

Before

Width:  |  Height:  |  Size: 422 KiB

After

Width:  |  Height:  |  Size: 422 KiB

View file

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 77 KiB

View file

@ -1,14 +1,14 @@
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired from wtforms.validators import DataRequired
class LoginForm(FlaskForm): class LoginForm(FlaskForm):
id_user = StringField('Id астронавта', validators=[DataRequired()]) id_user = StringField('Id астронавта', validators=[DataRequired()])
pas_user = PasswordField('Пароль астронавта', validators=[DataRequired()]) pas_user = PasswordField('Пароль астронавта', validators=[DataRequired()])
id_cap = StringField('Id капитана', validators=[DataRequired()]) id_cap = StringField('Id капитана', validators=[DataRequired()])
pas_cap = PasswordField('Пароль капитана', validators=[DataRequired()]) pas_cap = PasswordField('Пароль капитана', validators=[DataRequired()])
# remember_me = BooleanField('Запомнить меня') <p>{{ form.remember_me() }} {{ form.remember_me.label }}</p> # remember_me = BooleanField('Запомнить меня') <p>{{ form.remember_me() }} {{ form.remember_me.label }}</p>
submit = SubmitField('Доступ') submit = SubmitField('Доступ')

View file

@ -1,16 +1,16 @@
{ {
"news": [ "news": [
{ {
"title": "Сегодня хорошая погода", "title": "Сегодня хорошая погода",
"content": "Невероятно, сегодня хорошая погода" "content": "Невероятно, сегодня хорошая погода"
}, },
{ {
"title": "Завтра хорошая погода", "title": "Завтра хорошая погода",
"content": "С ума сойти, и завтра хорошая погода" "content": "С ума сойти, и завтра хорошая погода"
}, },
{ {
"title": "Послезавтра дождь", "title": "Послезавтра дождь",
"content": "Все вошло в норму" "content": "Все вошло в норму"
} }
] ]
} }

View file

@ -1,53 +1,55 @@
from flask import Flask, render_template, redirect import os
from loginform import LoginForm
from flask import Flask, render_template, redirect
from loginform import LoginForm
app = Flask(__name__)
app.config['SECRET_KEY'] = 'yandexlyceum_secret_key'
app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'yandexlyceum_secret_key')
@app.route('/<label>')
@app.route('/index/<label>')
def index(label): @app.route('/<label>')
param = {'label': 'И на Марсе будут яблони цвести!', 'title': label} @app.route('/index/<label>')
return render_template('index.html', **param) def index(label):
param = {'label': 'И на Марсе будут яблони цвести!', 'title': label}
return render_template('index.html', **param)
@app.route('/training/<prof>')
def training(prof):
param = {'title': "Тренировки в полёте", "spec": prof} @app.route('/training/<prof>')
return render_template('training.html', **param) def training(prof):
param = {'title': "Тренировки в полёте", "spec": prof}
return render_template('training.html', **param)
@app.route('/list_prof/<var>')
def list_prof(var):
param = {'title': "Список профессий", "var": var} @app.route('/list_prof/<var>')
return render_template('list_prof.html', **param) def list_prof(var):
param = {'title': "Список профессий", "var": var}
return render_template('list_prof.html', **param)
@app.route('/answer')
@app.route('/auto_answer')
def answer(): @app.route('/answer')
param = {'title': "Анкета", 'surname': "Фёдоров", 'name': "Марк", 'education': "Среднее", 'profession': " Писатель", @app.route('/auto_answer')
'sex': "Male", 'motivation': "Распространить хип-хоп в космос!", 'ready': "True"} def answer():
return render_template('auto_answer.html', **param) param = {'title': "Анкета", 'surname': "Фёдоров", 'name': "Марк", 'education': "Среднее", 'profession': " Писатель",
'sex': "Male", 'motivation': "Распространить хип-хоп в космос!", 'ready': "True"}
return render_template('auto_answer.html', **param)
@app.route('/login', methods=['GET', 'POST'])
def login():
param = {'title': "Аварийный доступ"} @app.route('/login', methods=['GET', 'POST'])
form = LoginForm() def login():
if form.validate_on_submit(): param = {'title': "Аварийный доступ"}
return redirect('/success') form = LoginForm()
return render_template('login.html', **param, form=form) if form.validate_on_submit():
return redirect('/success')
return render_template('login.html', **param, form=form)
@app.route('/distribution')
def success():
param = {'title': "По каютам!", "users": ['Ридли Скотт', 'Энди Уир', 'Марк Уотни', "Венката Капур", @app.route('/distribution')
'Тедди Сандерс', 'Шон Бин']} def success():
return render_template('distribution.html', **param) param = {'title': "По каютам!", "users": ['Ридли Скотт', 'Энди Уир', 'Марк Уотни', "Венката Капур",
'Тедди Сандерс', 'Шон Бин']}
return render_template('distribution.html', **param)
if __name__ == '__main__':
app.run(port=8080, host='127.0.0.1')
if __name__ == '__main__':
app.run(port=8080, host='127.0.0.1')

View file

@ -1,7 +1,7 @@
h4.color_edit { h4.color_edit {
color: "black"; color: "black";
} }
p.padmarging { p.padmarging {
margin-left: 20px; margin-left: 20px;
} }

View file

Before

Width:  |  Height:  |  Size: 268 KiB

After

Width:  |  Height:  |  Size: 268 KiB

View file

Before

Width:  |  Height:  |  Size: 267 KiB

After

Width:  |  Height:  |  Size: 267 KiB

View file

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -1,15 +1,15 @@
{% extends "base_two.html" %} {% extends "base_two.html" %}
{% block content1 %} {% block content1 %}
<div class="alert alert-light" role="alert"> <div class="alert alert-light" role="alert">
<h4><font color="#DCDCDC">И на Марсе будут яблони цвести!</font></h4> <h4><font color="#DCDCDC">И на Марсе будут яблони цвести!</font></h4>
</div> </div>
<h4 class="color_edit"><p style="padding-left: 60px">Фамилия: {{surname}}</p></h4> <h4 class="color_edit"><p style="padding-left: 60px">Фамилия: {{surname}}</p></h4>
<h4 class="color_edit"><p style="padding-left: 60px">Имя: {{name}}</p></h4> <h4 class="color_edit"><p style="padding-left: 60px">Имя: {{name}}</p></h4>
<h4 class="color_edit"><p style="padding-left: 60px">Образование: {{education}}</p></h4> <h4 class="color_edit"><p style="padding-left: 60px">Образование: {{education}}</p></h4>
<h4 class="color_edit"><p style="padding-left: 60px">Профессия: {{profession}}</p></h4> <h4 class="color_edit"><p style="padding-left: 60px">Профессия: {{profession}}</p></h4>
<h4 class="color_edit"><p style="padding-left: 60px">Пол: {{sex}}</p></h4> <h4 class="color_edit"><p style="padding-left: 60px">Пол: {{sex}}</p></h4>
<h4 class="color_edit"><p style="padding-left: 60px">Мотивация: {{motivation}}</p></h4> <h4 class="color_edit"><p style="padding-left: 60px">Мотивация: {{motivation}}</p></h4>
<h4 class="color_edit"><p style="padding-left: 60px">Готовы остаться на Марсе? {{ready}}</p></h4> <h4 class="color_edit"><p style="padding-left: 60px">Готовы остаться на Марсе? {{ready}}</p></h4>
{% endblock %} {% endblock %}

View file

@ -1,32 +1,32 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<!-- Required meta tags --> <!-- Required meta tags -->
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS --> <!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
crossorigin="anonymous"> crossorigin="anonymous">
<!-- <link rel="stylesheet" type="text/css" href="static/css/style.css" /> --> <!-- <link rel="stylesheet" type="text/css" href="static/css/style.css" /> -->
<title>{{title}}</title> <title>{{title}}</title>
</head> </head>
<body> <body>
<header> <header>
<nav class="navbar navbar-light bg-light"> <nav class="navbar navbar-light bg-light">
<div class="container-fluid"> <div class="container-fluid">
<a class="navbar-brand mb-0" href="#"><h1>Миссия Колонизация Марса</h1></a> <a class="navbar-brand mb-0" href="#"><h1>Миссия Колонизация Марса</h1></a>
<div class="navbar-text mb-0 h5"> <div class="navbar-text mb-0 h5">
Mars One Mars One
</div> </div>
</div> </div>
</nav> </nav>
</header> </header>
<!-- Begin page content --> <!-- Begin page content -->
<main role="main" class="container-fluid"> <main role="main" class="container-fluid">
{% block content1 %}{% endblock %} {% block content1 %}{% endblock %}
</main> </main>
</body> </body>
</html> </html>

View file

@ -1,10 +1,10 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content1 %} {% block content1 %}
<div class="alert alert-light" role="alert"> <div class="alert alert-light" role="alert">
<h4><font color="#DCDCDC">И на Марсе будут яблони цвести!</font></h4> <h4><font color="#DCDCDC">И на Марсе будут яблони цвести!</font></h4>
</div> </div>
<div class="container"> <div class="container">
{% block content2 %}{% endblock %} {% block content2 %}{% endblock %}
</div> </div>
{% endblock %} {% endblock %}

View file

@ -1,8 +1,8 @@
{% extends "base_two.html" %} {% extends "base_two.html" %}
{% block content2 %} {% block content2 %}
<h1 align="center">Размещение по каютам</h1> <h1 align="center">Размещение по каютам</h1>
{% for user in users %} {% for user in users %}
<h3>{{ user }} - Каюта № {{ loop.index }} {% if loop.first %} - первый в очереди {% endif %}</h3> <h3>{{ user }} - Каюта № {{ loop.index }} {% if loop.first %} - первый в очереди {% endif %}</h3>
{% endfor %} {% endfor %}
{% endblock %} {% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content1 %} {% block content1 %}
<h4>{{label}}</h4> <h4>{{label}}</h4>
{% endblock %} {% endblock %}

View file

@ -1,27 +1,27 @@
{% extends "base_two.html" %} {% extends "base_two.html" %}
{% block content2 %} {% block content2 %}
{% set all_prof = ['врач', "экзобиолог", "климатолог", "астрогеолог", "гляциолог", "метеоролог", {% set all_prof = ['врач', "экзобиолог", "климатолог", "астрогеолог", "гляциолог", "метеоролог",
'инженер-исследователь', "пилот", "строитель", "инженер по терраформированию", 'инженер-исследователь', "пилот", "строитель", "инженер по терраформированию",
"специалист по радиационной защите", "инженер жизнеобеспечения", "оператор марсохода", "специалист по радиационной защите", "инженер жизнеобеспечения", "оператор марсохода",
"киберинженер", "штурман", "пилот дронов"] %} "киберинженер", "штурман", "пилот дронов"] %}
{% if var == "ol" %} {% if var == "ol" %}
<p class="h1">Список профессий</p> <p class="h1">Список профессий</p>
<div class="container"> <div class="container">
<ol> <ol>
{% for p in all_prof %} {% for p in all_prof %}
<li class="h5">{{p}}</li> <li class="h5">{{p}}</li>
{% endfor %} {% endfor %}
</ol> </ol>
</div> </div>
{% elif var == "ul" %} {% elif var == "ul" %}
<p class="h1">Список профессий</p> <p class="h1">Список профессий</p>
<div class="container"> <div class="container">
{% for p in all_prof %} {% for p in all_prof %}
<li class="h5">{{p}}</li> <li class="h5">{{p}}</li>
{% endfor %} {% endfor %}
</div> </div>
{% else %} {% else %}
<p class="h1">Индекс указан неправильно!</p> <p class="h1">Индекс указан неправильно!</p>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View file

@ -1,45 +1,45 @@
{% extends "base_two.html" %} {% extends "base_two.html" %}
{% block content2 %} {% block content2 %}
<h1><img src="../static/img/mars_ico.png">Аварийный доступ</h1><br> <h1><img src="../static/img/mars_ico.png">Аварийный доступ</h1><br>
<form action="" method="post" novalidate> <form action="" method="post" novalidate>
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<p> <p>
<big>{{ form.id_user.label }}</big>><br> <big>{{ form.id_user.label }}</big>><br>
{{ form.id_user(class="form-control") }}<br> {{ form.id_user(class="form-control") }}<br>
{% for error in form.id_user.errors %} {% for error in form.id_user.errors %}
<div class="alert alert-danger" role="alert"> <div class="alert alert-danger" role="alert">
{{ error }} {{ error }}
</div> </div>
{% endfor %} {% endfor %}
</p> </p>
<p> <p>
<big>{{ form.pas_user.label }}</big><br> <big>{{ form.pas_user.label }}</big><br>
{{ form.pas_user(class="form-control", type="password") }}<br> {{ form.pas_user(class="form-control", type="password") }}<br>
{% for error in form.pas_user.errors %} {% for error in form.pas_user.errors %}
<div class="alert alert-danger" role="alert"> <div class="alert alert-danger" role="alert">
{{ error }} {{ error }}
</div> </div>
{% endfor %} {% endfor %}
</p> </p>
<p> <p>
<big>{{ form.id_cap.label }}</big><br> <big>{{ form.id_cap.label }}</big><br>
{{ form.id_cap(class="form-control") }}<br> {{ form.id_cap(class="form-control") }}<br>
{% for error in form.id_cap.errors %} {% for error in form.id_cap.errors %}
<div class="alert alert-danger" role="alert"> <div class="alert alert-danger" role="alert">
{{ error }} {{ error }}
</div> </div>
{% endfor %} {% endfor %}
</p> </p>
<p> <p>
<big>{{ form.pas_cap.label }}</big><br> <big>{{ form.pas_cap.label }}</big><br>
{{ form.pas_cap(class="form-control", type="password") }}<br> {{ form.pas_cap(class="form-control", type="password") }}<br>
{% for error in form.pas_cap.errors %} {% for error in form.pas_cap.errors %}
<div class="alert alert-danger" role="alert"> <div class="alert alert-danger" role="alert">
{{ error }} {{ error }}
</div> </div>
{% endfor %} {% endfor %}
</p> </p>
<p>{{ form.submit(type="submit", class="btn btn-primary") }}</p> <p>{{ form.submit(type="submit", class="btn btn-primary") }}</p>
</form> </form>
{% endblock %} {% endblock %}

View file

@ -1,17 +1,17 @@
{% extends "base_two.html" %} {% extends "base_two.html" %}
{% block content2 %} {% block content2 %}
{% set sin = ['врач', "экзобиолог", "климатолог", "астрогеолог", "гляциолог", "метеоролог"] %} {% set sin = ['врач', "экзобиолог", "климатолог", "астрогеолог", "гляциолог", "метеоролог"] %}
{% set inj = ['инженер-исследователь', "пилот", "строитель", "инженер по терраформированию", {% set inj = ['инженер-исследователь', "пилот", "строитель", "инженер по терраформированию",
"специалист по радиационной защите", "инженер жизнеобеспечения", "оператор марсохода", "специалист по радиационной защите", "инженер жизнеобеспечения", "оператор марсохода",
"киберинженер", "штурман", "пилот дронов"] %} "киберинженер", "штурман", "пилот дронов"] %}
{% if spec.lower() in sin %} {% if spec.lower() in sin %}
<p class="text-center h2">Научные симуляторы</p> <p class="text-center h2">Научные симуляторы</p>
<p class="text-center"><img src="/static/img/kb1.jpg" class="img-fluid"></p> <p class="text-center"><img src="/static/img/kb1.jpg" class="img-fluid"></p>
{% elif spec.lower() in inj %} {% elif spec.lower() in inj %}
<p class="text-center h2">Инженерные тренажеры</p> <p class="text-center h2">Инженерные тренажеры</p>
<p class="text-center"><img src="/static/img/kb2.jpg" class="img-fluid"></p> <p class="text-center"><img src="/static/img/kb2.jpg" class="img-fluid"></p>
{% else %} {% else %}
<p class="text-center h1">В наших списках ваша профессия отсутствует!</p> <p class="text-center h1">В наших списках ваша профессия отсутствует!</p>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View file

@ -1,21 +1,21 @@
def delta(org, adr): def delta(org, adr):
first = "0.0001" first = "0.0001"
adr = list(map(float, adr.split(','))) adr = list(map(float, adr.split(',')))
k1 = float(first) k1 = float(first)
while True: while True:
k = adr[0] + k1 k = adr[0] + k1
if org[0] + 0.001 >= k: if org[0] + 0.001 >= k:
k1 *= 10 k1 *= 10
else: else:
break break
k2 = float(first) k2 = float(first)
while True: while True:
k = adr[1] + k2 k = adr[1] + k2
if org[1] + 0.001 >= k: if org[1] + 0.001 >= k:
k2 *= 10 k2 *= 10
else: else:
break break
return str(max(k1, k2)) return str(max(k1, k2))

View file

@ -1,45 +1,46 @@
from io import BytesIO import os
from PIL import Image from io import BytesIO
from delta import delta from PIL import Image
import requests from delta import delta
import requests
search_api_server = "https://search-maps.yandex.ru/v1/"
api_key = "your_yandex_api_key_here" search_api_server = "https://search-maps.yandex.ru/v1/"
api_key = os.environ.get("YANDEX_API_KEY", "")
address_ll = "37.588392,55.734036"
address_ll = "37.588392,55.734036"
search_params = {
"apikey": api_key, search_params = {
"text": "магазин", "apikey": api_key,
"lang": "ru_RU", "text": "магазин",
"ll": address_ll, "lang": "ru_RU",
"type": "biz" "ll": address_ll,
} "type": "biz"
}
response = requests.get(search_api_server, params=search_params)
if not response: response = requests.get(search_api_server, params=search_params)
pass if not response:
pass
json_response = response.json()
json_response = response.json()
organization = json_response["features"][0]
org_name = organization["properties"]["CompanyMetaData"]["name"] organization = json_response["features"][0]
org_address = organization["properties"]["CompanyMetaData"]["address"] org_name = organization["properties"]["CompanyMetaData"]["name"]
org_address = organization["properties"]["CompanyMetaData"]["address"]
point = organization["geometry"]["coordinates"]
org_point = "{0},{1}".format(point[0], point[1]) point = organization["geometry"]["coordinates"]
delta = delta(point, address_ll) org_point = "{0},{1}".format(point[0], point[1])
delta = delta(point, address_ll)
map_params = {
"ll": address_ll, map_params = {
"spn": ",".join([delta, delta]), "ll": address_ll,
"l": "map", "spn": ",".join([delta, delta]),
"pt": "{0},pm2dgl".format(org_point) "l": "map",
} "pt": "{0},pm2dgl".format(org_point)
}
map_api_server = "http://static-maps.yandex.ru/1.x/"
response = requests.get(map_api_server, params=map_params) map_api_server = "http://static-maps.yandex.ru/1.x/"
response = requests.get(map_api_server, params=map_params)
Image.open(BytesIO(
Image.open(BytesIO(
response.content)).show() response.content)).show()

45
README.en.md Normal file
View file

@ -0,0 +1,45 @@
# Yandex Lyceum
[Русский](README.md) · **English**
[![CI](https://github.com/EDeev/yandex_lyceum/actions/workflows/ci.yml/badge.svg)](https://github.com/EDeev/yandex_lyceum/actions/workflows/ci.yml)
Solutions to the Yandex Lyceum web development course: Flask apps built around the "Mars colonization
mission" storyline, plus organization search via the Yandex Maps API.
**Status:** coursework (Yandex Lyceum, 2022), completed, archived
![Mission applicant form](docs/screenshots/astronaut.png)
**Stack:** Python · Flask · Jinja2 · Flask-WTF · Bootstrap · Yandex Maps API · Pillow
| Folder | Contents |
|---|---|
| `01-flask-html` | Flask and Jinja2 pages: mission promo page, applicant form, planet choice, selection results, photo upload |
| `02-flask-wtf-forms` | Flask-WTF forms: sign-in, training by profession, list of professions, form auto-answer, cabin assignment |
| `03-yandex-maps-search` | finding the nearest organization via the Yandex organization search API and showing a map |
## Running
```bash
pip install -r requirements.txt
cd 01-flask-html && python server.py # http://127.0.0.1:8080
YANDEX_API_KEY=your_key python 03-yandex-maps-search/main.py
```
`03-yandex-maps-search` needs a Yandex organization search API key in `YANDEX_API_KEY`.
## License
Coursework (Yandex Lyceum, 2021/22). The code is open for study; there is no separate license.
## Author
**Egor Deev** — [GitHub](https://github.com/EDeev) · [Telegram](https://t.me/DeevEgor) · [egor@deev.space](mailto:egor@deev.space)
---
<div align="center">
<sub>⭐ If you find this project useful, give it a star on GitHub!</sub>
<p><sub>Made with ❤️ — <a href="https://deev.space">deev.space</a></sub></p>
</div>

140
README.md
View file

@ -1,139 +1,45 @@
# Yandex Lyceum Projects 🚀 # Yandex Lyceum
> Коллекция веб-приложений разработанных в рамках образовательной программы Яндекс Лицея **Русский** · [English](README.en.md)
## 📋 Описание проекта [![CI](https://github.com/EDeev/yandex_lyceum/actions/workflows/ci.yml/badge.svg)](https://github.com/EDeev/yandex_lyceum/actions/workflows/ci.yml)
Репозиторий содержит комплекс учебных проектов, демонстрирующих эволюцию навыков веб-разработки от базовых HTML-страниц до полнофункциональных Flask-приложений с интеграцией внешних API. Решения задач курса веб-разработки Яндекс Лицея: Flask-приложения по сюжету «Миссия колонизации Марса»
и поиск организаций через API Яндекс.Карт.
## 🏗️ Архитектура решений **Статус:** учебный проект (Яндекс Лицей, 2022), завершён, в архиве
### Модули проекта: ![Анкета претендента на участие в миссии](docs/screenshots/astronaut.png)
#### 1. **WEB.HTML** - Базовая веб-разработка **Стек:** Python · Flask · Jinja2 · Flask-WTF · Bootstrap · API Яндекс.Карт · Pillow
- Статические HTML-страницы с CSS-стилизацией
- Обработка форм и файловых загрузок
- Маршрутизация и шаблонизация
#### 2. **WEB.HTML 2** - Продвинутая веб-архитектура | Папка | Что внутри |
- Flask-приложение с расширенной функциональностью |---|---|
- Интеграция Bootstrap для адаптивного дизайна | `01-flask-html` | страницы на Flask и Jinja2: рекламная страница миссии, анкета претендента, выбор планеты, результаты отбора, загрузка фото |
- Система аутентификации и авторизации | `02-flask-wtf-forms` | формы на Flask-WTF: вход, тренажёры по профессиям, список профессий, автоответ анкеты, распределение по каютам |
| `03-yandex-maps-search` | поиск ближайшей организации через API поиска по организациям и показ карты |
#### 3. **WEB.HTML flask-wtf** - Профессиональные веб-формы ## Запуск
- Валидация пользовательского ввода
- CSRF-защита
- Обработка сложных форм с множественными полями
#### 4. **Полный поиск** - Геоинформационные сервисы
- Интеграция с Yandex Maps API
- Алгоритмы геопозиционирования
- Динамическое построение карт
## 🛠️ Технологический стек
```
Backend: Flask 2.0+, Python 3.8+
Frontend: HTML5, CSS3, Bootstrap 5.1.3
Forms: Flask-WTF, WTForms
APIs: Yandex Maps Search API
Libraries: Pillow, Requests, Jinja2
```
## 🚀 Быстрый старт
### Установка зависимостей
```bash ```bash
pip install -r requirements.txt pip install -r requirements.txt
cd 01-flask-html && python server.py # http://127.0.0.1:8080
YANDEX_API_KEY=ваш_ключ python 03-yandex-maps-search/main.py
``` ```
### Запуск приложений Для `03-yandex-maps-search` нужен ключ API поиска по организациям Яндекса в переменной `YANDEX_API_KEY`.
```bash
# Базовое HTML-приложение
cd "WEB.HTML"
python server.py
# Flask-приложение с шаблонами ## Лицензия
cd "WEB.HTML 2"
python server.py
# Приложение с формами Учебный проект (Яндекс Лицей, 2021/22). Код открыт для изучения, отдельной лицензии нет.
cd "WEB.HTML flask-wtf"
python server.py
```
### Конфигурация API ## Автор
Для работы геосервисов необходимо настроить API-ключ в `main.py`:
```python
api_key = "your_yandex_api_key_here"
```
## 📁 Структура проекта **Деев Егор Викторович** — [GitHub](https://github.com/EDeev) · [Telegram](https://t.me/DeevEgor) · [egor@deev.space](mailto:egor@deev.space)
```
yandex_lyceum/
├── WEB.HTML/ # Базовые веб-технологии
│ ├── server.py # Flask-сервер
│ ├── static/ # Статические ресурсы
│ └── templates/ # HTML-шаблоны
├── WEB.HTML 2/ # Расширенная веб-архитектура
│ ├── server.py
│ ├── loginform.py # Формы авторизации
│ └── templates/
├── WEB.HTML flask-wtf/ # Профессиональные формы
└── Полный поиск/ # Геоинформационные сервисы
├── main.py # Основная логика
└── delta.py # Вспомогательные алгоритмы
```
## 🎯 Функциональные возможности
### Веб-интерфейсы:
- **Космическая тематика:** Интерактивные страницы миссии колонизации Марса
- **Система профессий:** Каталогизация и фильтрация специальностей
- **Анкетирование:** Комплексные формы с валидацией данных
- **Аутентификация:** Многоуровневая система доступа
### Геосервисы:
- **Поиск объектов:** Интеграция с Yandex Search API
- **Картографирование:** Динамическое построение карт
- **Геопозиционирование:** Алгоритмы расчета координат
## 🔧 Конфигурация
### Переменные окружения:
```bash
FLASK_ENV=development
SECRET_KEY=yandexlyceum_secret_key
YANDEX_API_KEY=your_api_key
```
### Порты по умолчанию:
- **Flask-приложения:** `127.0.0.1:8080`
- **Статические файлы:** автоматическая маршрутизация
## 🏆 Образовательные цели
Проект демонстрирует прогрессивное освоение:
- Фундаментальных веб-технологий
- Серверной архитектуры Flask
- Интеграции внешних API
- Профессиональных практик разработки
## 📄 Лицензия
Образовательный проект в рамках программы Яндекс Лицея
## 👨‍💻 Автор
**Деев Егор Викторович** - Backend Developer
- GitHub: [@EDeev](https://github.com/EDeev)
- Email: egor@deev.space
- Telegram: [@Egor_Deev](https://t.me/Egor_Deev)
--- ---
<div align="center"> <div align="center">
<sub>Разработано в рамках образовательной программы Яндекс Лицея для демонстрации навыков полного цикла веб-разработки</sub> <sub>⭐ Если проект оказался полезным, поставьте звёздочку на GitHub!</sub>
<p><sub>Создано с ❤️ от вашего дорогого - deev.space ©</sub></p> <p><sub>Сделано с ❤️ — <a href="https://deev.space">deev.space</a></sub></p>
</div> </div>

View file

@ -1,53 +0,0 @@
from flask import Flask, render_template, redirect
from loginform import LoginForm
app = Flask(__name__)
app.config['SECRET_KEY'] = 'yandexlyceum_secret_key'
@app.route('/')
@app.route('/index')
def index(label):
param = {'label': 'И на Марсе будут яблони цвести!', 'title': label}
return render_template('index.html', **param)
@app.route('/training/<prof>')
def training(prof):
param = {'title': "Тренировки в полёте", "spec": prof}
return render_template('training.html', **param)
@app.route('/list_prof/<var>')
def list_prof(var):
param = {'title': "Список профессий", "var": var}
return render_template('list_prof.html', **param)
@app.route('/answer')
@app.route('/auto_answer')
def answer():
param = {'title': "Анкета", 'surname': "Фёдоров", 'name': "Марк", 'education': "Среднее", 'profession': " Писатель",
'sex': "Male", 'motivation': "Распространить хип-хоп в космос!", 'ready': "True"}
return render_template('auto_answer.html', **param)
@app.route('/login', methods=['GET', 'POST'])
def login():
param = {'title': "Аварийный доступ"}
form = LoginForm()
if form.validate_on_submit():
return redirect('/success')
return render_template('login.html', **param, form=form)
@app.route('/distribution')
def success():
param = {'title': "По каютам!", "users": ['Ридли Скотт', 'Энди Уир', 'Марк Уотни', "Венката Капур",
'Тедди Сандерс', 'Шон Бин']}
return render_template('distribution.html', **param)
if __name__ == '__main__':
app.run(port=8080, host='127.0.0.1')

View file

@ -1,7 +0,0 @@
h4.color_edit {
color: "black";
}
p.padmarging {
margin-left: 20px;
}

View file

@ -1,15 +0,0 @@
{% extends "base_two.html" %}
{% block content1 %}
<div class="alert alert-light" role="alert">
<h4><font color="#DCDCDC">И на Марсе будут яблони цвести!</font></h4>
</div>
<h4 class="color_edit"><p style="padding-left: 60px">Фамилия: {{surname}}</p></h4>
<h4 class="color_edit"><p style="padding-left: 60px">Имя: {{name}}</p></h4>
<h4 class="color_edit"><p style="padding-left: 60px">Образование: {{education}}</p></h4>
<h4 class="color_edit"><p style="padding-left: 60px">Профессия: {{profession}}</p></h4>
<h4 class="color_edit"><p style="padding-left: 60px">Пол: {{sex}}</p></h4>
<h4 class="color_edit"><p style="padding-left: 60px">Мотивация: {{motivation}}</p></h4>
<h4 class="color_edit"><p style="padding-left: 60px">Готовы остаться на Марсе? {{ready}}</p></h4>
{% endblock %}

View file

@ -1,32 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
crossorigin="anonymous">
<!-- <link rel="stylesheet" type="text/css" href="static/css/style.css" /> -->
<title>{{title}}</title>
</head>
<body>
<header>
<nav class="navbar navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand mb-0" href="#"><h1>Миссия Колонизация Марса</h1></a>
<div class="navbar-text mb-0 h5">
Mars One
</div>
</div>
</nav>
</header>
<!-- Begin page content -->
<main role="main" class="container-fluid">
{% block content1 %}{% endblock %}
</main>
</body>
</html>

View file

@ -1,45 +0,0 @@
{% extends "base_two.html" %}
{% block content2 %}
<h1><img src="../static/img/mars_ico.png">Аварийный доступ</h1><br>
<form action="" method="post" novalidate>
{{ form.hidden_tag() }}
<p>
<big>{{ form.id_user.label }}</big>><br>
{{ form.id_user(class="form-control") }}<br>
{% for error in form.id_user.errors %}
<div class="alert alert-danger" role="alert">
{{ error }}
</div>
{% endfor %}
</p>
<p>
<big>{{ form.pas_user.label }}</big><br>
{{ form.pas_user(class="form-control", type="password") }}<br>
{% for error in form.pas_user.errors %}
<div class="alert alert-danger" role="alert">
{{ error }}
</div>
{% endfor %}
</p>
<p>
<big>{{ form.id_cap.label }}</big><br>
{{ form.id_cap(class="form-control") }}<br>
{% for error in form.id_cap.errors %}
<div class="alert alert-danger" role="alert">
{{ error }}
</div>
{% endfor %}
</p>
<p>
<big>{{ form.pas_cap.label }}</big><br>
{{ form.pas_cap(class="form-control", type="password") }}<br>
{% for error in form.pas_cap.errors %}
<div class="alert alert-danger" role="alert">
{{ error }}
</div>
{% endfor %}
</p>
<p>{{ form.submit(type="submit", class="btn btn-primary") }}</p>
</form>
{% endblock %}

View file

@ -1,17 +0,0 @@
{% extends "base_two.html" %}
{% block content2 %}
{% set sin = ['врач', "экзобиолог", "климатолог", "астрогеолог", "гляциолог", "метеоролог"] %}
{% set inj = ['инженер-исследователь', "пилот", "строитель", "инженер по терраформированию",
"специалист по радиационной защите", "инженер жизнеобеспечения", "оператор марсохода",
"киберинженер", "штурман", "пилот дронов"] %}
{% if spec.lower() in sin %}
<p class="text-center h2">Научные симуляторы</p>
<p class="text-center"><img src="/static/img/kb1.jpg" class="img-fluid"></p>
{% elif spec.lower() in inj %}
<p class="text-center h2">Инженерные тренажеры</p>
<p class="text-center"><img src="/static/img/kb2.jpg" class="img-fluid"></p>
{% else %}
<p class="text-center h1">В наших списках ваша профессия отсутствует!</p>
{% endif %}
{% endblock %}

View file

@ -1,14 +0,0 @@
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired
class LoginForm(FlaskForm):
id_user = StringField('Id астронавта', validators=[DataRequired()])
pas_user = PasswordField('Пароль астронавта', validators=[DataRequired()])
id_cap = StringField('Id капитана', validators=[DataRequired()])
pas_cap = PasswordField('Пароль капитана', validators=[DataRequired()])
# remember_me = BooleanField('Запомнить меня') <p>{{ form.remember_me() }} {{ form.remember_me.label }}</p>
submit = SubmitField('Доступ')

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View file

@ -1,10 +0,0 @@
{% extends "base.html" %}
{% block content1 %}
<div class="alert alert-light" role="alert">
<h4><font color="#DCDCDC">И на Марсе будут яблони цвести!</font></h4>
</div>
<div class="container">
{% block content2 %}{% endblock %}
</div>
{% endblock %}

View file

@ -1,8 +0,0 @@
{% extends "base_two.html" %}
{% block content2 %}
<h1 align="center">Размещение по каютам</h1>
{% for user in users %}
<h3>{{ user }} - Каюта № {{ loop.index }} {% if loop.first %} - первый в очереди {% endif %}</h3>
{% endfor %}
{% endblock %}

View file

@ -1,5 +0,0 @@
{% extends "base.html" %}
{% block content1 %}
<h4>{{label}}</h4>
{% endblock %}

View file

@ -1,27 +0,0 @@
{% extends "base_two.html" %}
{% block content2 %}
{% set all_prof = ['врач', "экзобиолог", "климатолог", "астрогеолог", "гляциолог", "метеоролог",
'инженер-исследователь', "пилот", "строитель", "инженер по терраформированию",
"специалист по радиационной защите", "инженер жизнеобеспечения", "оператор марсохода",
"киберинженер", "штурман", "пилот дронов"] %}
{% if var == "ol" %}
<p class="h1">Список профессий</p>
<div class="container">
<ol>
{% for p in all_prof %}
<li class="h5">{{p}}</li>
{% endfor %}
</ol>
</div>
{% elif var == "ul" %}
<p class="h1">Список профессий</p>
<div class="container">
{% for p in all_prof %}
<li class="h5">{{p}}</li>
{% endfor %}
</div>
{% else %}
<p class="h1">Индекс указан неправильно!</p>
{% endif %}
{% endblock %}

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

View file

@ -1,23 +1,6 @@
# Core Flask Framework
Flask==2.3.3 Flask==2.3.3
Jinja2==3.1.2 Werkzeug==2.3.8
# Flask Extensions
Flask-WTF==1.1.1 Flask-WTF==1.1.1
WTForms==3.0.1 WTForms==3.0.1
# HTTP Client Library
requests==2.31.0 requests==2.31.0
# Image Processing
Pillow==10.0.1 Pillow==10.0.1
# Security
cryptography==41.0.7
# Development Dependencies
python-dotenv==1.0.0
# Optional: Testing Framework
pytest==7.4.3
pytest-flask==1.3.0