This commit is contained in:
2026-03-18 11:32:41 +03:00
parent ab1d4c72fa
commit b268cedad6
5 changed files with 378 additions and 70 deletions

1
4/.gitignore vendored
View File

@ -1 +1,2 @@
data.sql
env.sh

260
4/data.js
View File

@ -0,0 +1,260 @@
import * as crypto from 'node:crypto'
import * as fs from 'node:fs'
const GenMaxUInt = (max) => Math.floor(Math.random() * max)
const GenInt = (from, to) => Math.floor(Math.random() * (to - from) + from)
const male_full_name = [
"Семенов Дмитрий Романович",
"Захаров Сергей Григорьевич",
"Панов Артём Маркович",
"Исаев Владимир Елисеевич",
"Кириллов Степан Дмитриевич",
"Никитин Георгий Егорович",
"Терехов Даниил Давидович",
"Николаев Роман Артёмович",
"Соболев Сергей Васильевич",
"Афанасьев Серафим Тимофеевич",
]
const female_full_name = [
"Макарова Виктория Глебовна",
"Фролова Александра Владимировна",
"Сычева Валерия Дмитриевна",
"Плотникова Милана Марковна",
"Носкова Таисия Тимуровна",
"Чернышева Алиса Михайловна",
"Михайлова Мария Яковлевна",
"Никитина Ева Владимировна",
"Лобанова Василиса Тимофеевна",
"Князева Анна Данииловна",
]
function GenFullName() {
const sex = GenMaxUInt(2) ? 'male' : 'female'
let full_name
if (sex == 'male') {
full_name = male_full_name
} else {
full_name = female_full_name
}
const l = full_name.length
const name_i = GenMaxUInt(l)
const surname_i = GenMaxUInt(l)
const middle_name_i = GenMaxUInt(l)
const [name] = full_name[name_i].split(' ')
const [, surname] = full_name[surname_i].split(' ')
const [, , middle_name] = full_name[middle_name_i].split(' ')
return `'${name} ${surname} ${middle_name}'`
}
const food_product_names = [
'хлеб',
'картофель',
'яйца',
'лук',
'чеснок',
'соль',
'сахар',
'масло',
'макароны',
'молоко',
]
const drug_names = [
"парацетамол",
"ибупрофен",
"аспирин",
"левомеколь",
"активированный уголь",
"но-шпа",
"цитрамон",
"лоратадин",
"кагоцел",
"амоксициллин",
]
const street_names = [
"Улица Ленина",
"Проспект Мира",
"Улица Гагарина",
"Набережная Реки Фонтанки",
"Бульвар Космонавтов",
"Переулок Строителей",
"Проезд Шевченко",
"Улица 8 Марта",
"Микрорайон Солнечный",
"Шоссе Энтузиастов",
]
function GenPassport() {
let res = ''
for (let i = 0; i < 10; i++) {
res += GenMaxUInt(10)
}
return `'` + res.slice(0, 4) + ' ' + res.slice(4) + `'`
}
function GenAddress() {
return `'` + street_names[GenMaxUInt(street_names.length)] + ' ' + GenMaxUInt(100) + `'`
}
function GenPasswdHash() {
return `decode('${crypto.randomBytes(32).toString('base64')}', 'base64')`
}
function GenEmail() {
return `'${GenMaxUInt(1000)}@mephi.ru'`
}
function GenPhone() {
let res = `'+7`
for (let i = 0; i < 10; i++) {
res += GenMaxUInt(10)
}
return res + `'`
}
const billing = [
'сбербанк',
'тинкофф',
'ипб',
]
function GenBillingAccount() {
return `'${billing[GenMaxUInt(billing.length)]}'`
}
function GenTimestamp() {
const start = new Date(2024, 0, 1).getTime()
const end = new Date(2026, 0, 1).getTime()
return `'${new Date(GenInt(start, end)).toISOString()}'`
}
function GenIntervalHours(max = 6) {
return `'${GenInt(1, max)} hours'`
}
function GenPrice(max = 1000) {
return (Math.random() * max + 10).toFixed(2)
}
const ClassTypes = ['Theory', 'Practice']
const RegistrationStatuses = ['NotPaid', 'Paid']
const EquipmentUsageTypes = ['PayOnce', 'PayMonthly']
function Values(callback) {
let res = 'VALUES\n'
const count = 5
for (let i = 0; i < count; i++) {
res += callback()
if (i == count - 1) {
res += ';\n\n'
} else {
res += ',\n'
}
}
return res
}
// Master
let script = 'INSERT INTO "Master" (full_name, passwd_hash, passport, readme) ' + Values(() => {
return `(${GenFullName()}, ${GenPasswdHash()}, ${GenPassport()}, 'aboba')`
})
// Client
script += 'INSERT INTO "Client" (full_name, passwd_hash, phone, email, billing_account) ' + Values(() => {
return `(${GenFullName()}, ${GenPasswdHash()}, ${GenPassport()}, ${GenPhone()}, ${GenBillingAccount()})`
})
// Studio
script += 'INSERT INTO "Studio" (address, capacity, begin_date, duration) ' + Values(() => {
return `(${GenAddress()}, ${GenInt(10, 80)}, ${GenTimestamp()}, ${GenIntervalHours(12)})`
})
// Course
script += 'INSERT INTO "Course" ("Master_id", name, price, duration) ' + Values(() => {
return `(${GenInt(1, 6)}, 'курс ${GenMaxUInt(100)}', ${GenPrice()}, ${GenIntervalHours(20)})`
})
// Class
script += 'INSERT INTO "Class" (begin_date, "Course_id", "Studio_id", name, type, duration) ' + Values(() => {
return `(${GenTimestamp()}, ${GenInt(1, 6)}, ${GenInt(1, 6)}, 'занятие ${GenMaxUInt(100)}', '${ClassTypes[GenMaxUInt(ClassTypes.length)]}', ${GenIntervalHours()})`
})
// Class_Master
script += `INSERT INTO "Class_Master" ("Class_begin_date", "Class_Course_id", "Master_id")
SELECT begin_date, "Course_id", ${GenInt(1, 6)} FROM "Class" LIMIT 1;\n\n`
// Registration
script += 'INSERT INTO "Registration" ("Client_id", "Course_id", date, status) ' + Values(() => {
return `(${GenInt(6, 11)}, ${GenInt(1, 6)}, ${GenTimestamp()}, '${RegistrationStatuses[GenMaxUInt(RegistrationStatuses.length)]}')`
})
// FoodProductEnum
script += 'INSERT INTO "FoodProductEnum" (name, avg_price) ' + Values(() => {
return `('${food_product_names[GenMaxUInt(food_product_names.length)]}', ${GenPrice(200)})`
})
// FoodProduct
script += `INSERT INTO "FoodProduct" ("FoodProductEnum_id", "Class_begin_date", "Class_Course_id", buy_price, buy_date, delivery_price, delivery_date, expires_date)
SELECT ${GenInt(1, 6)}, begin_date, "Course_id", ${GenPrice()}, ${GenTimestamp()}, ${GenPrice()}, ${GenTimestamp()}, ${GenTimestamp()} FROM "Class" LIMIT 1;\n\n`
// Equipment
script += 'INSERT INTO "Equipment" ("Studio_id", name, usage_price, usage_type, delivery_price, delivery_date) ' + Values(() => {
return `(${GenInt(1, 6)}, 'оборудование ${GenMaxUInt(100)}', ${GenPrice(300)}, '${EquipmentUsageTypes[GenMaxUInt(EquipmentUsageTypes.length)]}', ${GenPrice(200)}, ${GenTimestamp()})`
})
// Class_Equipment
script += `INSERT INTO "Class_Equipment" ("Class_begin_date", "Class_Course_id", "Equipment_id")
SELECT begin_date, "Course_id", ${GenInt(1, 6)} FROM "Class" LIMIT 1;\n\n`
// DrugEnum
script += 'INSERT INTO "DrugEnum" (name, avg_price) ' + Values(() => {
return `('${drug_names[GenMaxUInt(drug_names.length)]}', ${GenPrice(500)})`
})
// Drug
script += 'INSERT INTO "Drug" ("DrugEnum_id", "Course_id", buy_price, buy_date, delivery_price, delivery_date, expires_date) ' + Values(() => {
return `(${GenInt(1, 6)}, ${GenInt(1, 6)}, ${GenPrice()}, ${GenTimestamp()}, ${GenPrice()}, ${GenTimestamp()}, ${GenTimestamp()})`
})
// ActivityLog
script += 'INSERT INTO "ActivityLog" ("Person_id", action) ' + Values(() => {
return `(${GenInt(1, 10)}, 'login')`
})
const tables = [
'Person',
'Client',
'Master',
'Studio',
'Course',
'Class',
'Class_Master',
'Registration',
'FoodProductEnum',
'FoodProduct',
'Equipment',
'Class_Equipment',
'DrugEnum',
'Drug',
'DrugIntolerance',
'ActivityLog'
]
for (const table of tables) {
script += `SELECT * FROM "${table}";\n`
}
fs.writeFileSync('data.sql', script)

View File

@ -2,12 +2,6 @@
set -exu
SERVER=root@185.103.252.32
PSQL="sudo -u postgres psql"
DB=db2026
scp schema.sql data.sql "$SERVER:/tmp/$DB/"
ssh $SERVER \
"chmod -R 777 /tmp/$DB;" \
"$PSQL -d $DB -e -q -f /tmp/$DB/schema.sql;" \
"$PSQL -d $DB -e -q -f /tmp/$DB/data.sql;"
source env.sh
psql -e -q -f schema.sql
psql -e -q -f data.sql

View File

@ -23,3 +23,31 @@
* SQL-скрипт начального наполнения;
* скрипт генерации тестовых данных (Python или другой удобный вам язык);
* инструкция по запуску.
# schema.sql - SQL-скрипт создания схемы;
# data.js - скрипт генерации тестовых данных;
## инструкция по запуску:
```{sh}
node data.js
```
## инструкция по деплою:
создать файл env.sh
```{sh}
export PGUSER=""
export PGDATABASE=""
export PGPASSWORD=""
export PGHOST=""
export PGPORT=""
```
запустить
```{sh}
./deploy.sh
```
# data.sql - SQL-скрипт начального наполнения;

View File

@ -1,10 +1,14 @@
DROP TABLE IF EXISTS "Person" CASCADE;
DROP SEQUENCE IF EXISTS "PersonId";
CREATE SEQUENCE "PersonId";
CREATE TABLE "Person" (
id int PRIMARY KEY,
id int PRIMARY KEY DEFAULT nextval('"PersonId"'),
full_name text NOT NULL,
passwd_hash char(64) NOT NULL
passwd_hash bytea NOT NULL
);
@ -28,8 +32,10 @@ CREATE TABLE "Master" (
DROP TABLE IF EXISTS "Studio", "Course", "Class", "Class_Master", "Registration", "FoodProductEnum", "FoodProduct", "Equipment", "Class_Equipment", "DrugEnum", "Drug", "DrugIntolerance", "ActivityLog";
DROP TYPE IF EXISTS "ClassType", "RegistrationStatus", "EquipmentUsageType";
CREATE TABLE "Studio" (
id int PRIMARY KEY,
id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
address text UNIQUE NOT NULL,
capacity int NOT NULL CHECK (capacity > 0),
begin_date timestamptz NOT NULL,
@ -38,187 +44,206 @@ CREATE TABLE "Studio" (
CREATE TABLE "Course" (
id int PRIMARY KEY,
Master_id int,
id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
"Master_id" int,
name text NOT NULL,
price real NOT NULL CHECK (price > 0),
duration interval NOT NULL,
FOREIGN KEY (Master_id) REFERENCES "Master" (id)
FOREIGN KEY ("Master_id") REFERENCES "Master" (id)
ON DELETE SET NULL
ON UPDATE CASCADE
);
CREATE TYPE "ClassType" AS ENUM (
'Theory',
'Practice'
);
CREATE TABLE "Class" (
begin_date timestamptz,
Course_id int,
Studio_id int,
"Course_id" int,
"Studio_id" int,
name text NOT NULL,
type int NOT NULL,
type "ClassType" NOT NULL,
duration interval NOT NULL,
PRIMARY KEY (begin_date, Course_id),
PRIMARY KEY (begin_date, "Course_id"),
FOREIGN KEY (Course_id) REFERENCES "Course" (id)
FOREIGN KEY ("Course_id") REFERENCES "Course" (id)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (Studio_id) REFERENCES "Studio" (id)
FOREIGN KEY ("Studio_id") REFERENCES "Studio" (id)
ON DELETE SET NULL
ON UPDATE CASCADE
);
CREATE TABLE "Class_Master" (
Class_begin_date timestamptz,
Class_Course_id int,
Master_id int,
"Class_begin_date" timestamptz,
"Class_Course_id" int,
"Master_id" int,
PRIMARY KEY (Class_begin_date, Class_Course_id, Master_id),
PRIMARY KEY ("Class_begin_date", "Class_Course_id", "Master_id"),
FOREIGN KEY (Master_id) REFERENCES "Master" (id)
FOREIGN KEY ("Master_id") REFERENCES "Master" (id)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (Class_begin_date, Class_Course_id) REFERENCES "Class" (begin_date, Course_id)
FOREIGN KEY ("Class_begin_date", "Class_Course_id") REFERENCES "Class" (begin_date, "Course_id")
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TYPE "RegistrationStatus" AS ENUM (
'NotPaid',
'Paid'
);
CREATE TABLE "Registration" (
Client_id int,
Course_id int,
"Client_id" int,
"Course_id" int,
date timestamptz NOT NULL,
status int NOT NULL,
status "RegistrationStatus" NOT NULL,
PRIMARY KEY (Client_id, Course_id),
PRIMARY KEY ("Client_id", "Course_id"),
FOREIGN KEY (Client_id) REFERENCES "Client" (id)
FOREIGN KEY ("Client_id") REFERENCES "Client" (id)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (Course_id) REFERENCES "Course" (id)
FOREIGN KEY ("Course_id") REFERENCES "Course" (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE "FoodProductEnum" (
id int PRIMARY KEY,
id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name text NOT NULL,
avg_price real NOT NULL
);
CREATE TABLE "FoodProduct" (
id int,
FoodProductEnum_id int,
Class_begin_date timestamptz,
Class_Course_id int,
id int GENERATED ALWAYS AS IDENTITY,
"FoodProductEnum_id" int,
"Class_begin_date" timestamptz,
"Class_Course_id" int,
buy_price real NOT NULL,
buy_date timestamptz NOT NULL,
delivery_price real NOT NULL,
delivery_date timestamptz NOT NULL,
expires_date timestamptz NOT NULL,
PRIMARY KEY (id, FoodProductEnum_id),
PRIMARY KEY (id, "FoodProductEnum_id"),
FOREIGN KEY (FoodProductEnum_id) REFERENCES "FoodProductEnum" (id)
FOREIGN KEY ("FoodProductEnum_id") REFERENCES "FoodProductEnum" (id)
ON DELETE RESTRICT
ON UPDATE RESTRICT,
FOREIGN KEY (Class_begin_date, Class_Course_id) REFERENCES "Class" (begin_date, Course_id)
FOREIGN KEY ("Class_begin_date", "Class_Course_id") REFERENCES "Class" (begin_date, "Course_id")
ON DELETE SET NULL
ON UPDATE CASCADE
);
CREATE TYPE "EquipmentUsageType" AS ENUM (
'PayOnce',
'PayMonthly'
);
CREATE TABLE "Equipment" (
id int PRIMARY KEY,
Studio_id int,
id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
"Studio_id" int,
name text NOT NULL,
usage_price real NOT NULL,
usage_type int NOT NULL,
usage_type "EquipmentUsageType" NOT NULL,
delivery_price real NOT NULL,
delivery_date timestamptz NOT NULL,
FOREIGN KEY (Studio_id) REFERENCES "Studio" (id)
FOREIGN KEY ("Studio_id") REFERENCES "Studio" (id)
ON DELETE SET NULL
ON UPDATE CASCADE
);
CREATE TABLE "Class_Equipment" (
Class_begin_date timestamptz,
Class_Course_id int,
Equipment_id int,
"Class_begin_date" timestamptz,
"Class_Course_id" int,
"Equipment_id" int,
PRIMARY KEY (Class_begin_date, Class_Course_id, Equipment_id),
PRIMARY KEY ("Class_begin_date", "Class_Course_id", "Equipment_id"),
FOREIGN KEY (Class_begin_date, Class_course_id) REFERENCES "Class" (begin_date, Course_id)
FOREIGN KEY ("Class_begin_date", "Class_Course_id") REFERENCES "Class" (begin_date, "Course_id")
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (Equipment_id) REFERENCES "Equipment" (id)
FOREIGN KEY ("Equipment_id") REFERENCES "Equipment" (id)
ON DELETE RESTRICT
ON UPDATE CASCADE
);
CREATE TABLE "DrugEnum" (
id int PRIMARY KEY,
id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name text NOT NULL,
avg_price real NOT NULL
);
CREATE TABLE "Drug" (
id int,
DrugEnum_id int,
Course_id int,
id int GENERATED ALWAYS AS IDENTITY,
"DrugEnum_id" int,
"Course_id" int,
buy_price real NOT NULL,
buy_date timestamptz NOT NULL,
delivery_price real NOT NULL,
delivery_date timestamptz NOT NULL,
expires_date timestamptz NOT NULL,
PRIMARY KEY (id, DrugEnum_id),
PRIMARY KEY (id, "DrugEnum_id"),
FOREIGN KEY (DrugEnum_id) REFERENCES "DrugEnum" (id)
FOREIGN KEY ("DrugEnum_id") REFERENCES "DrugEnum" (id)
ON DELETE RESTRICT
ON UPDATE RESTRICT,
FOREIGN KEY (Course_id) REFERENCES "Course" (id)
FOREIGN KEY ("Course_id") REFERENCES "Course" (id)
ON DELETE SET NULL
ON UPDATE CASCADE
);
CREATE TABLE "DrugIntolerance" (
Person_id int,
DrugEnum_id int,
"Person_id" int,
"DrugEnum_id" int,
PRIMARY KEY (Person_id, DrugEnum_id),
PRIMARY KEY ("Person_id", "DrugEnum_id"),
FOREIGN KEY (Person_id) REFERENCES "Person" (id)
FOREIGN KEY ("Person_id") REFERENCES "Person" (id)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (DrugEnum_id) REFERENCES "DrugEnum" (id)
FOREIGN KEY ("DrugEnum_id") REFERENCES "DrugEnum" (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE "ActivityLog" (
id int,
Person_id int,
id int GENERATED ALWAYS AS IDENTITY,
"Person_id" int,
action text NOT NULL,
PRIMARY KEY (id, Person_id),
PRIMARY KEY (id, "Person_id"),
FOREIGN KEY (Person_id) REFERENCES "Person" (id)
FOREIGN KEY ("Person_id") REFERENCES "Person" (id)
ON DELETE NO ACTION
ON UPDATE CASCADE
);