~/демоэкзамен.рф
09.02.07Windows / Linux · C#, Python, SQL, Web~8 чБазовый → продвинутый

09.02.07 Информационные системы и программирование — пошаговая подготовка к демонстрационному экзамену

Полный разбор модулей: разработка приложения по ТЗ (ООП на Python и C#), проектирование и нормализация базы данных, SQL и транзакции, подключение приложения к БД, веб-приложение (REST + фронтенд), юнит-тестирование и отладка, работа с Git. Имена БД, пользователя и порт редактируются под свой вариант.

Любой подсвеченный IP-адрес, интерфейс или VLAN можно нажать и изменить под свой вариант — значение подставится во всю документацию и на схему. Всё хранится только в твоём браузере.

Структура

Модули задания и что проверяют

Задание собирается из модулей под профессиональные модули и квалификацию. Любой подсвеченный параметр БД (имя, пользователь, порт) можно нажать и заменить под свой вариант — он подставится во все примеры.

Типовые модули демоэкзамена 09.02.07
МодульЧто делает студентЧто оценивают
Разработка приложенияРеализовать по ТЗ логику, классы и интерфейсСоответствие ТЗ, ООП, обработка ошибок
База данныхСпроектировать схему, написать SQL, связать с приложениемНормализация, целостность, корректность запросов
Веб-приложениеСверстать и/или реализовать backend, связать с БДВёрстка, работа клиент-серверной части
Тестирование/отладкаНайти и исправить дефекты, проверить по сценариямПоиск ошибок, корректность исправлений
Сверяйся со своим КОД.Точный состав модулей, допустимые языки/СУБД, время и баллы — в КОД твоего года и уровня. Готовься на том стеке, который поддерживает твоя площадка. Ниже примеры на Python, C# и SQL — выбери свой.
Шаг 1

Разработка приложения по ТЗ (ООП)

Главное — структурировать код: выделить классы предметной области, бизнес-логику и хранилище. Ниже каркас «сущность + репозиторий + сервис» на двух языках.

Python: класс-сущность и репозиторий (CRUD в памяти)
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)
C#: класс и список с CRUD
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;
}
Сначала рабочее, потом красивое.Сделай минимально работающую версию по каждому пункту критериев, затем улучшай. Частично рабочее приложение приносит баллы, недописанный «идеальный» модуль — нет.
Шаг 2

Проектирование базы данных и SQL

Проектируем нормализованную схему (как правило, до 3НФ), создаём таблицы со связями и ограничениями целостности, наполняем тестовыми данными.

Создание базы и пользователя (MySQL/MariaDB)
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);
Запросы: выборка, агрегаты, JOIN
-- клиенты с суммой и числом заказов
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;
Для PostgreSQL.Поменяй порт на 5432, AUTO_INCREMENT на SERIAL/GENERATED ... AS IDENTITY, строку подключения на postgresql://… Логику и нормализацию это не меняет.
Шаг 3

Подключение приложения к базе данных

Связываем приложение с БД. Параметры подключения берём из переменных стенда — поменяешь вверху страницы, и все строки подключения обновятся.

Python (PyMySQL): параметризованные запросы
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()
C# (ADO.NET, MySqlConnector)
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();
Только параметризованные запросы.Никогда не склеивай SQL из строк с пользовательским вводом — это SQL-инъекция и потеря баллов за безопасность. Используй параметры (%s, @name), как в примерах.
Шаг 4

Веб-приложение: backend + фронтенд

Если модуль про веб — нужен серверный API и страница, которая с ним общается. Ниже минимальный REST на Flask и страница, забирающая данные через fetch.

Backend на Flask (REST: список и добавление)
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)
Фронтенд (HTML + fetch)
<!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>
Шаг 5

Тестирование и отладка

Модуль тестирования проверяет умение находить и фиксировать дефекты. Готовь юнит-тесты на ключевую логику и умей читать чужой код.

Python: юнит-тесты (pytest)
# 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 -q
C#: тест (xUnit)
public 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
  • Отладка: ставь точки останова, проверяй значения переменных по шагам, локализуй место дефекта.
  • Проверяй граничные случаи: пустой ввод, некорректные данные, отсутствующая запись.
  • Каждое исправление подтверждай повторным прогоном теста/сценария.
Шаг 6

Git: сохраняй работу по ходу

Если в задании есть контроль версий — фиксируй прогресс коммитами. Даже без требования это страховка от потери кода при сбое.

Базовый цикл работы
git init
git add .
git commit -m "каркас приложения DemoApp"
# ... после каждого готового пункта:
git add -A
git commit -m "модуль БД: таблицы и запросы"
git log --oneline
Финал

