From f799085d55960a0c05165a2ca1c9ce5fbbce3606 Mon Sep 17 00:00:00 2001 From: pajjilykk Date: Fri, 3 Apr 2026 21:12:02 +0700 Subject: [PATCH] lab4 kiri --- .shii/lab4/app.py | 138 +++++++++++++++ .shii/lab4/requirements.txt | 2 + .shii/lab4/run.sh | 54 ++++++ .shii/lab4/static/main.css | 198 ++++++++++++++++++++++ .shii/lab4/templates/base.html | 48 ++++++ .shii/lab4/templates/create_employee.html | 26 +++ .shii/lab4/templates/employee_detail.html | 11 ++ .shii/lab4/templates/employee_update.html | 24 +++ .shii/lab4/templates/employees.html | 20 +++ .shii/lab4/templates/error.html | 5 + .shii/lab4/templates/index.html | 6 + 11 files changed, 532 insertions(+) create mode 100644 .shii/lab4/app.py create mode 100644 .shii/lab4/requirements.txt create mode 100644 .shii/lab4/run.sh create mode 100644 .shii/lab4/static/main.css create mode 100644 .shii/lab4/templates/base.html create mode 100644 .shii/lab4/templates/create_employee.html create mode 100644 .shii/lab4/templates/employee_detail.html create mode 100644 .shii/lab4/templates/employee_update.html create mode 100644 .shii/lab4/templates/employees.html create mode 100644 .shii/lab4/templates/error.html create mode 100644 .shii/lab4/templates/index.html diff --git a/.shii/lab4/app.py b/.shii/lab4/app.py new file mode 100644 index 0000000..cbbf61f --- /dev/null +++ b/.shii/lab4/app.py @@ -0,0 +1,138 @@ +import re +from datetime import datetime + +from flask import Flask, render_template, request, redirect, url_for, flash +from flask_sqlalchemy import SQLAlchemy + +app = Flask(__name__) +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///accounting.db' +app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False +app.config['SECRET_KEY'] = "lab4_secret_key" + +db = SQLAlchemy(app) + + +# ---------------- МОДЕЛЬ ---------------- + +class Employee(db.Model): + id = db.Column(db.Integer, primary_key=True) + fullname = db.Column(db.String(120), nullable=False) + salary = db.Column(db.Integer, nullable=False) + created_at = db.Column(db.DateTime, default=datetime.utcnow) + + def __repr__(self): + return f'' + + +# ----------- ВАЛИДАЦИЯ БЭКЕНДА ------------ + +def validate_employee(fullname: str, salary: str): + errors = [] + + fullname = fullname.strip() + + if not fullname: + errors.append("ФИО не может быть пустым") + elif len(fullname) < 5 or len(fullname) > 120: + errors.append("ФИО должно быть от 5 до 120 символов") + elif not re.fullmatch(r"[A-Za-zА-Яа-яЁё\s\-\']+", fullname): + errors.append("ФИО может содержать только буквы, пробелы, дефисы и апострофы") + + if not salary: + errors.append("Зарплата обязательна") + else: + try: + salary = int(salary) + if salary <= 0 or salary > 10_000_000: + errors.append("Зарплата должна быть от 1 до 10 000 000") + except: + errors.append("Зарплата должна быть числом") + + return errors + + +# ---------------- РОУТЫ ---------------- + +@app.route("/") +def index(): + return render_template("index.html") + + +@app.route("/employees") +def employees(): + items = Employee.query.order_by(Employee.created_at.desc()).all() + total_salary = sum(e.salary for e in items) + return render_template("employees.html", employees=items, total_salary=total_salary) + + +@app.route("/create", methods=["GET", "POST"]) +def create_employee(): + if request.method == "POST": + fullname = request.form.get("fullname", "") + salary = request.form.get("salary", "") + + errors = validate_employee(fullname, salary) + if errors: + for e in errors: + flash(e, "error") + return render_template("create_employee.html", fullname=fullname, salary=salary) + + employee = Employee(fullname=fullname.strip(), salary=int(salary)) + + try: + db.session.add(employee) + db.session.commit() + return redirect(url_for("employees")) + except: + return render_template("error.html", msg="Ошибка добавления") + + return render_template("create_employee.html") + + +@app.route("/employees/") +def employee_detail(id): + emp = Employee.query.get_or_404(id) + return render_template("employee_detail.html", emp=emp) + + +@app.route("/employees//delete") +def employee_delete(id): + emp = Employee.query.get_or_404(id) + try: + db.session.delete(emp) + db.session.commit() + return redirect(url_for("employees")) + except: + return render_template("error.html", msg="Ошибка удаления") + + +@app.route("/employees//update", methods=["GET", "POST"]) +def employee_update(id): + emp = Employee.query.get_or_404(id) + + if request.method == "POST": + fullname = request.form.get("fullname", "") + salary = request.form.get("salary", "") + + errors = validate_employee(fullname, salary) + if errors: + for e in errors: + flash(e, "error") + return render_template("employee_update.html", emp=emp) + + emp.fullname = fullname.strip() + emp.salary = int(salary) + + try: + db.session.commit() + return redirect(url_for("employees")) + except: + return render_template("error.html", msg="Ошибка обновления") + + return render_template("employee_update.html", emp=emp) + + +if __name__ == "__main__": + with app.app_context(): + db.create_all() + app.run(debug=True) diff --git a/.shii/lab4/requirements.txt b/.shii/lab4/requirements.txt new file mode 100644 index 0000000..3ffc5b4 --- /dev/null +++ b/.shii/lab4/requirements.txt @@ -0,0 +1,2 @@ +Flask==3.0.0 +Flask-SQLAlchemy==3.1.1 \ No newline at end of file diff --git a/.shii/lab4/run.sh b/.shii/lab4/run.sh new file mode 100644 index 0000000..0ef23a0 --- /dev/null +++ b/.shii/lab4/run.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +# Get the directory where this script is located +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd "$SCRIPT_DIR" + +echo "🔧 Lab4 Application Setup & Launch" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + +# Check and create venv if not exists +if [ ! -d "venv" ]; then + echo "📦 Virtual environment not found. Creating venv..." + python3 -m venv venv + echo "✅ venv created" +else + echo "✅ venv already exists" +fi + +# Activate venv +echo "🔄 Activating virtual environment..." +source venv/bin/activate + +# Install/upgrade requirements +if [ -f "requirements.txt" ]; then + echo "📥 Installing dependencies from requirements.txt..." + pip install -q --upgrade pip + pip install -q -r requirements.txt + echo "✅ Dependencies installed" +else + echo "⚠️ requirements.txt not found!" + exit 1 +fi + +# Create instance directory if not exists +if [ ! -d "instance" ]; then + echo "📁 Creating instance directory..." + mkdir -p instance +fi + +# Remove old database to create fresh one +if [ -f "instance/accounting.db" ]; then + echo "🗑️ Removing old database..." + rm instance/accounting.db +fi + +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "🚀 Starting Flask Application..." +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# Run the Flask app +python app.py + diff --git a/.shii/lab4/static/main.css b/.shii/lab4/static/main.css new file mode 100644 index 0000000..a2b9e99 --- /dev/null +++ b/.shii/lab4/static/main.css @@ -0,0 +1,198 @@ +body { + margin: 0; + font-family: Arial, sans-serif; + background: linear-gradient(120deg,#e6f4ff,#f5fbff); + color:#1b2b34; +} + +.header { + background: linear-gradient(90deg, #084b83, #0d6ba3); + color: white; + padding: 20px 0; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.header-inner { + display: flex; + justify-content: space-between; + align-items: center; +} + +.logo { + font-size: 24px; + font-weight: bold; +} + +.header nav { + display: flex; + gap: 30px; +} + +.header nav a { + color: white; + text-decoration: none; + font-weight: 500; + transition: opacity 0.3s; +} + +.header nav a:hover { + opacity: 0.8; +} + +.footer { + text-align: center; + padding: 20px; + background: #f0f8ff; + border-top: 1px solid #b5d9ff; + margin-top: 40px; + color: #666; +} + +.container { + max-width: 900px; + margin: auto; + padding: 30px; +} + +.nav { + background: #cce7ff; + padding: 15px; + display:flex; + gap:20px; + justify-content:center; +} + +.nav a { + text-decoration:none; + font-weight:bold; + color:#084b83; +} + +h1,h2,h3 { word-wrap: break-word; } + +.card { + background:white; + padding:20px; + margin:20px 0; + border-radius:12px; + box-shadow:0 4px 12px rgba(0,0,0,0.1); + word-wrap: break-word; +} + +.form { + display:flex; + flex-direction:column; + gap:15px; +} + +.form-group { + display: flex; + flex-direction: column; + gap: 6px; +} + +.form-group label { + font-weight: 600; + color: #084b83; + font-size: 15px; +} + +.form-group small { + color: #666; + font-size: 13px; + font-weight: 400; +} + +input, textarea { + padding:12px; + border-radius:8px; + border:1px solid #b5d9ff; + font-size:16px; +} + +button, .btn { + background:#4da6ff; + color:white; + padding:10px 15px; + border-radius:8px; + text-decoration:none; + border:none; + cursor:pointer; + display:inline-block; + font-weight: 600; + transition: background 0.2s; +} + +button:hover, .btn:hover { + background: #3a8ee6; +} + +.btn-primary { + background: linear-gradient(135deg, #4da6ff 0%, #2e7fd6 100%); + padding: 12px 24px; + font-size: 16px; +} + +.btn-primary:hover { + background: linear-gradient(135deg, #3a8ee6 0%, #1f5db8 100%); +} + +.btn.danger { background:#ff6b6b; } + +.btn.danger:hover { background: #ff5252; } + +.alert { + background: linear-gradient(135deg, #ff6b6b 0%, #ff8787 100%); + border: none; + border-radius: 12px; + padding: 16px 20px; + margin-bottom: 25px; + color: white; + font-weight: 600; + box-shadow: 0 6px 20px rgba(255, 107, 107, 0.3); + animation: slideDown 0.3s ease-out; +} + +.error-window { + background: linear-gradient(135deg, #ff6b6b 0%, #ff8787 100%); + border: 3px solid #d63031; + border-radius: 14px; + padding: 20px; + margin-bottom: 30px; + box-shadow: 0 8px 25px rgba(255, 107, 107, 0.4); + animation: slideDown 0.3s ease-out; +} + +.error-header { + color: white; + font-weight: 700; + font-size: 18px; + margin-bottom: 15px; + display: flex; + align-items: center; + gap: 8px; +} + +.error-list { + display: flex; + flex-direction: column; + gap: 10px; +} + +.error-item { + color: white; + font-weight: 500; + font-size: 16px; + line-height: 1.4; +} + +@keyframes slideDown { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} \ No newline at end of file diff --git a/.shii/lab4/templates/base.html b/.shii/lab4/templates/base.html new file mode 100644 index 0000000..78d5be9 --- /dev/null +++ b/.shii/lab4/templates/base.html @@ -0,0 +1,48 @@ + + + + + {% block title %}Бухгалтерия{% endblock %} + + + + + + + + + +
+
+ + +
+
+ +
+ {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
+
⚠️ Ошибки валидации
+
+ {% for category, message in messages %} +
• {{ message }}
+ {% endfor %} +
+
+ {% endif %} + {% endwith %} + + {% block body %}{% endblock %} +
+ +
+ Flask + SQLite • Лабораторная №4 +
+ + + \ No newline at end of file diff --git a/.shii/lab4/templates/create_employee.html b/.shii/lab4/templates/create_employee.html new file mode 100644 index 0000000..fcc617f --- /dev/null +++ b/.shii/lab4/templates/create_employee.html @@ -0,0 +1,26 @@ +{% extends 'base.html' %} +{% block body %} +
+

Добавить сотрудника

+ +
+
+ + + Минимум 5 символов, поддерживаются дефисы и апострофы +
+ +
+ + + От 1 до 10 000 000 рублей +
+ + +
+
+{% endblock %} \ No newline at end of file diff --git a/.shii/lab4/templates/employee_detail.html b/.shii/lab4/templates/employee_detail.html new file mode 100644 index 0000000..ee079e6 --- /dev/null +++ b/.shii/lab4/templates/employee_detail.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% block body %} +
+

