Модули задания и что проверяют
Задание собирается из модулей под профессиональные модули и квалификацию. Любой подсвеченный параметр БД (имя, пользователь, порт) можно нажать и заменить под свой вариант — он подставится во все примеры.
| Модуль | Что делает студент | Что оценивают |
|---|---|---|
| Разработка приложения | Реализовать по ТЗ логику, классы и интерфейс | Соответствие ТЗ, ООП, обработка ошибок |
| База данных | Спроектировать схему, написать SQL, связать с приложением | Нормализация, целостность, корректность запросов |
| Веб-приложение | Сверстать и/или реализовать backend, связать с БД | Вёрстка, работа клиент-серверной части |
| Тестирование/отладка | Найти и исправить дефекты, проверить по сценариям | Поиск ошибок, корректность исправлений |
Разработка приложения по ТЗ (ООП)
Главное — структурировать код: выделить классы предметной области, бизнес-логику и хранилище. Ниже каркас «сущность + репозиторий + сервис» на двух языках.
from dataclasses import dataclass, field
@dataclass
class Client:
id: int
full_name: str
email: str
class ClientRepository:
def __init__(self):
self._items: dict[int, Client] = {}
self._seq = 0
def add(self, full_name: str, email: str) -> Client:
self._seq += 1
c = Client(self._seq, full_name, email)
self._items[c.id] = c
return c
def all(self) -> list[Client]:
return list(self._items.values())
def get(self, cid: int) -> Client | None:
return self._items.get(cid)
def delete(self, cid: int) -> bool:
return self._items.pop(cid, None) is not None
if __name__ == '__main__':
repo = ClientRepository()
repo.add('Иванов И.И.', 'ivanov@example.org')
for c in repo.all():
print(c.id, c.full_name, c.email)public class Client {
public int Id { get; set; }
public string FullName { get; set; } = "";
public string Email { get; set; } = "";
}
public class ClientRepository {
private readonly List<Client> _items = new();
private int _seq = 0;
public Client Add(string fullName, string email) {
var c = new Client { Id = ++_seq, FullName = fullName, Email = email };
_items.Add(c);
return c;
}
public IEnumerable<Client> All() => _items;
public Client? Get(int id) => _items.FirstOrDefault(x => x.Id == id);
public bool Delete(int id) => _items.RemoveAll(x => x.Id == id) > 0;
}Проектирование базы данных и SQL
Проектируем нормализованную схему (как правило, до 3НФ), создаём таблицы со связями и ограничениями целостности, наполняем тестовыми данными.
CREATE DATABASE demo_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'demo_user'@'localhost' IDENTIFIED BY 'P@ssw0rd';
GRANT ALL PRIVILEGES ON demo_db.* TO 'demo_user'@'localhost';
FLUSH PRIVILEGES;
USE demo_db;CREATE TABLE clients (
id INT PRIMARY KEY AUTO_INCREMENT,
full_name VARCHAR(120) NOT NULL,
email VARCHAR(120) UNIQUE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE orders (
id INT PRIMARY KEY AUTO_INCREMENT,
client_id INT NOT NULL,
amount DECIMAL(10,2) NOT NULL CHECK (amount >= 0),
status VARCHAR(30) DEFAULT 'new',
CONSTRAINT fk_orders_client
FOREIGN KEY (client_id) REFERENCES clients(id) ON DELETE CASCADE
);
CREATE INDEX idx_orders_client ON orders(client_id);-- клиенты с суммой и числом заказов
SELECT c.full_name, COUNT(o.id) AS cnt, COALESCE(SUM(o.amount),0) AS total
FROM clients c
LEFT JOIN orders o ON o.client_id = c.id
GROUP BY c.id, c.full_name
HAVING total > 0
ORDER BY total DESC;START TRANSACTION;
INSERT INTO clients(full_name, email) VALUES ('Петров П.П.', 'petrov@example.org');
INSERT INTO orders(client_id, amount) VALUES (LAST_INSERT_ID(), 1500.00);
COMMIT;
-- при ошибке: ROLLBACK;Подключение приложения к базе данных
Связываем приложение с БД. Параметры подключения берём из переменных стенда — поменяешь вверху страницы, и все строки подключения обновятся.
import pymysql
conn = pymysql.connect(
host='localhost', port=3306,
user='demo_user', password='P@ssw0rd',
database='demo_db', charset='utf8mb4',
)
with conn.cursor() as cur:
cur.execute(
'INSERT INTO clients(full_name, email) VALUES (%s, %s)',
('Сидоров С.С.', 'sidorov@example.org'),
)
conn.commit()
cur.execute('SELECT id, full_name FROM clients ORDER BY id')
for row in cur.fetchall():
print(row)
conn.close()using MySqlConnector;
var cs = "Server=localhost;Port=3306;Database=demo_db;Uid=demo_user;Pwd=P@ssw0rd;";
await using var conn = new MySqlConnection(cs);
await conn.OpenAsync();
await using var cmd = new MySqlCommand(
"INSERT INTO clients(full_name, email) VALUES (@n, @e)", conn);
cmd.Parameters.AddWithValue("@n", "Кузнецов К.К.");
cmd.Parameters.AddWithValue("@e", "kuznetsov@example.org");
await cmd.ExecuteNonQueryAsync();Веб-приложение: backend + фронтенд
Если модуль про веб — нужен серверный API и страница, которая с ним общается. Ниже минимальный REST на Flask и страница, забирающая данные через fetch.
from flask import Flask, request, jsonify
app = Flask('DemoApp')
clients = []
_seq = 0
@app.get('/api/clients')
def list_clients():
return jsonify(clients)
@app.post('/api/clients')
def add_client():
global _seq
data = request.get_json(force=True)
_seq += 1
item = {'id': _seq, 'full_name': data['full_name'], 'email': data.get('email', '')}
clients.append(item)
return jsonify(item), 201
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)<!doctype html>
<html lang="ru">
<head><meta charset="utf-8"><title>DemoApp</title></head>
<body>
<h1>Клиенты</h1>
<form id="f">
<input name="full_name" placeholder="ФИО" required>
<input name="email" placeholder="E-mail">
<button>Добавить</button>
</form>
<ul id="list"></ul>
<script>
async function load() {
const r = await fetch('/api/clients');
const data = await r.json();
document.getElementById('list').innerHTML =
data.map(c => '<li>' + c.id + ': ' + c.full_name + '</li>').join('');
}
document.getElementById('f').onsubmit = async (e) => {
e.preventDefault();
const fd = new FormData(e.target);
await fetch('/api/clients', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(Object.fromEntries(fd)),
});
e.target.reset();
load();
};
load();
</script>
</body>
</html>Тестирование и отладка
Модуль тестирования проверяет умение находить и фиксировать дефекты. Готовь юнит-тесты на ключевую логику и умей читать чужой код.
# test_repo.py
from app import ClientRepository
def test_add_and_get():
repo = ClientRepository()
c = repo.add('Иванов', 'i@x.org')
assert c.id == 1
assert repo.get(1).full_name == 'Иванов'
def test_delete():
repo = ClientRepository()
repo.add('A', 'a@x.org')
assert repo.delete(1) is True
assert repo.get(1) is None
# запуск: pytest -qpublic class RepoTests {
[Fact]
public void Add_Then_Get_Works() {
var repo = new ClientRepository();
var c = repo.Add("Иванов", "i@x.org");
Assert.Equal(1, c.Id);
Assert.Equal("Иванов", repo.Get(1)!.FullName);
}
}
// запуск: dotnet test- Отладка: ставь точки останова, проверяй значения переменных по шагам, локализуй место дефекта.
- Проверяй граничные случаи: пустой ввод, некорректные данные, отсутствующая запись.
- Каждое исправление подтверждай повторным прогоном теста/сценария.
Git: сохраняй работу по ходу
Если в задании есть контроль версий — фиксируй прогресс коммитами. Даже без требования это страховка от потери кода при сбое.
git init
git add .
git commit -m "каркас приложения DemoApp"
# ... после каждого готового пункта:
git add -A
git commit -m "модуль БД: таблицы и запросы"
git log --onelineСамопроверка и типичные ошибки
- Все требования ТЗ выписаны отдельным списком и выполнены по пунктам.
- Код структурирован: классы, методы, осмысленные имена, обработка ошибок.
- БД нормализована, есть связи и ограничения, запросы возвращают верный результат.
- Приложение подключается к БД параметризованными запросами.
- Веб-часть (если есть) работает: фронтенд получает и отправляет данные.
- Тесты проходят, исправления подтверждены повторным прогоном.
- Прогресс сохранён коммитами; перед сдачей — финальная самопроверка по критериям.
- Реализовали не то, что в ТЗ — перечитай требования и сверься по списку.
- Весь код в одном методе/файле — теряются баллы за структуру.
- Нет проверки ввода и обработки ошибок — приложение падает на крайних случаях.
- Склеивание SQL из строк — уязвимость и потеря баллов.
- Оставили самопроверку на последние минуты.
Практическая сборка приложения (как на экзамене)
На многих площадках демонстрационный экзамен 09.02.07 — это десктоп-приложение: среда Qt Creator (Qt Designer для форм) + Python/PyQt либо C#. Нужно собрать рабочее приложение с входом, таблицами и формами по ТЗ, связать его с базой данных и закрыть пункты критериев. Ниже — практический порядок сборки, как реально идёт работа на экзамене.
- Подготовить окружение: IDE, СУБД, инструмент диаграмм, Git.
- Построить диаграммы Use Case и ER по предметной области.
- Создать базу данных по ER-диаграмме.
- Сделать окно входа (авторизацию).
- Сверстать интерфейс: таблицы и формы.
- Реализовать CRUD к базе: добавление, просмотр, изменение, удаление.
- Проверить по критериям и оформить код по руководству стиля.
1. Подготовка окружения. Поставь и проверь всё заранее — на площадке настраивать будет некогда.
- IDE: Qt Creator (+ Qt Designer) или PyCharm / Visual Studio Code.
- Язык и GUI: Python + PyQt6/PySide6 либо C# (WinForms/WPF) — по своему КОД.
- СУБД: MySQL или PostgreSQL (база demo_db на localhost:3306).
- Диаграммы: drawio (онлайн или офлайн-версия).
- Контроль версий: Git.
2. Диаграммы (Use Case и ER). Use Case-диаграмма показывает роли и действия пользователей; ER-диаграмма — таблицы, поля и связи. Сначала рисуешь ER в drawio, затем переносишь её в SQL (CREATE TABLE) — это и есть твоя база.
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
login VARCHAR(50) UNIQUE NOT NULL,
pass_hash CHAR(64) NOT NULL
);
CREATE TABLE requests (
id INT PRIMARY KEY AUTO_INCREMENT,
client VARCHAR(100) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'новая',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);3. Окно входа (авторизация). Пароли храни не в открытом виде, а хэшем (например SHA-256).
import hashlib
from PyQt6 import QtWidgets
def check_login(conn, login: str, password: str) -> bool:
h = hashlib.sha256(password.encode()).hexdigest()
with conn.cursor() as cur:
cur.execute('SELECT id FROM users WHERE login=%s AND pass_hash=%s', (login, h))
return cur.fetchone() is not None
class LoginDialog(QtWidgets.QDialog):
def __init__(self, conn):
super().__init__()
self.conn = conn
self.setWindowTitle('Вход')
self.login = QtWidgets.QLineEdit()
self.passw = QtWidgets.QLineEdit()
self.passw.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password)
btn = QtWidgets.QPushButton('Войти')
btn.clicked.connect(self.try_login)
form = QtWidgets.QFormLayout(self)
form.addRow('Логин', self.login)
form.addRow('Пароль', self.passw)
form.addRow(btn)
def try_login(self):
if check_login(self.conn, self.login.text(), self.passw.text()):
self.accept()
else:
QtWidgets.QMessageBox.warning(self, 'Ошибка', 'Неверный логин или пароль')4. Интерфейс: таблица со списком и кнопка добавления — это сразу Create и Read из CRUD.
from PyQt6 import QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, conn):
super().__init__()
self.conn = conn
self.setWindowTitle('Заявки')
central = QtWidgets.QWidget()
self.setCentralWidget(central)
lay = QtWidgets.QVBoxLayout(central)
self.table = QtWidgets.QTableWidget(0, 3)
self.table.setHorizontalHeaderLabels(['ID', 'Клиент', 'Статус'])
add = QtWidgets.QPushButton('Добавить заявку')
add.clicked.connect(self.add_request)
lay.addWidget(self.table)
lay.addWidget(add)
self.reload()
def reload(self):
self.table.setRowCount(0)
with self.conn.cursor() as cur:
cur.execute('SELECT id, client, status FROM requests ORDER BY id')
for row in cur.fetchall():
r = self.table.rowCount()
self.table.insertRow(r)
for c, val in enumerate(row):
self.table.setItem(r, c, QtWidgets.QTableWidgetItem(str(val)))
def add_request(self):
name, ok = QtWidgets.QInputDialog.getText(self, 'Новая заявка', 'Клиент:')
if ok and name:
with self.conn.cursor() as cur:
cur.execute('INSERT INTO requests(client, status) VALUES (%s, %s)', (name, 'новая'))
self.conn.commit()
self.reload()5. Остальной CRUD — изменение и удаление. Подключение к базе — как в шаге про БД (pymysql с localhost, demo_user, demo_db).
def set_status(conn, req_id: int, status: str):
with conn.cursor() as cur:
cur.execute('UPDATE requests SET status=%s WHERE id=%s', (status, req_id))
conn.commit()
def delete_request(conn, req_id: int):
with conn.cursor() as cur:
cur.execute('DELETE FROM requests WHERE id=%s', (req_id,))
conn.commit()Остались вопросы по подготовке
Задавай вопросы ИИ-помощнику на странице — он отвечает по теме демонстрационного экзамена 09.02.07: разработка приложения, SQL и БД, веб-часть, тестирование, Git, разбор критериев. Значения в примерах (имя базы, пользователь, порт) меняй под свой вариант — они подставятся во всю документацию.
Инструменты подготовки
премиумОдин клик — и адреса, подсети, VLAN, порты и пароли меняются на новый согласованный вариант. Значения подставятся во все команды и таблицы на странице. Удобно тренироваться «как на экзамене», когда даны другие числа.
- 1. Как защититься от SQL-инъекций?
- 2. До какой нормальной формы обычно нормализуют схему БД?
- 3. Что возвращает LEFT JOIN?
Откройте все памятки, тренажёры и инструменты подготовки — разово за 99 ₽, без регистрации.
Задай вопрос по настройке, командам или теории этой специальности — отвечу на основе ИИ.
Премиум открывает сразу всё:
- ✓генератор варианта стенда
- ✓таймер экзамена и чек-лист
- ✓тренажёр-тесты с автопроверкой
- ✓памятки и экспорт в PDF
- ✓100 вопросов ИИ-помощнику
дешевле чашки кофе · разово · без подписки и регистрации