From 20532d077f06950c7c35089ee836e318ea5b6ca8 Mon Sep 17 00:00:00 2001 From: Jadowyne Ulve Date: Sat, 2 Aug 2025 18:59:14 -0500 Subject: [PATCH] finished migration of administration module --- .../administration/administration_api.py | 91 ++++++------------ .../administration/administration_database.py | 92 +++++++++++++++++++ .../sql/insertLoginsTupleFull.sql | 6 ++ .../administration/templates/role.html | 4 +- .../administration/templates/user.html | 7 +- database.log | 8 +- 6 files changed, 140 insertions(+), 68 deletions(-) create mode 100644 application/administration/sql/insertLoginsTupleFull.sql diff --git a/application/administration/administration_api.py b/application/administration/administration_api.py index d079f96..20b89fe 100644 --- a/application/administration/administration_api.py +++ b/application/administration/administration_api.py @@ -52,7 +52,7 @@ def adminUser(id): new_user_payload = database_payloads.LoginsPayload("", "", "", "") return render_template("user.html", user=new_user_payload.get_dictionary()) else: - user = administration_database.selectLoginsTuple(int(id)) + user = administration_database.selectLoginsTuple((int(id),)) return render_template('user.html', user=user) # API ROUTES @@ -133,106 +133,73 @@ def postAddSite(): return jsonify({'error': False, 'message': f"Zone added to {site_name}."}) return jsonify({'error': True, 'message': f"These was an error with adding this Zone to {site_name}."}) +# Added to Database @admin_api.route('/api/site/postEditSite', methods=["POST"]) def postEditSite(): if request.method == "POST": payload = request.get_json()['payload'] - database_config = config() - try: - with psycopg2.connect(**database_config) as conn: - postsqldb.SitesTable.update_tuple(conn, payload) - except Exception as error: - conn.rollback() - return jsonify({'error': True, 'message': error}) + administration_database.updateSitesTuple(payload) return jsonify({'error': False, 'message': f"Site updated."}) return jsonify({'error': True, 'message': f"These was an error with updating Site."}) +# Added to Database @admin_api.route('/api/role/postAddRole', methods=["POST"]) def postAddRole(): if request.method == "POST": payload = request.get_json()['payload'] - database_config = config() print(payload) - try: - with psycopg2.connect(**database_config) as conn: - role = postsqldb.RolesTable.Payload( - payload['role_name'], - payload['role_description'], - payload['site_id'] - ) - postsqldb.RolesTable.insert_tuple(conn, role.payload()) - - except Exception as error: - conn.rollback() - return jsonify({'error': True, 'message': error}) + role = database_payloads.RolePayload( + payload['role_name'], + payload['role_description'], + payload['site_id'] + ) + administration_database.insertRolesTuple(role.payload()) return jsonify({'error': False, 'message': f"Role added."}) return jsonify({'error': True, 'message': f"These was an error with adding this Role."}) +# Added to Database @admin_api.route('/api/role/postEditRole', methods=["POST"]) def postEditRole(): if request.method == "POST": payload = request.get_json()['payload'] - database_config = config() - print(payload) - try: - with psycopg2.connect(**database_config) as conn: - postsqldb.RolesTable.update_tuple(conn, payload) - - except Exception as error: - conn.rollback() - return jsonify({'error': True, 'message': error}) + administration_database.updateRolesTuple(payload) return jsonify({'error': False, 'message': f"Role updated."}) return jsonify({'error': True, 'message': f"These was an error with updating this Role."}) +# Added to database @admin_api.route('/api/user/postAddLogin', methods=["POST"]) def postAddLogin(): if request.method == "POST": payload = request.get_json()['payload'] - database_config = config() - user = [] - try: - with psycopg2.connect(**database_config) as conn: - user = postsqldb.LoginsTable.Payload( - payload['username'], - hashlib.sha256(payload['password'].encode()).hexdigest(), - payload['email'], - payload['row_type'] - ) - user = postsqldb.LoginsTable.insert_tuple(conn, user.payload()) - except postsqldb.DatabaseError as error: - conn.rollback() - return jsonify({'user': user, 'error': True, 'message': error}) + user = database_payloads.LoginsPayload( + payload['username'], + hashlib.sha256(payload['password'].encode()).hexdigest(), + payload['email'], + payload['row_type'] + ) + user = administration_database.insertLoginsTuple(user.payload()) + return jsonify({'user': user, 'error': False, 'message': f"User added."}) return jsonify({'user': user, 'error': True, 'message': f"These was an error with adding this User."}) +# Added to database @admin_api.route('/api/user/postEditLogin', methods=["POST"]) def postEditLogin(): if request.method == "POST": payload = request.get_json()['payload'] - database_config = config() - try: - with psycopg2.connect(**database_config) as conn: - postsqldb.LoginsTable.update_tuple(conn, payload) - except Exception as error: - conn.rollback() - return jsonify({'error': True, 'message': error}) + administration_database.updateLoginsTuple(payload) return jsonify({'error': False, 'message': f"User was Added Successfully."}) return jsonify({'error': True, 'message': f"These was an error with adding this user."}) +# Added to Database @admin_api.route('/api/user/postEditLoginPassword', methods=["POST"]) def postEditLoginPassword(): if request.method == "POST": payload = request.get_json()['payload'] - database_config = config() - try: - with psycopg2.connect(**database_config) as conn: - user = postsqldb.LoginsTable.select_tuple(conn, (payload['id'],)) - if hashlib.sha256(payload['current_password'].encode()).hexdigest() != user['password']: - return jsonify({'error': True, 'message': "The provided current password is incorrect"}) - payload['update']['password'] = hashlib.sha256(payload['update']['password'].encode()).hexdigest() - postsqldb.LoginsTable.update_tuple(conn, payload) - except Exception as error: - conn.rollback() - return jsonify({'error': True, 'message': error}) + user = administration_database.selectLoginsTuple((payload['id'],)) + if hashlib.sha256(payload['current_password'].encode()).hexdigest() != user['password']: + return jsonify({'error': True, 'message': "The provided current password is incorrect"}) + payload['update']['password'] = hashlib.sha256(payload['update']['password'].encode()).hexdigest() + administration_database.updateLoginsTuple(payload) return jsonify({'error': False, 'message': f"Password was changed successfully."}) return jsonify({'error': True, 'message': f"These was an error with updating this Users password."}) diff --git a/application/administration/administration_database.py b/application/administration/administration_database.py index 0fc77fc..2c98eaf 100644 --- a/application/administration/administration_database.py +++ b/application/administration/administration_database.py @@ -343,6 +343,37 @@ def insertRolesTuple(payload, convert=True, conn=None): except Exception as error: raise postsqldb.DatabaseError(error, payload, sql) +def insertLoginsTuple(payload, convert=True, conn=None): + """payload (tuple): (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)""" + login = () + self_conn = False + with open(f"application/administration/sql/insertLoginsTupleFull.sql", "r+") as file: + sql = file.read() + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = True + self_conn = True + + with conn.cursor() as cur: + cur.execute(sql, payload) + rows = cur.fetchone() + if rows and convert: + login = postsqldb.tupleDictionaryFactory(cur.description, rows) + elif rows and not convert: + login = rows + + if self_conn: + conn.commit() + conn.close() + + return login + except Exception as error: + raise postsqldb.DatabaseError(error, payload, sql) + def insertZonesTuple(site, payload, convert=True, conn=None): """ payload (tuple): (name[str],) """ zone = () @@ -572,6 +603,67 @@ def updateUsersRoles(payload, convert=True, conn=None): except Exception as error: raise error +def updateRolesTuple(payload, convert=True, conn=None): + """ payload (dict): {'id': row_id, 'update': {... column_to_update: value_to_update_to...}""" + updated = () + self_conn = False + set_clause, values = postsqldb.updateStringFactory(payload['update']) + values.append(payload['id']) + sql = f"UPDATE roles SET {set_clause} WHERE id=%s RETURNING *;" + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = True + self_conn = True + + with conn.cursor() as cur: + cur.execute(sql, values) + rows = cur.fetchone() + if rows and convert: + updated = postsqldb.tupleDictionaryFactory(cur.description, rows) + elif rows and not convert: + updated = rows + + if self_conn: + conn.commit() + conn.close() + + return updated + except Exception as error: + raise postsqldb.DatabaseError(error, payload, sql) + +def updateLoginsTuple(payload, convert=True, conn=None): + """ payload (dict): {'id': row_id, 'update': {... column_to_update: value_to_update_to...}} """ + updated = () + self_conn = False + set_clause, values = postsqldb.updateStringFactory(payload['update']) + values.append(payload['id']) + sql = f"UPDATE logins SET {set_clause} WHERE id=%s RETURNING *;" + try: + if not conn: + database_config = config.config() + conn = psycopg2.connect(**database_config) + conn.autocommit = True + self_conn = True + + with conn.cursor() as cur: + cur.execute(sql, values) + rows = cur.fetchone() + if rows and convert: + updated = postsqldb.tupleDictionaryFactory(cur.description, rows) + elif rows and not convert: + updated = rows + + if self_conn: + conn.commit() + conn.close() + + return updated + + except Exception as error: + raise postsqldb.DatabaseError(error, payload, sql) + def createTable(site, table, conn=None): self_conn = False with open(f"application/administration/sql/CREATE/{table}.sql", 'r') as file: diff --git a/application/administration/sql/insertLoginsTupleFull.sql b/application/administration/sql/insertLoginsTupleFull.sql new file mode 100644 index 0000000..8103c19 --- /dev/null +++ b/application/administration/sql/insertLoginsTupleFull.sql @@ -0,0 +1,6 @@ +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 diff --git a/application/administration/templates/role.html b/application/administration/templates/role.html index c316e87..bd11a0f 100644 --- a/application/administration/templates/role.html +++ b/application/administration/templates/role.html @@ -132,7 +132,7 @@ site_id: document.getElementById('site_id').value, } - const response = await fetch(`/admin/role/postAddRole`, { + const response = await fetch(`/admin/api/role/postAddRole`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -150,7 +150,7 @@ update: {role_name: document.getElementById('role_name').value, role_description: document.getElementById('role_description').value} } - const response = await fetch(`/admin/role/postEditRole`, { + const response = await fetch(`/admin/api/role/postEditRole`, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/application/administration/templates/user.html b/application/administration/templates/user.html index 383b730..8647e0a 100644 --- a/application/administration/templates/user.html +++ b/application/administration/templates/user.html @@ -256,7 +256,7 @@ row_type: document.getElementById('login_type').value, } - const response = await fetch(`/admin/user/postAddLogin`, { + const response = await fetch(`/admin/api/user/postAddLogin`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -276,6 +276,7 @@ timeout: 5000 }); } else { + console.log(data.user) window.location.href = `/admin/user/${data.user.id}` } } @@ -287,7 +288,7 @@ update: {password: document.getElementById('old_login_password_new').value} } - const response = await fetch(`/admin/user/postEditLoginPassword`, { + const response = await fetch(`/admin/api/user/postEditLoginPassword`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -335,7 +336,7 @@ } } - const response = await fetch(`/admin/user/postEditLogin`, { + const response = await fetch(`/admin/api/user/postEditLogin`, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/database.log b/database.log index f0ad305..e9ad79b 100644 --- a/database.log +++ b/database.log @@ -2005,4 +2005,10 @@ expires TIMESTAMP, vendor INTEGER DEFAULT 0 );, - sql='cost_layers') \ No newline at end of file + sql='cost_layers') +2025-08-02 18:37:44.167516 --- ERROR --- DatabaseError(message='duplicate key value violates unique constraint "roles_role_name_site_id_key"DETAIL: Key (role_name, site_id)=(User, 4) already exists.', + payload=('User', 'This is the basic role for users on the test site.', '4', '{}'), + sql='INSERT INTO roles(role_name, role_description, site_id, flags) VALUES (%s, %s, %s, %s) RETURNING *;') +2025-08-02 18:51:19.123988 --- ERROR --- DatabaseError(message=''int' object does not support indexing', + payload=42, + sql='WITH passed_id AS (SELECT %s AS passed_id), cte_login AS ( SELECT logins.* FROM logins WHERE logins.id = (SELECT passed_id FROM passed_id) ), cte_roles AS ( SELECT roles.*, row_to_json(sites.*) AS site FROM roles LEFT JOIN sites ON sites.id = roles.site_id WHERE roles.id = ANY(SELECT unnest(site_roles) FROM cte_login) )SELECT login.*, (SELECT COALESCE(array_agg(row_to_json(r)), '{}') FROM cte_roles r) AS site_rolesFROM cte_login login;') \ No newline at end of file