{{ emp.fullname }}

+

Зарплата: {{ emp.salary }} ₽

+

Добавлен: {{ emp.created_at.date() }}

+ + Редактировать + Удалить +
+{% endblock %} \ No newline at end of file diff --git a/.shii/lab4/templates/employee_update.html b/.shii/lab4/templates/employee_update.html new file mode 100644 index 0000000..36fed83 --- /dev/null +++ b/.shii/lab4/templates/employee_update.html @@ -0,0 +1,24 @@ +{% extends 'base.html' %} +{% block body %} +
+

Редактирование сотрудника

+ +
+
+ + + Минимум 5 символов, поддерживаются дефисы и апострофы +
+ +
+ + + От 1 до 10 000 000 рублей +
+ + +
+
+{% endblock %} \ No newline at end of file diff --git a/.shii/lab4/templates/employees.html b/.shii/lab4/templates/employees.html new file mode 100644 index 0000000..7a6e624 --- /dev/null +++ b/.shii/lab4/templates/employees.html @@ -0,0 +1,20 @@ +{% extends 'base.html' %} +{% block title %}Сотрудники{% endblock %} + +{% block body %} +

Список сотрудников

+ +

Общий фонд зарплат: {{ total_salary }} ₽

+ +{% if employees %} +{% for e in employees %} +
+

{{ e.fullname }}

+

Зарплата: {{ e.salary }} ₽

+ Подробнее +
+{% endfor %} +{% else %} +

Сотрудников пока нет

+{% endif %} +{% endblock %} \ No newline at end of file diff --git a/.shii/lab4/templates/error.html b/.shii/lab4/templates/error.html new file mode 100644 index 0000000..236b542 --- /dev/null +++ b/.shii/lab4/templates/error.html @@ -0,0 +1,5 @@ +{% extends 'base.html' %} +{% block body %} +

Ошибка

+

{{ msg }}

+{% endblock %} \ No newline at end of file diff --git a/.shii/lab4/templates/index.html b/.shii/lab4/templates/index.html new file mode 100644 index 0000000..aea8b18 --- /dev/null +++ b/.shii/lab4/templates/index.html @@ -0,0 +1,6 @@ +{% extends 'base.html' %} + +{% block body %} +

Добро пожаловать в систему бухгалтерии

+

Используйте меню для навигации по приложению.

+{% endblock %}