From df5b575e10b0940082126035d236b230e5b54b12 Mon Sep 17 00:00:00 2001 From: Jadowyne Ulve Date: Sat, 26 Apr 2025 18:30:11 -0500 Subject: [PATCH] recipes_api.getRecipes view updated for new standards! --- __pycache__/webserver.cpython-312.pyc | Bin 10482 -> 9329 bytes database.log | 5 +- .../database_recipes.cpython-312.pyc | Bin 1903 -> 4987 bytes .../__pycache__/recipes_api.cpython-312.pyc | Bin 16222 -> 17784 bytes scripts/recipes/database_recipes.py | 43 ++++++++++- scripts/recipes/recipes_api.py | 69 +++++++++++++++--- scripts/recipes/sql/getRecipes.sql | 3 + scripts/recipes/sql/getRecipesCount.sql | 1 + scripts/recipes/sql/itemsModal.sql | 1 + scripts/recipes/sql/postRecipeUpdate.sql | 1 + webserver.py | 15 ++++ 11 files changed, 127 insertions(+), 11 deletions(-) create mode 100644 scripts/recipes/sql/getRecipes.sql create mode 100644 scripts/recipes/sql/getRecipesCount.sql create mode 100644 scripts/recipes/sql/postRecipeUpdate.sql diff --git a/__pycache__/webserver.cpython-312.pyc b/__pycache__/webserver.cpython-312.pyc index fc5a7e2d9fdde9d6882cd27796e774c8beccf5fa..eed72736a406c9edfcc5ae2d9e6820a94263cc5c 100644 GIT binary patch delta 3011 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 W(#|5qw=TkV-J|~_dQyyn|NjM426IUO diff --git a/database.log b/database.log index f9616cb..d6dabb4 100644 --- a/database.log +++ b/database.log @@ -1838,4 +1838,7 @@ sql='INSERT INTO logins(username, password, email, favorites, unseen_pantry_items, unseen_groups, unseen_shopping_lists, unseen_recipes, seen_pantry_items, seen_groups, seen_shopping_lists, seen_recipes, sites, site_roles, system_admin, flags, row_type) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING *;') 2025-04-21 14:45:18.122865 --- ERROR --- DatabaseError(message='new row for relation "logins" violates check constraint "logins_email_check"DETAIL: Failing row contains (32, ScannerDeviceB, 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08, test, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, f, {}, device).', payload=('ScannerDeviceB', '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', 'test', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', False, '{}', 'device'), - sql='INSERT INTO logins(username, password, email, favorites, unseen_pantry_items, unseen_groups, unseen_shopping_lists, unseen_recipes, seen_pantry_items, seen_groups, seen_shopping_lists, seen_recipes, sites, site_roles, system_admin, flags, row_type) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING *;') \ No newline at end of file + sql='INSERT INTO logins(username, password, email, favorites, unseen_pantry_items, unseen_groups, unseen_shopping_lists, unseen_recipes, seen_pantry_items, seen_groups, seen_shopping_lists, seen_recipes, sites, site_roles, system_admin, flags, row_type) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING *;') +2025-04-26 18:29:30.532818 --- ERROR --- DatabaseError(message=''module' object is not callable. Did you mean: 'config.config(...)'?', + payload=(10, 0), + sql='SELECT *, (SELECT COALESCE(array_agg(row_to_json(g)), '{}') FROM main_recipe_items g WHERE rp_id = main_recipes.id) AS rp_items FROM main_recipes LIMIT %s OFFSET %s;') \ No newline at end of file diff --git a/scripts/recipes/__pycache__/database_recipes.cpython-312.pyc b/scripts/recipes/__pycache__/database_recipes.cpython-312.pyc index 5e28a337c8f7ca30c68998a38aa2f51a9f5ff6f3..70469287d675707cb9caae060ed45ec6a992f22e 100644 GIT binary patch literal 4987 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 R=3J||`EH+`a0&;3|33(E2+{xm delta 175 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 delta 3832 zcmai0YitzP6`s2@`|_^etats`_1oUH0S9axW5C83OnAgVu?tv;W$}#dwY?AS%mSu1 zcGWhoswB~k(x8$kYExCns6pC4v}zS7QiWPoUCUBPr)dOA|1?r7C$y!ae|pZHU3*!A zI;%N*?>&!u&pF>YcmDGl`RtPa_xbsG0z7vjj)`u+AbgIK%p;`-58o2s^nY4r5)>j^ zmD)v)Kz^T)~cf{aDE za7}llX;rEyN2)kWs(DqaL@s^8*19Yy|EiQL$5vUERA5!gonyyRh1>RepSkwBjwAI+K`c|tj@$)uF2wW%5Nwuv?)#pexWJx_YV+u5~ zm!#hn70DqjbS9)KL8^>~;|iNK-z9_0Y#ktF?4UIO^qh6P*ax|G!mADfSrGndVgIn+ zBsTUl+ktsOhF>PRM3@z1G6f)`WmdSRqn3~jm*ChL&@IuLYOaC_Or!Xb93!^H-jzqI}vs^N#i zI6KwlCrNfWe+S!IQpSGXn#Zo>Z+=md#Y{{x>70Wo$l^sP#aYuu*mX|S9AKM2nIf8# zG>kKUWHZKT&a4%(!m_FY*%20Ir4PtuvPGD)u=h*ay_Q+?tZ7ypGszYROgfk+p>pSh zIV-!pwW?FAOdAs>t1_dt|9R`Y=^~sVb3)lB+vU8g5-aYmJlS?ZJRzJX0p}oX0~KA0 zhsG7^#-VvS90_X_<2`hZ=+@C#^qfMq0bM#XtxNM>deGaX?WQ&nUW?SnUS#~AbqT2Vt?Za^tXw3+RXi3*5pr$UniR}wzL%qhSrEF z?SSC`?F4#F;^`#lXsxqGuqp5ER2(Zm5H zYg-+}DelJ)qGG<&Nipip9+KtsfsX-iYm$`zm=GWPprVufk(ZUH@(Z&#YZ)^!d)2Ki zgLF3s>jU_fhRC`O>G{c6H@?3&3y+!D zx_W5<#_T~|O{YqYewpg%;&{p zsy1w#N&((Uc-6Z=AXxc@Y_7hWd3V-#5c?dKqsw>C)w<+rz31v$a&_HYcXK9nWF$2< zy|h1)I{&Sdt83Xc`^o^8;vAIXpXwWZkE}Sx>YGbf-|a2U{U-5_$=vU-CdCF_wD9? znX$Nt+^7ra0FC1qVMEU%X<#32bj?2#CMwA21?lSOo1We^vu`(V_q=aWiY5|B&LPQ( zm(A#62xQ8SkQF&t(wB}2Kh_LaN9>>IfhJk#>1mpf-1py zNSmOexI+W$YwsTWn)Ff`**t?51LxmC6ZWz2bXxz(bE&zIcO>G7jC~Qj3V#5)?*8pF>Jp8eSD)GluYq8gZ}S#d{b_Q~^Iw@- zIBfV;u!a_%>w(AVJGTa7R70m&sM`l%U+8WFu)}?G?3sCO2J^<%Q!Fdf3S7h-Y8rT| zWFg-2_4g2~+UBk0urD&SjjMoX>Izu?5MEVcf9`EdzPhbNY+dt!cNlbo=M3r-hltw& z`Zf-y*k?V43+1e=x6E*(`v17WLcQ*$wKBD49Xhw8vG;m?6-L3Tp>s;sx!;BDf0g{W zw~LTXtgNrMk|Sv+tm@LF8jI>SY{Xg^K36JOyw6KicDZkdg$Ht&-S2Z5H8cRU?#xE=5RQzLxpY<57;dMi?jq-*;5#}6W6!T9pmJ|=hyE{FNBRu?L+jjUO zPE0F3bP=WmUiCQpXjlKK^$!Z%@8rLKVY%SB1;>5jyhlowNXZ-B%f$ByDg4qT5&uI` zFcl%GUXftB;uK8zhFGf9zf1yL&PL*7Rj_46!ud)Wi}!nq^T-ruZCf$naK*!}^iMwT uBnM5X(6wU1;fgQIYV(bz8=e#?Sti|Ig3jog`Y7ACr;@$5`(x=e`0zjXhE^#6 diff --git a/scripts/recipes/database_recipes.py b/scripts/recipes/database_recipes.py index 8ea6ed2..7b3f7bc 100644 --- a/scripts/recipes/database_recipes.py +++ b/scripts/recipes/database_recipes.py @@ -22,4 +22,45 @@ def getModalSKUs(site, payload, convert=True): if rows and count: return rows, count - return [], 0 \ No newline at end of file + return [], 0 + +def getRecipes(site:str, payload:tuple, convert=True): + recordset = [] + count = 0 + with open("scripts/recipes/sql/getRecipes.sql", "r+") as file: + sql = file.read().replace("%%site_name%%", site) + with open(f"scripts/recipes/sql/getRecipesCount.sql", "r+") as file: + sqlcount = file.read().replace("%%site_name%%", site) + try: + database_config = config.config() + with psycopg2.connect(**database_config) as conn: + with conn.cursor() as cur: + cur.execute(sql, payload) + rows = cur.fetchall() + if rows and convert: + recordset = [postsqldb.tupleDictionaryFactory(cur.description, row) for row in rows] + if rows and not convert: + recordset = rows + cur.execute(sqlcount) + count = cur.fetchone()[0] + except (Exception, psycopg2.DatabaseError) as error: + raise postsqldb.DatabaseError(error, payload, sql) + return recordset, count + +def postRecipeUpdate(site, payload, convert=True): + database_config = config.config() + updated = () + with psycopg2.connect(**database_config) as conn: + with conn.cursor() as cur: + set_clause, values = postsqldb.updateStringFactory(payload['update']) + with open("scripts/recipes/sql/postRecipeUpdate.sql") as file: + sql = file.read().replace("%%site_name%%", site).replace("%%set_clause%%", set_clause) + values.append(payload['id']) + cur.execute(sql, values) + + rows = cur.fetchone() + if rows and convert: + updated = postsqldb.tupleDictionaryFactory(cur.description, rows) + elif rows and not convert: + updated = rows + return updated \ No newline at end of file diff --git a/scripts/recipes/recipes_api.py b/scripts/recipes/recipes_api.py index 7b55f3a..2629590 100644 --- a/scripts/recipes/recipes_api.py +++ b/scripts/recipes/recipes_api.py @@ -5,13 +5,22 @@ from main import unfoldCostLayers from user_api import login_required import os import postsqldb, webpush +from flasgger import swag_from from scripts.recipes import database_recipes +from flask_restx import Api, fields recipes_api = Blueprint('recipes_api', __name__) +model_api = Api(recipes_api) @recipes_api.route("/recipes") @login_required def recipes(): + """This is the main endpoint to reach the webpage for a list of all recipes + --- + responses: + 200: + description: returns recipes/index.html with sites, current_site. + """ sites = [site[1] for site in main.get_sites(session['user']['sites'])] return render_template("recipes/index.html", current_site=session['selected_site'], @@ -19,7 +28,24 @@ def recipes(): @recipes_api.route("/recipe//") @login_required -def recipe(mode, id): +def recipe(id, mode='view'): + """This is the main endpoint to reach the webpage for a recipe's view or edit mode. + --- + parameters: + - name: mode + in: path + type: string + required: true + default: view + - name: id + in: path + type: integer + required: true + default: all + responses: + 200: + description: Respondes with either the Edit or View webpage for the recipe. + """ database_config = config() with psycopg2.connect(**database_config) as conn: units = postsqldb.UnitsTable.getAll(conn) @@ -32,16 +58,22 @@ def recipe(mode, id): @recipes_api.route('/recipes/getRecipes', methods=["GET"]) def getRecipes(): + """ Get a subquery of recipes from the database by passing a page, limit + --- + responses: + 200: + description: limit of rows passed returned to requester + """ recipes = [] + count=0 if request.method == "GET": page = int(request.args.get('page', 1)) limit = int(request.args.get('limit', 1)) offset = (page-1)*limit - database_config = config() site_name = session['selected_site'] - with psycopg2.connect(**database_config) as conn: - recipes, count = postsqldb.RecipesTable.getRecipes(conn, site_name, (limit, offset), convert=True) - return jsonify({'recipes': recipes, 'end': math.ceil(count/limit), 'error': False, 'message': 'bleh'}) + recipes, count = database_recipes.getRecipes(site_name, (limit, offset)) + return jsonify({'recipes': recipes, 'end': math.ceil(count/limit), 'error': False, 'message': 'fetch was successful!'}) + return jsonify({'recipes': recipes, 'end': math.ceil(count/limit), 'error': True, 'message': f'method is not allowed: {request.method}'}) @recipes_api.route('/recipe/getRecipe', methods=["GET"]) def getRecipe(): @@ -75,6 +107,12 @@ def addRecipe(): @recipes_api.route('/recipe/getItems', methods=["GET"]) def getItems(): + """ Pass along a page, limit, and search strings to get a pagination of items from the system + --- + responses: + 200: + description: Items were returned successfully! + """ recordset = [] count = {'count': 0} if request.method == "GET": @@ -83,22 +121,35 @@ def getItems(): search_string = request.args.get('search_string', 10) site_name = session['selected_site'] offset = (page - 1) * limit - recordset, count = database_recipes.getModalSKUs(site_name, (limit, offset)) + recordset, count = database_recipes.getModalSKUs(site_name, (search_string, limit, offset)) print(recordset) return jsonify({"items":recordset, "end":math.ceil(count/limit), "error":False, "message":"items fetched succesfully!"}) return jsonify({"items":recordset, "end":math.ceil(count/limit), "error":True, "message":"There was an error with this GET statement"}) +update_model = model_api.model('model', { + 'id': fields.Integer(min=1), + 'update': fields.Raw(required=True, description="all the data to be updated!") +}) + @recipes_api.route('/recipe/postUpdate', methods=["POST"]) +@model_api.expect(update_model) def postUpdate(): + """ This is an endpoint for updating an RecipeTuple in the sites recipes table + --- + responses: + 200: + description: The time was updated successfully! + + Returns: + dict: returns a dictionary containing the updated recipe object, error status, and a message to post for notifications + """ recipe = {} if request.method == "POST": recipe_id = int(request.get_json()['recipe_id']) update = request.get_json()['update'] - database_config = config() site_name = session['selected_site'] - with psycopg2.connect(**database_config) as conn: - recipe = postsqldb.RecipesTable.updateRecipe(conn, site_name, {'id': recipe_id, 'update': update}, convert=True) + recipe = database_recipes.postRecipeUpdate(site_name, {'id': recipe_id, 'update': update}) return jsonify({'recipe': recipe, 'error': False, 'message': 'Update of Recipe successful!'}) return jsonify({'recipe': recipe, 'error': True, 'message': 'Update of Recipe unsuccessful!'}) diff --git a/scripts/recipes/sql/getRecipes.sql b/scripts/recipes/sql/getRecipes.sql new file mode 100644 index 0000000..1703d1e --- /dev/null +++ b/scripts/recipes/sql/getRecipes.sql @@ -0,0 +1,3 @@ +SELECT *, + (SELECT COALESCE(array_agg(row_to_json(g)), '{}') FROM %%site_name%%_recipe_items g WHERE rp_id = %%site_name%%_recipes.id) AS rp_items + FROM %%site_name%%_recipes LIMIT %s OFFSET %s; \ No newline at end of file diff --git a/scripts/recipes/sql/getRecipesCount.sql b/scripts/recipes/sql/getRecipesCount.sql new file mode 100644 index 0000000..a9f5ad7 --- /dev/null +++ b/scripts/recipes/sql/getRecipesCount.sql @@ -0,0 +1 @@ +SELECT COUNT(*) FROM %%site_name%%_recipes; \ No newline at end of file diff --git a/scripts/recipes/sql/itemsModal.sql b/scripts/recipes/sql/itemsModal.sql index e304973..7af323e 100644 --- a/scripts/recipes/sql/itemsModal.sql +++ b/scripts/recipes/sql/itemsModal.sql @@ -1,2 +1,3 @@ SELECT item.id, item.barcode, item.item_name FROM %%site_name%%_items item +WHERE item.search_string LIKE '%%' || %s || '%%' LIMIT %s OFFSET %s; \ No newline at end of file diff --git a/scripts/recipes/sql/postRecipeUpdate.sql b/scripts/recipes/sql/postRecipeUpdate.sql new file mode 100644 index 0000000..79c3c61 --- /dev/null +++ b/scripts/recipes/sql/postRecipeUpdate.sql @@ -0,0 +1 @@ +UPDATE %%site_name%%_recipes SET %%set_clause%% WHERE id=%s RETURNING *; \ No newline at end of file diff --git a/webserver.py b/webserver.py index a323205..e07c9cc 100644 --- a/webserver.py +++ b/webserver.py @@ -9,8 +9,11 @@ import database import postsqldb from webpush import trigger_push_notifications_for_subscriptions from scripts.recipes import recipes_api +from flasgger import Swagger + app = Flask(__name__, instance_relative_config=True) +swagger = Swagger(app) UPLOAD_FOLDER = 'static/pictures' FILES_FOLDER = 'static/files' app.config.from_pyfile('application.cfg.py') @@ -66,6 +69,18 @@ def inject_user(): @app.route("/transactions/") @login_required def transactions(id): + """This is the main endpoint to reach the webpage for an items transaction history + --- + parameters: + - name: id + in: path + type: integer + required: true + default: all + responses: + 200: + description: Returns the transactions.html webpage for the item with passed ID + """ sites = [site[1] for site in main.get_sites(session['user']['sites'])] return render_template("items/transactions.html", id=id, current_site=session['selected_site'], sites=sites)