push 0.5 & create 0.6(update)

This commit is contained in:
ada-dmitry
2024-05-09 12:06:44 +03:00
parent eb0bdf5049
commit 76065b7ec2
35 changed files with 615 additions and 26 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+6
View File
@@ -0,0 +1,6 @@
# Базовые параметры настройки конфигурации проекта
dbname: DBProd # В классе менять не надо
user: postgres # Замените на свой логин от БД в классе
password: Qwerty # Замените на свой пароль от БД в классе
host: localhost # Замените на 192.168.0.48 в классе
dbtableprefix: "public." # Замените на свой логин от БД
+34
View File
@@ -0,0 +1,34 @@
# Установка соединения с базой данных
# (параметры передаются через класс конфиг).
import psycopg2
class DbConnection:
def __init__(self, config):
self.dbname = config.dbname
self.user = config.user
self.password = config.password
self.host = config.host
self.prefix = config.dbtableprefix
self.conn = psycopg2.connect(dbname = self.dbname,
user = self.user,
password = self.password,
host = self.host)
def __del__(self):
if self.conn:
self.conn.close()
def test(self):
cur = self.conn.cursor()
cur.execute("DROP TABLE IF EXISTS test CASCADE")
cur.execute("CREATE TABLE test(test integer)")
cur.execute("INSERT INTO test(test) VALUES(1)")
self.conn.commit()
cur.execute("SELECT * FROM test")
result = cur.fetchall()
cur.execute("DROP TABLE test")
self.conn.commit()
return (result[0][0] == 1)
+117
View File
@@ -0,0 +1,117 @@
# Базовые действия с таблицами
from dbconnection import *
'''
FIXME:
'''
class DbTable:
dbconn = None
def __init__(self):
return
def table_name(self):
return self.dbconn.prefix + "table"
def columns(self):
return {"test": ["integer", "PRIMARY KEY"]}
def column_names(self):
return sorted(self.columns().keys(), key = lambda x: x)
def primary_key(self):
return ['id']
def column_names_without_id(self):
res = sorted(self.columns().keys(), key = lambda x: x)
if 'id' in res:
res.remove('id')
return res
def table_constraints(self):
return []
def create(self):
sql = "CREATE TABLE " + self.table_name() + "("
arr = [k + " " + " ".join(v) for k, v in sorted(self.columns().items(), key = lambda x: x[0])]
sql += ", ".join(arr + self.table_constraints())
sql += ")"
cur = self.dbconn.conn.cursor()
cur.execute(sql)
self.dbconn.conn.commit()
return
def drop(self):
sql = "DROP TABLE IF EXISTS " + self.table_name()
cur = self.dbconn.conn.cursor()
cur.execute(sql)
self.dbconn.conn.commit()
return
def insert_one(self, vals):
for i in range(len(vals)):
if type(vals[i]) == str:
vals[i] = "'" + vals[i] + "'"
pass
else:
vals[i] = str(vals[i])
values = ", ".join(vals)
query = f"""INSERT INTO {self.table_name()}({", ".join(self.column_names_without_id())}) VALUES({values})"""
cur = self.dbconn.conn.cursor()
# cur.execute(sql)
try:
cur.execute(query)
self.dbconn.conn.commit()
except psycopg2.errors.UniqueViolation:
self.dbconn.conn.rollback()
return
def first(self):
sql = "SELECT * FROM " + self.table_name()
sql += " ORDER BY "
sql += ", ".join(self.primary_key())
cur = self.dbconn.conn.cursor()
cur.execute(sql)
return cur.fetchone()
def last(self):
sql = "SELECT * FROM " + self.table_name()
sql += " ORDER BY "
sql += ", ".join([x + " DESC" for x in self.primary_key()])
cur = self.dbconn.conn.cursor()
cur.execute(sql)
return cur.fetchone()
def all(self):
sql = "SELECT * FROM " + self.table_name()
sql += " ORDER BY "
sql += ", ".join(self.primary_key())
cur = self.dbconn.conn.cursor()
cur.execute(sql)
return cur.fetchall()
def select_one(self, **kwargs):
conditions = []
values = []
sorted_kwargs = sorted(kwargs.items(), key=lambda x: x[0])
for key, value in sorted_kwargs():
conditions.append(f"{key}=%s")
values.append(value)
sql = f"SELECT * FROM {self.table_name()} WHERE " + " AND ".join(conditions)
cur = self.dbconn.conn.cursor()
cur.execute(sql, tuple(values))
result = cur.fetchone()
cur.close()
if result:
return True
else:
return False
+204
View File
@@ -0,0 +1,204 @@
import sys
import texts
sys.path.append('tables')
from project_config import *
from dbconnection import *
from tables.dish_table import *
from tables.cath_table import *
'''
TODO:
1) Реализовать ввод порядкового номера - done
2) Удалить проверки на "русскоязычный ввод" - done
3) Устранить SQLi с помощью санации атрибутов - done
4) UPDATE для отдельный таблиц/общий?
FIXME:
'''
class Main:
config = ProjectConfig()
connection = DbConnection(config)
def __init__(self):
DbTable.dbconn = self.connection
return
def db_init(self):
cth = CathTable()
d = DishTable()
cth.create()
d.create()
return
def db_insert_somethings(self):
cth = CathTable()
dsh = DishTable()
cth.example_insert()
dsh.example_insert()
def db_drop(self):
cth = CathTable()
d = DishTable()
d.drop()
cth.drop()
return
def show_main_menu(self):
menu = texts.show_main_menu_txt
print(menu)
return
def read_next_step(self):
return input("=> ").strip()
def after_main_menu(self, next_step):
if next_step == "2":
self.db_drop()
self.db_init()
self.db_insert_somethings()
print("Таблицы созданы заново!")
return "0"
elif next_step != "1" and next_step != "9":
print("Выбрано неверное число! Повторите ввод!")
return "0"
else:
return next_step
def show_cath(self):
self.cath_id = -1
self.cath_arr = []
menu = texts.show_cath_1txt
print(menu)
lst = CathTable().all()
for i in lst:
self.cath_arr.append(str(i[0]))
for i in range(len(self.cath_arr)):
print(str(i+1) + "\t" + self.cath_arr[i])
menu = texts.show_cath_2txt
print(menu)
return
def after_show_cath(self, next_step):
"""Выбор действий после вывода категорий
"""
while True:
if next_step == "4":
x = int(input('Введите номер удаляемой категории(0 - для отмены): '))
if (x == 0):
return "1"
else:
CathTable().delete(self.cath_arr[x-1])
return "1"
elif next_step == "6": #Добавление блюда в категорию
DishTable().insert_dishone(self.cath_id)
next_step = "5"
elif next_step == "7":#Удаление блюда из категории
x = int(input('Введите номер удаляемого блюда (0 - для отмены): '))
if(x==0):
pass
else:
DishTable().delete(self.dish_arr[x-1][0])
next_step = "5"
elif next_step == "5":
next_step = self.show_dish_in_cath()
elif next_step != "0" and next_step != "9" and next_step != "3":
print("Выбрано неверное число! Повторите ввод!")
return "1"
else:
return next_step
def add_cath(self):
"""
Добавление новой категории в таблицу
"""
data = []
data.append(input("Введите название (1 - отмена): ").strip())
if data[0] == "1":
return
while((len(data[0].strip()) == 0)or(len(data[0].strip()) > 32)):
if (len(data[0].strip()) > 32):
data[0] = input("Название слишком длинное! Введите название заново (1 - отмена):").strip()
if data[0] == "1":
return
else:
data[0] = input("Название не может быть пустым! Введите название заново (1 - отмена):").strip()
if data[0] == "1":
return
CathTable().insert_one(data)
return
def show_dish_in_cath(self):
"""Вывод всех блюд в выбранной пользователем категории
"""
self.dish_arr = []
if self.cath_id == -1:
while True:
x = int(input('Выберите номер интересуемой категории (0 - отмена): '))
if(x==0):
return
else:
self.cath_id = CathTable().find_by_name(self.cath_arr[x-1])
self.cath_obj = self.cath_arr[x-1]
print("Выбрана категория: " + self.cath_obj)
print("Блюда:")
print("\tНазвание\tВремя приготовления\tКраткая инструкция\
\n-------------------------------------------------------------------------------------")
lst = DishTable().all_by_cath_id(self.cath_id)
for i in lst:
self.dish_arr.append([i[2], str(i[1]), str(i[4])])
for i in range(len(self.dish_arr)):
print(str(i+1) + "\t" + self.dish_arr[i][0] + "\t\t" + self.dish_arr[i][1] + "\t\t\t" + self.dish_arr[i][2])
menu = texts.show_dish_in_cath_txt
print(menu)
return self.read_next_step()
def main_cycle(self):
"""Основной цикл программы, регулирующий порядок действий
"""
current_menu = "0"
next_step = None
while(current_menu != "9"):
if current_menu == "0":
self.show_main_menu()
next_step = self.read_next_step()
current_menu = self.after_main_menu(next_step)
elif current_menu == "1":
self.show_cath()
next_step = self.read_next_step()
current_menu = self.after_show_cath(next_step)
elif current_menu == "2":
self.show_main_menu()
elif current_menu == "3":
self.add_cath()
current_menu = "1"
print("До свидания!")
return
def test(self):
DbTable.dbconn.test()
m = Main()
# m.test()
m.main_cycle()
+21
View File
@@ -0,0 +1,21 @@
# Загрузка настроек проекта (в данном случае только настроек соединения с БД)
# из файла config.yaml.
import yaml
class ProjectConfig:
"""Класс считывает базовые настройки из файла config.yaml"""
def __init__(self):
with open('config.yaml') as f:
config = yaml.safe_load(f)
self.dbname = config['dbname']
self.user = config['user']
self.password = config['password']
self.host = config['host']
self.dbtableprefix = config['dbtableprefix']
# Этот метод запускается только, если запускать
# данный файл, а не подключать его.
if __name__ == "__main__":
x = ProjectConfig()
print(x.dbfilepath)
+45
View File
@@ -0,0 +1,45 @@
# Таблица с категориями и действия с ними
from dbtable import *
class CathTable(DbTable):
def table_name(self):
return self.dbconn.prefix + "cath"
def columns(self):
return {"id": ["serial", "PRIMARY KEY"],
"cath_name": ["varchar(32)", "NOT NULL"]}
def table_constraints(self):
return ['CONSTRAINT "Name" UNIQUE (cath_name)']
def delete(self, val):
# sql = "DELETE FROM " + self.table_name()
# sql += " WHERE cath_name"
# sql += "=" + "'" + "".join(val) + "';"
param_sql = "DELETE FROM cath WHERE cath_name = %s;"
cur = self.dbconn.conn.cursor()
value = "".join(val)
cur.execute(param_sql, (value,))
self.dbconn.conn.commit()
def find_by_name(self, name):
cur = self.dbconn.conn.cursor()
param_query = "SELECT id FROM cath WHERE cath_name = %s;"
# sql_sel = "SELECT id FROM " + self.table_name()
# sql_sel += " WHERE cath_name = " + "'" + name + "'" + ";"
cur.execute(param_query, (name,))
ret = cur.fetchone()
return list(ret)[0]
# sql = "SELECT * FROM " + self.table_name()
# sql += " ORDER BY "
# sql += ", ".join(self.primary_key())
# sql += " LIMIT 1 OFFSET %(offset)s"
# cur.execute(sql, {"offset": num - 1})
# print(cur)
def example_insert(self):
self.insert_one(["Завтрак"])
self.insert_one(["Обед"])
self.insert_one(["Ужин"])
return
+99
View File
@@ -0,0 +1,99 @@
# Таблица с блюдами и действия с ними
from dbtable import *
class DishTable(DbTable):
def table_name(self):
return self.dbconn.prefix + "dish"
def columns(self):
return {"id": ["serial", "PRIMARY KEY"],
"cath_id": ["integer", "REFERENCES cath(id) ON DELETE CASCADE", "NOT NULL"],
"dish_name": ["varchar(32)", "NOT NULL"],
"cook_time": ["interval"],
"manual": ["text"]}
def table_constraints(self):
return ['CONSTRAINT "Name Dish" UNIQUE (dish_name)']
def find_by_position(self, num):
sql = "SELECT * FROM " + self.table_name()
sql += " ORDER BY "
sql += ", ".join(self.primary_key())
sql += " LIMIT 1 OFFSET %(offset)s"
cur = self.dbconn.conn.cursor()
cur.execute(sql, {"offset": num - 1})
return cur.fetchone()
def all_by_cath_id(self, cath_id):
sql = "SELECT * FROM " + self.table_name()
sql += " WHERE cath_id = " + str(cath_id)
sql += " ORDER BY "
sql += ", ".join(self.primary_key())
cur = self.dbconn.conn.cursor()
cur.execute(sql)
return cur.fetchall()
def delete(self, val):
sql = "DELETE FROM dish"
sql += " WHERE dish_name"
sql += "=" + "'" + val + "';"
cur = self.dbconn.conn.cursor()
cur.execute(sql)
self.dbconn.conn.commit()
def check_by_name(self, value):
sql = f"SELECT * FROM {self.table_name()} WHERE dish_name='{value}'"
cur = self.dbconn.conn.cursor()
cur.execute(sql)
result = cur.fetchone()
cur.close()
if result:
return True
else:
return False
def insert_dishone(self, cath_id):
ins_name = input('Введите название добавляемого блюда (1 - для отмены): ')
while (ins_name.strip() == '')or(len(ins_name.strip()) > 32)\
or(DishTable().check_by_name(ins_name)):
if(ins_name.strip() == ''):
ins_name = input("Пустая строка. Повторите ввод! Укажите название удаляемого блюда (0 - отмена): ")
if ins_name == "0":
return "1"
elif(len(ins_name.strip()) > 32):
ins_name = input("Слишком длинная строка. Повторите ввод!\
Укажите название удаляемого блюда (0 - отмена): ")
if ins_name == "0":
return "1"
else:
print('Такое блюдо уже существует')
ins_name = input("Повторите ввод! Укажите название блюда (0 - отмена): ")
if ins_name == "0":
return "1"
ins_time = input(f'Процесс добавления блюда: {ins_name}\
\nВведите время приготовления в формате 10 minutes/1 hour 5 minutes (1 - для отмены): ')
if ins_time == "0":
return "1"
ins_manual = input(f'Процесс добавления блюда: {ins_name}\
\nВведите краткую инструкцию приготовления блюда (1 - для отмены): ')
if ins_time == "0":
return "1"
insert = [cath_id, ins_time, ins_name, ins_manual]
DishTable().insert_one(insert)
def example_insert(self):
self.insert_one([1, "10 minutes", "Яичница", "Взбить яйца, нарезать помидоры, обжарить их вместе."])
self.insert_one([1, "20 minutes", "Блины", "Замесить тесто, да пожарить. ©️ Никита Сергеич"])
self.insert_one([2, "2 hours", "Хлэб", "Почти, как блины, но не совсем."])
self.insert_one([3, "40 minutes", "Паста", "Почувствуй себя итальянцем"])
return
+23
View File
@@ -0,0 +1,23 @@
# Таблица персоны и особые действия с ней
from dbtable import *
class PeopleTable(DbTable):
def table_name(self):
return self.dbconn.prefix + "people"
def columns(self):
return {"id": ["serial", "PRIMARY KEY"],
"last_name": ["varchar(32)", "NOT NULL"],
"first_name": ["varchar(32)", "NOT NULL"],
"second_name": ["varchar(32)"]}
def find_by_position(self, num):
sql = "SELECT * FROM " + self.table_name()
sql += " ORDER BY "
sql += ", ".join(self.primary_key())
sql += " LIMIT 1 OFFSET %(offset)s"
cur = self.dbconn.conn.cursor()
cur.execute(sql, {"offset": num - 1})
return cur.fetchone()
+27
View File
@@ -0,0 +1,27 @@
# Таблица Телефоны и особые действия с ней.
from dbtable import *
class PhonesTable(DbTable):
def table_name(self):
return self.dbconn.prefix + "phones"
def columns(self):
return {"person_id": ["integer", "REFERENCES people(id)"],
"phone": ["varchar(12)", "NOT NULL"]}
def primary_key(self):
return ['person_id', 'phone']
def table_constraints(self):
return ["PRIMARY KEY(person_id, phone)"]
def all_by_person_id(self, pid):
sql = "SELECT * FROM " + self.table_name()
sql += " WHERE person_id = %s"
sql += " ORDER BY "
sql += ", ".join(self.primary_key())
cur = self.dbconn.conn.cursor()
cur.execute(sql, str(pid))
return cur.fetchall()
+20
View File
@@ -0,0 +1,20 @@
show_main_menu_txt = """Привутствуем в меню, выберите действие:
1 - просмотр категорий;
2 - сброс и инициализация БД;
9 - выход;"""
show_cath_1txt = """Просмотр списка категорий!
\tНазвание\n-------------------------------------------------------------------------------------"""
show_cath_2txt = """-------------------------------------------------------------------------------------\nДальнейшие операции:
0 - возврат в главное меню;
3 - добавление новой категории;
4 - удаление категории;
5 - просмотр блюд в категории;
9 - выход."""
show_dish_in_cath_txt = """-------------------------------------------------------------------------------------\nДальнейшие операции:
0 - возврат в главное меню;
1 - возврат в просмотр категорий;
6 - добавление нового блюда;
7 - удаление блюда;
9 - выход."""