From ca4a98a675f16a195d34d0c35164fa8206119e4c Mon Sep 17 00:00:00 2001 From: Wudext <116914558+Wudext@users.noreply.github.com> Date: Sat, 5 Nov 2022 21:30:59 +0300 Subject: [PATCH 01/36] Add files via upload --- buttons.json | 75 ++++++++++++++++++++++++++++++++++++++++++++ buttons.sqlite | Bin 0 -> 12288 bytes create_database.py | 76 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 buttons.json create mode 100644 buttons.sqlite create mode 100644 create_database.py diff --git a/buttons.json b/buttons.json new file mode 100644 index 0000000..ce43b52 --- /dev/null +++ b/buttons.json @@ -0,0 +1,75 @@ +[ + { + "name": "Полезное", + "type": "grid3", + "items": [ + { + "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/printer.svg"}, + "text": "Бесплатный принтер", + "path": "https://printer.ui.profcomff.com" + }, + { + "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/fifth_floor.svg"}, + "text": "Схема этажей", + "path": "https://cdn.profcomff.com/app/map/" + } + ] + }, + { + "name": "Сервисы", + "type": "grid3", + "items": [ + { + "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/write_us.svg"}, + "text": "Написать в профком", + "path": "https://vk.me/profcomff" + }, + { + "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/study_office.svg"}, + "text": "Учебная часть", + "path": "https://phys.msu.ru/rus/students/obshaja_infa.php#:~:text=%D0%98%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%86%D0%B8%D1%8F%20%D1%83%D1%87%D0%B5%D0%B1%D0%BD%D0%BE%D0%B3%D0%BE%20%D0%BE%D1%82%D0%B4%D0%B5%D0%BB%D0%B0" + }, + { + "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/courses.svg"}, + "text": "МФК", + "path": "https://lk.msu.ru/course" + }, + { + "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/report.svg"}, + "text": "Жалоба", + "path": "https://vk.com/app5619682_-24234717" + }, + { + "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/money_help.svg"}, + "text": "Материальная помощь", + "path": "https://vk.com/profcomff?w=page-24234717_51953473" + }, + { + "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/join.svg"}, + "text": "Вступить в профсоюз", + "path": "https://lk.msuprof.com/" + } + ] + }, + { + "name": "Информация", + "type": "list", + "items": [ + { + "icon": {"src": "install_mobile"}, + "text": "Установить приложение", + "path": "https://pages.profcomff.com/installation" + }, + { + "icon": {"src": "feedback"}, + "text": "Обратная связь", + "path": "https://forms.yandex.ru/u/630f979143537dde00621b0b/" + }, + { + "icon": {"src": "info"}, + "text": "О приложении", + "path": "https://pages.profcomff.com/about" + } + ] + } +] \ No newline at end of file diff --git a/buttons.sqlite b/buttons.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..5a3cfcf0035974a05a5101c9eea352ab125d30b7 GIT binary patch literal 12288 zcmeI2&u|n}aVbLJMhirB9kMYL)FR72c9R8r*V!z~Rkbz=>o3hrU^C5h^N~Q#rhmJTrbX^X9$J zo6A@B>`dMcOkufRqa26>>|@3`J1PWYEZYCz5Vi>RvHn~QeB1oLrcpLAeQ6X|Bgw}s zwTK-xB0vO)01+SpM1Tko0U|&IhyW2F0z(pLpNNd4_wMCOb3wUMH!H0ma2>xtL?;bB zSJXu@_eEY8eWMsp#O<1xDirnOx*-bFMNv4F&+ik7xKnPJ;&jfKJdrch_i8yX=Ui{0 z^k%;@+f~=uzU`zj^;OO|E56jvigCM^QG1R}8~W7og0hWobCVHOQ;U28x z${JeR^7@=-*ADFAF}6Pqe?SkmU>!D~i?j7`N$LNcyPmPs{Zu~rck*U(CYc!hb98Dn zvFjz8sSyDpKm>>Y5g-CYfCvx)B0vN_2m+t()Yu`u8}15tj*4d!#Z3?XLV?r8`DWC1 zEH{(T*d!l=yReEoI&uluI&wwGHmu1N*pSOn%QS11a`nRDxW-C60}oI(EkP&LNJ>O4 z$>n$XUq}CbP_EZY4Yy*~&2M&S>=bw5S9M%of#-vxYp+lDb>_V5;l z`Vf8}-a*xEdA{i@4`*T;JHi*S5ZADltAmPN!`v0x_ejtWTD66eYgu;HRG!a8H8#U9 z!W~Re)u*j+s`9?65j<5ShmZTBhku_hwZg!Q5qyvFOK?yAfPtel7xL%uDXBO4Way$> zwiV2mth(!ZD(G*y#%AL!xPh|1jbU$vZ=fr$!;`_g?BXr>D2BeF>Df+ThTi`Hp2{e> literal 0 HcmV?d00001 diff --git a/create_database.py b/create_database.py new file mode 100644 index 0000000..3c853ef --- /dev/null +++ b/create_database.py @@ -0,0 +1,76 @@ +# This Python file uses the following encoding: utf-8 + +from faker import Faker + +from models.database import create_db, Session +from models.buttons import Button +from models.category import Category +import json + + +def create_database(load_fake_data: bool = True): + create_db() + + if load_fake_data: + _load_fake_data(Session()) + + +def _load_fake_data(session: Session): + with open('buttons.json', 'r', encoding="utf8") as f: + data = json.load(f) + + holder1 = Category(id=1, name=data[0]['name'], type=data[0]['type']) # Категория - Полезное + holder2 = Category(id=2, name=data[1]['name'], type=data[1]['type']) # Категория - Сервисы + holder3 = Category(id=3, name=data[2]['name'], type=data[2]['type']) # Категория - Информация + + + # Категория - Полезное + button1 = Button(id=1, name=data[0]['items'][0]['text'], icon=data[0]['items'][0]['icon'], # Полезное + category_id=holder1.give_id()) + button2 = Button(id=2, name=data[0]['items'][1]['text'], icon=data[0]['items'][1]['icon'], # Этажи + category_id=holder1.give_id()) + + # Категория - Сервисы + button3 = Button(id=3, name=data[1]['items'][0]['text'], icon=data[1]['items'][0]['icon'], # Написать + category_id=holder2.give_id()) + button4 = Button(id=4, name=data[1]['items'][1]['text'], icon=data[1]['items'][1]['icon'], # Учебная часть + category_id=holder2.give_id()) + button5 = Button(id=5, name=data[1]['items'][2]['text'], icon=data[1]['items'][2]['icon'], # МФК + category_id=holder2.give_id()) + button6 = Button(id=6, name=data[1]['items'][3]['text'], icon=data[1]['items'][3]['icon'], # Жалоба + category_id=holder2.give_id()) + button7 = Button(id=7, name=data[1]['items'][4]['text'], icon=data[1]['items'][4]['icon'], # Мат. помощь + category_id=holder2.give_id()) + button8 = Button(id=8, name=data[1]['items'][5]['text'], icon=data[1]['items'][5]['icon'], # Профсоюз + category_id=holder2.give_id()) + + # Категория - Информация + button9 = Button(id=9, name=data[2]['items'][0]['text'], icon=data[2]['items'][0]['icon'], # Приложение + category_id=holder3.give_id()) + button10 = Button(id=10, name=data[2]['items'][1]['text'], icon=data[2]['items'][1]['icon'], # Обратная связь + category_id=holder3.give_id()) + button11 = Button(id=11, name=data[2]['items'][2]['text'], icon=data[2]['items'][2]['icon'], # О приложении + category_id=holder3.give_id()) + + print(button1) + + session.add(holder1) + session.add(holder2) + session.add(holder3) + session.add(button1) + session.add(button2) + session.add(button3) + session.add(button4) + session.add(button5) + session.add(button6) + session.add(button7) + session.add(button8) + session.add(button9) + session.add(button10) + session.add(button11) + + faker = Faker('ru_RU') + session.commit() + + +create_database(True) From b5088fbca85ab5e23e4a5b25a4c58dc9143ee648 Mon Sep 17 00:00:00 2001 From: Wudext <116914558+Wudext@users.noreply.github.com> Date: Sat, 5 Nov 2022 21:31:46 +0300 Subject: [PATCH 02/36] Add files via upload --- models/__init__.py | 0 models/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 172 bytes models/__pycache__/buttons.cpython-310.pyc | Bin 0 -> 1175 bytes models/__pycache__/category.cpython-310.pyc | Bin 0 -> 913 bytes models/__pycache__/database.cpython-310.pyc | Bin 0 -> 896 bytes models/buttons.py | 24 ++++++++++++++++++++ models/category.py | 17 ++++++++++++++ models/database.py | 13 +++++++++++ 8 files changed, 54 insertions(+) create mode 100644 models/__init__.py create mode 100644 models/__pycache__/__init__.cpython-310.pyc create mode 100644 models/__pycache__/buttons.cpython-310.pyc create mode 100644 models/__pycache__/category.cpython-310.pyc create mode 100644 models/__pycache__/database.cpython-310.pyc create mode 100644 models/buttons.py create mode 100644 models/category.py create mode 100644 models/database.py diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/models/__pycache__/__init__.cpython-310.pyc b/models/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..75904fc6a04ec653027ecd8f9d71d79c17f1d8e0 GIT binary patch literal 172 zcmd1j<>g`kf`;a_WDxxrL?8o3AjbiSi&=m~3PUi1CZpdpDX1*T$j^gv4P$ciQ&Mw^ hW8&j8^D;}~iJ?J?cAXQ&W=f(LLt7C)Sp1sJ66KM>b(} z)byGor=+{j{TbPk?S0~(lO5nbUD=C+qR4>s!+Ol`1FKeFM@gVdv8BqLRv17wnJ(ud zSuyB54H8xTGYd3DVU~ssV0+qd&Y&+r&pm)49TGu?43G{jKsMx(UM4WJBmpXHrLuAz zcDZgrxNakK5T*e@U7o9W?SH-xM9)IVuVsLx`QC$E>AYv&nJ?zs=$U8Uz~pH3thZW* z8-Y$%b+)O(BJcIfVk1jWmN$AyCRLpGPE?rdmz5(#ltzUR$Rfu#u_=DjbZ2HP+T3YG zNWpvBFl~&kQ@;ii38`vW{)aRBH|G;d zjH6b%v$zFIM2N~20(2e3IByHFJqY3xPe+I%co?hkQlnDGc1^5lKzZRYuCvQDQ(VBNhujDAu=eqpn|ENJe- ztq}wfG$AQvG&Pu!n#`oQXCzi?GrO+M#7SM|Qu2@pOW0>b*wQ^UnJ1hB(szHuDWq>! z&M-^n>9leVaurXAuR2WR&Y*R^*dxx@_+RX(nW%gBx3e8F=Je$(iE311D<(pi577ZXy`m zO#gF?u4~5*6zCT`(h=lj#6tP7SL7RRT}{r)hz^!a1BtGaskkT#-T?n z>Kh(~%ka&5HiA#0rH_*^i6$~F8m^iF39HS2qd}%pW#i^n6ZAlYpc1}Ug#`6!YwWd{ VIvQ`Uv3Uww-b|i7IU7(XVjpOzU z1_F+VGbe<^Pw1|>a^eSY;=*&*vEse#Jw43GO!e$$$ zP9Si^afWhCF}AsrIk`*SI(IXYd(^9QlKIr<9&HHBebG2W@10{z1L2B*H%>8a3hx-v zkO#c^7SRG~2VUYpL+jZy>-DC@Ucvmt;`HHH^gQ27?J+Sc`!x~&ba gTMq7mNz+W+vx@5*5F!-e2nU!rt8VD{PT)lDUtLw`rvLx| literal 0 HcmV?d00001 diff --git a/models/buttons.py b/models/buttons.py new file mode 100644 index 0000000..3b0d916 --- /dev/null +++ b/models/buttons.py @@ -0,0 +1,24 @@ +from sqlalchemy import Column, Integer, String, ForeignKey, PickleType +from models.database import Base + + +class Button(Base): + __tablename__ = 'buttons' + + id = Column(Integer, primary_key=True) + name = Column(String) + category_id = Column(Integer, ForeignKey('category.id')) + icon = Column(String) + + def __init__(self, id: int, name: str, category_id: int, icon: dict): + self.id = id + self.name = name + self.category_id = category_id + self.icon = icon["src"] + + def __repr__(self): + info: str = f'Кнопка {self.name} (id: {self.id}):\n'\ + f'Находится в категории {self.category_id}\n' \ + f'Иконка: {self.icon}' + + return info diff --git a/models/category.py b/models/category.py new file mode 100644 index 0000000..92fb7f2 --- /dev/null +++ b/models/category.py @@ -0,0 +1,17 @@ +from sqlalchemy import Column, String, Integer +from sqlalchemy.orm import relationship +from models.database import Base + + +class Category(Base): + __tablename__ = 'category' + + id = Column(Integer, primary_key=True) + name = Column(String) + type = Column(String) + + def __repr__(self): + return f'Категория {self.name}. Тип: {self.type}' + + def give_id(self): # Небольшой костыль :) + return self.id diff --git a/models/database.py b/models/database.py new file mode 100644 index 0000000..603773d --- /dev/null +++ b/models/database.py @@ -0,0 +1,13 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from sqlalchemy.ext.declarative import declarative_base +import psycopg2 + +DATABASE_NAME = 'buttons.db' +engine = create_engine(f'postgresql+psycopg2://postgres:123@localhost:5432/{DATABASE_NAME}') +Session = sessionmaker(bind=engine) + +Base = declarative_base() + +def create_db(): + Base.metadata.create_all(engine) From bf5a4b2ff973dfc1011cfbfaf7925d676b0ca911 Mon Sep 17 00:00:00 2001 From: Wudext Date: Mon, 14 Nov 2022 18:29:24 +0300 Subject: [PATCH 03/36] Update --- .idea/.gitignore | 3 + .idea/desktop.ini | 2 + .idea/inspectionProfiles/desktop.ini | 2 + .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 4 + .idea/modules.xml | 8 ++ .idea/services-api.iml | 8 ++ .idea/vcs.xml | 6 + buttons.sqlite | Bin 12288 -> 12288 bytes create_database.py | 48 ++++---- desktop.ini | 2 + methods.py | 115 ++++++++++++++++++ models/CRUD.py | 47 +++++++ models/__pycache__/__init__.cpython-310.pyc | Bin 172 -> 196 bytes models/__pycache__/buttons.cpython-310.pyc | Bin 1175 -> 1187 bytes models/__pycache__/category.cpython-310.pyc | Bin 913 -> 0 bytes models/__pycache__/database.cpython-310.pyc | Bin 896 -> 967 bytes models/buttons.py | 6 +- models/categories.py | 21 ++++ models/category.py | 17 --- models/database.py | 20 ++- models/desktop.ini | 2 + 22 files changed, 273 insertions(+), 44 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/desktop.ini create mode 100644 .idea/inspectionProfiles/desktop.ini create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/services-api.iml create mode 100644 .idea/vcs.xml create mode 100644 desktop.ini create mode 100644 methods.py create mode 100644 models/CRUD.py delete mode 100644 models/__pycache__/category.cpython-310.pyc create mode 100644 models/categories.py delete mode 100644 models/category.py create mode 100644 models/desktop.ini diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/desktop.ini b/.idea/desktop.ini new file mode 100644 index 0000000..216df0d --- /dev/null +++ b/.idea/desktop.ini @@ -0,0 +1,2 @@ +[.ShellClassInfo] +IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 diff --git a/.idea/inspectionProfiles/desktop.ini b/.idea/inspectionProfiles/desktop.ini new file mode 100644 index 0000000..216df0d --- /dev/null +++ b/.idea/inspectionProfiles/desktop.ini @@ -0,0 +1,2 @@ +[.ShellClassInfo] +IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..dc9ea49 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..46ca921 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/services-api.iml b/.idea/services-api.iml new file mode 100644 index 0000000..8437fe6 --- /dev/null +++ b/.idea/services-api.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/buttons.sqlite b/buttons.sqlite index 5a3cfcf0035974a05a5101c9eea352ab125d30b7..b0b957870196a7ae3fea44233f75bf4406cab5c3 100644 GIT binary patch delta 239 zcmZojXh@hK&B!)U##xY!LC?RImw|zSiElCke>&gfjfKj5T#bQD?BdeWj7^o3_wntK zNKPzCP0uf?jL%HbVc*4u3wthXxY%}K$Ho4f%;FMB zCZN2gIMaoN7q$ZBHUY&OFLtLFWu_QQF|smnPJSsbEyKgJfq{P!zYpJAzR7%nd|bTC jcs+T!c<%G;;n}dUZ~_kx7e6b5C?hY(LYB>YbX=GL=0i*j delta 162 zcmV;T0A2rpV1Qtd8vz56977QU04`5`4FCWD0uHnQ4}T7{vk?$P4hn%t0tXix8v%l6 zlbsH{4ryd53Mqd92NxY30fAAIaSuk5;0_Rz-VY`U0}rkM59JTc53aKj5Qqv@#i>QbF&E}v*m`02#U6zVbAXJ!7kgp? fDw8u3i*f^s^0QKtONwI(DoZl*^PpVAiE}gn4Im-T diff --git a/models/__pycache__/buttons.cpython-310.pyc b/models/__pycache__/buttons.cpython-310.pyc index 59088a4609e30091046a140d601e7275cefe9179..93d88599d2dbed1207feb258d1290f7ba597b757 100644 GIT binary patch delta 520 zcmZusJxjwt7`{ucX{wF&qk@Wpi$f_E$5O<_K^H|(7YT%zyV^kd;awCuX|;c#WN;Bg z6v3^agNphK94>1%M}L63cc~v8yy4;HecpRM&Qez?qiz@jKs@K&b2d803@mo?lX(a* zz>1fsYC9SPD*&q)wE;#%J5_g*II#(G+K2p>Lmf8PxxN|8I<`;t>a?s)fm4BPxkES?QhC>**DVe@vw4HlGXm^BA2>lQPQB6H6CoL^eRjm2_5uEY}B8nkEHE z0VYWuAedHkI0SV#h0=Tu0wNtK|8MpvUuwT_9$3fT7TU0W2@e&qTr XV#i*EMR9-NktHb2sxR{mh3* delta 519 zcmZutJxjwt7{1F#leS5d_oWy_N(609n`~mLXC87@A@NoA&@B2LObC3Dkyzc9I0kG40aO`5Nufk@$v9#&Q zT^Rxdkl^M#rK>=&3y_GUYk(xIT!~#3$%mkp`{1KBIri5zTIdYya~B^)BOKp66^gbM z;!~eQ^`s5^mqpe{G!8n4QaVSa0J{$PhkFL3%vwHM40amEaezr+Oz)>J)7RN#ZF5CP;L$AQB_8G%~;a2vKn`45=7|gi2^&hos8sMGid9AK=nN zWxxdj?ydp}YJv(&P=(8amP^@#TAmhy3)gb(KK3FaBzsb&O>(TC&SmlH$O^5V#ePU( b)pY8QPimXxXtMsXi?<DGc1^5lKzZRYuCvQDQ(VBNhujDAu=eqpn|ENJe- ztq}wfG$AQvG&Pu!n#`oQXCzi?GrO+M#7SM|Qu2@pOW0>b*wQ^UnJ1hB(szHuDWq>! z&M-^n>9leVaurXAuR2WR&Y*R^*dxx@_+RX(nW%gBx3e8F=Je$(iE311D<(pi577ZXy`m zO#gF?u4~5*6zCT`(h=lj#6tP7SL7RRT}{r)hz^!a1BtGaskkT#-T?n z>Kh(~%ka&5HiA#0rH_*^i6$~F8m^iF39HS2qd}%pW#i^n6ZAlYpc1}Ug#`6!YwWd{ VIvQ`Uv3Ufgc5Zzrnj_cYc5>gL{14nWog%&OWp{AurBBbykS(YNlyQGG79Co*bR;_4h zul)x|wBpoTB{-A{_zlk0S5Eu@?##Mr34)cp`{vDhXWqz*+qdv zZ927wf@$i|X*gZ#QV&j#njKX4e?lZw$AuRQ7Ae+cgMKn#K)8}gnPh`BddURXHOgWh ziAW`DtlNtuYj60yRi(0lY{*wUQEcPpFq5h;7?{o3*>j_LYi@plXK}<=z}>uadwy>A zk;tCMS$c#EvzH8Ltdlrj7=R6*CgpY5c9255XbWvPo9HRR=o#8X2lx;WMV$-$8Xe(w zofOs1S>8n@=9$z=2<;sqF%2*M(sf;GhEF6DGRzN0@ABi(PME*Xzvo}B<)8DD(N2CE z<{$HK`H2P}M%&=}lpl}bXfHnn;%f*GtR*p%H=S?!ww-b|i7IU7(XVjpOzU z1_F+VGbe<^Pw1|>a^eSY;=*&*vEse#Jw43GO!e$$$ zP9Si^afWhCF}AsrIk`*SI(IXYd(^9QlKIr<9&HHBebG2W@10{z1L2B*H%>8a3hx-v zkO#c^7SRG~2VUYpL+jZy>-DC@Ucvmt;`HHH^gQ27?J+Sc`!x~&ba gTMq7mNz+W+vx@5*5F!-e2nU!rt8VD{PT)lDUtLw`rvLx| diff --git a/models/buttons.py b/models/buttons.py index 3b0d916..4f0474d 100644 --- a/models/buttons.py +++ b/models/buttons.py @@ -1,4 +1,4 @@ -from sqlalchemy import Column, Integer, String, ForeignKey, PickleType +from sqlalchemy import Column, Integer, String, ForeignKey from models.database import Base @@ -7,9 +7,11 @@ class Button(Base): id = Column(Integer, primary_key=True) name = Column(String) - category_id = Column(Integer, ForeignKey('category.id')) + category_id = Column(Integer, ForeignKey("category.category_id")) icon = Column(String) + # category = relationship("Category", back_populates="category") + def __init__(self, id: int, name: str, category_id: int, icon: dict): self.id = id self.name = name diff --git a/models/categories.py b/models/categories.py new file mode 100644 index 0000000..61e45cb --- /dev/null +++ b/models/categories.py @@ -0,0 +1,21 @@ +from sqlalchemy import Column, String, Integer +from sqlalchemy.orm import relationship +from models.database import Base + + +class Category(Base): + __tablename__ = 'category' + + id = Column('id', Integer, primary_key=True) + category_id = Column('category_id', Integer, unique=True) + name = Column('name', String) + type = Column('type', String) + + # category = relationship("Button", back_populates="category") + + def __repr__(self): + return f'Категория {self.name}. Тип: {self.type}\n' \ + f'ID: {self.category_id}' + + def give_id(self): # Небольшой костыль :) + return self.category_id diff --git a/models/category.py b/models/category.py deleted file mode 100644 index 92fb7f2..0000000 --- a/models/category.py +++ /dev/null @@ -1,17 +0,0 @@ -from sqlalchemy import Column, String, Integer -from sqlalchemy.orm import relationship -from models.database import Base - - -class Category(Base): - __tablename__ = 'category' - - id = Column(Integer, primary_key=True) - name = Column(String) - type = Column(String) - - def __repr__(self): - return f'Категория {self.name}. Тип: {self.type}' - - def give_id(self): # Небольшой костыль :) - return self.id diff --git a/models/database.py b/models/database.py index 603773d..1121b1c 100644 --- a/models/database.py +++ b/models/database.py @@ -3,11 +3,27 @@ from sqlalchemy.ext.declarative import declarative_base import psycopg2 -DATABASE_NAME = 'buttons.db' -engine = create_engine(f'postgresql+psycopg2://postgres:123@localhost:5432/{DATABASE_NAME}') +DATABASE_NAME = 'buttons.sqlite' +engine = create_engine(f'postgresql://postgres:123@localhost:5432/Profcom') +# engine = create_engine(f'sqlite:///{DATABASE_NAME}') Session = sessionmaker(bind=engine) Base = declarative_base() + +def get_db(): + db = Session() + try: + yield db + finally: + db.close() + + +def create_session(): + Session = sessionmaker(bind=engine) + + return Session + + def create_db(): Base.metadata.create_all(engine) diff --git a/models/desktop.ini b/models/desktop.ini new file mode 100644 index 0000000..216df0d --- /dev/null +++ b/models/desktop.ini @@ -0,0 +1,2 @@ +[.ShellClassInfo] +IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 From bc8d686abfe2554fb0accb37ca2fffe1e5a46c43 Mon Sep 17 00:00:00 2001 From: Wudext Date: Mon, 14 Nov 2022 19:19:14 +0300 Subject: [PATCH 04/36] s --- desktop.ini | 2 -- models/desktop.ini | 2 -- 2 files changed, 4 deletions(-) delete mode 100644 desktop.ini delete mode 100644 models/desktop.ini diff --git a/desktop.ini b/desktop.ini deleted file mode 100644 index 216df0d..0000000 --- a/desktop.ini +++ /dev/null @@ -1,2 +0,0 @@ -[.ShellClassInfo] -IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 diff --git a/models/desktop.ini b/models/desktop.ini deleted file mode 100644 index 216df0d..0000000 --- a/models/desktop.ini +++ /dev/null @@ -1,2 +0,0 @@ -[.ShellClassInfo] -IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 From d0106670c9507f62c53d055a1d8693c99109cf58 Mon Sep 17 00:00:00 2001 From: Wudext Date: Thu, 17 Nov 2022 14:18:38 +0300 Subject: [PATCH 05/36] FastApi --- create_database.py | 1 - desktop.ini | 2 + main_fastapi.py | 91 ++++++++++++++++++++ methods.py | 77 ++++++----------- models/CRUD.py | 2 +- models/__pycache__/buttons.cpython-310.pyc | Bin 1187 -> 1229 bytes models/__pycache__/database.cpython-310.pyc | Bin 967 -> 971 bytes models/buttons.py | 4 +- models/categories.py | 1 - models/database.py | 10 +-- models/desktop.ini | 2 + 11 files changed, 130 insertions(+), 60 deletions(-) create mode 100644 desktop.ini create mode 100644 main_fastapi.py create mode 100644 models/desktop.ini diff --git a/create_database.py b/create_database.py index a37b656..b95bf26 100644 --- a/create_database.py +++ b/create_database.py @@ -1,7 +1,6 @@ # This Python file uses the following encoding: utf-8 from faker import Faker - from models.database import create_db, Session, get_db from models.buttons import Button from models.categories import Category diff --git a/desktop.ini b/desktop.ini new file mode 100644 index 0000000..216df0d --- /dev/null +++ b/desktop.ini @@ -0,0 +1,2 @@ +[.ShellClassInfo] +IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 diff --git a/main_fastapi.py b/main_fastapi.py new file mode 100644 index 0000000..862ae00 --- /dev/null +++ b/main_fastapi.py @@ -0,0 +1,91 @@ +from sqlalchemy.orm import Session +import methods +from models import CRUD, database +from models.database import engine +from fastapi import Depends, FastAPI, HTTPException + +database.Base.metadata.create_all(bind=engine) +app = FastAPI() + + +def get_db(): + db = Session(bind=engine) + try: + yield db + finally: + db.close() + + +@app.post("/buttons/", response_model=CRUD.Button) +def create_button(button: CRUD.ButtonCreate, db: Session = Depends(get_db)): + db_button = methods.get_button(db, button_id=button.id) + if db_button: + raise HTTPException(status_code=400, detail='Button already exist') + return methods.create_button(db=db, button=button) + + +@app.get("/buttons/", response_model=list[CRUD.Button]) +def get_buttons(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): + return methods.get_buttons(db, skip=skip, limit=limit) + + +@app.get("/buttons/{button_id}", response_model=CRUD.Button) +def get_button(button_id: int, db: Session = Depends(get_db)): + db_button = methods.get_button(db, button_id) + if db_button is None: + raise HTTPException(status_code=404, detail="Button does not exist") + return db_button + + +@app.post("/buttons/{button_id}") +def remove_button(button_id: int, db: Session = Depends(get_db)): + db_button = methods.get_button(db, button_id) + if db_button is None: + raise HTTPException(status_code=404, detail="Button does not exist") + return methods.delete_button(db=db, button_id=button_id) + + +@app.post("/buttons/{button_id}/update") +def update_button(button: CRUD.ButtonCreate, db: Session = Depends(get_db)): + db_old_button = get_button(button_id=button.id, db=db) + if db_old_button is None: + raise HTTPException(status_code=404, detail="Button does not exist") + return methods.update_button(db=db, button_id=button.id, button=button) + + +@app.post("/categories/", response_model=CRUD.Category) +def create_category(category: CRUD.CategoryCreate, db: Session = Depends(get_db)): + db_category = methods.get_category(category_id=category.id, db=db) + if db_category: + raise HTTPException(status_code=400, detail="Category already exist") + return methods.create_category(db=db, category=category) + + +@app.get("/categories/", response_model=list[CRUD.Category]) +def get_categories(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): + return methods.get_categories(db, skip=skip, limit=limit) + + +@app.get("/categories/{category_id}", response_model=CRUD.Category) +def get_category(category_id: int, db: Session = Depends(get_db)): + db_category = methods.get_category(category_id=category_id, db=db) + if db_category is None: + raise HTTPException(status_code=404, detail="Category does not exist") + return db_category + + +@app.post("/categories/{category_id}") +def remove_category(category_id: int, db: Session = Depends(get_db)): + db_category = methods.get_category(db, category_id=category_id) + if db_category is None: + raise HTTPException(status_code=404, detail="Category does not exist") + return methods.delete_category(db=db, category_id=category_id) + + +@app.post("/categories/{category_id}/update") +def update_category(category: CRUD.CategoryCreate, db: Session = Depends(get_db)): + db_old_category = methods.get_category(db=db, category_id=category.id) + if db_old_category is None: + raise HTTPException(status_code=404, detail="Category does not exist") + else: + return methods.update_category(db=db, category_id=category.id, category=category) \ No newline at end of file diff --git a/methods.py b/methods.py index 0168e2b..69adf84 100644 --- a/methods.py +++ b/methods.py @@ -21,7 +21,7 @@ def get_categories(db: Session, skip: int, limit: int): def create_button(db: Session, button: CRUD.ButtonCreate): db_button = buttons.Button(id=button.id, category_id=button.category_id, - name=button.name, icon={"src": button.icon}) + name=button.name, icon=button.icon["src"]) try: db.add(db_button) db.commit() @@ -54,62 +54,39 @@ def delete_button(db: Session, button_id: int): def delete_category(db: Session, category_id: int): delete = db.query(categories.Category).filter(categories.Category.id == category_id).first() - try: - db.delete(delete) + d = db.query(buttons.Button).filter(buttons.Button.category_id == category_id).all() + for button in d: + db.delete(button) db.commit() - except: - raise ValueError('Object doesn`t exist') + db.delete(delete) + db.commit() db.close() -def update_button(db: Session, button_id: int, object: str, value: str): - button = db.query(buttons.Button).filter(buttons.Button.id == button_id).first() - if not button: - raise ValueError('Object doesn`t exist') - category_id = button.category_id - if object == 'category': - value = int(value) - delete_button(db=Session(bind=engine), button_id=button_id) - create_button(db=Session(bind=engine), button=CRUD.ButtonCreate( - id=button_id, category_id=value, name=button.name, icon=button.icon)) - elif object == 'name': - icon = button.icon - delete_button(db=Session(bind=engine), button_id=button_id) - create_button(db=Session(bind=engine), button=CRUD.ButtonCreate( - id=button_id, category_id=category_id, name=value, icon=icon)) - - elif object == 'icon': - name = button.name - delete_button(db=Session(bind=engine), button_id=button_id) - create_button(db=Session(bind=engine), button=CRUD.ButtonCreate( - id=button_id, category_id=category_id, name=name, icon=value)) - else: - raise ValueError('Invalid object') - - -# ТЕСТЫ CRUD РУЧЕК (вроде работает) - -# print(get_button(db=Session(bind=engine), button_id=7)) -# print(get_category(db=Session(bind=engine), category_id=2)) -# -# create_category(db=Session(bind=engine), -# category=CRUD.CategoryCreate( -# id=4, category_id=4, type='test', name='test')) -# -# print(get_category(db=Session(bind=engine), category_id=4)) -# print(get_buttons(db=Session(bind=engine), skip=0, limit=100)) -# print(get_categories(db=Session(bind=engine), skip=0, limit=100)) +def update_button(db: Session, button_id: int, button: CRUD.ButtonCreate): + old_button = db.query(buttons.Button).filter(buttons.Button.id == button_id).first() + new_button = buttons.Button(id=button.id, category_id=button.category_id, + name=button.name, icon=button.icon) + delete_button(db=db, button_id=old_button.id) + create_button(db=db, button=new_button) + + +def update_category(db: Session, category_id: int, category: CRUD.CategoryCreate): + old_category = db.query(buttons.Button).filter(buttons.Button.id == category_id).first() + new_category = categories.Category(id=category.id, category_id=category.category_id, + name=category.name, type=category.type) + delete_category(db=db, category_id=old_category.id) + create_category(db=db, category=new_category) + # create_button(db=Session(bind=engine), button=CRUD.ButtonCreate( -# id=15, category_id=4, name='test', icon='test')) +# id=15, category_id=4, name='test', icon={"src": 'test'})) # print(get_button(db=Session(bind=engine), button_id=15)) - -# delete_button(db=Session(bind=engine), button_id=15) +# update_button(db=Session(bind=engine), button_id=15, button=CRUD.ButtonCreate( +# id=15, category_id=4, name='test', icon={"src": 'lmao'})) # print(get_button(db=Session(bind=engine), button_id=15)) -# delete_category(db=Session(bind=engine), category_id=4) # print(get_category(db=Session(bind=engine), category_id=4)) -# create_button(db=Session(bind=engine), button=CRUD.ButtonCreate( -# id=15, category_id=3, name='test', icon='test')) -# update_button(db=Session(bind=engine), button_id=15, object='category', value='3') -# print(get_button(db=Session(bind=engine), button_id=15)) +# update_category(db=Session(bind=engine), category_id=4, category=CRUD.CategoryCreate( +# id=4, category_id=4, name='быбик', type='насрали')) +# print(get_category(db=Session(bind=engine), category_id=4)) diff --git a/models/CRUD.py b/models/CRUD.py index 721fa9a..7f39a3c 100644 --- a/models/CRUD.py +++ b/models/CRUD.py @@ -31,7 +31,7 @@ class ButtonBase(BaseModel): class ButtonCreate(ButtonBase): name: str - icon: str + icon: dict class ButtonDelete(ButtonBase): diff --git a/models/__pycache__/buttons.cpython-310.pyc b/models/__pycache__/buttons.cpython-310.pyc index 93d88599d2dbed1207feb258d1290f7ba597b757..e660eaaf0a09b847f0591d445e9dbd532f85603c 100644 GIT binary patch delta 334 zcmZ3?d6ttmpO=@50SFEVmL(TVMDPI#P1aj1#i==I7;=*zGIld^P0nT7D*)C3)(g=# z*@}6B6qwHfH;@%!>f|TPMyeb@J{u4h3jhfY1{OvhCJshEMh-?PW*)X8kgT63*JNuJ zMIFB4!komM&HCc*4#usse z2p$l@3nVmIZ?P1o=A>cBP5#2z&B!;ofoZP*SO-`yMB8Lv<_S_@J`3DHR)ndOzcU-D zvIF@bv0?!r!NI`7$iu|J$j8XRD8<6VRs@pu)8v@!$D%m7g+-8!4ahHIpDf5CE_sW? XCO1E&G$+-L5u_a?Bf-GKD8dW?lf5)1 diff --git a/models/__pycache__/database.cpython-310.pyc b/models/__pycache__/database.cpython-310.pyc index a97b143a361673083367e838bc528f5b7ed65bc3..11cd0f0218de68114beb3b08d02d622d92be5203 100644 GIT binary patch delta 399 zcmYjLF-rq67|oaSa_QZr?Ll1%4k`$Ts6$sNoK$oWskAOxYH4B>u06T7PU3Q|f=4zd z{Sp2K2M0$XzroeX#as*01m4Gc`SRX-cRn1JFyP81q=djm~y2mPjnE0m6&!x)X9Cp_J&MxUofy9OBe6 zLlZe3UXNu6Y01#E_^|+{F7UctHGGf8~J4sMQo4|g9m8gBUi^Ly|A?!8ar!(cvR*%ZFRyyrYcY#l_jy>q~Ik>Q2}p-+HQ zsYxn~7vQGMOj0~6s4$;XJSVauhi(Z=Fmxu;(OcuRHO_ZOVd#57DY$q0p&aiH{UGcq ziC8a}|De8CsUEw&({b;xU9as|E9Fb&-#Y#vHo?_%c-6OvAK@ncf)cP8NOdcN#UfD} z+EkZbukXnsD2k|N`CqYOPBtF!3ma2< dH1Ng9l?^}&R#2>z1r|_96AD?P6RJ^a=|Au!Soi<{ diff --git a/models/buttons.py b/models/buttons.py index 4f0474d..c11046e 100644 --- a/models/buttons.py +++ b/models/buttons.py @@ -1,4 +1,5 @@ from sqlalchemy import Column, Integer, String, ForeignKey +from sqlalchemy.orm import relationship from models.database import Base @@ -9,14 +10,13 @@ class Button(Base): name = Column(String) category_id = Column(Integer, ForeignKey("category.category_id")) icon = Column(String) - # category = relationship("Category", back_populates="category") def __init__(self, id: int, name: str, category_id: int, icon: dict): self.id = id self.name = name self.category_id = category_id - self.icon = icon["src"] + self.icon = icon def __repr__(self): info: str = f'Кнопка {self.name} (id: {self.id}):\n'\ diff --git a/models/categories.py b/models/categories.py index 61e45cb..96e386c 100644 --- a/models/categories.py +++ b/models/categories.py @@ -10,7 +10,6 @@ class Category(Base): category_id = Column('category_id', Integer, unique=True) name = Column('name', String) type = Column('type', String) - # category = relationship("Button", back_populates="category") def __repr__(self): diff --git a/models/database.py b/models/database.py index 1121b1c..ba1e6f1 100644 --- a/models/database.py +++ b/models/database.py @@ -1,13 +1,13 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from sqlalchemy.ext.declarative import declarative_base -import psycopg2 +from dotenv import load_dotenv +import os -DATABASE_NAME = 'buttons.sqlite' -engine = create_engine(f'postgresql://postgres:123@localhost:5432/Profcom') -# engine = create_engine(f'sqlite:///{DATABASE_NAME}') +load_dotenv() +path = os.getenv('SQL_DATABASE_PATH') +engine = create_engine(path) Session = sessionmaker(bind=engine) - Base = declarative_base() diff --git a/models/desktop.ini b/models/desktop.ini new file mode 100644 index 0000000..216df0d --- /dev/null +++ b/models/desktop.ini @@ -0,0 +1,2 @@ +[.ShellClassInfo] +IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 From a0753a8ae43be6b427fa921f29cbf7c3f5e3a460 Mon Sep 17 00:00:00 2001 From: Wudext Date: Thu, 17 Nov 2022 14:20:33 +0300 Subject: [PATCH 06/36] =?UTF-8?q?=D1=84=D1=8B=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- desktop.ini | 2 -- models/desktop.ini | 2 -- 2 files changed, 4 deletions(-) delete mode 100644 desktop.ini delete mode 100644 models/desktop.ini diff --git a/desktop.ini b/desktop.ini deleted file mode 100644 index 216df0d..0000000 --- a/desktop.ini +++ /dev/null @@ -1,2 +0,0 @@ -[.ShellClassInfo] -IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 diff --git a/models/desktop.ini b/models/desktop.ini deleted file mode 100644 index 216df0d..0000000 --- a/models/desktop.ini +++ /dev/null @@ -1,2 +0,0 @@ -[.ShellClassInfo] -IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 From 2eca50d53025d22c71b02db7bc175544aa484924 Mon Sep 17 00:00:00 2001 From: Wudext Date: Thu, 17 Nov 2022 14:39:25 +0300 Subject: [PATCH 07/36] Update main_fastapi.py --- main_fastapi.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main_fastapi.py b/main_fastapi.py index 862ae00..6c0544b 100644 --- a/main_fastapi.py +++ b/main_fastapi.py @@ -37,7 +37,7 @@ def get_button(button_id: int, db: Session = Depends(get_db)): return db_button -@app.post("/buttons/{button_id}") +@app.delete("/buttons/") def remove_button(button_id: int, db: Session = Depends(get_db)): db_button = methods.get_button(db, button_id) if db_button is None: @@ -45,7 +45,7 @@ def remove_button(button_id: int, db: Session = Depends(get_db)): return methods.delete_button(db=db, button_id=button_id) -@app.post("/buttons/{button_id}/update") +@app.patch("/buttons/") def update_button(button: CRUD.ButtonCreate, db: Session = Depends(get_db)): db_old_button = get_button(button_id=button.id, db=db) if db_old_button is None: @@ -74,7 +74,7 @@ def get_category(category_id: int, db: Session = Depends(get_db)): return db_category -@app.post("/categories/{category_id}") +@app.delete("/categories/") def remove_category(category_id: int, db: Session = Depends(get_db)): db_category = methods.get_category(db, category_id=category_id) if db_category is None: @@ -82,7 +82,7 @@ def remove_category(category_id: int, db: Session = Depends(get_db)): return methods.delete_category(db=db, category_id=category_id) -@app.post("/categories/{category_id}/update") +@app.patch("/categories/") def update_category(category: CRUD.CategoryCreate, db: Session = Depends(get_db)): db_old_category = methods.get_category(db=db, category_id=category.id) if db_old_category is None: From cf2d10f4ebd9bd0062f71b5c19ca07fa2fb7e280 Mon Sep 17 00:00:00 2001 From: Wudext Date: Thu, 17 Nov 2022 14:41:14 +0300 Subject: [PATCH 08/36] Update main_fastapi.py --- main_fastapi.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/main_fastapi.py b/main_fastapi.py index 6c0544b..20d3f8c 100644 --- a/main_fastapi.py +++ b/main_fastapi.py @@ -16,7 +16,7 @@ def get_db(): db.close() -@app.post("/buttons/", response_model=CRUD.Button) +@app.post("/button/", response_model=CRUD.Button) def create_button(button: CRUD.ButtonCreate, db: Session = Depends(get_db)): db_button = methods.get_button(db, button_id=button.id) if db_button: @@ -24,12 +24,12 @@ def create_button(button: CRUD.ButtonCreate, db: Session = Depends(get_db)): return methods.create_button(db=db, button=button) -@app.get("/buttons/", response_model=list[CRUD.Button]) +@app.get("/button/", response_model=list[CRUD.Button]) def get_buttons(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): return methods.get_buttons(db, skip=skip, limit=limit) -@app.get("/buttons/{button_id}", response_model=CRUD.Button) +@app.get("/button/{button_id}", response_model=CRUD.Button) def get_button(button_id: int, db: Session = Depends(get_db)): db_button = methods.get_button(db, button_id) if db_button is None: @@ -37,7 +37,7 @@ def get_button(button_id: int, db: Session = Depends(get_db)): return db_button -@app.delete("/buttons/") +@app.delete("/button/") def remove_button(button_id: int, db: Session = Depends(get_db)): db_button = methods.get_button(db, button_id) if db_button is None: @@ -45,7 +45,7 @@ def remove_button(button_id: int, db: Session = Depends(get_db)): return methods.delete_button(db=db, button_id=button_id) -@app.patch("/buttons/") +@app.patch("/button/") def update_button(button: CRUD.ButtonCreate, db: Session = Depends(get_db)): db_old_button = get_button(button_id=button.id, db=db) if db_old_button is None: @@ -53,7 +53,7 @@ def update_button(button: CRUD.ButtonCreate, db: Session = Depends(get_db)): return methods.update_button(db=db, button_id=button.id, button=button) -@app.post("/categories/", response_model=CRUD.Category) +@app.post("/category/", response_model=CRUD.Category) def create_category(category: CRUD.CategoryCreate, db: Session = Depends(get_db)): db_category = methods.get_category(category_id=category.id, db=db) if db_category: @@ -61,12 +61,12 @@ def create_category(category: CRUD.CategoryCreate, db: Session = Depends(get_db) return methods.create_category(db=db, category=category) -@app.get("/categories/", response_model=list[CRUD.Category]) +@app.get("/category/", response_model=list[CRUD.Category]) def get_categories(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): return methods.get_categories(db, skip=skip, limit=limit) -@app.get("/categories/{category_id}", response_model=CRUD.Category) +@app.get("/category/{category_id}", response_model=CRUD.Category) def get_category(category_id: int, db: Session = Depends(get_db)): db_category = methods.get_category(category_id=category_id, db=db) if db_category is None: @@ -74,7 +74,7 @@ def get_category(category_id: int, db: Session = Depends(get_db)): return db_category -@app.delete("/categories/") +@app.delete("/category/") def remove_category(category_id: int, db: Session = Depends(get_db)): db_category = methods.get_category(db, category_id=category_id) if db_category is None: @@ -82,7 +82,7 @@ def remove_category(category_id: int, db: Session = Depends(get_db)): return methods.delete_category(db=db, category_id=category_id) -@app.patch("/categories/") +@app.patch("/category/") def update_category(category: CRUD.CategoryCreate, db: Session = Depends(get_db)): db_old_category = methods.get_category(db=db, category_id=category.id) if db_old_category is None: From 82534c97b99938c97d3d8a2c4d86e46576ba0974 Mon Sep 17 00:00:00 2001 From: Wudext Date: Fri, 18 Nov 2022 22:08:00 +0300 Subject: [PATCH 09/36] Optimisation + cleaning --- .gitignore | 1 + __init__.py | 28 ++++++++++ create_database.py | 58 ++++++++------------ desktop.ini | 2 + main_fastapi.py | 35 ++++-------- methods.py | 48 ++++++---------- models/CRUD.py | 39 +++---------- models/__init__.py | 0 models/__pycache__/__init__.cpython-310.pyc | Bin 196 -> 0 bytes models/__pycache__/buttons.cpython-310.pyc | Bin 1229 -> 990 bytes models/__pycache__/database.cpython-310.pyc | Bin 971 -> 0 bytes models/buttons.py | 23 +++----- models/categories.py | 6 +- models/desktop.ini | 2 + {models => trash}/database.py | 50 +++++++---------- 15 files changed, 124 insertions(+), 168 deletions(-) create mode 100644 __init__.py create mode 100644 desktop.ini delete mode 100644 models/__init__.py delete mode 100644 models/__pycache__/__init__.cpython-310.pyc delete mode 100644 models/__pycache__/database.cpython-310.pyc create mode 100644 models/desktop.ini rename {models => trash}/database.py (78%) diff --git a/.gitignore b/.gitignore index b6e4761..60e05dd 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ parts/ sdist/ var/ wheels/ +trash/ pip-wheel-metadata/ share/python-wheels/ *.egg-info/ diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..f4e2808 --- /dev/null +++ b/__init__.py @@ -0,0 +1,28 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from sqlalchemy.ext.declarative import declarative_base +from fastapi import FastAPI +from dotenv import load_dotenv +import os + +db = None +load_dotenv() +login = os.getenv('SQL_LOGIN') +password = os.getenv('SQL_PASSWORD') +host = os.getenv('SQL_HOST') +port = int(os.getenv('SQL_PORT')) +tablespace = os.getenv('SQL_TABLESPACE') +path = f'postgresql://{login}:{password}@{host}:{port}/{tablespace}' +engine = create_engine(path) +Session = sessionmaker(bind=engine) +Base = declarative_base() +Base.metadata.create_all(bind=create_engine(path)) +app = FastAPI() + + +def get_db(): + db = Session(bind=engine) + try: + yield db + finally: + db.close() diff --git a/create_database.py b/create_database.py index b95bf26..016b9ee 100644 --- a/create_database.py +++ b/create_database.py @@ -1,20 +1,13 @@ # This Python file uses the following encoding: utf-8 from faker import Faker -from models.database import create_db, Session, get_db from models.buttons import Button from models.categories import Category from fastapi import Depends +from __init__ import get_db, Session import json -def create_database(load_fake_data: bool = True): - create_db() - - if load_fake_data: - _load_fake_data(Session()) - - def _load_fake_data(session: Session = Depends(get_db)): with open('buttons.json', 'r', encoding="utf8") as f: data = json.load(f) @@ -24,41 +17,38 @@ def _load_fake_data(session: Session = Depends(get_db)): category3 = Category(id=3, category_id=3, name=data[2]['name'], type=data[2]['type']) # Категория - Информация # Категория - Полезное - button1 = Button(id=1, name=data[0]['items'][0]['text'], icon=data[0]['items'][0]['icon'], # Полезное - category_id=category1.give_id()) - button2 = Button(id=2, name=data[0]['items'][1]['text'], icon=data[0]['items'][1]['icon'], # Этажи - category_id=category1.give_id()) + button1 = Button(id=1, name=data[0]['items'][0]['text'], icon=data[0]['items'][0]['icon']["src"], # Полезное + category_id=1) + button2 = Button(id=2, name=data[0]['items'][1]['text'], icon=data[0]['items'][1]['icon']["src"], # Этажи + category_id=1) # Категория - Сервисы - button3 = Button(id=3, name=data[1]['items'][0]['text'], icon=data[1]['items'][0]['icon'], # Написать - category_id=category2.give_id()) - button4 = Button(id=4, name=data[1]['items'][1]['text'], icon=data[1]['items'][1]['icon'], # Учебная часть - category_id=category2.give_id()) - button5 = Button(id=5, name=data[1]['items'][2]['text'], icon=data[1]['items'][2]['icon'], # МФК - category_id=category2.give_id()) - button6 = Button(id=6, name=data[1]['items'][3]['text'], icon=data[1]['items'][3]['icon'], # Жалоба - category_id=category2.give_id()) - button7 = Button(id=7, name=data[1]['items'][4]['text'], icon=data[1]['items'][4]['icon'], # Мат. помощь - category_id=category2.give_id()) - button8 = Button(id=8, name=data[1]['items'][5]['text'], icon=data[1]['items'][5]['icon'], # Профсоюз - category_id=category2.give_id()) + button3 = Button(id=3, name=data[1]['items'][0]['text'], icon=data[1]['items'][0]['icon']["src"], # Написать + category_id=2) + button4 = Button(id=4, name=data[1]['items'][1]['text'], icon=data[1]['items'][1]['icon']["src"], # Учебная часть + category_id=2) + button5 = Button(id=5, name=data[1]['items'][2]['text'], icon=data[1]['items'][2]['icon']["src"], # МФК + category_id=2) + button6 = Button(id=6, name=data[1]['items'][3]['text'], icon=data[1]['items'][3]['icon']["src"], # Жалоба + category_id=2) + button7 = Button(id=7, name=data[1]['items'][4]['text'], icon=data[1]['items'][4]['icon']["src"], # Мат. помощь + category_id=2) + button8 = Button(id=8, name=data[1]['items'][5]['text'], icon=data[1]['items'][5]['icon']["src"], # Профсоюз + category_id=2) # Категория - Информация - button9 = Button(id=9, name=data[2]['items'][0]['text'], icon=data[2]['items'][0]['icon'], # Приложение - category_id=category3.give_id()) - button10 = Button(id=10, name=data[2]['items'][1]['text'], icon=data[2]['items'][1]['icon'], # Обратная связь - category_id=category3.give_id()) - button11 = Button(id=11, name=data[2]['items'][2]['text'], icon=data[2]['items'][2]['icon'], # О приложении - category_id=category3.give_id()) + button9 = Button(id=9, name=data[2]['items'][0]['text'], icon=data[2]['items'][0]['icon']["src"], # Приложение + category_id=3) + button10 = Button(id=10, name=data[2]['items'][1]['text'], icon=data[2]['items'][1]['icon']["src"], # Обратная связь + category_id=3) + button11 = Button(id=11, name=data[2]['items'][2]['text'], icon=data[2]['items'][2]['icon']["src"], # О приложении + category_id=3) faker = Faker('ru_RU') session.add(category1) session.add(category2) session.add(category3) - - session.commit() - session.add(button1) session.add(button2) session.add(button3) @@ -74,4 +64,4 @@ def _load_fake_data(session: Session = Depends(get_db)): session.commit() -create_database(True) +_load_fake_data(Session()) diff --git a/desktop.ini b/desktop.ini new file mode 100644 index 0000000..216df0d --- /dev/null +++ b/desktop.ini @@ -0,0 +1,2 @@ +[.ShellClassInfo] +IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 diff --git a/main_fastapi.py b/main_fastapi.py index 20d3f8c..54a1690 100644 --- a/main_fastapi.py +++ b/main_fastapi.py @@ -1,23 +1,13 @@ +from fastapi import Depends, HTTPException from sqlalchemy.orm import Session -import methods -from models import CRUD, database -from models.database import engine -from fastapi import Depends, FastAPI, HTTPException - -database.Base.metadata.create_all(bind=engine) -app = FastAPI() - -def get_db(): - db = Session(bind=engine) - try: - yield db - finally: - db.close() +from __init__ import get_db, app +import methods +from models import CRUD @app.post("/button/", response_model=CRUD.Button) -def create_button(button: CRUD.ButtonCreate, db: Session = Depends(get_db)): +def create_button(button: CRUD.Button, db: Session = Depends(get_db)): db_button = methods.get_button(db, button_id=button.id) if db_button: raise HTTPException(status_code=400, detail='Button already exist') @@ -37,7 +27,7 @@ def get_button(button_id: int, db: Session = Depends(get_db)): return db_button -@app.delete("/button/") +@app.delete("/button/", response_model=CRUD.Button) def remove_button(button_id: int, db: Session = Depends(get_db)): db_button = methods.get_button(db, button_id) if db_button is None: @@ -45,8 +35,8 @@ def remove_button(button_id: int, db: Session = Depends(get_db)): return methods.delete_button(db=db, button_id=button_id) -@app.patch("/button/") -def update_button(button: CRUD.ButtonCreate, db: Session = Depends(get_db)): +@app.patch("/button/", response_model=CRUD.Button) +def update_button(button: CRUD.Button, db: Session = Depends(get_db)): db_old_button = get_button(button_id=button.id, db=db) if db_old_button is None: raise HTTPException(status_code=404, detail="Button does not exist") @@ -54,7 +44,7 @@ def update_button(button: CRUD.ButtonCreate, db: Session = Depends(get_db)): @app.post("/category/", response_model=CRUD.Category) -def create_category(category: CRUD.CategoryCreate, db: Session = Depends(get_db)): +def create_category(category: CRUD.Category, db: Session = Depends(get_db)): db_category = methods.get_category(category_id=category.id, db=db) if db_category: raise HTTPException(status_code=400, detail="Category already exist") @@ -82,10 +72,9 @@ def remove_category(category_id: int, db: Session = Depends(get_db)): return methods.delete_category(db=db, category_id=category_id) -@app.patch("/category/") -def update_category(category: CRUD.CategoryCreate, db: Session = Depends(get_db)): +@app.patch("/category/", response_model=CRUD.Category) +def update_category(category: CRUD.Category, db: Session = Depends(get_db)): db_old_category = methods.get_category(db=db, category_id=category.id) if db_old_category is None: raise HTTPException(status_code=404, detail="Category does not exist") - else: - return methods.update_category(db=db, category_id=category.id, category=category) \ No newline at end of file + return methods.update_category(db=db, category_id=category.id, category=category) diff --git a/methods.py b/methods.py index 69adf84..81dc29e 100644 --- a/methods.py +++ b/methods.py @@ -1,6 +1,5 @@ from sqlalchemy.orm import Session from models import CRUD, buttons, categories -from models.database import engine def get_button(db: Session, button_id: int): @@ -19,20 +18,18 @@ def get_categories(db: Session, skip: int, limit: int): return db.query(categories.Category).offset(skip).limit(limit).all() -def create_button(db: Session, button: CRUD.ButtonCreate): +def create_button(db: Session, button: CRUD.Button): db_button = buttons.Button(id=button.id, category_id=button.category_id, - name=button.name, icon=button.icon["src"]) - try: - db.add(db_button) - db.commit() - db.refresh(db_button) - return db_button - except: - print('Кнопка уже существует или неверно указан параметр категории') + name=button.name, icon=button.icon) + db.add(db_button) + db.commit() + db.refresh(db_button) db.close() + return db_button + -def create_category(db: Session, category: CRUD.CategoryCreate): +def create_category(db: Session, category: CRUD.Category): db_category = categories.Category(id=category.id, category_id=category.category_id, type=category.type, name=category.name) db.add(db_category) @@ -44,12 +41,10 @@ def create_category(db: Session, category: CRUD.CategoryCreate): def delete_button(db: Session, button_id: int): delete = db.query(buttons.Button).filter(buttons.Button.id == button_id).first() - try: - db.delete(delete) - db.commit() - except: - raise ValueError('Object doesn`t exist') + db.delete(delete) + db.commit() db.close() + return delete def delete_category(db: Session, category_id: int): @@ -62,31 +57,24 @@ def delete_category(db: Session, category_id: int): db.commit() db.close() + return delete + -def update_button(db: Session, button_id: int, button: CRUD.ButtonCreate): +def update_button(db: Session, button_id: int, button: CRUD.Button): old_button = db.query(buttons.Button).filter(buttons.Button.id == button_id).first() new_button = buttons.Button(id=button.id, category_id=button.category_id, name=button.name, icon=button.icon) delete_button(db=db, button_id=old_button.id) create_button(db=db, button=new_button) + return new_button + -def update_category(db: Session, category_id: int, category: CRUD.CategoryCreate): +def update_category(db: Session, category_id: int, category: CRUD.Category): old_category = db.query(buttons.Button).filter(buttons.Button.id == category_id).first() new_category = categories.Category(id=category.id, category_id=category.category_id, name=category.name, type=category.type) delete_category(db=db, category_id=old_category.id) create_category(db=db, category=new_category) - -# create_button(db=Session(bind=engine), button=CRUD.ButtonCreate( -# id=15, category_id=4, name='test', icon={"src": 'test'})) -# print(get_button(db=Session(bind=engine), button_id=15)) -# update_button(db=Session(bind=engine), button_id=15, button=CRUD.ButtonCreate( -# id=15, category_id=4, name='test', icon={"src": 'lmao'})) -# print(get_button(db=Session(bind=engine), button_id=15)) - -# print(get_category(db=Session(bind=engine), category_id=4)) -# update_category(db=Session(bind=engine), category_id=4, category=CRUD.CategoryCreate( -# id=4, category_id=4, name='быбик', type='насрали')) -# print(get_category(db=Session(bind=engine), category_id=4)) + return new_category diff --git a/models/CRUD.py b/models/CRUD.py index 7f39a3c..f98a0af 100644 --- a/models/CRUD.py +++ b/models/CRUD.py @@ -1,47 +1,22 @@ from pydantic import BaseModel +from typing import Optional -class CategoryBase(BaseModel): +class Category(BaseModel): id: int category_id: int - - -class CategoryCreate(CategoryBase): - type: str - name: str - - -class CategoryDelete(CategoryBase): - type: str - name: str - - -class Category(CategoryBase): - type: str - name: str + type: Optional[str] + name: Optional[str] class Config: orm_mode = True -class ButtonBase(BaseModel): +class Button(BaseModel): id: int category_id: int - - -class ButtonCreate(ButtonBase): - name: str - icon: dict - - -class ButtonDelete(ButtonBase): - name: str - icon: str - - -class Button(ButtonBase): - name: str - icon: str + name: Optional[str] + icon: Optional[str] class Config: orm_mode = True diff --git a/models/__init__.py b/models/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/models/__pycache__/__init__.cpython-310.pyc b/models/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index c4fbb1bcd128f0620b424f9f9d36ba013464c723..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196 zcmd1j<>g`kf;B6$lR@-j5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;yrBkn3y}6Iz^F zR2*|*;l-v48!k4+T$poV--VqD7q(p3ak25ju9yppFYLXr1H_nru>mNx`N9S$^J2?| zT|j|7F+c;#GLut_brTCRV{-FTQge!9;^Q;(GE3s)^$IF)ao7L_OLJ1~Kn^Ho0un3? E0BZ3@V*mgE diff --git a/models/__pycache__/buttons.cpython-310.pyc b/models/__pycache__/buttons.cpython-310.pyc index e660eaaf0a09b847f0591d445e9dbd532f85603c..1afd6eb7bc4f9d1d783e34703196b87e0c52db3b 100644 GIT binary patch delta 401 zcmYjNJx{|h5RF|o32hVF4j?{7rGgY8d18T~14SKK5KB}MrO6dirA=|dM_I!k2%dqN zfrbCU+93n~f)%i7kvQp|@1%F{`R;w;Lo*}8C@CVI9&fxeoSEyWlkK(&YN}*|{mrTm zUg$D639iBExiMHM*V}Fa=MkHDS#2Ym?rcG6pr$Crb{Pn0DY7d*@r(D0ZTRYmVrhI2 zHP$>NUFIQcU1VEi+~lu#n@@3{=49j!A;*EAm%X`(BN`HT(MULx7{dOG!m48I6CUvC z5$z5mAHulXyGoL16w~n}*Byt!m^qFq)#Y+^L=gEVSpB+NJp3inKhCjb^5;UUSrIfw zB$Oo-C1?^12~vsGd5tvcveXrVDp*HVq>Gn#h;^_UUxsezUBGZc;WnZF?#5IxV%KIL VrSrW4h-dn`P+jJ4WCbr3egLT~XJr5Y delta 666 zcmY*X!EVz)5cTX%64$Yvrb;)lXGj-9R@q*kta zNF~mZzkvgP!4DvDUJ3q!D=-_kQW$HVXJ?*0&rkl<`@R(xLEt$Q--e%(IbVf)@MiVo z6}YSH1O|`dQYDtX87>`OQOQfwZy|7=5{6Vp9fPIHrvD9Z zXI~w?O_bkB^MJi?G}}E+^RyCTK+V!x(FJ>Xa5*?yR8^6Wg=iTG(DW{>O3Pj=ecYVF z0bW53&1aZiw{q6P0;05%n6?`Xrunp3D`;V8o30RA&9xA_bSmw~>;MA(Mb`a;xFMuv zokTp;bo-_$~39;j-8B} zdwhRn&2b$BpSci%3-`I-ZQk|$K#neGzN3~05=%CU^A_>_LG<_mw$ GM*JTNj+DCq diff --git a/models/__pycache__/database.cpython-310.pyc b/models/__pycache__/database.cpython-310.pyc deleted file mode 100644 index 11cd0f0218de68114beb3b08d02d622d92be5203..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 971 zcmZuv&2G~`5Z+n;#BrQ7kx&m62ae=G9snUk+9Gj46(|KH%TnZcmo~7D%dT4}YNeL+ z+BZO=5vSZL!J$;ZE8tvx<-`MU;=-(xmLOQmv)}w^XTF(9s#TZZ`Sxo&TBU@1*UJ1c zP`QJ@+C#wz13_Z&0W_w9#)fYc*bruH`Bs5VVaFxERA5UuzQapH0)#s!WoD1ScR6r( z2!4edyuwPy;CtK}62HnERz4zrjk&CX_X6`+74JH;I;2_qffbM@Fy&o7*ys;9 z8a&07>L-IZe9k56b;ctR$}sKk@NO?uI;J9$kaby-^1;rU-{?HN-(795x0l(!A7} z)|Fe4NQx>b>y3%@@TH$mQLNky9w{zWkc}p9veU_7kiE^mW}mKQAG5Q`VRjy5@3Sx2 znFjAB2k82cozCFoC_6{P=Kvqr=|^1M2)Ft{D~J=ug$h`hhMIb7Yd5zyc-m#Xnr5M= zO%H6K;J_l4+V&4+J@XYEti35ofx$8CBR!k~U6VFin^fwk+`brsETdX5n~JLT)@GBM z&1Vs9%0%IUNo5_uU#U?im#MWkCC}i5rpBmPw+@kd7ELoZmyuZR#5@f(&D@=(35Adg z7&2FeZnZ3GDbIpf?<(39?mDT1yno3h2`Kz=w zw`Nk)%I)cXNlPms6@6-8K+*f63q=+GE!w$- Date: Fri, 18 Nov 2022 22:40:03 +0300 Subject: [PATCH 10/36] Delete .idea directory --- .idea/.gitignore | 3 --- .idea/desktop.ini | 2 -- .idea/inspectionProfiles/desktop.ini | 2 -- .idea/inspectionProfiles/profiles_settings.xml | 6 ------ .idea/misc.xml | 4 ---- .idea/modules.xml | 8 -------- .idea/services-api.iml | 8 -------- .idea/vcs.xml | 6 ------ 8 files changed, 39 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/desktop.ini delete mode 100644 .idea/inspectionProfiles/desktop.ini delete mode 100644 .idea/inspectionProfiles/profiles_settings.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/services-api.iml delete mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/desktop.ini b/.idea/desktop.ini deleted file mode 100644 index 216df0d..0000000 --- a/.idea/desktop.ini +++ /dev/null @@ -1,2 +0,0 @@ -[.ShellClassInfo] -IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 diff --git a/.idea/inspectionProfiles/desktop.ini b/.idea/inspectionProfiles/desktop.ini deleted file mode 100644 index 216df0d..0000000 --- a/.idea/inspectionProfiles/desktop.ini +++ /dev/null @@ -1,2 +0,0 @@ -[.ShellClassInfo] -IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 105ce2d..0000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index dc9ea49..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 46ca921..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/services-api.iml b/.idea/services-api.iml deleted file mode 100644 index 8437fe6..0000000 --- a/.idea/services-api.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From fc7ba7c94673c0202db00bfde979af2777d426fb Mon Sep 17 00:00:00 2001 From: Wudext <116914558+Wudext@users.noreply.github.com> Date: Sat, 19 Nov 2022 19:05:50 +0300 Subject: [PATCH 11/36] Delete models/__pycache__ directory --- models/__pycache__/buttons.cpython-310.pyc | Bin 990 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 models/__pycache__/buttons.cpython-310.pyc diff --git a/models/__pycache__/buttons.cpython-310.pyc b/models/__pycache__/buttons.cpython-310.pyc deleted file mode 100644 index 1afd6eb7bc4f9d1d783e34703196b87e0c52db3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 990 zcmYjPOK;Oa5Z<*N$90mv4hV7bfdi2sKOjOCBB%%<^-{R3ELN^}DK2)BuH!-~4zwvp z#1&BrLY%loJfu)T_zUZ+ublV+T!EP+Kvvpso;&k-Je^w2C2(GyJP7X?gnUP1{#amK zft#)a2qI`q63S>|Fe5RUnOMxC=r`h0Vl#W9n{hdDm_x~RA}molBBCVi0U4NVLD-w5 zUj6~KNZl&zRwvGqc2U03?#XSb3VXAsqV{&-u6LA-w%a#lzfOx~A;UOOK`+{qd@D#L z_?%V%Pdcg}Vy*e}kMT9|O2Vih%n+2B(vl@%2=l4IY|zUC${h5`1?leC13EBRW#+F= zd9&J-Z((1}dc6)1-I~bs7BHxERg?s(&+p5ANaq-!?!axqP0s@e`iSh1V;B-h3ep5= zfV4oG$K=F;p_DeV8szF1`l}x6Z~9aZy?xue5Q(OjU4%%z>FN_b%%4E`qaNjl`9c27 z)9*b@%@6e_{XviO$9kkkUbX}eT>TtEW2grSO%LX6wSzz7dQvo|4dpih1M7RrxMc@3?{Kw&sj^P@@g zFFq95i$a-R3c8Ws@RN>^aq7xFf29P(mmp;Pe$zP^$gc6Mo35a4t`0u!f75V%k|!rB!;y`~%9fAq)Tj From 500bcc74a0d7608588d79fa5b58b6f99062b1b7d Mon Sep 17 00:00:00 2001 From: Wudext <116914558+Wudext@users.noreply.github.com> Date: Sat, 19 Nov 2022 19:06:00 +0300 Subject: [PATCH 12/36] Delete trash directory --- trash/database.py | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 trash/database.py diff --git a/trash/database.py b/trash/database.py deleted file mode 100644 index b31bc12..0000000 --- a/trash/database.py +++ /dev/null @@ -1,21 +0,0 @@ -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker -from sqlalchemy.ext.declarative import declarative_base -from dotenv import load_dotenv -import os - -load_dotenv() -path = os.getenv('SQL_DATABASE_PATH') -engine = create_engine(path) -Session = sessionmaker(bind=engine) -Base = declarative_base() - - -def create_session(): - Session = sessionmaker(bind=engine) - - return Session - - -def create_db(): - Base.metadata.create_all(engine) From 7af0548a8b760c5e91b25820c5341d047aee196f Mon Sep 17 00:00:00 2001 From: Wudext <116914558+Wudext@users.noreply.github.com> Date: Sat, 19 Nov 2022 19:06:09 +0300 Subject: [PATCH 13/36] Delete desktop.ini --- desktop.ini | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 desktop.ini diff --git a/desktop.ini b/desktop.ini deleted file mode 100644 index 216df0d..0000000 --- a/desktop.ini +++ /dev/null @@ -1,2 +0,0 @@ -[.ShellClassInfo] -IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 From 501227f757cb66407d27aa2778af248344c29549 Mon Sep 17 00:00:00 2001 From: Wudext <116914558+Wudext@users.noreply.github.com> Date: Sat, 19 Nov 2022 19:06:19 +0300 Subject: [PATCH 14/36] Delete buttons.sqlite --- buttons.sqlite | Bin 12288 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 buttons.sqlite diff --git a/buttons.sqlite b/buttons.sqlite deleted file mode 100644 index b0b957870196a7ae3fea44233f75bf4406cab5c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2&u<$=6vt<5Z&I7s=Hf=B3K|Ybu>|p<>ZMghO|&f$#vvOQsAy&R$8NH%y=(2R zsVr0#heCxKh@cVz1Vsge#LbD6CT(i>+}oSKfD2dt2i~|@HHn~dFXi$^+MU^%H*eni zyt#a3FD?}9z!aA2H7kKQ#$IHcv(rK_#>Vi8;uE$A4r1X4_I%IyzlLLMX8vjtS0kxg zEWLy;YD9nt5CI}U1c(3;AOb{y2oM1xKm;BW=q^OYCyyTGE1jTHZJ5<|5V(%N+eK#$ zJzvsADgS0c7rT>UIuW<)Vy;-y&+3LK&X+{-T%quqNW`5=(-iOJjoG*IhWf5m0&~gr zI^`$!YTB;3&R(;3jk$O7#(D9!eqK!5^{lcvGjHf~XNziXdXJl|F!VFJp%-WMMG?-6 zeHMvq_b?xyoSNd@Yr~vEv#^is%PO3GN?M`GfiwyMF8?uCe&*lkhVPU0i=?)ZeLFsfAP``FnCMnV9%<;sGvDBLYN#2oM1xKm>>Y5g-CYfC&6g z1QKbDWq494qSz}hXzV1)IrmU}ZZ khObfAcQNdp;T`D9n{Y3@mwh}1FU80wv^?7h%%S&x07x!6xc~qF From 08642cf68eb6591e5cf2b9148aa0d70e88510654 Mon Sep 17 00:00:00 2001 From: Wudext Date: Sun, 20 Nov 2022 19:45:32 +0300 Subject: [PATCH 15/36] =?UTF-8?q?=D0=A8=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/.gitignore | 3 + .idea/inspectionProfiles/Project_Default.xml | 28 ++++ .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 4 + .idea/modules.xml | 8 ++ .idea/services-api.iml | 8 ++ .idea/vcs.xml | 6 + buttons.sqlite | Bin 12288 -> 0 bytes desktop.ini | 2 - main_fastapi.py | 6 +- methods.py | 14 +- models/categories.py | 8 +- models/desktop.ini | 2 - services-api/.gitignore | 129 ++++++++++++++++++ services-api/Dockerfile | 11 ++ services-api/LICENSE | 29 ++++ services-api/Makefile | 8 ++ services-api/README.md | 27 ++++ services-api/alembic.ini | 102 ++++++++++++++ services-api/flake8.conf | 35 +++++ services-api/gunicorn_conf.py | 67 +++++++++ services-api/migrations/README | 1 + services-api/migrations/env.py | 81 +++++++++++ services-api/migrations/script.py.mako | 24 ++++ services-api/migrations/versions/.gitkeep | 0 services-api/pyproject.toml | 22 +++ services-api/requirements.txt | 8 ++ services-api/services-backend/__init__.py | 0 services-api/services-backend/__main__.py | 6 + services-api/services-backend/exceptions.py | 0 .../services-backend/models/__init__.py | 0 services-api/services-backend/models/base.py | 22 +++ .../services-backend/models/database.py | 16 +++ .../services-backend/routes/__init__.py | 0 services-api/services-backend/routes/base.py | 22 +++ .../services-backend/routes/button.py | 0 .../services-backend/routes/category.py | 0 .../routes/models/__init__.py | 0 .../services-backend/routes/models/base.py | 12 ++ .../services-backend/routes/models/models.py | 0 services-api/services-backend/settings.py | 24 ++++ .../services-backend/utils/__init__.py | 0 42 files changed, 724 insertions(+), 17 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/services-api.iml create mode 100644 .idea/vcs.xml delete mode 100644 buttons.sqlite delete mode 100644 desktop.ini delete mode 100644 models/desktop.ini create mode 100644 services-api/.gitignore create mode 100644 services-api/Dockerfile create mode 100644 services-api/LICENSE create mode 100644 services-api/Makefile create mode 100644 services-api/README.md create mode 100644 services-api/alembic.ini create mode 100644 services-api/flake8.conf create mode 100644 services-api/gunicorn_conf.py create mode 100644 services-api/migrations/README create mode 100644 services-api/migrations/env.py create mode 100644 services-api/migrations/script.py.mako create mode 100644 services-api/migrations/versions/.gitkeep create mode 100644 services-api/pyproject.toml create mode 100644 services-api/requirements.txt create mode 100644 services-api/services-backend/__init__.py create mode 100644 services-api/services-backend/__main__.py create mode 100644 services-api/services-backend/exceptions.py create mode 100644 services-api/services-backend/models/__init__.py create mode 100644 services-api/services-backend/models/base.py create mode 100644 services-api/services-backend/models/database.py create mode 100644 services-api/services-backend/routes/__init__.py create mode 100644 services-api/services-backend/routes/base.py create mode 100644 services-api/services-backend/routes/button.py create mode 100644 services-api/services-backend/routes/category.py create mode 100644 services-api/services-backend/routes/models/__init__.py create mode 100644 services-api/services-backend/routes/models/base.py create mode 100644 services-api/services-backend/routes/models/models.py create mode 100644 services-api/services-backend/settings.py create mode 100644 services-api/services-backend/utils/__init__.py diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..fd95cf1 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,28 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..dc9ea49 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..46ca921 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/services-api.iml b/.idea/services-api.iml new file mode 100644 index 0000000..d0876a7 --- /dev/null +++ b/.idea/services-api.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/buttons.sqlite b/buttons.sqlite deleted file mode 100644 index b0b957870196a7ae3fea44233f75bf4406cab5c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2&u<$=6vt<5Z&I7s=Hf=B3K|Ybu>|p<>ZMghO|&f$#vvOQsAy&R$8NH%y=(2R zsVr0#heCxKh@cVz1Vsge#LbD6CT(i>+}oSKfD2dt2i~|@HHn~dFXi$^+MU^%H*eni zyt#a3FD?}9z!aA2H7kKQ#$IHcv(rK_#>Vi8;uE$A4r1X4_I%IyzlLLMX8vjtS0kxg zEWLy;YD9nt5CI}U1c(3;AOb{y2oM1xKm;BW=q^OYCyyTGE1jTHZJ5<|5V(%N+eK#$ zJzvsADgS0c7rT>UIuW<)Vy;-y&+3LK&X+{-T%quqNW`5=(-iOJjoG*IhWf5m0&~gr zI^`$!YTB;3&R(;3jk$O7#(D9!eqK!5^{lcvGjHf~XNziXdXJl|F!VFJp%-WMMG?-6 zeHMvq_b?xyoSNd@Yr~vEv#^is%PO3GN?M`GfiwyMF8?uCe&*lkhVPU0i=?)ZeLFsfAP``FnCMnV9%<;sGvDBLYN#2oM1xKm>>Y5g-CYfC&6g z1QKbDWq494qSz}hXzV1)IrmU}ZZ khObfAcQNdp;T`D9n{Y3@mwh}1FU80wv^?7h%%S&x07x!6xc~qF diff --git a/desktop.ini b/desktop.ini deleted file mode 100644 index 216df0d..0000000 --- a/desktop.ini +++ /dev/null @@ -1,2 +0,0 @@ -[.ShellClassInfo] -IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 diff --git a/main_fastapi.py b/main_fastapi.py index 54a1690..6ae52f9 100644 --- a/main_fastapi.py +++ b/main_fastapi.py @@ -10,12 +10,12 @@ def create_button(button: CRUD.Button, db: Session = Depends(get_db)): db_button = methods.get_button(db, button_id=button.id) if db_button: - raise HTTPException(status_code=400, detail='Button already exist') + raise HTTPException(status_code=409, detail='Button already exist') return methods.create_button(db=db, button=button) @app.get("/button/", response_model=list[CRUD.Button]) -def get_buttons(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): +def get_buttons(skip: int, limit: int, db: Session = Depends(get_db)): return methods.get_buttons(db, skip=skip, limit=limit) @@ -47,7 +47,7 @@ def update_button(button: CRUD.Button, db: Session = Depends(get_db)): def create_category(category: CRUD.Category, db: Session = Depends(get_db)): db_category = methods.get_category(category_id=category.id, db=db) if db_category: - raise HTTPException(status_code=400, detail="Category already exist") + raise HTTPException(status_code=409, detail="Category already exist") return methods.create_category(db=db, category=category) diff --git a/methods.py b/methods.py index 81dc29e..787848f 100644 --- a/methods.py +++ b/methods.py @@ -61,13 +61,15 @@ def delete_category(db: Session, category_id: int): def update_button(db: Session, button_id: int, button: CRUD.Button): - old_button = db.query(buttons.Button).filter(buttons.Button.id == button_id).first() - new_button = buttons.Button(id=button.id, category_id=button.category_id, - name=button.name, icon=button.icon) - delete_button(db=db, button_id=old_button.id) - create_button(db=db, button=new_button) + # old_button = db.query(buttons.Button).filter(buttons.Button.id == button_id).first() + # new_button = buttons.Button(id=button.id, category_id=button.category_id, + # name=button.name, icon=button.icon) + # delete_button(db=db, button_id=old_button.id) + # create_button(db=db, button=new_button) - return new_button + db.query(buttons.Button).update(button) + + return button def update_category(db: Session, category_id: int, category: CRUD.Category): diff --git a/models/categories.py b/models/categories.py index 4c02433..4db88a0 100644 --- a/models/categories.py +++ b/models/categories.py @@ -6,10 +6,10 @@ class Category(declarative_base()): __tablename__ = 'category' - id = Column('id', Integer, primary_key=True) - category_id = Column('category_id', Integer, unique=True) - name = Column('name', String) - type = Column('type', String) + id = Column(Integer, primary_key=True) + category_id = Column(Integer, unique=True) + name = Column(String) + type = Column(String) # category = relationship("Button", back_populates="category") def __repr__(self): diff --git a/models/desktop.ini b/models/desktop.ini deleted file mode 100644 index 216df0d..0000000 --- a/models/desktop.ini +++ /dev/null @@ -1,2 +0,0 @@ -[.ShellClassInfo] -IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 diff --git a/services-api/.gitignore b/services-api/.gitignore new file mode 100644 index 0000000..b6e4761 --- /dev/null +++ b/services-api/.gitignore @@ -0,0 +1,129 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/services-api/Dockerfile b/services-api/Dockerfile new file mode 100644 index 0000000..cbec1e2 --- /dev/null +++ b/services-api/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.10 +WORKDIR /app + +COPY ./requirements.txt /app/ +RUN pip install --no-cache-dir -r /app/requirements.txt + +ADD gunicorn_conf.py alembic.ini /app/ +ADD migrations /app/migrations +ADD services-backend /app/services-backend + +CMD [ "gunicorn", "-k", "uvicorn.workers.UvicornWorker", "-c", "/app/gunicorn_conf.py", "services-backend.routes.base:app" ] diff --git a/services-api/LICENSE b/services-api/LICENSE new file mode 100644 index 0000000..5494ac6 --- /dev/null +++ b/services-api/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2022, Профком студентов физфака МГУ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/services-api/Makefile b/services-api/Makefile new file mode 100644 index 0000000..45cd466 --- /dev/null +++ b/services-api/Makefile @@ -0,0 +1,8 @@ +run: + source ./venv/bin/activate && uvicorn --reload --log-level debug services-backend.routes.base:app + +db: + docker run -d -p 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust --name db-services-backend postgres:15 + +migrate: db + alembic upgrade head diff --git a/services-api/README.md b/services-api/README.md new file mode 100644 index 0000000..4e7d1c2 --- /dev/null +++ b/services-api/README.md @@ -0,0 +1,27 @@ +# services-api + +Бэкэдн сервисов приложения Твой ФФ для профкома ФФ МГУ + +## Запуск + +1) Перейдите в папку проекта + +2) Создайте виртуальное окружение командой: +```console +foo@bar:~$ python3 -m venv ./venv/ +``` + +3) Установите библиотеки +```console +foo@bar:~$ pip install -m requirements.txt +``` +4) Запускайте приложение! +```console +foo@bar:~$ python -m services-backend +``` + +## ENV-file description + +DB_DSN= + +--- diff --git a/services-api/alembic.ini b/services-api/alembic.ini new file mode 100644 index 0000000..f6c8899 --- /dev/null +++ b/services-api/alembic.ini @@ -0,0 +1,102 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = migrations + +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python-dateutil library that can be +# installed by adding `alembic[tz]` to the pip requirements +# string value is passed to dateutil.tz.gettz() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the +# "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to migrations/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +version_path_separator = os # Use os.pathsep. Default configuration used for new projects. + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = driver://user:pass@localhost/dbname + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/services-api/flake8.conf b/services-api/flake8.conf new file mode 100644 index 0000000..547208a --- /dev/null +++ b/services-api/flake8.conf @@ -0,0 +1,35 @@ +[flake8] +select = + E, W, # pep8 errors and warnings + F, # pyflakes + C9, # McCabe + N8, # Naming Conventions + #B, S, # bandit + #C, # commas + #D, # docstrings + #P, # string-format + #Q, # quotes + +ignore = + E122, # continuation line missing indentation or outdented + E123, # closing bracket does not match indentation of opening bracket's line + E127, # continuation line over-indented for visual indent + E131, # continuation line unaligned for hanging + E203, # whitespace before ':' + E225, # missing whitespace around operator + E226, # missing whitespace around arithmetic operator + E24, # multiple spaces after ',' or tab after ',' + E275, # missing whitespace after keyword + E305, # expected 2 blank lines after end of function or class + E306, # expected 1 blank line before a nested definition + E402, # module level import not at top of file + E722, # do not use bare except, specify exception instead + E731, # do not assign a lambda expression, use a def + E741, # do not use variables named 'l', 'O', or 'I' + + F722, # syntax error in forward annotation + + W503, # line break before binary operator + W504, # line break after binary operator + +max-line-length = 120 \ No newline at end of file diff --git a/services-api/gunicorn_conf.py b/services-api/gunicorn_conf.py new file mode 100644 index 0000000..7dd141d --- /dev/null +++ b/services-api/gunicorn_conf.py @@ -0,0 +1,67 @@ +import json +import multiprocessing +import os + +workers_per_core_str = os.getenv("WORKERS_PER_CORE", "1") +max_workers_str = os.getenv("MAX_WORKERS") +use_max_workers = None +if max_workers_str: + use_max_workers = int(max_workers_str) +web_concurrency_str = os.getenv("WEB_CONCURRENCY", None) + +host = os.getenv("HOST", "0.0.0.0") +port = os.getenv("PORT", "80") +bind_env = os.getenv("BIND", None) +use_loglevel = os.getenv("LOG_LEVEL", "info") +if bind_env: + use_bind = bind_env +else: + use_bind = f"{host}:{port}" + +cores = multiprocessing.cpu_count() +workers_per_core = float(workers_per_core_str) +default_web_concurrency = workers_per_core * cores +if web_concurrency_str: + web_concurrency = int(web_concurrency_str) + assert web_concurrency > 0 +else: + web_concurrency = max(int(default_web_concurrency), 2) + if use_max_workers: + web_concurrency = min(web_concurrency, use_max_workers) +accesslog_var = os.getenv("ACCESS_LOG", "-") +use_accesslog = accesslog_var or None +errorlog_var = os.getenv("ERROR_LOG", "-") +use_errorlog = errorlog_var or None +graceful_timeout_str = os.getenv("GRACEFUL_TIMEOUT", "120") +timeout_str = os.getenv("TIMEOUT", "120") +keepalive_str = os.getenv("KEEP_ALIVE", "5") + +# Gunicorn config variables +loglevel = use_loglevel +workers = web_concurrency +bind = use_bind +errorlog = use_errorlog +worker_tmp_dir = "/dev/shm" +accesslog = use_accesslog +graceful_timeout = int(graceful_timeout_str) +timeout = int(timeout_str) +keepalive = int(keepalive_str) + + +# For debugging and testing +log_data = { + "loglevel": loglevel, + "workers": workers, + "bind": bind, + "graceful_timeout": graceful_timeout, + "timeout": timeout, + "keepalive": keepalive, + "errorlog": errorlog, + "accesslog": accesslog, + # Additional, non-gunicorn variables + "workers_per_core": workers_per_core, + "use_max_workers": use_max_workers, + "host": host, + "port": port, +} +print(json.dumps(log_data)) diff --git a/services-api/migrations/README b/services-api/migrations/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/services-api/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/services-api/migrations/env.py b/services-api/migrations/env.py new file mode 100644 index 0000000..5bfafb7 --- /dev/null +++ b/services-api/migrations/env.py @@ -0,0 +1,81 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context +from services-backend.models.base import Base +from services-backend.settings import get_settings + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config +settings = get_settings() + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + configuration = config.get_section(config.config_ini_section) + configuration['sqlalchemy.url'] = settings.DB_DSN + connectable = engine_from_config( + configuration, + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure(connection=connection, target_metadata=target_metadata) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/services-api/migrations/script.py.mako b/services-api/migrations/script.py.mako new file mode 100644 index 0000000..2c01563 --- /dev/null +++ b/services-api/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/services-api/migrations/versions/.gitkeep b/services-api/migrations/versions/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/services-api/pyproject.toml b/services-api/pyproject.toml new file mode 100644 index 0000000..2368eb9 --- /dev/null +++ b/services-api/pyproject.toml @@ -0,0 +1,22 @@ +[tool.black] +line-length = 120 +target-version = ['py310'] +skip-string-normalization = true + +[tool.isort] +line_length = 120 +multi_line_output = 3 +lines_after_imports = 2 +include_trailing_comma = true + +[tool.pytest.ini_options] +minversion = "7.0" +python_files = "*.py" +testpaths = [ + "tests" +] +pythonpath = [ + "." +] +log_cli=true +log_level=0 diff --git a/services-api/requirements.txt b/services-api/requirements.txt new file mode 100644 index 0000000..dfdfe90 --- /dev/null +++ b/services-api/requirements.txt @@ -0,0 +1,8 @@ +fastapi +fastapi-sqlalchemy +psycopg2-binary +pydantic[dotenv] +uvicorn +alembic +SQLAlchemy +gunicorn diff --git a/services-api/services-backend/__init__.py b/services-api/services-backend/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/services-api/services-backend/__main__.py b/services-api/services-backend/__main__.py new file mode 100644 index 0000000..e145c78 --- /dev/null +++ b/services-api/services-backend/__main__.py @@ -0,0 +1,6 @@ +from services-backend.routes.base import app +import uvicorn + + +if __name__ == '__main__': + uvicorn.run(app) \ No newline at end of file diff --git a/services-api/services-backend/exceptions.py b/services-api/services-backend/exceptions.py new file mode 100644 index 0000000..e69de29 diff --git a/services-api/services-backend/models/__init__.py b/services-api/services-backend/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/services-api/services-backend/models/base.py b/services-api/services-backend/models/base.py new file mode 100644 index 0000000..2279e14 --- /dev/null +++ b/services-api/services-backend/models/base.py @@ -0,0 +1,22 @@ +import re +from sqlalchemy.ext.declarative import as_declarative, declared_attr + + +@as_declarative() +class Base: + """Base class for all database entities""" + + @classmethod + @declared_attr + def __tablename__(cls) -> str: + """Generate database table name automatically. + Convert CamelCase class name to snake_case db table name. + """ + return re.sub(r"(? str: + attrs = [] + for c in self.__table__.columns: + attrs.append(f"{c.name}={getattr(self, c.name)}") + return "{}({})".format(self.__class__.__name__, ', '.join(attrs)) + diff --git a/services-api/services-backend/models/database.py b/services-api/services-backend/models/database.py new file mode 100644 index 0000000..24e626f --- /dev/null +++ b/services-api/services-backend/models/database.py @@ -0,0 +1,16 @@ +from sqlalchemy import Column, Integer, String, ForeignKey +from .base import Base + + +class Category(Base): + id = Column(Integer, primary_key=True) + category_id = Column(Integer, unique=True) + name = Column(String) + type = Column(String) + # category = relationship("Button", back_populates="category") + +class Button(Base): + id = Column(Integer, primary_key=True) + name = Column(String) + category_id = Column(Integer, ForeignKey(Category.id)) + icon = Column(String) diff --git a/services-api/services-backend/routes/__init__.py b/services-api/services-backend/routes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/services-api/services-backend/routes/base.py b/services-api/services-backend/routes/base.py new file mode 100644 index 0000000..a78c88e --- /dev/null +++ b/services-api/services-backend/routes/base.py @@ -0,0 +1,22 @@ +from fastapi import FastAPI + +from ..settings import Settings +from fastapi.middleware.cors import CORSMiddleware +from fastapi_sqlalchemy import DBSessionMiddleware + +settings = Settings() +app = FastAPI() + +app.add_middleware( + DBSessionMiddleware, + db_url=settings.DB_DSN, + session_args={"autocommit": True}, +) + +app.add_middleware( + CORSMiddleware, + allow_origins=settings.CORS_ALLOW_ORIGINS, + allow_credentials=settings.CORS_ALLOW_CREDENTIALS, + allow_methods=settings.CORS_ALLOW_METHODS, + allow_headers=settings.CORS_ALLOW_HEADERS, +) \ No newline at end of file diff --git a/services-api/services-backend/routes/button.py b/services-api/services-backend/routes/button.py new file mode 100644 index 0000000..e69de29 diff --git a/services-api/services-backend/routes/category.py b/services-api/services-backend/routes/category.py new file mode 100644 index 0000000..e69de29 diff --git a/services-api/services-backend/routes/models/__init__.py b/services-api/services-backend/routes/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/services-api/services-backend/routes/models/base.py b/services-api/services-backend/routes/models/base.py new file mode 100644 index 0000000..05d0cdb --- /dev/null +++ b/services-api/services-backend/routes/models/base.py @@ -0,0 +1,12 @@ +from pydantic import BaseModel + + +class Base(BaseModel): + def __repr__(self) -> str: + attrs = [] + for k, v in self.__class__.schema().items(): + attrs.append(f"{k}={v}") + return "{}({})".format(self.__class__.__name__, ', '.join(attrs)) + + class Config: + orm_mode = True \ No newline at end of file diff --git a/services-api/services-backend/routes/models/models.py b/services-api/services-backend/routes/models/models.py new file mode 100644 index 0000000..e69de29 diff --git a/services-api/services-backend/settings.py b/services-api/services-backend/settings.py new file mode 100644 index 0000000..07d1286 --- /dev/null +++ b/services-api/services-backend/settings.py @@ -0,0 +1,24 @@ +from pydantic import BaseSettings, PostgresDsn +from functools import lru_cache + + +class Settings(BaseSettings): + """Application settings""" + DB_DSN: PostgresDsn + + CORS_ALLOW_ORIGINS: list[str] = ['*'] + CORS_ALLOW_CREDENTIALS: bool = True + CORS_ALLOW_METHODS: list[str] = ['*'] + CORS_ALLOW_HEADERS: list[str] = ['*'] + + class Config: + """Pydantic BaseSettings config""" + + case_sensitive = True + env_file = ".env" + + +@lru_cache +def get_settings() -> Settings: + settings = Settings() + return settings \ No newline at end of file diff --git a/services-api/services-backend/utils/__init__.py b/services-api/services-backend/utils/__init__.py new file mode 100644 index 0000000..e69de29 From c677da6444e4e6f3bd6dd753567152a3f06a7793 Mon Sep 17 00:00:00 2001 From: Wudext Date: Fri, 25 Nov 2022 12:18:41 +0300 Subject: [PATCH 16/36] Structure --- .gitignore | 1 - .idea/desktop.ini | 2 - .idea/inspectionProfiles/Project_Default.xml | 28 ++++ .idea/inspectionProfiles/desktop.ini | 2 - .idea/services-api.iml | 9 +- Dockerfile | 11 ++ LICENSE | 8 +- Makefile | 8 + README.md | 27 ++++ alembic.ini | 102 ++++++++++++ buttons.sqlite | Bin 12288 -> 0 bytes desktop.ini | 2 - flake8.conf | 35 +++++ gunicorn_conf.py | 67 ++++++++ main_fastapi.py | 80 ---------- migrations/README | 1 + migrations/env.py | 81 ++++++++++ migrations/script.py.mako | 24 +++ migrations/versions/.gitkeep | 0 models/__pycache__/buttons.cpython-310.pyc | Bin 990 -> 0 bytes models/desktop.ini | 2 - pyproject.toml | 22 +++ requirements.txt | 12 ++ services_backend/__init__.py | 0 services_backend/__main__.py | 6 + services_backend/exceptions.py | 0 services_backend/models/__init__.py | 0 services_backend/models/base.py | 22 +++ services_backend/models/database.py | 17 ++ services_backend/routes/__init__.py | 0 services_backend/routes/base.py | 28 ++++ services_backend/routes/button.py | 61 ++++++++ services_backend/routes/category.py | 68 ++++++++ services_backend/routes/models/__init__.py | 0 services_backend/routes/models/base.py | 12 ++ services_backend/routes/models/button.py | 19 +++ services_backend/routes/models/category.py | 20 +++ services_backend/settings.py | 24 +++ services_backend/utils/__init__.py | 0 __init__.py => trash/__init__.py | 0 buttons.json => trash/buttons.json | 148 +++++++++--------- .../create_database.py | 134 ++++++++-------- trash/database.py | 21 --- methods.py => trash/methods.py | 17 +- {models => trash/models}/CRUD.py | 0 {models => trash/models}/buttons.py | 34 ++-- {models => trash/models}/categories.py | 8 +- 47 files changed, 880 insertions(+), 283 deletions(-) delete mode 100644 .idea/desktop.ini create mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 .idea/inspectionProfiles/desktop.ini create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 README.md create mode 100644 alembic.ini delete mode 100644 buttons.sqlite delete mode 100644 desktop.ini create mode 100644 flake8.conf create mode 100644 gunicorn_conf.py delete mode 100644 main_fastapi.py create mode 100644 migrations/README create mode 100644 migrations/env.py create mode 100644 migrations/script.py.mako create mode 100644 migrations/versions/.gitkeep delete mode 100644 models/__pycache__/buttons.cpython-310.pyc delete mode 100644 models/desktop.ini create mode 100644 pyproject.toml create mode 100644 requirements.txt create mode 100644 services_backend/__init__.py create mode 100644 services_backend/__main__.py create mode 100644 services_backend/exceptions.py create mode 100644 services_backend/models/__init__.py create mode 100644 services_backend/models/base.py create mode 100644 services_backend/models/database.py create mode 100644 services_backend/routes/__init__.py create mode 100644 services_backend/routes/base.py create mode 100644 services_backend/routes/button.py create mode 100644 services_backend/routes/category.py create mode 100644 services_backend/routes/models/__init__.py create mode 100644 services_backend/routes/models/base.py create mode 100644 services_backend/routes/models/button.py create mode 100644 services_backend/routes/models/category.py create mode 100644 services_backend/settings.py create mode 100644 services_backend/utils/__init__.py rename __init__.py => trash/__init__.py (100%) rename buttons.json => trash/buttons.json (97%) rename create_database.py => trash/create_database.py (95%) delete mode 100644 trash/database.py rename methods.py => trash/methods.py (83%) rename {models => trash/models}/CRUD.py (100%) rename {models => trash/models}/buttons.py (90%) rename {models => trash/models}/categories.py (70%) diff --git a/.gitignore b/.gitignore index 60e05dd..b6e4761 100644 --- a/.gitignore +++ b/.gitignore @@ -20,7 +20,6 @@ parts/ sdist/ var/ wheels/ -trash/ pip-wheel-metadata/ share/python-wheels/ *.egg-info/ diff --git a/.idea/desktop.ini b/.idea/desktop.ini deleted file mode 100644 index 216df0d..0000000 --- a/.idea/desktop.ini +++ /dev/null @@ -1,2 +0,0 @@ -[.ShellClassInfo] -IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..fd95cf1 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,28 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/desktop.ini b/.idea/inspectionProfiles/desktop.ini deleted file mode 100644 index 216df0d..0000000 --- a/.idea/inspectionProfiles/desktop.ini +++ /dev/null @@ -1,2 +0,0 @@ -[.ShellClassInfo] -IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 diff --git a/.idea/services-api.iml b/.idea/services-api.iml index 8437fe6..6ae7352 100644 --- a/.idea/services-api.iml +++ b/.idea/services-api.iml @@ -2,7 +2,14 @@ - + + + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..cbec1e2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.10 +WORKDIR /app + +COPY ./requirements.txt /app/ +RUN pip install --no-cache-dir -r /app/requirements.txt + +ADD gunicorn_conf.py alembic.ini /app/ +ADD migrations /app/migrations +ADD services-backend /app/services-backend + +CMD [ "gunicorn", "-k", "uvicorn.workers.UvicornWorker", "-c", "/app/gunicorn_conf.py", "services-backend.routes.base:app" ] diff --git a/LICENSE b/LICENSE index 92e7e9c..5494ac6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -BSD 2-Clause License +BSD 3-Clause License Copyright (c) 2022, Профком студентов физфака МГУ All rights reserved. @@ -13,6 +13,10 @@ modification, are permitted provided that the following conditions are met: this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -22,4 +26,4 @@ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..45cd466 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +run: + source ./venv/bin/activate && uvicorn --reload --log-level debug services-backend.routes.base:app + +db: + docker run -d -p 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust --name db-services-backend postgres:15 + +migrate: db + alembic upgrade head diff --git a/README.md b/README.md new file mode 100644 index 0000000..4e7d1c2 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# services-api + +Бэкэдн сервисов приложения Твой ФФ для профкома ФФ МГУ + +## Запуск + +1) Перейдите в папку проекта + +2) Создайте виртуальное окружение командой: +```console +foo@bar:~$ python3 -m venv ./venv/ +``` + +3) Установите библиотеки +```console +foo@bar:~$ pip install -m requirements.txt +``` +4) Запускайте приложение! +```console +foo@bar:~$ python -m services-backend +``` + +## ENV-file description + +DB_DSN= + +--- diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 0000000..f6c8899 --- /dev/null +++ b/alembic.ini @@ -0,0 +1,102 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = migrations + +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python-dateutil library that can be +# installed by adding `alembic[tz]` to the pip requirements +# string value is passed to dateutil.tz.gettz() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the +# "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to migrations/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +version_path_separator = os # Use os.pathsep. Default configuration used for new projects. + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = driver://user:pass@localhost/dbname + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/buttons.sqlite b/buttons.sqlite deleted file mode 100644 index b0b957870196a7ae3fea44233f75bf4406cab5c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2&u<$=6vt<5Z&I7s=Hf=B3K|Ybu>|p<>ZMghO|&f$#vvOQsAy&R$8NH%y=(2R zsVr0#heCxKh@cVz1Vsge#LbD6CT(i>+}oSKfD2dt2i~|@HHn~dFXi$^+MU^%H*eni zyt#a3FD?}9z!aA2H7kKQ#$IHcv(rK_#>Vi8;uE$A4r1X4_I%IyzlLLMX8vjtS0kxg zEWLy;YD9nt5CI}U1c(3;AOb{y2oM1xKm;BW=q^OYCyyTGE1jTHZJ5<|5V(%N+eK#$ zJzvsADgS0c7rT>UIuW<)Vy;-y&+3LK&X+{-T%quqNW`5=(-iOJjoG*IhWf5m0&~gr zI^`$!YTB;3&R(;3jk$O7#(D9!eqK!5^{lcvGjHf~XNziXdXJl|F!VFJp%-WMMG?-6 zeHMvq_b?xyoSNd@Yr~vEv#^is%PO3GN?M`GfiwyMF8?uCe&*lkhVPU0i=?)ZeLFsfAP``FnCMnV9%<;sGvDBLYN#2oM1xKm>>Y5g-CYfC&6g z1QKbDWq494qSz}hXzV1)IrmU}ZZ khObfAcQNdp;T`D9n{Y3@mwh}1FU80wv^?7h%%S&x07x!6xc~qF diff --git a/desktop.ini b/desktop.ini deleted file mode 100644 index 216df0d..0000000 --- a/desktop.ini +++ /dev/null @@ -1,2 +0,0 @@ -[.ShellClassInfo] -IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 diff --git a/flake8.conf b/flake8.conf new file mode 100644 index 0000000..547208a --- /dev/null +++ b/flake8.conf @@ -0,0 +1,35 @@ +[flake8] +select = + E, W, # pep8 errors and warnings + F, # pyflakes + C9, # McCabe + N8, # Naming Conventions + #B, S, # bandit + #C, # commas + #D, # docstrings + #P, # string-format + #Q, # quotes + +ignore = + E122, # continuation line missing indentation or outdented + E123, # closing bracket does not match indentation of opening bracket's line + E127, # continuation line over-indented for visual indent + E131, # continuation line unaligned for hanging + E203, # whitespace before ':' + E225, # missing whitespace around operator + E226, # missing whitespace around arithmetic operator + E24, # multiple spaces after ',' or tab after ',' + E275, # missing whitespace after keyword + E305, # expected 2 blank lines after end of function or class + E306, # expected 1 blank line before a nested definition + E402, # module level import not at top of file + E722, # do not use bare except, specify exception instead + E731, # do not assign a lambda expression, use a def + E741, # do not use variables named 'l', 'O', or 'I' + + F722, # syntax error in forward annotation + + W503, # line break before binary operator + W504, # line break after binary operator + +max-line-length = 120 \ No newline at end of file diff --git a/gunicorn_conf.py b/gunicorn_conf.py new file mode 100644 index 0000000..7dd141d --- /dev/null +++ b/gunicorn_conf.py @@ -0,0 +1,67 @@ +import json +import multiprocessing +import os + +workers_per_core_str = os.getenv("WORKERS_PER_CORE", "1") +max_workers_str = os.getenv("MAX_WORKERS") +use_max_workers = None +if max_workers_str: + use_max_workers = int(max_workers_str) +web_concurrency_str = os.getenv("WEB_CONCURRENCY", None) + +host = os.getenv("HOST", "0.0.0.0") +port = os.getenv("PORT", "80") +bind_env = os.getenv("BIND", None) +use_loglevel = os.getenv("LOG_LEVEL", "info") +if bind_env: + use_bind = bind_env +else: + use_bind = f"{host}:{port}" + +cores = multiprocessing.cpu_count() +workers_per_core = float(workers_per_core_str) +default_web_concurrency = workers_per_core * cores +if web_concurrency_str: + web_concurrency = int(web_concurrency_str) + assert web_concurrency > 0 +else: + web_concurrency = max(int(default_web_concurrency), 2) + if use_max_workers: + web_concurrency = min(web_concurrency, use_max_workers) +accesslog_var = os.getenv("ACCESS_LOG", "-") +use_accesslog = accesslog_var or None +errorlog_var = os.getenv("ERROR_LOG", "-") +use_errorlog = errorlog_var or None +graceful_timeout_str = os.getenv("GRACEFUL_TIMEOUT", "120") +timeout_str = os.getenv("TIMEOUT", "120") +keepalive_str = os.getenv("KEEP_ALIVE", "5") + +# Gunicorn config variables +loglevel = use_loglevel +workers = web_concurrency +bind = use_bind +errorlog = use_errorlog +worker_tmp_dir = "/dev/shm" +accesslog = use_accesslog +graceful_timeout = int(graceful_timeout_str) +timeout = int(timeout_str) +keepalive = int(keepalive_str) + + +# For debugging and testing +log_data = { + "loglevel": loglevel, + "workers": workers, + "bind": bind, + "graceful_timeout": graceful_timeout, + "timeout": timeout, + "keepalive": keepalive, + "errorlog": errorlog, + "accesslog": accesslog, + # Additional, non-gunicorn variables + "workers_per_core": workers_per_core, + "use_max_workers": use_max_workers, + "host": host, + "port": port, +} +print(json.dumps(log_data)) diff --git a/main_fastapi.py b/main_fastapi.py deleted file mode 100644 index 54a1690..0000000 --- a/main_fastapi.py +++ /dev/null @@ -1,80 +0,0 @@ -from fastapi import Depends, HTTPException -from sqlalchemy.orm import Session - -from __init__ import get_db, app -import methods -from models import CRUD - - -@app.post("/button/", response_model=CRUD.Button) -def create_button(button: CRUD.Button, db: Session = Depends(get_db)): - db_button = methods.get_button(db, button_id=button.id) - if db_button: - raise HTTPException(status_code=400, detail='Button already exist') - return methods.create_button(db=db, button=button) - - -@app.get("/button/", response_model=list[CRUD.Button]) -def get_buttons(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - return methods.get_buttons(db, skip=skip, limit=limit) - - -@app.get("/button/{button_id}", response_model=CRUD.Button) -def get_button(button_id: int, db: Session = Depends(get_db)): - db_button = methods.get_button(db, button_id) - if db_button is None: - raise HTTPException(status_code=404, detail="Button does not exist") - return db_button - - -@app.delete("/button/", response_model=CRUD.Button) -def remove_button(button_id: int, db: Session = Depends(get_db)): - db_button = methods.get_button(db, button_id) - if db_button is None: - raise HTTPException(status_code=404, detail="Button does not exist") - return methods.delete_button(db=db, button_id=button_id) - - -@app.patch("/button/", response_model=CRUD.Button) -def update_button(button: CRUD.Button, db: Session = Depends(get_db)): - db_old_button = get_button(button_id=button.id, db=db) - if db_old_button is None: - raise HTTPException(status_code=404, detail="Button does not exist") - return methods.update_button(db=db, button_id=button.id, button=button) - - -@app.post("/category/", response_model=CRUD.Category) -def create_category(category: CRUD.Category, db: Session = Depends(get_db)): - db_category = methods.get_category(category_id=category.id, db=db) - if db_category: - raise HTTPException(status_code=400, detail="Category already exist") - return methods.create_category(db=db, category=category) - - -@app.get("/category/", response_model=list[CRUD.Category]) -def get_categories(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - return methods.get_categories(db, skip=skip, limit=limit) - - -@app.get("/category/{category_id}", response_model=CRUD.Category) -def get_category(category_id: int, db: Session = Depends(get_db)): - db_category = methods.get_category(category_id=category_id, db=db) - if db_category is None: - raise HTTPException(status_code=404, detail="Category does not exist") - return db_category - - -@app.delete("/category/") -def remove_category(category_id: int, db: Session = Depends(get_db)): - db_category = methods.get_category(db, category_id=category_id) - if db_category is None: - raise HTTPException(status_code=404, detail="Category does not exist") - return methods.delete_category(db=db, category_id=category_id) - - -@app.patch("/category/", response_model=CRUD.Category) -def update_category(category: CRUD.Category, db: Session = Depends(get_db)): - db_old_category = methods.get_category(db=db, category_id=category.id) - if db_old_category is None: - raise HTTPException(status_code=404, detail="Category does not exist") - return methods.update_category(db=db, category_id=category.id, category=category) diff --git a/migrations/README b/migrations/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/migrations/env.py b/migrations/env.py new file mode 100644 index 0000000..8be350c --- /dev/null +++ b/migrations/env.py @@ -0,0 +1,81 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context +from ..services_backend.models.base import Base +from ..services_backend.settings import get_settings + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config +settings = get_settings() + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + configuration = config.get_section(config.config_ini_section) + configuration['sqlalchemy.url'] = settings.DB_DSN + connectable = engine_from_config( + configuration, + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure(connection=connection, target_metadata=target_metadata) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/migrations/script.py.mako b/migrations/script.py.mako new file mode 100644 index 0000000..2c01563 --- /dev/null +++ b/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/migrations/versions/.gitkeep b/migrations/versions/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/models/__pycache__/buttons.cpython-310.pyc b/models/__pycache__/buttons.cpython-310.pyc deleted file mode 100644 index 1afd6eb7bc4f9d1d783e34703196b87e0c52db3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 990 zcmYjPOK;Oa5Z<*N$90mv4hV7bfdi2sKOjOCBB%%<^-{R3ELN^}DK2)BuH!-~4zwvp z#1&BrLY%loJfu)T_zUZ+ublV+T!EP+Kvvpso;&k-Je^w2C2(GyJP7X?gnUP1{#amK zft#)a2qI`q63S>|Fe5RUnOMxC=r`h0Vl#W9n{hdDm_x~RA}molBBCVi0U4NVLD-w5 zUj6~KNZl&zRwvGqc2U03?#XSb3VXAsqV{&-u6LA-w%a#lzfOx~A;UOOK`+{qd@D#L z_?%V%Pdcg}Vy*e}kMT9|O2Vih%n+2B(vl@%2=l4IY|zUC${h5`1?leC13EBRW#+F= zd9&J-Z((1}dc6)1-I~bs7BHxERg?s(&+p5ANaq-!?!axqP0s@e`iSh1V;B-h3ep5= zfV4oG$K=F;p_DeV8szF1`l}x6Z~9aZy?xue5Q(OjU4%%z>FN_b%%4E`qaNjl`9c27 z)9*b@%@6e_{XviO$9kkkUbX}eT>TtEW2grSO%LX6wSzz7dQvo|4dpih1M7RrxMc@3?{Kw&sj^P@@g zFFq95i$a-R3c8Ws@RN>^aq7xFf29P(mmp;Pe$zP^$gc6Mo35a4t`0u!f75V%k|!rB!;y`~%9fAq)Tj diff --git a/models/desktop.ini b/models/desktop.ini deleted file mode 100644 index 216df0d..0000000 --- a/models/desktop.ini +++ /dev/null @@ -1,2 +0,0 @@ -[.ShellClassInfo] -IconResource=C:\Program Files\Google\Drive File Stream\66.0.3.0\GoogleDriveFS.exe,23 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..2368eb9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[tool.black] +line-length = 120 +target-version = ['py310'] +skip-string-normalization = true + +[tool.isort] +line_length = 120 +multi_line_output = 3 +lines_after_imports = 2 +include_trailing_comma = true + +[tool.pytest.ini_options] +minversion = "7.0" +python_files = "*.py" +testpaths = [ + "tests" +] +pythonpath = [ + "." +] +log_cli=true +log_level=0 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1ff2691 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,12 @@ +fastapi~=0.86.0 +fastapi-sqlalchemy +psycopg2-binary +pydantic[dotenv]~=1.10.2 +uvicorn~=0.19.0 +alembic~=1.8.1 +SQLAlchemy~=1.4.43 +gunicorn + +python-dotenv~=0.21.0 +Faker~=15.3.1 +starlette~=0.20.4 \ No newline at end of file diff --git a/services_backend/__init__.py b/services_backend/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/services_backend/__main__.py b/services_backend/__main__.py new file mode 100644 index 0000000..3e28e7a --- /dev/null +++ b/services_backend/__main__.py @@ -0,0 +1,6 @@ +from .routes.base import app +import uvicorn + + +if __name__ == '__main__': + uvicorn.run(app) \ No newline at end of file diff --git a/services_backend/exceptions.py b/services_backend/exceptions.py new file mode 100644 index 0000000..e69de29 diff --git a/services_backend/models/__init__.py b/services_backend/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/services_backend/models/base.py b/services_backend/models/base.py new file mode 100644 index 0000000..2279e14 --- /dev/null +++ b/services_backend/models/base.py @@ -0,0 +1,22 @@ +import re +from sqlalchemy.ext.declarative import as_declarative, declared_attr + + +@as_declarative() +class Base: + """Base class for all database entities""" + + @classmethod + @declared_attr + def __tablename__(cls) -> str: + """Generate database table name automatically. + Convert CamelCase class name to snake_case db table name. + """ + return re.sub(r"(? str: + attrs = [] + for c in self.__table__.columns: + attrs.append(f"{c.name}={getattr(self, c.name)}") + return "{}({})".format(self.__class__.__name__, ', '.join(attrs)) + diff --git a/services_backend/models/database.py b/services_backend/models/database.py new file mode 100644 index 0000000..97b9bae --- /dev/null +++ b/services_backend/models/database.py @@ -0,0 +1,17 @@ +from sqlalchemy import Column, Integer, String, ForeignKey +from .base import Base + + +class Category(Base): + id = Column(Integer, primary_key=True) + category_id = Column(Integer, unique=True) + name = Column(String) + type = Column(String) + # category = relationship("Button", back_populates="category") + + +class Button(Base): + id = Column(Integer, primary_key=True) + name = Column(String) + category_id = Column(Integer, ForeignKey(Category.id)) + icon = Column(String) diff --git a/services_backend/routes/__init__.py b/services_backend/routes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/services_backend/routes/base.py b/services_backend/routes/base.py new file mode 100644 index 0000000..7800227 --- /dev/null +++ b/services_backend/routes/base.py @@ -0,0 +1,28 @@ +from fastapi import FastAPI + +from ..settings import Settings +from .button import button +from .category import category +from fastapi.middleware.cors import CORSMiddleware +from fastapi_sqlalchemy import DBSessionMiddleware + +# Насрано вот тут +settings = Settings() +app = FastAPI(debug=True) +app.mount("/button", button) +app.mount("/category", category) + + +app.add_middleware( + DBSessionMiddleware, + db_url=settings.DB_DSN, + session_args={"autocommit": True}, +) + +app.add_middleware( + CORSMiddleware, + allow_origins=settings.CORS_ALLOW_ORIGINS, + allow_credentials=settings.CORS_ALLOW_CREDENTIALS, + allow_methods=settings.CORS_ALLOW_METHODS, + allow_headers=settings.CORS_ALLOW_HEADERS, +) diff --git a/services_backend/routes/button.py b/services_backend/routes/button.py new file mode 100644 index 0000000..5d4b072 --- /dev/null +++ b/services_backend/routes/button.py @@ -0,0 +1,61 @@ +from fastapi import HTTPException, APIRouter, Depends +from sqlalchemy.orm import Session + +from .models.button import ButtonCreate, ButtonUpdate, ButtonGet +from ..models.database import Button + +button = APIRouter( + prefix="/button", + tags=["button"], + responses={404: {"description": "You tried, but no"}} +) + + +# Да, без этого говна ничего не работает, я честно пытался +def get_db(): + db = Session() + try: + yield db + finally: + db.close() + + +@button.post("/", response_model=ButtonCreate) +def create_button(button: ButtonCreate, db: Session = Depends(get_db)): + db_button = ButtonCreate(category_id=button.category_id, name=button.name, + icon=button.icon) + db.add(db_button) + db.commit() + db.refresh(db_button) + db.close() + + return db_button + + +@button.get("/", response_model=list[ButtonGet]) +def get_buttons(skip: int, limit: int, db: Session = Depends(get_db)): + return db.query(Button).offset(skip).limit(limit).all() + + +@button.get("/{button_id}", response_model=ButtonGet) +def get_button(button_id: int, db: Session = Depends(get_db)): + return db.query(Button).filter(Button.id == button_id).first() + + +@button.delete("/") +def remove_button(button_id: int, db: Session = Depends(get_db)): + db_button = get_button(button_id=button_id, db=db) + if db_button is None: + raise HTTPException(status_code=404, detail="Button does not exist") + delete = db.query(Button).filter(Button.id == button_id).first() + db.delete(delete) + db.commit() + db.close() + + +@button.patch("/", response_model=ButtonUpdate) +def update_button(button: ButtonUpdate, db: Session = Depends(get_db)): + db_old_button = get_button(button_id=button.id, db=db) + if db_old_button is None: + raise HTTPException(status_code=404, detail="Button does not exist") + return db.query(Button).update(button) diff --git a/services_backend/routes/category.py b/services_backend/routes/category.py new file mode 100644 index 0000000..586b8be --- /dev/null +++ b/services_backend/routes/category.py @@ -0,0 +1,68 @@ +from fastapi import HTTPException, APIRouter, Depends +from sqlalchemy.orm import Session + +from .models.category import CategoryCreate, CategoryUpdate, CategoryGet +from ..models.database import Category, Button + +category = APIRouter( + prefix="/category", + tags=["category"], + responses={404: {"description": "You tried, but no"}} +) + + +# Да, без этого говна ничего не работает, я честно пытался +def get_db(): + db = Session() + try: + yield db + finally: + db.close() + + +@category.post("/", response_model=CategoryCreate) +def create_category(category: CategoryCreate, db: Session = Depends(get_db)): + db_category = CategoryCreate(category_id=category.category_id, + type=category.type, name=category.name) + db.add(db_category) + db.commit() + db.refresh(db_category) + db.close() + + return db_category + + +@category.get("/", response_model=list[CategoryGet]) +def get_categories(db: Session = Depends(get_db), skip: int = 0, limit: int = 100): + return db.query(Category).offset(skip).limit(limit).all() + + +@category.get("/{category_id}", response_model=CategoryGet) +def get_category(category_id: int, db: Session = Depends(get_db)): + db_category = db.query(Category).filter(Category.id == category_id).first() + if db_category is None: + raise HTTPException(status_code=404, detail="Category does not exist") + return db_category + + +@category.delete("/") +def remove_category(category_id: int, db: Session = Depends(get_db)): + db_category = get_category(db=db, category_id=category_id) + if db_category is None: + raise HTTPException(status_code=404, detail="Category does not exist") + delete = db.query(Category).filter(Category.id == category_id).first() + d = db.query(Button).filter(Button.category_id == category_id).all() + for button in d: + db.delete(button) + db.commit() + db.delete(delete) + db.commit() + db.close() + + +@category.patch("/", response_model=CategoryUpdate) +def update_category(category: CategoryUpdate, db: Session = Depends(get_db)): + db_old_category = get_category(db=db, category_id=category.id) + if db_old_category is None: + raise HTTPException(status_code=404, detail="Category does not exist") + return db.query(Category).update(category) diff --git a/services_backend/routes/models/__init__.py b/services_backend/routes/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/services_backend/routes/models/base.py b/services_backend/routes/models/base.py new file mode 100644 index 0000000..05d0cdb --- /dev/null +++ b/services_backend/routes/models/base.py @@ -0,0 +1,12 @@ +from pydantic import BaseModel + + +class Base(BaseModel): + def __repr__(self) -> str: + attrs = [] + for k, v in self.__class__.schema().items(): + attrs.append(f"{k}={v}") + return "{}({})".format(self.__class__.__name__, ', '.join(attrs)) + + class Config: + orm_mode = True \ No newline at end of file diff --git a/services_backend/routes/models/button.py b/services_backend/routes/models/button.py new file mode 100644 index 0000000..ce96cac --- /dev/null +++ b/services_backend/routes/models/button.py @@ -0,0 +1,19 @@ +from .base import Base +from typing import Optional + + +class ButtonCreate(Base): + category_id: int + icon: Optional[str] + name: Optional[str] + + +class ButtonUpdate(Base): + id: int + category_id: Optional[int] + icon: Optional[str] + name: Optional[str] + + +class ButtonGet(Base): + id: int diff --git a/services_backend/routes/models/category.py b/services_backend/routes/models/category.py new file mode 100644 index 0000000..aa79a13 --- /dev/null +++ b/services_backend/routes/models/category.py @@ -0,0 +1,20 @@ +from .base import Base +from typing import Optional + + +class CategoryCreate(Base): + category_id: int + type: Optional[str] + name: Optional[str] + + +class CategoryUpdate(Base): + id: int + category_id: Optional[int] + type: Optional[str] + name: Optional[str] + + +class CategoryGet(Base): + id: int + diff --git a/services_backend/settings.py b/services_backend/settings.py new file mode 100644 index 0000000..fb6392d --- /dev/null +++ b/services_backend/settings.py @@ -0,0 +1,24 @@ +from pydantic import BaseSettings, PostgresDsn +from functools import lru_cache + + +class Settings(BaseSettings): + """Application settings""" + DB_DSN: PostgresDsn = f'postgresql://postgres:123@localhost:5432/Profcom' + + CORS_ALLOW_ORIGINS: list[str] = ['*'] + CORS_ALLOW_CREDENTIALS: bool = True + CORS_ALLOW_METHODS: list[str] = ['*'] + CORS_ALLOW_HEADERS: list[str] = ['*'] + + class Config: + """Pydantic BaseSettings config""" + + case_sensitive = True + env_file = ".env" + + +@lru_cache +def get_settings() -> Settings: + settings = Settings() + return settings diff --git a/services_backend/utils/__init__.py b/services_backend/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/__init__.py b/trash/__init__.py similarity index 100% rename from __init__.py rename to trash/__init__.py diff --git a/buttons.json b/trash/buttons.json similarity index 97% rename from buttons.json rename to trash/buttons.json index ce43b52..ecfabcf 100644 --- a/buttons.json +++ b/trash/buttons.json @@ -1,75 +1,75 @@ -[ - { - "name": "Полезное", - "type": "grid3", - "items": [ - { - "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/printer.svg"}, - "text": "Бесплатный принтер", - "path": "https://printer.ui.profcomff.com" - }, - { - "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/fifth_floor.svg"}, - "text": "Схема этажей", - "path": "https://cdn.profcomff.com/app/map/" - } - ] - }, - { - "name": "Сервисы", - "type": "grid3", - "items": [ - { - "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/write_us.svg"}, - "text": "Написать в профком", - "path": "https://vk.me/profcomff" - }, - { - "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/study_office.svg"}, - "text": "Учебная часть", - "path": "https://phys.msu.ru/rus/students/obshaja_infa.php#:~:text=%D0%98%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%86%D0%B8%D1%8F%20%D1%83%D1%87%D0%B5%D0%B1%D0%BD%D0%BE%D0%B3%D0%BE%20%D0%BE%D1%82%D0%B4%D0%B5%D0%BB%D0%B0" - }, - { - "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/courses.svg"}, - "text": "МФК", - "path": "https://lk.msu.ru/course" - }, - { - "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/report.svg"}, - "text": "Жалоба", - "path": "https://vk.com/app5619682_-24234717" - }, - { - "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/money_help.svg"}, - "text": "Материальная помощь", - "path": "https://vk.com/profcomff?w=page-24234717_51953473" - }, - { - "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/join.svg"}, - "text": "Вступить в профсоюз", - "path": "https://lk.msuprof.com/" - } - ] - }, - { - "name": "Информация", - "type": "list", - "items": [ - { - "icon": {"src": "install_mobile"}, - "text": "Установить приложение", - "path": "https://pages.profcomff.com/installation" - }, - { - "icon": {"src": "feedback"}, - "text": "Обратная связь", - "path": "https://forms.yandex.ru/u/630f979143537dde00621b0b/" - }, - { - "icon": {"src": "info"}, - "text": "О приложении", - "path": "https://pages.profcomff.com/about" - } - ] - } +[ + { + "name": "Полезное", + "type": "grid3", + "items": [ + { + "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/printer.svg"}, + "text": "Бесплатный принтер", + "path": "https://printer.ui.profcomff.com" + }, + { + "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/fifth_floor.svg"}, + "text": "Схема этажей", + "path": "https://cdn.profcomff.com/app/map/" + } + ] + }, + { + "name": "Сервисы", + "type": "grid3", + "items": [ + { + "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/write_us.svg"}, + "text": "Написать в профком", + "path": "https://vk.me/profcomff" + }, + { + "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/study_office.svg"}, + "text": "Учебная часть", + "path": "https://phys.msu.ru/rus/students/obshaja_infa.php#:~:text=%D0%98%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%86%D0%B8%D1%8F%20%D1%83%D1%87%D0%B5%D0%B1%D0%BD%D0%BE%D0%B3%D0%BE%20%D0%BE%D1%82%D0%B4%D0%B5%D0%BB%D0%B0" + }, + { + "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/courses.svg"}, + "text": "МФК", + "path": "https://lk.msu.ru/course" + }, + { + "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/report.svg"}, + "text": "Жалоба", + "path": "https://vk.com/app5619682_-24234717" + }, + { + "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/money_help.svg"}, + "text": "Материальная помощь", + "path": "https://vk.com/profcomff?w=page-24234717_51953473" + }, + { + "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/join.svg"}, + "text": "Вступить в профсоюз", + "path": "https://lk.msuprof.com/" + } + ] + }, + { + "name": "Информация", + "type": "list", + "items": [ + { + "icon": {"src": "install_mobile"}, + "text": "Установить приложение", + "path": "https://pages.profcomff.com/installation" + }, + { + "icon": {"src": "feedback"}, + "text": "Обратная связь", + "path": "https://forms.yandex.ru/u/630f979143537dde00621b0b/" + }, + { + "icon": {"src": "info"}, + "text": "О приложении", + "path": "https://pages.profcomff.com/about" + } + ] + } ] \ No newline at end of file diff --git a/create_database.py b/trash/create_database.py similarity index 95% rename from create_database.py rename to trash/create_database.py index 016b9ee..f8ae4dc 100644 --- a/create_database.py +++ b/trash/create_database.py @@ -1,67 +1,67 @@ -# This Python file uses the following encoding: utf-8 - -from faker import Faker -from models.buttons import Button -from models.categories import Category -from fastapi import Depends -from __init__ import get_db, Session -import json - - -def _load_fake_data(session: Session = Depends(get_db)): - with open('buttons.json', 'r', encoding="utf8") as f: - data = json.load(f) - - category1 = Category(id=1, category_id=1, name=data[0]['name'], type=data[0]['type']) # Категория - Полезное - category2 = Category(id=2, category_id=2, name=data[1]['name'], type=data[1]['type']) # Категория - Сервисы - category3 = Category(id=3, category_id=3, name=data[2]['name'], type=data[2]['type']) # Категория - Информация - - # Категория - Полезное - button1 = Button(id=1, name=data[0]['items'][0]['text'], icon=data[0]['items'][0]['icon']["src"], # Полезное - category_id=1) - button2 = Button(id=2, name=data[0]['items'][1]['text'], icon=data[0]['items'][1]['icon']["src"], # Этажи - category_id=1) - - # Категория - Сервисы - button3 = Button(id=3, name=data[1]['items'][0]['text'], icon=data[1]['items'][0]['icon']["src"], # Написать - category_id=2) - button4 = Button(id=4, name=data[1]['items'][1]['text'], icon=data[1]['items'][1]['icon']["src"], # Учебная часть - category_id=2) - button5 = Button(id=5, name=data[1]['items'][2]['text'], icon=data[1]['items'][2]['icon']["src"], # МФК - category_id=2) - button6 = Button(id=6, name=data[1]['items'][3]['text'], icon=data[1]['items'][3]['icon']["src"], # Жалоба - category_id=2) - button7 = Button(id=7, name=data[1]['items'][4]['text'], icon=data[1]['items'][4]['icon']["src"], # Мат. помощь - category_id=2) - button8 = Button(id=8, name=data[1]['items'][5]['text'], icon=data[1]['items'][5]['icon']["src"], # Профсоюз - category_id=2) - - # Категория - Информация - button9 = Button(id=9, name=data[2]['items'][0]['text'], icon=data[2]['items'][0]['icon']["src"], # Приложение - category_id=3) - button10 = Button(id=10, name=data[2]['items'][1]['text'], icon=data[2]['items'][1]['icon']["src"], # Обратная связь - category_id=3) - button11 = Button(id=11, name=data[2]['items'][2]['text'], icon=data[2]['items'][2]['icon']["src"], # О приложении - category_id=3) - - faker = Faker('ru_RU') - - session.add(category1) - session.add(category2) - session.add(category3) - session.add(button1) - session.add(button2) - session.add(button3) - session.add(button4) - session.add(button5) - session.add(button6) - session.add(button7) - session.add(button8) - session.add(button9) - session.add(button10) - session.add(button11) - - session.commit() - - -_load_fake_data(Session()) +# This Python file uses the following encoding: utf-8 + +from faker import Faker +from trash.models.buttons import Button +from trash.models.categories import Category +from fastapi import Depends +from __init__ import get_db, Session +import json + + +def _load_fake_data(session: Session = Depends(get_db)): + with open('buttons.json', 'r', encoding="utf8") as f: + data = json.load(f) + + category1 = Category(id=1, category_id=1, name=data[0]['name'], type=data[0]['type']) # Категория - Полезное + category2 = Category(id=2, category_id=2, name=data[1]['name'], type=data[1]['type']) # Категория - Сервисы + category3 = Category(id=3, category_id=3, name=data[2]['name'], type=data[2]['type']) # Категория - Информация + + # Категория - Полезное + button1 = Button(id=1, name=data[0]['items'][0]['text'], icon=data[0]['items'][0]['icon']["src"], # Полезное + category_id=1) + button2 = Button(id=2, name=data[0]['items'][1]['text'], icon=data[0]['items'][1]['icon']["src"], # Этажи + category_id=1) + + # Категория - Сервисы + button3 = Button(id=3, name=data[1]['items'][0]['text'], icon=data[1]['items'][0]['icon']["src"], # Написать + category_id=2) + button4 = Button(id=4, name=data[1]['items'][1]['text'], icon=data[1]['items'][1]['icon']["src"], # Учебная часть + category_id=2) + button5 = Button(id=5, name=data[1]['items'][2]['text'], icon=data[1]['items'][2]['icon']["src"], # МФК + category_id=2) + button6 = Button(id=6, name=data[1]['items'][3]['text'], icon=data[1]['items'][3]['icon']["src"], # Жалоба + category_id=2) + button7 = Button(id=7, name=data[1]['items'][4]['text'], icon=data[1]['items'][4]['icon']["src"], # Мат. помощь + category_id=2) + button8 = Button(id=8, name=data[1]['items'][5]['text'], icon=data[1]['items'][5]['icon']["src"], # Профсоюз + category_id=2) + + # Категория - Информация + button9 = Button(id=9, name=data[2]['items'][0]['text'], icon=data[2]['items'][0]['icon']["src"], # Приложение + category_id=3) + button10 = Button(id=10, name=data[2]['items'][1]['text'], icon=data[2]['items'][1]['icon']["src"], # Обратная связь + category_id=3) + button11 = Button(id=11, name=data[2]['items'][2]['text'], icon=data[2]['items'][2]['icon']["src"], # О приложении + category_id=3) + + faker = Faker('ru_RU') + + session.add(category1) + session.add(category2) + session.add(category3) + session.add(button1) + session.add(button2) + session.add(button3) + session.add(button4) + session.add(button5) + session.add(button6) + session.add(button7) + session.add(button8) + session.add(button9) + session.add(button10) + session.add(button11) + + session.commit() + + +_load_fake_data(Session()) diff --git a/trash/database.py b/trash/database.py deleted file mode 100644 index b31bc12..0000000 --- a/trash/database.py +++ /dev/null @@ -1,21 +0,0 @@ -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker -from sqlalchemy.ext.declarative import declarative_base -from dotenv import load_dotenv -import os - -load_dotenv() -path = os.getenv('SQL_DATABASE_PATH') -engine = create_engine(path) -Session = sessionmaker(bind=engine) -Base = declarative_base() - - -def create_session(): - Session = sessionmaker(bind=engine) - - return Session - - -def create_db(): - Base.metadata.create_all(engine) diff --git a/methods.py b/trash/methods.py similarity index 83% rename from methods.py rename to trash/methods.py index 81dc29e..cddcbd7 100644 --- a/methods.py +++ b/trash/methods.py @@ -1,5 +1,5 @@ from sqlalchemy.orm import Session -from models import CRUD, buttons, categories +from trash.models import buttons, categories, CRUD def get_button(db: Session, button_id: int): @@ -61,13 +61,14 @@ def delete_category(db: Session, category_id: int): def update_button(db: Session, button_id: int, button: CRUD.Button): - old_button = db.query(buttons.Button).filter(buttons.Button.id == button_id).first() - new_button = buttons.Button(id=button.id, category_id=button.category_id, - name=button.name, icon=button.icon) - delete_button(db=db, button_id=old_button.id) - create_button(db=db, button=new_button) - - return new_button + # old_button = db.query(buttons.Button).filter(buttons.Button.id == button_id).first() + # new_button = buttons.Button(id=button.id, category_id=button.category_id, + # name=button.name, icon=button.icon) + # delete_button(db=db, button_id=old_button.id) + # create_button(db=db, button=new_button) + db.query(buttons.Button).update(button) + + return button def update_category(db: Session, category_id: int, category: CRUD.Category): diff --git a/models/CRUD.py b/trash/models/CRUD.py similarity index 100% rename from models/CRUD.py rename to trash/models/CRUD.py diff --git a/models/buttons.py b/trash/models/buttons.py similarity index 90% rename from models/buttons.py rename to trash/models/buttons.py index ab0b634..5315f26 100644 --- a/models/buttons.py +++ b/trash/models/buttons.py @@ -1,17 +1,17 @@ -from sqlalchemy import Column, Integer, String, ForeignKey -from sqlalchemy.ext.declarative import declarative_base -from models.categories import Category - - -class Button(declarative_base()): - __tablename__ = 'buttons' - - id = Column(Integer, primary_key=True) - name = Column(String) - category_id = Column(Integer, ForeignKey(Category.id)) - icon = Column(String) - - def __repr__(self): - return f'Кнопка {self.name} (id: {self.id}):\n'\ - f'Находится в категории {self.category_id}\n' \ - f'Иконка: {self.icon}' +from sqlalchemy import Column, Integer, String, ForeignKey +from sqlalchemy.ext.declarative import declarative_base +from trash.models.categories import Category + + +class Button(declarative_base()): + __tablename__ = 'buttons' + + id = Column(Integer, primary_key=True) + name = Column(String) + category_id = Column(Integer, ForeignKey(Category.id)) + icon = Column(String) + + def __repr__(self): + return f'Кнопка {self.name} (id: {self.id}):\n'\ + f'Находится в категории {self.category_id}\n' \ + f'Иконка: {self.icon}' diff --git a/models/categories.py b/trash/models/categories.py similarity index 70% rename from models/categories.py rename to trash/models/categories.py index 4c02433..4db88a0 100644 --- a/models/categories.py +++ b/trash/models/categories.py @@ -6,10 +6,10 @@ class Category(declarative_base()): __tablename__ = 'category' - id = Column('id', Integer, primary_key=True) - category_id = Column('category_id', Integer, unique=True) - name = Column('name', String) - type = Column('type', String) + id = Column(Integer, primary_key=True) + category_id = Column(Integer, unique=True) + name = Column(String) + type = Column(String) # category = relationship("Button", back_populates="category") def __repr__(self): From 0f5b0df054998aae6b21a3fc6934950d622bd748 Mon Sep 17 00:00:00 2001 From: Wudext Date: Fri, 25 Nov 2022 13:00:46 +0300 Subject: [PATCH 17/36] Update .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index b6e4761..afa1b7d 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,5 @@ dmypy.json # Pyre type checker .pyre/ + +desktop.ini \ No newline at end of file From 041eae8b98af3e501ec5f53b270ee49403acb25e Mon Sep 17 00:00:00 2001 From: Wudext <116914558+Wudext@users.noreply.github.com> Date: Fri, 25 Nov 2022 13:29:52 +0300 Subject: [PATCH 18/36] Delete .idea directory --- .idea/.gitignore | 3 -- .idea/inspectionProfiles/Project_Default.xml | 28 ------------------- .../inspectionProfiles/profiles_settings.xml | 6 ---- .idea/misc.xml | 4 --- .idea/modules.xml | 8 ------ .idea/services-api.iml | 15 ---------- .idea/vcs.xml | 6 ---- 7 files changed, 70 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 .idea/inspectionProfiles/profiles_settings.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/services-api.iml delete mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index fd95cf1..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 105ce2d..0000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index dc9ea49..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 46ca921..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/services-api.iml b/.idea/services-api.iml deleted file mode 100644 index 6ae7352..0000000 --- a/.idea/services-api.iml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 8ac4c8428254c13d2156fcfeae46812775cb54af Mon Sep 17 00:00:00 2001 From: Wudext <116914558+Wudext@users.noreply.github.com> Date: Fri, 25 Nov 2022 13:31:10 +0300 Subject: [PATCH 19/36] Delete trash directory --- trash/__init__.py | 28 -------------- trash/buttons.json | 75 -------------------------------------- trash/create_database.py | 67 ---------------------------------- trash/models/CRUD.py | 22 ----------- trash/models/buttons.py | 17 --------- trash/models/categories.py | 18 --------- 6 files changed, 227 deletions(-) delete mode 100644 trash/__init__.py delete mode 100644 trash/buttons.json delete mode 100644 trash/create_database.py delete mode 100644 trash/models/CRUD.py delete mode 100644 trash/models/buttons.py delete mode 100644 trash/models/categories.py diff --git a/trash/__init__.py b/trash/__init__.py deleted file mode 100644 index f4e2808..0000000 --- a/trash/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker -from sqlalchemy.ext.declarative import declarative_base -from fastapi import FastAPI -from dotenv import load_dotenv -import os - -db = None -load_dotenv() -login = os.getenv('SQL_LOGIN') -password = os.getenv('SQL_PASSWORD') -host = os.getenv('SQL_HOST') -port = int(os.getenv('SQL_PORT')) -tablespace = os.getenv('SQL_TABLESPACE') -path = f'postgresql://{login}:{password}@{host}:{port}/{tablespace}' -engine = create_engine(path) -Session = sessionmaker(bind=engine) -Base = declarative_base() -Base.metadata.create_all(bind=create_engine(path)) -app = FastAPI() - - -def get_db(): - db = Session(bind=engine) - try: - yield db - finally: - db.close() diff --git a/trash/buttons.json b/trash/buttons.json deleted file mode 100644 index ecfabcf..0000000 --- a/trash/buttons.json +++ /dev/null @@ -1,75 +0,0 @@ -[ - { - "name": "Полезное", - "type": "grid3", - "items": [ - { - "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/printer.svg"}, - "text": "Бесплатный принтер", - "path": "https://printer.ui.profcomff.com" - }, - { - "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/fifth_floor.svg"}, - "text": "Схема этажей", - "path": "https://cdn.profcomff.com/app/map/" - } - ] - }, - { - "name": "Сервисы", - "type": "grid3", - "items": [ - { - "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/write_us.svg"}, - "text": "Написать в профком", - "path": "https://vk.me/profcomff" - }, - { - "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/study_office.svg"}, - "text": "Учебная часть", - "path": "https://phys.msu.ru/rus/students/obshaja_infa.php#:~:text=%D0%98%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%86%D0%B8%D1%8F%20%D1%83%D1%87%D0%B5%D0%B1%D0%BD%D0%BE%D0%B3%D0%BE%20%D0%BE%D1%82%D0%B4%D0%B5%D0%BB%D0%B0" - }, - { - "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/courses.svg"}, - "text": "МФК", - "path": "https://lk.msu.ru/course" - }, - { - "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/report.svg"}, - "text": "Жалоба", - "path": "https://vk.com/app5619682_-24234717" - }, - { - "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/money_help.svg"}, - "text": "Материальная помощь", - "path": "https://vk.com/profcomff?w=page-24234717_51953473" - }, - { - "icon": {"src": "https://cdn.profcomff.com/app/menu_icons/join.svg"}, - "text": "Вступить в профсоюз", - "path": "https://lk.msuprof.com/" - } - ] - }, - { - "name": "Информация", - "type": "list", - "items": [ - { - "icon": {"src": "install_mobile"}, - "text": "Установить приложение", - "path": "https://pages.profcomff.com/installation" - }, - { - "icon": {"src": "feedback"}, - "text": "Обратная связь", - "path": "https://forms.yandex.ru/u/630f979143537dde00621b0b/" - }, - { - "icon": {"src": "info"}, - "text": "О приложении", - "path": "https://pages.profcomff.com/about" - } - ] - } -] \ No newline at end of file diff --git a/trash/create_database.py b/trash/create_database.py deleted file mode 100644 index f8ae4dc..0000000 --- a/trash/create_database.py +++ /dev/null @@ -1,67 +0,0 @@ -# This Python file uses the following encoding: utf-8 - -from faker import Faker -from trash.models.buttons import Button -from trash.models.categories import Category -from fastapi import Depends -from __init__ import get_db, Session -import json - - -def _load_fake_data(session: Session = Depends(get_db)): - with open('buttons.json', 'r', encoding="utf8") as f: - data = json.load(f) - - category1 = Category(id=1, category_id=1, name=data[0]['name'], type=data[0]['type']) # Категория - Полезное - category2 = Category(id=2, category_id=2, name=data[1]['name'], type=data[1]['type']) # Категория - Сервисы - category3 = Category(id=3, category_id=3, name=data[2]['name'], type=data[2]['type']) # Категория - Информация - - # Категория - Полезное - button1 = Button(id=1, name=data[0]['items'][0]['text'], icon=data[0]['items'][0]['icon']["src"], # Полезное - category_id=1) - button2 = Button(id=2, name=data[0]['items'][1]['text'], icon=data[0]['items'][1]['icon']["src"], # Этажи - category_id=1) - - # Категория - Сервисы - button3 = Button(id=3, name=data[1]['items'][0]['text'], icon=data[1]['items'][0]['icon']["src"], # Написать - category_id=2) - button4 = Button(id=4, name=data[1]['items'][1]['text'], icon=data[1]['items'][1]['icon']["src"], # Учебная часть - category_id=2) - button5 = Button(id=5, name=data[1]['items'][2]['text'], icon=data[1]['items'][2]['icon']["src"], # МФК - category_id=2) - button6 = Button(id=6, name=data[1]['items'][3]['text'], icon=data[1]['items'][3]['icon']["src"], # Жалоба - category_id=2) - button7 = Button(id=7, name=data[1]['items'][4]['text'], icon=data[1]['items'][4]['icon']["src"], # Мат. помощь - category_id=2) - button8 = Button(id=8, name=data[1]['items'][5]['text'], icon=data[1]['items'][5]['icon']["src"], # Профсоюз - category_id=2) - - # Категория - Информация - button9 = Button(id=9, name=data[2]['items'][0]['text'], icon=data[2]['items'][0]['icon']["src"], # Приложение - category_id=3) - button10 = Button(id=10, name=data[2]['items'][1]['text'], icon=data[2]['items'][1]['icon']["src"], # Обратная связь - category_id=3) - button11 = Button(id=11, name=data[2]['items'][2]['text'], icon=data[2]['items'][2]['icon']["src"], # О приложении - category_id=3) - - faker = Faker('ru_RU') - - session.add(category1) - session.add(category2) - session.add(category3) - session.add(button1) - session.add(button2) - session.add(button3) - session.add(button4) - session.add(button5) - session.add(button6) - session.add(button7) - session.add(button8) - session.add(button9) - session.add(button10) - session.add(button11) - - session.commit() - - -_load_fake_data(Session()) diff --git a/trash/models/CRUD.py b/trash/models/CRUD.py deleted file mode 100644 index f98a0af..0000000 --- a/trash/models/CRUD.py +++ /dev/null @@ -1,22 +0,0 @@ -from pydantic import BaseModel -from typing import Optional - - -class Category(BaseModel): - id: int - category_id: int - type: Optional[str] - name: Optional[str] - - class Config: - orm_mode = True - - -class Button(BaseModel): - id: int - category_id: int - name: Optional[str] - icon: Optional[str] - - class Config: - orm_mode = True diff --git a/trash/models/buttons.py b/trash/models/buttons.py deleted file mode 100644 index 5315f26..0000000 --- a/trash/models/buttons.py +++ /dev/null @@ -1,17 +0,0 @@ -from sqlalchemy import Column, Integer, String, ForeignKey -from sqlalchemy.ext.declarative import declarative_base -from trash.models.categories import Category - - -class Button(declarative_base()): - __tablename__ = 'buttons' - - id = Column(Integer, primary_key=True) - name = Column(String) - category_id = Column(Integer, ForeignKey(Category.id)) - icon = Column(String) - - def __repr__(self): - return f'Кнопка {self.name} (id: {self.id}):\n'\ - f'Находится в категории {self.category_id}\n' \ - f'Иконка: {self.icon}' diff --git a/trash/models/categories.py b/trash/models/categories.py deleted file mode 100644 index 4db88a0..0000000 --- a/trash/models/categories.py +++ /dev/null @@ -1,18 +0,0 @@ -from sqlalchemy import Column, String, Integer -from sqlalchemy.orm import relationship -from sqlalchemy.ext.declarative import declarative_base - - -class Category(declarative_base()): - __tablename__ = 'category' - - id = Column(Integer, primary_key=True) - category_id = Column(Integer, unique=True) - name = Column(String) - type = Column(String) - # category = relationship("Button", back_populates="category") - - def __repr__(self): - return f'Категория {self.name}. Тип: {self.type}\n' \ - f'ID: {self.category_id}' - From 40f06a775596987dd554793669abd80867e3e686 Mon Sep 17 00:00:00 2001 From: Wudext Date: Fri, 25 Nov 2022 20:44:02 +0300 Subject: [PATCH 20/36] Cleaning --- .gitignore | 1 + services-api/.gitignore | 129 ------------------ services-api/Dockerfile | 11 -- services-api/LICENSE | 29 ---- services-api/Makefile | 8 -- services-api/README.md | 27 ---- services-api/alembic.ini | 102 -------------- services-api/flake8.conf | 35 ----- services-api/gunicorn_conf.py | 67 --------- services-api/migrations/README | 1 - services-api/migrations/env.py | 81 ----------- services-api/migrations/script.py.mako | 24 ---- services-api/migrations/versions/.gitkeep | 0 services-api/pyproject.toml | 22 --- services-api/requirements.txt | 8 -- services-api/services-backend/__init__.py | 0 services-api/services-backend/__main__.py | 6 - services-api/services-backend/exceptions.py | 0 .../services-backend/models/__init__.py | 0 services-api/services-backend/models/base.py | 22 --- .../services-backend/models/database.py | 16 --- .../services-backend/routes/__init__.py | 0 services-api/services-backend/routes/base.py | 22 --- .../services-backend/routes/button.py | 0 .../services-backend/routes/category.py | 0 .../routes/models/__init__.py | 0 .../services-backend/routes/models/base.py | 12 -- .../services-backend/routes/models/models.py | 0 services-api/services-backend/settings.py | 24 ---- .../services-backend/utils/__init__.py | 0 30 files changed, 1 insertion(+), 646 deletions(-) delete mode 100644 services-api/.gitignore delete mode 100644 services-api/Dockerfile delete mode 100644 services-api/LICENSE delete mode 100644 services-api/Makefile delete mode 100644 services-api/README.md delete mode 100644 services-api/alembic.ini delete mode 100644 services-api/flake8.conf delete mode 100644 services-api/gunicorn_conf.py delete mode 100644 services-api/migrations/README delete mode 100644 services-api/migrations/env.py delete mode 100644 services-api/migrations/script.py.mako delete mode 100644 services-api/migrations/versions/.gitkeep delete mode 100644 services-api/pyproject.toml delete mode 100644 services-api/requirements.txt delete mode 100644 services-api/services-backend/__init__.py delete mode 100644 services-api/services-backend/__main__.py delete mode 100644 services-api/services-backend/exceptions.py delete mode 100644 services-api/services-backend/models/__init__.py delete mode 100644 services-api/services-backend/models/base.py delete mode 100644 services-api/services-backend/models/database.py delete mode 100644 services-api/services-backend/routes/__init__.py delete mode 100644 services-api/services-backend/routes/base.py delete mode 100644 services-api/services-backend/routes/button.py delete mode 100644 services-api/services-backend/routes/category.py delete mode 100644 services-api/services-backend/routes/models/__init__.py delete mode 100644 services-api/services-backend/routes/models/base.py delete mode 100644 services-api/services-backend/routes/models/models.py delete mode 100644 services-api/services-backend/settings.py delete mode 100644 services-api/services-backend/utils/__init__.py diff --git a/.gitignore b/.gitignore index afa1b7d..ebc9451 100644 --- a/.gitignore +++ b/.gitignore @@ -106,6 +106,7 @@ celerybeat.pid .venv env/ venv/ +.idea ENV/ env.bak/ venv.bak/ diff --git a/services-api/.gitignore b/services-api/.gitignore deleted file mode 100644 index b6e4761..0000000 --- a/services-api/.gitignore +++ /dev/null @@ -1,129 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -.python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ diff --git a/services-api/Dockerfile b/services-api/Dockerfile deleted file mode 100644 index cbec1e2..0000000 --- a/services-api/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM python:3.10 -WORKDIR /app - -COPY ./requirements.txt /app/ -RUN pip install --no-cache-dir -r /app/requirements.txt - -ADD gunicorn_conf.py alembic.ini /app/ -ADD migrations /app/migrations -ADD services-backend /app/services-backend - -CMD [ "gunicorn", "-k", "uvicorn.workers.UvicornWorker", "-c", "/app/gunicorn_conf.py", "services-backend.routes.base:app" ] diff --git a/services-api/LICENSE b/services-api/LICENSE deleted file mode 100644 index 5494ac6..0000000 --- a/services-api/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -BSD 3-Clause License - -Copyright (c) 2022, Профком студентов физфака МГУ -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/services-api/Makefile b/services-api/Makefile deleted file mode 100644 index 45cd466..0000000 --- a/services-api/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -run: - source ./venv/bin/activate && uvicorn --reload --log-level debug services-backend.routes.base:app - -db: - docker run -d -p 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust --name db-services-backend postgres:15 - -migrate: db - alembic upgrade head diff --git a/services-api/README.md b/services-api/README.md deleted file mode 100644 index 4e7d1c2..0000000 --- a/services-api/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# services-api - -Бэкэдн сервисов приложения Твой ФФ для профкома ФФ МГУ - -## Запуск - -1) Перейдите в папку проекта - -2) Создайте виртуальное окружение командой: -```console -foo@bar:~$ python3 -m venv ./venv/ -``` - -3) Установите библиотеки -```console -foo@bar:~$ pip install -m requirements.txt -``` -4) Запускайте приложение! -```console -foo@bar:~$ python -m services-backend -``` - -## ENV-file description - -DB_DSN= - ---- diff --git a/services-api/alembic.ini b/services-api/alembic.ini deleted file mode 100644 index f6c8899..0000000 --- a/services-api/alembic.ini +++ /dev/null @@ -1,102 +0,0 @@ -# A generic, single database configuration. - -[alembic] -# path to migration scripts -script_location = migrations - -# template used to generate migration files -# file_template = %%(rev)s_%%(slug)s - -# sys.path path, will be prepended to sys.path if present. -# defaults to the current working directory. -prepend_sys_path = . - -# timezone to use when rendering the date within the migration file -# as well as the filename. -# If specified, requires the python-dateutil library that can be -# installed by adding `alembic[tz]` to the pip requirements -# string value is passed to dateutil.tz.gettz() -# leave blank for localtime -# timezone = - -# max length of characters to apply to the -# "slug" field -# truncate_slug_length = 40 - -# set to 'true' to run the environment during -# the 'revision' command, regardless of autogenerate -# revision_environment = false - -# set to 'true' to allow .pyc and .pyo files without -# a source .py file to be detected as revisions in the -# versions/ directory -# sourceless = false - -# version location specification; This defaults -# to migrations/versions. When using multiple version -# directories, initial revisions must be specified with --version-path. -# The path separator used here should be the separator specified by "version_path_separator" below. -# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions - -# version path separator; As mentioned above, this is the character used to split -# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. -# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. -# Valid values for version_path_separator are: -# -# version_path_separator = : -# version_path_separator = ; -# version_path_separator = space -version_path_separator = os # Use os.pathsep. Default configuration used for new projects. - -# the output encoding used when revision files -# are written from script.py.mako -# output_encoding = utf-8 - -sqlalchemy.url = driver://user:pass@localhost/dbname - - -[post_write_hooks] -# post_write_hooks defines scripts or Python functions that are run -# on newly generated revision scripts. See the documentation for further -# detail and examples - -# format using "black" - use the console_scripts runner, against the "black" entrypoint -# hooks = black -# black.type = console_scripts -# black.entrypoint = black -# black.options = -l 79 REVISION_SCRIPT_FILENAME - -# Logging configuration -[loggers] -keys = root,sqlalchemy,alembic - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = WARN -handlers = console -qualname = - -[logger_sqlalchemy] -level = WARN -handlers = -qualname = sqlalchemy.engine - -[logger_alembic] -level = INFO -handlers = -qualname = alembic - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(levelname)-5.5s [%(name)s] %(message)s -datefmt = %H:%M:%S diff --git a/services-api/flake8.conf b/services-api/flake8.conf deleted file mode 100644 index 547208a..0000000 --- a/services-api/flake8.conf +++ /dev/null @@ -1,35 +0,0 @@ -[flake8] -select = - E, W, # pep8 errors and warnings - F, # pyflakes - C9, # McCabe - N8, # Naming Conventions - #B, S, # bandit - #C, # commas - #D, # docstrings - #P, # string-format - #Q, # quotes - -ignore = - E122, # continuation line missing indentation or outdented - E123, # closing bracket does not match indentation of opening bracket's line - E127, # continuation line over-indented for visual indent - E131, # continuation line unaligned for hanging - E203, # whitespace before ':' - E225, # missing whitespace around operator - E226, # missing whitespace around arithmetic operator - E24, # multiple spaces after ',' or tab after ',' - E275, # missing whitespace after keyword - E305, # expected 2 blank lines after end of function or class - E306, # expected 1 blank line before a nested definition - E402, # module level import not at top of file - E722, # do not use bare except, specify exception instead - E731, # do not assign a lambda expression, use a def - E741, # do not use variables named 'l', 'O', or 'I' - - F722, # syntax error in forward annotation - - W503, # line break before binary operator - W504, # line break after binary operator - -max-line-length = 120 \ No newline at end of file diff --git a/services-api/gunicorn_conf.py b/services-api/gunicorn_conf.py deleted file mode 100644 index 7dd141d..0000000 --- a/services-api/gunicorn_conf.py +++ /dev/null @@ -1,67 +0,0 @@ -import json -import multiprocessing -import os - -workers_per_core_str = os.getenv("WORKERS_PER_CORE", "1") -max_workers_str = os.getenv("MAX_WORKERS") -use_max_workers = None -if max_workers_str: - use_max_workers = int(max_workers_str) -web_concurrency_str = os.getenv("WEB_CONCURRENCY", None) - -host = os.getenv("HOST", "0.0.0.0") -port = os.getenv("PORT", "80") -bind_env = os.getenv("BIND", None) -use_loglevel = os.getenv("LOG_LEVEL", "info") -if bind_env: - use_bind = bind_env -else: - use_bind = f"{host}:{port}" - -cores = multiprocessing.cpu_count() -workers_per_core = float(workers_per_core_str) -default_web_concurrency = workers_per_core * cores -if web_concurrency_str: - web_concurrency = int(web_concurrency_str) - assert web_concurrency > 0 -else: - web_concurrency = max(int(default_web_concurrency), 2) - if use_max_workers: - web_concurrency = min(web_concurrency, use_max_workers) -accesslog_var = os.getenv("ACCESS_LOG", "-") -use_accesslog = accesslog_var or None -errorlog_var = os.getenv("ERROR_LOG", "-") -use_errorlog = errorlog_var or None -graceful_timeout_str = os.getenv("GRACEFUL_TIMEOUT", "120") -timeout_str = os.getenv("TIMEOUT", "120") -keepalive_str = os.getenv("KEEP_ALIVE", "5") - -# Gunicorn config variables -loglevel = use_loglevel -workers = web_concurrency -bind = use_bind -errorlog = use_errorlog -worker_tmp_dir = "/dev/shm" -accesslog = use_accesslog -graceful_timeout = int(graceful_timeout_str) -timeout = int(timeout_str) -keepalive = int(keepalive_str) - - -# For debugging and testing -log_data = { - "loglevel": loglevel, - "workers": workers, - "bind": bind, - "graceful_timeout": graceful_timeout, - "timeout": timeout, - "keepalive": keepalive, - "errorlog": errorlog, - "accesslog": accesslog, - # Additional, non-gunicorn variables - "workers_per_core": workers_per_core, - "use_max_workers": use_max_workers, - "host": host, - "port": port, -} -print(json.dumps(log_data)) diff --git a/services-api/migrations/README b/services-api/migrations/README deleted file mode 100644 index 98e4f9c..0000000 --- a/services-api/migrations/README +++ /dev/null @@ -1 +0,0 @@ -Generic single-database configuration. \ No newline at end of file diff --git a/services-api/migrations/env.py b/services-api/migrations/env.py deleted file mode 100644 index 5bfafb7..0000000 --- a/services-api/migrations/env.py +++ /dev/null @@ -1,81 +0,0 @@ -from logging.config import fileConfig - -from sqlalchemy import engine_from_config -from sqlalchemy import pool - -from alembic import context -from services-backend.models.base import Base -from services-backend.settings import get_settings - -# this is the Alembic Config object, which provides -# access to the values within the .ini file in use. -config = context.config -settings = get_settings() - -# Interpret the config file for Python logging. -# This line sets up loggers basically. -if config.config_file_name is not None: - fileConfig(config.config_file_name) - -# add your model's MetaData object here -# for 'autogenerate' support -# from myapp import mymodel -# target_metadata = mymodel.Base.metadata -target_metadata = Base.metadata - -# other values from the config, defined by the needs of env.py, -# can be acquired: -# my_important_option = config.get_main_option("my_important_option") -# ... etc. - - -def run_migrations_offline(): - """Run migrations in 'offline' mode. - - This configures the context with just a URL - and not an Engine, though an Engine is acceptable - here as well. By skipping the Engine creation - we don't even need a DBAPI to be available. - - Calls to context.execute() here emit the given string to the - script output. - - """ - url = config.get_main_option("sqlalchemy.url") - context.configure( - url=url, - target_metadata=target_metadata, - literal_binds=True, - dialect_opts={"paramstyle": "named"}, - ) - - with context.begin_transaction(): - context.run_migrations() - - -def run_migrations_online(): - """Run migrations in 'online' mode. - - In this scenario we need to create an Engine - and associate a connection with the context. - - """ - configuration = config.get_section(config.config_ini_section) - configuration['sqlalchemy.url'] = settings.DB_DSN - connectable = engine_from_config( - configuration, - prefix="sqlalchemy.", - poolclass=pool.NullPool, - ) - - with connectable.connect() as connection: - context.configure(connection=connection, target_metadata=target_metadata) - - with context.begin_transaction(): - context.run_migrations() - - -if context.is_offline_mode(): - run_migrations_offline() -else: - run_migrations_online() diff --git a/services-api/migrations/script.py.mako b/services-api/migrations/script.py.mako deleted file mode 100644 index 2c01563..0000000 --- a/services-api/migrations/script.py.mako +++ /dev/null @@ -1,24 +0,0 @@ -"""${message} - -Revision ID: ${up_revision} -Revises: ${down_revision | comma,n} -Create Date: ${create_date} - -""" -from alembic import op -import sqlalchemy as sa -${imports if imports else ""} - -# revision identifiers, used by Alembic. -revision = ${repr(up_revision)} -down_revision = ${repr(down_revision)} -branch_labels = ${repr(branch_labels)} -depends_on = ${repr(depends_on)} - - -def upgrade(): - ${upgrades if upgrades else "pass"} - - -def downgrade(): - ${downgrades if downgrades else "pass"} diff --git a/services-api/migrations/versions/.gitkeep b/services-api/migrations/versions/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/services-api/pyproject.toml b/services-api/pyproject.toml deleted file mode 100644 index 2368eb9..0000000 --- a/services-api/pyproject.toml +++ /dev/null @@ -1,22 +0,0 @@ -[tool.black] -line-length = 120 -target-version = ['py310'] -skip-string-normalization = true - -[tool.isort] -line_length = 120 -multi_line_output = 3 -lines_after_imports = 2 -include_trailing_comma = true - -[tool.pytest.ini_options] -minversion = "7.0" -python_files = "*.py" -testpaths = [ - "tests" -] -pythonpath = [ - "." -] -log_cli=true -log_level=0 diff --git a/services-api/requirements.txt b/services-api/requirements.txt deleted file mode 100644 index dfdfe90..0000000 --- a/services-api/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -fastapi -fastapi-sqlalchemy -psycopg2-binary -pydantic[dotenv] -uvicorn -alembic -SQLAlchemy -gunicorn diff --git a/services-api/services-backend/__init__.py b/services-api/services-backend/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/services-api/services-backend/__main__.py b/services-api/services-backend/__main__.py deleted file mode 100644 index e145c78..0000000 --- a/services-api/services-backend/__main__.py +++ /dev/null @@ -1,6 +0,0 @@ -from services-backend.routes.base import app -import uvicorn - - -if __name__ == '__main__': - uvicorn.run(app) \ No newline at end of file diff --git a/services-api/services-backend/exceptions.py b/services-api/services-backend/exceptions.py deleted file mode 100644 index e69de29..0000000 diff --git a/services-api/services-backend/models/__init__.py b/services-api/services-backend/models/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/services-api/services-backend/models/base.py b/services-api/services-backend/models/base.py deleted file mode 100644 index 2279e14..0000000 --- a/services-api/services-backend/models/base.py +++ /dev/null @@ -1,22 +0,0 @@ -import re -from sqlalchemy.ext.declarative import as_declarative, declared_attr - - -@as_declarative() -class Base: - """Base class for all database entities""" - - @classmethod - @declared_attr - def __tablename__(cls) -> str: - """Generate database table name automatically. - Convert CamelCase class name to snake_case db table name. - """ - return re.sub(r"(? str: - attrs = [] - for c in self.__table__.columns: - attrs.append(f"{c.name}={getattr(self, c.name)}") - return "{}({})".format(self.__class__.__name__, ', '.join(attrs)) - diff --git a/services-api/services-backend/models/database.py b/services-api/services-backend/models/database.py deleted file mode 100644 index 24e626f..0000000 --- a/services-api/services-backend/models/database.py +++ /dev/null @@ -1,16 +0,0 @@ -from sqlalchemy import Column, Integer, String, ForeignKey -from .base import Base - - -class Category(Base): - id = Column(Integer, primary_key=True) - category_id = Column(Integer, unique=True) - name = Column(String) - type = Column(String) - # category = relationship("Button", back_populates="category") - -class Button(Base): - id = Column(Integer, primary_key=True) - name = Column(String) - category_id = Column(Integer, ForeignKey(Category.id)) - icon = Column(String) diff --git a/services-api/services-backend/routes/__init__.py b/services-api/services-backend/routes/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/services-api/services-backend/routes/base.py b/services-api/services-backend/routes/base.py deleted file mode 100644 index a78c88e..0000000 --- a/services-api/services-backend/routes/base.py +++ /dev/null @@ -1,22 +0,0 @@ -from fastapi import FastAPI - -from ..settings import Settings -from fastapi.middleware.cors import CORSMiddleware -from fastapi_sqlalchemy import DBSessionMiddleware - -settings = Settings() -app = FastAPI() - -app.add_middleware( - DBSessionMiddleware, - db_url=settings.DB_DSN, - session_args={"autocommit": True}, -) - -app.add_middleware( - CORSMiddleware, - allow_origins=settings.CORS_ALLOW_ORIGINS, - allow_credentials=settings.CORS_ALLOW_CREDENTIALS, - allow_methods=settings.CORS_ALLOW_METHODS, - allow_headers=settings.CORS_ALLOW_HEADERS, -) \ No newline at end of file diff --git a/services-api/services-backend/routes/button.py b/services-api/services-backend/routes/button.py deleted file mode 100644 index e69de29..0000000 diff --git a/services-api/services-backend/routes/category.py b/services-api/services-backend/routes/category.py deleted file mode 100644 index e69de29..0000000 diff --git a/services-api/services-backend/routes/models/__init__.py b/services-api/services-backend/routes/models/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/services-api/services-backend/routes/models/base.py b/services-api/services-backend/routes/models/base.py deleted file mode 100644 index 05d0cdb..0000000 --- a/services-api/services-backend/routes/models/base.py +++ /dev/null @@ -1,12 +0,0 @@ -from pydantic import BaseModel - - -class Base(BaseModel): - def __repr__(self) -> str: - attrs = [] - for k, v in self.__class__.schema().items(): - attrs.append(f"{k}={v}") - return "{}({})".format(self.__class__.__name__, ', '.join(attrs)) - - class Config: - orm_mode = True \ No newline at end of file diff --git a/services-api/services-backend/routes/models/models.py b/services-api/services-backend/routes/models/models.py deleted file mode 100644 index e69de29..0000000 diff --git a/services-api/services-backend/settings.py b/services-api/services-backend/settings.py deleted file mode 100644 index 07d1286..0000000 --- a/services-api/services-backend/settings.py +++ /dev/null @@ -1,24 +0,0 @@ -from pydantic import BaseSettings, PostgresDsn -from functools import lru_cache - - -class Settings(BaseSettings): - """Application settings""" - DB_DSN: PostgresDsn - - CORS_ALLOW_ORIGINS: list[str] = ['*'] - CORS_ALLOW_CREDENTIALS: bool = True - CORS_ALLOW_METHODS: list[str] = ['*'] - CORS_ALLOW_HEADERS: list[str] = ['*'] - - class Config: - """Pydantic BaseSettings config""" - - case_sensitive = True - env_file = ".env" - - -@lru_cache -def get_settings() -> Settings: - settings = Settings() - return settings \ No newline at end of file diff --git a/services-api/services-backend/utils/__init__.py b/services-api/services-backend/utils/__init__.py deleted file mode 100644 index e69de29..0000000 From 4a1a1c2d6f673cba54e1df1a34cc19e366f956d2 Mon Sep 17 00:00:00 2001 From: Wudext <116914558+Wudext@users.noreply.github.com> Date: Fri, 25 Nov 2022 20:46:18 +0300 Subject: [PATCH 21/36] Update requirements.txt --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1ff2691..9f083fb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,4 +9,3 @@ gunicorn python-dotenv~=0.21.0 Faker~=15.3.1 -starlette~=0.20.4 \ No newline at end of file From 2df7e0970449483c1358f3bc1ee82fc0fbcede62 Mon Sep 17 00:00:00 2001 From: Wudext Date: Tue, 29 Nov 2022 22:56:49 +0300 Subject: [PATCH 22/36] Bugcreating (xD) --- alembic.ini | 2 +- docker-compose.yml | 20 ++++++++++++++++++++ migrations/env.py | 4 ++-- migrations/versions/3df3f8bfa89e_.py | 24 ++++++++++++++++++++++++ services_backend/routes/base.py | 4 ++-- services_backend/routes/button.py | 8 +++++++- services_backend/routes/category.py | 10 ++++++++-- services_backend/settings.py | 4 +++- 8 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 docker-compose.yml create mode 100644 migrations/versions/3df3f8bfa89e_.py diff --git a/alembic.ini b/alembic.ini index f6c8899..1bda588 100644 --- a/alembic.ini +++ b/alembic.ini @@ -52,7 +52,7 @@ version_path_separator = os # Use os.pathsep. Default configuration used for ne # are written from script.py.mako # output_encoding = utf-8 -sqlalchemy.url = driver://user:pass@localhost/dbname +sqlalchemy.url = postgresql://postgres:123@localhost:5432/Profcom [post_write_hooks] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2dc6153 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,20 @@ +version: "1.0" + +services: + db: + image: postgres:13.3 + ports: + - "127.0.0.1:5432:5432" + environment: + - POSTGRES_PASSWORD=123 + + api: + build: . + env_file: .env + command: bash -c "alembic upgrade head && uvicorn services_backend.__main__:app --reload" + volumes: + - .:/app + ports: + - "127.0.0.1:8000:8000" + depends_on: + - db \ No newline at end of file diff --git a/migrations/env.py b/migrations/env.py index 8be350c..77e8e95 100644 --- a/migrations/env.py +++ b/migrations/env.py @@ -4,8 +4,8 @@ from sqlalchemy import pool from alembic import context -from ..services_backend.models.base import Base -from ..services_backend.settings import get_settings +from services_backend.models.base import Base +from services_backend.settings import get_settings # this is the Alembic Config object, which provides # access to the values within the .ini file in use. diff --git a/migrations/versions/3df3f8bfa89e_.py b/migrations/versions/3df3f8bfa89e_.py new file mode 100644 index 0000000..b15669c --- /dev/null +++ b/migrations/versions/3df3f8bfa89e_.py @@ -0,0 +1,24 @@ +"""empty message + +Revision ID: 3df3f8bfa89e +Revises: +Create Date: 2022-11-29 16:17:04.310375 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '3df3f8bfa89e' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + pass + + +def downgrade(): + pass diff --git a/services_backend/routes/base.py b/services_backend/routes/base.py index 7800227..82f49f0 100644 --- a/services_backend/routes/base.py +++ b/services_backend/routes/base.py @@ -9,8 +9,8 @@ # Насрано вот тут settings = Settings() app = FastAPI(debug=True) -app.mount("/button", button) -app.mount("/category", category) +app.include_router(button) +app.include_router(category) app.add_middleware( diff --git a/services_backend/routes/button.py b/services_backend/routes/button.py index 5d4b072..8e3da7b 100644 --- a/services_backend/routes/button.py +++ b/services_backend/routes/button.py @@ -1,5 +1,7 @@ from fastapi import HTTPException, APIRouter, Depends from sqlalchemy.orm import Session +from sqlalchemy.engine import create_engine +from sqlalchemy.ext.declarative import declarative_base from .models.button import ButtonCreate, ButtonUpdate, ButtonGet from ..models.database import Button @@ -10,10 +12,14 @@ responses={404: {"description": "You tried, but no"}} ) +Base = declarative_base() +engine = create_engine('postgresql+psycopg2://postgres:123@localhost:5432/Profcom') +Base.metadata.create_all(bind=engine) + # Да, без этого говна ничего не работает, я честно пытался def get_db(): - db = Session() + db = Session(bind=engine) try: yield db finally: diff --git a/services_backend/routes/category.py b/services_backend/routes/category.py index 586b8be..59c8681 100644 --- a/services_backend/routes/category.py +++ b/services_backend/routes/category.py @@ -1,5 +1,7 @@ from fastapi import HTTPException, APIRouter, Depends from sqlalchemy.orm import Session +from sqlalchemy.engine import create_engine +from sqlalchemy.ext.declarative import declarative_base from .models.category import CategoryCreate, CategoryUpdate, CategoryGet from ..models.database import Category, Button @@ -10,10 +12,14 @@ responses={404: {"description": "You tried, but no"}} ) +Base = declarative_base() +engine = create_engine('postgresql+psycopg2://postgres:123@localhost:5432/Profcom') +Base.metadata.create_all(bind=engine) + # Да, без этого говна ничего не работает, я честно пытался def get_db(): - db = Session() + db = Session(bind=engine) try: yield db finally: @@ -33,7 +39,7 @@ def create_category(category: CategoryCreate, db: Session = Depends(get_db)): @category.get("/", response_model=list[CategoryGet]) -def get_categories(db: Session = Depends(get_db), skip: int = 0, limit: int = 100): +def get_categories(skip: int = 0, limit: int = 100, db: Session = Depends(get_db), ): return db.query(Category).offset(skip).limit(limit).all() diff --git a/services_backend/settings.py b/services_backend/settings.py index fb6392d..f48621d 100644 --- a/services_backend/settings.py +++ b/services_backend/settings.py @@ -1,10 +1,12 @@ +import os + from pydantic import BaseSettings, PostgresDsn from functools import lru_cache class Settings(BaseSettings): """Application settings""" - DB_DSN: PostgresDsn = f'postgresql://postgres:123@localhost:5432/Profcom' + DB_DSN: PostgresDsn = os.getenv(".env") CORS_ALLOW_ORIGINS: list[str] = ['*'] CORS_ALLOW_CREDENTIALS: bool = True From 25ba41e116d831db562ce3ba0a89f279f3cfe201 Mon Sep 17 00:00:00 2001 From: Wudext Date: Thu, 1 Dec 2022 12:17:55 +0300 Subject: [PATCH 23/36] Bugfixing 1.0 --- .gitignore | 2 +- docker-compose.yml | 2 +- services_backend/models/database.py | 6 ++++-- services_backend/routes/base.py | 7 ++++--- services_backend/routes/button.py | 3 +-- services_backend/routes/category.py | 3 +-- services_backend/routes/models/category.py | 3 +++ services_backend/settings.py | 8 ++++++-- 8 files changed, 21 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index ebc9451..c564a62 100644 --- a/.gitignore +++ b/.gitignore @@ -102,7 +102,7 @@ celerybeat.pid *.sage.py # Environments -.env +services_backend/env.env .venv env/ venv/ diff --git a/docker-compose.yml b/docker-compose.yml index 2dc6153..0636f02 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,7 +10,7 @@ services: api: build: . - env_file: .env + env_file: env.env command: bash -c "alembic upgrade head && uvicorn services_backend.__main__:app --reload" volumes: - .:/app diff --git a/services_backend/models/database.py b/services_backend/models/database.py index 97b9bae..8b44584 100644 --- a/services_backend/models/database.py +++ b/services_backend/models/database.py @@ -1,4 +1,5 @@ from sqlalchemy import Column, Integer, String, ForeignKey +from sqlalchemy.orm import relationship from .base import Base @@ -7,11 +8,12 @@ class Category(Base): category_id = Column(Integer, unique=True) name = Column(String) type = Column(String) - # category = relationship("Button", back_populates="category") + category = relationship("Button", back_populates="category") class Button(Base): id = Column(Integer, primary_key=True) name = Column(String) - category_id = Column(Integer, ForeignKey(Category.id)) + category_id = Column(Integer, ForeignKey(Category.category_id)) + category = relationship("Category", back_populates="category") icon = Column(String) diff --git a/services_backend/routes/base.py b/services_backend/routes/base.py index 82f49f0..e29fe76 100644 --- a/services_backend/routes/base.py +++ b/services_backend/routes/base.py @@ -6,11 +6,9 @@ from fastapi.middleware.cors import CORSMiddleware from fastapi_sqlalchemy import DBSessionMiddleware -# Насрано вот тут + settings = Settings() app = FastAPI(debug=True) -app.include_router(button) -app.include_router(category) app.add_middleware( @@ -26,3 +24,6 @@ allow_methods=settings.CORS_ALLOW_METHODS, allow_headers=settings.CORS_ALLOW_HEADERS, ) + +app.include_router(button, prefix='/button') +app.include_router(category, prefix='/category') \ No newline at end of file diff --git a/services_backend/routes/button.py b/services_backend/routes/button.py index 8e3da7b..1478d44 100644 --- a/services_backend/routes/button.py +++ b/services_backend/routes/button.py @@ -7,7 +7,6 @@ from ..models.database import Button button = APIRouter( - prefix="/button", tags=["button"], responses={404: {"description": "You tried, but no"}} ) @@ -39,7 +38,7 @@ def create_button(button: ButtonCreate, db: Session = Depends(get_db)): @button.get("/", response_model=list[ButtonGet]) -def get_buttons(skip: int, limit: int, db: Session = Depends(get_db)): +def get_buttons(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): return db.query(Button).offset(skip).limit(limit).all() diff --git a/services_backend/routes/category.py b/services_backend/routes/category.py index 59c8681..77c42cc 100644 --- a/services_backend/routes/category.py +++ b/services_backend/routes/category.py @@ -7,7 +7,6 @@ from ..models.database import Category, Button category = APIRouter( - prefix="/category", tags=["category"], responses={404: {"description": "You tried, but no"}} ) @@ -39,7 +38,7 @@ def create_category(category: CategoryCreate, db: Session = Depends(get_db)): @category.get("/", response_model=list[CategoryGet]) -def get_categories(skip: int = 0, limit: int = 100, db: Session = Depends(get_db), ): +def get_categories(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): return db.query(Category).offset(skip).limit(limit).all() diff --git a/services_backend/routes/models/category.py b/services_backend/routes/models/category.py index aa79a13..2ea38b6 100644 --- a/services_backend/routes/models/category.py +++ b/services_backend/routes/models/category.py @@ -17,4 +17,7 @@ class CategoryUpdate(Base): class CategoryGet(Base): id: int + category_id: Optional[int] + type: Optional[str] + name: Optional[str] diff --git a/services_backend/settings.py b/services_backend/settings.py index f48621d..7674e3f 100644 --- a/services_backend/settings.py +++ b/services_backend/settings.py @@ -1,12 +1,16 @@ import os +from pathlib import Path +from dotenv import load_dotenv from pydantic import BaseSettings, PostgresDsn from functools import lru_cache class Settings(BaseSettings): """Application settings""" - DB_DSN: PostgresDsn = os.getenv(".env") + path = os.path.join(os.path.dirname(__file__), 'env.env') + load_dotenv(path) + DB_DSN: PostgresDsn = os.getenv('DATABASE_URL') CORS_ALLOW_ORIGINS: list[str] = ['*'] CORS_ALLOW_CREDENTIALS: bool = True @@ -17,7 +21,7 @@ class Config: """Pydantic BaseSettings config""" case_sensitive = True - env_file = ".env" + env_file = "env.env" @lru_cache From bae663a8fcfcbde024b0a6b912036c908635280a Mon Sep 17 00:00:00 2001 From: Dyakov Roman Date: Thu, 1 Dec 2022 12:24:14 +0300 Subject: [PATCH 24/36] Fix alembic --- .gitignore | 4 ++-- Makefile | 2 +- requirements.dev.txt | 1 + requirements.txt | 13 +++++-------- services_backend/settings.py | 6 ++---- 5 files changed, 11 insertions(+), 15 deletions(-) create mode 100644 requirements.dev.txt diff --git a/.gitignore b/.gitignore index c564a62..f7f6a3b 100644 --- a/.gitignore +++ b/.gitignore @@ -102,7 +102,7 @@ celerybeat.pid *.sage.py # Environments -services_backend/env.env +.env .venv env/ venv/ @@ -129,4 +129,4 @@ dmypy.json # Pyre type checker .pyre/ -desktop.ini \ No newline at end of file +desktop.ini diff --git a/Makefile b/Makefile index 45cd466..8f886f4 100644 --- a/Makefile +++ b/Makefile @@ -4,5 +4,5 @@ run: db: docker run -d -p 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust --name db-services-backend postgres:15 -migrate: db +migrate: alembic upgrade head diff --git a/requirements.dev.txt b/requirements.dev.txt new file mode 100644 index 0000000..a07e975 --- /dev/null +++ b/requirements.dev.txt @@ -0,0 +1 @@ +Faker diff --git a/requirements.txt b/requirements.txt index 9f083fb..dfdfe90 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,8 @@ -fastapi~=0.86.0 +fastapi fastapi-sqlalchemy psycopg2-binary -pydantic[dotenv]~=1.10.2 -uvicorn~=0.19.0 -alembic~=1.8.1 -SQLAlchemy~=1.4.43 +pydantic[dotenv] +uvicorn +alembic +SQLAlchemy gunicorn - -python-dotenv~=0.21.0 -Faker~=15.3.1 diff --git a/services_backend/settings.py b/services_backend/settings.py index 7674e3f..8726c4d 100644 --- a/services_backend/settings.py +++ b/services_backend/settings.py @@ -8,9 +8,7 @@ class Settings(BaseSettings): """Application settings""" - path = os.path.join(os.path.dirname(__file__), 'env.env') - load_dotenv(path) - DB_DSN: PostgresDsn = os.getenv('DATABASE_URL') + DB_DSN: PostgresDsn = 'postgresql://postgres@localhost:5432/postgres' CORS_ALLOW_ORIGINS: list[str] = ['*'] CORS_ALLOW_CREDENTIALS: bool = True @@ -21,7 +19,7 @@ class Config: """Pydantic BaseSettings config""" case_sensitive = True - env_file = "env.env" + env_file = ".env" @lru_cache From 719dae5807f4354966cc68e5d6b0456f9ddffec5 Mon Sep 17 00:00:00 2001 From: Dyakov Roman Date: Thu, 1 Dec 2022 12:38:25 +0300 Subject: [PATCH 25/36] Fix db --- Makefile | 2 +- alembic.ini | 3 -- services_backend/routes/button.py | 46 +++++++------------------ services_backend/routes/category.py | 53 +++++++++-------------------- 4 files changed, 29 insertions(+), 75 deletions(-) diff --git a/Makefile b/Makefile index 8f886f4..d16211d 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ run: - source ./venv/bin/activate && uvicorn --reload --log-level debug services-backend.routes.base:app + source ./venv/bin/activate && uvicorn --reload --log-level debug services_backend.routes.base:app db: docker run -d -p 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust --name db-services-backend postgres:15 diff --git a/alembic.ini b/alembic.ini index 1bda588..8921c49 100644 --- a/alembic.ini +++ b/alembic.ini @@ -52,9 +52,6 @@ version_path_separator = os # Use os.pathsep. Default configuration used for ne # are written from script.py.mako # output_encoding = utf-8 -sqlalchemy.url = postgresql://postgres:123@localhost:5432/Profcom - - [post_write_hooks] # post_write_hooks defines scripts or Python functions that are run # on newly generated revision scripts. See the documentation for further diff --git a/services_backend/routes/button.py b/services_backend/routes/button.py index 1478d44..64dfbe7 100644 --- a/services_backend/routes/button.py +++ b/services_backend/routes/button.py @@ -1,7 +1,5 @@ -from fastapi import HTTPException, APIRouter, Depends -from sqlalchemy.orm import Session -from sqlalchemy.engine import create_engine -from sqlalchemy.ext.declarative import declarative_base +from fastapi import HTTPException, APIRouter +from fastapi_sqlalchemy import db from .models.button import ButtonCreate, ButtonUpdate, ButtonGet from ..models.database import Button @@ -11,56 +9,36 @@ responses={404: {"description": "You tried, but no"}} ) -Base = declarative_base() -engine = create_engine('postgresql+psycopg2://postgres:123@localhost:5432/Profcom') -Base.metadata.create_all(bind=engine) - - -# Да, без этого говна ничего не работает, я честно пытался -def get_db(): - db = Session(bind=engine) - try: - yield db - finally: - db.close() - @button.post("/", response_model=ButtonCreate) -def create_button(button: ButtonCreate, db: Session = Depends(get_db)): +def create_button(button: ButtonCreate): db_button = ButtonCreate(category_id=button.category_id, name=button.name, icon=button.icon) - db.add(db_button) - db.commit() - db.refresh(db_button) - db.close() - + db.session.add(db_button) return db_button @button.get("/", response_model=list[ButtonGet]) -def get_buttons(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - return db.query(Button).offset(skip).limit(limit).all() +def get_buttons(skip: int = 0, limit: int = 100): + return db.session.query(Button).offset(skip).limit(limit).all() @button.get("/{button_id}", response_model=ButtonGet) -def get_button(button_id: int, db: Session = Depends(get_db)): - return db.query(Button).filter(Button.id == button_id).first() +def get_button(button_id: int): + return db.session.query(Button).filter(Button.id == button_id).first() @button.delete("/") -def remove_button(button_id: int, db: Session = Depends(get_db)): +def remove_button(button_id: int): db_button = get_button(button_id=button_id, db=db) if db_button is None: raise HTTPException(status_code=404, detail="Button does not exist") - delete = db.query(Button).filter(Button.id == button_id).first() - db.delete(delete) - db.commit() - db.close() + db.session.query(Button).filter(Button.id == button_id).first() @button.patch("/", response_model=ButtonUpdate) -def update_button(button: ButtonUpdate, db: Session = Depends(get_db)): +def update_button(button: ButtonUpdate): db_old_button = get_button(button_id=button.id, db=db) if db_old_button is None: raise HTTPException(status_code=404, detail="Button does not exist") - return db.query(Button).update(button) + return db.session.query(Button).update(button) diff --git a/services_backend/routes/category.py b/services_backend/routes/category.py index 77c42cc..2eb211a 100644 --- a/services_backend/routes/category.py +++ b/services_backend/routes/category.py @@ -1,7 +1,5 @@ -from fastapi import HTTPException, APIRouter, Depends -from sqlalchemy.orm import Session -from sqlalchemy.engine import create_engine -from sqlalchemy.ext.declarative import declarative_base +from fastapi import HTTPException, APIRouter +from fastapi_sqlalchemy import db from .models.category import CategoryCreate, CategoryUpdate, CategoryGet from ..models.database import Category, Button @@ -11,63 +9,44 @@ responses={404: {"description": "You tried, but no"}} ) -Base = declarative_base() -engine = create_engine('postgresql+psycopg2://postgres:123@localhost:5432/Profcom') -Base.metadata.create_all(bind=engine) - - -# Да, без этого говна ничего не работает, я честно пытался -def get_db(): - db = Session(bind=engine) - try: - yield db - finally: - db.close() - @category.post("/", response_model=CategoryCreate) -def create_category(category: CategoryCreate, db: Session = Depends(get_db)): +def create_category(category: CategoryCreate): db_category = CategoryCreate(category_id=category.category_id, type=category.type, name=category.name) - db.add(db_category) - db.commit() - db.refresh(db_category) - db.close() - + db.session.add(db_category) return db_category @category.get("/", response_model=list[CategoryGet]) -def get_categories(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - return db.query(Category).offset(skip).limit(limit).all() +def get_categories(skip: int = 0, limit: int = 100): + return db.session.query(Category).offset(skip).limit(limit).all() @category.get("/{category_id}", response_model=CategoryGet) -def get_category(category_id: int, db: Session = Depends(get_db)): - db_category = db.query(Category).filter(Category.id == category_id).first() +def get_category(category_id: int): + db_category = db.session.query(Category).filter(Category.id == category_id).first() if db_category is None: raise HTTPException(status_code=404, detail="Category does not exist") return db_category @category.delete("/") -def remove_category(category_id: int, db: Session = Depends(get_db)): +def remove_category(category_id: int): db_category = get_category(db=db, category_id=category_id) if db_category is None: raise HTTPException(status_code=404, detail="Category does not exist") - delete = db.query(Category).filter(Category.id == category_id).first() - d = db.query(Button).filter(Button.category_id == category_id).all() + delete = db.session.query(Category).filter(Category.id == category_id).first() + d = db.session.query(Button).filter(Button.category_id == category_id).all() for button in d: - db.delete(button) - db.commit() - db.delete(delete) - db.commit() - db.close() + db.session.delete(button) + db.session.flush() + db.session.delete(delete) @category.patch("/", response_model=CategoryUpdate) -def update_category(category: CategoryUpdate, db: Session = Depends(get_db)): +def update_category(category: CategoryUpdate): db_old_category = get_category(db=db, category_id=category.id) if db_old_category is None: raise HTTPException(status_code=404, detail="Category does not exist") - return db.query(Category).update(category) + return db.session.query(Category).update(category) From 6c78dda657cc7dda64f6464d9216b4107881e4d5 Mon Sep 17 00:00:00 2001 From: Wudext Date: Thu, 1 Dec 2022 14:42:46 +0300 Subject: [PATCH 26/36] Alembic --- migrations/versions/5cd3c5d90b30_.py | 24 ++++++++++++++ migrations/versions/f81089f999c2_init.py | 42 ++++++++++++++++++++++++ services_backend/env.env | 1 + services_backend/routes/base.py | 2 +- services_backend/settings.py | 8 ++--- 5 files changed, 70 insertions(+), 7 deletions(-) create mode 100644 migrations/versions/5cd3c5d90b30_.py create mode 100644 migrations/versions/f81089f999c2_init.py create mode 100644 services_backend/env.env diff --git a/migrations/versions/5cd3c5d90b30_.py b/migrations/versions/5cd3c5d90b30_.py new file mode 100644 index 0000000..e1a41c6 --- /dev/null +++ b/migrations/versions/5cd3c5d90b30_.py @@ -0,0 +1,24 @@ +"""empty message + +Revision ID: 5cd3c5d90b30 +Revises: 3df3f8bfa89e +Create Date: 2022-12-01 12:20:31.302842 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '5cd3c5d90b30' +down_revision = '3df3f8bfa89e' +branch_labels = None +depends_on = None + + +def upgrade(): + pass + + +def downgrade(): + pass diff --git a/migrations/versions/f81089f999c2_init.py b/migrations/versions/f81089f999c2_init.py new file mode 100644 index 0000000..136c5dc --- /dev/null +++ b/migrations/versions/f81089f999c2_init.py @@ -0,0 +1,42 @@ +"""Init + +Revision ID: f81089f999c2 +Revises: 5cd3c5d90b30 +Create Date: 2022-12-01 14:37:14.842533 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'f81089f999c2' +down_revision = '5cd3c5d90b30' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('category') + op.drop_table('buttons') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('buttons', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column('category_id', sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column('icon', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.ForeignKeyConstraint(['category_id'], ['category.id'], name='buttons_category_id_fkey'), + sa.PrimaryKeyConstraint('id', name='buttons_pkey') + ) + op.create_table('category', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column('type', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.PrimaryKeyConstraint('id', name='category_pkey') + ) + # ### end Alembic commands ### diff --git a/services_backend/env.env b/services_backend/env.env new file mode 100644 index 0000000..9d2ee29 --- /dev/null +++ b/services_backend/env.env @@ -0,0 +1 @@ +DATABASE_URL = postgresql://postgres:123@localhost:5432/Profcom \ No newline at end of file diff --git a/services_backend/routes/base.py b/services_backend/routes/base.py index e29fe76..84dd2ce 100644 --- a/services_backend/routes/base.py +++ b/services_backend/routes/base.py @@ -6,7 +6,6 @@ from fastapi.middleware.cors import CORSMiddleware from fastapi_sqlalchemy import DBSessionMiddleware - settings = Settings() app = FastAPI(debug=True) @@ -25,5 +24,6 @@ allow_headers=settings.CORS_ALLOW_HEADERS, ) + app.include_router(button, prefix='/button') app.include_router(category, prefix='/category') \ No newline at end of file diff --git a/services_backend/settings.py b/services_backend/settings.py index 8726c4d..4ac7893 100644 --- a/services_backend/settings.py +++ b/services_backend/settings.py @@ -1,14 +1,10 @@ -import os -from pathlib import Path - -from dotenv import load_dotenv from pydantic import BaseSettings, PostgresDsn from functools import lru_cache class Settings(BaseSettings): """Application settings""" - DB_DSN: PostgresDsn = 'postgresql://postgres@localhost:5432/postgres' + DB_DSN: PostgresDsn = 'postgresql://postgres:123@localhost:5432/postgres' CORS_ALLOW_ORIGINS: list[str] = ['*'] CORS_ALLOW_CREDENTIALS: bool = True @@ -19,7 +15,7 @@ class Config: """Pydantic BaseSettings config""" case_sensitive = True - env_file = ".env" + env_file = "env.env" @lru_cache From ff9c977558834489814d0112ebc6112d3c553d22 Mon Sep 17 00:00:00 2001 From: Wudext Date: Thu, 1 Dec 2022 16:46:28 +0300 Subject: [PATCH 27/36] Bugfixing 2.0 --- docker-compose.yml | 2 +- migrations/versions/3df3f8bfa89e_.py | 24 ---------------------- migrations/versions/5cd3c5d90b30_.py | 24 ---------------------- migrations/versions/f81089f999c2_init.py | 2 +- services_backend/env.env | 1 - services_backend/models/database.py | 4 ++-- services_backend/routes/base.py | 2 +- services_backend/routes/button.py | 15 ++++++++++---- services_backend/routes/category.py | 7 +++---- services_backend/routes/models/category.py | 3 --- services_backend/settings.py | 10 +++++++-- 11 files changed, 27 insertions(+), 67 deletions(-) delete mode 100644 migrations/versions/3df3f8bfa89e_.py delete mode 100644 migrations/versions/5cd3c5d90b30_.py delete mode 100644 services_backend/env.env diff --git a/docker-compose.yml b/docker-compose.yml index 0636f02..d45b706 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,7 +10,7 @@ services: api: build: . - env_file: env.env + env_file: .env. command: bash -c "alembic upgrade head && uvicorn services_backend.__main__:app --reload" volumes: - .:/app diff --git a/migrations/versions/3df3f8bfa89e_.py b/migrations/versions/3df3f8bfa89e_.py deleted file mode 100644 index b15669c..0000000 --- a/migrations/versions/3df3f8bfa89e_.py +++ /dev/null @@ -1,24 +0,0 @@ -"""empty message - -Revision ID: 3df3f8bfa89e -Revises: -Create Date: 2022-11-29 16:17:04.310375 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '3df3f8bfa89e' -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade(): - pass - - -def downgrade(): - pass diff --git a/migrations/versions/5cd3c5d90b30_.py b/migrations/versions/5cd3c5d90b30_.py deleted file mode 100644 index e1a41c6..0000000 --- a/migrations/versions/5cd3c5d90b30_.py +++ /dev/null @@ -1,24 +0,0 @@ -"""empty message - -Revision ID: 5cd3c5d90b30 -Revises: 3df3f8bfa89e -Create Date: 2022-12-01 12:20:31.302842 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '5cd3c5d90b30' -down_revision = '3df3f8bfa89e' -branch_labels = None -depends_on = None - - -def upgrade(): - pass - - -def downgrade(): - pass diff --git a/migrations/versions/f81089f999c2_init.py b/migrations/versions/f81089f999c2_init.py index 136c5dc..517ecf1 100644 --- a/migrations/versions/f81089f999c2_init.py +++ b/migrations/versions/f81089f999c2_init.py @@ -11,7 +11,7 @@ # revision identifiers, used by Alembic. revision = 'f81089f999c2' -down_revision = '5cd3c5d90b30' +down_revision = None branch_labels = None depends_on = None diff --git a/services_backend/env.env b/services_backend/env.env deleted file mode 100644 index 9d2ee29..0000000 --- a/services_backend/env.env +++ /dev/null @@ -1 +0,0 @@ -DATABASE_URL = postgresql://postgres:123@localhost:5432/Profcom \ No newline at end of file diff --git a/services_backend/models/database.py b/services_backend/models/database.py index 8b44584..0bead8b 100644 --- a/services_backend/models/database.py +++ b/services_backend/models/database.py @@ -5,7 +5,7 @@ class Category(Base): id = Column(Integer, primary_key=True) - category_id = Column(Integer, unique=True) + # category_id = Column(Integer, unique=True) name = Column(String) type = Column(String) category = relationship("Button", back_populates="category") @@ -14,6 +14,6 @@ class Category(Base): class Button(Base): id = Column(Integer, primary_key=True) name = Column(String) - category_id = Column(Integer, ForeignKey(Category.category_id)) + category_id = Column(Integer, ForeignKey(Category.id)) category = relationship("Category", back_populates="category") icon = Column(String) diff --git a/services_backend/routes/base.py b/services_backend/routes/base.py index 84dd2ce..6c97816 100644 --- a/services_backend/routes/base.py +++ b/services_backend/routes/base.py @@ -1,6 +1,6 @@ from fastapi import FastAPI -from ..settings import Settings +from services_backend.settings import Settings from .button import button from .category import category from fastapi.middleware.cors import CORSMiddleware diff --git a/services_backend/routes/button.py b/services_backend/routes/button.py index 64dfbe7..2de27bd 100644 --- a/services_backend/routes/button.py +++ b/services_backend/routes/button.py @@ -3,15 +3,19 @@ from .models.button import ButtonCreate, ButtonUpdate, ButtonGet from ..models.database import Button +from .category import get_category button = APIRouter( tags=["button"], - responses={404: {"description": "You tried, but no"}} + responses={200: {"description": "Ok"}} ) @button.post("/", response_model=ButtonCreate) def create_button(button: ButtonCreate): + db_category = get_category(category_id=button.category_id) + if db_category is None: + raise HTTPException(status_code=404, detail="Category does not exist") db_button = ButtonCreate(category_id=button.category_id, name=button.name, icon=button.icon) db.session.add(db_button) @@ -25,12 +29,15 @@ def get_buttons(skip: int = 0, limit: int = 100): @button.get("/{button_id}", response_model=ButtonGet) def get_button(button_id: int): - return db.session.query(Button).filter(Button.id == button_id).first() + db_button = db.session.query(Button).filter(Button.id == button_id).first() + if db_button is None: + raise HTTPException(status_code=404, detail="Button does not exist") + return db_button @button.delete("/") def remove_button(button_id: int): - db_button = get_button(button_id=button_id, db=db) + db_button = get_button(button_id=button_id) if db_button is None: raise HTTPException(status_code=404, detail="Button does not exist") db.session.query(Button).filter(Button.id == button_id).first() @@ -38,7 +45,7 @@ def remove_button(button_id: int): @button.patch("/", response_model=ButtonUpdate) def update_button(button: ButtonUpdate): - db_old_button = get_button(button_id=button.id, db=db) + db_old_button = get_button(button_id=button.id) if db_old_button is None: raise HTTPException(status_code=404, detail="Button does not exist") return db.session.query(Button).update(button) diff --git a/services_backend/routes/category.py b/services_backend/routes/category.py index 2eb211a..50af6fb 100644 --- a/services_backend/routes/category.py +++ b/services_backend/routes/category.py @@ -6,14 +6,13 @@ category = APIRouter( tags=["category"], - responses={404: {"description": "You tried, but no"}} + responses={200: {"description": "Ok"}} ) @category.post("/", response_model=CategoryCreate) def create_category(category: CategoryCreate): - db_category = CategoryCreate(category_id=category.category_id, - type=category.type, name=category.name) + db_category = Category(type=category.type, name=category.name) db.session.add(db_category) return db_category @@ -33,7 +32,7 @@ def get_category(category_id: int): @category.delete("/") def remove_category(category_id: int): - db_category = get_category(db=db, category_id=category_id) + db_category = get_category(category_id=category_id) if db_category is None: raise HTTPException(status_code=404, detail="Category does not exist") delete = db.session.query(Category).filter(Category.id == category_id).first() diff --git a/services_backend/routes/models/category.py b/services_backend/routes/models/category.py index 2ea38b6..a153395 100644 --- a/services_backend/routes/models/category.py +++ b/services_backend/routes/models/category.py @@ -3,21 +3,18 @@ class CategoryCreate(Base): - category_id: int type: Optional[str] name: Optional[str] class CategoryUpdate(Base): id: int - category_id: Optional[int] type: Optional[str] name: Optional[str] class CategoryGet(Base): id: int - category_id: Optional[int] type: Optional[str] name: Optional[str] diff --git a/services_backend/settings.py b/services_backend/settings.py index 4ac7893..5db0214 100644 --- a/services_backend/settings.py +++ b/services_backend/settings.py @@ -1,10 +1,16 @@ +import os +from dotenv import load_dotenv + from pydantic import BaseSettings, PostgresDsn from functools import lru_cache class Settings(BaseSettings): """Application settings""" - DB_DSN: PostgresDsn = 'postgresql://postgres:123@localhost:5432/postgres' + # path = os.path.join(os.path.dirname(__file__), '../.env') + # load_dotenv(path) + # DB_DSN: PostgresDsn = os.environ.get('DATABASE_URL') + DB_DSN: PostgresDsn = os.getenv('DATABASE_URL') CORS_ALLOW_ORIGINS: list[str] = ['*'] CORS_ALLOW_CREDENTIALS: bool = True @@ -15,7 +21,7 @@ class Config: """Pydantic BaseSettings config""" case_sensitive = True - env_file = "env.env" + env_file = ".env" @lru_cache From 78c012e4781c813b27fe9012adb554fa4e2396db Mon Sep 17 00:00:00 2001 From: Wudext Date: Thu, 1 Dec 2022 19:08:46 +0300 Subject: [PATCH 28/36] Bugfixes 3.0 --- migrations/versions/f81089f999c2_init.py | 42 ------------------------ services_backend/models/database.py | 1 - services_backend/routes/base.py | 1 + services_backend/routes/button.py | 3 +- services_backend/routes/category.py | 4 ++- services_backend/routes/models/button.py | 5 ++- 6 files changed, 10 insertions(+), 46 deletions(-) delete mode 100644 migrations/versions/f81089f999c2_init.py diff --git a/migrations/versions/f81089f999c2_init.py b/migrations/versions/f81089f999c2_init.py deleted file mode 100644 index 517ecf1..0000000 --- a/migrations/versions/f81089f999c2_init.py +++ /dev/null @@ -1,42 +0,0 @@ -"""Init - -Revision ID: f81089f999c2 -Revises: 5cd3c5d90b30 -Create Date: 2022-12-01 14:37:14.842533 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = 'f81089f999c2' -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('category') - op.drop_table('buttons') - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('buttons', - sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), - sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=True), - sa.Column('category_id', sa.INTEGER(), autoincrement=False, nullable=True), - sa.Column('icon', sa.VARCHAR(), autoincrement=False, nullable=True), - sa.ForeignKeyConstraint(['category_id'], ['category.id'], name='buttons_category_id_fkey'), - sa.PrimaryKeyConstraint('id', name='buttons_pkey') - ) - op.create_table('category', - sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), - sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=True), - sa.Column('type', sa.VARCHAR(), autoincrement=False, nullable=True), - sa.PrimaryKeyConstraint('id', name='category_pkey') - ) - # ### end Alembic commands ### diff --git a/services_backend/models/database.py b/services_backend/models/database.py index 0bead8b..b56274d 100644 --- a/services_backend/models/database.py +++ b/services_backend/models/database.py @@ -5,7 +5,6 @@ class Category(Base): id = Column(Integer, primary_key=True) - # category_id = Column(Integer, unique=True) name = Column(String) type = Column(String) category = relationship("Button", back_populates="category") diff --git a/services_backend/routes/base.py b/services_backend/routes/base.py index 6c97816..1db713b 100644 --- a/services_backend/routes/base.py +++ b/services_backend/routes/base.py @@ -14,6 +14,7 @@ DBSessionMiddleware, db_url=settings.DB_DSN, session_args={"autocommit": True}, + engine_args={"pool_pre_ping": True} ) app.add_middleware( diff --git a/services_backend/routes/button.py b/services_backend/routes/button.py index 2de27bd..87957bf 100644 --- a/services_backend/routes/button.py +++ b/services_backend/routes/button.py @@ -16,9 +16,10 @@ def create_button(button: ButtonCreate): db_category = get_category(category_id=button.category_id) if db_category is None: raise HTTPException(status_code=404, detail="Category does not exist") - db_button = ButtonCreate(category_id=button.category_id, name=button.name, + db_button = Button(category_id=button.category_id, name=button.name, icon=button.icon) db.session.add(db_button) + db.session.flush() return db_button diff --git a/services_backend/routes/category.py b/services_backend/routes/category.py index 50af6fb..7ffd5ef 100644 --- a/services_backend/routes/category.py +++ b/services_backend/routes/category.py @@ -14,6 +14,7 @@ def create_category(category: CategoryCreate): db_category = Category(type=category.type, name=category.name) db.session.add(db_category) + db.session.flush() return db_category @@ -41,11 +42,12 @@ def remove_category(category_id: int): db.session.delete(button) db.session.flush() db.session.delete(delete) + db.session.flush() @category.patch("/", response_model=CategoryUpdate) def update_category(category: CategoryUpdate): - db_old_category = get_category(db=db, category_id=category.id) + db_old_category = get_category(category_id=category.id) if db_old_category is None: raise HTTPException(status_code=404, detail="Category does not exist") return db.session.query(Category).update(category) diff --git a/services_backend/routes/models/button.py b/services_backend/routes/models/button.py index ce96cac..7f37895 100644 --- a/services_backend/routes/models/button.py +++ b/services_backend/routes/models/button.py @@ -15,5 +15,8 @@ class ButtonUpdate(Base): name: Optional[str] -class ButtonGet(Base): +class ButtonGet(Base): id: int + category_id: int + icon: Optional[str] + name: Optional[str] From d0f3645500a195ba35f93af53d480e666cf74fd6 Mon Sep 17 00:00:00 2001 From: Wudext Date: Mon, 5 Dec 2022 19:49:53 +0300 Subject: [PATCH 29/36] Bugfixes 4.0 --- .../versions/6739a3c84646_update_1_0.py | 43 +++++++++++++++++++ services_backend/routes/button.py | 11 ++--- services_backend/routes/models/button.py | 4 +- services_backend/settings.py | 3 -- 4 files changed, 51 insertions(+), 10 deletions(-) create mode 100644 migrations/versions/6739a3c84646_update_1_0.py diff --git a/migrations/versions/6739a3c84646_update_1_0.py b/migrations/versions/6739a3c84646_update_1_0.py new file mode 100644 index 0000000..1249fa8 --- /dev/null +++ b/migrations/versions/6739a3c84646_update_1_0.py @@ -0,0 +1,43 @@ +"""Update 1.0 + +Revision ID: 6739a3c84646 +Revises: +Create Date: 2022-12-02 16:02:15.329952 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '6739a3c84646' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('button') + op.drop_table('category') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('category', + sa.Column('id', sa.INTEGER(), server_default=sa.text("nextval('category_id_seq'::regclass)"), autoincrement=True, nullable=False), + sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column('type', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.PrimaryKeyConstraint('id', name='category_pkey'), + postgresql_ignore_search_path=False + ) + op.create_table('button', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column('category_id', sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column('icon', sa.VARCHAR(), autoincrement=False, nullable=True), + sa.ForeignKeyConstraint(['category_id'], ['category.id'], name='buttons_category_id_fkey'), + sa.PrimaryKeyConstraint('id', name='buttons_pkey') + ) + # ### end Alembic commands ### diff --git a/services_backend/routes/button.py b/services_backend/routes/button.py index 87957bf..2c7d267 100644 --- a/services_backend/routes/button.py +++ b/services_backend/routes/button.py @@ -41,12 +41,13 @@ def remove_button(button_id: int): db_button = get_button(button_id=button_id) if db_button is None: raise HTTPException(status_code=404, detail="Button does not exist") - db.session.query(Button).filter(Button.id == button_id).first() + db.session.delete(db_button) + db.session.flush() -@button.patch("/", response_model=ButtonUpdate) +@button.patch("/{button_id}", response_model=ButtonUpdate) def update_button(button: ButtonUpdate): db_old_button = get_button(button_id=button.id) - if db_old_button is None: - raise HTTPException(status_code=404, detail="Button does not exist") - return db.session.query(Button).update(button) + db.session.query(Button).filter(Button.id == button.id).update(button).returning(**button.dict()) + db.session.flush() + return db_old_button diff --git a/services_backend/routes/models/button.py b/services_backend/routes/models/button.py index 7f37895..cc45c37 100644 --- a/services_backend/routes/models/button.py +++ b/services_backend/routes/models/button.py @@ -9,13 +9,13 @@ class ButtonCreate(Base): class ButtonUpdate(Base): - id: int + id: Optional[int] category_id: Optional[int] icon: Optional[str] name: Optional[str] -class ButtonGet(Base): +class ButtonGet(Base): id: int category_id: int icon: Optional[str] diff --git a/services_backend/settings.py b/services_backend/settings.py index 5db0214..96230f8 100644 --- a/services_backend/settings.py +++ b/services_backend/settings.py @@ -7,9 +7,6 @@ class Settings(BaseSettings): """Application settings""" - # path = os.path.join(os.path.dirname(__file__), '../.env') - # load_dotenv(path) - # DB_DSN: PostgresDsn = os.environ.get('DATABASE_URL') DB_DSN: PostgresDsn = os.getenv('DATABASE_URL') CORS_ALLOW_ORIGINS: list[str] = ['*'] From 6c5928505c61f06c6300fe5bfef5c40a81d42690 Mon Sep 17 00:00:00 2001 From: Wudext <116914558+Wudext@users.noreply.github.com> Date: Mon, 5 Dec 2022 19:56:52 +0300 Subject: [PATCH 30/36] Bugfixes 4.0 --- services_backend/routes/button.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/services_backend/routes/button.py b/services_backend/routes/button.py index 2c7d267..dd18ce2 100644 --- a/services_backend/routes/button.py +++ b/services_backend/routes/button.py @@ -30,7 +30,7 @@ def get_buttons(skip: int = 0, limit: int = 100): @button.get("/{button_id}", response_model=ButtonGet) def get_button(button_id: int): - db_button = db.session.query(Button).filter(Button.id == button_id).first() + db_button = db.session.query(Button).filter(Button.id == button_id).one_or_none() if db_button is None: raise HTTPException(status_code=404, detail="Button does not exist") return db_button @@ -44,10 +44,12 @@ def remove_button(button_id: int): db.session.delete(db_button) db.session.flush() - -@button.patch("/{button_id}", response_model=ButtonUpdate) + +@button.patch("/{button_id}", response_model=ButtonGet) def update_button(button: ButtonUpdate): - db_old_button = get_button(button_id=button.id) - db.session.query(Button).filter(Button.id == button.id).update(button).returning(**button.dict()) - db.session.flush() - return db_old_button + db_old_button = db.session.query(Button).filter(button.id == button_id).one_or_none() + if db_old_button is None: + raise HTTPException(status_code=404, detail="Button does not exist") + # db_old_button.id = + return db.session.query(Button).filter(Button.id == button.id).update(**button.dict(exclude_unset=True)) + # db.session.flush() From 5bd0469fe8156cf8075cfd5fdf19fed3c3f61033 Mon Sep 17 00:00:00 2001 From: Wudext Date: Mon, 5 Dec 2022 20:40:44 +0300 Subject: [PATCH 31/36] Release --- gunicorn_conf.py | 3 - main_fastapi.py | 80 ------------------- migrations/env.py | 16 +--- .../versions/6739a3c84646_update_1_0.py | 5 -- services_backend/models/database.py | 4 +- services_backend/routes/base.py | 2 +- services_backend/routes/button.py | 18 +++-- services_backend/routes/category.py | 17 ++-- services_backend/routes/models/button.py | 1 - services_backend/routes/models/category.py | 1 - services_backend/settings.py | 5 +- 11 files changed, 29 insertions(+), 123 deletions(-) delete mode 100644 main_fastapi.py diff --git a/gunicorn_conf.py b/gunicorn_conf.py index 7dd141d..98e1e8d 100644 --- a/gunicorn_conf.py +++ b/gunicorn_conf.py @@ -36,7 +36,6 @@ timeout_str = os.getenv("TIMEOUT", "120") keepalive_str = os.getenv("KEEP_ALIVE", "5") -# Gunicorn config variables loglevel = use_loglevel workers = web_concurrency bind = use_bind @@ -48,7 +47,6 @@ keepalive = int(keepalive_str) -# For debugging and testing log_data = { "loglevel": loglevel, "workers": workers, @@ -58,7 +56,6 @@ "keepalive": keepalive, "errorlog": errorlog, "accesslog": accesslog, - # Additional, non-gunicorn variables "workers_per_core": workers_per_core, "use_max_workers": use_max_workers, "host": host, diff --git a/main_fastapi.py b/main_fastapi.py deleted file mode 100644 index 6ae52f9..0000000 --- a/main_fastapi.py +++ /dev/null @@ -1,80 +0,0 @@ -from fastapi import Depends, HTTPException -from sqlalchemy.orm import Session - -from __init__ import get_db, app -import methods -from models import CRUD - - -@app.post("/button/", response_model=CRUD.Button) -def create_button(button: CRUD.Button, db: Session = Depends(get_db)): - db_button = methods.get_button(db, button_id=button.id) - if db_button: - raise HTTPException(status_code=409, detail='Button already exist') - return methods.create_button(db=db, button=button) - - -@app.get("/button/", response_model=list[CRUD.Button]) -def get_buttons(skip: int, limit: int, db: Session = Depends(get_db)): - return methods.get_buttons(db, skip=skip, limit=limit) - - -@app.get("/button/{button_id}", response_model=CRUD.Button) -def get_button(button_id: int, db: Session = Depends(get_db)): - db_button = methods.get_button(db, button_id) - if db_button is None: - raise HTTPException(status_code=404, detail="Button does not exist") - return db_button - - -@app.delete("/button/", response_model=CRUD.Button) -def remove_button(button_id: int, db: Session = Depends(get_db)): - db_button = methods.get_button(db, button_id) - if db_button is None: - raise HTTPException(status_code=404, detail="Button does not exist") - return methods.delete_button(db=db, button_id=button_id) - - -@app.patch("/button/", response_model=CRUD.Button) -def update_button(button: CRUD.Button, db: Session = Depends(get_db)): - db_old_button = get_button(button_id=button.id, db=db) - if db_old_button is None: - raise HTTPException(status_code=404, detail="Button does not exist") - return methods.update_button(db=db, button_id=button.id, button=button) - - -@app.post("/category/", response_model=CRUD.Category) -def create_category(category: CRUD.Category, db: Session = Depends(get_db)): - db_category = methods.get_category(category_id=category.id, db=db) - if db_category: - raise HTTPException(status_code=409, detail="Category already exist") - return methods.create_category(db=db, category=category) - - -@app.get("/category/", response_model=list[CRUD.Category]) -def get_categories(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - return methods.get_categories(db, skip=skip, limit=limit) - - -@app.get("/category/{category_id}", response_model=CRUD.Category) -def get_category(category_id: int, db: Session = Depends(get_db)): - db_category = methods.get_category(category_id=category_id, db=db) - if db_category is None: - raise HTTPException(status_code=404, detail="Category does not exist") - return db_category - - -@app.delete("/category/") -def remove_category(category_id: int, db: Session = Depends(get_db)): - db_category = methods.get_category(db, category_id=category_id) - if db_category is None: - raise HTTPException(status_code=404, detail="Category does not exist") - return methods.delete_category(db=db, category_id=category_id) - - -@app.patch("/category/", response_model=CRUD.Category) -def update_category(category: CRUD.Category, db: Session = Depends(get_db)): - db_old_category = methods.get_category(db=db, category_id=category.id) - if db_old_category is None: - raise HTTPException(status_code=404, detail="Category does not exist") - return methods.update_category(db=db, category_id=category.id, category=category) diff --git a/migrations/env.py b/migrations/env.py index 77e8e95..a9746a9 100644 --- a/migrations/env.py +++ b/migrations/env.py @@ -7,26 +7,16 @@ from services_backend.models.base import Base from services_backend.settings import get_settings -# this is the Alembic Config object, which provides -# access to the values within the .ini file in use. + config = context.config settings = get_settings() -# Interpret the config file for Python logging. -# This line sets up loggers basically. + if config.config_file_name is not None: fileConfig(config.config_file_name) -# add your model's MetaData object here -# for 'autogenerate' support -# from myapp import mymodel -# target_metadata = mymodel.Base.metadata -target_metadata = Base.metadata -# other values from the config, defined by the needs of env.py, -# can be acquired: -# my_important_option = config.get_main_option("my_important_option") -# ... etc. +target_metadata = Base.metadata def run_migrations_offline(): diff --git a/migrations/versions/6739a3c84646_update_1_0.py b/migrations/versions/6739a3c84646_update_1_0.py index 1249fa8..e6963de 100644 --- a/migrations/versions/6739a3c84646_update_1_0.py +++ b/migrations/versions/6739a3c84646_update_1_0.py @@ -9,7 +9,6 @@ import sqlalchemy as sa -# revision identifiers, used by Alembic. revision = '6739a3c84646' down_revision = None branch_labels = None @@ -17,14 +16,11 @@ def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### op.drop_table('button') op.drop_table('category') - # ### end Alembic commands ### def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### op.create_table('category', sa.Column('id', sa.INTEGER(), server_default=sa.text("nextval('category_id_seq'::regclass)"), autoincrement=True, nullable=False), sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=True), @@ -40,4 +36,3 @@ def downgrade(): sa.ForeignKeyConstraint(['category_id'], ['category.id'], name='buttons_category_id_fkey'), sa.PrimaryKeyConstraint('id', name='buttons_pkey') ) - # ### end Alembic commands ### diff --git a/services_backend/models/database.py b/services_backend/models/database.py index b56274d..6527859 100644 --- a/services_backend/models/database.py +++ b/services_backend/models/database.py @@ -7,12 +7,12 @@ class Category(Base): id = Column(Integer, primary_key=True) name = Column(String) type = Column(String) - category = relationship("Button", back_populates="category") + button = relationship("Button", back_populates="category") class Button(Base): id = Column(Integer, primary_key=True) name = Column(String) category_id = Column(Integer, ForeignKey(Category.id)) - category = relationship("Category", back_populates="category") + category = relationship("Category", back_populates="button") icon = Column(String) diff --git a/services_backend/routes/base.py b/services_backend/routes/base.py index 1db713b..eea49cb 100644 --- a/services_backend/routes/base.py +++ b/services_backend/routes/base.py @@ -7,7 +7,7 @@ from fastapi_sqlalchemy import DBSessionMiddleware settings = Settings() -app = FastAPI(debug=True) +app = FastAPI() app.add_middleware( diff --git a/services_backend/routes/button.py b/services_backend/routes/button.py index dd18ce2..5ca1bb2 100644 --- a/services_backend/routes/button.py +++ b/services_backend/routes/button.py @@ -2,7 +2,7 @@ from fastapi_sqlalchemy import db from .models.button import ButtonCreate, ButtonUpdate, ButtonGet -from ..models.database import Button +from ..models.database import Button, Category from .category import get_category button = APIRouter( @@ -13,7 +13,7 @@ @button.post("/", response_model=ButtonCreate) def create_button(button: ButtonCreate): - db_category = get_category(category_id=button.category_id) + db_category = db.session.query(Category).filter(Category.id == button.category_id).one_or_none() if db_category is None: raise HTTPException(status_code=404, detail="Category does not exist") db_button = Button(category_id=button.category_id, name=button.name, @@ -46,10 +46,14 @@ def remove_button(button_id: int): @button.patch("/{button_id}", response_model=ButtonGet) -def update_button(button: ButtonUpdate): - db_old_button = db.session.query(Button).filter(button.id == button_id).one_or_none() +def update_button(button: ButtonUpdate, button_id: int): + db_old_button = db.session.query(Button).filter(Button.id == button_id).one_or_none() if db_old_button is None: raise HTTPException(status_code=404, detail="Button does not exist") - # db_old_button.id = - return db.session.query(Button).filter(Button.id == button.id).update(**button.dict(exclude_unset=True)) - # db.session.flush() + db_old_button.category_id = button.category_id or db_old_button.category_id + db_old_button.icon = button.icon or db_old_button.icon + db_old_button.name = button.name or db_old_button.name + db.session.flush() + + return db_old_button + diff --git a/services_backend/routes/category.py b/services_backend/routes/category.py index 7ffd5ef..7b42c30 100644 --- a/services_backend/routes/category.py +++ b/services_backend/routes/category.py @@ -25,7 +25,7 @@ def get_categories(skip: int = 0, limit: int = 100): @category.get("/{category_id}", response_model=CategoryGet) def get_category(category_id: int): - db_category = db.session.query(Category).filter(Category.id == category_id).first() + db_category = db.session.query(Category).filter(Category.id == category_id).one_or_none() if db_category is None: raise HTTPException(status_code=404, detail="Category does not exist") return db_category @@ -36,7 +36,7 @@ def remove_category(category_id: int): db_category = get_category(category_id=category_id) if db_category is None: raise HTTPException(status_code=404, detail="Category does not exist") - delete = db.session.query(Category).filter(Category.id == category_id).first() + delete = db.session.query(Category).filter(Category.id == category_id).one_or_none() d = db.session.query(Button).filter(Button.category_id == category_id).all() for button in d: db.session.delete(button) @@ -45,9 +45,14 @@ def remove_category(category_id: int): db.session.flush() -@category.patch("/", response_model=CategoryUpdate) -def update_category(category: CategoryUpdate): - db_old_category = get_category(category_id=category.id) +@category.patch("/{category_id}", response_model=CategoryUpdate) +def update_category(category: CategoryUpdate, category_id: int): + db_old_category = db.session.query(Category).filter(Category.id == category_id).one_or_none() if db_old_category is None: raise HTTPException(status_code=404, detail="Category does not exist") - return db.session.query(Category).update(category) + + db_old_category.type = category.type or db_old_category.type + db_old_category.name = category.name or db_old_category.name + db.session.flush() + + return db_old_category diff --git a/services_backend/routes/models/button.py b/services_backend/routes/models/button.py index cc45c37..797ec87 100644 --- a/services_backend/routes/models/button.py +++ b/services_backend/routes/models/button.py @@ -9,7 +9,6 @@ class ButtonCreate(Base): class ButtonUpdate(Base): - id: Optional[int] category_id: Optional[int] icon: Optional[str] name: Optional[str] diff --git a/services_backend/routes/models/category.py b/services_backend/routes/models/category.py index a153395..beecd6a 100644 --- a/services_backend/routes/models/category.py +++ b/services_backend/routes/models/category.py @@ -8,7 +8,6 @@ class CategoryCreate(Base): class CategoryUpdate(Base): - id: int type: Optional[str] name: Optional[str] diff --git a/services_backend/settings.py b/services_backend/settings.py index 96230f8..ef3a2eb 100644 --- a/services_backend/settings.py +++ b/services_backend/settings.py @@ -1,13 +1,10 @@ -import os -from dotenv import load_dotenv - from pydantic import BaseSettings, PostgresDsn from functools import lru_cache class Settings(BaseSettings): """Application settings""" - DB_DSN: PostgresDsn = os.getenv('DATABASE_URL') + DB_DSN: PostgresDsn CORS_ALLOW_ORIGINS: list[str] = ['*'] CORS_ALLOW_CREDENTIALS: bool = True From e02afce8d19d88f5f7e379e70001532d257626fd Mon Sep 17 00:00:00 2001 From: Wudext Date: Wed, 7 Dec 2022 21:31:02 +0300 Subject: [PATCH 32/36] Cleaning --- .gitignore | 4 +- .../versions/6739a3c84646_update_1_0.py | 7 --- requirements.dev.txt | 1 - services_backend/routes/button.py | 49 +++++++++---------- services_backend/routes/category.py | 33 ++++++------- services_backend/routes/models/button.py | 15 +++--- services_backend/routes/models/category.py | 13 +++-- 7 files changed, 52 insertions(+), 70 deletions(-) diff --git a/.gitignore b/.gitignore index f7f6a3b..0a3ac4b 100644 --- a/.gitignore +++ b/.gitignore @@ -127,6 +127,4 @@ venv.bak/ dmypy.json # Pyre type checker -.pyre/ - -desktop.ini +.pyre/ \ No newline at end of file diff --git a/migrations/versions/6739a3c84646_update_1_0.py b/migrations/versions/6739a3c84646_update_1_0.py index e6963de..816350f 100644 --- a/migrations/versions/6739a3c84646_update_1_0.py +++ b/migrations/versions/6739a3c84646_update_1_0.py @@ -1,10 +1,3 @@ -"""Update 1.0 - -Revision ID: 6739a3c84646 -Revises: -Create Date: 2022-12-02 16:02:15.329952 - -""" from alembic import op import sqlalchemy as sa diff --git a/requirements.dev.txt b/requirements.dev.txt index a07e975..e69de29 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -1 +0,0 @@ -Faker diff --git a/services_backend/routes/button.py b/services_backend/routes/button.py index 5ca1bb2..715b443 100644 --- a/services_backend/routes/button.py +++ b/services_backend/routes/button.py @@ -3,7 +3,6 @@ from .models.button import ButtonCreate, ButtonUpdate, ButtonGet from ..models.database import Button, Category -from .category import get_category button = APIRouter( tags=["button"], @@ -12,48 +11,46 @@ @button.post("/", response_model=ButtonCreate) -def create_button(button: ButtonCreate): - db_category = db.session.query(Category).filter(Category.id == button.category_id).one_or_none() - if db_category is None: +def create_button(button_inp: ButtonCreate): + category = db.session.query(Category).filter(Category.id == button_inp.category_id).one_or_none() + if not category: raise HTTPException(status_code=404, detail="Category does not exist") - db_button = Button(category_id=button.category_id, name=button.name, - icon=button.icon) - db.session.add(db_button) + button = Button(category_id=button_inp.category_id, name=button_inp.name, + icon=button_inp.icon) + db.session.add(button) db.session.flush() - return db_button + return button @button.get("/", response_model=list[ButtonGet]) -def get_buttons(skip: int = 0, limit: int = 100): - return db.session.query(Button).offset(skip).limit(limit).all() +def get_buttons(offset: int = 0, limit: int = 100): + return db.session.query(Button).offset(offset).limit(limit).all() @button.get("/{button_id}", response_model=ButtonGet) def get_button(button_id: int): - db_button = db.session.query(Button).filter(Button.id == button_id).one_or_none() - if db_button is None: + button = db.session.query(Button).filter(Button.id == button_id).one_or_none() + if not button: raise HTTPException(status_code=404, detail="Button does not exist") - return db_button + return button -@button.delete("/") +@button.delete("/{button_id}", response_model=None) def remove_button(button_id: int): - db_button = get_button(button_id=button_id) - if db_button is None: + button = get_button(button_id=button_id) + if not button: raise HTTPException(status_code=404, detail="Button does not exist") - db.session.delete(db_button) + db.session.delete(button) db.session.flush() @button.patch("/{button_id}", response_model=ButtonGet) -def update_button(button: ButtonUpdate, button_id: int): - db_old_button = db.session.query(Button).filter(Button.id == button_id).one_or_none() - if db_old_button is None: +def update_button(button_inp: ButtonUpdate, button_id: int): + button = db.session.query(Button).filter(Button.id == button_id).one_or_none() + if not button: raise HTTPException(status_code=404, detail="Button does not exist") - db_old_button.category_id = button.category_id or db_old_button.category_id - db_old_button.icon = button.icon or db_old_button.icon - db_old_button.name = button.name or db_old_button.name + button.category_id = button_inp.category_id or button.category_id + button.icon = button_inp.icon or button.icon + button.name = button_inp.name or button.name db.session.flush() - - return db_old_button - + return button diff --git a/services_backend/routes/category.py b/services_backend/routes/category.py index 7b42c30..5aa3b8e 100644 --- a/services_backend/routes/category.py +++ b/services_backend/routes/category.py @@ -19,26 +19,25 @@ def create_category(category: CategoryCreate): @category.get("/", response_model=list[CategoryGet]) -def get_categories(skip: int = 0, limit: int = 100): - return db.session.query(Category).offset(skip).limit(limit).all() +def get_categories(offset: int = 0, limit: int = 100): + return db.session.query(Category).offset(offset).limit(limit).all() @category.get("/{category_id}", response_model=CategoryGet) def get_category(category_id: int): - db_category = db.session.query(Category).filter(Category.id == category_id).one_or_none() - if db_category is None: + category = db.session.query(Category).filter(Category.id == category_id).one_or_none() + if not category: raise HTTPException(status_code=404, detail="Category does not exist") - return db_category + return category -@category.delete("/") +@category.delete("/{category_id}", response_model=None) def remove_category(category_id: int): - db_category = get_category(category_id=category_id) - if db_category is None: + category = get_category(category_id=category_id) + if category is None: raise HTTPException(status_code=404, detail="Category does not exist") delete = db.session.query(Category).filter(Category.id == category_id).one_or_none() - d = db.session.query(Button).filter(Button.category_id == category_id).all() - for button in d: + for button in db.session.query(Button).filter(Button.category_id == category_id).all(): db.session.delete(button) db.session.flush() db.session.delete(delete) @@ -46,13 +45,11 @@ def remove_category(category_id: int): @category.patch("/{category_id}", response_model=CategoryUpdate) -def update_category(category: CategoryUpdate, category_id: int): - db_old_category = db.session.query(Category).filter(Category.id == category_id).one_or_none() - if db_old_category is None: +def update_category(category_inp: CategoryUpdate, category_id: int): + category = db.session.query(Category).filter(Category.id == category_id).one_or_none() + if category is None: raise HTTPException(status_code=404, detail="Category does not exist") - - db_old_category.type = category.type or db_old_category.type - db_old_category.name = category.name or db_old_category.name + category.type = category_inp.type or category.type + category.name = category_inp.name or category.name db.session.flush() - - return db_old_category + return category diff --git a/services_backend/routes/models/button.py b/services_backend/routes/models/button.py index 797ec87..d608542 100644 --- a/services_backend/routes/models/button.py +++ b/services_backend/routes/models/button.py @@ -1,21 +1,20 @@ from .base import Base -from typing import Optional class ButtonCreate(Base): category_id: int - icon: Optional[str] - name: Optional[str] + icon: str | None + name: str | None class ButtonUpdate(Base): - category_id: Optional[int] - icon: Optional[str] - name: Optional[str] + category_id: int | None + icon: str | None + name: str | None class ButtonGet(Base): id: int category_id: int - icon: Optional[str] - name: Optional[str] + icon: str | None + name: str | None diff --git a/services_backend/routes/models/category.py b/services_backend/routes/models/category.py index beecd6a..a148d98 100644 --- a/services_backend/routes/models/category.py +++ b/services_backend/routes/models/category.py @@ -1,19 +1,18 @@ from .base import Base -from typing import Optional class CategoryCreate(Base): - type: Optional[str] - name: Optional[str] + type: str | None + name: str | None class CategoryUpdate(Base): - type: Optional[str] - name: Optional[str] + type: str | None + name: str | None class CategoryGet(Base): id: int - type: Optional[str] - name: Optional[str] + type: str | None + name: str | None From 9f41f77374038d0ebbc0ce1a30f66ad227c34fe5 Mon Sep 17 00:00:00 2001 From: Wudext Date: Wed, 7 Dec 2022 22:05:46 +0300 Subject: [PATCH 33/36] Release v1.1 --- .gitignore | 1 - migrations/versions/670f4caac7dd_init.py | 29 +++++++++++++++++ .../versions/6739a3c84646_update_1_0.py | 31 ------------------- services_backend/models/__init__.py | 3 ++ services_backend/models/database.py | 4 +-- services_backend/routes/button.py | 3 +- 6 files changed, 35 insertions(+), 36 deletions(-) create mode 100644 migrations/versions/670f4caac7dd_init.py delete mode 100644 migrations/versions/6739a3c84646_update_1_0.py diff --git a/.gitignore b/.gitignore index 0a3ac4b..9fe17bc 100644 --- a/.gitignore +++ b/.gitignore @@ -106,7 +106,6 @@ celerybeat.pid .venv env/ venv/ -.idea ENV/ env.bak/ venv.bak/ diff --git a/migrations/versions/670f4caac7dd_init.py b/migrations/versions/670f4caac7dd_init.py new file mode 100644 index 0000000..3f30c54 --- /dev/null +++ b/migrations/versions/670f4caac7dd_init.py @@ -0,0 +1,29 @@ +from alembic import op +import sqlalchemy as sa + +revision = '670f4caac7dd' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table('category', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(), nullable=True), + sa.Column('type', sa.String(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('button', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(), nullable=True), + sa.Column('category_id', sa.Integer(), nullable=True), + sa.Column('icon', sa.String(), nullable=True), + sa.ForeignKeyConstraint(['category_id'], ['category.id'], ), + sa.PrimaryKeyConstraint('id') + ) + + +def downgrade(): + op.drop_table('button') + op.drop_table('category') diff --git a/migrations/versions/6739a3c84646_update_1_0.py b/migrations/versions/6739a3c84646_update_1_0.py deleted file mode 100644 index 816350f..0000000 --- a/migrations/versions/6739a3c84646_update_1_0.py +++ /dev/null @@ -1,31 +0,0 @@ -from alembic import op -import sqlalchemy as sa - - -revision = '6739a3c84646' -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade(): - op.drop_table('button') - op.drop_table('category') - - -def downgrade(): - op.create_table('category', - sa.Column('id', sa.INTEGER(), server_default=sa.text("nextval('category_id_seq'::regclass)"), autoincrement=True, nullable=False), - sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=True), - sa.Column('type', sa.VARCHAR(), autoincrement=False, nullable=True), - sa.PrimaryKeyConstraint('id', name='category_pkey'), - postgresql_ignore_search_path=False - ) - op.create_table('button', - sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), - sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=True), - sa.Column('category_id', sa.INTEGER(), autoincrement=False, nullable=True), - sa.Column('icon', sa.VARCHAR(), autoincrement=False, nullable=True), - sa.ForeignKeyConstraint(['category_id'], ['category.id'], name='buttons_category_id_fkey'), - sa.PrimaryKeyConstraint('id', name='buttons_pkey') - ) diff --git a/services_backend/models/__init__.py b/services_backend/models/__init__.py index e69de29..eac587f 100644 --- a/services_backend/models/__init__.py +++ b/services_backend/models/__init__.py @@ -0,0 +1,3 @@ +from .database import Button, Category + +__all__ = ["Button", "Category"] \ No newline at end of file diff --git a/services_backend/models/database.py b/services_backend/models/database.py index 6527859..c275dc2 100644 --- a/services_backend/models/database.py +++ b/services_backend/models/database.py @@ -7,12 +7,12 @@ class Category(Base): id = Column(Integer, primary_key=True) name = Column(String) type = Column(String) - button = relationship("Button", back_populates="category") + button = relationship("Button", back_populates="category", foreign_keys="Button.category_id") class Button(Base): id = Column(Integer, primary_key=True) name = Column(String) category_id = Column(Integer, ForeignKey(Category.id)) - category = relationship("Category", back_populates="button") + category = relationship("Category", back_populates="button", foreign_keys=[category_id]) icon = Column(String) diff --git a/services_backend/routes/button.py b/services_backend/routes/button.py index 715b443..cce160b 100644 --- a/services_backend/routes/button.py +++ b/services_backend/routes/button.py @@ -15,8 +15,7 @@ def create_button(button_inp: ButtonCreate): category = db.session.query(Category).filter(Category.id == button_inp.category_id).one_or_none() if not category: raise HTTPException(status_code=404, detail="Category does not exist") - button = Button(category_id=button_inp.category_id, name=button_inp.name, - icon=button_inp.icon) + button = Button(**button_inp.dict()) db.session.add(button) db.session.flush() return button From e36adc76f2b0dac1d26808fa78bfb87e97d5590c Mon Sep 17 00:00:00 2001 From: Wudext Date: Wed, 7 Dec 2022 22:08:16 +0300 Subject: [PATCH 34/36] Update category.py --- services_backend/routes/category.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services_backend/routes/category.py b/services_backend/routes/category.py index 5aa3b8e..45ccab2 100644 --- a/services_backend/routes/category.py +++ b/services_backend/routes/category.py @@ -11,11 +11,11 @@ @category.post("/", response_model=CategoryCreate) -def create_category(category: CategoryCreate): - db_category = Category(type=category.type, name=category.name) - db.session.add(db_category) +def create_category(category_inp: CategoryCreate): + category = Category(**category_inp.dict()) + db.session.add(category) db.session.flush() - return db_category + return category @category.get("/", response_model=list[CategoryGet]) From d09bae48958f0b1e17a55c105610391a87410b4a Mon Sep 17 00:00:00 2001 From: Wudext Date: Wed, 7 Dec 2022 22:14:09 +0300 Subject: [PATCH 35/36] Bugfixing 5.0 --- services_backend/routes/button.py | 2 +- services_backend/routes/category.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services_backend/routes/button.py b/services_backend/routes/button.py index cce160b..b48ad4e 100644 --- a/services_backend/routes/button.py +++ b/services_backend/routes/button.py @@ -36,7 +36,7 @@ def get_button(button_id: int): @button.delete("/{button_id}", response_model=None) def remove_button(button_id: int): - button = get_button(button_id=button_id) + button = db.session.query(Button).filter(Button.id == button_id).one_or_none() if not button: raise HTTPException(status_code=404, detail="Button does not exist") db.session.delete(button) diff --git a/services_backend/routes/category.py b/services_backend/routes/category.py index 45ccab2..2b87c4d 100644 --- a/services_backend/routes/category.py +++ b/services_backend/routes/category.py @@ -33,7 +33,7 @@ def get_category(category_id: int): @category.delete("/{category_id}", response_model=None) def remove_category(category_id: int): - category = get_category(category_id=category_id) + category = db.session.query(Category).filter(Category.id == category_id).one_or_none() if category is None: raise HTTPException(status_code=404, detail="Category does not exist") delete = db.session.query(Category).filter(Category.id == category_id).one_or_none() From 880fb70d16edceb96e884a12108817a1467daca2 Mon Sep 17 00:00:00 2001 From: Wudext Date: Wed, 7 Dec 2022 22:19:27 +0300 Subject: [PATCH 36/36] Bugfixing 6.0 --- services_backend/routes/base.py | 5 ++--- services_backend/routes/button.py | 5 +---- services_backend/routes/category.py | 6 +----- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/services_backend/routes/base.py b/services_backend/routes/base.py index eea49cb..657ef1b 100644 --- a/services_backend/routes/base.py +++ b/services_backend/routes/base.py @@ -25,6 +25,5 @@ allow_headers=settings.CORS_ALLOW_HEADERS, ) - -app.include_router(button, prefix='/button') -app.include_router(category, prefix='/category') \ No newline at end of file +app.include_router(button, prefix='/button', tags=["category"]) +app.include_router(category, prefix='/category', tags=["button"]) \ No newline at end of file diff --git a/services_backend/routes/button.py b/services_backend/routes/button.py index b48ad4e..bb6d54f 100644 --- a/services_backend/routes/button.py +++ b/services_backend/routes/button.py @@ -4,10 +4,7 @@ from .models.button import ButtonCreate, ButtonUpdate, ButtonGet from ..models.database import Button, Category -button = APIRouter( - tags=["button"], - responses={200: {"description": "Ok"}} -) +button = APIRouter() @button.post("/", response_model=ButtonCreate) diff --git a/services_backend/routes/category.py b/services_backend/routes/category.py index 2b87c4d..d538082 100644 --- a/services_backend/routes/category.py +++ b/services_backend/routes/category.py @@ -4,11 +4,7 @@ from .models.category import CategoryCreate, CategoryUpdate, CategoryGet from ..models.database import Category, Button -category = APIRouter( - tags=["category"], - responses={200: {"description": "Ok"}} -) - +category = APIRouter() @category.post("/", response_model=CategoryCreate) def create_category(category_inp: CategoryCreate):