структура

korochki_est/
│
├── app.py
├── requirements.txt
├── database.db (создастся автоматически)
│
├── static/
│   ├── css/
│   │   └── style.css
│   └── js/
│       └── script.js
│
└── templates/
    ├── base.html
    ├── index.html
    ├── register.html
    ├── login.html
    ├── create_application.html
    ├── applications.html
    └── admin.html

##################################################################################################

from flask import Flask, render_template, request, redirect, session, flash
import sqlite3
import re
from datetime import datetime

app = Flask(__name__)
app.secret_key = 'education_secret'

def connect_db():
    conn = sqlite3.connect('database.db')
    conn.row_factory = sqlite3.Row
    return conn

def create_tables():
    conn = connect_db()
    cursor = conn.cursor()

    cursor.execute('''
    CREATE TABLE IF NOT EXISTS roles(
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        role_name TEXT
    )
    ''')

    cursor.execute('''
    CREATE TABLE IF NOT EXISTS users(
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        full_name TEXT,
        phone TEXT,
        email TEXT,
        login TEXT UNIQUE,
        password TEXT,
        role_id INTEGER,
        FOREIGN KEY (role_id) REFERENCES roles(id)
    )
    ''')

    cursor.execute('''
    CREATE TABLE IF NOT EXISTS statuses(
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        status_name TEXT
    )
    ''')

    cursor.execute('''
    CREATE TABLE IF NOT EXISTS payment_methods(
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        method_name TEXT
    )
    ''')

    cursor.execute('''
    CREATE TABLE IF NOT EXISTS courses(
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        course_name TEXT
    )
    ''')

    cursor.execute('''
    CREATE TABLE IF NOT EXISTS applications(
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        user_id INTEGER,
        course_id INTEGER,
        payment_method_id INTEGER,
        status_id INTEGER,
        start_date TEXT,
        created_at TEXT,
        FOREIGN KEY (user_id) REFERENCES users(id),
        FOREIGN KEY (course_id) REFERENCES courses(id),
        FOREIGN KEY (payment_method_id) REFERENCES payment_methods(id),
        FOREIGN KEY (status_id) REFERENCES statuses(id)
    )
    ''')

    cursor.execute('''
    CREATE TABLE IF NOT EXISTS reviews(
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        user_id INTEGER,
        application_id INTEGER,
        review_text TEXT,
        created_at TEXT,
        FOREIGN KEY (user_id) REFERENCES users(id),
        FOREIGN KEY (application_id) REFERENCES applications(id)
    )
    ''')

    conn.commit()

    cursor.execute("SELECT * FROM roles")
    if not cursor.fetchall():
        cursor.execute("INSERT INTO roles(role_name) VALUES ('admin')")
        cursor.execute("INSERT INTO roles(role_name) VALUES ('user')")

    cursor.execute("SELECT * FROM statuses")
    if not cursor.fetchall():
        cursor.execute("INSERT INTO statuses(status_name) VALUES ('Новая')")
        cursor.execute("INSERT INTO statuses(status_name) VALUES ('Идет обучение')")
        cursor.execute("INSERT INTO statuses(status_name) VALUES ('Обучение завершено')")

    cursor.execute("SELECT * FROM payment_methods")
    if not cursor.fetchall():
        cursor.execute("INSERT INTO payment_methods(method_name) VALUES ('Наличные')")
        cursor.execute("INSERT INTO payment_methods(method_name) VALUES ('Банковский перевод')")

    cursor.execute("SELECT * FROM courses")
    if not cursor.fetchall():
        cursor.execute("INSERT INTO courses(course_name) VALUES ('Основы алгоритмизации и программирования')")
        cursor.execute("INSERT INTO courses(course_name) VALUES ('Основы веб-дизайна')")
        cursor.execute("INSERT INTO courses(course_name) VALUES ('Основы проектирования баз данных')")

    cursor.execute("SELECT * FROM users WHERE login='admin'")
    if not cursor.fetchone():
        cursor.execute('''
        INSERT INTO users(full_name, phone, email, login, password, role_id)
        VALUES (?, ?, ?, ?, ?, ?)
        ''', ('Администратор', '+7(999)-999-99-99', 'admin@mail.ru', 'admin', 'education', 1))

    conn.commit()
    conn.close()

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        full_name = request.form['full_name']
        phone = request.form['phone']
        email = request.form['email']
        login = request.form['login']
        password = request.form['password']

        if len(login) < 6:
            flash('Логин минимум 6 символов')
            return redirect('/register')
        if len(password) < 6:
            flash('Пароль минимум 6 символов')
            return redirect('/register')
        if not re.match(r'^[А-Яа-яЁё ]+$', full_name):
            flash('ФИО должно быть кириллицей')
            return redirect('/register')
        if not re.match(r'^\+7\(\d{3}\)-\d{3}-\d{2}-\d{2}$', phone):
            flash('Телефон введен неверно')
            return redirect('/register')

        conn = connect_db()
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM users WHERE login=?", (login,))
        if cursor.fetchone():
            flash('Логин уже существует')
            return redirect('/register')

        cursor.execute('''
        INSERT INTO users(full_name, phone, email, login, password, role_id)
        VALUES (?, ?, ?, ?, ?, ?)
        ''', (full_name, phone, email, login, password, 2))
        conn.commit()
        conn.close()
        flash('Регистрация успешна')
        return redirect('/login')
    return render_template('register.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        login = request.form['login']
        password = request.form['password']
        conn = connect_db()
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM users WHERE login=? AND password=?", (login, password))
        user = cursor.fetchone()
        conn.close()
        if user:
            session['user_id'] = user['id']
            session['login'] = user['login']
            session['role_id'] = user['role_id']
            if login == 'admin':
                return redirect('/admin')
            return redirect('/applications')
        flash('Неверный логин или пароль')
        return redirect('/login')
    return render_template('login.html')

@app.route('/logout')
def logout():
    session.clear()
    return redirect('/')

@app.route('/create_application', methods=['GET', 'POST'])
def create_application():
    if 'user_id' not in session:
        return redirect('/login')
    conn = connect_db()
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM courses")
    courses = cursor.fetchall()
    cursor.execute("SELECT * FROM payment_methods")
    payments = cursor.fetchall()
    if request.method == 'POST':
        course_id = request.form['course_id']
        payment_method_id = request.form['payment_method_id']
        start_date = request.form['start_date']
        cursor.execute('''
        INSERT INTO applications(user_id, course_id, payment_method_id, status_id, start_date, created_at)
        VALUES (?, ?, ?, ?, ?, ?)
        ''', (session['user_id'], course_id, payment_method_id, 1, start_date, datetime.now()))
        conn.commit()
        conn.close()
        flash('Заявка отправлена')
        return redirect('/applications')
    return render_template('create_application.html', courses=courses, payments=payments)

@app.route('/applications')
def applications():
    if 'user_id' not in session:
        return redirect('/login')
    conn = connect_db()
    cursor = conn.cursor()
    cursor.execute('''
    SELECT applications.*, courses.course_name, payment_methods.method_name, statuses.status_name
    FROM applications
    JOIN courses ON applications.course_id = courses.id
    JOIN payment_methods ON applications.payment_method_id = payment_methods.id
    JOIN statuses ON applications.status_id = statuses.id
    WHERE applications.user_id=?
    ''', (session['user_id'],))
    applications_list = cursor.fetchall()
    reviews = {}
    for app in applications_list:
        cursor.execute("SELECT * FROM reviews WHERE application_id=?", (app['id'],))
        reviews[app['id']] = cursor.fetchall()
    conn.close()
    return render_template('applications.html', applications=applications_list, reviews=reviews)

@app.route('/add_review/<int:application_id>', methods=['POST'])
def add_review(application_id):
    review_text = request.form['review_text']
    conn = connect_db()
    cursor = conn.cursor()
    cursor.execute('''
    INSERT INTO reviews(user_id, application_id, review_text, created_at)
    VALUES (?, ?, ?, ?)
    ''', (session['user_id'], application_id, review_text, datetime.now()))
    conn.commit()
    conn.close()
    flash('Отзыв добавлен')
    return redirect('/applications')

@app.route('/admin')
def admin():
    if 'user_id' not in session or session.get('login') != 'admin':
        return redirect('/')
    conn = connect_db()
    cursor = conn.cursor()
    cursor.execute('''
    SELECT applications.*, users.full_name, courses.course_name, statuses.status_name
    FROM applications
    JOIN users ON applications.user_id = users.id
    JOIN courses ON applications.course_id = courses.id
    JOIN statuses ON applications.status_id = statuses.id
    ''')
    applications_list = cursor.fetchall()
    cursor.execute("SELECT * FROM statuses")
    statuses = cursor.fetchall()
    conn.close()
    return render_template('admin.html', applications=applications_list, statuses=statuses)

@app.route('/change_status/<int:id>', methods=['POST'])
def change_status(id):
    status_id = request.form['status_id']
    conn = connect_db()
    cursor = conn.cursor()
    cursor.execute("UPDATE applications SET status_id=? WHERE id=?", (status_id, id))
    conn.commit()
    conn.close()
    flash('Статус обновлен')
    return redirect('/admin')

if __name__ == '__main__':
    create_tables()
    app.run(debug=True)

##################################################################################################

templates/base.html

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Корочки.есть</title>
    <link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
    <nav>
        <a href="/">Главная</a>
        {% if session.user_id %}
            <a href="/applications">Мои заявки</a>
            <a href="/create_application">Новая заявка</a>
            <a href="/logout">Выйти</a>
            {% if session.login == 'admin' %}
                <a href="/admin">Админ панель</a>
            {% endif %}
        {% else %}
            <a href="/login">Вход</a>
            <a href="/register">Регистрация</a>
        {% endif %}
    </nav>
    <div class="container">
        {% with messages = get_flashed_messages() %}
            {% if messages %}
                {% for msg in messages %}
                    <div class="flash">{{ msg }}</div>
                {% endfor %}
            {% endif %}
        {% endwith %}
        {% block content %}{% endblock %}
    </div>
    <script src="/static/js/script.js"></script>
</body>
</html>

##################################################################################################

templates/index.html

{% extends "base.html" %}
{% block content %}
<h1>Добро пожаловать в «Корочки.есть»</h1>
<p>Онлайн-курсы с гарантией получения корочек</p>
{% endblock %}

##################################################################################################

templates/register.html

{% extends "base.html" %}
{% block content %}
<h2>Регистрация</h2>
<form method="post">
    <input type="text" name="full_name" placeholder="ФИО кириллицей" required>
    <input type="text" name="phone" placeholder="+7(999)-999-99-99" required>
    <input type="email" name="email" placeholder="Email">
    <input type="text" name="login" placeholder="Логин (мин 6 символов)" required>
    <input type="password" name="password" placeholder="Пароль (мин 6 символов)" required>
    <button type="submit">Зарегистрироваться</button>
</form>
{% endblock %}

##################################################################################################

templates/login.html

{% extends "base.html" %}
{% block content %}
<h2>Вход</h2>
<form method="post">
    <input type="text" name="login" placeholder="Логин">
    <input type="password" name="password" placeholder="Пароль">
    <button type="submit">Войти</button>
</form>
{% endblock %}

##################################################################################################

templates/create_application.html

{% extends "base.html" %}
{% block content %}
<h2>Новая заявка на курс</h2>
<form method="post">
    <select name="course_id" required>
        {% for c in courses %}
        <option value="{{ c.id }}">{{ c.course_name }}</option>
        {% endfor %}
    </select>
    <select name="payment_method_id" required>
        {% for p in payments %}
        <option value="{{ p.id }}">{{ p.method_name }}</option>
        {% endfor %}
    </select>
    <input type="date" name="start_date" required>
    <button type="submit">Отправить заявку</button>
</form>
{% endblock %}

##################################################################################################

templates/applications.html

{% extends "base.html" %}
{% block content %}
<h2>Мои заявки</h2>
{% for app in applications %}
<div class="app-card">
    <p><strong>{{ app.course_name }}</strong></p>
    <p>Оплата: {{ app.method_name }}</p>
    <p>Дата начала: {{ app.start_date }}</p>
    <p>Статус: {{ app.status_name }}</p>
    <hr>
    <h4>Отзывы:</h4>
    {% for rev in reviews[app.id] %}
        <div class="review">{{ rev.review_text }} <em>({{ rev.created_at }})</em></div>
    {% else %}
        <p>Нет отзывов</p>
    {% endfor %}
    <form method="post" action="/add_review/{{ app.id }}">
        <textarea name="review_text" placeholder="Ваш отзыв после курса"></textarea>
        <button type="submit">Добавить отзыв</button>
    </form>
</div>
{% endfor %}
{% endblock %}

##################################################################################################

templates/admin.html

{% extends "base.html" %}
{% block content %}
<h2>Админ панель — управление заявками</h2>
{% for app in applications %}
<div class="admin-card">
    <p>Пользователь: {{ app.full_name }}</p>
    <p>Курс: {{ app.course_name }}</p>
    <p>Текущий статус: {{ app.status_name }}</p>
    <form method="post" action="/change_status/{{ app.id }}">
        <select name="status_id">
            {% for s in statuses %}
            <option value="{{ s.id }}" {% if s.id == app.status_id %}selected{% endif %}>{{ s.status_name }}</option>
            {% endfor %}
        </select>
        <button type="submit">Изменить статус</button>
    </form>
</div>
{% endfor %}
{% endblock %}

##################################################################################################

body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 0;
    background: #f4f4f4;
}
nav {
    background: #333;
    padding: 10px;
}
nav a {
    color: white;
    margin-right: 15px;
    text-decoration: none;
}
.container {
    width: 90%;
    margin: 20px auto;
    background: white;
    padding: 20px;
    border-radius: 8px;
}
.flash {
    background: #ffc;
    padding: 10px;
    margin-bottom: 10px;
    border-left: 5px solid orange;
}
.app-card, .admin-card {
    border: 1px solid #ccc;
    padding: 15px;
    margin-bottom: 15px;
    border-radius: 5px;
}
textarea {
    width: 100%;
    height: 60px;
}
button {
    margin-top: 5px;
    padding: 8px 12px;
    background: #2c3e70;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

##################################################################################################

static/js/script.js

// простой JS для подтверждения действий
document.addEventListener('DOMContentLoaded', function() {
    const forms = document.querySelectorAll('form');
    forms.forEach(form => {
        form.addEventListener('submit', function(e) {
            if (form.querySelector('button[type="submit"]')) {
                // можно добавить подтверждение
            }
        });
    });
});

##################################################################################################

pgAdmin

CREATE TABLE roles (
    id SERIAL PRIMARY KEY,
    role_name TEXT
);

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    full_name TEXT,
    phone TEXT,
    email TEXT,
    login TEXT UNIQUE,
    password TEXT,
    role_id INTEGER REFERENCES roles(id)
);

CREATE TABLE statuses (
    id SERIAL PRIMARY KEY,
    status_name TEXT
);

CREATE TABLE payment_methods (
    id SERIAL PRIMARY KEY,
    method_name TEXT
);

CREATE TABLE courses (
    id SERIAL PRIMARY KEY,
    course_name TEXT
);

CREATE TABLE applications (
    id SERIAL PRIMARY KEY,
    user_id INTEGER REFERENCES users(id),
    course_id INTEGER REFERENCES courses(id),
    payment_method_id INTEGER REFERENCES payment_methods(id),
    status_id INTEGER REFERENCES statuses(id),
    start_date TEXT,
    created_at TEXT
);

CREATE TABLE reviews (
    id SERIAL PRIMARY KEY,
    user_id INTEGER REFERENCES users(id),
    application_id INTEGER REFERENCES applications(id),
    review_text TEXT,
    created_at TEXT
);

##################################################################################################

Как запустить

# 1. Создайте папку korochki_est
# 2. Скопируйте все файлы по структуре
# 3. Откройте терминал в этой папке
# 4. Создайте виртуальное окружение
python3 -m venv название
source название/bin/activate
# 5. Установите Flask
pip install -r requirements.txt
# 6. Запустите приложение
python app.py