From 3325e4a6a094872f7b767212599d1b872d139c71 Mon Sep 17 00:00:00 2001 From: zaapptt Date: Sat, 20 Sep 2025 15:41:18 +0800 Subject: [PATCH] feat: Implementation of Flask API --- .gitignore | 1 + app.py | 22 ++++++++ controller/__init__.py | 1 + .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 265 bytes .../library_controller.cpython-313.pyc | Bin 0 -> 3451 bytes controller/library_controller.py | 47 ++++++++++++++++++ instance/library.db | Bin 0 -> 12288 bytes models/__init__.py | 5 ++ models/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 291 bytes models/__pycache__/library.cpython-313.pyc | Bin 0 -> 1019 bytes models/library.py | 15 ++++++ repositories/__init__.py | 1 + .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 238 bytes .../library_repositories.cpython-313.pyc | Bin 0 -> 2355 bytes repositories/library_repositories.py | 39 +++++++++++++++ requirements.txt | 15 ++++++ service/__init__.py | 1 + service/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 225 bytes .../library_service.cpython-313.pyc | Bin 0 -> 1715 bytes service/library_service.py | 28 +++++++++++ 20 files changed, 175 insertions(+) create mode 100644 .gitignore create mode 100644 app.py create mode 100644 controller/__init__.py create mode 100644 controller/__pycache__/__init__.cpython-313.pyc create mode 100644 controller/__pycache__/library_controller.cpython-313.pyc create mode 100644 controller/library_controller.py create mode 100644 instance/library.db create mode 100644 models/__init__.py create mode 100644 models/__pycache__/__init__.cpython-313.pyc create mode 100644 models/__pycache__/library.cpython-313.pyc create mode 100644 models/library.py create mode 100644 repositories/__init__.py create mode 100644 repositories/__pycache__/__init__.cpython-313.pyc create mode 100644 repositories/__pycache__/library_repositories.cpython-313.pyc create mode 100644 repositories/library_repositories.py create mode 100644 requirements.txt create mode 100644 service/__init__.py create mode 100644 service/__pycache__/__init__.cpython-313.pyc create mode 100644 service/__pycache__/library_service.cpython-313.pyc create mode 100644 service/library_service.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b694934 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.venv \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..5e9dcc5 --- /dev/null +++ b/app.py @@ -0,0 +1,22 @@ +from flask import Flask +from flask_restful import Api +from models import db +from controller import LibraryListResource, LibraryResource + +app = Flask(__name__) +api = Api(app) + +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///library.db' +app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + +db.init_app(app) + +@app.before_request +def create_tables(): + db.create_all() + +api.add_resource(LibraryListResource, '/libraries') +api.add_resource(LibraryResource, '/libraries/') + +if __name__ == '__main__': + app.run(debug=True) \ No newline at end of file diff --git a/controller/__init__.py b/controller/__init__.py new file mode 100644 index 0000000..5c80529 --- /dev/null +++ b/controller/__init__.py @@ -0,0 +1 @@ +from .library_controller import LibraryListResource, LibraryResource \ No newline at end of file diff --git a/controller/__pycache__/__init__.cpython-313.pyc b/controller/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc9f346ae09de609149e8dfb51986306a6e2db3d GIT binary patch literal 265 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 Z7GYYr+4q28|G?+~`R=8M1bcp~{{d4LzwrP7 literal 0 HcmV?d00001 diff --git a/controller/library_controller.py b/controller/library_controller.py new file mode 100644 index 0000000..a8695c7 --- /dev/null +++ b/controller/library_controller.py @@ -0,0 +1,47 @@ +from flask_restful import Resource +from flask import request +from service import LibraryService +from models import db + +class LibraryListResource(Resource): + def get(self): + books = LibraryService.get_all() + return [book.to_dict() for book in books], 200 + + def post(self): + try: + data = request.get_json() + if 'isbn' not in data or 'title' not in data or 'author' not in data: + return {"message": "ISBN, title, and author are required"}, 400 + book = LibraryService.create(data) + return book.to_dict(), 201 + except Exception as e: + db.session.rollback() + return {"error": "Unexpected error occured", "details": str(e)}, 500 + +class LibraryResource(Resource): + def get(self, isbn): + book = LibraryService.get_by_id(isbn) + if not book: + return {"message": "Book not found"}, 404 + return book.to_dict(), 200 + + def put(self, isbn): + try: + data = request.get_json() + book = LibraryService.update(isbn, data) + if not book: + return {"message": "Book not found"}, 404 + return book.to_dict(), 200 + except Exception as e: + db.session.rollback() + return {"error": "Unexpected error occured", "details": str(e)}, 500 + + def delete(self, isbn): + try: + book = LibraryService.delete(isbn) + if not book: + return {"message": "Book not found"}, 404 + return {"message": "Book deleted"}, 200 + except Exception as e: + return {"error": "Unexpected error occured", "details": str(e)}, 500 diff --git a/instance/library.db b/instance/library.db new file mode 100644 index 0000000000000000000000000000000000000000..8748686c538b3f6b2d5fbab2fe4d19168d0339ef GIT binary patch literal 12288 zcmeI#F-yZh6bJCTG*J^M*TK@kA#ZC7A{3`?#u8MFRbv!7g%nQ=!~~Noxa#Ql@&maE zF6M*=ghEH5{2yHMULJSk{kFTg4pJrQp(vjcMTe}zIA=#hj4`WTt$Hnk*NM3->bI`f zg~bluciZBN8KTcbUwo|FfIb8u009U<00Izz00bZa0SG|g9|d0b_;$~A`J1JZc_y=T zUM6KKt0plfq3=aLMc#4XQ}dYyj-6KXobJ4Ea_WUNy^Lsj8wC60*eX?-T-iw$>ai$S zwywgn3oo3}xj&a3N5)pqcR5S*d-+m5XZ4Lfu8$~w>$v%uc+{l&UcaL~+xpRA zOnmAD0Rad=00Izz00bZa0SG_<0uX?}MhLXIDZ1V9iOgjrE9K~+^!@*PBc{U;5P$## aAOHafKmY;|fB*y_009X66);=E9{&J=PEKL~ literal 0 HcmV?d00001 diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..2b42497 --- /dev/null +++ b/models/__init__.py @@ -0,0 +1,5 @@ +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() + +from .library import Library \ No newline at end of file diff --git a/models/__pycache__/__init__.cpython-313.pyc b/models/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..787f371997bb2b9bac0ca55ad13601a9084058f3 GIT binary patch literal 291 zcmey&%ge<81d2iDG9rNVV-N=hn4pZ$T0q8BhG2$ZMsEf$#v(=qh8RXA22-eX5mPWz zFtaza7fTU~0z*2BChJR(j3(nPuHZl)$DHJh)ZEIKj1VDqpUkA9#G*<+P1aiiX*r3- z+404NIf)QOMa)1&x0q6rZn5V;lohc8MOHF=29gZ7-1I|>Q;UlAqZ0E{Qj7FmQj4=o z@(c6>g8bcm9R1w&11d{0^7Hf|ra}$S&&^Ls%_-K8kI&4@EQycTE2zB1VFMH_%}KQ@ k;sF{9a#*ntkodsN$jEq?LH!{se{)O&(=!H{B2d@>0Gq)}LjV8( literal 0 HcmV?d00001 diff --git a/models/__pycache__/library.cpython-313.pyc b/models/__pycache__/library.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0c61c86cada8039d054e17f638a5ca3e48db4ebb GIT binary patch literal 1019 zcmbVK%}*0S6o32Gk8Kxf1cO{aqK95sIB=*jCPqFiLM0tJl#onz+l6kr-QvvdVQ)Pd zB{8K3(tiQ|3;zNWZp|bnB%ZioDtBkPtzh(`bNJ2s`n~t&%^Mho4*Yy7f2oHOz&D|c zk2VqpPq}ak2$1v|HYFm>!wf6{l4k)a`%+mQ6K?MnC6QIJg3eXX3m8y?FG!0i)%+L40Pf=p8X$kI@Pv(o>?(ou32NCu@y z_As5-S#ry@iAAC-@p_i_hMMcpxBTsyBjPr>4>z1>o$Dz-^gPS0Du|OKqQNfg5-ZOITr0ZxNE=bl#3i9q)jO zR_587xl0{F%{|MnImBFbXd`GHnLFh-8(S|*8|F^LnZ9}8S+s%UM@D9|RdYOQj?~4Y zh$Vv-uDR8KO^(L0I5LQhqmMYW;c{xGSL_!1(+_)l-M#*U*@aXB#$-wa+!v4&Al7#XA}@W#;XU-H z=Gxnr8KYA;E}WEmm2Ty1^&+#}UhQWmI>qDSiQRL%?%9)z>{5HJZ%lS7$CVS(i@MR- z%7wAqUcXT_O^=I+{H46gAZ}dS79) OV0Y>t>o>r~VfvpPS=#FW literal 0 HcmV?d00001 diff --git a/models/library.py b/models/library.py new file mode 100644 index 0000000..03c03f7 --- /dev/null +++ b/models/library.py @@ -0,0 +1,15 @@ +from . import db + +class Library(db.Model): + __tablename__ = 'libraries' + + isbn = db.Column(db.String, primary_key=True) + title = db.Column(db.String, nullable=False) + author = db.Column(db.String, nullable=False) + + def to_dict(self): + return { + 'isbn': self.isbn, + 'title': self.title, + 'author': self.author + } \ No newline at end of file diff --git a/repositories/__init__.py b/repositories/__init__.py new file mode 100644 index 0000000..ed39183 --- /dev/null +++ b/repositories/__init__.py @@ -0,0 +1 @@ +from .library_repositories import LibraryRepository diff --git a/repositories/__pycache__/__init__.cpython-313.pyc b/repositories/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..46f587b535bd77c81cf367551502ce72bcec8210 GIT binary patch literal 238 zcmey&%ge<81O}n!GE9KVNJ$c zfjfh5B%AN|nc)S_bj zsKmUK)FOSC)Z*-t`~v-eAb)osM?ZJ{fXb4L{5<`%oW$bnc&KUm$foJX$7kkcmc+;F m6;$5hu*uC&Da}c>E8+ke4su#Ci1C4$k&*EpgK-fXkOKfEA3^2- literal 0 HcmV?d00001 diff --git a/repositories/__pycache__/library_repositories.cpython-313.pyc b/repositories/__pycache__/library_repositories.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..befe856a1db75561886a301d8fe65ec2a9e6c2de GIT binary patch literal 2355 zcmbtV&1)M+6rcT)*7C>3vEt7(QEWA?LWwBYw-Q=XaB&kiNQj2mu&lIWd#jb?%&Y=B z1ad0&$+;NZOPg!{nUG*po58e&_L7@Y6?!XuZ+6#KWygj39p1k8=FQBT_xsIkXK*k@ zK$73QT3^rz`3;>|^vLLZ1jY_2lA`hzSyY&^s4_J}#>q4(s*|Ls&8uN1)7JEy@fMw& zDw6lSGILlaGo2iOEw3z8#Mt zzWfTPXXJNlbo=Sn(*rB>#>(tj)342GHV7jX1yn3}4B3~@!1zCXDUtbTy)0pUut?M! zh8z^y8Vz!a&^=zRbIi-IA&jSFz2%nex#H@3P9sN6$mV09dd~AAy=UEgZQeZRM9*N> z#ZHv@FP%PD|dh%a9`sFQ7FplIsq4f`B1@ z0U8obhNNT@pmGKqLHh$#lc0oFr7dARar`Fb_>^Q&LgFWy8-N5Af10GP z9~p86BhG#WUctr}rRc@wn*7_t>HF(ve0-coo!lL zZM516urM}z9Gi_|nvsrTV6{_nM#@O<&UFZUq9L%_<8p~)6ziy9g>_&FOF3O6pbgaL qA)tC(S5Lvh{50&=9rg*N01pYro7L77MfrtX`%BL%=|2c4x%fYi?5Jk| literal 0 HcmV?d00001 diff --git a/repositories/library_repositories.py b/repositories/library_repositories.py new file mode 100644 index 0000000..22842d1 --- /dev/null +++ b/repositories/library_repositories.py @@ -0,0 +1,39 @@ +from models import Library, db + +class LibraryRepository: + @staticmethod + def find_all(): + return Library.query.all() + + @staticmethod + def find_by_id(isbn): + return Library.query.get(isbn) + + @staticmethod + def save(book): + db.session.add(book) + db.session.commit() + return book + + @staticmethod + def update(isbn, data: dict): + book = Library.query.get(isbn) + if not book: + return None + + for key, value in data.items(): + if hasattr(book, key): + setattr(book, key, value) + + db.session.commit() + return book + + @staticmethod + def delete(isbn): + book = Library.query.get(isbn) + if not book: + return None + + db.session.delete(book) + db.session.commit() + return book \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..d494ed6 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,15 @@ +aniso8601==10.0.1 +blinker==1.9.0 +click==8.3.0 +Flask==3.1.2 +Flask-RESTful==0.3.10 +Flask-SQLAlchemy==3.1.1 +greenlet==3.2.4 +itsdangerous==2.2.0 +Jinja2==3.1.6 +MarkupSafe==3.0.2 +pytz==2025.2 +six==1.17.0 +SQLAlchemy==2.0.43 +typing_extensions==4.15.0 +Werkzeug==3.1.3 diff --git a/service/__init__.py b/service/__init__.py new file mode 100644 index 0000000..f6bf3ff --- /dev/null +++ b/service/__init__.py @@ -0,0 +1 @@ +from .library_service import LibraryService \ No newline at end of file diff --git a/service/__pycache__/__init__.cpython-313.pyc b/service/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f26b5a97c9f63def15dc888c49774c15f221cd56 GIT binary patch literal 225 zcmey&%ge<81g;_HGPHs8V-N=hn4pZ$0zk%8hG2$ZMsEf$#v(=qhIA%P=9i2>VNJ$c zd_I{;MTtd~!Kp=MnaQbsnoPI&b09+T#SpO~W}woQ44;7{!!39HP@r(JepF&!N@|h5 zOKNd;Nq&KTK#;$?kE5Tven4eOMt+`tT25kdc0AM|eW*eD@$s2?nI-Y@dIgoYIBatB fQ%ZAE?TR>n=7L;P3}Sp>W@Kc%$Dm!r2IK$$BOE*b literal 0 HcmV?d00001 diff --git a/service/__pycache__/library_service.cpython-313.pyc b/service/__pycache__/library_service.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f57f28161dbab0f9d6d58315125143f0f8b2d0c GIT binary patch literal 1715 zcmb_c&ube;6rR~%S}S(ec526%Kvk0vHjbqxu@8X~C|A}`NkvTp$TS<&9Ma*nDZ>KR`3K`U~SmG z^NC!a>#4_a3qt6x%I&U-(=PN8tQ26Il7_1&XM+flUG|It4;UB$2lJT~81rnU$OIc> zyt0fPjIEKlg0VdkyBIqo@mNrS>W1r&>5B!CK*GzoEkBXyGo(xe2RAcPPYVi+tq$py z2*XYcDP3iIyy+W2rP^JOJCO*JWRDFfTQ?hPN`h+q7q)3KMUwZgffYotQN*H%>)U-@*^nunuV?=$r(WPTUaF?hyk@_9wGW_L4+jOlnW1nQw_pwp0(4eBeM^Ne zrEh2}3e!+qt!{Ty(VexT6HXgsm`Xi!stq|z%~H!~s0Xu0Q!{VYcdi^w&%SxJvvhRn z@>_T3-sg%nS^e4iv&tsF`rutV@UFd!MyFc}#8ZgYf0*tMeE6e2HC?B-jdD;{u6&+Y zj);OU5jdkLubHLXk*Quk@UFkN4!t}3?wzw!{lA{06UjuT5~k_)lXnOU=Ml|wGg0Rd zUD|h-GNNz+i<|xh?F!Kex1Luil3-O{CCdG~?#Ci-WP2!I1bZJ|qVH&r$ zBtD@CF5*$}1(_>ksDA}FA@W2KrYXd&UMiIk+0{7-dLclMKQlC76`}cw9obW#4Q}5Y zn9P|zChyomkFeV%cDulwG7r7u$s%vL%v&yN>lrq{Q|dVZy1VJydK`v-@v&6-+jp=T vGEw=jA>&9bJK?R}Z}N8C1NwiZxu_X5$0q0eBfIgD&HrW1@%kSOO;+|dSL|GP literal 0 HcmV?d00001 diff --git a/service/library_service.py b/service/library_service.py new file mode 100644 index 0000000..9914d53 --- /dev/null +++ b/service/library_service.py @@ -0,0 +1,28 @@ +from repositories import LibraryRepository +from models import Library + +class LibraryService: + @staticmethod + def get_all(): + return LibraryRepository.find_all() + + @staticmethod + def get_by_id(isbn): + return LibraryRepository.find_by_id(isbn) + + @staticmethod + def create(data): + book = Library( + isbn=data['isbn'], + title=data['title'], + author=data['author'] + ) + return LibraryRepository.save(book) + + @staticmethod + def update(isbn, data): + return LibraryRepository.update(isbn, data) + + @staticmethod + def delete(isbn): + return LibraryRepository.delete(isbn) \ No newline at end of file