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/README.md b/README.md index b65519c..545d89c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,48 @@ -# 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. + +![Screenshot 2022-11-27 at 14 03 19](https://user-images.githubusercontent.com/108811520/230594419-a0a90fba-8949-436b-9ccf-c798dc762c90.png) + + + + +## 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 +Mentor: [@iremugurlu](https://github.com/iremugurlu) +- [@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. diff --git a/cli_library.sql b/cli_library.sql new file mode 100644 index 0000000..65a30f3 --- /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" varchar(255) 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, + "book_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, + "book_id" bigint 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 ("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"); + +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 ("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'); + + + + + + + + + + + + + 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.py b/database.py new file mode 100644 index 0000000..22cddbb --- /dev/null +++ b/database.py @@ -0,0 +1,735 @@ +import psycopg2 +import typer +from rich.console import Console +from rich.table import Table +import datetime + +from config import config + +console = Console() +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: + # Create tables after the CREATE DATABASE statement + 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: + # If the database exists and tables don't exist, then create tables + 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: + pass + + 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 + + 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.') + +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";') + + # 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): + '''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}\'') + 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: + 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) + + # 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() + + + 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}\');') + 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): + '''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() + # 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]) + 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: + cur.execute(i) + typer.secho(f'You borrowed book {id}!') + break + else: + typer.secho('This book is not available') + + + +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) + conn.autocommit = True + cur = conn.cursor() + + 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); + 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): + ''' 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 + cur = conn.cursor() + + 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}\');' + 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): + '''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 + cur = conn.cursor() + + cur.execute(f'SELECT id FROM books;') + books = cur.fetchall() + + 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) + + # 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: + 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): + '''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() + + # 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() + + # 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() + + 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 + 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 ) : + '''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") + 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) : + '''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 + 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() + # 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") + 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(GENRE : str): + + 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 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 + 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 + 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 + 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;''' + + cur.execute(command) + most_read = cur.fetchall() + 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 + 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_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 + 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_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() + + # 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) + most_genres = cur.fetchall() + + return most_genres + + + +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) 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) + most_read = cur.fetchall() + return most_read + + +if __name__ == '__main__': + connect() + \ No newline at end of file diff --git a/main.py b/main.py index 0219f98..7753481 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 * console = Console() @@ -9,27 +10,231 @@ @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) - # 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") -def sign_up(username: str): +def sign_up(username: str, password: int): + """This command allows the user to sign up""" 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("add_book") +def add_book(): + """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: ") + 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) + +@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: + # 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: ")) + + 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) + +@app.command("search_by_name") +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) -# Example function for tables, you can add more columns/row. -@app.command("display_table") -def display_table(): +@app.command("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(genre) + + +@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("Column 1", style="dim", width=10) - table.add_column("Column 2", style="dim", min_width=10, justify=True) + + 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])) - table.add_row('Value 1', 'Value 2') - table.add_row('Value 3', 'Value 4') - table.add_row('Value 5', 'Value 6') + 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: ") + 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) + +@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) + + +@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) + +@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) + + + + + + + +# function for tables, you can add more columns/row +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) + 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 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) if __name__ == "__main__":