Самопроверка и типичные ошибки

  1. Все требования ТЗ выписаны отдельным списком и выполнены по пунктам.
  2. Код структурирован: классы, методы, осмысленные имена, обработка ошибок.
  3. БД нормализована, есть связи и ограничения, запросы возвращают верный результат.
  4. Приложение подключается к БД параметризованными запросами.
  5. Веб-часть (если есть) работает: фронтенд получает и отправляет данные.
  6. Тесты проходят, исправления подтверждены повторным прогоном.
  7. Прогресс сохранён коммитами; перед сдачей — финальная самопроверка по критериям.
  • Реализовали не то, что в ТЗ — перечитай требования и сверься по списку.
  • Весь код в одном методе/файле — теряются баллы за структуру.
  • Нет проверки ввода и обработки ошибок — приложение падает на крайних случаях.
  • Склеивание SQL из строк — уязвимость и потеря баллов.
  • Оставили самопроверку на последние минуты.
Практика

Практическая сборка приложения (как на экзамене)

На многих площадках демонстрационный экзамен 09.02.07 — это десктоп-приложение: среда Qt Creator (Qt Designer для форм) + Python/PyQt либо C#. Нужно собрать рабочее приложение с входом, таблицами и формами по ТЗ, связать его с базой данных и закрыть пункты критериев. Ниже — практический порядок сборки, как реально идёт работа на экзамене.

  1. Подготовить окружение: IDE, СУБД, инструмент диаграмм, Git.
  2. Построить диаграммы Use Case и ER по предметной области.
  3. Создать базу данных по ER-диаграмме.
  4. Сделать окно входа (авторизацию).
  5. Сверстать интерфейс: таблицы и формы.
  6. Реализовать CRUD к базе: добавление, просмотр, изменение, удаление.
  7. Проверить по критериям и оформить код по руководству стиля.

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) — это и есть твоя база.

SQL: таблицы users и requests по ER-диаграмме
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).

Python (PyQt6): окно входа с проверкой по БД
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.

Python (PyQt6): главное окно — список заявок и добавление
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).

Python: изменение статуса и удаление заявки
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()
Руководство по стилю влияет на баллы.Оформление кода — отдельный пункт критериев. Python — по PEP 8: snake_case для функций и переменных, PascalCase для классов, осмысленные имена, без закомментированного мусора. Единый стиль важнее, чем «как покрасивее».
Помощь

Остались вопросы по подготовке

Задавай вопросы ИИ-помощнику на странице — он отвечает по теме демонстрационного экзамена 09.02.07: разработка приложения, SQL и БД, веб-часть, тестирование, Git, разбор критериев. Значения в примерах (имя базы, пользователь, порт) меняй под свой вариант — они подставятся во всю документацию.

Инструменты подготовки

премиум
Генератор варианта стенда

Один клик — и адреса, подсети, VLAN, порты и пароли меняются на новый согласованный вариант. Значения подставятся во все команды и таблицы на странице. Удобно тренироваться «как на экзамене», когда даны другие числа.

Таймер экзамена
8:00:00
Длительность:мин
Чек-лист
0/0
    Тренажёр-тест
    1. 1. Как защититься от SQL-инъекций?
    2. 2. До какой нормальной формы обычно нормализуют схему БД?
    3. 3. Что возвращает LEFT JOIN?
    🔒
    Инструменты подготовки к экзамену

    Откройте все памятки, тренажёры и инструменты подготовки — разово за 99, без регистрации.

    Документация обновлена: 2026. Материал по настройке оборудования на ОС Альт Linux. Проверяй актуальные значения под своё оборудование.
    ИИ-помощник
    по специальности 09.02.07

    Задай вопрос по настройке, командам или теории этой специальности — отвечу на основе ИИ.

    Бесплатных вопросов осталось: 3.
    🚀 Готовься уверенно, а не вслепую

    Премиум открывает сразу всё:

    • генератор варианта стенда
    • таймер экзамена и чек-лист
    • тренажёр-тесты с автопроверкой
    • памятки и экспорт в PDF
    • 100 вопросов ИИ-помощнику

    дешевле чашки кофе · разово · без подписки и регистрации

    Частые вопросы по специальности

    Что входит в демонстрационный экзамен по 09.02.07?

    Разработка приложения по техническому заданию (ООП), проектирование и нормализация базы данных, SQL, подключение приложения к БД, веб-приложение, юнит-тестирование и работа с Git.

    Как защититься от SQL-инъекций?

    Использовать параметризованные запросы (подготовленные выражения), а не склеивать SQL из строк.

    До какой нормальной формы обычно нормализуют базу данных?

    Как правило, до третьей нормальной формы (3НФ) — этого достаточно для большинства учебных заданий.

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

    Памятки к экзамену