From 62cd4af78ea2debe4679f444c2cc3ebc7083ddbf Mon Sep 17 00:00:00 2001 From: Jadowyne Ulve Date: Sun, 10 Aug 2025 07:12:06 -0500 Subject: [PATCH] Implemented CSV download of items --- .../database_items.cpython-313.pyc | Bin 60485 -> 62057 bytes .../__pycache__/items_API.cpython-313.pyc | Bin 33884 -> 35177 bytes application/items/database_items.py | 20 ++++++++++++++ application/items/items_API.py | 25 +++++++++++++++++- application/items/sql/getItemsAll.sql | 4 +++ application/items/templates/index.html | 3 +-- 6 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 application/items/sql/getItemsAll.sql diff --git a/application/items/__pycache__/database_items.cpython-313.pyc b/application/items/__pycache__/database_items.cpython-313.pyc index 15865c06b8035b5cb94d1442d5f3e8938be65a39..49e3d4fce448d760a3b324890ab696564b68085c 100644 GIT binary patch delta 3872 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 V;4knzAH3Nh7Tmg1BmE!NKLM6&MzjC` diff --git a/application/items/__pycache__/items_API.cpython-313.pyc b/application/items/__pycache__/items_API.cpython-313.pyc index 288fd8f130cdaabc0face28f9533adb25a6cd09e..edd7e52125047db8d77d6aba95a02d34d58c80e5 100644 GIT binary patch delta 2458 zcmZ|Qe{2&~902fp@9uinmTTF1`(ZcMQULLDWA;#KidGZxcZOP!j{4BeEO|iW-eEL6G<}D!$ve>$LIGzWd&N zzVG|q-L>x~N8tBQL)TS7a8P9X;6Q%+g}X;x)!_BfVAn8=&qA{#N`i3#2~&F)a*;h9 zcgNhw?Wai0#AnCKP}x)rZ zFWDtW8-r>kr+~OzLLJ*+NWyu?8xU0BHNu+!)MD1XAATo zvOznOo{xo|QxK%_nY4ojGS+)J+BQTMBIQx-i{2o1Z9hia9ygBv(5^-5u)gC61kd2z z@;C!t#4%-oF}a<9GUhrbvq?7Lx&8=d?2^#T@KgVjLH{eG zLX)@DF-ay-r9GSQE|P6Y%0)?R>ISoD%41k_WWG>}16O%TH zGu{SC&PBDrWhBEzmWSWkuAYtXHv6Iwu;+e1L-w&vvB8Kml5 zPgc$JD?LL@6S-+ayqdXRjTn)qGbgJVSzXwbRR(fOR?X(*Au+A0>8;!Q$@%vCvV9pj z-Jgkupw9Pa`!nhT0~wv$o=Nv+kb*86!+k{!llE&Pyg-^z8DflhmC>(gO2r}L7EvhudoYjv7TMYjziGD)QK)BD_@ZZ{)E=SkAqbVls0Pdy=gV0 zlG*6AoEt<=GCqZjH+9I4))@&L%52S{UIkq>j#y*TiVl@5`pr1D<#wq9yHuSwuTJMt zhFrg_WE49|@S7X*8|kynU)RYezK{B5m~-ua=;4R*72{mpq*#+* zJ0^xl?301!bAhFw1(v>jS7BXpEU|scb4~?F?*J;Bk?)DdW^5mSDoeOPLz2L*NoO^b4Z6u-=Wk|?l?Vs{ElJj&(vpheq5Q@IP?S%Y}T!?lTy5&MJH|88DFasz-E;Y1LXYcDvD?1SR zdr{MmnI)0-$)1q&XXDhbM)N1Wv_}Py0b0-AT8Eq5DDt6{zqH5p`t0W^@^PG}-1z0a suiBa~GcK?bT=rAYx!?D&?~w0U>#^WIUjZz-0?UY8r774l9ZpT*AI$H8%m4rY delta 1461 zcmZY9TSyd97zgnA&RkY^o!x3DFIl;5rEP0#7faVmx)52rbkPxnTC%%^Q8_*sT}2-X z5=u`{;6u?%^$SC{ z&Jh2Eu6reRZBJg@cci7?pAQH6OV;d=Q#r{TD<>qGlBuNhq~G#S)g%>2PP3*@)oN-+ zGH3;-YK_%~W&G=znas8FLQ)#SlIcn3n^|VI>5VH^f$5tiS+mjwO%)VkK|NKx&Im+xXempeTNUYW1F#iU5cNQO)@331U4*UScDsPly9nt5el=EsTH z6J;LeP9GB|bBxJ+AwJoWsBEYUxaf0TGrO2C6drQ<6XwO->6eVj0mV>Dgj6d|=2j+) zxah^Mnb(*{gfDXW1oOq*X_4wou2T&)Dx^I_&N3z z<{>6!LIyMB0h6U%^a?Zj-~aM>40W0CxXVrE%em829w+lMlNCaqXUJzJ3y(<%$S8=Cf*-qvkCe=cs zUPn@1L#^SWZ*tAN$h@|VL>h6_dz-)vyrAC!D8N+UG=VU_nf{wV6ki(w0_FHV_ai_R zKFjwjkig;D*)nX#_eDJfw&L#a7X|iWbID@`PM}d5RiF>+m$oP{h?mM=DR2Y#$Ab#o z!xOb(8J^;m`lGV;ik%gmLo|^#=>7U2HmrFe!zkvi^U3fT!>MmX`(bxnwL37j@h#E3 z?1bs;XU{<_Y7EFwh-J+Q86w!x(m}KuTemPR!N8VM0&B4*Jx-tzW33+vY@@N(g;=%o z1A%r7?eWN3m)+kxmA~Ow5S#a1m!TJbw?85g!=diOKodO$kzZ_&_QBWrQj?^wG64Kk2xvbgcKdGMCNv4AjYvN1>$$1<#uYzw JfYroS`~?G+SF!*A diff --git a/application/items/database_items.py b/application/items/database_items.py index c5c0bc6..1837c0a 100644 --- a/application/items/database_items.py +++ b/application/items/database_items.py @@ -64,6 +64,26 @@ def getItemAllByID(site:str, payload: tuple, convert:bool=True): except Exception as error: postsqldb.DatabaseError(error, payload, sql) +def getItemsAll(site:str, convert:bool=True): + database_config = config.config() + with open('application/items/sql/getItemsAll.sql', 'r+') as file: + sql = file.read().replace("%%site_name%%", site) + record = () + try: + with psycopg2.connect(**database_config) as conn: + with conn.cursor() as cur: + cur.execute(sql) + rows = cur.fetchall() + headers = [desc[0] for desc in cur.description] + types = [desc[1] for desc in cur.description] + if rows and convert: + record = postsqldb.tupleDictionaryFactory(cur.description, rows) + if rows and not convert: + record = rows + return record, headers, types + except Exception as error: + postsqldb.DatabaseError(error, (), sql) + def getItemAllByBarcode(site:str, payload: tuple, convert:bool=True): database_config = config.config() with open('application/items/sql/getItemAllByBarcode.sql', 'r+') as file: diff --git a/application/items/items_API.py b/application/items/items_API.py index 6ab288a..ab96e2c 100644 --- a/application/items/items_API.py +++ b/application/items/items_API.py @@ -3,7 +3,10 @@ from flask import ( Blueprint, request, render_template, redirect, session, url_for, send_file, jsonify, Response ) import psycopg2 -import math +import math +import io +import csv +import datetime # APPLICATION IMPORTS from config import config @@ -472,4 +475,24 @@ def deleteBarcode(): return jsonify(status=201, message=f"{barcode} was deleted successfully.") except Exception as error: return jsonify(status=400, message=str(error)) + return jsonify(status=405, message=f"The request method: {request.method} is not allowed on this endpoint!") + +@items_api.route('/download_csv', methods=["GET"]) +def downloadItemsCSV(): + if request.method == "GET": + site_name = session['selected_site'] + records, headers, types = database_items.getItemsAll(site_name, convert=False) + si = io.StringIO() + writer = csv.writer(si) + writer.writerow(headers) + writer.writerow(types) + writer.writerows(records) + output = si.getvalue() + filename = f"{site_name}_items_{str(datetime.datetime.now())}.csv" + response = Response( + output, + mimetype='text/csv', + headers={"Content-Disposition": f"attachment;filename={filename}"} + ) + return response return jsonify(status=405, message=f"The request method: {request.method} is not allowed on this endpoint!") \ No newline at end of file diff --git a/application/items/sql/getItemsAll.sql b/application/items/sql/getItemsAll.sql new file mode 100644 index 0000000..b323140 --- /dev/null +++ b/application/items/sql/getItemsAll.sql @@ -0,0 +1,4 @@ +SELECT * FROM %%site_name%%_items items +LEFT JOIN %%site_name%%_item_info item_info ON item_info.id = items.item_info_id +LEFT JOIN %%site_name%%_food_info food_info ON food_info.id = items.food_info_id +LEFT JOIN %%site_name%%_logistics_info logistics_info ON logistics_info.id = items.logistics_info_id; diff --git a/application/items/templates/index.html b/application/items/templates/index.html index fc7867b..1931405 100644 --- a/application/items/templates/index.html +++ b/application/items/templates/index.html @@ -109,8 +109,7 @@