From 14d4dbad02faa0463d4c4d05b33d291da6530eb3 Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Sat, 1 Apr 2023 00:29:31 +0200 Subject: [PATCH 01/27] Create database --- ERD_pgadmin.pgerd | 1 + cli_library.sql | 141 ++++++++++++++++++++++++++++++++++++++++++++++ config.py | 18 ++++++ database.ini | 11 ++++ database.py | 117 ++++++++++++++++++++++++++++++++++++++ main.py | 5 +- 6 files changed, 291 insertions(+), 2 deletions(-) create mode 100644 ERD_pgadmin.pgerd create mode 100644 cli_library.sql create mode 100644 config.py create mode 100644 database.ini create mode 100644 database.py diff --git a/ERD_pgadmin.pgerd b/ERD_pgadmin.pgerd new file mode 100644 index 0000000..59715a0 --- /dev/null +++ b/ERD_pgadmin.pgerd @@ -0,0 +1 @@ +{"version":"61900","data":{"id":"b41eebce-a176-4486-8c81-1fc06b5e7688","offsetX":50.54391676608046,"offsetY":89.10141817942956,"zoom":61.1001571746585,"gridSize":15,"layers":[{"id":"b887badb-1d8d-457b-97ec-bfb9fe483884","type":"diagram-links","isSvg":true,"transformed":true,"models":{"6515d4b3-6d5e-4cb4-9a7f-e9f8cb4b20f7":{"id":"6515d4b3-6d5e-4cb4-9a7f-e9f8cb4b20f7","locked":true,"type":"onetomany","source":"c1fdccbc-74d1-496b-9142-656123067b0d","sourcePort":"98f66729-6d74-4d74-ab9c-2a9e99633c72","target":"1d28ddb0-5e6b-42cd-a90d-fc34f11279ef","targetPort":"00c9d9d4-f959-49e8-8b1f-b9df732cb115","points":[{"id":"0bbb927b-1973-46dc-ba8f-4b41c3c043c6","type":"point","x":491.25002695399326,"y":97.00000885587966},{"id":"d0d19dd5-fe76-4806-949e-72e5d1a25e31","type":"point","x":215.00000136632178,"y":186},{"id":"c7ef2514-14c7-4913-99fe-38c089a2a751","type":"point","x":215.00000136632178,"y":315.4999868091702}],"labels":[],"width":1,"color":"gray","curvyness":50,"selectedColor":"rgb(0,192,255)","data":{"local_table_uid":"1d28ddb0-5e6b-42cd-a90d-fc34f11279ef","local_column_attnum":1,"referenced_table_uid":"c1fdccbc-74d1-496b-9142-656123067b0d","referenced_column_attnum":1}},"a40b1153-d189-4ed6-ab1c-bc7b99db50d7":{"id":"a40b1153-d189-4ed6-ab1c-bc7b99db50d7","locked":true,"type":"onetomany","source":"fbe973a1-7623-4936-b20d-cec69adb7a8a","sourcePort":"7af7336a-8eac-4d0e-b151-52393fe64972","target":"1d28ddb0-5e6b-42cd-a90d-fc34f11279ef","targetPort":"b8320f0e-765a-4be1-8347-0bc2c2acc6b4","points":[{"id":"dd449db5-3e96-438a-acb7-80d8ffe67502","type":"point","x":205.0000011180822,"y":103.49998654117184},{"id":"3323656f-5cc9-4706-bccd-d1c6221a2745","type":"point","x":215.00000136632178,"y":186},{"id":"41c62ed5-41fd-4f34-994f-8f53ef52027d","type":"point","x":215.00000136632178,"y":340.50001240317306}],"labels":[],"width":1,"color":"gray","curvyness":50,"selectedColor":"rgb(0,192,255)","data":{"local_table_uid":"1d28ddb0-5e6b-42cd-a90d-fc34f11279ef","local_column_attnum":2,"referenced_table_uid":"fbe973a1-7623-4936-b20d-cec69adb7a8a","referenced_column_attnum":1}},"5ef677b4-9bac-4cf6-b108-3ab512338ab4":{"id":"5ef677b4-9bac-4cf6-b108-3ab512338ab4","locked":true,"type":"onetomany","source":"29b34d1a-c75a-434e-9d48-11ae9cfa4b98","sourcePort":"fc7c6ac7-aa77-4756-b74e-0404e31c759b","target":"0847390b-f33d-44c3-9231-2d95af94484d","targetPort":"3a668bf8-70dd-46b7-9eca-11bf0423ed10","points":[{"id":"76591a83-64fe-43c7-a6af-95cef1496f64","type":"point","x":1073.749978980439,"y":302.9999989855727},{"id":"ee8ab820-aa0b-48b8-8ceb-1a3f1e39a05c","type":"point","x":838.7500480670208,"y":392},{"id":"440e427e-25c3-44b8-8c2d-da83f48325cc","type":"point","x":838.7500480670208,"y":533.9999897358647}],"labels":[],"width":1,"color":"gray","curvyness":50,"selectedColor":"rgb(0,192,255)","data":{"local_table_uid":"0847390b-f33d-44c3-9231-2d95af94484d","local_column_attnum":2,"referenced_table_uid":"29b34d1a-c75a-434e-9d48-11ae9cfa4b98","referenced_column_attnum":1}},"0ac97408-789f-4117-8749-5792a8ef44f4":{"id":"0ac97408-789f-4117-8749-5792a8ef44f4","locked":true,"type":"onetomany","source":"547ab8da-828a-4da4-813c-4f49478cb2e0","sourcePort":"bdb75708-6cb6-414e-887a-80c8c8406ae0","target":"0847390b-f33d-44c3-9231-2d95af94484d","targetPort":"127204e8-cf67-4eb2-8901-0fdf3e9e2a7f","points":[{"id":"45dc2622-cb53-43d4-9f40-f49acae13d04","type":"point","x":777.5000278165003,"y":315.4999868091702},{"id":"4d7d8080-6b8f-45e4-a029-047f445d9f90","type":"point","x":838.7500480670208,"y":392},{"id":"d078a23f-9d12-4c27-b859-fddb63746a6e","type":"point","x":838.7500480670208,"y":564.999975521365}],"labels":[],"width":1,"color":"gray","curvyness":50,"selectedColor":"rgb(0,192,255)","data":{"local_table_uid":"0847390b-f33d-44c3-9231-2d95af94484d","local_column_attnum":3,"referenced_table_uid":"547ab8da-828a-4da4-813c-4f49478cb2e0","referenced_column_attnum":1}},"30c29456-f934-4463-b30f-b37c0bb7e6a9":{"id":"30c29456-f934-4463-b30f-b37c0bb7e6a9","locked":true,"type":"onetomany","source":"c1fdccbc-74d1-496b-9142-656123067b0d","sourcePort":"98f66729-6d74-4d74-ab9c-2a9e99633c72","target":"eeed6c09-6955-4882-b65f-8d7a274beebb","targetPort":"1cba911c-e90c-4f71-b57b-8f87b023672c","points":[{"id":"2eb91747-9a95-488b-9d6f-b6a621d1b180","type":"point","x":491.25002695399326,"y":97.00000885587966},{"id":"422798c9-7427-46a7-9e01-cf5aac5ceeb0","type":"point","x":491.25002695399326,"y":186},{"id":"9ea687ed-f6ee-4489-b57d-69ef2e654f5c","type":"point","x":276.25,"y":289},{"id":"d4ef7a02-3627-4443-ae25-92d4decc80b7","type":"point","x":501.25002720223284,"y":392},{"id":"c40f3039-5b5c-4390-8b8b-72f8a003b00a","type":"point","x":501.25002720223284,"y":533.9999897358647}],"labels":[],"width":1,"color":"gray","curvyness":50,"selectedColor":"rgb(0,192,255)","data":{"local_table_uid":"eeed6c09-6955-4882-b65f-8d7a274beebb","local_column_attnum":2,"referenced_table_uid":"c1fdccbc-74d1-496b-9142-656123067b0d","referenced_column_attnum":1}},"61a12a09-86b1-423d-8730-d934bfa0bf7a":{"id":"61a12a09-86b1-423d-8730-d934bfa0bf7a","locked":true,"type":"onetomany","source":"547ab8da-828a-4da4-813c-4f49478cb2e0","sourcePort":"bdb75708-6cb6-414e-887a-80c8c8406ae0","target":"eeed6c09-6955-4882-b65f-8d7a274beebb","targetPort":"191307db-843c-4872-bfac-3f393342dcc6","points":[{"id":"400b68da-ccff-4239-959b-629d56099734","type":"point","x":777.5000278165003,"y":315.4999868091702},{"id":"5e9ebb72-d015-4fc9-b77f-5e1b610c5303","type":"point","x":501.25002720223284,"y":392},{"id":"634e0df7-82d3-4400-bee0-fec9cfe26413","type":"point","x":501.25002720223284,"y":564.999975521365}],"labels":[],"width":1,"color":"gray","curvyness":50,"selectedColor":"rgb(0,192,255)","data":{"local_table_uid":"eeed6c09-6955-4882-b65f-8d7a274beebb","local_column_attnum":3,"referenced_table_uid":"547ab8da-828a-4da4-813c-4f49478cb2e0","referenced_column_attnum":1}},"205dff12-3209-4e26-81ae-7bc5309164a3":{"id":"205dff12-3209-4e26-81ae-7bc5309164a3","locked":true,"type":"onetomany","source":"c1fdccbc-74d1-496b-9142-656123067b0d","sourcePort":"98f66729-6d74-4d74-ab9c-2a9e99633c72","target":"4f8664ed-c05b-4aff-be60-83bcafc7a9d6","targetPort":"e95f15da-96c9-4285-b756-62d27c689ed7","points":[{"id":"0c587dfb-accb-42a8-abcd-17fffa9286a4","type":"point","x":491.25002695399326,"y":97.00000885587966},{"id":"379e19bf-19af-4dfd-8ae1-2efc7a93e8d9","type":"point","x":552.4999972577058,"y":186},{"id":"67bbc557-35bf-4ec1-8c02-d76f820c4bf8","type":"point","x":552.4999972577058,"y":315.4999868091702}],"labels":[],"width":1,"color":"gray","curvyness":50,"selectedColor":"rgb(0,192,255)","data":{"local_table_uid":"4f8664ed-c05b-4aff-be60-83bcafc7a9d6","local_column_attnum":1,"referenced_table_uid":"c1fdccbc-74d1-496b-9142-656123067b0d","referenced_column_attnum":1}},"7fa69583-b9f5-4e3c-9752-6bc830f7b767":{"id":"7fa69583-b9f5-4e3c-9752-6bc830f7b767","locked":true,"type":"onetomany","source":"0fbca123-9dfc-4baf-a4e1-3fba899e1337","sourcePort":"b213fddd-731a-4ba0-a19a-126d4ac9123e","target":"4f8664ed-c05b-4aff-be60-83bcafc7a9d6","targetPort":"f5a28c1e-3130-4b18-888a-19836346a859","points":[{"id":"ddcccce8-0e67-4971-aecc-562f83d47e2e","type":"point","x":1083.7500291754866,"y":109.49999667947716},{"id":"f3257cb0-270f-4860-9b07-672ba60c3e4e","type":"point","x":552.4999972577058,"y":186},{"id":"df2cab32-5138-4e66-82b1-15330792c316","type":"point","x":552.4999972577058,"y":340.50001240317306}],"labels":[],"width":1,"color":"gray","curvyness":50,"selectedColor":"rgb(0,192,255)","data":{"local_table_uid":"4f8664ed-c05b-4aff-be60-83bcafc7a9d6","local_column_attnum":2,"referenced_table_uid":"0fbca123-9dfc-4baf-a4e1-3fba899e1337","referenced_column_attnum":1}},"bfb73a50-5896-49f5-87d7-081ec6498321":{"id":"bfb73a50-5896-49f5-87d7-081ec6498321","locked":true,"type":"onetomany","source":"c1fdccbc-74d1-496b-9142-656123067b0d","sourcePort":"98f66729-6d74-4d74-ab9c-2a9e99633c72","target":"29b34d1a-c75a-434e-9d48-11ae9cfa4b98","targetPort":"e8b0f8fc-5eb4-4853-b2bf-c1bf2a599a1f","points":[{"id":"08627433-e808-43be-8edf-446cf787728d","type":"point","x":491.25002695399326,"y":97.00000885587966},{"id":"6e4320c0-db59-4337-b141-ffefaf3b55d1","type":"point","x":1073.749978980439,"y":186},{"id":"fde1a187-4740-43d8-9013-fe4fd23a445e","type":"point","x":1073.749978980439,"y":327.9999746327677}],"labels":[],"width":1,"color":"gray","curvyness":50,"selectedColor":"rgb(0,192,255)","data":{"local_table_uid":"29b34d1a-c75a-434e-9d48-11ae9cfa4b98","local_column_attnum":2,"referenced_table_uid":"c1fdccbc-74d1-496b-9142-656123067b0d","referenced_column_attnum":1}},"afdade8e-d42b-4239-a28f-d9cf708c8024":{"id":"afdade8e-d42b-4239-a28f-d9cf708c8024","locked":true,"type":"onetomany","source":"547ab8da-828a-4da4-813c-4f49478cb2e0","sourcePort":"bdb75708-6cb6-414e-887a-80c8c8406ae0","target":"c9ddebcd-6725-4c03-b607-a492250e61d8","targetPort":"fdf04ed8-6e4f-4fed-8143-f09bf60ca64a","points":[{"id":"e9bcd57e-64e6-45c9-8f13-c51fc0da72c1","type":"point","x":777.5000278165003,"y":315.4999868091702},{"id":"41adc5ed-6023-47a8-9730-bc5122907ff3","type":"point","x":777.5000278165003,"y":392},{"id":"47415886-738d-4ed1-b28e-ed4c30b833d3","type":"point","x":941.25,"y":501},{"id":"8a7b739f-9a9a-4a76-85c2-6716475a3780","type":"point","x":838.7500480670208,"y":610},{"id":"70852b3a-b4b9-4a6b-83eb-e3c7b1729985","type":"point","x":838.7500480670208,"y":757.9999853070698}],"labels":[],"width":1,"color":"gray","curvyness":50,"selectedColor":"rgb(0,192,255)","data":{"local_table_uid":"c9ddebcd-6725-4c03-b607-a492250e61d8","local_column_attnum":2,"referenced_table_uid":"547ab8da-828a-4da4-813c-4f49478cb2e0","referenced_column_attnum":1}},"faeba05a-1d1d-4907-b565-39763ed2ecaa":{"id":"faeba05a-1d1d-4907-b565-39763ed2ecaa","locked":true,"type":"onetomany","source":"0847390b-f33d-44c3-9231-2d95af94484d","sourcePort":"1ee6e35b-917f-4385-83a7-b54071fea295","target":"c9ddebcd-6725-4c03-b607-a492250e61d8","targetPort":"61695174-3f0c-4e07-857e-20e5b07a334d","points":[{"id":"790f1ecf-143a-4e24-bc73-06315c84a841","type":"point","x":838.7500480670208,"y":509.0000140886697},{"id":"9a198268-9809-48b6-a59b-c3bf97e87e26","type":"point","x":838.7500480670208,"y":610},{"id":"56cf0bca-93ff-49af-b060-c3750ad21a50","type":"point","x":838.7500480670208,"y":788.99997109257}],"labels":[],"width":1,"color":"gray","curvyness":50,"selectedColor":"rgb(0,192,255)","data":{"local_table_uid":"c9ddebcd-6725-4c03-b607-a492250e61d8","local_column_attnum":3,"referenced_table_uid":"0847390b-f33d-44c3-9231-2d95af94484d","referenced_column_attnum":1}}}},{"id":"3b099ebb-a451-49e5-a3ac-efef2d12644d","type":"diagram-nodes","isSvg":false,"transformed":true,"models":{"fbe973a1-7623-4936-b20d-cec69adb7a8a":{"id":"fbe973a1-7623-4936-b20d-cec69adb7a8a","type":"table","x":5,"y":11.5,"ports":[{"id":"76d639b3-34ea-4541-8c85-f9bc7a32b8fb","type":"onetomany","x":10.000003265480075,"y":103.49998654117184,"name":"coll-port-1-left","alignment":"left","parentNode":"fbe973a1-7623-4936-b20d-cec69adb7a8a","links":[]},{"id":"7af7336a-8eac-4d0e-b151-52393fe64972","type":"onetomany","x":175.0000011180822,"y":103.49998654117184,"name":"coll-port-1-right","alignment":"right","parentNode":"fbe973a1-7623-4936-b20d-cec69adb7a8a","links":["a40b1153-d189-4ed6-ab1c-bc7b99db50d7"]}],"name":"Untitled","color":"rgb(0,192,255)","portsInOrder":[],"portsOutOrder":["76d639b3-34ea-4541-8c85-f9bc7a32b8fb","7af7336a-8eac-4d0e-b151-52393fe64972"],"otherInfo":{"data":{"columns":[{"name":"id","atttypid":23,"attlen":null,"attnum":1,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":"nextval('author_id_seq'::regclass)","typname":"integer","displaytypname":"integer","cltype":"integer","elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"author","is_view_only":false,"seqrelid":20844,"seqtypid":23,"seqstart":"1","seqincrement":"1","seqmax":"2147483647","seqmin":"1","seqcache":"1","seqcycle":false,"is_pk":true,"is_primary_key":true,"attprecision":null,"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"author_name","atttypid":1043,"attlen":"255","attnum":2,"attndims":0,"atttypmod":259,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":null,"typname":"character varying","displaytypname":"character varying(255)","cltype":"character varying","elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"author","is_view_only":false,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"edit_types":["\"char\"","character","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]}],"name":"author","schema":"public","description":null,"rlspolicy":false,"forcerlspolicy":false,"fillfactor":null,"toast_tuple_target":null,"parallel_workers":null,"relpersistence":false,"primary_key":[{"oid":20849,"name":"author_pk","col_count":1,"spcname":"pg_default","comment":null,"condeferrable":false,"condeferred":false,"conislocal":true,"fillfactor":null,"columns":[{"column":"id"}],"include":[]}],"unique_constraint":[],"foreign_key":[]},"note":"","metadata":{"data_failed":false,"is_promise":false}}},"1d28ddb0-5e6b-42cd-a90d-fc34f11279ef":{"id":"1d28ddb0-5e6b-42cd-a90d-fc34f11279ef","type":"table","x":15,"y":223.5,"ports":[{"id":"879aab09-7520-4f32-bd43-d960427449dc","type":"onetomany","x":20.00000351371966,"y":315.4999868091702,"name":"coll-port-1-left","alignment":"left","parentNode":"1d28ddb0-5e6b-42cd-a90d-fc34f11279ef","links":[]},{"id":"00c9d9d4-f959-49e8-8b1f-b9df732cb115","type":"onetomany","x":185.00000136632178,"y":315.4999868091702,"name":"coll-port-1-right","alignment":"right","parentNode":"1d28ddb0-5e6b-42cd-a90d-fc34f11279ef","links":["6515d4b3-6d5e-4cb4-9a7f-e9f8cb4b20f7"]},{"id":"3194c32c-ccaa-42bf-b1fb-faa410f6937e","type":"onetomany","x":20.00000351371966,"y":340.50001240317306,"name":"coll-port-2-left","alignment":"left","parentNode":"1d28ddb0-5e6b-42cd-a90d-fc34f11279ef","links":[]},{"id":"b8320f0e-765a-4be1-8347-0bc2c2acc6b4","type":"onetomany","x":185.00000136632178,"y":340.50001240317306,"name":"coll-port-2-right","alignment":"right","parentNode":"1d28ddb0-5e6b-42cd-a90d-fc34f11279ef","links":["a40b1153-d189-4ed6-ab1c-bc7b99db50d7"]}],"name":"Untitled","color":"rgb(0,192,255)","portsInOrder":[],"portsOutOrder":["879aab09-7520-4f32-bd43-d960427449dc","00c9d9d4-f959-49e8-8b1f-b9df732cb115","3194c32c-ccaa-42bf-b1fb-faa410f6937e","b8320f0e-765a-4be1-8347-0bc2c2acc6b4"],"otherInfo":{"data":{"columns":[{"name":"book_id","atttypid":20,"attlen":null,"attnum":1,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":null,"typname":"bigint","displaytypname":"bigint","cltype":"bigint","elemoid":20,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":null,"isdup":false,"collspcname":"","is_fk":true,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"book_author","is_view_only":false,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"edit_types":["bigint","double precision","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"author_id","atttypid":20,"attlen":null,"attnum":2,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":null,"typname":"bigint","displaytypname":"bigint","cltype":"bigint","elemoid":20,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":null,"isdup":false,"collspcname":"","is_fk":true,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"book_author","is_view_only":false,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"edit_types":["bigint","bigint","double precision","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]}],"name":"book_author","schema":"public","description":null,"rlspolicy":false,"forcerlspolicy":false,"fillfactor":null,"toast_tuple_target":null,"parallel_workers":null,"relpersistence":false,"primary_key":[],"foreign_key":[{"name":"book_author_fk0","condeferrable":false,"condeferred":false,"confupdtype":"a","confdeltype":"a","confmatchtype":false,"conkey":[1],"confkey":[1],"confrelid":20836,"fknsp":"public","fktab":"book_author","refnspoid":2200,"refnsp":"public","reftab":"books","comment":null,"convalidated":true,"conislocal":true,"columns":[{"local_column":"book_id","references":"c1fdccbc-74d1-496b-9142-656123067b0d","referenced":"id","references_table_name":"public.books"}],"remote_schema":"public","remote_table":"books","coveringindex":null,"autoindex":false,"hasindex":false},{"name":"book_author_fk1","condeferrable":false,"condeferred":false,"confupdtype":"a","confdeltype":"a","confmatchtype":false,"conkey":[2],"confkey":[1],"confrelid":20845,"fknsp":"public","fktab":"book_author","refnspoid":2200,"refnsp":"public","reftab":"author","comment":null,"convalidated":true,"conislocal":true,"columns":[{"local_column":"author_id","references":"fbe973a1-7623-4936-b20d-cec69adb7a8a","referenced":"id","references_table_name":"public.author"}],"remote_schema":"public","remote_table":"author","coveringindex":null,"autoindex":false,"hasindex":false}],"unique_constraint":[]},"note":"","metadata":{"data_failed":false,"is_promise":false}}},"c1fdccbc-74d1-496b-9142-656123067b0d":{"id":"c1fdccbc-74d1-496b-9142-656123067b0d","type":"table","x":291.25,"y":5,"ports":[{"id":"1c31002f-968c-4e31-bc3e-9d44bb19184b","type":"onetomany","x":296.2499978846362,"y":97.00000885587966,"name":"coll-port-1-left","alignment":"left","parentNode":"c1fdccbc-74d1-496b-9142-656123067b0d","links":[]},{"id":"98f66729-6d74-4d74-ab9c-2a9e99633c72","type":"onetomany","x":461.25002695399326,"y":97.00000885587966,"name":"coll-port-1-right","alignment":"right","parentNode":"c1fdccbc-74d1-496b-9142-656123067b0d","links":["6515d4b3-6d5e-4cb4-9a7f-e9f8cb4b20f7","30c29456-f934-4463-b30f-b37c0bb7e6a9","205dff12-3209-4e26-81ae-7bc5309164a3","bfb73a50-5896-49f5-87d7-081ec6498321"]}],"name":"Untitled","color":"rgb(0,192,255)","portsInOrder":[],"portsOutOrder":["1c31002f-968c-4e31-bc3e-9d44bb19184b","98f66729-6d74-4d74-ab9c-2a9e99633c72"],"otherInfo":{"data":{"columns":[{"name":"id","atttypid":23,"attlen":null,"attnum":1,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":"nextval('books_id_seq'::regclass)","typname":"integer","displaytypname":"integer","cltype":"integer","elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"books","is_view_only":false,"seqrelid":20835,"seqtypid":23,"seqstart":"1","seqincrement":"1","seqmax":"2147483647","seqmin":"1","seqcache":"1","seqcycle":false,"is_pk":true,"is_primary_key":true,"attprecision":null,"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"name","atttypid":25,"attlen":null,"attnum":2,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":null,"typname":"text","displaytypname":"text","cltype":"text","elemoid":25,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"books","is_view_only":false,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"edit_types":["\"char\"","character","character varying","name","regclass","text"]},{"name":"pages","atttypid":20,"attlen":null,"attnum":3,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":null,"typname":"bigint","displaytypname":"bigint","cltype":"bigint","elemoid":20,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"books","is_view_only":false,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"edit_types":["bigint","double precision","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]}],"name":"books","schema":"public","description":null,"rlspolicy":false,"forcerlspolicy":false,"fillfactor":null,"toast_tuple_target":null,"parallel_workers":null,"relpersistence":false,"primary_key":[{"oid":20842,"name":"books_pk","col_count":1,"spcname":"pg_default","comment":null,"condeferrable":false,"condeferred":false,"conislocal":true,"fillfactor":null,"columns":[{"column":"id"}],"include":[]}],"unique_constraint":[],"foreign_key":[]},"note":"","metadata":{"data_failed":false,"is_promise":false}}},"0847390b-f33d-44c3-9231-2d95af94484d":{"id":"0847390b-f33d-44c3-9231-2d95af94484d","type":"table","x":638.75,"y":417,"ports":[{"id":"7ab5643b-246e-418e-944b-86a899eddbac","type":"onetomany","x":643.7500189976637,"y":533.9999897358647,"name":"coll-port-2-left","alignment":"left","parentNode":"0847390b-f33d-44c3-9231-2d95af94484d","links":[]},{"id":"3a668bf8-70dd-46b7-9eca-11bf0423ed10","type":"onetomany","x":808.7500480670208,"y":533.9999897358647,"name":"coll-port-2-right","alignment":"right","parentNode":"0847390b-f33d-44c3-9231-2d95af94484d","links":["5ef677b4-9bac-4cf6-b108-3ab512338ab4"]},{"id":"9948af02-009e-4a9e-9c61-d95bb6a6bddd","type":"onetomany","x":643.7500189976637,"y":564.999975521365,"name":"coll-port-3-left","alignment":"left","parentNode":"0847390b-f33d-44c3-9231-2d95af94484d","links":[]},{"id":"127204e8-cf67-4eb2-8901-0fdf3e9e2a7f","type":"onetomany","x":808.7500480670208,"y":564.999975521365,"name":"coll-port-3-right","alignment":"right","parentNode":"0847390b-f33d-44c3-9231-2d95af94484d","links":["0ac97408-789f-4117-8749-5792a8ef44f4"]},{"id":"982411d9-6f5b-46c8-8a10-d3edc9c47853","type":"onetomany","x":643.7500189976637,"y":509.0000140886697,"name":"coll-port-1-left","alignment":"left","parentNode":"0847390b-f33d-44c3-9231-2d95af94484d","links":[]},{"id":"1ee6e35b-917f-4385-83a7-b54071fea295","type":"onetomany","x":808.7500480670208,"y":509.0000140886697,"name":"coll-port-1-right","alignment":"right","parentNode":"0847390b-f33d-44c3-9231-2d95af94484d","links":["faeba05a-1d1d-4907-b565-39763ed2ecaa"]}],"name":"Untitled","color":"rgb(0,192,255)","portsInOrder":[],"portsOutOrder":["7ab5643b-246e-418e-944b-86a899eddbac","3a668bf8-70dd-46b7-9eca-11bf0423ed10","9948af02-009e-4a9e-9c61-d95bb6a6bddd","127204e8-cf67-4eb2-8901-0fdf3e9e2a7f","982411d9-6f5b-46c8-8a10-d3edc9c47853","1ee6e35b-917f-4385-83a7-b54071fea295"],"otherInfo":{"data":{"columns":[{"name":"bb_id","atttypid":23,"attlen":null,"attnum":1,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":"nextval('borrowed_books_bb_id_seq'::regclass)","typname":"integer","displaytypname":"integer","cltype":"integer","elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"borrowed_books","is_view_only":false,"seqrelid":20866,"seqtypid":23,"seqstart":"1","seqincrement":"1","seqmax":"2147483647","seqmin":"1","seqcache":"1","seqcycle":false,"is_pk":true,"is_primary_key":true,"attprecision":null,"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"inventory_id","atttypid":20,"attlen":null,"attnum":2,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":null,"typname":"bigint","displaytypname":"bigint","cltype":"bigint","elemoid":20,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":true,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"borrowed_books","is_view_only":false,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"edit_types":["bigint","double precision","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"username","atttypid":1043,"attlen":"255","attnum":3,"attndims":0,"atttypmod":259,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":null,"typname":"character varying","displaytypname":"character varying(255)","cltype":"character varying","elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":true,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"borrowed_books","is_view_only":false,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"edit_types":["\"char\"","character","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]}],"name":"borrowed_books","schema":"public","description":null,"rlspolicy":false,"forcerlspolicy":false,"fillfactor":null,"toast_tuple_target":null,"parallel_workers":null,"relpersistence":false,"primary_key":[{"oid":20871,"name":"borrowed_books_pk","col_count":1,"spcname":"pg_default","comment":null,"condeferrable":false,"condeferred":false,"conislocal":true,"fillfactor":null,"columns":[{"column":"bb_id"}],"include":[]}],"foreign_key":[{"name":"borrowed_books_fk0","condeferrable":false,"condeferred":false,"confupdtype":"a","confdeltype":"a","confmatchtype":false,"conkey":[2],"confkey":[1],"confrelid":20874,"fknsp":"public","fktab":"borrowed_books","refnspoid":2200,"refnsp":"public","reftab":"inventory","comment":null,"convalidated":true,"conislocal":true,"columns":[{"local_column":"inventory_id","references":"29b34d1a-c75a-434e-9d48-11ae9cfa4b98","referenced":"inventory_id","references_table_name":"public.inventory"}],"remote_schema":"public","remote_table":"inventory","coveringindex":null,"autoindex":false,"hasindex":false},{"name":"borrowed_books_fk1","condeferrable":false,"condeferred":false,"confupdtype":"a","confdeltype":"a","confmatchtype":false,"conkey":[3],"confkey":[1],"confrelid":20830,"fknsp":"public","fktab":"borrowed_books","refnspoid":2200,"refnsp":"public","reftab":"user","comment":null,"convalidated":true,"conislocal":true,"columns":[{"local_column":"username","references":"547ab8da-828a-4da4-813c-4f49478cb2e0","referenced":"username","references_table_name":"public.user"}],"remote_schema":"public","remote_table":"user","coveringindex":null,"autoindex":false,"hasindex":false}],"unique_constraint":[]},"note":"","metadata":{"data_failed":false,"is_promise":false}}},"eeed6c09-6955-4882-b65f-8d7a274beebb":{"id":"eeed6c09-6955-4882-b65f-8d7a274beebb","type":"table","x":301.25,"y":417,"ports":[{"id":"dcf83a81-1d79-416c-ae69-3e72853e2ca6","type":"onetomany","x":306.24999813287576,"y":533.9999897358647,"name":"coll-port-2-left","alignment":"left","parentNode":"eeed6c09-6955-4882-b65f-8d7a274beebb","links":[]},{"id":"1cba911c-e90c-4f71-b57b-8f87b023672c","type":"onetomany","x":471.25002720223284,"y":533.9999897358647,"name":"coll-port-2-right","alignment":"right","parentNode":"eeed6c09-6955-4882-b65f-8d7a274beebb","links":["30c29456-f934-4463-b30f-b37c0bb7e6a9"]},{"id":"b17bbe2b-3769-4e25-b673-4b3192534c1d","type":"onetomany","x":306.24999813287576,"y":564.999975521365,"name":"coll-port-3-left","alignment":"left","parentNode":"eeed6c09-6955-4882-b65f-8d7a274beebb","links":[]},{"id":"191307db-843c-4872-bfac-3f393342dcc6","type":"onetomany","x":471.25002720223284,"y":564.999975521365,"name":"coll-port-3-right","alignment":"right","parentNode":"eeed6c09-6955-4882-b65f-8d7a274beebb","links":["61a12a09-86b1-423d-8730-d934bfa0bf7a"]}],"name":"Untitled","color":"rgb(0,192,255)","portsInOrder":[],"portsOutOrder":["dcf83a81-1d79-416c-ae69-3e72853e2ca6","1cba911c-e90c-4f71-b57b-8f87b023672c","b17bbe2b-3769-4e25-b673-4b3192534c1d","191307db-843c-4872-bfac-3f393342dcc6"],"otherInfo":{"data":{"columns":[{"name":"fav_id","atttypid":23,"attlen":null,"attnum":1,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":"nextval('fav_books_fav_id_seq'::regclass)","typname":"integer","displaytypname":"integer","cltype":"integer","elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"fav_books","is_view_only":false,"seqrelid":20880,"seqtypid":23,"seqstart":"1","seqincrement":"1","seqmax":"2147483647","seqmin":"1","seqcache":"1","seqcycle":false,"is_pk":true,"is_primary_key":true,"attprecision":null,"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"book_id","atttypid":20,"attlen":null,"attnum":2,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":null,"typname":"bigint","displaytypname":"bigint","cltype":"bigint","elemoid":20,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":true,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"fav_books","is_view_only":false,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"edit_types":["bigint","double precision","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"username","atttypid":1043,"attlen":"255","attnum":3,"attndims":0,"atttypmod":259,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":null,"typname":"character varying","displaytypname":"character varying(255)","cltype":"character varying","elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":true,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"fav_books","is_view_only":false,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"edit_types":["\"char\"","character","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]}],"name":"fav_books","schema":"public","description":null,"rlspolicy":false,"forcerlspolicy":false,"fillfactor":null,"toast_tuple_target":null,"parallel_workers":null,"relpersistence":false,"primary_key":[{"oid":20885,"name":"fav_books_pk","col_count":1,"spcname":"pg_default","comment":null,"condeferrable":false,"condeferred":false,"conislocal":true,"fillfactor":null,"columns":[{"column":"fav_id"}],"include":[]}],"foreign_key":[{"name":"fav_books_fk0","condeferrable":false,"condeferred":false,"confupdtype":"a","confdeltype":"a","confmatchtype":false,"conkey":[2],"confkey":[1],"confrelid":20836,"fknsp":"public","fktab":"fav_books","refnspoid":2200,"refnsp":"public","reftab":"books","comment":null,"convalidated":true,"conislocal":true,"columns":[{"local_column":"book_id","references":"c1fdccbc-74d1-496b-9142-656123067b0d","referenced":"id","references_table_name":"public.books"}],"remote_schema":"public","remote_table":"books","coveringindex":null,"autoindex":false,"hasindex":false},{"name":"fav_books_fk1","condeferrable":false,"condeferred":false,"confupdtype":"a","confdeltype":"a","confmatchtype":false,"conkey":[3],"confkey":[1],"confrelid":20830,"fknsp":"public","fktab":"fav_books","refnspoid":2200,"refnsp":"public","reftab":"user","comment":null,"convalidated":true,"conislocal":true,"columns":[{"local_column":"username","references":"547ab8da-828a-4da4-813c-4f49478cb2e0","referenced":"username","references_table_name":"public.user"}],"remote_schema":"public","remote_table":"user","coveringindex":null,"autoindex":false,"hasindex":false}],"unique_constraint":[]},"note":"","metadata":{"data_failed":false,"is_promise":false}}},"0fbca123-9dfc-4baf-a4e1-3fba899e1337":{"id":"0fbca123-9dfc-4baf-a4e1-3fba899e1337","type":"table","x":883.75,"y":17.5,"ports":[{"id":"ff7a2b62-961a-4bd0-9c00-e4efbb909aff","type":"onetomany","x":888.7500500529374,"y":109.49999667947716,"name":"coll-port-1-left","alignment":"left","parentNode":"0fbca123-9dfc-4baf-a4e1-3fba899e1337","links":[]},{"id":"b213fddd-731a-4ba0-a19a-126d4ac9123e","type":"onetomany","x":1053.7500291754866,"y":109.49999667947716,"name":"coll-port-1-right","alignment":"right","parentNode":"0fbca123-9dfc-4baf-a4e1-3fba899e1337","links":["7fa69583-b9f5-4e3c-9752-6bc830f7b767"]}],"name":"Untitled","color":"rgb(0,192,255)","portsInOrder":[],"portsOutOrder":["ff7a2b62-961a-4bd0-9c00-e4efbb909aff","b213fddd-731a-4ba0-a19a-126d4ac9123e"],"otherInfo":{"data":{"columns":[{"name":"genre_id","atttypid":23,"attlen":null,"attnum":1,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":"nextval('genre_genre_id_seq'::regclass)","typname":"integer","displaytypname":"integer","cltype":"integer","elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"genre","is_view_only":false,"seqrelid":20854,"seqtypid":23,"seqstart":"1","seqincrement":"1","seqmax":"2147483647","seqmin":"1","seqcache":"1","seqcycle":false,"is_pk":true,"is_primary_key":true,"attprecision":null,"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"title","atttypid":25,"attlen":null,"attnum":2,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":null,"typname":"text","displaytypname":"text","cltype":"text","elemoid":25,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"genre","is_view_only":false,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"edit_types":["\"char\"","character","character varying","name","regclass","text"]}],"name":"genre","schema":"public","description":null,"rlspolicy":false,"forcerlspolicy":false,"fillfactor":null,"toast_tuple_target":null,"parallel_workers":null,"relpersistence":false,"primary_key":[{"oid":20861,"name":"genre_pk","col_count":1,"spcname":"pg_default","comment":null,"condeferrable":false,"condeferred":false,"conislocal":true,"fillfactor":null,"columns":[{"column":"genre_id"}],"include":[]}],"unique_constraint":[],"foreign_key":[]},"note":"","metadata":{"data_failed":false,"is_promise":false}}},"4f8664ed-c05b-4aff-be60-83bcafc7a9d6":{"id":"4f8664ed-c05b-4aff-be60-83bcafc7a9d6","type":"table","x":352.5,"y":223.5,"ports":[{"id":"608db16c-c5bc-4b58-ba69-74c7eae5e0bd","type":"onetomany","x":357.50001813515655,"y":315.4999868091702,"name":"coll-port-1-left","alignment":"left","parentNode":"4f8664ed-c05b-4aff-be60-83bcafc7a9d6","links":[]},{"id":"e95f15da-96c9-4285-b756-62d27c689ed7","type":"onetomany","x":522.4999972577058,"y":315.4999868091702,"name":"coll-port-1-right","alignment":"right","parentNode":"4f8664ed-c05b-4aff-be60-83bcafc7a9d6","links":["205dff12-3209-4e26-81ae-7bc5309164a3"]},{"id":"82bb4687-5b39-41e4-94f8-87cb72afa7cd","type":"onetomany","x":357.50001813515655,"y":340.50001240317306,"name":"coll-port-2-left","alignment":"left","parentNode":"4f8664ed-c05b-4aff-be60-83bcafc7a9d6","links":[]},{"id":"f5a28c1e-3130-4b18-888a-19836346a859","type":"onetomany","x":522.4999972577058,"y":340.50001240317306,"name":"coll-port-2-right","alignment":"right","parentNode":"4f8664ed-c05b-4aff-be60-83bcafc7a9d6","links":["7fa69583-b9f5-4e3c-9752-6bc830f7b767"]}],"name":"Untitled","color":"rgb(0,192,255)","portsInOrder":[],"portsOutOrder":["608db16c-c5bc-4b58-ba69-74c7eae5e0bd","e95f15da-96c9-4285-b756-62d27c689ed7","82bb4687-5b39-41e4-94f8-87cb72afa7cd","f5a28c1e-3130-4b18-888a-19836346a859"],"otherInfo":{"data":{"columns":[{"name":"book_id","atttypid":20,"attlen":null,"attnum":1,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":null,"typname":"bigint","displaytypname":"bigint","cltype":"bigint","elemoid":20,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":null,"isdup":false,"collspcname":"","is_fk":true,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"genre_book","is_view_only":false,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"edit_types":["bigint","double precision","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"genre_id","atttypid":20,"attlen":null,"attnum":2,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":null,"typname":"bigint","displaytypname":"bigint","cltype":"bigint","elemoid":20,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":null,"isdup":false,"collspcname":"","is_fk":true,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"genre_book","is_view_only":false,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"edit_types":["bigint","bigint","double precision","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]}],"name":"genre_book","schema":"public","description":null,"rlspolicy":false,"forcerlspolicy":false,"fillfactor":null,"toast_tuple_target":null,"parallel_workers":null,"relpersistence":false,"primary_key":[],"foreign_key":[{"name":"genre_book_fk0","condeferrable":false,"condeferred":false,"confupdtype":"a","confdeltype":"a","confmatchtype":false,"conkey":[1],"confkey":[1],"confrelid":20836,"fknsp":"public","fktab":"genre_book","refnspoid":2200,"refnsp":"public","reftab":"books","comment":null,"convalidated":true,"conislocal":true,"columns":[{"local_column":"book_id","references":"c1fdccbc-74d1-496b-9142-656123067b0d","referenced":"id","references_table_name":"public.books"}],"remote_schema":"public","remote_table":"books","coveringindex":null,"autoindex":false,"hasindex":false},{"name":"genre_book_fk1","condeferrable":false,"condeferred":false,"confupdtype":"a","confdeltype":"a","confmatchtype":false,"conkey":[2],"confkey":[1],"confrelid":20855,"fknsp":"public","fktab":"genre_book","refnspoid":2200,"refnsp":"public","reftab":"genre","comment":null,"convalidated":true,"conislocal":true,"columns":[{"local_column":"genre_id","references":"0fbca123-9dfc-4baf-a4e1-3fba899e1337","referenced":"genre_id","references_table_name":"public.genre"}],"remote_schema":"public","remote_table":"genre","coveringindex":null,"autoindex":false,"hasindex":false}],"unique_constraint":[]},"note":"","metadata":{"data_failed":false,"is_promise":false}}},"29b34d1a-c75a-434e-9d48-11ae9cfa4b98":{"id":"29b34d1a-c75a-434e-9d48-11ae9cfa4b98","type":"table","x":873.75,"y":211,"ports":[{"id":"069a33c3-5dcf-4bc8-9187-6bc7d0c84b0e","type":"onetomany","x":878.74999985789,"y":302.9999989855727,"name":"coll-port-1-left","alignment":"left","parentNode":"29b34d1a-c75a-434e-9d48-11ae9cfa4b98","links":[]},{"id":"fc7c6ac7-aa77-4756-b74e-0404e31c759b","type":"onetomany","x":1043.749978980439,"y":302.9999989855727,"name":"coll-port-1-right","alignment":"right","parentNode":"29b34d1a-c75a-434e-9d48-11ae9cfa4b98","links":["5ef677b4-9bac-4cf6-b108-3ab512338ab4"]},{"id":"7d14eb04-87fc-4418-b2fc-bb954a8f4437","type":"onetomany","x":878.74999985789,"y":327.9999746327677,"name":"coll-port-2-left","alignment":"left","parentNode":"29b34d1a-c75a-434e-9d48-11ae9cfa4b98","links":[]},{"id":"e8b0f8fc-5eb4-4853-b2bf-c1bf2a599a1f","type":"onetomany","x":1043.749978980439,"y":327.9999746327677,"name":"coll-port-2-right","alignment":"right","parentNode":"29b34d1a-c75a-434e-9d48-11ae9cfa4b98","links":["bfb73a50-5896-49f5-87d7-081ec6498321"]}],"name":"Untitled","color":"rgb(0,192,255)","portsInOrder":[],"portsOutOrder":["069a33c3-5dcf-4bc8-9187-6bc7d0c84b0e","fc7c6ac7-aa77-4756-b74e-0404e31c759b","7d14eb04-87fc-4418-b2fc-bb954a8f4437","e8b0f8fc-5eb4-4853-b2bf-c1bf2a599a1f"],"otherInfo":{"data":{"columns":[{"name":"inventory_id","atttypid":23,"attlen":null,"attnum":1,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":"nextval('inventory_inventory_id_seq'::regclass)","typname":"integer","displaytypname":"integer","cltype":"integer","elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"inventory","is_view_only":false,"seqrelid":20873,"seqtypid":23,"seqstart":"1","seqincrement":"1","seqmax":"2147483647","seqmin":"1","seqcache":"1","seqcycle":false,"is_pk":true,"is_primary_key":true,"attprecision":null,"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"book_id","atttypid":20,"attlen":null,"attnum":2,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":null,"typname":"bigint","displaytypname":"bigint","cltype":"bigint","elemoid":20,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":true,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"inventory","is_view_only":false,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"edit_types":["bigint","double precision","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"last_update","atttypid":1082,"attlen":null,"attnum":3,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":null,"typname":"date","displaytypname":"date","cltype":"date","elemoid":1082,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"inventory","is_view_only":false,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"edit_types":["date","timestamp with time zone","timestamp without time zone"]}],"name":"inventory","schema":"public","description":null,"rlspolicy":false,"forcerlspolicy":false,"fillfactor":null,"toast_tuple_target":null,"parallel_workers":null,"relpersistence":false,"primary_key":[{"oid":20878,"name":"inventory_pk","col_count":1,"spcname":"pg_default","comment":null,"condeferrable":false,"condeferred":false,"conislocal":true,"fillfactor":null,"columns":[{"column":"inventory_id"}],"include":[]}],"foreign_key":[{"name":"inventory_fk0","condeferrable":false,"condeferred":false,"confupdtype":"a","confdeltype":"a","confmatchtype":false,"conkey":[2],"confkey":[1],"confrelid":20836,"fknsp":"public","fktab":"inventory","refnspoid":2200,"refnsp":"public","reftab":"books","comment":null,"convalidated":true,"conislocal":true,"columns":[{"local_column":"book_id","references":"c1fdccbc-74d1-496b-9142-656123067b0d","referenced":"id","references_table_name":"public.books"}],"remote_schema":"public","remote_table":"books","coveringindex":null,"autoindex":false,"hasindex":false}],"unique_constraint":[]},"note":"","metadata":{"data_failed":false,"is_promise":false}}},"c9ddebcd-6725-4c03-b607-a492250e61d8":{"id":"c9ddebcd-6725-4c03-b607-a492250e61d8","type":"table","x":638.75,"y":635,"ports":[{"id":"ad97ac45-7701-4910-8f6e-2baa4cbc7c54","type":"onetomany","x":643.7500189976637,"y":757.9999853070698,"name":"coll-port-2-left","alignment":"left","parentNode":"c9ddebcd-6725-4c03-b607-a492250e61d8","links":[]},{"id":"fdf04ed8-6e4f-4fed-8143-f09bf60ca64a","type":"onetomany","x":808.7500480670208,"y":757.9999853070698,"name":"coll-port-2-right","alignment":"right","parentNode":"c9ddebcd-6725-4c03-b607-a492250e61d8","links":["afdade8e-d42b-4239-a28f-d9cf708c8024"]},{"id":"92aed1f9-d997-4def-a664-7e60ad0a64b7","type":"onetomany","x":643.7500189976637,"y":788.99997109257,"name":"coll-port-3-left","alignment":"left","parentNode":"c9ddebcd-6725-4c03-b607-a492250e61d8","links":[]},{"id":"61695174-3f0c-4e07-857e-20e5b07a334d","type":"onetomany","x":808.7500480670208,"y":788.99997109257,"name":"coll-port-3-right","alignment":"right","parentNode":"c9ddebcd-6725-4c03-b607-a492250e61d8","links":["faeba05a-1d1d-4907-b565-39763ed2ecaa"]}],"name":"Untitled","color":"rgb(0,192,255)","portsInOrder":[],"portsOutOrder":["ad97ac45-7701-4910-8f6e-2baa4cbc7c54","fdf04ed8-6e4f-4fed-8143-f09bf60ca64a","92aed1f9-d997-4def-a664-7e60ad0a64b7","61695174-3f0c-4e07-857e-20e5b07a334d"],"otherInfo":{"data":{"columns":[{"name":"mark_id","atttypid":23,"attlen":null,"attnum":1,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":"nextval('read_books_mark_id_seq'::regclass)","typname":"integer","displaytypname":"integer","cltype":"integer","elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"read_books","is_view_only":false,"seqrelid":20887,"seqtypid":23,"seqstart":"1","seqincrement":"1","seqmax":"2147483647","seqmin":"1","seqcache":"1","seqcycle":false,"is_pk":true,"is_primary_key":true,"attprecision":null,"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]},{"name":"username","atttypid":1043,"attlen":"255","attnum":2,"attndims":0,"atttypmod":259,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":null,"typname":"character varying","displaytypname":"character varying(255)","cltype":"character varying","elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":true,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"read_books","is_view_only":false,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"edit_types":["\"char\"","character","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]},{"name":"bb_id","atttypid":23,"attlen":null,"attnum":3,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":"nextval('read_books_bb_id_seq'::regclass)","typname":"integer","displaytypname":"integer","cltype":"integer","elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":true,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"read_books","is_view_only":false,"seqrelid":20888,"seqtypid":23,"seqstart":"1","seqincrement":"1","seqmax":"2147483647","seqmin":"1","seqcache":"1","seqcycle":false,"is_pk":false,"is_primary_key":false,"attprecision":null,"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]}],"name":"read_books","schema":"public","description":null,"rlspolicy":false,"forcerlspolicy":false,"fillfactor":null,"toast_tuple_target":null,"parallel_workers":null,"relpersistence":false,"primary_key":[{"oid":20894,"name":"read_books_pk","col_count":1,"spcname":"pg_default","comment":null,"condeferrable":false,"condeferred":false,"conislocal":true,"fillfactor":null,"columns":[{"column":"mark_id"}],"include":[]}],"foreign_key":[{"name":"read_books_fk0","condeferrable":false,"condeferred":false,"confupdtype":"a","confdeltype":"a","confmatchtype":false,"conkey":[2],"confkey":[1],"confrelid":20830,"fknsp":"public","fktab":"read_books","refnspoid":2200,"refnsp":"public","reftab":"user","comment":null,"convalidated":true,"conislocal":true,"columns":[{"local_column":"username","references":"547ab8da-828a-4da4-813c-4f49478cb2e0","referenced":"username","references_table_name":"public.user"}],"remote_schema":"public","remote_table":"user","coveringindex":null,"autoindex":false,"hasindex":false},{"name":"read_books_fk1","condeferrable":false,"condeferred":false,"confupdtype":"a","confdeltype":"a","confmatchtype":false,"conkey":[3],"confkey":[1],"confrelid":20867,"fknsp":"public","fktab":"read_books","refnspoid":2200,"refnsp":"public","reftab":"borrowed_books","comment":null,"convalidated":true,"conislocal":true,"columns":[{"local_column":"bb_id","references":"0847390b-f33d-44c3-9231-2d95af94484d","referenced":"bb_id","references_table_name":"public.borrowed_books"}],"remote_schema":"public","remote_table":"borrowed_books","coveringindex":null,"autoindex":false,"hasindex":false}],"unique_constraint":[]},"note":"","metadata":{"data_failed":false,"is_promise":false}}},"547ab8da-828a-4da4-813c-4f49478cb2e0":{"id":"547ab8da-828a-4da4-813c-4f49478cb2e0","type":"table","x":577.5,"y":217.5,"ports":[{"id":"9a7c1978-d1a1-4c3c-9f29-423c289ddf1c","type":"onetomany","x":582.4999987471433,"y":315.4999868091702,"name":"coll-port-1-left","alignment":"left","parentNode":"547ab8da-828a-4da4-813c-4f49478cb2e0","links":[]},{"id":"bdb75708-6cb6-414e-887a-80c8c8406ae0","type":"onetomany","x":747.5000278165003,"y":315.4999868091702,"name":"coll-port-1-right","alignment":"right","parentNode":"547ab8da-828a-4da4-813c-4f49478cb2e0","links":["0ac97408-789f-4117-8749-5792a8ef44f4","61a12a09-86b1-423d-8730-d934bfa0bf7a","afdade8e-d42b-4239-a28f-d9cf708c8024"]}],"name":"Untitled","color":"rgb(0,192,255)","portsInOrder":[],"portsOutOrder":["9a7c1978-d1a1-4c3c-9f29-423c289ddf1c","bdb75708-6cb6-414e-887a-80c8c8406ae0"],"otherInfo":{"data":{"columns":[{"name":"username","atttypid":1043,"attlen":"255","attnum":1,"attndims":0,"atttypmod":259,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"x","attidentity":"","defval":null,"typname":"character varying","displaytypname":"character varying(255)","cltype":"character varying","elemoid":1043,"typnspname":"pg_catalog","defaultstorage":"x","description":null,"indkey":"1","isdup":false,"collspcname":"pg_catalog.\"default\"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"user","is_view_only":false,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":true,"is_primary_key":true,"attprecision":null,"edit_types":["\"char\"","character","character varying","character varying","information_schema.character_data","information_schema.yes_or_no","name","regclass","text"]},{"name":"password","atttypid":23,"attlen":null,"attnum":2,"attndims":0,"atttypmod":-1,"attacl":[],"attnotnull":true,"attoptions":null,"attstattarget":-1,"attstorage":"p","attidentity":"","defval":null,"typname":"integer","displaytypname":"integer","cltype":"integer","elemoid":23,"typnspname":"pg_catalog","defaultstorage":"p","description":null,"indkey":"1","isdup":false,"collspcname":"","is_fk":false,"seclabels":null,"is_sys_column":false,"colconstype":"n","genexpr":null,"relname":"user","is_view_only":false,"seqrelid":null,"seqtypid":null,"seqstart":null,"seqincrement":null,"seqmax":null,"seqmin":null,"seqcache":null,"seqcycle":null,"is_pk":false,"is_primary_key":false,"attprecision":null,"edit_types":["bigint","double precision","information_schema.cardinal_number","integer","money","numeric","oid","real","regclass","regcollation","regconfig","regdictionary","regnamespace","regoper","regoperator","regproc","regprocedure","regrole","regtype","smallint"]}],"name":"user","schema":"public","description":null,"rlspolicy":false,"forcerlspolicy":false,"fillfactor":null,"toast_tuple_target":null,"parallel_workers":null,"relpersistence":false,"primary_key":[{"oid":20833,"name":"user_pk","col_count":1,"spcname":"pg_default","comment":null,"condeferrable":false,"condeferred":false,"conislocal":true,"fillfactor":null,"columns":[{"column":"username"}],"include":[]}],"unique_constraint":[],"foreign_key":[]},"note":"","metadata":{"data_failed":false,"is_promise":false}}}}}]}} \ No newline at end of file diff --git a/cli_library.sql b/cli_library.sql new file mode 100644 index 0000000..df7fdec --- /dev/null +++ b/cli_library.sql @@ -0,0 +1,141 @@ +CREATE TABLE "user" ( + "username" varchar(255) NOT NULL, + "password" int NOT NULL, + CONSTRAINT "user_pk" PRIMARY KEY ("username") +) WITH ( + OIDS=FALSE +); + + + +CREATE TABLE "books" ( + "id" serial NOT NULL, + "name" TEXT NOT NULL, + "pages" bigint NOT NULL, + CONSTRAINT "books_pk" PRIMARY KEY ("id") +) WITH ( + OIDS=FALSE +); + + + +CREATE TABLE "author" ( + "id" serial NOT NULL, + "author_name" varchar(255) NOT NULL, + CONSTRAINT "author_pk" PRIMARY KEY ("id") +) WITH ( + OIDS=FALSE +); + + + +CREATE TABLE "book_author" ( + "book_id" bigint NOT NULL, + "author_id" bigint NOT NULL +) WITH ( + OIDS=FALSE +); + + + +CREATE TABLE "genre" ( + "genre_id" serial NOT NULL, + "title" TEXT NOT NULL, + CONSTRAINT "genre_pk" PRIMARY KEY ("genre_id") +) WITH ( + OIDS=FALSE +); + + + +CREATE TABLE "genre_book" ( + "book_id" bigint NOT NULL, + "genre_id" bigint NOT NULL +) WITH ( + OIDS=FALSE +); + + + +CREATE TABLE "borrowed_books" ( + "bb_id" serial NOT NULL, + "inventory_id" bigint NOT NULL, + "username" varchar(255) NOT NULL, + CONSTRAINT "borrowed_books_pk" PRIMARY KEY ("bb_id") +) WITH ( + OIDS=FALSE +); + + + +CREATE TABLE "inventory" ( + "inventory_id" serial NOT NULL, + "book_id" bigint NOT NULL, + "last_update" DATE NOT NULL, + CONSTRAINT "inventory_pk" PRIMARY KEY ("inventory_id") +) WITH ( + OIDS=FALSE +); + + + +CREATE TABLE "fav_books" ( + "fav_id" serial NOT NULL, + "book_id" bigint NOT NULL, + "username" varchar(255) NOT NULL, + CONSTRAINT "fav_books_pk" PRIMARY KEY ("fav_id") +) WITH ( + OIDS=FALSE +); + + + +CREATE TABLE "read_books" ( + "mark_id" serial NOT NULL, + "username" varchar(255) NOT NULL, + "bb_id" serial NOT NULL, + CONSTRAINT "read_books_pk" PRIMARY KEY ("mark_id") +) WITH ( + OIDS=FALSE +); + + + + + + +ALTER TABLE "book_author" ADD CONSTRAINT "book_author_fk0" FOREIGN KEY ("book_id") REFERENCES "books"("id"); +ALTER TABLE "book_author" ADD CONSTRAINT "book_author_fk1" FOREIGN KEY ("author_id") REFERENCES "author"("id"); + + +ALTER TABLE "genre_book" ADD CONSTRAINT "genre_book_fk0" FOREIGN KEY ("book_id") REFERENCES "books"("id"); +ALTER TABLE "genre_book" ADD CONSTRAINT "genre_book_fk1" FOREIGN KEY ("genre_id") REFERENCES "genre"("genre_id"); + +ALTER TABLE "borrowed_books" ADD CONSTRAINT "borrowed_books_fk0" FOREIGN KEY ("inventory_id") REFERENCES "inventory"("inventory_id"); +ALTER TABLE "borrowed_books" ADD CONSTRAINT "borrowed_books_fk1" FOREIGN KEY ("username") REFERENCES "user"("username"); + +ALTER TABLE "inventory" ADD CONSTRAINT "inventory_fk0" FOREIGN KEY ("book_id") REFERENCES "books"("id"); + +ALTER TABLE "fav_books" ADD CONSTRAINT "fav_books_fk0" FOREIGN KEY ("book_id") REFERENCES "books"("id"); +ALTER TABLE "fav_books" ADD CONSTRAINT "fav_books_fk1" FOREIGN KEY ("username") REFERENCES "user"("username"); + +ALTER TABLE "read_books" ADD CONSTRAINT "read_books_fk0" FOREIGN KEY ("username") REFERENCES "user"("username"); +ALTER TABLE "read_books" ADD CONSTRAINT "read_books_fk1" FOREIGN KEY ("bb_id") REFERENCES "borrowed_books"("bb_id"); + + +INSERT INTO "user" (username, password) VALUES ('john2', '12345'); +INSERT INTO "user" (username, password) VALUES ('jbiever1', '4568'); +INSERT INTO "user" (username, password) VALUES ('sthomazet2', '7891'); + + + + + + + + + + + + + diff --git a/config.py b/config.py new file mode 100644 index 0000000..082594f --- /dev/null +++ b/config.py @@ -0,0 +1,18 @@ +from configparser import ConfigParser + +def config(filename, section): + # create a parser + parser = ConfigParser() + # read config file + parser.read(filename) + + # get section, default to postgresql + db = {} + if parser.has_section(section): + params = parser.items(section) + for param in params: + db[param[0]] = param[1] + else: + raise Exception('Section {0} not found in the {1} file'.format(section, filename)) + + return db \ No newline at end of file diff --git a/database.ini b/database.ini new file mode 100644 index 0000000..e0b5a06 --- /dev/null +++ b/database.ini @@ -0,0 +1,11 @@ +[CLI_Library] +host=localhost +database=cli_library +user=postgres +password=1234 + +[postgres] +host=localhost +database=postgres +user=postgres +password=1234 \ No newline at end of file diff --git a/database.py b/database.py new file mode 100644 index 0000000..267dda4 --- /dev/null +++ b/database.py @@ -0,0 +1,117 @@ +import psycopg2 +import typer +from rich.console import Console +from rich.table import Table + +from config import config + + +def connect(): + conn = None + try: + try: + # try to connect to database + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + + # create a cursor + cur = conn.cursor() + except (Exception, psycopg2.DatabaseError) as error: + # if database doesn't exist, create it + try: + params = config('database.ini','postgres') + conn = psycopg2.connect(**params) + conn.autocommit = True # this command enables autocommit to postgreSQL otherwise at the end of each operation you must do conn.commit() + cur = conn.cursor() + + cur.execute("CREATE DATABASE cli_library;") + typer.secho(f"Database created successfully", fg=typer.colors.GREEN) + + + + + + except psycopg2.Error as e: + # If the CREATE DATABASE statement fails or another error occurs, catch the exception and print an error message + typer.echo(f"The CREATE DATABASE statement failed: {e}") + + + else: + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + curr = conn.cursor() + curr.execute("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'") + + file = open('cli_library.sql', 'r') + sqlFile = file.read() + file.close() + sqlCommands = sqlFile.split(';')[:-1] + # Execute every command from the input file + for command in sqlCommands: + try: + curr.execute(command) + except (Exception, psycopg2.DatabaseError) as error: + print("Command skipped: ", error) + break + conn.close() + cur.close() + + + else: + conn.autocommit = True + curr = conn.cursor() + + curr.execute("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'") + table_names = curr.fetchall() + + if len(table_names) != 0: + typer.echo("Database tables are already created!") + typer.echo("Existing tables:") + for table in table_names: + curr.execute(f'SELECT * FROM {table[0]};') + typer.echo(table) + typer.echo(curr.fetchall()) + + else: + file = open('cli_library.sql', 'r') + sqlFile = file.read() + file.close() + sqlCommands = sqlFile.split(';')[:-1] + # Execute every command from the input file + for command in sqlCommands: + try: + curr.execute(command) + except (Exception, psycopg2.DatabaseError) as error: + print("Command skipped: ", error) + break + + # curr.execute("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'") + # table_names = curr.fetchall() + # for table in table_names: + # # cur.execute(f'SELECT * FROM {table[0]};') + # print(table) + # print(cur.fetchall()) + + # print(len(curr.fetchall()), ' tables created.') + # cur.execute('SELECT * FROM students;') + # print(cur.fetchall()) + # cur.execute('SELECT * from teachers;') + # print(cur.fetchall()) + # curr.close() + + except (Exception, psycopg2.DatabaseError) as error: + print(error) + + + + finally: + if conn is not None: + conn.close() + print('Database connection closed.') + + +if __name__ == '__main__': + connect() + + diff --git a/main.py b/main.py index 0219f98..2025ce5 100644 --- a/main.py +++ b/main.py @@ -2,6 +2,7 @@ from rich.console import Console from rich.table import Table from typing import Optional +from database import connect console = Console() @@ -11,7 +12,7 @@ def start(): typer.secho(f'''Welcome to Library CLI!\n\n You can execute command '--help' to see the possible commands''', fg=typer.colors.GREEN) - # TODO: connect to database + connect() # This is how you can get arguments, here username is a mandatory argument for this command. @app.command("sign_up") @@ -19,7 +20,7 @@ def sign_up(username: str): typer.echo(f"Nice that you are signing up!") # TODO: Add user with name {username} to database table -# Example function for tables, you can add more columns/row. +# Example function for tables, you can add more columns/row @app.command("display_table") def display_table(): table = Table(show_header=True, header_style="bold blue") From f3c0270d509ad1716783a892bb6c5b0f0cb3126b Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Sat, 1 Apr 2023 18:43:50 +0200 Subject: [PATCH 02/27] updated --- database.py | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++ main.py | 22 +++++++++++++++++--- 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/database.py b/database.py index 267dda4..3ceb972 100644 --- a/database.py +++ b/database.py @@ -110,6 +110,66 @@ def connect(): conn.close() print('Database connection closed.') +def connect_to_db(): + conn = None + try: + try: + # try to connect to database + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + + # create a cursor + cur = conn.cursor() + except (Exception, psycopg2.DatabaseError) as error: + # if database doesn't exist, create it + print(error) + + + except (Exception, psycopg2.DatabaseError) as error: + print(error) + +def singUp(username: str, password: int): + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + cur.execute('SELECT username FROM public."user";') + + user = cur.fetchall() + for i in user: + if username == i[0]: + typer.secho(f"This user already exist! Try different user", fg=typer.colors.RED) + break + else: + command = f'INSERT INTO "user" (username, password) VALUES (\'{username}\',\'{password}\');' + cur.execute(command) + cur.close() + +def signIn(username: str, password: int): + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + cur.execute('SELECT username FROM public."user";') + user = cur.fetchall() + + cur.execute('SELECT password FROM public."user";') + user_pass = cur.fetchall() + + for i,j in zip(user, user_pass): + if username == i[0] and password == j[0]: + typer.secho(f"You are signed in", fg=typer.colors.GREEN) + break + else: + typer.secho(f"username or password is wrong!", fg=typer.colors.RED) + + + +if __name__ == '__main__': + connect() + + + if __name__ == '__main__': connect() diff --git a/main.py b/main.py index 2025ce5..9cb5481 100644 --- a/main.py +++ b/main.py @@ -2,7 +2,7 @@ from rich.console import Console from rich.table import Table from typing import Optional -from database import connect +from database import * console = Console() @@ -16,9 +16,25 @@ def start(): # This is how you can get arguments, here username is a mandatory argument for this command. @app.command("sign_up") -def sign_up(username: str): +def sign_up(username: str, password: int): typer.echo(f"Nice that you are signing up!") - # TODO: Add user with name {username} to database table + singUp(username, password) + +# This is to sign in the user +@app.command("sign_in") +def sign_in(username: str, password: int): + typer.echo(f"Nice that you are signing in!") + signIn(username, password) + + +@app.command() +def borrow_book(): + pass + # + # + # + # + # Example function for tables, you can add more columns/row @app.command("display_table") From b704fc7b91bce229dc42eaec9b09afeafc3ac767 Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Mon, 3 Apr 2023 02:45:37 +0200 Subject: [PATCH 03/27] add_book --- database.py | 115 ++++++++++++++++++++++++++++++++++++++++------------ main.py | 27 ++++++++---- 2 files changed, 110 insertions(+), 32 deletions(-) diff --git a/database.py b/database.py index 3ceb972..1a3acb3 100644 --- a/database.py +++ b/database.py @@ -2,6 +2,7 @@ import typer from rich.console import Console from rich.table import Table +import datetime from config import config @@ -146,32 +147,96 @@ def singUp(username: str, password: int): cur.close() def signIn(username: str, password: int): - params = config('database.ini','CLI_Library') - conn = psycopg2.connect(**params) - conn.autocommit = True - cur = conn.cursor() - cur.execute('SELECT username FROM public."user";') - user = cur.fetchall() - - cur.execute('SELECT password FROM public."user";') - user_pass = cur.fetchall() - - for i,j in zip(user, user_pass): - if username == i[0] and password == j[0]: - typer.secho(f"You are signed in", fg=typer.colors.GREEN) - break - else: - typer.secho(f"username or password is wrong!", fg=typer.colors.RED) + try: + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + cur.execute('SELECT username FROM public."user";') + user = cur.fetchall() + cur.execute('SELECT password FROM public."user";') + user_pass = cur.fetchall() - - -if __name__ == '__main__': - connect() - - + for i,j in zip(user, user_pass): + if username == i[0] and password == j[0]: + typer.secho(f"You are signed in", fg=typer.colors.GREEN) + success = True + break + + else: + typer.secho(f"username or password is wrong!", fg=typer.colors.RED) + success = False + + return success + except ValueError: + print('Please Enter valid information') +def addBook(bookname: str, author: str, pages: int, genre: str): + try: + try: + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + + command_query = f''' SELECT name FROM books; + SELECT author_name FROM author;'''.split('\n') + + queried_data = [] + for i in command_query: + cur.execute(i) + queried_data.append(cur.fetchall()) + + + + + + for i,j in zip(*queried_data): + if i[0] == bookname and j[0] == author: + cur.execute(f'SELECT id FROM books WHERE name = \'{bookname}\'') + book_id = cur.fetchone() + command = f'''INSERT INTO inventory (book_id, last_update) VALUES (\'{int(book_id[0])}\',\'{datetime.date.today()}\');''' + cur.execute(command) + break + elif i[0] != bookname and j[0] == author: + commandd = f'''INSERT INTO "books" (name,pages) VALUES (\'{bookname}\',\'{pages}\'); + INSERT INTO "book_author" (book_id, author_id) VALUES ((SELECT id FROM "books" WHERE name = \'{bookname}\'), (SELECT id FROM "author" WHERE author_name = \'{author}\')); + INSERT INTO "inventory" (book_id, last_update) VALUES ((SELECT id FROM "books" WHERE name = \'{bookname}'), \'{datetime.date.today()}\');''' + cur.execute(commandd) + break + else: + print('hello') + commandd = f'''INSERT INTO "books" (name,pages) VALUES (\'{bookname}\',\'{pages}\'); + INSERT INTO "author" (author_name) VALUES (\'{author}\'); + INSERT INTO "book_author" (book_id, author_id) VALUES ((SELECT id FROM "books" WHERE name = \'{bookname}\'), (SELECT id FROM "author" WHERE author_name = \'{author}\')); + INSERT INTO "inventory" (book_id, last_update) VALUES ((SELECT id FROM "books" WHERE name = \'{bookname}'), \'{datetime.date.today()}\');''' + cur.execute(commandd) + + command = f'SELECT title FROM genre;' + cur.execute(command) + genre_title = cur.fetchall() + + + for title in genre_title: + if title[0] == genre: + command = f'INSERT INTO "genre_book" (book_id, genre_id) VALUES ((SELECT id FROM "books" WHERE name = \'{bookname}\'), (SELECT genre_id FROM "genre" WHERE title = \'{genre}\'));' + cur.execute(command) + break + else: + cur.execute(f'INSERT INTO "genre" (title) VALUES (\'{genre}\');') + + except psycopg2.DatabaseError as e: + print(e) + + + except (SyntaxError, ValueError, psycopg2.DatabaseError) as e: + typer.echo(f"Could not sign in", e) + + + + + + if __name__ == '__main__': - connect() - - + connect() \ No newline at end of file diff --git a/main.py b/main.py index 9cb5481..821a99d 100644 --- a/main.py +++ b/main.py @@ -27,13 +27,26 @@ def sign_in(username: str, password: int): signIn(username, password) -@app.command() -def borrow_book(): - pass - # - # - # - # +@app.command("add_book") +def add_book(): + """Adds a new book to the library""" + + typer.secho(f"Sign in first to add a new book", fg=typer.colors.GREEN) + username = input("Enter username: ") + password = int(input("Enter password: ")) + if signIn(username, password): + typer.secho(f"Please enter the required book info to add!", fg=typer.colors.BLUE) + name = input("Name: ") + author = input("Author: ") + pages = int(input("# Pages: ")) + genre = input("Genres: ") + + addBook(name, author, pages, genre) + typer.secho(f"Seccuessfully added book!", fg=typer.colors.GREEN) + + else: + typer.secho(f"Please sign in again!", fg=typer.colors.RED) + # Example function for tables, you can add more columns/row From ca28b6c995417d813d8b30fdb358d8d643943fb3 Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Mon, 3 Apr 2023 09:05:06 +0200 Subject: [PATCH 04/27] borrow_book --- cli_library.sql | 21 +++++++++++++----- database.py | 59 ++++++++++++++++++++++++++++++++++++++++++++++--- main.py | 5 +++++ 3 files changed, 76 insertions(+), 9 deletions(-) diff --git a/cli_library.sql b/cli_library.sql index df7fdec..8d81904 100644 --- a/cli_library.sql +++ b/cli_library.sql @@ -10,7 +10,7 @@ CREATE TABLE "user" ( CREATE TABLE "books" ( "id" serial NOT NULL, - "name" TEXT NOT NULL, + "name" varchar(255) NOT NULL, "pages" bigint NOT NULL, CONSTRAINT "books_pk" PRIMARY KEY ("id") ) WITH ( @@ -59,7 +59,7 @@ CREATE TABLE "genre_book" ( CREATE TABLE "borrowed_books" ( "bb_id" serial NOT NULL, - "inventory_id" bigint NOT NULL, + "book_id" bigint NOT NULL, "username" varchar(255) NOT NULL, CONSTRAINT "borrowed_books_pk" PRIMARY KEY ("bb_id") ) WITH ( @@ -69,10 +69,8 @@ CREATE TABLE "borrowed_books" ( CREATE TABLE "inventory" ( - "inventory_id" serial NOT NULL, "book_id" bigint NOT NULL, - "last_update" DATE NOT NULL, - CONSTRAINT "inventory_pk" PRIMARY KEY ("inventory_id") + "last_update" DATE NOT NULL ) WITH ( OIDS=FALSE ); @@ -111,7 +109,7 @@ ALTER TABLE "book_author" ADD CONSTRAINT "book_author_fk1" FOREIGN KEY ("author_ ALTER TABLE "genre_book" ADD CONSTRAINT "genre_book_fk0" FOREIGN KEY ("book_id") REFERENCES "books"("id"); ALTER TABLE "genre_book" ADD CONSTRAINT "genre_book_fk1" FOREIGN KEY ("genre_id") REFERENCES "genre"("genre_id"); -ALTER TABLE "borrowed_books" ADD CONSTRAINT "borrowed_books_fk0" FOREIGN KEY ("inventory_id") REFERENCES "inventory"("inventory_id"); +ALTER TABLE "borrowed_books" ADD CONSTRAINT "borrowed_books_fk0" FOREIGN KEY ("book_id") REFERENCES "books"("id"); ALTER TABLE "borrowed_books" ADD CONSTRAINT "borrowed_books_fk1" FOREIGN KEY ("username") REFERENCES "user"("username"); ALTER TABLE "inventory" ADD CONSTRAINT "inventory_fk0" FOREIGN KEY ("book_id") REFERENCES "books"("id"); @@ -123,6 +121,17 @@ ALTER TABLE "read_books" ADD CONSTRAINT "read_books_fk0" FOREIGN KEY ("username" ALTER TABLE "read_books" ADD CONSTRAINT "read_books_fk1" FOREIGN KEY ("bb_id") REFERENCES "borrowed_books"("bb_id"); + + + + + + + + + + + INSERT INTO "user" (username, password) VALUES ('john2', '12345'); INSERT INTO "user" (username, password) VALUES ('jbiever1', '4568'); INSERT INTO "user" (username, password) VALUES ('sthomazet2', '7891'); diff --git a/database.py b/database.py index 1a3acb3..065cb74 100644 --- a/database.py +++ b/database.py @@ -205,7 +205,6 @@ def addBook(bookname: str, author: str, pages: int, genre: str): cur.execute(commandd) break else: - print('hello') commandd = f'''INSERT INTO "books" (name,pages) VALUES (\'{bookname}\',\'{pages}\'); INSERT INTO "author" (author_name) VALUES (\'{author}\'); INSERT INTO "book_author" (book_id, author_id) VALUES ((SELECT id FROM "books" WHERE name = \'{bookname}\'), (SELECT id FROM "author" WHERE author_name = \'{author}\')); @@ -225,13 +224,66 @@ def addBook(bookname: str, author: str, pages: int, genre: str): else: cur.execute(f'INSERT INTO "genre" (title) VALUES (\'{genre}\');') - + command = f'INSERT INTO "genre_book" (book_id, genre_id) VALUES ((SELECT id FROM "books" WHERE name = \'{bookname}\'), (SELECT genre_id FROM "genre" WHERE title = \'{genre}\'));' + cur.execute(command) except psycopg2.DatabaseError as e: print(e) + except (SyntaxError, ValueError, psycopg2.DatabaseError) as e: typer.echo(f"Could not sign in", e) + + +def borrowBook(id: int): + + username = input("Username: ") + password = int(input("Password: ")) + + if signIn(username, password): + + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + + cur.execute(f'SELECT book_id FROM inventory;') + available_books = cur.fetchall() + for book in available_books: + if book[0] == id: + print(book[0]) + command = f'''DELETE FROM inventory WHERE book_id = \'{id}\'; + INSERT INTO borrowed_books (book_id, username) VALUES (\'{id}\', \'{username}\');'''.split('\n') + + for i in command: + cur.execute(i) + typer.secho(f'You borrowed book {id}!') + break + else: + typer.secho('This book is not available') + + # available_books = cur.fetchall() + + + + + + # books = [] + # for bb in bb_books: + # cur.execute(f'SELECT book_id FROM inventory WHERE inventory_id = \'{bb[0]}\';') + # a = cur.fetchone() + # books.append(a) + + # for book in books: + # if book[0] == id: + # cur.execute(f'INSERT INTO borrowed') + + # available_books = cur.fetchall() + + # for book in available_books: + # if book[0] == id: + # command = f'''DELETE FROM inventory WHERE book_id = \'{id}\');''' + @@ -239,4 +291,5 @@ def addBook(bookname: str, author: str, pages: int, genre: str): if __name__ == '__main__': - connect() \ No newline at end of file + # connect() + borrowBook(7) \ No newline at end of file diff --git a/main.py b/main.py index 821a99d..7f1dfea 100644 --- a/main.py +++ b/main.py @@ -46,6 +46,11 @@ def add_book(): else: typer.secho(f"Please sign in again!", fg=typer.colors.RED) + +@app.command("borrow_book") +def borrow_book(id: int): + typer.secho(f"Sign in first to add borrow book", fg=typer.colors.GREEN) + borrowBook(id) From 39274e15107e85d16d517b451432d2b8cb481e9c Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Mon, 3 Apr 2023 19:05:13 +0200 Subject: [PATCH 05/27] Create .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d75edea --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +venv +__pycache__ \ No newline at end of file From 64aacb1bef30253d70f685aeee2e194fa5ed6821 Mon Sep 17 00:00:00 2001 From: Mohammad_Falah Date: Mon, 3 Apr 2023 21:31:52 +0200 Subject: [PATCH 06/27] search_about_the_book_using_nameadn_ author --- database.ini | 4 +-- database.py | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++- main.py | 11 ++++++- 3 files changed, 99 insertions(+), 4 deletions(-) diff --git a/database.ini b/database.ini index e0b5a06..84052c7 100644 --- a/database.ini +++ b/database.ini @@ -2,10 +2,10 @@ host=localhost database=cli_library user=postgres -password=1234 +password=12345678 [postgres] host=localhost database=postgres user=postgres -password=1234 \ No newline at end of file +password=12345678 \ No newline at end of file diff --git a/database.py b/database.py index 065cb74..82d8dc5 100644 --- a/database.py +++ b/database.py @@ -6,7 +6,7 @@ from config import config - +console = Console() def connect(): conn = None try: @@ -261,6 +261,92 @@ def borrowBook(id: int): break else: typer.secho('This book is not available') + + + +def Search_by_name(name : str ) : + try : + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + + cur.execute(f"SELECT books.id,name,author_name ,pages ,genre.title FROM books JOIN book_author on books.id = book_author.book_id JOIN author ON book_author.author_id = author.id JOIN genre ON books.id = genre.genre_id WHERE name= \'{name}\' order by name,pages ; ") + book_names = cur.fetchall() + for i in book_names: + if i[1] == name : + table = Table(show_header=True, header_style="bold blue") + table.add_column("#", style="dim", width=10) + table.add_column("BookID", style="dim", min_width=10, justify=True) + table.add_column("Name", style="dim", min_width=10, justify=True) + table.add_column("Author", style="dim", min_width=10, justify=True) + table.add_column("Pages", style="dim", min_width=10, justify=True) + table.add_column("Gener", style="dim", min_width=10, justify=True) + table.add_column("Availabili__", style="dim", min_width=10, justify=True) + table.add_row(f"{i[0]}", f"{i[0]}",f"{i[1]}",f"{i[2]}",f"{i[3]}",f"{i[4]}","True") + console.print(table) + break + else: + typer.secho(f"the name is wrong", fg=typer.colors.RED) + raise Exception + + except : + table = Table(show_header=True, header_style="bold blue") + table.add_column("#", style="dim", width=10) + table.add_column("BookID", style="dim", min_width=10, justify=True) + table.add_column("Name", style="dim", min_width=10, justify=True) + table.add_column("Author", style="dim", min_width=10, justify=True) + table.add_column("Pages", style="dim", min_width=10, justify=True) + table.add_column("Gener", style="dim", min_width=10, justify=True) + table.add_column("Availabili__", style="dim", min_width=10, justify=True) + table.add_row("--", "--","--","--","--","--","False") + console.print(table) + + + + + + + + +def Search_by_author(author : str) : + try : + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + cur.execute(f"SELECT books.id,name ,author_name ,pages ,genre.title FROM books JOIN book_author ON books.id = book_author.book_id JOIN author ON book_author.author_id = author.id JOIN genre ON books.id = genre.genre_id where author_name =\'{author}\' order by name,pages ;") + aurthor_name = cur.fetchmany() + for i in aurthor_name: + if i[2] == author : + table = Table(show_header=True, header_style="bold blue") + table.add_column("#", style="dim", width=10) + table.add_column("BookID", style="dim", min_width=10, justify=True) + table.add_column("Name", style="dim", min_width=10, justify=True) + table.add_column("Author", style="dim", min_width=10, justify=True) + table.add_column("Pages", style="dim", min_width=10, justify=True) + table.add_column("Gener", style="dim", min_width=10, justify=True) + table.add_column("Availabili__", style="dim", min_width=10, justify=True) + table.add_row(f"{i[0]}", f"{i[0]}",f"{i[1]}",f"{i[2]}",f"{i[3]}",f"{i[4]}","True") + console.print(table) + break + else: + typer.secho(f"the name is wrong", fg=typer.colors.RED) + raise Exception + + except : + table = Table(show_header=True, header_style="bold blue") + table.add_column("#", style="dim", width=10) + table.add_column("BookID", style="dim", min_width=10, justify=True) + table.add_column("Name", style="dim", min_width=10, justify=True) + table.add_column("Author", style="dim", min_width=10, justify=True) + table.add_column("Pages", style="dim", min_width=10, justify=True) + table.add_column("Gener", style="dim", min_width=10, justify=True) + table.add_column("Availabili__", style="dim", min_width=10, justify=True) + table.add_row("--", "--","--","--","--","--","False") + console.print(table) + + # available_books = cur.fetchall() diff --git a/main.py b/main.py index 7f1dfea..ade9e7c 100644 --- a/main.py +++ b/main.py @@ -51,7 +51,16 @@ def add_book(): def borrow_book(id: int): typer.secho(f"Sign in first to add borrow book", fg=typer.colors.GREEN) borrowBook(id) - +@app.command("search_by_name") +def search_by_name(name : str) : + typer.echo(f"lets search about {name}") + Search_by_name(name) + +@app.command("search_by_author") +def search_by_author(author : str): + typer.echo(f"lets search using author {author}") + Search_by_author(author) + # Example function for tables, you can add more columns/row From c26636e047c7f1972ab4ce374b535998bbf2ef43 Mon Sep 17 00:00:00 2001 From: Mohammad_Falah Date: Mon, 3 Apr 2023 21:35:47 +0200 Subject: [PATCH 07/27] new_test --- database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database.py b/database.py index 82d8dc5..cad4a03 100644 --- a/database.py +++ b/database.py @@ -308,7 +308,7 @@ def Search_by_name(name : str ) : - +#sdsdsdf def Search_by_author(author : str) : try : params = config('database.ini','CLI_Library') From 8371cf7a9a3d9f107f749f25e104458740a68945 Mon Sep 17 00:00:00 2001 From: Mohammad_Falah Date: Mon, 3 Apr 2023 23:57:20 +0200 Subject: [PATCH 08/27] Recently_added --- database.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++++---- main.py | 6 ++++++ 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/database.py b/database.py index cad4a03..53f2e1d 100644 --- a/database.py +++ b/database.py @@ -271,7 +271,11 @@ def Search_by_name(name : str ) : conn.autocommit = True cur = conn.cursor() - cur.execute(f"SELECT books.id,name,author_name ,pages ,genre.title FROM books JOIN book_author on books.id = book_author.book_id JOIN author ON book_author.author_id = author.id JOIN genre ON books.id = genre.genre_id WHERE name= \'{name}\' order by name,pages ; ") + cur.execute(f"""SELECT books.id,name,author_name ,pages ,genre.title FROM books + JOIN book_author on books.id = book_author.book_id + JOIN author ON book_author.author_id = author.id + JOIN genre ON books.id = genre.genre_id + WHERE name= \'{name}\' order by name,pages ; """) book_names = cur.fetchall() for i in book_names: if i[1] == name : @@ -308,14 +312,18 @@ def Search_by_name(name : str ) : -#sdsdsdf + def Search_by_author(author : str) : try : params = config('database.ini','CLI_Library') conn = psycopg2.connect(**params) conn.autocommit = True cur = conn.cursor() - cur.execute(f"SELECT books.id,name ,author_name ,pages ,genre.title FROM books JOIN book_author ON books.id = book_author.book_id JOIN author ON book_author.author_id = author.id JOIN genre ON books.id = genre.genre_id where author_name =\'{author}\' order by name,pages ;") + cur.execute(f"""SELECT books.id,name ,author_name ,pages ,genre.title FROM books + JOIN book_author ON books.id = book_author.book_id + JOIN author ON book_author.author_id = author.id + JOIN genre ON books.id = genre.genre_id + where author_name =\'{author}\' order by name,pages ;""") aurthor_name = cur.fetchmany() for i in aurthor_name: if i[2] == author : @@ -347,7 +355,43 @@ def Search_by_author(author : str) : console.print(table) - + +def Recently_added(): + + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + cur.execute("""SELECT books.id,name ,author_name ,pages ,genre.title FROM books + JOIN book_author ON books.id = book_author.book_id + JOIN author ON book_author.author_id = author.id + JOIN genre ON books.id = genre.genre_id + ORDER BY id DESC LIMIT 5 ;""") + books = cur.fetchall() + x = len(books) + i = 0 + my_result = [i for i in books] + ii = 0 + table = Table(show_header=True, header_style="bold blue") + table.add_column("#", style="dim", width=10) + table.add_column("BookID", style="dim", min_width=10, justify=True) + table.add_column("Name", style="dim", min_width=10, justify=True) + table.add_column("Author", style="dim", min_width=10, justify=True) + table.add_column("Pages", style="dim", min_width=10, justify=True) + table.add_column("Gener", style="dim", min_width=10, justify=True) + table.add_column("Availabili__", style="dim", min_width=10, justify=True) + while ii < x : + table.add_row(f"{my_result[ii][0]}", f"{my_result[ii][0]}",f"{my_result[ii][1]}",f"{my_result[ii][2]}",f"{my_result[ii][3]}",f"{my_result[ii][4]}","True") + ii += 1 + console.print(table) + + + + + + + + # available_books = cur.fetchall() diff --git a/main.py b/main.py index ade9e7c..6aa407a 100644 --- a/main.py +++ b/main.py @@ -61,6 +61,12 @@ def search_by_author(author : str): typer.echo(f"lets search using author {author}") Search_by_author(author) +@app.command("recently_added") +def recently_added(): + typer.echo(f"lets see which book recently added") + Recently_added() + + # Example function for tables, you can add more columns/row From 7bc203a7f74c925c1879791c132d7ca40e7c9aa3 Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Tue, 4 Apr 2023 06:38:27 +0200 Subject: [PATCH 09/27] updated --- cli_library.sql | 8 ++- database.py | 143 ++++++++++++++++++++++++++++++++++++++++++++---- main.py | 57 +++++++++++++++---- 3 files changed, 185 insertions(+), 23 deletions(-) diff --git a/cli_library.sql b/cli_library.sql index 8d81904..63bc2b8 100644 --- a/cli_library.sql +++ b/cli_library.sql @@ -69,8 +69,10 @@ CREATE TABLE "borrowed_books" ( CREATE TABLE "inventory" ( + "inventory_id" serial NOT NULL, "book_id" bigint NOT NULL, - "last_update" DATE NOT NULL + "last_update" DATE NOT NULL, + CONSTRAINT "inventory_pk" PRIMARY KEY ("inventory_id") ) WITH ( OIDS=FALSE ); @@ -91,7 +93,7 @@ CREATE TABLE "fav_books" ( CREATE TABLE "read_books" ( "mark_id" serial NOT NULL, "username" varchar(255) NOT NULL, - "bb_id" serial NOT NULL, + "book_id" bigint NOT NULL, CONSTRAINT "read_books_pk" PRIMARY KEY ("mark_id") ) WITH ( OIDS=FALSE @@ -118,7 +120,7 @@ ALTER TABLE "fav_books" ADD CONSTRAINT "fav_books_fk0" FOREIGN KEY ("book_id") R ALTER TABLE "fav_books" ADD CONSTRAINT "fav_books_fk1" FOREIGN KEY ("username") REFERENCES "user"("username"); ALTER TABLE "read_books" ADD CONSTRAINT "read_books_fk0" FOREIGN KEY ("username") REFERENCES "user"("username"); -ALTER TABLE "read_books" ADD CONSTRAINT "read_books_fk1" FOREIGN KEY ("bb_id") REFERENCES "borrowed_books"("bb_id"); +ALTER TABLE "read_books" ADD CONSTRAINT "read_books_fk1" FOREIGN KEY ("book_id") REFERENCES "books"("id"); diff --git a/database.py b/database.py index 065cb74..3270bba 100644 --- a/database.py +++ b/database.py @@ -28,14 +28,9 @@ def connect(): cur.execute("CREATE DATABASE cli_library;") typer.secho(f"Database created successfully", fg=typer.colors.GREEN) - - - - except psycopg2.Error as e: # If the CREATE DATABASE statement fails or another error occurs, catch the exception and print an error message typer.echo(f"The CREATE DATABASE statement failed: {e}") - else: params = config('database.ini','CLI_Library') @@ -218,8 +213,9 @@ def addBook(bookname: str, author: str, pages: int, genre: str): for title in genre_title: if title[0] == genre: - command = f'INSERT INTO "genre_book" (book_id, genre_id) VALUES ((SELECT id FROM "books" WHERE name = \'{bookname}\'), (SELECT genre_id FROM "genre" WHERE title = \'{genre}\'));' - cur.execute(command) + pass + # command = f'INSERT INTO "genre_book" (book_id, genre_id) VALUES ((SELECT id FROM "books" WHERE name = \'{bookname}\'), (SELECT genre_id FROM "genre" WHERE title = \'{genre}\'));' + # cur.execute(command) break else: @@ -247,12 +243,15 @@ def borrowBook(id: int): conn.autocommit = True cur = conn.cursor() + + cur.execute(f'SELECT book_id FROM inventory;') available_books = cur.fetchall() + # cur.execute(f'SELECT date FROM inventory WHERE book_id = 1 FETCH ONLY for book in available_books: if book[0] == id: print(book[0]) - command = f'''DELETE FROM inventory WHERE book_id = \'{id}\'; + command = f'''DELETE FROM inventory WHERE inventory_id IN (SELECT inventory_id FROM inventory WHERE book_id = {id} limit 1); INSERT INTO borrowed_books (book_id, username) VALUES (\'{id}\', \'{username}\');'''.split('\n') for i in command: @@ -284,11 +283,135 @@ def borrowBook(id: int): # if book[0] == id: # command = f'''DELETE FROM inventory WHERE book_id = \'{id}\');''' - - +def returnBook(id: int): + username = input("Username: ") + password = int(input("Password: ")) + + if signIn(username, password): + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + cur.execute(f'SELECT book_id FROM borrowed_books WHERE username = \'{username}\';') + borrowed_books = cur.fetchall() + for book in borrowed_books: + if book[0] == id: + command = f'''DELETE FROM borrowed_books WHERE bb_id IN (SELECT bb_id FROM borrowed_books WHERE book_id = {id} limit 1); + INSERT INTO "inventory" (book_id, last_update) VALUES (\'{id}\', \'{datetime.date.today()}\'); ''' + cur.execute(command) + typer.secho(f'You returned book {id}!') + break + else: + typer.echo(f'You didn\'t borrow book {id}') + else: + typer.echo(f"Could not sign in") + + + +def markRead(id: int): + + try: + username = input("Username: ") + password = int(input("Password: ")) + + if signIn(username, password): + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + + cur.execute(f'SELECT id FROM books;') + books = cur.fetchall() + for book in books: + if book[0] == id: + command = f'INSERT INTO read_books (username, book_id) VALUES (\'{username}\', \'{id}\');' + cur.execute(command) + typer.secho(f'You marked book {id} as read!') + break + else: + typer.echo(f'Book {id} doesn\'t exist!') + else: + typer.echo(f"Could not sign in") + except (ValueError,AttributeError) as e: + typer.echo(f"Sign in failed",e) + + + +def favBook(id: int): + username = input("Username: ") + password = int(input("Password: ")) + + if signIn(username, password): + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + + cur.execute(f'SELECT id FROM books;') + books = cur.fetchall() + + for book in books: + if book[0] == id: + command = f'INSERT INTO fav_books (book_id, username) VALUES (\'{id}\',\'{username}\');' + cur.execute(command) + typer.secho(f'You added book {id} to your favorites!') + break + else: + typer.echo(f'Book {id} doesn\'t exist!') + else: + typer.echo(f"Could not sign in") + + + +def readBooks(user: str): + + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + + command = f'''SELECT DISTINCT mr.book_id, b.name, a.author_name, b.pages, g.title, count(inv.book_id) AS Availability + FROM read_books mr + INNER JOIN books b ON b.id = mr.book_id + RIGHT JOIN book_author ab on ab.book_id = b.id + LEFT JOIN author a ON a.id = ab.author_id + right JOIN genre_book gb on gb.book_id = b.id + left join genre g on g.genre_id = gb.genre_id + INNER JOIN inventory inv on inv.book_id = b.id + WHERE mr.username = '{user}' + GROUP BY mr.book_id,b.name,a.author_name,b.pages, g.title;''' + + cur.execute(command) + read_books = cur.fetchall() + + return read_books + +def favoriteBooks(user: str): + + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + + command2 = f'''SELECT DISTINCT fav.book_id, b.name, a.author_name, b.pages, g.title, count(inv.book_id) AS Availability + FROM fav_books fav + INNER JOIN books b ON b.id = fav.book_id + RIGHT JOIN book_author ab on ab.book_id = b.id + LEFT JOIN author a ON a.id = ab.author_id + right JOIN genre_book gb on gb.book_id = b.id + left join genre g on g.genre_id = gb.genre_id + INNER JOIN inventory inv on inv.book_id = b.id + WHERE fav.username = '{user}' + GROUP BY fav.book_id,b.name,a.author_name,b.pages, g.title''' + + cur.execute(command2) + fav_books = cur.fetchall() + + return fav_books + if __name__ == '__main__': # connect() diff --git a/main.py b/main.py index 7f1dfea..4ea60a3 100644 --- a/main.py +++ b/main.py @@ -49,22 +49,59 @@ def add_book(): @app.command("borrow_book") def borrow_book(id: int): - typer.secho(f"Sign in first to add borrow book", fg=typer.colors.GREEN) + typer.secho(f"Sign in first to borrow book", fg=typer.colors.GREEN) borrowBook(id) +@app.command("return_book") +def return_book(id: int): + typer.secho(f"Sign in first to return book", fg=typer.colors.GREEN) + returnBook(id) + +@app.command("mark_read") +def mark_read(id: int): + typer.secho(f"Sign in first to mark book as read", fg=typer.colors.GREEN) + markRead(id) + +@app.command("fav_book") +def fav_book(id: int): + typer.secho(f"Sign in first to mark book as read", fg=typer.colors.GREEN) + favBook(id) - # Example function for tables, you can add more columns/row -@app.command("display_table") -def display_table(): +@app.command("my_books") +def my_books(): + try: + username = input("Username: ") + password = int(input("Password: ")) + + if signIn(username, password): + books = readBooks(username) + typer.secho(f"BOOKS YOU READ") + display_table(books) + books2 = favoriteBooks(username) + typer.secho(f"YOUR FAVORITE BOOKS") + display_table(books2) + else: + typer.secho(f"Sign in again!") + except (ValueError,AttributeError) as e: + typer.echo(f'Sign in again!', e) + + +def display_table(books): + table = Table(show_header=True, header_style="bold blue") - table.add_column("Column 1", style="dim", width=10) - table.add_column("Column 2", style="dim", min_width=10, justify=True) - - table.add_row('Value 1', 'Value 2') - table.add_row('Value 3', 'Value 4') - table.add_row('Value 5', 'Value 6') + + table.add_column("Book ID", style="dim", min_width=10, justify=True) + table.add_column("Book Name", style="dim", min_width=10, justify=True) + table.add_column("Author", style="dim", min_width=10, justify=True) + table.add_column("Pages", style="dim", min_width=10, justify=True) + table.add_column("Genre", style="dim", min_width=10, justify=True) + table.add_column("Availability", style="dim", min_width=10, justify=True) + + for book in books: + table.add_row(str(book[0]),book[1], book[2], str(book[3]), book[4], str(book[5])) + console.print(table) if __name__ == "__main__": From f5279a6fd040902d2948596f0ea1dc0da7a6526f Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Tue, 4 Apr 2023 06:41:45 +0200 Subject: [PATCH 10/27] Update database.py --- database.py | 1 + 1 file changed, 1 insertion(+) diff --git a/database.py b/database.py index 3270bba..729999d 100644 --- a/database.py +++ b/database.py @@ -387,6 +387,7 @@ def readBooks(user: str): cur.execute(command) read_books = cur.fetchall() + return read_books def favoriteBooks(user: str): From 513e21eaa19363d3805f791e91767ffddb61e9ac Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Tue, 4 Apr 2023 06:51:21 +0200 Subject: [PATCH 11/27] updated --- .gitignore | 3 ++- database.ini | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index d75edea..d7ea7da 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ venv -__pycache__ \ No newline at end of file +__pycache__ +database.ini \ No newline at end of file diff --git a/database.ini b/database.ini index 84052c7..e0b5a06 100644 --- a/database.ini +++ b/database.ini @@ -2,10 +2,10 @@ host=localhost database=cli_library user=postgres -password=12345678 +password=1234 [postgres] host=localhost database=postgres user=postgres -password=12345678 \ No newline at end of file +password=1234 \ No newline at end of file From d86e40a43643ac9e9f67f5187ddd41cf16b7f866 Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Tue, 4 Apr 2023 06:52:30 +0200 Subject: [PATCH 12/27] Update database.py --- database.py | 255 ++++++++++++++++++++++++++-------------------------- 1 file changed, 125 insertions(+), 130 deletions(-) diff --git a/database.py b/database.py index be16ace..9844b76 100644 --- a/database.py +++ b/database.py @@ -260,136 +260,6 @@ def borrowBook(id: int): break else: typer.secho('This book is not available') - - - -def Search_by_name(name : str ) : - try : - params = config('database.ini','CLI_Library') - conn = psycopg2.connect(**params) - conn.autocommit = True - cur = conn.cursor() - - cur.execute(f"""SELECT books.id,name,author_name ,pages ,genre.title FROM books - JOIN book_author on books.id = book_author.book_id - JOIN author ON book_author.author_id = author.id - JOIN genre ON books.id = genre.genre_id - WHERE name= \'{name}\' order by name,pages ; """) - book_names = cur.fetchall() - for i in book_names: - if i[1] == name : - table = Table(show_header=True, header_style="bold blue") - table.add_column("#", style="dim", width=10) - table.add_column("BookID", style="dim", min_width=10, justify=True) - table.add_column("Name", style="dim", min_width=10, justify=True) - table.add_column("Author", style="dim", min_width=10, justify=True) - table.add_column("Pages", style="dim", min_width=10, justify=True) - table.add_column("Gener", style="dim", min_width=10, justify=True) - table.add_column("Availabili__", style="dim", min_width=10, justify=True) - table.add_row(f"{i[0]}", f"{i[0]}",f"{i[1]}",f"{i[2]}",f"{i[3]}",f"{i[4]}","True") - console.print(table) - break - else: - typer.secho(f"the name is wrong", fg=typer.colors.RED) - raise Exception - - except : - table = Table(show_header=True, header_style="bold blue") - table.add_column("#", style="dim", width=10) - table.add_column("BookID", style="dim", min_width=10, justify=True) - table.add_column("Name", style="dim", min_width=10, justify=True) - table.add_column("Author", style="dim", min_width=10, justify=True) - table.add_column("Pages", style="dim", min_width=10, justify=True) - table.add_column("Gener", style="dim", min_width=10, justify=True) - table.add_column("Availabili__", style="dim", min_width=10, justify=True) - table.add_row("--", "--","--","--","--","--","False") - console.print(table) - - - - - - - - -def Search_by_author(author : str) : - try : - params = config('database.ini','CLI_Library') - conn = psycopg2.connect(**params) - conn.autocommit = True - cur = conn.cursor() - cur.execute(f"""SELECT books.id,name ,author_name ,pages ,genre.title FROM books - JOIN book_author ON books.id = book_author.book_id - JOIN author ON book_author.author_id = author.id - JOIN genre ON books.id = genre.genre_id - where author_name =\'{author}\' order by name,pages ;""") - aurthor_name = cur.fetchmany() - for i in aurthor_name: - if i[2] == author : - table = Table(show_header=True, header_style="bold blue") - table.add_column("#", style="dim", width=10) - table.add_column("BookID", style="dim", min_width=10, justify=True) - table.add_column("Name", style="dim", min_width=10, justify=True) - table.add_column("Author", style="dim", min_width=10, justify=True) - table.add_column("Pages", style="dim", min_width=10, justify=True) - table.add_column("Gener", style="dim", min_width=10, justify=True) - table.add_column("Availabili__", style="dim", min_width=10, justify=True) - table.add_row(f"{i[0]}", f"{i[0]}",f"{i[1]}",f"{i[2]}",f"{i[3]}",f"{i[4]}","True") - console.print(table) - break - else: - typer.secho(f"the name is wrong", fg=typer.colors.RED) - raise Exception - - except : - table = Table(show_header=True, header_style="bold blue") - table.add_column("#", style="dim", width=10) - table.add_column("BookID", style="dim", min_width=10, justify=True) - table.add_column("Name", style="dim", min_width=10, justify=True) - table.add_column("Author", style="dim", min_width=10, justify=True) - table.add_column("Pages", style="dim", min_width=10, justify=True) - table.add_column("Gener", style="dim", min_width=10, justify=True) - table.add_column("Availabili__", style="dim", min_width=10, justify=True) - table.add_row("--", "--","--","--","--","--","False") - console.print(table) - - - -def Recently_added(): - - params = config('database.ini','CLI_Library') - conn = psycopg2.connect(**params) - conn.autocommit = True - cur = conn.cursor() - cur.execute("""SELECT books.id,name ,author_name ,pages ,genre.title FROM books - JOIN book_author ON books.id = book_author.book_id - JOIN author ON book_author.author_id = author.id - JOIN genre ON books.id = genre.genre_id - ORDER BY id DESC LIMIT 5 ;""") - books = cur.fetchall() - x = len(books) - i = 0 - my_result = [i for i in books] - ii = 0 - table = Table(show_header=True, header_style="bold blue") - table.add_column("#", style="dim", width=10) - table.add_column("BookID", style="dim", min_width=10, justify=True) - table.add_column("Name", style="dim", min_width=10, justify=True) - table.add_column("Author", style="dim", min_width=10, justify=True) - table.add_column("Pages", style="dim", min_width=10, justify=True) - table.add_column("Gener", style="dim", min_width=10, justify=True) - table.add_column("Availabili__", style="dim", min_width=10, justify=True) - while ii < x : - table.add_row(f"{my_result[ii][0]}", f"{my_result[ii][0]}",f"{my_result[ii][1]}",f"{my_result[ii][2]}",f"{my_result[ii][3]}",f"{my_result[ii][4]}","True") - ii += 1 - console.print(table) - - - - - - - # available_books = cur.fetchall() @@ -542,7 +412,132 @@ def favoriteBooks(user: str): fav_books = cur.fetchall() return fav_books + +def Search_by_name(name : str ) : + try : + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + + cur.execute(f"""SELECT books.id,name,author_name ,pages ,genre.title FROM books + JOIN book_author on books.id = book_author.book_id + JOIN author ON book_author.author_id = author.id + JOIN genre ON books.id = genre.genre_id + WHERE name= \'{name}\' order by name,pages ; """) + book_names = cur.fetchall() + for i in book_names: + if i[1] == name : + table = Table(show_header=True, header_style="bold blue") + table.add_column("#", style="dim", width=10) + table.add_column("BookID", style="dim", min_width=10, justify=True) + table.add_column("Name", style="dim", min_width=10, justify=True) + table.add_column("Author", style="dim", min_width=10, justify=True) + table.add_column("Pages", style="dim", min_width=10, justify=True) + table.add_column("Gener", style="dim", min_width=10, justify=True) + table.add_column("Availabili__", style="dim", min_width=10, justify=True) + table.add_row(f"{i[0]}", f"{i[0]}",f"{i[1]}",f"{i[2]}",f"{i[3]}",f"{i[4]}","True") + console.print(table) + break + else: + typer.secho(f"the name is wrong", fg=typer.colors.RED) + raise Exception + + except : + table = Table(show_header=True, header_style="bold blue") + table.add_column("#", style="dim", width=10) + table.add_column("BookID", style="dim", min_width=10, justify=True) + table.add_column("Name", style="dim", min_width=10, justify=True) + table.add_column("Author", style="dim", min_width=10, justify=True) + table.add_column("Pages", style="dim", min_width=10, justify=True) + table.add_column("Gener", style="dim", min_width=10, justify=True) + table.add_column("Availabili__", style="dim", min_width=10, justify=True) + table.add_row("--", "--","--","--","--","--","False") + console.print(table) + + + + + + + +def Search_by_author(author : str) : + try : + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + cur.execute(f"""SELECT books.id,name ,author_name ,pages ,genre.title FROM books + JOIN book_author ON books.id = book_author.book_id + JOIN author ON book_author.author_id = author.id + JOIN genre ON books.id = genre.genre_id + where author_name =\'{author}\' order by name,pages ;""") + aurthor_name = cur.fetchmany() + for i in aurthor_name: + if i[2] == author : + table = Table(show_header=True, header_style="bold blue") + table.add_column("#", style="dim", width=10) + table.add_column("BookID", style="dim", min_width=10, justify=True) + table.add_column("Name", style="dim", min_width=10, justify=True) + table.add_column("Author", style="dim", min_width=10, justify=True) + table.add_column("Pages", style="dim", min_width=10, justify=True) + table.add_column("Gener", style="dim", min_width=10, justify=True) + table.add_column("Availabili__", style="dim", min_width=10, justify=True) + table.add_row(f"{i[0]}", f"{i[0]}",f"{i[1]}",f"{i[2]}",f"{i[3]}",f"{i[4]}","True") + console.print(table) + break + else: + typer.secho(f"the name is wrong", fg=typer.colors.RED) + raise Exception + + except : + table = Table(show_header=True, header_style="bold blue") + table.add_column("#", style="dim", width=10) + table.add_column("BookID", style="dim", min_width=10, justify=True) + table.add_column("Name", style="dim", min_width=10, justify=True) + table.add_column("Author", style="dim", min_width=10, justify=True) + table.add_column("Pages", style="dim", min_width=10, justify=True) + table.add_column("Gener", style="dim", min_width=10, justify=True) + table.add_column("Availabili__", style="dim", min_width=10, justify=True) + table.add_row("--", "--","--","--","--","--","False") + console.print(table) + + + +def Recently_added(): + + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + cur.execute("""SELECT books.id,name ,author_name ,pages ,genre.title FROM books + JOIN book_author ON books.id = book_author.book_id + JOIN author ON book_author.author_id = author.id + JOIN genre ON books.id = genre.genre_id + ORDER BY id DESC LIMIT 5 ;""") + books = cur.fetchall() + x = len(books) + i = 0 + my_result = [i for i in books] + ii = 0 + table = Table(show_header=True, header_style="bold blue") + table.add_column("#", style="dim", width=10) + table.add_column("BookID", style="dim", min_width=10, justify=True) + table.add_column("Name", style="dim", min_width=10, justify=True) + table.add_column("Author", style="dim", min_width=10, justify=True) + table.add_column("Pages", style="dim", min_width=10, justify=True) + table.add_column("Gener", style="dim", min_width=10, justify=True) + table.add_column("Availabili__", style="dim", min_width=10, justify=True) + while ii < x : + table.add_row(f"{my_result[ii][0]}", f"{my_result[ii][0]}",f"{my_result[ii][1]}",f"{my_result[ii][2]}",f"{my_result[ii][3]}",f"{my_result[ii][4]}","True") + ii += 1 + console.print(table) + + + + + if __name__ == '__main__': # connect() From 3de9cf366ec2667c385da83161c3cbe60e21e752 Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Tue, 4 Apr 2023 06:55:01 +0200 Subject: [PATCH 13/27] Update cli_library.sql --- cli_library.sql | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/cli_library.sql b/cli_library.sql index 63bc2b8..65a30f3 100644 --- a/cli_library.sql +++ b/cli_library.sql @@ -123,17 +123,6 @@ ALTER TABLE "read_books" ADD CONSTRAINT "read_books_fk0" FOREIGN KEY ("username" ALTER TABLE "read_books" ADD CONSTRAINT "read_books_fk1" FOREIGN KEY ("book_id") REFERENCES "books"("id"); - - - - - - - - - - - INSERT INTO "user" (username, password) VALUES ('john2', '12345'); INSERT INTO "user" (username, password) VALUES ('jbiever1', '4568'); INSERT INTO "user" (username, password) VALUES ('sthomazet2', '7891'); From 81061d85c9cd2f5c4820042d9b495e6a1ca4ba56 Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Tue, 4 Apr 2023 07:00:02 +0200 Subject: [PATCH 14/27] Update main.py --- main.py | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/main.py b/main.py index 0e17799..0eefdee 100644 --- a/main.py +++ b/main.py @@ -68,24 +68,7 @@ def fav_book(id: int): typer.secho(f"Sign in first to mark book as read", fg=typer.colors.GREEN) favBook(id) -@app.command("search_by_name") -def search_by_name(name : str) : - typer.echo(f"lets search about {name}") - Search_by_name(name) - -@app.command("search_by_author") -def search_by_author(author : str): - typer.echo(f"lets search using author {author}") - Search_by_author(author) -@app.command("recently_added") -def recently_added(): - typer.echo(f"lets see which book recently added") - Recently_added() - - - -# Example function for tables, you can add more columns/row @app.command("my_books") def my_books(): try: @@ -104,7 +87,23 @@ def my_books(): except (ValueError,AttributeError) as e: typer.echo(f'Sign in again!', e) +@app.command("search_by_name") +def search_by_name(name : str) : + typer.echo(f"lets search about {name}") + Search_by_name(name) + +@app.command("search_by_author") +def search_by_author(author : str): + typer.echo(f"lets search using author {author}") + Search_by_author(author) + +@app.command("recently_added") +def recently_added(): + typer.echo(f"lets see which book recently added") + Recently_added() + +# Example function for tables, you can add more columns/row def display_table(books): table = Table(show_header=True, header_style="bold blue") From c979344ae13af6472842fbc42262952bd658dfc9 Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Tue, 4 Apr 2023 07:04:01 +0200 Subject: [PATCH 15/27] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index d7ea7da..24c6c97 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ venv __pycache__ +.gitignore database.ini \ No newline at end of file From 31a8612d37828ca0b06a55cc0d9061c4de9938df Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Tue, 4 Apr 2023 07:08:56 +0200 Subject: [PATCH 16/27] updated --- .gitignore | 4 ---- database.ini | 11 ----------- 2 files changed, 15 deletions(-) delete mode 100644 .gitignore delete mode 100644 database.ini diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 24c6c97..0000000 --- a/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -venv -__pycache__ -.gitignore -database.ini \ No newline at end of file diff --git a/database.ini b/database.ini deleted file mode 100644 index e0b5a06..0000000 --- a/database.ini +++ /dev/null @@ -1,11 +0,0 @@ -[CLI_Library] -host=localhost -database=cli_library -user=postgres -password=1234 - -[postgres] -host=localhost -database=postgres -user=postgres -password=1234 \ No newline at end of file From 3b3bd8ca5c0fb8e5b6667b556488336acb1a0bd9 Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Tue, 4 Apr 2023 07:11:45 +0200 Subject: [PATCH 17/27] Update main.py --- main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/main.py b/main.py index 0eefdee..7768679 100644 --- a/main.py +++ b/main.py @@ -72,6 +72,7 @@ def fav_book(id: int): @app.command("my_books") def my_books(): try: + typer.secho(f"Sign in first to show books", fg=typer.colors.GREEN) username = input("Username: ") password = int(input("Password: ")) From c0b2117766640aafabb8b7c1b9cd7d5fa31ece10 Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Wed, 5 Apr 2023 00:53:07 +0200 Subject: [PATCH 18/27] statistics --- database.py | 29 +++++++++++++++++++++++++++-- main.py | 25 +++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/database.py b/database.py index 9844b76..7a463af 100644 --- a/database.py +++ b/database.py @@ -413,6 +413,31 @@ def favoriteBooks(user: str): return fav_books +def statestics(user: str): + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + + command = f'''SELECT COUNT(*) FROM read_books WHERE username = '{user}';, + SELECT COUNT(ba.author_id) FROM book_author ba + INNER JOIN read_books rb on rb.book_id = ba.book_id + WHERE username = '{user}';, + SELECT COUNT(gb.genre_id) FROM genre_book gb + INNER JOIN read_books rb on rb.book_id = gb.book_id + WHERE username = '{user}';, + SELECT CAST(SUM(b.pages) AS BIGINT) FROM books b + INNER JOIN read_books rb on rb.book_id = b.id + WHERE username = '{user}';'''.split(',') + + stats = [] + for i in command: + cur.execute(i) + stats.append(*cur.fetchall()) + + return stats + + def Search_by_name(name : str ) : try : params = config('database.ini','CLI_Library') @@ -540,5 +565,5 @@ def Recently_added(): if __name__ == '__main__': - # connect() - borrowBook(7) \ No newline at end of file + connect() + \ No newline at end of file diff --git a/main.py b/main.py index 7768679..067396a 100644 --- a/main.py +++ b/main.py @@ -103,6 +103,31 @@ def recently_added(): typer.echo(f"lets see which book recently added") Recently_added() +@app.command("statistics") +def Statistics(): + try : + typer.secho(f"Sign in first to show statistics about user", fg=typer.colors.GREEN) + username = input("Username: ") + password = int(input("Password: ")) + + if signIn(username,password): + stats = statestics(username) + table = Table(show_header=True, header_style="bold blue") + + table.add_column("Statistics", style="dim", min_width=10, justify=True) + table.add_column("Numbers", style="dim", min_width=10, justify=True) + + names = ['Books you read', 'Authors you read', 'Genres you read', 'Total pages you read'] + + for i,j in enumerate(zip(stats)): + table.add_row(names[i],str(*j[0])) + + console.print(table) + + + except (ValueError,AttributeError, psycopg2.DatabaseError) as e: + typer.echo(f'Sign in failed!', e) + # Example function for tables, you can add more columns/row def display_table(books): From d6a21d41483351d49fa6527aefbbe85b075f930a Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Wed, 5 Apr 2023 06:36:49 +0200 Subject: [PATCH 19/27] updated --- database.py | 72 +++++++++++++++++++++++++++++++++++++++++++++-------- main.py | 48 +++++++++++++++++++++++++++++------ 2 files changed, 102 insertions(+), 18 deletions(-) diff --git a/database.py b/database.py index 7a463af..6413585 100644 --- a/database.py +++ b/database.py @@ -353,14 +353,30 @@ def favBook(id: int): cur.execute(f'SELECT id FROM books;') books = cur.fetchall() - for book in books: - if book[0] == id: - command = f'INSERT INTO fav_books (book_id, username) VALUES (\'{id}\',\'{username}\');' - cur.execute(command) - typer.secho(f'You added book {id} to your favorites!') - break + cur.execute(f'SELECT book_id, username FROM fav_books;') + fav_books = zip(*cur.fetchall()) + + + fuser = [] + fbook = [] + for i,j in zip(*fav_books): + fuser.append(j) + fbook.append(i) + + if username in fuser and id in fbook: + typer.secho(f'Book {id} is already in your favorite list!', fg= typer.colors.RED) else: - typer.echo(f'Book {id} doesn\'t exist!') + for book in books: + if book[0] == id: + command = f'INSERT INTO fav_books (book_id, username) VALUES (\'{id}\',\'{username}\');' + cur.execute(command) + typer.secho(f'You added book {id} to your favorites!') + break + else: + typer.echo(f'Book {id} doesn\'t exist!') + + + else: typer.echo(f"Could not sign in") @@ -558,11 +574,45 @@ def Recently_added(): table.add_row(f"{my_result[ii][0]}", f"{my_result[ii][0]}",f"{my_result[ii][1]}",f"{my_result[ii][2]}",f"{my_result[ii][3]}",f"{my_result[ii][4]}","True") ii += 1 console.print(table) + +def mostReadBooks(GENRE : str): + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + + if GENRE: + command = f'''SELECT rb.book_id, b.name, a.author_name, g.title, count(rb.book_id) as Number_read + FROM read_books rb + LEFT JOIN books b ON rb.book_id = b.id + RIGHT JOIN book_author ab ON ab.book_id = rb.book_id + RIGHT JOIN author a ON a.id = ab.author_id + RIGHT JOIN genre_book gb ON gb.book_id = rb.book_id + RIGHT JOIN genre g ON g.genre_id = gb.genre_id + WHERE g.title = '{GENRE}' + GROUP BY rb.book_id, b.name, a.author_name,g.title + ORDER BY Number_read DESC + LIMIT 10;''' - - - - + cur.execute(command) + most_read = cur.fetchall() + return most_read + else: + command = f'''SELECT rb.book_id, b.name, a.author_name, g.title, count(rb.book_id) as Number_read + FROM read_books rb + LEFT JOIN books b ON rb.book_id = b.id + RIGHT JOIN book_author ab ON ab.book_id = rb.book_id + RIGHT JOIN author a ON a.id = ab.author_id + RIGHT JOIN genre_book gb ON gb.book_id = rb.book_id + RIGHT JOIN genre g ON g.genre_id = gb.genre_id + GROUP BY rb.book_id, b.name, a.author_name,g.title + ORDER BY Number_read DESC + LIMIT 10;''' + + cur.execute(command) + most_read = cur.fetchall() + return most_read + if __name__ == '__main__': connect() diff --git a/main.py b/main.py index 067396a..9eb5653 100644 --- a/main.py +++ b/main.py @@ -10,6 +10,7 @@ @app.command("start") def start(): + """This command creats and connects to the database""" typer.secho(f'''Welcome to Library CLI!\n\n You can execute command '--help' to see the possible commands''', fg=typer.colors.GREEN) connect() @@ -17,6 +18,7 @@ def start(): # This is how you can get arguments, here username is a mandatory argument for this command. @app.command("sign_up") def sign_up(username: str, password: int): + """This command allows the user to sign up""" typer.echo(f"Nice that you are signing up!") singUp(username, password) @@ -29,7 +31,7 @@ def sign_in(username: str, password: int): @app.command("add_book") def add_book(): - """Adds a new book to the library""" + """This command adds a new book to the library""" typer.secho(f"Sign in first to add a new book", fg=typer.colors.GREEN) username = input("Enter username: ") @@ -49,28 +51,33 @@ def add_book(): @app.command("borrow_book") def borrow_book(id: int): + """This command allows the user to borrow a book from the library""" typer.secho(f"Sign in first to borrow book", fg=typer.colors.GREEN) borrowBook(id) @app.command("return_book") def return_book(id: int): + """This command allows the user to return a book to the library""" typer.secho(f"Sign in first to return book", fg=typer.colors.GREEN) returnBook(id) @app.command("mark_read") def mark_read(id: int): + """This command allows the user to mark a book as read""" typer.secho(f"Sign in first to mark book as read", fg=typer.colors.GREEN) markRead(id) @app.command("fav_book") def fav_book(id: int): + """This command allows the user to add books to their favorite list""" typer.secho(f"Sign in first to mark book as read", fg=typer.colors.GREEN) favBook(id) @app.command("my_books") def my_books(): + """This command gives a summary of the books the user has read and their book favorite list""" try: typer.secho(f"Sign in first to show books", fg=typer.colors.GREEN) username = input("Username: ") @@ -89,22 +96,48 @@ def my_books(): typer.echo(f'Sign in again!', e) @app.command("search_by_name") -def search_by_name(name : str) : +def search_by_name(name : str): + """This command allows the user to search for a book by its name""" typer.echo(f"lets search about {name}") Search_by_name(name) @app.command("search_by_author") def search_by_author(author : str): + """This command allows the user to search for a book by author's name""" typer.echo(f"lets search using author {author}") Search_by_author(author) @app.command("recently_added") def recently_added(): - typer.echo(f"lets see which book recently added") - Recently_added() + """This command shows the user the books that were recently added""" + typer.echo(f"lets see which book recently added") + Recently_added() + + +@app.command("most_read_books") +def most_read_books(genre : Optional[str]= typer.Argument("")): + """This command shows the user their most read books and how many times they read the book""" + books = mostReadBooks(genre) + table = Table(show_header=True, header_style="bold blue") + + table.add_column("#", style="dim", min_width=10, justify=True) + table.add_column("Book ID", style="dim", min_width=10, justify=True) + table.add_column("Book Name", style="dim", min_width=10, justify=True) + table.add_column("Author", style="dim", min_width=10, justify=True) + table.add_column("Genre", style="dim", min_width=10, justify=True) + table.add_column("Times read", style="dim", min_width=10, justify=True) + + for i,book in enumerate(books): + table.add_row(str(i+1),str(book[0]),book[1], book[2], book[3], str(book[4])) + + console.print(table) + + + @app.command("statistics") def Statistics(): + """This command shows a statistics of books, authors, genres and no. of pages the user has read""" try : typer.secho(f"Sign in first to show statistics about user", fg=typer.colors.GREEN) username = input("Username: ") @@ -133,7 +166,8 @@ def Statistics(): def display_table(books): table = Table(show_header=True, header_style="bold blue") - + + table.add_column("#", style="dim", min_width=10, justify=True) table.add_column("Book ID", style="dim", min_width=10, justify=True) table.add_column("Book Name", style="dim", min_width=10, justify=True) table.add_column("Author", style="dim", min_width=10, justify=True) @@ -142,8 +176,8 @@ def display_table(books): table.add_column("Availability", style="dim", min_width=10, justify=True) - for book in books: - table.add_row(str(book[0]),book[1], book[2], str(book[3]), book[4], str(book[5])) + for i, book in enumerate(books): + table.add_row(str(i+1),str(book[0]),book[1], book[2], str(book[3]), book[4], str(book[5])) console.print(table) From 9a063b19af9fd06c3e7978f646c16d0268f009ee Mon Sep 17 00:00:00 2001 From: Mohammad_Falah Date: Wed, 5 Apr 2023 21:51:27 +0200 Subject: [PATCH 20/27] fav_book --- database.py | 39 +++++++++++++++++++++++++++++++++++++++ main.py | 22 +++++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/database.py b/database.py index 6413585..be6425f 100644 --- a/database.py +++ b/database.py @@ -612,7 +612,46 @@ def mostReadBooks(GENRE : str): cur.execute(command) most_read = cur.fetchall() return most_read + +def Most_favorite_books(GENRE : str): + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + if GENRE: + command = f'''SELECT fb.book_id, b.name, a.author_name, g.title, count(fb.book_id) as Number_read + FROM fav_books fb + LEFT JOIN books b ON fb.book_id = b.id + RIGHT JOIN book_author ab ON ab.book_id = fb.book_id + RIGHT JOIN author a ON a.id = ab.author_id + RIGHT JOIN genre_book gb ON gb.book_id = fb.book_id + RIGHT JOIN genre g ON g.genre_id = gb.genre_id + WHERE g.title = '{GENRE}' + GROUP BY fb.book_id, b.name, a.author_name,g.title + ORDER BY Number_read DESC + LIMIT 10;''' + + cur.execute(command) + most_read = cur.fetchall() + return most_read + else: + command = f'''SELECT fb.book_id, b.name, a.author_name, g.title, count(fb.book_id) as Number_read + FROM fav_books fb + LEFT JOIN books b ON fb.book_id = b.id + RIGHT JOIN book_author ab ON ab.book_id = fb.book_id + RIGHT JOIN author a ON a.id = ab.author_id + RIGHT JOIN genre_book gb ON gb.book_id = fb.book_id + RIGHT JOIN genre g ON g.genre_id = gb.genre_id + GROUP BY fb.book_id, b.name, a.author_name,g.title + ORDER BY Number_read DESC + LIMIT 10;''' + + cur.execute(command) + most_read = cur.fetchall() + return most_read + + if __name__ == '__main__': connect() diff --git a/main.py b/main.py index 9eb5653..fcac438 100644 --- a/main.py +++ b/main.py @@ -156,10 +156,30 @@ def Statistics(): table.add_row(names[i],str(*j[0])) console.print(table) - + except (ValueError,AttributeError, psycopg2.DatabaseError) as e: typer.echo(f'Sign in failed!', e) + +@app.command("most_favorite_books") +def most_read_books(genre : Optional[str]= typer.Argument("")): + """This command shows the user their most favorite books and how many times they read the book""" + books = Most_favorite_books(genre) + table = Table(show_header=True, header_style="bold blue") + + table.add_column("#", style="dim", min_width=10, justify=True) + table.add_column("Book ID", style="dim", min_width=10, justify=True) + table.add_column("Book Name", style="dim", min_width=10, justify=True) + table.add_column("Author", style="dim", min_width=10, justify=True) + table.add_column("Genre", style="dim", min_width=10, justify=True) + table.add_column("Times read", style="dim", min_width=10, justify=True) + + for i,book in enumerate(books): + table.add_row(str(i+1),str(book[0]),book[1], book[2], book[3], str(book[4])) + + console.print(table) + + # Example function for tables, you can add more columns/row From 7a27b91fdd57348ecf9ec9f0f6dcd6098ff4cd54 Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Wed, 5 Apr 2023 22:30:07 +0200 Subject: [PATCH 21/27] most read genres --- database.py | 18 ++++++++++++++++++ main.py | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/database.py b/database.py index be6425f..4b8eada 100644 --- a/database.py +++ b/database.py @@ -650,6 +650,24 @@ def Most_favorite_books(GENRE : str): cur.execute(command) most_read = cur.fetchall() return most_read + +def mostReadGenres(): + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + + command = f'''SELECT g.title, count(rb.book_id) FROM genre g + LEFT JOIN genre_book gb ON gb.genre_id = g.genre_id + LEFT JOIN read_books rb ON rb.book_id = gb.book_id + GROUP BY g.title + LIMIT 5;''' + + cur.execute(command) + most_genres = cur.fetchall() + + return most_genres + diff --git a/main.py b/main.py index fcac438..40c903a 100644 --- a/main.py +++ b/main.py @@ -179,6 +179,24 @@ def most_read_books(genre : Optional[str]= typer.Argument("")): console.print(table) +@app.command("most_read_genres") +def most_read_genres(): + """This command shows the user their most read genres and how many times they read them""" + books = mostReadGenres() + + table = Table(show_header=True, header_style="bold blue") + + table.add_column("#", style="dim", min_width=10, justify=True) + table.add_column("Genre", style="dim", min_width=10, justify=True) + table.add_column("Count", style="dim", min_width=10, justify=True) + + for i,book in enumerate(books): + table.add_row(str(i+1),book[0],str(book[1])) + + console.print(table) + + + From e44a31121b5b62e72f2887b49d79a9571c2b0d04 Mon Sep 17 00:00:00 2001 From: Mohammad_Falah Date: Wed, 5 Apr 2023 22:31:32 +0200 Subject: [PATCH 22/27] most_author --- database.py | 23 +++++++++++++++++++++++ main.py | 15 +++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/database.py b/database.py index be6425f..8ecd107 100644 --- a/database.py +++ b/database.py @@ -652,6 +652,29 @@ def Most_favorite_books(GENRE : str): return most_read +def Most_read_author(): + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + command = f''' + SELECT author.author_name, count(rb.book_id) FROM author + LEFT JOIN book_author ON author.id = book_author.author_id + LEFT JOIN read_books rb ON rb.book_id = book_author.book_id + GROUP BY author_name + LIMIT 3; ''' + + cur.execute(command) + most_read = cur.fetchall() + return most_read + + + + + + + + if __name__ == '__main__': connect() diff --git a/main.py b/main.py index fcac438..f31e13a 100644 --- a/main.py +++ b/main.py @@ -179,6 +179,21 @@ def most_read_books(genre : Optional[str]= typer.Argument("")): console.print(table) +@app.command("most_read_author") +def most_read_author(): + """This command shows the user their most favorite books and how many times they read the book""" + books = Most_read_author() + table = Table(show_header=True, header_style="bold blue") + + table.add_column("#", style="dim", min_width=10, justify=True) + table.add_column("Author", style="dim", min_width=10, justify=True) + table.add_column("Count", style="dim", min_width=10, justify=True) + + for i,book in enumerate(books): + table.add_row(str(i+1),book[0],str(book[1])) + + console.print(table) + From cbf6f5bb299e8ff4995240b62d7b0f408c8f94ab Mon Sep 17 00:00:00 2001 From: yunusemre000 Date: Fri, 7 Apr 2023 00:19:04 +0200 Subject: [PATCH 23/27] readme readme --- README.md | 49 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b65519c..532bac1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,47 @@ -# Sample Library CLI App +# Library CLI App -This repository can be used as a starting point to the project. +## Description + +This is an library management system.This Library CLI app has all the features of a Library Management System . + +## CLI Application + +The primary goal of CLI is to allow the developer to create commands quickly and with very little compile or runtime configuration. + + + + + + +## Features + +- Sign Up & Sign in +- Search by name +- Search by author +- Recently added +- Most Read Books +- Most Favorite Books +- Most Read Genres +- Most Read Authors +- Add Book +- Borrow Book +- Return Book +- Statistic + + +## General Requirements + +- PostgreSQL +- Python +- Typer Library for Python + + +## Credits + +- [@abdullahalzaghir](https://github.com/abdullahalzaghir) +- [@Mohammadfalah2400](https://github.com/Mohammadfalah2400) + +- [@gmaknl21](https://github.com/gmaknl21) +- [@Burhan-Data](https://github.com/Burhan-Data) +- [@yunusemre000](https://github.com/yunusemre000) -- You need to add `@app.command()` to the beginning of your function to add new commands. -- There is an example `display_table()` function showing how you can create tables. From d56b79b9afb9dd8fec96ef39f6ff2cd6d7fad018 Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Fri, 7 Apr 2023 09:43:30 +0200 Subject: [PATCH 24/27] updated --- database.py | 387 ++++++++++++++++++++++++++++------------------------ main.py | 7 +- 2 files changed, 216 insertions(+), 178 deletions(-) diff --git a/database.py b/database.py index cd11b6d..c1e9810 100644 --- a/database.py +++ b/database.py @@ -33,6 +33,7 @@ def connect(): typer.echo(f"The CREATE DATABASE statement failed: {e}") else: + # Create tables after the CREATE DATABASE statement params = config('database.ini','CLI_Library') conn = psycopg2.connect(**params) conn.autocommit = True @@ -55,19 +56,14 @@ def connect(): else: + # If the database exists and tables don't exist, then create tables conn.autocommit = True - curr = conn.cursor() - + curr = conn.cursor() curr.execute("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'") table_names = curr.fetchall() if len(table_names) != 0: - typer.echo("Database tables are already created!") - typer.echo("Existing tables:") - for table in table_names: - curr.execute(f'SELECT * FROM {table[0]};') - typer.echo(table) - typer.echo(curr.fetchall()) + pass else: file = open('cli_library.sql', 'r') @@ -81,111 +77,102 @@ def connect(): except (Exception, psycopg2.DatabaseError) as error: print("Command skipped: ", error) break - - # curr.execute("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'") - # table_names = curr.fetchall() - # for table in table_names: - # # cur.execute(f'SELECT * FROM {table[0]};') - # print(table) - # print(cur.fetchall()) - - # print(len(curr.fetchall()), ' tables created.') - # cur.execute('SELECT * FROM students;') - # print(cur.fetchall()) - # cur.execute('SELECT * from teachers;') - # print(cur.fetchall()) - # curr.close() except (Exception, psycopg2.DatabaseError) as error: + # If an error occurred, print the error print(error) - - finally: + # Close database connection if conn is not None: conn.close() print('Database connection closed.') -def connect_to_db(): - conn = None - try: - try: - # try to connect to database - params = config('database.ini','CLI_Library') - conn = psycopg2.connect(**params) - - # create a cursor - cur = conn.cursor() - except (Exception, psycopg2.DatabaseError) as error: - # if database doesn't exist, create it - print(error) - - - except (Exception, psycopg2.DatabaseError) as error: - print(error) +from psycopg2.errors import UniqueViolation +from psycopg2 import errors def singUp(username: str, password: int): + ''' This function is called to sign up the user and store their information in the database''' + # Connect to database params = config('database.ini','CLI_Library') conn = psycopg2.connect(**params) conn.autocommit = True cur = conn.cursor() cur.execute('SELECT username FROM public."user";') - user = cur.fetchall() - for i in user: - if username == i[0]: - typer.secho(f"This user already exist! Try different user", fg=typer.colors.RED) - break - else: - command = f'INSERT INTO "user" (username, password) VALUES (\'{username}\',\'{password}\');' - cur.execute(command) - cur.close() + # Check if the user is already in the database, else insert their information into the database + user = list(*zip(*cur.fetchall())) + while True: + try: + if username in user: + typer.secho(f"This user already exist! Try different user", fg=typer.colors.RED) + username = input("Please Enter another username: ") + continue + else: + command = f'INSERT INTO "user" (username, password) VALUES (\'{username}\',\'{password}\');' + cur.execute(command) + cur.close() + break + + except UniqueViolation as e: + print("hi", e) + username = input("Enter another username: ") + continue + def signIn(username: str, password: int): + '''This function is called to sign in the user and store their information accordingly''' try: + # Connect to the database params = config('database.ini','CLI_Library') conn = psycopg2.connect(**params) conn.autocommit = True cur = conn.cursor() + + # Collect usernames and passowrd of user cur.execute('SELECT username FROM public."user";') user = cur.fetchall() cur.execute('SELECT password FROM public."user";') user_pass = cur.fetchall() + # if username or password entered correctly, sign the user in. else, show the error message for i,j in zip(user, user_pass): if username == i[0] and password == j[0]: typer.secho(f"You are signed in", fg=typer.colors.GREEN) + # return True if sign in was successful success = True break else: typer.secho(f"username or password is wrong!", fg=typer.colors.RED) + # return false if sign in was unsuccessful success = False return success except ValueError: print('Please Enter valid information') -def addBook(bookname: str, author: str, pages: int, genre: str): +def addBook(bookname: str, author: str, pages: int, genre: str): + '''This function is called to add a book into the database''' try: try: + # connect to the database params = config('database.ini','CLI_Library') conn = psycopg2.connect(**params) conn.autocommit = True cur = conn.cursor() + # Select all books and authors names command_query = f''' SELECT name FROM books; SELECT author_name FROM author;'''.split('\n') + # store all books and authors into a list queried_data = [] for i in command_query: cur.execute(i) queried_data.append(cur.fetchall()) - - - - + # check of book and author names in the database, if exists add to the inventory without author namd and book name. else add them to database for i,j in zip(*queried_data): if i[0] == bookname and j[0] == author: cur.execute(f'SELECT id FROM books WHERE name = \'{bookname}\'') @@ -206,6 +193,7 @@ def addBook(bookname: str, author: str, pages: int, genre: str): INSERT INTO "inventory" (book_id, last_update) VALUES ((SELECT id FROM "books" WHERE name = \'{bookname}'), \'{datetime.date.today()}\');''' cur.execute(commandd) + # check if genre is already exists, else add it to avoid duplicate command = f'SELECT title FROM genre;' cur.execute(command) genre_title = cur.fetchall() @@ -232,22 +220,23 @@ def addBook(bookname: str, author: str, pages: int, genre: str): def borrowBook(id: int): - + '''This function is called to borrow a book from the inventory to the borrowed book table''' + # Takes the user's username and passwrod username = input("Username: ") password = int(input("Password: ")) + # sign in the user first to borrow book if signIn(username, password): - + # connect to database params = config('database.ini','CLI_Library') conn = psycopg2.connect(**params) conn.autocommit = True cur = conn.cursor() - - + # Select all books from inventory cur.execute(f'SELECT book_id FROM inventory;') available_books = cur.fetchall() - # cur.execute(f'SELECT date FROM inventory WHERE book_id = 1 FETCH ONLY + # check of book exist to add it to the borrowed books table, else show that the table doesn't exist for book in available_books: if book[0] == id: print(book[0]) @@ -261,32 +250,14 @@ def borrowBook(id: int): else: typer.secho('This book is not available') - # available_books = cur.fetchall() - - - - - - # books = [] - # for bb in bb_books: - # cur.execute(f'SELECT book_id FROM inventory WHERE inventory_id = \'{bb[0]}\';') - # a = cur.fetchone() - # books.append(a) - - # for book in books: - # if book[0] == id: - # cur.execute(f'INSERT INTO borrowed') - - # available_books = cur.fetchall() - - # for book in available_books: - # if book[0] == id: - # command = f'''DELETE FROM inventory WHERE book_id = \'{id}\');''' + def returnBook(id: int): + '''This function is called to return a book to the inventory by the user''' username = input("Username: ") password = int(input("Password: ")) + # sign in the user first to allow them to return the book if signIn(username, password): params = config('database.ini','CLI_Library') conn = psycopg2.connect(**params) @@ -296,6 +267,7 @@ def returnBook(id: int): cur.execute(f'SELECT book_id FROM borrowed_books WHERE username = \'{username}\';') borrowed_books = cur.fetchall() + # check if the user has borrowed the book, or add them back to the inventory for book in borrowed_books: if book[0] == id: command = f'''DELETE FROM borrowed_books WHERE bb_id IN (SELECT bb_id FROM borrowed_books WHERE book_id = {id} limit 1); @@ -311,12 +283,14 @@ def returnBook(id: int): def markRead(id: int): - + ''' this function is called to mark a book as read''' try: username = input("Username: ") password = int(input("Password: ")) + # sign in the user first to allow them to mark book as read if signIn(username, password): + # connect to database params = config('database.ini','CLI_Library') conn = psycopg2.connect(**params) conn.autocommit = True @@ -325,6 +299,7 @@ def markRead(id: int): cur.execute(f'SELECT id FROM books;') books = cur.fetchall() + # check if the book exists in database and add it to the mark read table, if not display that the book doesn't exist for book in books: if book[0] == id: command = f'INSERT INTO read_books (username, book_id) VALUES (\'{username}\', \'{id}\');' @@ -341,10 +316,13 @@ def markRead(id: int): def favBook(id: int): + '''this function is called to add the book to the favorite book table''' username = input("Username: ") password = int(input("Password: ")) + # sign in the user first to allow them to add a book to their favorites if signIn(username, password): + # connect to the database params = config('database.ini','CLI_Library') conn = psycopg2.connect(**params) conn.autocommit = True @@ -363,6 +341,7 @@ def favBook(id: int): fuser.append(j) fbook.append(i) + # check if the book is already in the favorite list and if it exists, else add it to user's favorite books list if username in fuser and id in fbook: typer.secho(f'Book {id} is already in your favorite list!', fg= typer.colors.RED) else: @@ -374,8 +353,6 @@ def favBook(id: int): break else: typer.echo(f'Book {id} doesn\'t exist!') - - else: typer.echo(f"Could not sign in") @@ -383,46 +360,54 @@ def favBook(id: int): def readBooks(user: str): - + '''this function reads a list of books that the user has read''' + # connect to the database params = config('database.ini','CLI_Library') conn = psycopg2.connect(**params) conn.autocommit = True cur = conn.cursor() - command = f'''SELECT DISTINCT mr.book_id, b.name, a.author_name, b.pages, g.title, count(inv.book_id) AS Availability - FROM read_books mr - INNER JOIN books b ON b.id = mr.book_id - RIGHT JOIN book_author ab on ab.book_id = b.id - LEFT JOIN author a ON a.id = ab.author_id - right JOIN genre_book gb on gb.book_id = b.id - left join genre g on g.genre_id = gb.genre_id - INNER JOIN inventory inv on inv.book_id = b.id - WHERE mr.username = '{user}' - GROUP BY mr.book_id,b.name,a.author_name,b.pages, g.title;''' - + # fetch all the books the user has read and return them in a list + command = f'''SELECT DISTINCT mr.book_id, b.name, a.author_name, b.pages, g.title, + CASE + WHEN inv.book_id IS NULL THEN 'False' + ELSE 'True' + END AS availability + FROM read_books mr + JOIN books b ON b.id = mr.book_id + JOIN book_author ab on ab.book_id = b.id + JOIN author a ON a.id = ab.author_id + JOIN genre_book gb on gb.book_id = b.id + join genre g on g.genre_id = gb.genre_id + LEFT JOIN inventory inv ON inv.book_id = mr.book_id + WHERE mr.username = '{user}';''' cur.execute(command) read_books = cur.fetchall() - - + return read_books def favoriteBooks(user: str): - + '''This function is called to read the favorite books the user has in their list''' + # connect to database params = config('database.ini','CLI_Library') conn = psycopg2.connect(**params) conn.autocommit = True cur = conn.cursor() - command2 = f'''SELECT DISTINCT fav.book_id, b.name, a.author_name, b.pages, g.title, count(inv.book_id) AS Availability - FROM fav_books fav - INNER JOIN books b ON b.id = fav.book_id - RIGHT JOIN book_author ab on ab.book_id = b.id - LEFT JOIN author a ON a.id = ab.author_id - right JOIN genre_book gb on gb.book_id = b.id - left join genre g on g.genre_id = gb.genre_id - INNER JOIN inventory inv on inv.book_id = b.id - WHERE fav.username = '{user}' - GROUP BY fav.book_id,b.name,a.author_name,b.pages, g.title''' + # fetch all the books the user has in their favorite list and return them in a list + command2 = f'''SELECT DISTINCT fav.book_id, b.name, a.author_name, b.pages, g.title, + CASE + WHEN inv.book_id IS NULL THEN 'False' + ELSE 'True' + END AS availability + FROM fav_books fav + INNER JOIN books b ON b.id = fav.book_id + RIGHT JOIN book_author ab on ab.book_id = b.id + LEFT JOIN author a ON a.id = ab.author_id + right JOIN genre_book gb on gb.book_id = b.id + left join genre g on g.genre_id = gb.genre_id + LEFT JOIN inventory inv on inv.book_id = fav.book_id + WHERE fav.username = '{user}';''' cur.execute(command2) fav_books = cur.fetchall() @@ -430,11 +415,14 @@ def favoriteBooks(user: str): return fav_books def statestics(user: str): + '''This function is called to show a statistics table of the user's books readings''' + # connect to the database params = config('database.ini','CLI_Library') conn = psycopg2.connect(**params) conn.autocommit = True cur = conn.cursor() + # Fetch all the required information and return them as a list command = f'''SELECT COUNT(*) FROM read_books WHERE username = '{user}';, SELECT COUNT(ba.author_id) FROM book_author ba INNER JOIN read_books rb on rb.book_id = ba.book_id @@ -455,18 +443,22 @@ def statestics(user: str): def Search_by_name(name : str ) : + '''This function is called to allow the user to search for a book by name''' try : + # connect to database params = config('database.ini','CLI_Library') conn = psycopg2.connect(**params) conn.autocommit = True cur = conn.cursor() - + + # fetch all information of a book and display them in a table cur.execute(f"""SELECT books.id,name,author_name ,pages ,genre.title FROM books JOIN book_author on books.id = book_author.book_id JOIN author ON book_author.author_id = author.id JOIN genre ON books.id = genre.genre_id WHERE name= \'{name}\' order by name,pages ; """) book_names = cur.fetchall() + # check if the book exists in the inventory, else display the book as not available for i in book_names: if i[1] == name : table = Table(show_header=True, header_style="bold blue") @@ -495,16 +487,12 @@ def Search_by_name(name : str ) : table.add_column("Availabili__", style="dim", min_width=10, justify=True) table.add_row("--", "--","--","--","--","--","False") console.print(table) - - - - - - def Search_by_author(author : str) : + '''This function is called to allow the user to search for a book by author''' try : + # connect to database params = config('database.ini','CLI_Library') conn = psycopg2.connect(**params) conn.autocommit = True @@ -515,6 +503,7 @@ def Search_by_author(author : str) : JOIN genre ON books.id = genre.genre_id where author_name =\'{author}\' order by name,pages ;""") aurthor_name = cur.fetchmany() + # check if the book exists in the inventory, else display the book as not available for i in aurthor_name: if i[2] == author : table = Table(show_header=True, header_style="bold blue") @@ -546,49 +535,92 @@ def Search_by_author(author : str) : -def Recently_added(): +def Recently_added(GENRE : str): - params = config('database.ini','CLI_Library') - conn = psycopg2.connect(**params) - conn.autocommit = True - cur = conn.cursor() - cur.execute("""SELECT books.id,name ,author_name ,pages ,genre.title FROM books - JOIN book_author ON books.id = book_author.book_id + params = config('database.ini','CLI_Library') + conn = psycopg2.connect(**params) + conn.autocommit = True + cur = conn.cursor() + + if GENRE: + cur.execute(f'''SELECT b.id , b.name ,author_name ,pages ,g.title, + CASE + WHEN inv.book_id IS NULL THEN 'False' + ELSE 'True' + END AS availability + FROM books b + JOIN book_author ON b.id = book_author.book_id JOIN author ON book_author.author_id = author.id - JOIN genre ON books.id = genre.genre_id - ORDER BY id DESC LIMIT 5 ;""") - books = cur.fetchall() - x = len(books) - i = 0 - my_result = [i for i in books] - ii = 0 - table = Table(show_header=True, header_style="bold blue") - table.add_column("#", style="dim", width=10) - table.add_column("BookID", style="dim", min_width=10, justify=True) - table.add_column("Name", style="dim", min_width=10, justify=True) - table.add_column("Author", style="dim", min_width=10, justify=True) - table.add_column("Pages", style="dim", min_width=10, justify=True) - table.add_column("Gener", style="dim", min_width=10, justify=True) - table.add_column("Availabili__", style="dim", min_width=10, justify=True) - while ii < x : - table.add_row(f"{my_result[ii][0]}", f"{my_result[ii][0]}",f"{my_result[ii][1]}",f"{my_result[ii][2]}",f"{my_result[ii][3]}",f"{my_result[ii][4]}","True") - ii += 1 - console.print(table) + JOIN genre g ON b.id = g.genre_id + LEFT JOIN inventory inv ON inv.book_id = b.id + WHERE inv.last_update = cast('07-04-2023' AS DATE) AND g.title = '{GENRE}' + ORDER BY id DESC LIMIT 5;''') + books = cur.fetchall() + x = len(books) + i = 0 + my_result = [i for i in books] + ii = 0 + table = Table(show_header=True, header_style="bold blue") + table.add_column("#", style="dim", width=10) + table.add_column("BookID", style="dim", min_width=10, justify=True) + table.add_column("Name", style="dim", min_width=10, justify=True) + table.add_column("Author", style="dim", min_width=10, justify=True) + table.add_column("Pages", style="dim", min_width=10, justify=True) + table.add_column("Gener", style="dim", min_width=10, justify=True) + table.add_column("Availabili__", style="dim", min_width=10, justify=True) + while ii < x : + table.add_row(f"{my_result[ii][0]}", f"{my_result[ii][0]}",f"{my_result[ii][1]}",f"{my_result[ii][2]}",f"{my_result[ii][3]}",f"{my_result[ii][4]}",f"{my_result[ii][5]}") + ii += 1 + console.print(table) + + else: + cur.execute("""SELECT b.id , b.name ,author_name ,pages ,g.title, + CASE + WHEN inv.book_id IS NULL THEN 'False' + ELSE 'True' + END AS availability + FROM books b + JOIN book_author ON b.id = book_author.book_id + JOIN author ON book_author.author_id = author.id + JOIN genre g ON b.id = g.genre_id + LEFT JOIN inventory inv ON inv.book_id = b.id + WHERE inv.last_update = cast('07-04-2023' AS DATE) + ORDER BY id DESC LIMIT 5;""") + books = cur.fetchall() + x = len(books) + i = 0 + my_result = [i for i in books] + ii = 0 + table = Table(show_header=True, header_style="bold blue") + table.add_column("#", style="dim", width=10) + table.add_column("BookID", style="dim", min_width=10, justify=True) + table.add_column("Name", style="dim", min_width=10, justify=True) + table.add_column("Author", style="dim", min_width=10, justify=True) + table.add_column("Pages", style="dim", min_width=10, justify=True) + table.add_column("Gener", style="dim", min_width=10, justify=True) + table.add_column("Availabili__", style="dim", min_width=10, justify=True) + while ii < x : + table.add_row(f"{my_result[ii][0]}", f"{my_result[ii][0]}",f"{my_result[ii][1]}",f"{my_result[ii][2]}",f"{my_result[ii][3]}",f"{my_result[ii][4]}",f"{my_result[ii][5]}") + ii += 1 + console.print(table) def mostReadBooks(GENRE : str): + '''This function is called to show the most read books given the genre as an optional argument''' + # connect to the database params = config('database.ini','CLI_Library') conn = psycopg2.connect(**params) conn.autocommit = True cur = conn.cursor() + # if genre argument was given then show most read books according to the genre given, else return most read books if GENRE: command = f'''SELECT rb.book_id, b.name, a.author_name, g.title, count(rb.book_id) as Number_read FROM read_books rb - LEFT JOIN books b ON rb.book_id = b.id - RIGHT JOIN book_author ab ON ab.book_id = rb.book_id - RIGHT JOIN author a ON a.id = ab.author_id - RIGHT JOIN genre_book gb ON gb.book_id = rb.book_id - RIGHT JOIN genre g ON g.genre_id = gb.genre_id + JOIN books b ON rb.book_id = b.id + JOIN book_author ab ON ab.book_id = rb.book_id + JOIN author a ON a.id = ab.author_id + JOIN genre_book gb ON gb.book_id = rb.book_id + JOIN genre g ON g.genre_id = gb.genre_id WHERE g.title = '{GENRE}' GROUP BY rb.book_id, b.name, a.author_name,g.title ORDER BY Number_read DESC @@ -600,11 +632,11 @@ def mostReadBooks(GENRE : str): else: command = f'''SELECT rb.book_id, b.name, a.author_name, g.title, count(rb.book_id) as Number_read FROM read_books rb - LEFT JOIN books b ON rb.book_id = b.id - RIGHT JOIN book_author ab ON ab.book_id = rb.book_id - RIGHT JOIN author a ON a.id = ab.author_id - RIGHT JOIN genre_book gb ON gb.book_id = rb.book_id - RIGHT JOIN genre g ON g.genre_id = gb.genre_id + JOIN books b ON rb.book_id = b.id + JOIN book_author ab ON ab.book_id = rb.book_id + JOIN author a ON a.id = ab.author_id + JOIN genre_book gb ON gb.book_id = rb.book_id + JOIN genre g ON g.genre_id = gb.genre_id GROUP BY rb.book_id, b.name, a.author_name,g.title ORDER BY Number_read DESC LIMIT 10;''' @@ -614,53 +646,60 @@ def mostReadBooks(GENRE : str): return most_read def Most_favorite_books(GENRE : str): + '''This function is called to show the most favorite books''' + # connect to database params = config('database.ini','CLI_Library') conn = psycopg2.connect(**params) conn.autocommit = True cur = conn.cursor() + # if genre argument was given then show most favorite books according to the genre given, else return most favorite books if GENRE: command = f'''SELECT fb.book_id, b.name, a.author_name, g.title, count(fb.book_id) as Number_read FROM fav_books fb - LEFT JOIN books b ON fb.book_id = b.id - RIGHT JOIN book_author ab ON ab.book_id = fb.book_id - RIGHT JOIN author a ON a.id = ab.author_id - RIGHT JOIN genre_book gb ON gb.book_id = fb.book_id - RIGHT JOIN genre g ON g.genre_id = gb.genre_id + JOIN books b ON fb.book_id = b.id + JOIN book_author ab ON ab.book_id = fb.book_id + JOIN author a ON a.id = ab.author_id + JOIN genre_book gb ON gb.book_id = fb.book_id + JOIN genre g ON g.genre_id = gb.genre_id WHERE g.title = '{GENRE}' GROUP BY fb.book_id, b.name, a.author_name,g.title ORDER BY Number_read DESC LIMIT 10;''' cur.execute(command) - most_read = cur.fetchall() - return most_read + most_fav = cur.fetchall() + return most_fav else: command = f'''SELECT fb.book_id, b.name, a.author_name, g.title, count(fb.book_id) as Number_read FROM fav_books fb - LEFT JOIN books b ON fb.book_id = b.id - RIGHT JOIN book_author ab ON ab.book_id = fb.book_id - RIGHT JOIN author a ON a.id = ab.author_id - RIGHT JOIN genre_book gb ON gb.book_id = fb.book_id - RIGHT JOIN genre g ON g.genre_id = gb.genre_id + JOIN books b ON fb.book_id = b.id + JOIN book_author ab ON ab.book_id = fb.book_id + JOIN author a ON a.id = ab.author_id + JOIN genre_book gb ON gb.book_id = fb.book_id + JOIN genre g ON g.genre_id = gb.genre_id GROUP BY fb.book_id, b.name, a.author_name,g.title ORDER BY Number_read DESC LIMIT 10;''' cur.execute(command) - most_read = cur.fetchall() - return most_read + most_fav = cur.fetchall() + return most_fav def mostReadGenres(): + '''This function is called to show the most read genres''' + # connect to the database params = config('database.ini','CLI_Library') conn = psycopg2.connect(**params) conn.autocommit = True cur = conn.cursor() - command = f'''SELECT g.title, count(rb.book_id) FROM genre g + # fetch most read genres from database and retrun them as a list + command = f'''SELECT g.title, count(rb.book_id) AS coun FROM genre g LEFT JOIN genre_book gb ON gb.genre_id = g.genre_id LEFT JOIN read_books rb ON rb.book_id = gb.book_id GROUP BY g.title + ORDER BY coun DESC LIMIT 5;''' cur.execute(command) @@ -671,15 +710,20 @@ def mostReadGenres(): def Most_read_author(): + '''This function is called to return the most read authors''' + # connect to the databse params = config('database.ini','CLI_Library') conn = psycopg2.connect(**params) conn.autocommit = True cur = conn.cursor() + + # fetch all most read authors from the database and return them as a list command = f''' - SELECT author.author_name, count(rb.book_id) FROM author + SELECT author.author_name, count(rb.book_id) AS coun FROM author LEFT JOIN book_author ON author.id = book_author.author_id LEFT JOIN read_books rb ON rb.book_id = book_author.book_id GROUP BY author_name + ORDER BY coun DESC LIMIT 3; ''' cur.execute(command) @@ -687,13 +731,6 @@ def Most_read_author(): return most_read - - - - - - - if __name__ == '__main__': connect() \ No newline at end of file diff --git a/main.py b/main.py index 9133ed9..7753481 100644 --- a/main.py +++ b/main.py @@ -79,6 +79,7 @@ def fav_book(id: int): def my_books(): """This command gives a summary of the books the user has read and their book favorite list""" try: + # sign in the user first to show the books they have read and their book favorite list typer.secho(f"Sign in first to show books", fg=typer.colors.GREEN) username = input("Username: ") password = int(input("Password: ")) @@ -108,10 +109,10 @@ def search_by_author(author : str): Search_by_author(author) @app.command("recently_added") -def recently_added(): +def recently_added(genre : Optional[str]= typer.Argument("")): """This command shows the user the books that were recently added""" typer.echo(f"lets see which book recently added") - Recently_added() + Recently_added(genre) @app.command("most_read_books") @@ -217,7 +218,7 @@ def most_read_genres(): -# Example function for tables, you can add more columns/row +# function for tables, you can add more columns/row def display_table(books): table = Table(show_header=True, header_style="bold blue") From 9e804edeb2873ce28ed0620216d4724c75e83dca Mon Sep 17 00:00:00 2001 From: yunusemre000 <108811520+yunusemre000@users.noreply.github.com> Date: Fri, 7 Apr 2023 12:36:18 +0200 Subject: [PATCH 25/27] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 532bac1..0d3e9cd 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This is an library management system.This Library CLI app has all the features o The primary goal of CLI is to allow the developer to create commands quickly and with very little compile or runtime configuration. - +![Screenshot 2022-11-27 at 14 03 19](https://user-images.githubusercontent.com/108811520/230594419-a0a90fba-8949-436b-9ccf-c798dc762c90.png) @@ -29,6 +29,7 @@ The primary goal of CLI is to allow the developer to create commands quickly and - Statistic + ## General Requirements - PostgreSQL From 302b03a252656d1f5b3db7f4eb4b96375389e87b Mon Sep 17 00:00:00 2001 From: yunusemre000 <108811520+yunusemre000@users.noreply.github.com> Date: Fri, 7 Apr 2023 12:53:37 +0200 Subject: [PATCH 26/27] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0d3e9cd..545d89c 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ The primary goal of CLI is to allow the developer to create commands quickly and ## Credits - +Mentor: [@iremugurlu](https://github.com/iremugurlu) - [@abdullahalzaghir](https://github.com/abdullahalzaghir) - [@Mohammadfalah2400](https://github.com/Mohammadfalah2400) From b536a1fcd319f5b7bceb04b4b2486f8c3f2756d4 Mon Sep 17 00:00:00 2001 From: abdullahalzaghir <121679105+abdullahalzaghir@users.noreply.github.com> Date: Thu, 13 Apr 2023 07:42:17 +0200 Subject: [PATCH 27/27] update --- database.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/database.py b/database.py index c1e9810..22cddbb 100644 --- a/database.py +++ b/database.py @@ -201,9 +201,8 @@ def addBook(bookname: str, author: str, pages: int, genre: str): for title in genre_title: if title[0] == genre: - pass - # command = f'INSERT INTO "genre_book" (book_id, genre_id) VALUES ((SELECT id FROM "books" WHERE name = \'{bookname}\'), (SELECT genre_id FROM "genre" WHERE title = \'{genre}\'));' - # cur.execute(command) + command = f'INSERT INTO "genre_book" (book_id, genre_id) VALUES ((SELECT id FROM "books" WHERE name = \'{bookname}\'), (SELECT genre_id FROM "genre" WHERE title = \'{genre}\'));' + cur.execute(command) break else: