From e8697ffceb69e88ef07d858f6531a51323250361 Mon Sep 17 00:00:00 2001 From: Jadowyne Ulve Date: Sun, 20 Oct 2024 10:03:14 -0500 Subject: [PATCH] Lots of work --- __pycache__/api.cpython-312.pyc | Bin 7649 -> 34087 bytes __pycache__/config.cpython-312.pyc | Bin 836 -> 2687 bytes __pycache__/main.cpython-312.pyc | Bin 23989 -> 24007 bytes __pycache__/webserver.cpython-312.pyc | Bin 936 -> 1878 bytes api.py | 453 ++++++++++++++++- config.py | 47 +- database.ini | 14 +- main.py | 21 +- manage.py | 4 + sites/default/sql/create/item.sql | 2 +- sites/default/sql/create/shopping_lists.sql | 7 +- sites/main/sql/create/item.sql | 1 + sites/main/sql/create/shopping_lists.sql | 7 +- sites/main/sql/unique/select_item_all.sql | 65 +-- sites/test/site.ini | 9 + sites/test/sql/create/brands.sql | 4 + sites/test/sql/create/food_info.sql | 7 + sites/test/sql/create/groups.sql | 8 + sites/test/sql/create/item.sql | 28 ++ sites/test/sql/create/item_info.sql | 15 + sites/test/sql/create/linked_items.sql | 8 + sites/test/sql/create/locations.sql | 11 + sites/test/sql/create/logins.sql | 16 + sites/test/sql/create/logistics_info.sql | 10 + sites/test/sql/create/receipt_items.sql | 9 + sites/test/sql/create/receipts.sql | 10 + sites/test/sql/create/recipes.sql | 12 + sites/test/sql/create/shopping_lists.sql | 14 + sites/test/sql/create/transactions.sql | 15 + sites/test/sql/create/vendors.sql | 8 + sites/test/sql/create/zones.sql | 5 + sites/test/sql/drop/brands.sql | 1 + sites/test/sql/drop/food_info.sql | 1 + sites/test/sql/drop/groups.sql | 1 + sites/test/sql/drop/item_info.sql | 1 + sites/test/sql/drop/items.sql | 1 + sites/test/sql/drop/linked_items.sql | 1 + sites/test/sql/drop/locations.sql | 1 + sites/test/sql/drop/logistics_info.sql | 1 + sites/test/sql/drop/receipt_items.sql | 1 + sites/test/sql/drop/receipts.sql | 1 + sites/test/sql/drop/recipes.sql | 1 + sites/test/sql/drop/shopping_lists.sql | 1 + sites/test/sql/drop/transactions.sql | 1 + sites/test/sql/drop/vendors.sql | 1 + sites/test/sql/drop/zones.sql | 1 + sites/test/sql/unique/select_item_all.sql | 45 ++ sites/test2/site.ini | 9 + sites/test2/sql/create/brands.sql | 4 + sites/test2/sql/create/food_info.sql | 7 + sites/test2/sql/create/groups.sql | 8 + sites/test2/sql/create/item.sql | 28 ++ sites/test2/sql/create/item_info.sql | 15 + sites/test2/sql/create/linked_items.sql | 8 + sites/test2/sql/create/locations.sql | 11 + sites/test2/sql/create/logins.sql | 16 + sites/test2/sql/create/logistics_info.sql | 10 + sites/test2/sql/create/receipt_items.sql | 9 + sites/test2/sql/create/receipts.sql | 10 + sites/test2/sql/create/recipes.sql | 12 + sites/test2/sql/create/shopping_lists.sql | 14 + sites/test2/sql/create/transactions.sql | 15 + sites/test2/sql/create/vendors.sql | 8 + sites/test2/sql/create/zones.sql | 5 + sites/test2/sql/drop/brands.sql | 1 + sites/test2/sql/drop/food_info.sql | 1 + sites/test2/sql/drop/groups.sql | 1 + sites/test2/sql/drop/item_info.sql | 1 + sites/test2/sql/drop/items.sql | 1 + sites/test2/sql/drop/linked_items.sql | 1 + sites/test2/sql/drop/locations.sql | 1 + sites/test2/sql/drop/logistics_info.sql | 1 + sites/test2/sql/drop/receipt_items.sql | 1 + sites/test2/sql/drop/receipts.sql | 1 + sites/test2/sql/drop/recipes.sql | 1 + sites/test2/sql/drop/shopping_lists.sql | 1 + sites/test2/sql/drop/transactions.sql | 1 + sites/test2/sql/drop/vendors.sql | 1 + sites/test2/sql/drop/zones.sql | 1 + sites/test2/sql/unique/select_item_all.sql | 45 ++ templates/groups/group.html | 352 +++++++++++++ templates/groups/index.html | 448 +++++++++++++++++ templates/item_page/index.html | 138 ------ templates/{home.html => items/index.html} | 206 ++++++-- templates/items/item.html | 233 +++++++++ templates/shopping-lists/edit.html | 522 ++++++++++++++++++++ templates/shopping-lists/index.html | 332 +++++++++++++ templates/shopping-lists/view.html | 173 +++++++ webserver.py | 42 +- 89 files changed, 3306 insertions(+), 259 deletions(-) create mode 100644 sites/test/site.ini create mode 100644 sites/test/sql/create/brands.sql create mode 100644 sites/test/sql/create/food_info.sql create mode 100644 sites/test/sql/create/groups.sql create mode 100644 sites/test/sql/create/item.sql create mode 100644 sites/test/sql/create/item_info.sql create mode 100644 sites/test/sql/create/linked_items.sql create mode 100644 sites/test/sql/create/locations.sql create mode 100644 sites/test/sql/create/logins.sql create mode 100644 sites/test/sql/create/logistics_info.sql create mode 100644 sites/test/sql/create/receipt_items.sql create mode 100644 sites/test/sql/create/receipts.sql create mode 100644 sites/test/sql/create/recipes.sql create mode 100644 sites/test/sql/create/shopping_lists.sql create mode 100644 sites/test/sql/create/transactions.sql create mode 100644 sites/test/sql/create/vendors.sql create mode 100644 sites/test/sql/create/zones.sql create mode 100644 sites/test/sql/drop/brands.sql create mode 100644 sites/test/sql/drop/food_info.sql create mode 100644 sites/test/sql/drop/groups.sql create mode 100644 sites/test/sql/drop/item_info.sql create mode 100644 sites/test/sql/drop/items.sql create mode 100644 sites/test/sql/drop/linked_items.sql create mode 100644 sites/test/sql/drop/locations.sql create mode 100644 sites/test/sql/drop/logistics_info.sql create mode 100644 sites/test/sql/drop/receipt_items.sql create mode 100644 sites/test/sql/drop/receipts.sql create mode 100644 sites/test/sql/drop/recipes.sql create mode 100644 sites/test/sql/drop/shopping_lists.sql create mode 100644 sites/test/sql/drop/transactions.sql create mode 100644 sites/test/sql/drop/vendors.sql create mode 100644 sites/test/sql/drop/zones.sql create mode 100644 sites/test/sql/unique/select_item_all.sql create mode 100644 sites/test2/site.ini create mode 100644 sites/test2/sql/create/brands.sql create mode 100644 sites/test2/sql/create/food_info.sql create mode 100644 sites/test2/sql/create/groups.sql create mode 100644 sites/test2/sql/create/item.sql create mode 100644 sites/test2/sql/create/item_info.sql create mode 100644 sites/test2/sql/create/linked_items.sql create mode 100644 sites/test2/sql/create/locations.sql create mode 100644 sites/test2/sql/create/logins.sql create mode 100644 sites/test2/sql/create/logistics_info.sql create mode 100644 sites/test2/sql/create/receipt_items.sql create mode 100644 sites/test2/sql/create/receipts.sql create mode 100644 sites/test2/sql/create/recipes.sql create mode 100644 sites/test2/sql/create/shopping_lists.sql create mode 100644 sites/test2/sql/create/transactions.sql create mode 100644 sites/test2/sql/create/vendors.sql create mode 100644 sites/test2/sql/create/zones.sql create mode 100644 sites/test2/sql/drop/brands.sql create mode 100644 sites/test2/sql/drop/food_info.sql create mode 100644 sites/test2/sql/drop/groups.sql create mode 100644 sites/test2/sql/drop/item_info.sql create mode 100644 sites/test2/sql/drop/items.sql create mode 100644 sites/test2/sql/drop/linked_items.sql create mode 100644 sites/test2/sql/drop/locations.sql create mode 100644 sites/test2/sql/drop/logistics_info.sql create mode 100644 sites/test2/sql/drop/receipt_items.sql create mode 100644 sites/test2/sql/drop/receipts.sql create mode 100644 sites/test2/sql/drop/recipes.sql create mode 100644 sites/test2/sql/drop/shopping_lists.sql create mode 100644 sites/test2/sql/drop/transactions.sql create mode 100644 sites/test2/sql/drop/vendors.sql create mode 100644 sites/test2/sql/drop/zones.sql create mode 100644 sites/test2/sql/unique/select_item_all.sql create mode 100644 templates/groups/group.html create mode 100644 templates/groups/index.html delete mode 100644 templates/item_page/index.html rename templates/{home.html => items/index.html} (64%) create mode 100644 templates/items/item.html create mode 100644 templates/shopping-lists/edit.html create mode 100644 templates/shopping-lists/index.html create mode 100644 templates/shopping-lists/view.html diff --git a/__pycache__/api.cpython-312.pyc b/__pycache__/api.cpython-312.pyc index 65cb803dd73a727f3aea6e8f305ee652faa822ae..86796ff551435801c7611a0f1678896a5799cb6e 100644 GIT binary patch literal 34087 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 z zObIP6Z0?+vRx3s05$GFkAXVTCSDr>&gxiWcKA(lr(^PHR`uTXTxZbd3`Ov!8Z~b#e zsKv*(P?+=n8_bFRX>7GdXG&!T_3CUHp*Pqfe-EMXoBT!Y;!BrY>#e<4!)xI|=pDZ# zuD3?l!n-QG9iFN%4+GT9LmpOm(29KFwYL60Hr?@#u5qIen{lA0jDS3P$WpB`qV$^w zK@`|_W#4Dv{#)Vxwfa5Qug`8NHG_dN0`ka^VG}EUJTc6!)Hh5B%u0J;{26{_cVN8F z^$8wpfX_&RnvSykP=vz^BCXu-tcJ(FrK}+D18|&~X)r?Qz09PR)`O2n-Ggjho6>hFIM< znFF~_6$;3DY5&RHU-+7qC%<%gfBeSfxvMYyZF}Ec(f^KqN8J9EH+XUS^2xQv&fDIu zHLj~tP=%~l-X1KCfdMj23C#>!j60zmj}uyL|%V7v61T`~*mu#Tb8%^v{s~ccg!gLjOQ5U)D8WuDdEO+^*|i_I)jM z;Lfk7uz)+^L1yYc!;QCaS-v0$`|FQW1 diff --git a/__pycache__/config.cpython-312.pyc b/__pycache__/config.cpython-312.pyc index 05684f21cfd96576ee7d43eba9f31b195d28873b..10a5f907c2bac01c83e134a4a44519e9b39227a5 100644 GIT binary patch literal 2687 zcmcJR&rcgi6vt=wmu;}kkI;ezCFWOyr8Fv0Ql(Xtsw5xN zZ)SHs^PT-U6skdB@gG7{f)AnJ*dNC@yRd%&3QLF}g3~C2;~29tm*(Oe@ezIj$9du> z0^9{6E}*zbB=FK47YjUNDPq_RUe(me>Ixl6IGI_i1Q!q2{;%Mckb&W5*}8o% zaCt7pB92%dt=q8}B~gKgDDg-L{LN!0iuOPRJmXRGA__uYxQ0FrAyg3aBEb>XpNA61 zN|)jKrcQvGQDg+1EQC+hI%V&k$1pqlDV_1Zz%gOu8JjX?EeAcLO2SBLYIOeXLR8g^ z=%hBIl4w$m8q-R2zON9SOs18XMA@^O0##&U)=ta%gkI@qib+Gs=w@JGHlbu$CR3W! zXht?-lIdrCRXL-WJ||qtT5sRv$SsqVW#}tb?raUBmK_XVeQ*3@SR{QsB@-<-eoafv zWE9oV#}gV+#8Pl3tRr8izld#tMTz1ZsDn)t<^0P7Yd2mJaG!*&z zv*O?rq2{aMJHw0qZ6UHJp_*W^zw8U`_#&IW$nu$OUuU_Yd8zGw+w#;_! zz@|7t+4ecWXDvJ_Td$bUf{(IYL;cLL`Iw@fO(zY?l!LOtfYBf;v%zQ>97F({I<03F z4d8T-Fxtohf>ywU%?EpgW)`*p?j$}!b;W^~;Nv{-(cZQC!OHY^>WcbkbgR9uG_csS zjPLi}`;dWho`KR(yzy_K&?cBj!j&TU!(M?UQ$tXD94HmJC}6{tDoVF9BZD|Cb>_#`A8q}J`(V`S8MnThC#*~N;`vNb(odEUuk z9S?9sIDIghLtUs3={Uz>G=?zz=FluZhH`jK8lf$a=^#jCL%t>J%DJSPG^LEJ%2Nt` z9hyv>iv=h{1mub)Rl}6zY*tZ;$?J;Y0cr{4^~95t=rIrp2*s;q-3(sm1FLK#gpvE89{RhBj6nx-(RoU2b0Odf5AY@1u9O zIxlVu?>r5JO6qcAE6}-zu-LJhD*MCV2JZ$JemX);yuq$HLkCM6c9>Sg9--r_3A zFD^+hN-ZwT(PX~GlboNImYH6VSX7)^RKyI_R|L|$lA%Z##1;e+zc_4i^HWN5QtgVw cfm}u)F6IRiAD9^#8J{w!eqrNhb%7 diff --git a/__pycache__/main.cpython-312.pyc b/__pycache__/main.cpython-312.pyc index ba529c25d5f16784c6c5c9b80951f4282f54f55c..899b7b5a424ec737fbcd89dcf764e08a8c0c6bcf 100644 GIT binary patch delta 2196 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�Q_mbrApn delta 318 zcmcb{w}PGTG%qg~0}%YY#G3BSIFV0+=^4XBjf0xvAVE}+!k)sB!nuZPH8VtoDir9e4ia;8QI6wp^khsMHk}d)nzzxJmCRtzRR=>ca z4m4>6s|F*_@#Jl6A|`AM yOswsajglS4&2k@^7#TG_Gczy=d;qaNurV;Qw6iy|cSOzzSs~KEew9HF>^K11)<*^a diff --git a/api.py b/api.py index 01dc3a1..f8d209f 100644 --- a/api.py +++ b/api.py @@ -1,9 +1,14 @@ from flask import Blueprint, request, render_template, redirect, session, url_for, send_file, jsonify, Response -import psycopg2, math -from config import config +import psycopg2, math, json, datetime, main, copy +from config import config, sites_config database_api= Blueprint('database_api', __name__) +@database_api.route("/changeSite") +def changeSite(): + site = request.args.get('site', 'main') + session['selected_site'] = site + return jsonify({'status': 'SUCCESS'}) def paginate_with_params(cur, limit, offset, params): sql = f"SELECT * FROM main_items LEFT JOIN main_logistics_info ON main_items.logistics_info_id = main_logistics_info.id" @@ -48,15 +53,87 @@ def paginate_default(cur, limit, offset): count = cur.fetchone()[0] return pantry_inventory, count +def paginate_with_params_groups(cur, limit, offset, params): + sql = f"SELECT * FROM main_groups" + count = f"SELECT COUNT(*) FROM main_groups" + # WHERE search_string LIKE '%{search_string}%' + strings = [] + count_strings = [] + if params['search_string'] != "": + s = params['search_string'] + strings.append(f" search_string LIKE '%{s}%'") + count_strings.append(f" search_string LIKE '%{s}%'") + + + # LIMIT {limit} OFFSET {offset};" + + if len(strings) > 0: + sql = f"{sql} WHERE{" AND".join(strings)}" + + if len(count_strings) > 0: + count = f"{count} WHERE{" AND".join(count_strings)}" + + sql = f"{sql} ORDER BY main_logistics_info.quantity_on_hand LIMIT {limit} OFFSET {offset};" + count = f"{count};" + print(count) + print(sql) + cur.execute(sql) + pantry_inventory = cur.fetchall() + cur.execute(count) + count = cur.fetchone()[0] + return pantry_inventory, count + +@database_api.route("/getGroups") +def paginate_groups(): + page = int(request.args.get('page', 1)) + limit = int(request.args.get('limit', 10)) + site_name = session['selected_site'] + offset = (page - 1) * limit + + groups = [] + count = 0 + + database_config = config() + with psycopg2.connect(**database_config) as conn: + try: + with conn.cursor() as cur: + sql = f"SELECT * FROM {site_name}_groups LIMIT %s OFFSET %s;" + count = f"SELECT COUNT(*) FROM {site_name}_groups" + + cur.execute(sql, (limit, offset)) + groups = cur.fetchall() + cur.execute(count) + count = cur.fetchone()[0] + + + sql_item = f"SELECT {site_name}_items.barcode, {site_name}_items.item_name, {site_name}_logistics_info.quantity_on_hand FROM {site_name}_items LEFT JOIN {site_name}_logistics_info ON {site_name}_items.logistics_info_id = {site_name}_logistics_info.id WHERE {site_name}_items.id = %s; " + new_groups = [] + for group in groups: + qty = 0 + group = list(group) + items = [] + print(group[3]) + for item_id in group[3]: + cur.execute(sql_item, (item_id,)) + item_row = cur.fetchone() + qty += float(item_row[2]) + items.append(item_row) + group[3] = items + group.append(qty) + new_groups.append(group) + except (Exception, psycopg2.DatabaseError) as error: + print(error) + + return jsonify({'groups': new_groups, "end": math.ceil(count/limit)}) @database_api.route("/getItems") def pagninate_items(): - print("hello") page = int(request.args.get('page', 1)) limit = int(request.args.get('limit', 10)) search_string = str(request.args.get('search_text', "")) sort_order = int(request.args.get('sort_order', 1)) view = int(request.args.get('view', 0)) + site_name = session['selected_site'] offset = (page - 1) * limit @@ -67,30 +144,25 @@ def pagninate_items(): with psycopg2.connect(**database_config) as conn: try: with conn.cursor() as cur: - pantry_inventory, count = paginate_with_params( - cur, limit, offset, - {'search_string': search_string, - 'view': view} - ) + sql = f"SELECT * FROM {site_name}_items LEFT JOIN {site_name}_logistics_info ON {site_name}_items.logistics_info_id = {site_name}_logistics_info.id LIMIT {limit} OFFSET {offset};" + count = f"SELECT COUNT(*) FROM {site_name}_items LEFT JOIN {site_name}_logistics_info ON {site_name}_items.logistics_info_id = {site_name}_logistics_info.id;" + cur.execute(sql) + pantry_inventory = cur.fetchall() + cur.execute(count) + count = cur.fetchone()[0] except (Exception, psycopg2.DatabaseError) as error: print(error) - if sort_order == 0: - pantry_inventory = sorted(pantry_inventory, key=lambda x: x[1]) - - if sort_order == 1: - pantry_inventory = sorted(pantry_inventory, key=lambda x: x[2]) - - if sort_order == 2: - pantry_inventory = sorted(pantry_inventory, key=lambda x: x[18]) - return jsonify({'items': pantry_inventory, "end": math.ceil(count/limit)}) @database_api.route("/getItem") def get_item(): id = int(request.args.get('id', 1)) database_config = config() - site_name = "main" + site_name = session['selected_site'] + sites = sites_config() + + item = [] with psycopg2.connect(**database_config) as conn: try: @@ -99,24 +171,351 @@ def get_item(): sql = file.read() cur.execute(sql, (id, )) item = list(cur.fetchone()) - item[5] = {'walmart': 'https://www.walmart.com/ip/Ptasie-Mleczko-Chocolate-Covered-Vanilla-Marshmallow-birds-milk-chocolate-13-4-Oz-Includes-Our-Exclusive-HolanDeli-Chocolate-Mints/965551629?classType=REGULAR&from=/search', 'target': 'https://www.target.com/p/hershey-39-s-cookies-39-n-39-cr-232-me-fangs-halloween-candy-snack-size-9-45oz/-/A-79687769#lnk=sametab'} - item[22] = ['test_list', 'main_list'] - item[23] = ['test_recipe',] - item[24] = ['test_group', 'main_group', 'test2_group'] + SQL_groups = f"SELECT * FROM {site_name}_groups WHERE included_items @> ARRAY[%s];" + cur.execute(SQL_groups, (item[0], )) + item[25] = list(cur.fetchall()) + SQL_shopping_lists = f"SELECT * FROM {site_name}_shopping_lists WHERE pantry_items @> ARRAY[%s];" + cur.execute(SQL_shopping_lists, (item[0], )) + item[23] = list(cur.fetchall()) + print(item) except (Exception, psycopg2.DatabaseError) as error: print(error) - return render_template(f"item_page/index.html", item=item) + return render_template(f"items/item.html", item=item, current_site=site_name, sites=sites['sites']) + +@database_api.route("/addItem") +def addItem(): + barcode = str(request.args.get('barcode', "")) + name = str(request.args.get('item_name', "")) + description = str(request.args.get('item_description', "")) + item_type = str(request.args.get('item_type', "")) + subtype = str(request.args.get('sub_type', "")) + site_name = session['selected_site'] + state = "FAILED" + + payload = copy.deepcopy(main.payload_food_item) + + defaults = config(filename=f"sites/{site_name}/site.ini", section="defaults") + uuid = f"{defaults["default_zone"]}@{defaults["default_primary_location"]}" + name = name.replace("'", "@&apostraphe&") + payload["logistics_info"]["primary_location"] = uuid + payload["logistics_info"]["auto_issue_location"] = uuid + + + database_config = config() + with psycopg2.connect(**database_config) as conn: + logistics_info_id = main.create_logistics_info(conn, site_name, barcode, payload["logistics_info"]) + if not logistics_info_id: + return jsonify({'state': str(logistics_info_id)}) + item_info_id = main.create_item_info(conn, site_name, barcode, payload["item_info"]) + if not item_info_id: + return jsonify({'state': str(item_info_id)}) + food_info_id = main.create_food_info(conn, site_name, payload["food_info"]) + if not food_info_id: + return jsonify({'state': str(food_info_id)}) + + sqltwo = f"INSERT INTO {site_name}_items(barcode, item_name, description, item_info_id, logistics_info_id, food_info_id, row_type, item_type, search_string) VALUES('{barcode}', '{name}', '{description}', {item_info_id}, {logistics_info_id}, {food_info_id}, '{item_type}', '{subtype}', '{barcode}%{name}') RETURNING *;" + row = None + try: + with conn.cursor() as cur: + cur.execute(sqltwo) + rows = cur.fetchone() + if rows: + row = rows[:] + except (Exception, psycopg2.DatabaseError) as error: + print(error) + conn.rollback() + return jsonify({'state': str(error)}) + + + conn.commit() + + + main.add_transaction(site_name, barcode, qty=0, user_id=1, description="Added Item to System!") + + + + return jsonify({'state': "SUCCESS"}) @database_api.route("/addGroup") def addGroup(): name = str(request.args.get('name', "")) description = str(request.args.get('description', "")) group_type = str(request.args.get('type', "")) - + site_name = session['selected_site'] state = "FAILED" - if name or description or group_type == "": - print("this is empty") + database_config = config() + with psycopg2.connect(**database_config) as conn: + try: + with conn.cursor() as cur: + sql = f"INSERT INTO {site_name}_groups (name, description, included_items, group_type) VALUES (%s, %s, %s, %s);" + cur.execute(sql, (name, description, json.dumps({}), group_type)) + state = "SUCCESS" + conn.commit() + except (Exception, psycopg2.DatabaseError) as error: + print(error) + conn.rollback() - return jsonify({'state': state}) \ No newline at end of file + + return jsonify({'state': state}) + +@database_api.route("/getGroup") +def get_group(): + id = int(request.args.get('id', 1)) + database_config = config() + site_name = session['selected_site'] + + group = [] + with psycopg2.connect(**database_config) as conn: + try: + with conn.cursor() as cur: + sql = f"SELECT * FROM {site_name}_groups WHERE id=%s;" + cur.execute(sql, (id, )) + group = list(cur.fetchone()) + + sql_item = f"SELECT {site_name}_items.id, {site_name}_items.barcode, {site_name}_items.item_name, {site_name}_logistics_info.quantity_on_hand FROM {site_name}_items LEFT JOIN {site_name}_logistics_info ON {site_name}_items.logistics_info_id = {site_name}_logistics_info.id WHERE {site_name}_items.id = %s;" + qty = 0 + group = list(group) + items = [] + print(group[3]) + for item_id in group[3]: + cur.execute(sql_item, (item_id,)) + item_row = cur.fetchone() + qty += float(item_row[3]) + items.append(item_row) + group[3] = items + group.append(qty) + except (Exception, psycopg2.DatabaseError) as error: + print(error) + + return jsonify(group=group) + +@database_api.route("/updateGroup", methods=["POST"]) +def update_group(): + if request.method == "POST": + site_name = session['selected_site'] + group_id = request.get_json()['id'] + items = request.get_json()['items'] + name = request.get_json()['name'] + description = request.get_json()['description'] + group_type = request.get_json()['group_type'] + data = (name, description, items, group_type, group_id) + database_config = config() + with psycopg2.connect(**database_config) as conn: + try: + with conn.cursor() as cur: + # Start by updating the group -> included items with the up to date list + sql = f"UPDATE {site_name}_groups SET name = %s, description = %s, included_items = %s, group_type = %s WHERE id=%s;" + cur.execute(sql, data) + + update_item_sql = f"UPDATE {site_name}_item_info SET groups = %s WHERE id = %s;" + select_item_sql = f"SELECT {site_name}_item_info.id, {site_name}_item_info.groups FROM {site_name}_items LEFT JOIN {site_name}_item_info ON {site_name}_items.item_info_id = {site_name}_item_info.id WHERE {site_name}_items.id = %s;" + # Now we will fetch each item row one by one and check if the group id is already inside of its groups array + for item_id in items: + cur.execute(select_item_sql, (item_id, )) + item = cur.fetchone() + print(item) + item_groups: set = set(item[1]) + # Condition check, adds it if it doesnt exist. + if group_id not in item_groups: + item_groups.add(group_id) + cur.execute(update_item_sql, (list(item_groups), item[0])) + + # Now we fetch all items that have the group id in its groups array + fetch_items_with_group = f"SELECT {site_name}_items.id, groups, {site_name}_item_info.id FROM {site_name}_item_info LEFT JOIN {site_name}_items ON {site_name}_items.item_info_id = {site_name}_item_info.id WHERE groups @> ARRAY[%s];" + cur.execute(fetch_items_with_group, (group_id, )) + group_items = cur.fetchall() + print(items) + # We will then check each item id against the groups new included_items list to see if the item should be in there + for item_id, group, info_id in group_items: + # If it is not we remove the group form the items list and update the item + if item_id not in items: + groups: list = list(group) + groups.remove(group_id) + cur.execute(update_item_sql, (list(groups), info_id)) + + conn.commit() + except (Exception, psycopg2.DatabaseError) as error: + print(error) + conn.rollback() + + return jsonify({"state": "SUCCESS"}) + return jsonify({"state": "FAILED"}) + +@database_api.route("/addList") +def addList(): + name = str(request.args.get('name', "")) + description = str(request.args.get('description', "")) + list_type = str(request.args.get('type', "")) + site_name = session['selected_site'] + + print(name, description, list_type) + state = "FAILED" + + #if name or description or group_type == "": + # print("this is empty") + # return jsonify({'state': state}) + timestamp = datetime.datetime.now() + data = (name, description, [], json.dumps({}), [], [], 0, timestamp, list_type) + database_config = config() + with psycopg2.connect(**database_config) as conn: + try: + with conn.cursor() as cur: + sql = f"INSERT INTO {site_name}_shopping_lists (name, description, pantry_items, custom_items, recipes, groups, author, creation_date, type) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s);" + cur.execute(sql, data) + state = "SUCCESS" + conn.commit() + except (Exception, psycopg2.DatabaseError) as error: + print(error) + conn.rollback() + + + return jsonify({'state': state}) + +@database_api.route("/getLists") +def paginate_lists(): + page = int(request.args.get('page', 1)) + limit = int(request.args.get('limit', 10)) + site_name = session['selected_site'] + + offset = (page - 1) * limit + + lists = [] + count = 0 + + database_config = config() + with psycopg2.connect(**database_config) as conn: + try: + with conn.cursor() as cur: + sql = f"SELECT * FROM {site_name}_shopping_lists LIMIT %s OFFSET %s;" + count = f"SELECT COUNT(*) FROM {site_name}_shopping_lists;" + + cur.execute(sql, (limit, offset)) + temp_lists = list(cur.fetchall()) + cur.execute(count) + count = cur.fetchone()[0] + + for shopping_list in temp_lists: + shopping_list: list = list(shopping_list) + pantry_items = shopping_list[3] + custom_items = shopping_list[4] + list_length = len(custom_items) + + if shopping_list[10] == 'calculated': + item_sql = f"SELECT COUNT(*) FROM {site_name}_items LEFT JOIN {site_name}_logistics_info ON {site_name}_items.logistics_info_id = {site_name}_logistics_info.id LEFT JOIN {site_name}n_item_info ON {site_name}_items.item_info_id = {site_name}_item_info.id LEFT JOIN {site_name}_food_info ON {site_name}_items.food_info_id = {site_name}_food_info.id WHERE {site_name}_logistics_info.quantity_on_hand < {site_name}_item_info.safety_stock AND shopping_lists @> ARRAY[%s];" + cur.execute(item_sql, (shopping_list[0], )) + list_length += cur.fetchone()[0] + else: + list_length += len(pantry_items) + + shopping_list.append(list_length) + lists.append(shopping_list) + + except (Exception, psycopg2.DatabaseError) as error: + print(error) + + return jsonify({'lists': lists, 'end': math.ceil(count/limit)}) + +@database_api.route("/getListView") +def get_list_view(): + id = int(request.args.get('id', 1)) + site_name = session['selected_site'] + shopping_list = [] + database_config = config() + with psycopg2.connect(**database_config) as conn: + try: + with conn.cursor() as cur: + sql = f"SELECT * FROM {site_name}_shopping_lists WHERE id=%s;" + cur.execute(sql, (id, )) + shopping_list = list(cur.fetchone()) + + if shopping_list[10] == "calculated": + itemSQL = f"SELECT {site_name}_items.id, {site_name}_items.barcode, {site_name}_items.item_name, {site_name}_items.links, {site_name}_logistics_info.quantity_on_hand, {site_name}_item_info.safety_stock, {site_name}_item_info.uom FROM {site_name}_items LEFT JOIN {site_name}_logistics_info ON {site_name}_items.logistics_info_id = {site_name}_logistics_info.id LEFT JOIN {site_name}_item_info ON {site_name}_items.item_info_id = {site_name}_item_info.id LEFT JOIN {site_name}_food_info ON {site_name}_items.food_info_id = {site_name}_food_info.id WHERE {site_name}_logistics_info.quantity_on_hand < {site_name}_item_info.safety_stock AND shopping_lists @> ARRAY[%s];" + else: + itemSQL = f"SELECT {site_name}_items.id, {site_name}_items.barcode, {site_name}_items.item_name, {site_name}_items.links, {site_name}_logistics_info.quantity_on_hand, {site_name}_item_info.safety_stock, {site_name}_item_info.uom FROM {site_name}_items LEFT JOIN {site_name}_logistics_info ON {site_name}_items.logistics_info_id = {site_name}_logistics_info.id LEFT JOIN {site_name}_item_info ON {site_name}_items.item_info_id = {site_name}_item_info.id LEFT JOIN {site_name}_food_info ON {site_name}_items.food_info_id = {site_name}_food_info.id WHERE shopping_lists @> ARRAY[%s];" + + cur.execute(itemSQL, (id, )) + shopping_list[3] = list(cur.fetchall()) + print(shopping_list) + + except (Exception, psycopg2.DatabaseError) as error: + print(error) + + return jsonify(shopping_list=shopping_list) + +@database_api.route("/getList") +def get_list(): + id = int(request.args.get('id', 1)) + database_config = config() + site_name = session['selected_site'] + shopping_list = [] + with psycopg2.connect(**database_config) as conn: + try: + with conn.cursor() as cur: + sql = f"SELECT * FROM {site_name}_shopping_lists WHERE id=%s;" + cur.execute(sql, (id, )) + shopping_list = list(cur.fetchone()) + itemSQL = f"SELECT {site_name}_items.id, {site_name}_items.barcode, {site_name}_items.item_name, {site_name}_items.links, {site_name}_item_info.uom FROM {site_name}_items LEFT JOIN {site_name}_item_info ON {site_name}_items.item_info_id = {site_name}_item_info.id WHERE {site_name}_item_info.shopping_lists @> ARRAY[%s];" + cur.execute(itemSQL, (id, )) + shopping_list[3] = list(cur.fetchall()) + print(shopping_list) + except (Exception, psycopg2.DatabaseError) as error: + print(error) + + return jsonify(shopping_list=shopping_list) + +@database_api.route("/updateList", methods=["POST"]) +def update_list(): + if request.method == "POST": + site_name = session['selected_site'] + list_id = request.get_json()['id'] + items = request.get_json()['items'] + print(items) + custom_items = request.get_json()['custom'] + name = request.get_json()['name'] + description = request.get_json()['description'] + list_type = request.get_json()['list_type'] + quantities = request.get_json()['quantities'] + data = (name, description, items, json.dumps(custom_items), list_type, json.dumps(quantities), list_id) + database_config = config() + with psycopg2.connect(**database_config) as conn: + try: + with conn.cursor() as cur: + # Start by updating the group -> included items with the up to date list + sql = f"UPDATE {site_name}_shopping_lists SET name = %s, description = %s, pantry_items = %s, custom_items = %s, type = %s, quantities = %s WHERE id=%s;" + cur.execute(sql, data) + + update_item_sql = f"UPDATE {site_name}_item_info SET shopping_lists = %s WHERE id = %s;" + select_item_sql = f"SELECT {site_name}_item_info.id, {site_name}_item_info.shopping_lists FROM {site_name}_items LEFT JOIN {site_name}_item_info ON {site_name}_items.item_info_id = {site_name}_item_info.id WHERE {site_name}_items.id = %s;" + # Now we will fetch each item row one by one and check if the group id is already inside of its groups array + for item_id in items: + cur.execute(select_item_sql, (item_id, )) + item = cur.fetchone() + print(item) + shopping_lists: set = set(item[1]) + # Condition check, adds it if it doesnt exist. + if list_id not in shopping_lists: + shopping_lists.add(list_id) + cur.execute(update_item_sql, (list(shopping_lists), item[0])) + + # Now we fetch all items that have the group id in its groups array + fetch_items_with_list = f"SELECT {site_name}_items.id, {site_name}_item_info.shopping_lists, {site_name}_item_info.id FROM {site_name}_item_info LEFT JOIN {site_name}_items ON {site_name}_items.item_info_id = {site_name}_item_info.id WHERE {site_name}_item_info.shopping_lists @> ARRAY[%s];" + cur.execute(fetch_items_with_list, (list_id, )) + list_items = cur.fetchall() + print(items) + # We will then check each item id against the groups new included_items list to see if the item should be in there + for item_id, shopping_list, info_id in list_items: + # If it is not we remove the group form the items list and update the item + if item_id not in items: + shopping_lists: list = list(shopping_list) + shopping_lists.remove(list_id) + cur.execute(update_item_sql, (list(shopping_lists), info_id)) + + conn.commit() + except (Exception, psycopg2.DatabaseError) as error: + print(error) + conn.rollback() + + return jsonify({"state": "SUCCESS"}) + return jsonify({"state": "FAILED"}) \ No newline at end of file diff --git a/config.py b/config.py index e131417..0d83e02 100644 --- a/config.py +++ b/config.py @@ -1,5 +1,6 @@ #!/usr/bin/python -from configparser import ConfigParser +from configparser import ConfigParser +import json def config(filename='database.ini', section='postgresql'): @@ -19,3 +20,47 @@ def config(filename='database.ini', section='postgresql'): return db +def sites_config(filename='database.ini', section='manage'): + # create a parser + parser = ConfigParser() + # read config file + parser.read(filename) + + # get section, default to postgresql + sites = {} + if parser.has_section(section): + params = parser.items(section) + for param in params: + sites[param[0]] = param[1].split(',') + else: + raise Exception('Section {0} not found in the {1} file'.format(section, filename)) + + return sites + + +def write_new_site(site_name): + + old_value = sites_config()['sites'] + print(old_value) + + old_value.append(site_name) + old_value = set(old_value) + + config = ConfigParser() + config.read('database.ini') + config.set('manage', 'sites', ','.join(old_value)) + + with open('database.ini', 'w') as configFile: + config.write(configFile) + +def delete_site(site_name): + old_value = sites_config()['sites'] + old_value.remove(site_name) + + config = ConfigParser() + config.read('database.ini') + config.set('manage', 'sites', ','.join(old_value)) + + with open('database.ini', 'w') as configFile: + config.write(configFile) + diff --git a/database.ini b/database.ini index 4b683f2..e7ad19d 100644 --- a/database.ini +++ b/database.ini @@ -1,6 +1,10 @@ [postgresql] -host=192.168.1.67 -database=test -user=test -password=test -port=5432 \ No newline at end of file +host = 192.168.1.67 +database = test +user = test +password = test +port = 5432 + +[manage] +sites = test,test2,main + diff --git a/main.py b/main.py index dc996f3..caee6ac 100644 --- a/main.py +++ b/main.py @@ -35,7 +35,7 @@ def update_item_primary(site_name, barcode, new_primary: str): return False def insert_row(table_name, name): - sql = f"INSERT INTO {table_name}(id, name) VALUES(%s, %s) RETURNING id" + sql = f"INSERT INTO {table_name}(id, name) VALUES(%s, %s) RETURNING id;" id = None try: database_config = config() @@ -85,7 +85,7 @@ def create_logistics_info(conn, site_name, barcode, payload): except (Exception, psycopg2.DatabaseError) as error: print(error) conn.rollback() - return False + return error return logistics_info_id @@ -103,7 +103,7 @@ def create_item_info(conn, site_name, barcode, payload): except (Exception, psycopg2.DatabaseError) as error: print(error) conn.rollback() - return False + return error return item_info_id @@ -136,7 +136,7 @@ def add_location(site_name, name, zone_id): except (Exception, psycopg2.DatabaseError) as error: print(error) conn.rollback() - return False + return error uuid = f"{zone_name}@{name}" try: @@ -145,7 +145,7 @@ def add_location(site_name, name, zone_id): except (Exception, psycopg2.DatabaseError) as error: print(error) conn.rollback() - return False + return error def add_zone(site_name, name): database_config = config() @@ -157,7 +157,7 @@ def add_zone(site_name, name): except (Exception, psycopg2.DatabaseError) as error: print(error) conn.rollback() - return False + return error def add_transaction(site_name, barcode, qty, user_id, transaction_type = "info", description = "", data = {}, location=None): database_config = config() @@ -192,7 +192,7 @@ def add_transaction(site_name, barcode, qty, user_id, transaction_type = "info", except (Exception, psycopg2.DatabaseError) as error: print(error) conn.rollback() - return False + return error if not location: mover = logistics_info[2] @@ -211,7 +211,7 @@ def add_transaction(site_name, barcode, qty, user_id, transaction_type = "info", except (Exception, psycopg2.DatabaseError) as error: print(error) conn.rollback() - return False + return error if logistics_info[3] in location_items.keys(): location_items[logistics_info[3]] = location_items[logistics_info[3]] + qty @@ -234,11 +234,11 @@ def add_transaction(site_name, barcode, qty, user_id, transaction_type = "info", except (Exception, psycopg2.DatabaseError) as error: print(error) conn.rollback() - return False + return error conn.commit() -def add_food_item(site_name: str, barcode: str, name: str, qty: float, payload: dict): +def add_food_item(site_name: str, barcode: str, name: str, payload: dict): # TODO: I need to validate the name so that it doesnt have characters against the SQL database schema such as ' @@ -279,7 +279,6 @@ def add_food_item(site_name: str, barcode: str, name: str, qty: float, payload: add_transaction(site_name, barcode, qty=0, user_id=1, description="Added Item to System!") - add_transaction(site_name, barcode, qty=qty, user_id=1, description="scan in") def drop_table(sql_file: str): database_config = config() diff --git a/manage.py b/manage.py index 9f84298..c955e49 100644 --- a/manage.py +++ b/manage.py @@ -1,5 +1,6 @@ import sys, os, shutil import main +import config as cfg """ Manage.py is where the databases and configuration is set up. Its a CLI for quick serving the databases necessary for @@ -90,6 +91,7 @@ def create(): config.write(f"default_primary_location={default_location_name}\n") config.write(f"default_auto_issue_location={default_location_name}\n") + cfg.write_new_site(site_name) print(f"Site {site_name} config created!") print(f"Site {site_name} created!") @@ -108,6 +110,8 @@ if __name__ == "__main__": if func_name == "delete" and argument == "site": main.delete_site(sys.argv[3]) shutil.rmtree(f"sites/{sys.argv[3]}") + cfg.delete_site(sys.argv[3]) + if func_name == "item": if argument == "add": diff --git a/sites/default/sql/create/item.sql b/sites/default/sql/create/item.sql index 2d6081d..3e08ff9 100644 --- a/sites/default/sql/create/item.sql +++ b/sites/default/sql/create/item.sql @@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS %sitename%_items( barcode VARCHAR(255) NOT NULL, item_name VARCHAR(255) NOT NULL, brand INTEGER, - description TEXT; + description TEXT, tags TEXT [], links JSONB, item_info_id INTEGER NOT NULL, diff --git a/sites/default/sql/create/shopping_lists.sql b/sites/default/sql/create/shopping_lists.sql index 5569a9f..3496d8d 100644 --- a/sites/default/sql/create/shopping_lists.sql +++ b/sites/default/sql/create/shopping_lists.sql @@ -2,10 +2,11 @@ CREATE TABLE IF NOT EXISTS %sitename%_shopping_lists ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, description TEXT, - pantry_items JSONB, + pantry_items INTEGER [], custom_items JSONB, - recipes JSONB, - groups JSONB, + recipes INTEGER [], + groups INTEGER [], + quantities JSONB, author INTEGER, creation_date TIMESTAMP, type VARCHAR(64), diff --git a/sites/main/sql/create/item.sql b/sites/main/sql/create/item.sql index d736b6b..8295054 100644 --- a/sites/main/sql/create/item.sql +++ b/sites/main/sql/create/item.sql @@ -3,6 +3,7 @@ CREATE TABLE IF NOT EXISTS main_items( barcode VARCHAR(255) NOT NULL, item_name VARCHAR(255) NOT NULL, brand INTEGER, + description TEXT, tags TEXT [], links JSONB, item_info_id INTEGER NOT NULL, diff --git a/sites/main/sql/create/shopping_lists.sql b/sites/main/sql/create/shopping_lists.sql index ec12db3..cfd9945 100644 --- a/sites/main/sql/create/shopping_lists.sql +++ b/sites/main/sql/create/shopping_lists.sql @@ -2,10 +2,11 @@ CREATE TABLE IF NOT EXISTS main_shopping_lists ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, description TEXT, - pantry_items JSONB, + pantry_items INTEGER [], custom_items JSONB, - recipes JSONB, - groups JSONB, + recipes INTEGER [], + groups INTEGER [], + quantities JSONB, author INTEGER, creation_date TIMESTAMP, type VARCHAR(64), diff --git a/sites/main/sql/unique/select_item_all.sql b/sites/main/sql/unique/select_item_all.sql index b5ca580..23e4f69 100644 --- a/sites/main/sql/unique/select_item_all.sql +++ b/sites/main/sql/unique/select_item_all.sql @@ -9,36 +9,37 @@ WHERE main_items.id=%s; 01 - barcode 02 - item_name 03 - brand (id) -04 - tags -05 - links -06 - item_info_id -07 - logistics_info_id -08 - food_info_id -09 - row_type -10 - item_type -11 - search_string -12 - logistics_info_id -13 - barcode -14 - primary_location -15 - auto_issue_location -16 - dynamic_locations -17 - location_data -18 - quantity_on_hand -19 - item_info_id -20 - barcode -21 - linked_items -22 - shopping_lists -23 - recipes -24 - groups -25 - packaging -26 - uom -27 - cost -28 - safety_stock -29 - lead_time_days -30 - ai_pick -31 - food_info_id -32 - food_groups -33 - ingrediants -34 - nutrients -35 - expires +04 - description +05 - tags +06 - links +07 - item_info_id +08 - logistics_info_id +09 - food_info_id +10 - row_type +11 - item_type +12 - search_string +13 - logistics_info_id +14 - barcode +15 - primary_location +16 - auto_issue_location +17 - dynamic_locations +18 - location_data +19 - quantity_on_hand +20 - item_info_id +21 - barcode +22 - linked_items +23 - shopping_lists +24 - recipes +25 - groups +26 - packaging +27 - uom +28 - cost +29 - safety_stock +30 - lead_time_days +31 - ai_pick +32 - food_info_id +33 - food_groups +34 - ingrediants +35 - nutrients +36 - expires */ \ No newline at end of file diff --git a/sites/test/site.ini b/sites/test/site.ini new file mode 100644 index 0000000..388ddf8 --- /dev/null +++ b/sites/test/site.ini @@ -0,0 +1,9 @@ +[site] +site_name=test +site_owner= +email= + +[defaults] +default_zone=default +default_primary_location=all +default_auto_issue_location=all diff --git a/sites/test/sql/create/brands.sql b/sites/test/sql/create/brands.sql new file mode 100644 index 0000000..6cd9f9c --- /dev/null +++ b/sites/test/sql/create/brands.sql @@ -0,0 +1,4 @@ +CREATE TABLE IF NOT EXISTS test_brands ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) +); \ No newline at end of file diff --git a/sites/test/sql/create/food_info.sql b/sites/test/sql/create/food_info.sql new file mode 100644 index 0000000..bedbe47 --- /dev/null +++ b/sites/test/sql/create/food_info.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS test_food_info ( + id SERIAL PRIMARY KEY, + food_groups TEXT [], + ingrediants TEXT [], + nutrients JSONB, + expires BOOLEAN +); \ No newline at end of file diff --git a/sites/test/sql/create/groups.sql b/sites/test/sql/create/groups.sql new file mode 100644 index 0000000..b97fdab --- /dev/null +++ b/sites/test/sql/create/groups.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS test_groups( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description TEXT, + included_items INTEGER [], + group_type VARCHAR(255), + UNIQUE (name) +); \ No newline at end of file diff --git a/sites/test/sql/create/item.sql b/sites/test/sql/create/item.sql new file mode 100644 index 0000000..c9606af --- /dev/null +++ b/sites/test/sql/create/item.sql @@ -0,0 +1,28 @@ +CREATE TABLE IF NOT EXISTS test_items( + id SERIAL PRIMARY KEY, + barcode VARCHAR(255) NOT NULL, + item_name VARCHAR(255) NOT NULL, + brand INTEGER, + description TEXT, + tags TEXT [], + links JSONB, + item_info_id INTEGER NOT NULL, + logistics_info_id INTEGER NOT NULL, + food_info_id INTEGER, + row_type VARCHAR(255) NOT NULL, + item_type VARCHAR(255) NOT NULL, + search_string TEXT NOT NULL, + UNIQUE(barcode, item_info_id), + CONSTRAINT fk_item_info + FOREIGN KEY(item_info_id) + REFERENCES test_item_info(id), + CONSTRAINT fk_food_info + FOREIGN KEY(food_info_id) + REFERENCES test_food_info(id), + CONSTRAINT fk_brand + FOREIGN KEY(brand) + REFERENCES test_brands(id), + CONSTRAINT fk_logistics_info + FOREIGN KEY(logistics_info_id) + REFERENCES test_logistics_info(id) +); diff --git a/sites/test/sql/create/item_info.sql b/sites/test/sql/create/item_info.sql new file mode 100644 index 0000000..317106c --- /dev/null +++ b/sites/test/sql/create/item_info.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOt EXISTS test_item_info ( + id SERIAL PRIMARY KEY, + barcode VARCHAR(255) NOT NULL, + linked_items INTEGER [], + shopping_lists INTEGER [], + recipes INTEGER [], + groups INTEGER [], + packaging VARCHAR(255), + uom VARCHAR(255), + cost FLOAT8, + safety_stock FLOAT8, + lead_time_days FLOAT8, + ai_pick BOOLEAN, + UNIQUE(barcode) +); \ No newline at end of file diff --git a/sites/test/sql/create/linked_items.sql b/sites/test/sql/create/linked_items.sql new file mode 100644 index 0000000..16a24e2 --- /dev/null +++ b/sites/test/sql/create/linked_items.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS test_itemlinks ( + id SERIAL PRIMARY KEY, + barcode VARCHAR(255) NOt NULL, + link INTEGER NOT NULL, + data JSONB NOT NULL, + conv_factor FLOAT8 NOt NULL, + UNIQUE(barcode) +); \ No newline at end of file diff --git a/sites/test/sql/create/locations.sql b/sites/test/sql/create/locations.sql new file mode 100644 index 0000000..da7ecdc --- /dev/null +++ b/sites/test/sql/create/locations.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS test_locations( + id SERIAL PRIMARY KEY, + uuid VARCHAR(255) NOT NULL, + name VARCHAR(32) NOT NULL, + zone_id INTEGER NOT NULL, + items JSONB, + UNIQUE(uuid), + CONSTRAINT fk_zone + FOREIGN KEY(zone_id) + REFERENCES test_zones(id) +); \ No newline at end of file diff --git a/sites/test/sql/create/logins.sql b/sites/test/sql/create/logins.sql new file mode 100644 index 0000000..f69b01b --- /dev/null +++ b/sites/test/sql/create/logins.sql @@ -0,0 +1,16 @@ +CREATE TABLE IF NOT EXISTS logins( + id SERIAL PRIMARY KEY, + username VARCHAR(255), + password VARCHAR(255), + favorites JSONB, + unseen_pantry_items INTEGER [], + unseen_groups INTEGER [], + unseen_shopping_lists INTEGER [], + unseen_recipes INTEGER [], + seen_pantry_items INTEGER [], + seen_groups INTEGER[], + seen_shopping_lists INTEGER [], + seen_recipes INTEGER [], + flags JSONB +); + diff --git a/sites/test/sql/create/logistics_info.sql b/sites/test/sql/create/logistics_info.sql new file mode 100644 index 0000000..7a77d79 --- /dev/null +++ b/sites/test/sql/create/logistics_info.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS test_logistics_info( + id SERIAL PRIMARY KEY, + barcode VARCHAR(255) NOT NULL, + primary_location VARCHAR(64), + auto_issue_location VARCHAR(64), + dynamic_locations JSONB, + location_data JSONB, + quantity_on_hand FLOAT8 NOT NULL, + UNIQUE(barcode) +); \ No newline at end of file diff --git a/sites/test/sql/create/receipt_items.sql b/sites/test/sql/create/receipt_items.sql new file mode 100644 index 0000000..f52a7eb --- /dev/null +++ b/sites/test/sql/create/receipt_items.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS test_receipt_items ( + id SERIAL PRIMARY KEY, + type VARCHAR(255) NOT NULL, + barcode VARCHAR(255) NOT NULL, + name VARCHAR(255) NOT NULL, + qty FLOAT8 NOT NULL, + data JSONB, + status VARCHAR (64) +); \ No newline at end of file diff --git a/sites/test/sql/create/receipts.sql b/sites/test/sql/create/receipts.sql new file mode 100644 index 0000000..c89b484 --- /dev/null +++ b/sites/test/sql/create/receipts.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS test_receipts ( + id SERIAL PRIMARY KEY, + receipt_id INTEGER NOT NULL, + receipt_status VARCHAR (64) NOT NULL, + date_submitted TIMESTAMP NOT NULL, + submitted_by INTEGER NOT NULL, + vendor_id INTEGER, + files JSONB, + UNIQUE(receipt_id) +); \ No newline at end of file diff --git a/sites/test/sql/create/recipes.sql b/sites/test/sql/create/recipes.sql new file mode 100644 index 0000000..150b4cc --- /dev/null +++ b/sites/test/sql/create/recipes.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS test_recipes ( + id SERIAL PRIMARY KEY, + name VARCHAR, + author INTEGER, + description TEXT, + creation_date TIMESTAMP, + custom_items JSONB, + pantry_items JSONB, + group_items JSONB, + instructions TEXT [], + picture_path TEXT +); \ No newline at end of file diff --git a/sites/test/sql/create/shopping_lists.sql b/sites/test/sql/create/shopping_lists.sql new file mode 100644 index 0000000..9661ef6 --- /dev/null +++ b/sites/test/sql/create/shopping_lists.sql @@ -0,0 +1,14 @@ +CREATE TABLE IF NOT EXISTS test_shopping_lists ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description TEXT, + pantry_items INTEGER [], + custom_items JSONB, + recipes INTEGER [], + groups INTEGER [], + quantities JSONB, + author INTEGER, + creation_date TIMESTAMP, + type VARCHAR(64), + UNIQUE(name) +); \ No newline at end of file diff --git a/sites/test/sql/create/transactions.sql b/sites/test/sql/create/transactions.sql new file mode 100644 index 0000000..c781b14 --- /dev/null +++ b/sites/test/sql/create/transactions.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOT EXISTS test_Transactions ( + id SERIAL PRIMARY KEY, + timestamp TIMESTAMP, + logistics_info_id INTEGER NOT NULL, + barcode VARCHAR(255) NOT NULL, + name VARCHAR(255), + transaction_type VARCHAR(255) NOT NULL, + quantity FLOAT8 NOT NULL, + description TEXT, + user_id INTEGER NOT NULL, + data JSONB, + CONSTRAINT fk_logistics_info + FOREIGN KEY(logistics_info_id) + REFERENCES test_logistics_info(id) +); \ No newline at end of file diff --git a/sites/test/sql/create/vendors.sql b/sites/test/sql/create/vendors.sql new file mode 100644 index 0000000..bc113b6 --- /dev/null +++ b/sites/test/sql/create/vendors.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS test_vendors ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + address VARCHAR(255), + creation_date TIMESTAMP NOT NULL, + created_by TIMESTAMP NOT NULL, + phone_number VARCHAR(32) +); \ No newline at end of file diff --git a/sites/test/sql/create/zones.sql b/sites/test/sql/create/zones.sql new file mode 100644 index 0000000..0ed0a9e --- /dev/null +++ b/sites/test/sql/create/zones.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS test_zones( + id SERIAL PRIMARY KEY, + name VARCHAR(32) NOT NULL, + UNIQUE(name) +); diff --git a/sites/test/sql/drop/brands.sql b/sites/test/sql/drop/brands.sql new file mode 100644 index 0000000..f407a8c --- /dev/null +++ b/sites/test/sql/drop/brands.sql @@ -0,0 +1 @@ +DROP TABLE test_brands CASCADE; \ No newline at end of file diff --git a/sites/test/sql/drop/food_info.sql b/sites/test/sql/drop/food_info.sql new file mode 100644 index 0000000..a4c3056 --- /dev/null +++ b/sites/test/sql/drop/food_info.sql @@ -0,0 +1 @@ +DROP TABLE test_food_info CASCADE; \ No newline at end of file diff --git a/sites/test/sql/drop/groups.sql b/sites/test/sql/drop/groups.sql new file mode 100644 index 0000000..6b62f3b --- /dev/null +++ b/sites/test/sql/drop/groups.sql @@ -0,0 +1 @@ +DROP TABLE test_groups CASCADE; \ No newline at end of file diff --git a/sites/test/sql/drop/item_info.sql b/sites/test/sql/drop/item_info.sql new file mode 100644 index 0000000..c84b0d1 --- /dev/null +++ b/sites/test/sql/drop/item_info.sql @@ -0,0 +1 @@ +DROP TABLE test_item_info CASCADE; \ No newline at end of file diff --git a/sites/test/sql/drop/items.sql b/sites/test/sql/drop/items.sql new file mode 100644 index 0000000..097b9a7 --- /dev/null +++ b/sites/test/sql/drop/items.sql @@ -0,0 +1 @@ +DROP TABLE test_items CASCADE; \ No newline at end of file diff --git a/sites/test/sql/drop/linked_items.sql b/sites/test/sql/drop/linked_items.sql new file mode 100644 index 0000000..bcc9093 --- /dev/null +++ b/sites/test/sql/drop/linked_items.sql @@ -0,0 +1 @@ +DROP TABLE test_itemlinks CASCADE; \ No newline at end of file diff --git a/sites/test/sql/drop/locations.sql b/sites/test/sql/drop/locations.sql new file mode 100644 index 0000000..4a1f42e --- /dev/null +++ b/sites/test/sql/drop/locations.sql @@ -0,0 +1 @@ +DROP TABLE test_locations CASCADE; \ No newline at end of file diff --git a/sites/test/sql/drop/logistics_info.sql b/sites/test/sql/drop/logistics_info.sql new file mode 100644 index 0000000..cb5abcd --- /dev/null +++ b/sites/test/sql/drop/logistics_info.sql @@ -0,0 +1 @@ +DROP TABLE test_logistics_info CASCADE; \ No newline at end of file diff --git a/sites/test/sql/drop/receipt_items.sql b/sites/test/sql/drop/receipt_items.sql new file mode 100644 index 0000000..0f77f60 --- /dev/null +++ b/sites/test/sql/drop/receipt_items.sql @@ -0,0 +1 @@ +DROP TABLE test_receipt_items CASCADE; \ No newline at end of file diff --git a/sites/test/sql/drop/receipts.sql b/sites/test/sql/drop/receipts.sql new file mode 100644 index 0000000..1d9502f --- /dev/null +++ b/sites/test/sql/drop/receipts.sql @@ -0,0 +1 @@ +DROP TABLE test_receipts CASCADE; \ No newline at end of file diff --git a/sites/test/sql/drop/recipes.sql b/sites/test/sql/drop/recipes.sql new file mode 100644 index 0000000..0a626bd --- /dev/null +++ b/sites/test/sql/drop/recipes.sql @@ -0,0 +1 @@ +DROP TABLE test_recipes CASCADE; \ No newline at end of file diff --git a/sites/test/sql/drop/shopping_lists.sql b/sites/test/sql/drop/shopping_lists.sql new file mode 100644 index 0000000..a4cec6d --- /dev/null +++ b/sites/test/sql/drop/shopping_lists.sql @@ -0,0 +1 @@ +DROP TABLE test_shopping_lists CASCADE; \ No newline at end of file diff --git a/sites/test/sql/drop/transactions.sql b/sites/test/sql/drop/transactions.sql new file mode 100644 index 0000000..1356e47 --- /dev/null +++ b/sites/test/sql/drop/transactions.sql @@ -0,0 +1 @@ +DROP TABLE test_transactions CASCADE; \ No newline at end of file diff --git a/sites/test/sql/drop/vendors.sql b/sites/test/sql/drop/vendors.sql new file mode 100644 index 0000000..28351e8 --- /dev/null +++ b/sites/test/sql/drop/vendors.sql @@ -0,0 +1 @@ +DROP TABLE test_vendors CASCADE; \ No newline at end of file diff --git a/sites/test/sql/drop/zones.sql b/sites/test/sql/drop/zones.sql new file mode 100644 index 0000000..c84c67b --- /dev/null +++ b/sites/test/sql/drop/zones.sql @@ -0,0 +1 @@ +DROP TABLE test_zones CASCADE; \ No newline at end of file diff --git a/sites/test/sql/unique/select_item_all.sql b/sites/test/sql/unique/select_item_all.sql new file mode 100644 index 0000000..0824e68 --- /dev/null +++ b/sites/test/sql/unique/select_item_all.sql @@ -0,0 +1,45 @@ +SELECT * FROM test_items + LEFT JOIN test_logistics_info ON test_items.logistics_info_id = test_logistics_info.id + LEFT JOIN test_item_info ON test_items.item_info_id = test_item_info.id + LEFT JOIN test_food_info ON test_items.food_info_id = test_food_info.id +WHERE test_items.id=%s; + +/* +00 - item_id +01 - barcode +02 - item_name +03 - brand (id) +04 - description +05 - tags +06 - links +07 - item_info_id +08 - logistics_info_id +09 - food_info_id +10 - row_type +11 - item_type +12 - search_string +13 - logistics_info_id +14 - barcode +15 - primary_location +16 - auto_issue_location +17 - dynamic_locations +18 - location_data +19 - quantity_on_hand +20 - item_info_id +21 - barcode +22 - linked_items +23 - shopping_lists +24 - recipes +25 - groups <-- +26 - packaging +27 - uom +28 - cost +29 - safety_stock +30 - lead_time_days +31 - ai_pick +32 - food_info_id +33 - food_groups +34 - ingrediants +35 - nutrients +36 - expires +*/ \ No newline at end of file diff --git a/sites/test2/site.ini b/sites/test2/site.ini new file mode 100644 index 0000000..719bed4 --- /dev/null +++ b/sites/test2/site.ini @@ -0,0 +1,9 @@ +[site] +site_name=test2 +site_owner=joe +email=jdoe@gmail.com + +[defaults] +default_zone=default +default_primary_location=all +default_auto_issue_location=all diff --git a/sites/test2/sql/create/brands.sql b/sites/test2/sql/create/brands.sql new file mode 100644 index 0000000..bb70d3c --- /dev/null +++ b/sites/test2/sql/create/brands.sql @@ -0,0 +1,4 @@ +CREATE TABLE IF NOT EXISTS test2_brands ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) +); \ No newline at end of file diff --git a/sites/test2/sql/create/food_info.sql b/sites/test2/sql/create/food_info.sql new file mode 100644 index 0000000..a37324a --- /dev/null +++ b/sites/test2/sql/create/food_info.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS test2_food_info ( + id SERIAL PRIMARY KEY, + food_groups TEXT [], + ingrediants TEXT [], + nutrients JSONB, + expires BOOLEAN +); \ No newline at end of file diff --git a/sites/test2/sql/create/groups.sql b/sites/test2/sql/create/groups.sql new file mode 100644 index 0000000..b5d51f1 --- /dev/null +++ b/sites/test2/sql/create/groups.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS test2_groups( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description TEXT, + included_items INTEGER [], + group_type VARCHAR(255), + UNIQUE (name) +); \ No newline at end of file diff --git a/sites/test2/sql/create/item.sql b/sites/test2/sql/create/item.sql new file mode 100644 index 0000000..71c9b76 --- /dev/null +++ b/sites/test2/sql/create/item.sql @@ -0,0 +1,28 @@ +CREATE TABLE IF NOT EXISTS test2_items( + id SERIAL PRIMARY KEY, + barcode VARCHAR(255) NOT NULL, + item_name VARCHAR(255) NOT NULL, + brand INTEGER, + description TEXT, + tags TEXT [], + links JSONB, + item_info_id INTEGER NOT NULL, + logistics_info_id INTEGER NOT NULL, + food_info_id INTEGER, + row_type VARCHAR(255) NOT NULL, + item_type VARCHAR(255) NOT NULL, + search_string TEXT NOT NULL, + UNIQUE(barcode, item_info_id), + CONSTRAINT fk_item_info + FOREIGN KEY(item_info_id) + REFERENCES test2_item_info(id), + CONSTRAINT fk_food_info + FOREIGN KEY(food_info_id) + REFERENCES test2_food_info(id), + CONSTRAINT fk_brand + FOREIGN KEY(brand) + REFERENCES test2_brands(id), + CONSTRAINT fk_logistics_info + FOREIGN KEY(logistics_info_id) + REFERENCES test2_logistics_info(id) +); diff --git a/sites/test2/sql/create/item_info.sql b/sites/test2/sql/create/item_info.sql new file mode 100644 index 0000000..1df3026 --- /dev/null +++ b/sites/test2/sql/create/item_info.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOt EXISTS test2_item_info ( + id SERIAL PRIMARY KEY, + barcode VARCHAR(255) NOT NULL, + linked_items INTEGER [], + shopping_lists INTEGER [], + recipes INTEGER [], + groups INTEGER [], + packaging VARCHAR(255), + uom VARCHAR(255), + cost FLOAT8, + safety_stock FLOAT8, + lead_time_days FLOAT8, + ai_pick BOOLEAN, + UNIQUE(barcode) +); \ No newline at end of file diff --git a/sites/test2/sql/create/linked_items.sql b/sites/test2/sql/create/linked_items.sql new file mode 100644 index 0000000..3bfa112 --- /dev/null +++ b/sites/test2/sql/create/linked_items.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS test2_itemlinks ( + id SERIAL PRIMARY KEY, + barcode VARCHAR(255) NOt NULL, + link INTEGER NOT NULL, + data JSONB NOT NULL, + conv_factor FLOAT8 NOt NULL, + UNIQUE(barcode) +); \ No newline at end of file diff --git a/sites/test2/sql/create/locations.sql b/sites/test2/sql/create/locations.sql new file mode 100644 index 0000000..aadb380 --- /dev/null +++ b/sites/test2/sql/create/locations.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS test2_locations( + id SERIAL PRIMARY KEY, + uuid VARCHAR(255) NOT NULL, + name VARCHAR(32) NOT NULL, + zone_id INTEGER NOT NULL, + items JSONB, + UNIQUE(uuid), + CONSTRAINT fk_zone + FOREIGN KEY(zone_id) + REFERENCES test2_zones(id) +); \ No newline at end of file diff --git a/sites/test2/sql/create/logins.sql b/sites/test2/sql/create/logins.sql new file mode 100644 index 0000000..f69b01b --- /dev/null +++ b/sites/test2/sql/create/logins.sql @@ -0,0 +1,16 @@ +CREATE TABLE IF NOT EXISTS logins( + id SERIAL PRIMARY KEY, + username VARCHAR(255), + password VARCHAR(255), + favorites JSONB, + unseen_pantry_items INTEGER [], + unseen_groups INTEGER [], + unseen_shopping_lists INTEGER [], + unseen_recipes INTEGER [], + seen_pantry_items INTEGER [], + seen_groups INTEGER[], + seen_shopping_lists INTEGER [], + seen_recipes INTEGER [], + flags JSONB +); + diff --git a/sites/test2/sql/create/logistics_info.sql b/sites/test2/sql/create/logistics_info.sql new file mode 100644 index 0000000..3917bf0 --- /dev/null +++ b/sites/test2/sql/create/logistics_info.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS test2_logistics_info( + id SERIAL PRIMARY KEY, + barcode VARCHAR(255) NOT NULL, + primary_location VARCHAR(64), + auto_issue_location VARCHAR(64), + dynamic_locations JSONB, + location_data JSONB, + quantity_on_hand FLOAT8 NOT NULL, + UNIQUE(barcode) +); \ No newline at end of file diff --git a/sites/test2/sql/create/receipt_items.sql b/sites/test2/sql/create/receipt_items.sql new file mode 100644 index 0000000..04145f1 --- /dev/null +++ b/sites/test2/sql/create/receipt_items.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS test2_receipt_items ( + id SERIAL PRIMARY KEY, + type VARCHAR(255) NOT NULL, + barcode VARCHAR(255) NOT NULL, + name VARCHAR(255) NOT NULL, + qty FLOAT8 NOT NULL, + data JSONB, + status VARCHAR (64) +); \ No newline at end of file diff --git a/sites/test2/sql/create/receipts.sql b/sites/test2/sql/create/receipts.sql new file mode 100644 index 0000000..806ec2f --- /dev/null +++ b/sites/test2/sql/create/receipts.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS test2_receipts ( + id SERIAL PRIMARY KEY, + receipt_id INTEGER NOT NULL, + receipt_status VARCHAR (64) NOT NULL, + date_submitted TIMESTAMP NOT NULL, + submitted_by INTEGER NOT NULL, + vendor_id INTEGER, + files JSONB, + UNIQUE(receipt_id) +); \ No newline at end of file diff --git a/sites/test2/sql/create/recipes.sql b/sites/test2/sql/create/recipes.sql new file mode 100644 index 0000000..ab89b63 --- /dev/null +++ b/sites/test2/sql/create/recipes.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS test2_recipes ( + id SERIAL PRIMARY KEY, + name VARCHAR, + author INTEGER, + description TEXT, + creation_date TIMESTAMP, + custom_items JSONB, + pantry_items JSONB, + group_items JSONB, + instructions TEXT [], + picture_path TEXT +); \ No newline at end of file diff --git a/sites/test2/sql/create/shopping_lists.sql b/sites/test2/sql/create/shopping_lists.sql new file mode 100644 index 0000000..cb52654 --- /dev/null +++ b/sites/test2/sql/create/shopping_lists.sql @@ -0,0 +1,14 @@ +CREATE TABLE IF NOT EXISTS test2_shopping_lists ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description TEXT, + pantry_items INTEGER [], + custom_items JSONB, + recipes INTEGER [], + groups INTEGER [], + quantities JSONB, + author INTEGER, + creation_date TIMESTAMP, + type VARCHAR(64), + UNIQUE(name) +); \ No newline at end of file diff --git a/sites/test2/sql/create/transactions.sql b/sites/test2/sql/create/transactions.sql new file mode 100644 index 0000000..77c42d0 --- /dev/null +++ b/sites/test2/sql/create/transactions.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOT EXISTS test2_Transactions ( + id SERIAL PRIMARY KEY, + timestamp TIMESTAMP, + logistics_info_id INTEGER NOT NULL, + barcode VARCHAR(255) NOT NULL, + name VARCHAR(255), + transaction_type VARCHAR(255) NOT NULL, + quantity FLOAT8 NOT NULL, + description TEXT, + user_id INTEGER NOT NULL, + data JSONB, + CONSTRAINT fk_logistics_info + FOREIGN KEY(logistics_info_id) + REFERENCES test2_logistics_info(id) +); \ No newline at end of file diff --git a/sites/test2/sql/create/vendors.sql b/sites/test2/sql/create/vendors.sql new file mode 100644 index 0000000..2d7a277 --- /dev/null +++ b/sites/test2/sql/create/vendors.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS test2_vendors ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + address VARCHAR(255), + creation_date TIMESTAMP NOT NULL, + created_by TIMESTAMP NOT NULL, + phone_number VARCHAR(32) +); \ No newline at end of file diff --git a/sites/test2/sql/create/zones.sql b/sites/test2/sql/create/zones.sql new file mode 100644 index 0000000..17a555e --- /dev/null +++ b/sites/test2/sql/create/zones.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS test2_zones( + id SERIAL PRIMARY KEY, + name VARCHAR(32) NOT NULL, + UNIQUE(name) +); diff --git a/sites/test2/sql/drop/brands.sql b/sites/test2/sql/drop/brands.sql new file mode 100644 index 0000000..0159f3f --- /dev/null +++ b/sites/test2/sql/drop/brands.sql @@ -0,0 +1 @@ +DROP TABLE test2_brands CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/food_info.sql b/sites/test2/sql/drop/food_info.sql new file mode 100644 index 0000000..7c1cb91 --- /dev/null +++ b/sites/test2/sql/drop/food_info.sql @@ -0,0 +1 @@ +DROP TABLE test2_food_info CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/groups.sql b/sites/test2/sql/drop/groups.sql new file mode 100644 index 0000000..0513fb2 --- /dev/null +++ b/sites/test2/sql/drop/groups.sql @@ -0,0 +1 @@ +DROP TABLE test2_groups CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/item_info.sql b/sites/test2/sql/drop/item_info.sql new file mode 100644 index 0000000..c786efe --- /dev/null +++ b/sites/test2/sql/drop/item_info.sql @@ -0,0 +1 @@ +DROP TABLE test2_item_info CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/items.sql b/sites/test2/sql/drop/items.sql new file mode 100644 index 0000000..df1f8a9 --- /dev/null +++ b/sites/test2/sql/drop/items.sql @@ -0,0 +1 @@ +DROP TABLE test2_items CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/linked_items.sql b/sites/test2/sql/drop/linked_items.sql new file mode 100644 index 0000000..0795351 --- /dev/null +++ b/sites/test2/sql/drop/linked_items.sql @@ -0,0 +1 @@ +DROP TABLE test2_itemlinks CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/locations.sql b/sites/test2/sql/drop/locations.sql new file mode 100644 index 0000000..8ea7d24 --- /dev/null +++ b/sites/test2/sql/drop/locations.sql @@ -0,0 +1 @@ +DROP TABLE test2_locations CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/logistics_info.sql b/sites/test2/sql/drop/logistics_info.sql new file mode 100644 index 0000000..5d5a625 --- /dev/null +++ b/sites/test2/sql/drop/logistics_info.sql @@ -0,0 +1 @@ +DROP TABLE test2_logistics_info CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/receipt_items.sql b/sites/test2/sql/drop/receipt_items.sql new file mode 100644 index 0000000..18c80dd --- /dev/null +++ b/sites/test2/sql/drop/receipt_items.sql @@ -0,0 +1 @@ +DROP TABLE test2_receipt_items CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/receipts.sql b/sites/test2/sql/drop/receipts.sql new file mode 100644 index 0000000..7795023 --- /dev/null +++ b/sites/test2/sql/drop/receipts.sql @@ -0,0 +1 @@ +DROP TABLE test2_receipts CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/recipes.sql b/sites/test2/sql/drop/recipes.sql new file mode 100644 index 0000000..ba30922 --- /dev/null +++ b/sites/test2/sql/drop/recipes.sql @@ -0,0 +1 @@ +DROP TABLE test2_recipes CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/shopping_lists.sql b/sites/test2/sql/drop/shopping_lists.sql new file mode 100644 index 0000000..e4475ab --- /dev/null +++ b/sites/test2/sql/drop/shopping_lists.sql @@ -0,0 +1 @@ +DROP TABLE test2_shopping_lists CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/transactions.sql b/sites/test2/sql/drop/transactions.sql new file mode 100644 index 0000000..330d9af --- /dev/null +++ b/sites/test2/sql/drop/transactions.sql @@ -0,0 +1 @@ +DROP TABLE test2_transactions CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/vendors.sql b/sites/test2/sql/drop/vendors.sql new file mode 100644 index 0000000..5fa0868 --- /dev/null +++ b/sites/test2/sql/drop/vendors.sql @@ -0,0 +1 @@ +DROP TABLE test2_vendors CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/zones.sql b/sites/test2/sql/drop/zones.sql new file mode 100644 index 0000000..fc30e59 --- /dev/null +++ b/sites/test2/sql/drop/zones.sql @@ -0,0 +1 @@ +DROP TABLE test2_zones CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/unique/select_item_all.sql b/sites/test2/sql/unique/select_item_all.sql new file mode 100644 index 0000000..c339e15 --- /dev/null +++ b/sites/test2/sql/unique/select_item_all.sql @@ -0,0 +1,45 @@ +SELECT * FROM test2_items + LEFT JOIN test2_logistics_info ON test2_items.logistics_info_id = test2_logistics_info.id + LEFT JOIN test2_item_info ON test2_items.item_info_id = test2_item_info.id + LEFT JOIN test2_food_info ON test2_items.food_info_id = test2_food_info.id +WHERE test2_items.id=%s; + +/* +00 - item_id +01 - barcode +02 - item_name +03 - brand (id) +04 - description +05 - tags +06 - links +07 - item_info_id +08 - logistics_info_id +09 - food_info_id +10 - row_type +11 - item_type +12 - search_string +13 - logistics_info_id +14 - barcode +15 - primary_location +16 - auto_issue_location +17 - dynamic_locations +18 - location_data +19 - quantity_on_hand +20 - item_info_id +21 - barcode +22 - linked_items +23 - shopping_lists +24 - recipes +25 - groups +26 - packaging +27 - uom +28 - cost +29 - safety_stock +30 - lead_time_days +31 - ai_pick +32 - food_info_id +33 - food_groups +34 - ingrediants +35 - nutrients +36 - expires +*/ \ No newline at end of file diff --git a/templates/groups/group.html b/templates/groups/group.html new file mode 100644 index 0000000..1f1f5d4 --- /dev/null +++ b/templates/groups/group.html @@ -0,0 +1,352 @@ + + + + + My Pantry - Groups + + + + + + + + + + + + + + + +
+
+
+
+
+
+

menu

+
+
+

+
+
+
+
+
+
+
+
+
+ + +
+
+ + +
+
+
+ +
+
+
+
+
+
+
+ +
+ + + + \ No newline at end of file diff --git a/templates/groups/index.html b/templates/groups/index.html new file mode 100644 index 0000000..8007a27 --- /dev/null +++ b/templates/groups/index.html @@ -0,0 +1,448 @@ + + + + + My Pantry - Groups + + + + + + + + + + + + + + + + +
+
+ +
+
+ menu +
+
+ search + +
+
+ tune +
+
+
+ +
+
+

Set Items Per Page

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+ + + + + + + \ No newline at end of file diff --git a/templates/item_page/index.html b/templates/item_page/index.html deleted file mode 100644 index 703602c..0000000 --- a/templates/item_page/index.html +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - - - - -
-
-
-

{{item[2]}}

-
Database ID: {{item[0]}}
-
Barcode: {{item[1]}}
-
-
- - -
-
- - -
- -
-
-

Links

- -
-
- -
-
- -
- - - - - - - - - -
Reference TypeReference Name
-
-
Food Info
-
Logistics Info
-
Linked Items
-
-
-
-
- - - \ No newline at end of file diff --git a/templates/home.html b/templates/items/index.html similarity index 64% rename from templates/home.html rename to templates/items/index.html index 0aaf1d4..01c2c21 100644 --- a/templates/home.html +++ b/templates/items/index.html @@ -2,7 +2,7 @@ - My Pantry + My Pantry - Items @@ -22,17 +22,57 @@ border: 2px solid rgb(0 128 0 / 30%); /* Outline color */ background-color: rgb(0 128 0 / 30%); /* Fill color */ } + header, main, footer, body { + padding-left: 300px; + } + + @media only screen and (max-width : 992px) { + header, main, footer, body { + padding-left: 0; + } + } + .dropdown-disabled { + pointer-events: none; + opacity: 0.5; /* or your desired degree of transparency */ + } + - + +
-
- search - -
-
- tune +
+
+ menu +
+
+ search + +
+
+ tune +
@@ -146,10 +186,64 @@ more_vert
+ \ No newline at end of file diff --git a/templates/items/item.html b/templates/items/item.html new file mode 100644 index 0000000..35eaeca --- /dev/null +++ b/templates/items/item.html @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + + + + + + +
+
+
+

{{item[2]}}

+
Database ID: {{item[0]}}
+
Barcode: {{item[1]}}
+
+
+ + +
+
+ + +
+ +
+
+
+
+ Links +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+ + + + + + + + + +
Reference TypeReference Name
+
+
Food Info
+
Logistics Info
+
Linked Items
+
+
+
+
+ + + + \ No newline at end of file diff --git a/templates/shopping-lists/edit.html b/templates/shopping-lists/edit.html new file mode 100644 index 0000000..4c11d01 --- /dev/null +++ b/templates/shopping-lists/edit.html @@ -0,0 +1,522 @@ + + + + + My Pantry + + + + + + + + + + + + + + + +
+
+
+
+
+
+

menu

+
+
+

+
+
+
+
+
+
+
+
+
+ + +
+
+ + +
+
+
+ + + + +
+
+
+
+
+
+
+ +
+ + + + + + + \ No newline at end of file diff --git a/templates/shopping-lists/index.html b/templates/shopping-lists/index.html new file mode 100644 index 0000000..ccd307d --- /dev/null +++ b/templates/shopping-lists/index.html @@ -0,0 +1,332 @@ + + + + + My Pantry + + + + + + + + + + + + + + + +
+
+
+
+ menu +
+
+ search + +
+
+ tune +
+
+
+ +
+
+

Set Items Per Page

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+ + more_vert + + +
+ + + + + \ No newline at end of file diff --git a/templates/shopping-lists/view.html b/templates/shopping-lists/view.html new file mode 100644 index 0000000..53b2978 --- /dev/null +++ b/templates/shopping-lists/view.html @@ -0,0 +1,173 @@ + + + + + My Pantry + + + + + + + + + + + + + + + +
+
+
+

+
+

+
+
+

+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/webserver.py b/webserver.py index b12ddea..ea3e3bc 100644 --- a/webserver.py +++ b/webserver.py @@ -1,15 +1,47 @@ -from flask import Flask, render_template -import api +from flask import Flask, render_template, session +import api, config app = Flask(__name__) - +app.secret_key = '11gs22h2h1a4h6ah8e413a45' app.register_blueprint(api.database_api) +@app.route("/group/") +def group(id): + sites = config.sites_config() + return render_template("groups/group.html", id=id, current_site=session['selected_site'], sites=sites['sites']) + @app.route("/workshop") def workshop(): - return render_template("workshop.html") + sites = config.sites_config() + return render_template("workshop.html", current_site=session['selected_site'], sites=sites['sites']) + +@app.route("/shopping-list/view/") +def shopping_lists_view(id): + sites = config.sites_config() + return render_template("shopping-lists/view.html", id=id, current_site=session['selected_site'], sites=sites['sites']) + +@app.route("/shopping-list/edit/") +def shopping_lists_edit(id): + sites = config.sites_config() + return render_template("shopping-lists/edit.html", id=id, current_site=session['selected_site'], sites=sites['sites']) + +@app.route("/shopping-lists") +def shopping_lists(): + sites = config.sites_config() + return render_template("shopping-lists/index.html", current_site=session['selected_site'], sites=sites['sites']) + +@app.route("/groups") +def groups(): + sites = config.sites_config() + return render_template("groups/index.html", current_site=session['selected_site'], sites=sites['sites']) + +@app.route("/items") +def items(): + sites = config.sites_config() + return render_template("items/index.html", current_site=session['selected_site'], sites=sites['sites']) @app.route("/") def home(): - return render_template("home.html") + session['selected_site'] = 'main' + return render_template("items/index.html") app.run(host="0.0.0.0", port=5002, debug=True) \ No newline at end of file