test
This commit is contained in:
parent
3bc3655778
commit
93e8fa3888
@ -331,6 +331,8 @@ class ShoppingListPayload:
|
|||||||
self.type
|
self.type
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# DONE
|
||||||
@dataclass
|
@dataclass
|
||||||
class SitePayload:
|
class SitePayload:
|
||||||
site_name: str
|
site_name: str
|
||||||
@ -357,6 +359,7 @@ class SitePayload:
|
|||||||
self.default_primary_location
|
self.default_primary_location
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#DONE
|
||||||
@dataclass
|
@dataclass
|
||||||
class RolePayload:
|
class RolePayload:
|
||||||
role_name:str
|
role_name:str
|
||||||
@ -408,7 +411,8 @@ class SiteManager:
|
|||||||
"shopping_lists",
|
"shopping_lists",
|
||||||
"shopping_list_items",
|
"shopping_list_items",
|
||||||
"item_locations",
|
"item_locations",
|
||||||
"conversions"
|
"conversions",
|
||||||
|
"sku_prefix"
|
||||||
]
|
]
|
||||||
self.drop_order = [
|
self.drop_order = [
|
||||||
"item_info",
|
"item_info",
|
||||||
@ -431,5 +435,6 @@ class SiteManager:
|
|||||||
"shopping_list_items",
|
"shopping_list_items",
|
||||||
"shopping_lists",
|
"shopping_lists",
|
||||||
"item_locations",
|
"item_locations",
|
||||||
"conversions"
|
"conversions",
|
||||||
|
"sku_prefix"
|
||||||
]
|
]
|
||||||
Binary file not shown.
BIN
__pycache__/admin_api.cpython-312.pyc
Normal file
BIN
__pycache__/admin_api.cpython-312.pyc
Normal file
Binary file not shown.
BIN
__pycache__/api_admin.cpython-312.pyc
Normal file
BIN
__pycache__/api_admin.cpython-312.pyc
Normal file
Binary file not shown.
BIN
__pycache__/database_admin.cpython-312.pyc
Normal file
BIN
__pycache__/database_admin.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
122
admin.py
122
admin.py
@ -1,122 +0,0 @@
|
|||||||
from flask import Blueprint, request, render_template, redirect, session, url_for, send_file, jsonify, Response
|
|
||||||
import psycopg2, math, json, datetime, main, copy, requests
|
|
||||||
from config import config, sites_config
|
|
||||||
from main import unfoldCostLayers, get_sites, get_roles, create_site_secondary, getUser
|
|
||||||
from manage import create
|
|
||||||
|
|
||||||
admin = Blueprint('admin_api', __name__)
|
|
||||||
|
|
||||||
@admin.route("/admin/getSites")
|
|
||||||
def getSites():
|
|
||||||
sites = get_sites(session.get('user')[13])
|
|
||||||
return jsonify(sites=sites)
|
|
||||||
|
|
||||||
@admin.route("/getRoles")
|
|
||||||
def getRoles():
|
|
||||||
sites_roles = {}
|
|
||||||
sites = get_sites(session.get('user')[13])
|
|
||||||
for site in sites:
|
|
||||||
site_roles = get_roles(site_id=site[0])
|
|
||||||
sites_roles[site[1]] = site_roles
|
|
||||||
return jsonify(sites=sites_roles)
|
|
||||||
|
|
||||||
@admin.route("/admin/getUsers", methods=["POST"])
|
|
||||||
def getUsers():
|
|
||||||
if request.method == "POST":
|
|
||||||
page = request.get_json()['page']
|
|
||||||
limit = request.get_json()['limit']
|
|
||||||
offset = (page - 1) * limit
|
|
||||||
|
|
||||||
database_config = config()
|
|
||||||
with psycopg2.connect(**database_config) as conn:
|
|
||||||
try:
|
|
||||||
with conn.cursor() as cur:
|
|
||||||
sql = f"SELECT * FROM logins LIMIT %s OFFSET %s;"
|
|
||||||
cur.execute(sql, (limit, offset))
|
|
||||||
users = cur.fetchall()
|
|
||||||
cur.execute("SELECT COUNT(*) FROM main_items;")
|
|
||||||
count = cur.fetchone()[0]
|
|
||||||
return jsonify(users=users, endpage=math.ceil(count/limit))
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
print(error)
|
|
||||||
conn.rollback()
|
|
||||||
return jsonify(message="FAILED")
|
|
||||||
return jsonify(message="FAILED")
|
|
||||||
|
|
||||||
|
|
||||||
@admin.route("/admin/editRole/<id>")
|
|
||||||
def getRole(id):
|
|
||||||
database_config = config()
|
|
||||||
with psycopg2.connect(**database_config) as conn:
|
|
||||||
try:
|
|
||||||
with conn.cursor() as cur:
|
|
||||||
sql = f"SELECT * FROM roles LEFT JOIN sites ON sites.id = roles.site_id WHERE roles.id = %s;"
|
|
||||||
cur.execute(sql, (id, ))
|
|
||||||
role = cur.fetchone()
|
|
||||||
return render_template("admin/role.html", role=role, proto={'referrer': request.referrer})
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
print(error)
|
|
||||||
conn.rollback()
|
|
||||||
return jsonify(message="FAILED")
|
|
||||||
|
|
||||||
@admin.route("/addRole", methods=["POST"])
|
|
||||||
def addRole():
|
|
||||||
if request.method == "POST":
|
|
||||||
role_name = request.get_json()['role_name']
|
|
||||||
role_description = request.get_json()['role_description']
|
|
||||||
site_id = request.get_json()['site_id']
|
|
||||||
|
|
||||||
|
|
||||||
sql = f"INSERT INTO roles (role_name, role_description, site_id) VALUES (%s, %s, %s);"
|
|
||||||
print(role_name, role_description, site_id)
|
|
||||||
|
|
||||||
|
|
||||||
database_config = config()
|
|
||||||
with psycopg2.connect(**database_config) as conn:
|
|
||||||
try:
|
|
||||||
with conn.cursor() as cur:
|
|
||||||
data = (role_name, role_description, site_id)
|
|
||||||
cur.execute(sql, data)
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
print(error)
|
|
||||||
conn.rollback()
|
|
||||||
return jsonify(message="FAILED")
|
|
||||||
|
|
||||||
return jsonify(message="SUCCESS")
|
|
||||||
|
|
||||||
return jsonify(message="FAILED")
|
|
||||||
|
|
||||||
@admin.route("/deleteRole", methods=["POST"])
|
|
||||||
def deleteRole():
|
|
||||||
if request.method == "POST":
|
|
||||||
role_id = request.get_json()['role_id']
|
|
||||||
database_config = config()
|
|
||||||
with psycopg2.connect(**database_config) as conn:
|
|
||||||
try:
|
|
||||||
with conn.cursor() as cur:
|
|
||||||
sql = f"DELETE FROM roles WHERE roles.id = %s;"
|
|
||||||
cur.execute(sql, (role_id, ))
|
|
||||||
return jsonify(message="Role Deleted!")
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
print(error)
|
|
||||||
conn.rollback()
|
|
||||||
return jsonify(message=error)
|
|
||||||
return jsonify(message="FAILED")
|
|
||||||
|
|
||||||
@admin.route("/addSite", methods=["POST"])
|
|
||||||
async def addSite():
|
|
||||||
if request.method == "POST":
|
|
||||||
site_name = request.get_json()['site_name']
|
|
||||||
site_description = request.get_json()['site_description']
|
|
||||||
default_zone = request.get_json()["default_zone"]
|
|
||||||
default_location = request.get_json()['default_location']
|
|
||||||
username = session.get('user')[1]
|
|
||||||
user_id = session.get('user')[0]
|
|
||||||
|
|
||||||
create(site_name, username, default_zone, default_location)
|
|
||||||
result = await create_site_secondary(site_name, user_id, default_zone, default_location, default_location, site_description)
|
|
||||||
|
|
||||||
if result:
|
|
||||||
return jsonify(message="Success!")
|
|
||||||
|
|
||||||
return jsonify(message="Failed!")
|
|
||||||
263
api_admin.py
Normal file
263
api_admin.py
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
from flask import Blueprint, request, render_template, redirect, session, url_for, send_file, jsonify, Response
|
||||||
|
import psycopg2, math, json, datetime, main, copy, requests
|
||||||
|
from config import config, sites_config
|
||||||
|
from main import unfoldCostLayers, get_sites, get_roles, create_site_secondary, getUser
|
||||||
|
from manage import create
|
||||||
|
from user_api import login_required
|
||||||
|
import postsqldb, process, hashlib, database_admin
|
||||||
|
|
||||||
|
|
||||||
|
admin_api = Blueprint('admin_api', __name__)
|
||||||
|
|
||||||
|
@admin_api.route('/admin')
|
||||||
|
def admin_index():
|
||||||
|
sites = [site[1] for site in main.get_sites(session['user']['sites'])]
|
||||||
|
return render_template("admin/index.html",
|
||||||
|
current_site=session['selected_site'],
|
||||||
|
sites=sites)
|
||||||
|
|
||||||
|
@admin_api.route('/admin/site/<id>')
|
||||||
|
@login_required
|
||||||
|
def adminSites(id):
|
||||||
|
if id == "new":
|
||||||
|
new_site = postsqldb.SitesTable.Payload(
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
session['user_id']
|
||||||
|
)
|
||||||
|
return render_template("admin/site.html", site=new_site.get_dictionary())
|
||||||
|
else:
|
||||||
|
database_config = config()
|
||||||
|
with psycopg2.connect(**database_config) as conn:
|
||||||
|
site = postsqldb.SitesTable.select_tuple(conn, (id,))
|
||||||
|
return render_template('admin/site.html', site=site)
|
||||||
|
|
||||||
|
@admin_api.route('/admin/role/<id>')
|
||||||
|
@login_required
|
||||||
|
def adminRoles(id):
|
||||||
|
database_config = config()
|
||||||
|
with psycopg2.connect(**database_config) as conn:
|
||||||
|
sites = postsqldb.SitesTable.selectTuples(conn)
|
||||||
|
if id == "new":
|
||||||
|
new_role = postsqldb.RolesTable.Payload(
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
0
|
||||||
|
)
|
||||||
|
return render_template("admin/role.html", role=new_role.get_dictionary(), sites=sites)
|
||||||
|
else:
|
||||||
|
role = postsqldb.RolesTable.select_tuple(conn, (id,))
|
||||||
|
return render_template('admin/role.html', role=role, sites=sites)
|
||||||
|
|
||||||
|
@admin_api.route('/admin/user/<id>')
|
||||||
|
@login_required
|
||||||
|
def adminUser(id):
|
||||||
|
database_config = config()
|
||||||
|
with psycopg2.connect(**database_config) as conn:
|
||||||
|
if id == "new":
|
||||||
|
new_user = postsqldb.LoginsTable.Payload("", "", "", "")
|
||||||
|
return render_template("admin/user.html", user=new_user.get_dictionary())
|
||||||
|
else:
|
||||||
|
user = database_admin.selectLoginsUser(int(id))
|
||||||
|
return render_template('admin/user.html', user=user)
|
||||||
|
|
||||||
|
@admin_api.route('/admin/getSites', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def getSites():
|
||||||
|
if request.method == "GET":
|
||||||
|
records = []
|
||||||
|
count = 0
|
||||||
|
page = int(request.args.get('page', 1))
|
||||||
|
limit = int(request.args.get('limit', 10))
|
||||||
|
offset = (page - 1) * limit
|
||||||
|
database_config = config()
|
||||||
|
with psycopg2.connect(**database_config) as conn:
|
||||||
|
records, count = postsqldb.SitesTable.paginateTuples(conn, (limit, offset))
|
||||||
|
return jsonify({'sites': records, "end": math.ceil(count/limit), 'error':False, 'message': 'Sites Loaded Successfully!'})
|
||||||
|
return jsonify({'sites': records, "end": math.ceil(count/limit), 'error':True, 'message': 'There was a problem loading Sites!'})
|
||||||
|
|
||||||
|
@admin_api.route('/admin/getRoles', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def getRoles():
|
||||||
|
if request.method == "GET":
|
||||||
|
records = []
|
||||||
|
count = 0
|
||||||
|
page = int(request.args.get('page', 1))
|
||||||
|
limit = int(request.args.get('limit', 10))
|
||||||
|
offset = (page - 1) * limit
|
||||||
|
database_config = config()
|
||||||
|
with psycopg2.connect(**database_config) as conn:
|
||||||
|
records, count = postsqldb.RolesTable.paginate_tuples(conn, (limit, offset))
|
||||||
|
return jsonify({'roles': records, "end": math.ceil(count/limit), 'error':False, 'message': 'Roles Loaded Successfully!'})
|
||||||
|
return jsonify({'roles': records, "end": math.ceil(count/limit), 'error':True, 'message': 'There was a problem loading Roles!'})
|
||||||
|
|
||||||
|
@admin_api.route('/admin/getLogins', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def getLogins():
|
||||||
|
if request.method == "GET":
|
||||||
|
records = []
|
||||||
|
count = 0
|
||||||
|
page = int(request.args.get('page', 1))
|
||||||
|
limit = int(request.args.get('limit', 10))
|
||||||
|
offset = (page - 1) * limit
|
||||||
|
database_config = config()
|
||||||
|
with psycopg2.connect(**database_config) as conn:
|
||||||
|
records, count = postsqldb.LoginsTable.paginate_tuples(conn, (limit, offset))
|
||||||
|
return jsonify({'logins': records, "end": math.ceil(count/limit), 'error':False, 'message': 'logins Loaded Successfully!'})
|
||||||
|
return jsonify({'logins': records, "end": math.ceil(count/limit), 'error':True, 'message': 'There was a problem loading logins!'})
|
||||||
|
|
||||||
|
@admin_api.route('/admin/site/postDeleteSite', methods=["POST"])
|
||||||
|
def postDeleteSite():
|
||||||
|
if request.method == "POST":
|
||||||
|
site_id = request.get_json()['site_id']
|
||||||
|
database_config = config()
|
||||||
|
user_id = session['user_id']
|
||||||
|
try:
|
||||||
|
with psycopg2.connect(**database_config) as conn:
|
||||||
|
user = postsqldb.LoginsTable.select_tuple(conn, (user_id,))
|
||||||
|
admin_user = (user['username'], user['password'], user['email'], user['row_type'])
|
||||||
|
site = postsqldb.SitesTable.select_tuple(conn, (site_id,))
|
||||||
|
site = postsqldb.SitesTable.Manager(
|
||||||
|
site['site_name'],
|
||||||
|
admin_user,
|
||||||
|
site['default_zone'],
|
||||||
|
site['default_primary_location'],
|
||||||
|
site['site_description']
|
||||||
|
)
|
||||||
|
process.deleteSite(site_manager=site)
|
||||||
|
except Exception as error:
|
||||||
|
conn.rollback()
|
||||||
|
return jsonify({'error': True, 'message': error})
|
||||||
|
return jsonify({'error': False, 'message': f""})
|
||||||
|
return jsonify({'error': True, 'message': f""})
|
||||||
|
|
||||||
|
@admin_api.route('/admin/site/postAddSite', methods=["POST"])
|
||||||
|
def postAddSite():
|
||||||
|
if request.method == "POST":
|
||||||
|
payload = request.get_json()['payload']
|
||||||
|
database_config = config()
|
||||||
|
site_name = session['selected_site']
|
||||||
|
user_id = session['user_id']
|
||||||
|
print(payload)
|
||||||
|
try:
|
||||||
|
with psycopg2.connect(**database_config) as conn:
|
||||||
|
user = postsqldb.LoginsTable.select_tuple(conn, (user_id,))
|
||||||
|
admin_user = (user['username'], user['password'], user['email'], user['row_type'])
|
||||||
|
site = postsqldb.SitesTable.Manager(
|
||||||
|
payload['site_name'],
|
||||||
|
admin_user,
|
||||||
|
payload['default_zone'],
|
||||||
|
payload['default_primary_location'],
|
||||||
|
payload['site_description']
|
||||||
|
)
|
||||||
|
process.addSite(site_manager=site)
|
||||||
|
|
||||||
|
except Exception as error:
|
||||||
|
conn.rollback()
|
||||||
|
return jsonify({'error': True, 'message': error})
|
||||||
|
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}."})
|
||||||
|
|
||||||
|
@admin_api.route('/admin/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})
|
||||||
|
return jsonify({'error': False, 'message': f"Site updated."})
|
||||||
|
return jsonify({'error': True, 'message': f"These was an error with updating Site."})
|
||||||
|
|
||||||
|
@admin_api.route('/admin/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})
|
||||||
|
return jsonify({'error': False, 'message': f"Role added."})
|
||||||
|
return jsonify({'error': True, 'message': f"These was an error with adding this Role."})
|
||||||
|
|
||||||
|
@admin_api.route('/admin/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})
|
||||||
|
return jsonify({'error': False, 'message': f"Role updated."})
|
||||||
|
return jsonify({'error': True, 'message': f"These was an error with updating this Role."})
|
||||||
|
|
||||||
|
@admin_api.route('/admin/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})
|
||||||
|
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."})
|
||||||
|
|
||||||
|
@admin_api.route('/admin/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})
|
||||||
|
return jsonify({'error': False, 'message': f"User was Added Successfully."})
|
||||||
|
return jsonify({'error': True, 'message': f"These was an error with adding this user."})
|
||||||
|
|
||||||
|
@admin_api.route('/admin/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})
|
||||||
|
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."})
|
||||||
30
database.log
30
database.log
@ -1809,3 +1809,33 @@
|
|||||||
2025-04-20 09:54:33.948670 --- ERROR --- DatabaseError(message=''int' object is not iterable',
|
2025-04-20 09:54:33.948670 --- ERROR --- DatabaseError(message=''int' object is not iterable',
|
||||||
payload=(),
|
payload=(),
|
||||||
sql='SELECT * FROM sites')
|
sql='SELECT * FROM sites')
|
||||||
|
2025-04-20 19:52:54.986069 --- ERROR --- DatabaseError(message='table "testsite_zones" does not exist',
|
||||||
|
payload=DROP TABLE TestSite_zones CASCADE;,
|
||||||
|
sql='zones')
|
||||||
|
2025-04-20 20:11:14.230809 --- ERROR --- DatabaseError(message='tuple index out of range',
|
||||||
|
payload=('main', ''),
|
||||||
|
sql='INSERT INTO testsite_zones(name, description, site_id) VALUES (%s, %s, %s) RETURNING *;')
|
||||||
|
2025-04-20 20:32:54.096676 --- ERROR --- DatabaseError(message='relation "testsite_sku_prefix" does not existLINE 1: SELECT * FROM TestSite_sku_prefix LIMIT 25 OFFSET 0; ^',
|
||||||
|
payload=(25, 0),
|
||||||
|
sql='SELECT * FROM TestSite_sku_prefix LIMIT %s OFFSET %s;')
|
||||||
|
2025-04-20 20:33:30.514244 --- ERROR --- DatabaseError(message='relation "testsite_item_locations" does not existLINE 3: FROM TestSite_item_locations mil ^',
|
||||||
|
payload=['', 50, 0],
|
||||||
|
sql='WITH sum_cte AS ( SELECT mi.id, SUM(mil.quantity_on_hand)::FLOAT8 AS total_sum FROM TestSite_item_locations mil JOIN TestSite_items mi ON mil.part_id = mi.id GROUP BY mi.id )SELECT TestSite_items.*, row_to_json(TestSite_item_info.*) as item_info, sum_cte.total_sum as total_qoh, (SELECT COALESCE(row_to_json(u), '{}') FROM units as u WHERE u.id=TestSite_item_info.uom) as uomFROM TestSite_itemsLEFT JOIN sum_cte ON TestSite_items.id = sum_cte.idLEFT JOIN TestSite_item_info ON TestSite_items.item_info_id = TestSite_item_info.idWHERE TestSite_items.search_string LIKE '%%' || %s || '%%'ORDER BY TestSite_items.id ASCLIMIT %s OFFSET %s;')
|
||||||
|
2025-04-20 20:34:46.464320 --- ERROR --- DatabaseError(message='relation "testsite_item_locations" does not existLINE 3: FROM TestSite_item_locations mil ^',
|
||||||
|
payload=['', 50, 0],
|
||||||
|
sql='WITH sum_cte AS ( SELECT mi.id, SUM(mil.quantity_on_hand)::FLOAT8 AS total_sum FROM TestSite_item_locations mil JOIN TestSite_items mi ON mil.part_id = mi.id GROUP BY mi.id )SELECT TestSite_items.*, row_to_json(TestSite_item_info.*) as item_info, sum_cte.total_sum as total_qoh, (SELECT COALESCE(row_to_json(u), '{}') FROM units as u WHERE u.id=TestSite_item_info.uom) as uomFROM TestSite_itemsLEFT JOIN sum_cte ON TestSite_items.id = sum_cte.idLEFT JOIN TestSite_item_info ON TestSite_items.item_info_id = TestSite_item_info.idWHERE TestSite_items.search_string LIKE '%%' || %s || '%%'ORDER BY TestSite_items.id ASCLIMIT %s OFFSET %s;')
|
||||||
|
2025-04-20 21:10:59.263962 --- ERROR --- DatabaseError(message='relation "testsite_item_locations" does not existLINE 3: FROM TestSite_item_locations mil ^',
|
||||||
|
payload=['', 50, 0],
|
||||||
|
sql='WITH sum_cte AS ( SELECT mi.id, SUM(mil.quantity_on_hand)::FLOAT8 AS total_sum FROM TestSite_item_locations mil JOIN TestSite_items mi ON mil.part_id = mi.id GROUP BY mi.id )SELECT TestSite_items.*, row_to_json(TestSite_item_info.*) as item_info, sum_cte.total_sum as total_qoh, (SELECT COALESCE(row_to_json(u), '{}') FROM units as u WHERE u.id=TestSite_item_info.uom) as uomFROM TestSite_itemsLEFT JOIN sum_cte ON TestSite_items.id = sum_cte.idLEFT JOIN TestSite_item_info ON TestSite_items.item_info_id = TestSite_item_info.idWHERE TestSite_items.search_string LIKE '%%' || %s || '%%'ORDER BY TestSite_items.id ASCLIMIT %s OFFSET %s;')
|
||||||
|
2025-04-21 14:42:32.973758 --- ERROR --- DatabaseError(message='new row for relation "logins" violates check constraint "logins_email_check"DETAIL: Failing row contains (29, 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 *;')
|
||||||
|
2025-04-21 14:43:23.959085 --- ERROR --- DatabaseError(message='new row for relation "logins" violates check constraint "logins_email_check"DETAIL: Failing row contains (30, 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 *;')
|
||||||
|
2025-04-21 14:44:16.605030 --- ERROR --- DatabaseError(message='new row for relation "logins" violates check constraint "logins_email_check"DETAIL: Failing row contains (31, 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 *;')
|
||||||
|
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 *;')
|
||||||
18
database_admin.py
Normal file
18
database_admin.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import postsqldb
|
||||||
|
import psycopg2
|
||||||
|
import config
|
||||||
|
|
||||||
|
def selectLoginsUser(login_id):
|
||||||
|
database_config = config.config()
|
||||||
|
try:
|
||||||
|
with psycopg2.connect(**database_config) as conn:
|
||||||
|
with open("sql/SELECT/admin/selectLoginsUser.sql", "r") as file:
|
||||||
|
sql = file.read()
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql, (login_id,))
|
||||||
|
user = cur.fetchone()
|
||||||
|
if user:
|
||||||
|
user = postsqldb.tupleDictionaryFactory(cur.description, user)
|
||||||
|
return user
|
||||||
|
except Exception as error:
|
||||||
|
raise postsqldb.DatabaseError(error, login_id, sql)
|
||||||
535
postsqldb.py
535
postsqldb.py
@ -1137,7 +1137,6 @@ class ZonesTable:
|
|||||||
@dataclass
|
@dataclass
|
||||||
class Payload:
|
class Payload:
|
||||||
name: str
|
name: str
|
||||||
site_id: int
|
|
||||||
description: str = ""
|
description: str = ""
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
@ -1148,7 +1147,6 @@ class ZonesTable:
|
|||||||
return (
|
return (
|
||||||
self.name,
|
self.name,
|
||||||
self.description,
|
self.description,
|
||||||
self.site_id
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -1798,6 +1796,163 @@ class CycleCountsTable:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class SitesTable:
|
class SitesTable:
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Manager:
|
||||||
|
site_name: str
|
||||||
|
admin_user: tuple
|
||||||
|
default_zone: int
|
||||||
|
default_location: int
|
||||||
|
description: str
|
||||||
|
create_order: list = field(init=False)
|
||||||
|
drop_order: list = field(init=False)
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
self.create_order = [
|
||||||
|
"logins",
|
||||||
|
"sites",
|
||||||
|
"roles",
|
||||||
|
"units",
|
||||||
|
"cost_layers",
|
||||||
|
"linked_items",
|
||||||
|
"brands",
|
||||||
|
"food_info",
|
||||||
|
"item_info",
|
||||||
|
"zones",
|
||||||
|
"locations",
|
||||||
|
"logistics_info",
|
||||||
|
"transactions",
|
||||||
|
"item",
|
||||||
|
"vendors",
|
||||||
|
"groups",
|
||||||
|
"group_items",
|
||||||
|
"receipts",
|
||||||
|
"receipt_items",
|
||||||
|
"recipes",
|
||||||
|
"recipe_items",
|
||||||
|
"shopping_lists",
|
||||||
|
"shopping_list_items",
|
||||||
|
"item_locations",
|
||||||
|
"conversions"
|
||||||
|
]
|
||||||
|
self.drop_order = [
|
||||||
|
"item_info",
|
||||||
|
"items",
|
||||||
|
"cost_layers",
|
||||||
|
"linked_items",
|
||||||
|
"transactions",
|
||||||
|
"brands",
|
||||||
|
"food_info",
|
||||||
|
"logistics_info",
|
||||||
|
"zones",
|
||||||
|
"locations",
|
||||||
|
"vendors",
|
||||||
|
"group_items",
|
||||||
|
"groups",
|
||||||
|
"receipt_items",
|
||||||
|
"receipts",
|
||||||
|
"recipe_items",
|
||||||
|
"recipes",
|
||||||
|
"shopping_list_items",
|
||||||
|
"shopping_lists",
|
||||||
|
"item_locations",
|
||||||
|
"conversions"
|
||||||
|
]
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Payload:
|
||||||
|
site_name: str
|
||||||
|
site_description: str
|
||||||
|
site_owner_id: int
|
||||||
|
default_zone: str = None
|
||||||
|
default_auto_issue_location: str = None
|
||||||
|
default_primary_location: str = None
|
||||||
|
creation_date: datetime.datetime = field(init=False)
|
||||||
|
flags: dict = field(default_factory=dict)
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
self.creation_date = datetime.datetime.now()
|
||||||
|
|
||||||
|
def payload(self):
|
||||||
|
return (
|
||||||
|
self.site_name,
|
||||||
|
self.site_description,
|
||||||
|
self.creation_date,
|
||||||
|
self.site_owner_id,
|
||||||
|
json.dumps(self.flags),
|
||||||
|
self.default_zone,
|
||||||
|
self.default_auto_issue_location,
|
||||||
|
self.default_primary_location
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_dictionary(self):
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def insert_tuple(self, conn, payload:tuple, convert=True):
|
||||||
|
"""inserts payload into sites table
|
||||||
|
|
||||||
|
Args:
|
||||||
|
conn (_T_connector@connect): Postgresql Connector
|
||||||
|
payload (tuple): (site_name[str], site_description[str], creation_date[timestamp], site_owner_id[int],
|
||||||
|
flags[dict], default_zone[str], default_auto_issue_location[str], default_primary_location[str])
|
||||||
|
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
DatabaseError:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple or dict: inserted tuple
|
||||||
|
"""
|
||||||
|
site_tuple = ()
|
||||||
|
with open(f"sql/INSERT/insertSitesTuple.sql", "r+") as file:
|
||||||
|
sql = file.read()
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql, payload)
|
||||||
|
rows = cur.fetchone()
|
||||||
|
if rows and convert:
|
||||||
|
site_tuple = tupleDictionaryFactory(cur.description, rows)
|
||||||
|
elif rows and not convert:
|
||||||
|
site_tuple = rows
|
||||||
|
except Exception as error:
|
||||||
|
raise DatabaseError(error, payload, sql)
|
||||||
|
return site_tuple
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def paginateTuples(self, conn, payload: tuple, convert=True):
|
||||||
|
"""_summary_
|
||||||
|
|
||||||
|
Args:
|
||||||
|
conn (_type_): _description_
|
||||||
|
payload (tuple): (limit, offset)
|
||||||
|
convert (bool, optional): _description_. Defaults to True.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
DatabaseError: _description_
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
_type_: _description_
|
||||||
|
"""
|
||||||
|
recordsets = []
|
||||||
|
count = 0
|
||||||
|
sql = f"SELECT * FROM sites LIMIT %s OFFSET %s;"
|
||||||
|
sql_count = f"SELECT COUNT(*) FROM sites;"
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql, payload)
|
||||||
|
rows = cur.fetchall()
|
||||||
|
if rows and convert:
|
||||||
|
recordsets = [tupleDictionaryFactory(cur.description, row) for row in rows]
|
||||||
|
elif rows and not convert:
|
||||||
|
recordsets = rows
|
||||||
|
cur.execute(sql_count)
|
||||||
|
count = cur.fetchone()[0]
|
||||||
|
except Exception as error:
|
||||||
|
raise DatabaseError(error, (), sql)
|
||||||
|
return recordsets, count
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def selectTuples(self, conn, convert=True):
|
def selectTuples(self, conn, convert=True):
|
||||||
recordsets = []
|
recordsets = []
|
||||||
@ -1813,3 +1968,379 @@ class SitesTable:
|
|||||||
except Exception as error:
|
except Exception as error:
|
||||||
raise DatabaseError(error, (), sql)
|
raise DatabaseError(error, (), sql)
|
||||||
return recordsets
|
return recordsets
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def select_tuple(self, conn, payload:tuple, convert=True):
|
||||||
|
record = []
|
||||||
|
sql = f"SELECT * FROM sites WHERE id=%s;"
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql, payload)
|
||||||
|
rows = cur.fetchone()
|
||||||
|
if rows and convert:
|
||||||
|
record = tupleDictionaryFactory(cur.description, rows)
|
||||||
|
elif rows and not convert:
|
||||||
|
record = rows
|
||||||
|
except Exception as error:
|
||||||
|
raise DatabaseError(error, (), sql)
|
||||||
|
return record
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update_tuple(self, conn, payload, convert=True):
|
||||||
|
"""_summary_
|
||||||
|
|
||||||
|
Args:
|
||||||
|
conn (_T_connector@connect): Postgresql Connector
|
||||||
|
site (str):
|
||||||
|
table (str):
|
||||||
|
payload (dict): {'id': row_id, 'update': {... column_to_update: value_to_update_to...}}
|
||||||
|
convert (bool, optional): determines if to return tuple as dictionary. Defaults to False.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
DatabaseError:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple or dict: updated tuple
|
||||||
|
"""
|
||||||
|
updated = ()
|
||||||
|
|
||||||
|
set_clause, values = updateStringFactory(payload['update'])
|
||||||
|
values.append(payload['id'])
|
||||||
|
sql = f"UPDATE sites SET {set_clause} WHERE id=%s RETURNING *;"
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql, values)
|
||||||
|
rows = cur.fetchone()
|
||||||
|
if rows and convert:
|
||||||
|
updated = tupleDictionaryFactory(cur.description, rows)
|
||||||
|
elif rows and not convert:
|
||||||
|
updated = rows
|
||||||
|
except Exception as error:
|
||||||
|
raise DatabaseError(error, payload, sql)
|
||||||
|
return updated
|
||||||
|
|
||||||
|
class RolesTable:
|
||||||
|
@dataclass
|
||||||
|
class Payload:
|
||||||
|
role_name:str
|
||||||
|
role_description:str
|
||||||
|
site_id: int
|
||||||
|
flags: dict = field(default_factory=dict)
|
||||||
|
|
||||||
|
def payload(self):
|
||||||
|
return (
|
||||||
|
self.role_name,
|
||||||
|
self.role_description,
|
||||||
|
self.site_id,
|
||||||
|
json.dumps(self.flags)
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_dictionary(self):
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def insert_tuple(self, conn, payload:tuple, convert=True):
|
||||||
|
"""inserts payload into roles table
|
||||||
|
|
||||||
|
Args:
|
||||||
|
conn (_T_connector@connect): Postgresql Connector
|
||||||
|
payload (tuple): (role_name[str], role_description[str], site_id[int], flags[jsonb])
|
||||||
|
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
DatabaseError:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple or dict: inserted tuple
|
||||||
|
"""
|
||||||
|
role_tuple = ()
|
||||||
|
with open(f"sql/INSERT/insertRolesTuple.sql", "r+") as file:
|
||||||
|
sql = file.read()
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql, payload)
|
||||||
|
rows = cur.fetchone()
|
||||||
|
if rows and convert:
|
||||||
|
role_tuple = tupleDictionaryFactory(cur.description, rows)
|
||||||
|
elif rows and not convert:
|
||||||
|
role_tuple = rows
|
||||||
|
except Exception as error:
|
||||||
|
raise DatabaseError(error, payload, sql)
|
||||||
|
return role_tuple
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def select_tuple(self, conn, payload:tuple, convert=True):
|
||||||
|
record = []
|
||||||
|
sql = f"SELECT roles.*, row_to_json(sites.*) as site FROM roles LEFT JOIN sites ON sites.id = roles.site_id WHERE roles.id=%s;"
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql, payload)
|
||||||
|
rows = cur.fetchone()
|
||||||
|
if rows and convert:
|
||||||
|
record = tupleDictionaryFactory(cur.description, rows)
|
||||||
|
elif rows and not convert:
|
||||||
|
record = rows
|
||||||
|
except Exception as error:
|
||||||
|
raise DatabaseError(error, (), sql)
|
||||||
|
return record
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def paginate_tuples(self, conn, payload:tuple, convert=True):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
conn (_T_connector@connect): Postgresql Connector
|
||||||
|
payload (tuple): (limit, offset)
|
||||||
|
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
DatabaseError:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple or dict: inserted tuple
|
||||||
|
"""
|
||||||
|
recordset = []
|
||||||
|
|
||||||
|
sql = f"SELECT roles.*, row_to_json(sites.*) as site FROM roles LEFT JOIN sites ON sites.id = roles.site_id LIMIT %s OFFSET %s;"
|
||||||
|
sql_count = f"SELECT COUNT(*) FROM roles;"
|
||||||
|
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql, payload)
|
||||||
|
rows = cur.fetchall()
|
||||||
|
if rows and convert:
|
||||||
|
recordset = [tupleDictionaryFactory(cur.description, row) for row in rows]
|
||||||
|
elif rows and not convert:
|
||||||
|
recordset = rows
|
||||||
|
|
||||||
|
|
||||||
|
cur.execute(sql_count)
|
||||||
|
count = cur.fetchone()[0]
|
||||||
|
except Exception as error:
|
||||||
|
raise DatabaseError(error, payload, sql)
|
||||||
|
return recordset, count
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update_tuple(self, conn, payload, convert=True):
|
||||||
|
"""_summary_
|
||||||
|
|
||||||
|
Args:
|
||||||
|
conn (_T_connector@connect): Postgresql Connector
|
||||||
|
site (str):
|
||||||
|
table (str):
|
||||||
|
payload (dict): {'id': row_id, 'update': {... column_to_update: value_to_update_to...}}
|
||||||
|
convert (bool, optional): determines if to return tuple as dictionary. Defaults to False.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
DatabaseError:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple or dict: updated tuple
|
||||||
|
"""
|
||||||
|
updated = ()
|
||||||
|
|
||||||
|
set_clause, values = updateStringFactory(payload['update'])
|
||||||
|
values.append(payload['id'])
|
||||||
|
sql = f"UPDATE roles SET {set_clause} WHERE id=%s RETURNING *;"
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql, values)
|
||||||
|
rows = cur.fetchone()
|
||||||
|
if rows and convert:
|
||||||
|
updated = tupleDictionaryFactory(cur.description, rows)
|
||||||
|
elif rows and not convert:
|
||||||
|
updated = rows
|
||||||
|
except Exception as error:
|
||||||
|
raise DatabaseError(error, payload, sql)
|
||||||
|
return updated
|
||||||
|
|
||||||
|
class LoginsTable:
|
||||||
|
@dataclass
|
||||||
|
class Payload:
|
||||||
|
username:str
|
||||||
|
password:str
|
||||||
|
email: str
|
||||||
|
row_type: str
|
||||||
|
system_admin: bool = False
|
||||||
|
flags: dict = field(default_factory=dict)
|
||||||
|
favorites: dict = field(default_factory=dict)
|
||||||
|
unseen_pantry_items: list = field(default_factory=list)
|
||||||
|
unseen_groups: list = field(default_factory=list)
|
||||||
|
unseen_shopping_lists: list = field(default_factory=list)
|
||||||
|
unseen_recipes: list = field(default_factory=list)
|
||||||
|
seen_pantry_items: list = field(default_factory=list)
|
||||||
|
seen_groups: list = field(default_factory=list)
|
||||||
|
seen_shopping_lists: list = field(default_factory=list)
|
||||||
|
seen_recipes: list = field(default_factory=list)
|
||||||
|
sites: list = field(default_factory=list)
|
||||||
|
site_roles: list = field(default_factory=list)
|
||||||
|
|
||||||
|
def payload(self):
|
||||||
|
return (
|
||||||
|
self.username,
|
||||||
|
self.password,
|
||||||
|
self.email,
|
||||||
|
json.dumps(self.favorites),
|
||||||
|
lst2pgarr(self.unseen_pantry_items),
|
||||||
|
lst2pgarr(self.unseen_groups),
|
||||||
|
lst2pgarr(self.unseen_shopping_lists),
|
||||||
|
lst2pgarr(self.unseen_recipes),
|
||||||
|
lst2pgarr(self.seen_pantry_items),
|
||||||
|
lst2pgarr(self.seen_groups),
|
||||||
|
lst2pgarr(self.seen_shopping_lists),
|
||||||
|
lst2pgarr(self.seen_recipes),
|
||||||
|
lst2pgarr(self.sites),
|
||||||
|
lst2pgarr(self.site_roles),
|
||||||
|
self.system_admin,
|
||||||
|
json.dumps(self.flags),
|
||||||
|
self.row_type
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_dictionary(self):
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def insert_tuple(self, conn, payload:tuple, convert=True):
|
||||||
|
"""inserts payload into roles table
|
||||||
|
|
||||||
|
Args:
|
||||||
|
conn (_T_connector@connect): Postgresql Connector
|
||||||
|
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)
|
||||||
|
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
DatabaseError:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple or dict: inserted tuple
|
||||||
|
"""
|
||||||
|
login = ()
|
||||||
|
with open(f"sql/INSERT/insertLoginsTupleTwo.sql", "r+") as file:
|
||||||
|
sql = file.read()
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql, payload)
|
||||||
|
rows = cur.fetchone()
|
||||||
|
if rows and convert:
|
||||||
|
login = tupleDictionaryFactory(cur.description, rows)
|
||||||
|
elif rows and not convert:
|
||||||
|
login = rows
|
||||||
|
except Exception as error:
|
||||||
|
raise DatabaseError(error, payload, sql)
|
||||||
|
return login
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def select_tuple(self, conn, payload:tuple, convert=True):
|
||||||
|
"""_summary_
|
||||||
|
|
||||||
|
Args:
|
||||||
|
conn (_type_): _description_
|
||||||
|
payload (tuple): (user_id,)
|
||||||
|
convert (bool, optional): _description_. Defaults to False.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
DatabaseError: _description_
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
_type_: _description_
|
||||||
|
"""
|
||||||
|
user = ()
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
sql = f"SELECT * FROM logins WHERE id=%s;"
|
||||||
|
cur.execute(sql, payload)
|
||||||
|
rows = cur.fetchone()
|
||||||
|
if rows and convert:
|
||||||
|
user = tupleDictionaryFactory(cur.description, rows)
|
||||||
|
elif rows and not convert:
|
||||||
|
user = rows
|
||||||
|
except Exception as error:
|
||||||
|
raise DatabaseError(error, payload, sql)
|
||||||
|
return user
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def paginate_tuples(self, conn, payload:tuple, convert=True):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
conn (_T_connector@connect): Postgresql Connector
|
||||||
|
payload (tuple): (limit, offset)
|
||||||
|
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
DatabaseError:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple or dict: inserted tuple
|
||||||
|
"""
|
||||||
|
recordset = []
|
||||||
|
|
||||||
|
sql = f"SELECT * FROM logins LIMIT %s OFFSET %s;"
|
||||||
|
sql_count = f"SELECT COUNT(*) FROM logins;"
|
||||||
|
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql, payload)
|
||||||
|
rows = cur.fetchall()
|
||||||
|
if rows and convert:
|
||||||
|
recordset = [tupleDictionaryFactory(cur.description, row) for row in rows]
|
||||||
|
elif rows and not convert:
|
||||||
|
recordset = rows
|
||||||
|
|
||||||
|
cur.execute(sql_count)
|
||||||
|
count = cur.fetchone()[0]
|
||||||
|
except Exception as error:
|
||||||
|
raise DatabaseError(error, payload, sql)
|
||||||
|
return recordset, count
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_washed_tuple(self, conn, payload:tuple, convert=True):
|
||||||
|
user = ()
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
sql = f"SELECT id, username, sites, site_roles, system_admin, flags FROM logins WHERE id=%s;"
|
||||||
|
cur.execute(sql, payload)
|
||||||
|
rows = cur.fetchone()
|
||||||
|
if rows and convert:
|
||||||
|
user = tupleDictionaryFactory(cur.description, rows)
|
||||||
|
elif rows and not convert:
|
||||||
|
user = rows
|
||||||
|
except Exception as error:
|
||||||
|
raise DatabaseError(error, payload, sql)
|
||||||
|
return user
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update_tuple(self, conn, payload, convert=True):
|
||||||
|
"""_summary_
|
||||||
|
|
||||||
|
Args:
|
||||||
|
conn (_T_connector@connect): Postgresql Connector
|
||||||
|
site (str):
|
||||||
|
table (str):
|
||||||
|
payload (dict): {'id': row_id, 'update': {... column_to_update: value_to_update_to...}}
|
||||||
|
convert (bool, optional): determines if to return tuple as dictionary. Defaults to False.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
DatabaseError:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple or dict: updated tuple
|
||||||
|
"""
|
||||||
|
updated = ()
|
||||||
|
|
||||||
|
set_clause, values = updateStringFactory(payload['update'])
|
||||||
|
values.append(payload['id'])
|
||||||
|
sql = f"UPDATE logins SET {set_clause} WHERE id=%s RETURNING *;"
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql, values)
|
||||||
|
rows = cur.fetchone()
|
||||||
|
if rows and convert:
|
||||||
|
updated = tupleDictionaryFactory(cur.description, rows)
|
||||||
|
elif rows and not convert:
|
||||||
|
updated = rows
|
||||||
|
except Exception as error:
|
||||||
|
raise DatabaseError(error, payload, sql)
|
||||||
|
return updated
|
||||||
|
|||||||
569
process.log
569
process.log
@ -418,4 +418,571 @@
|
|||||||
2025-04-07 18:13:10.570396 --- CAUTION --- DatabaseError(message='current transaction is aborted, commands ignored until end of transaction block', payload=('loaves', ' loaf', ' Loaf', ' A single loaf of a unit.'), sql='INSERT INTO units(plural, single, fullname, description) VALUES (%s, %s, %s, %s) RETURNING *;')
|
2025-04-07 18:13:10.570396 --- CAUTION --- DatabaseError(message='current transaction is aborted, commands ignored until end of transaction block', payload=('loaves', ' loaf', ' Loaf', ' A single loaf of a unit.'), sql='INSERT INTO units(plural, single, fullname, description) VALUES (%s, %s, %s, %s) RETURNING *;')
|
||||||
["loaves", " loaf", " Loaf", " A single loaf of a unit."]
|
["loaves", " loaf", " Loaf", " A single loaf of a unit."]
|
||||||
2025-04-07 18:13:10.579705 --- CAUTION --- DatabaseError(message='current transaction is aborted, commands ignored until end of transaction block', payload=('packs', ' pack', ' Pack', ' A Single Pack of a unit.'), sql='INSERT INTO units(plural, single, fullname, description) VALUES (%s, %s, %s, %s) RETURNING *;')
|
2025-04-07 18:13:10.579705 --- CAUTION --- DatabaseError(message='current transaction is aborted, commands ignored until end of transaction block', payload=('packs', ' pack', ' Pack', ' A Single Pack of a unit.'), sql='INSERT INTO units(plural, single, fullname, description) VALUES (%s, %s, %s, %s) RETURNING *;')
|
||||||
["packs", " pack", " Pack", " A Single Pack of a unit."]
|
["packs", " pack", " Pack", " A Single Pack of a unit."]2025-04-20 19:12:42.901904 --- INFO --- logins Created!
|
||||||
|
2025-04-20 19:12:42.907042 --- INFO --- sites Created!
|
||||||
|
2025-04-20 19:12:42.916090 --- INFO --- roles Created!
|
||||||
|
2025-04-20 19:12:42.924299 --- INFO --- units Created!
|
||||||
|
2025-04-20 19:12:42.936431 --- INFO --- cost_layers Created!
|
||||||
|
2025-04-20 19:12:42.948063 --- INFO --- linked_items Created!
|
||||||
|
2025-04-20 19:12:42.956357 --- INFO --- brands Created!
|
||||||
|
2025-04-20 19:12:42.965966 --- INFO --- food_info Created!
|
||||||
|
2025-04-20 19:12:42.978005 --- INFO --- item_info Created!
|
||||||
|
2025-04-20 19:12:42.989756 --- INFO --- zones Created!
|
||||||
|
2025-04-20 19:12:42.999753 --- INFO --- locations Created!
|
||||||
|
2025-04-20 19:12:43.010766 --- INFO --- logistics_info Created!
|
||||||
|
2025-04-20 19:12:43.020793 --- INFO --- transactions Created!
|
||||||
|
2025-04-20 19:12:43.032889 --- INFO --- item Created!
|
||||||
|
2025-04-20 19:12:43.042155 --- INFO --- vendors Created!
|
||||||
|
2025-04-20 19:12:43.052984 --- INFO --- groups Created!
|
||||||
|
2025-04-20 19:12:43.065257 --- INFO --- group_items Created!
|
||||||
|
2025-04-20 19:12:43.075250 --- INFO --- receipts Created!
|
||||||
|
2025-04-20 19:12:43.085279 --- INFO --- receipt_items Created!
|
||||||
|
2025-04-20 19:12:43.095708 --- INFO --- recipes Created!
|
||||||
|
2025-04-20 19:12:43.108432 --- INFO --- recipe_items Created!
|
||||||
|
2025-04-20 19:12:43.118248 --- INFO --- shopping_lists Created!
|
||||||
|
2025-04-20 19:12:43.130093 --- INFO --- shopping_list_items Created!
|
||||||
|
2025-04-20 19:12:43.142255 --- INFO --- item_locations Created!
|
||||||
|
2025-04-20 19:12:43.152766 --- INFO --- conversions Created!
|
||||||
|
2025-04-20 19:12:43.157756 --- INFO --- Admin User Created!
|
||||||
|
2025-04-20 19:12:43.173869 --- ERROR --- module 'MyDataclasses' has no attribute 'ZonePayload'
|
||||||
|
2025-04-20 19:13:09.179803 --- INFO --- logins Created!
|
||||||
|
2025-04-20 19:13:09.186834 --- INFO --- sites Created!
|
||||||
|
2025-04-20 19:13:09.191988 --- INFO --- roles Created!
|
||||||
|
2025-04-20 19:13:09.196031 --- INFO --- units Created!
|
||||||
|
2025-04-20 19:13:09.203142 --- INFO --- cost_layers Created!
|
||||||
|
2025-04-20 19:13:09.211122 --- INFO --- linked_items Created!
|
||||||
|
2025-04-20 19:13:09.217381 --- INFO --- brands Created!
|
||||||
|
2025-04-20 19:13:09.223367 --- INFO --- food_info Created!
|
||||||
|
2025-04-20 19:13:09.231980 --- INFO --- item_info Created!
|
||||||
|
2025-04-20 19:13:09.239962 --- INFO --- zones Created!
|
||||||
|
2025-04-20 19:13:09.247043 --- INFO --- locations Created!
|
||||||
|
2025-04-20 19:13:09.254389 --- INFO --- logistics_info Created!
|
||||||
|
2025-04-20 19:13:09.261528 --- INFO --- transactions Created!
|
||||||
|
2025-04-20 19:13:09.270171 --- INFO --- item Created!
|
||||||
|
2025-04-20 19:13:09.276674 --- INFO --- vendors Created!
|
||||||
|
2025-04-20 19:13:09.283569 --- INFO --- groups Created!
|
||||||
|
2025-04-20 19:13:09.291142 --- INFO --- group_items Created!
|
||||||
|
2025-04-20 19:13:09.300047 --- INFO --- receipts Created!
|
||||||
|
2025-04-20 19:13:09.307082 --- INFO --- receipt_items Created!
|
||||||
|
2025-04-20 19:13:09.314189 --- INFO --- recipes Created!
|
||||||
|
2025-04-20 19:13:09.323320 --- INFO --- recipe_items Created!
|
||||||
|
2025-04-20 19:13:09.331286 --- INFO --- shopping_lists Created!
|
||||||
|
2025-04-20 19:13:09.338671 --- INFO --- shopping_list_items Created!
|
||||||
|
2025-04-20 19:13:09.347666 --- INFO --- item_locations Created!
|
||||||
|
2025-04-20 19:13:09.354290 --- INFO --- conversions Created!
|
||||||
|
2025-04-20 19:13:09.358352 --- INFO --- Admin User Created!
|
||||||
|
2025-04-20 19:13:09.373201 --- ERROR --- module 'MyDataclasses' has no attribute 'ZonePayload'
|
||||||
|
2025-04-20 19:14:30.040093 --- INFO --- logins Created!
|
||||||
|
2025-04-20 19:14:30.047388 --- INFO --- sites Created!
|
||||||
|
2025-04-20 19:14:30.053137 --- INFO --- roles Created!
|
||||||
|
2025-04-20 19:14:30.058339 --- INFO --- units Created!
|
||||||
|
2025-04-20 19:14:30.066445 --- INFO --- cost_layers Created!
|
||||||
|
2025-04-20 19:14:30.074589 --- INFO --- linked_items Created!
|
||||||
|
2025-04-20 19:14:30.080372 --- INFO --- brands Created!
|
||||||
|
2025-04-20 19:14:30.088112 --- INFO --- food_info Created!
|
||||||
|
2025-04-20 19:14:30.095435 --- INFO --- item_info Created!
|
||||||
|
2025-04-20 19:14:30.104268 --- INFO --- zones Created!
|
||||||
|
2025-04-20 19:14:30.111070 --- INFO --- locations Created!
|
||||||
|
2025-04-20 19:14:30.118747 --- INFO --- logistics_info Created!
|
||||||
|
2025-04-20 19:14:30.126464 --- INFO --- transactions Created!
|
||||||
|
2025-04-20 19:14:30.136123 --- INFO --- item Created!
|
||||||
|
2025-04-20 19:14:30.142326 --- INFO --- vendors Created!
|
||||||
|
2025-04-20 19:14:30.150179 --- INFO --- groups Created!
|
||||||
|
2025-04-20 19:14:30.159002 --- INFO --- group_items Created!
|
||||||
|
2025-04-20 19:14:30.165728 --- INFO --- receipts Created!
|
||||||
|
2025-04-20 19:14:30.173195 --- INFO --- receipt_items Created!
|
||||||
|
2025-04-20 19:14:30.180307 --- INFO --- recipes Created!
|
||||||
|
2025-04-20 19:14:30.188377 --- INFO --- recipe_items Created!
|
||||||
|
2025-04-20 19:14:30.195275 --- INFO --- shopping_lists Created!
|
||||||
|
2025-04-20 19:14:30.204333 --- INFO --- shopping_list_items Created!
|
||||||
|
2025-04-20 19:14:30.212189 --- INFO --- item_locations Created!
|
||||||
|
2025-04-20 19:14:30.218980 --- INFO --- conversions Created!
|
||||||
|
2025-04-20 19:14:30.223144 --- INFO --- Admin User Created!
|
||||||
|
2025-04-20 19:14:30.237347 --- ERROR --- module 'MyDataclasses' has no attribute 'ZonePayload'
|
||||||
|
2025-04-20 19:15:37.294592 --- INFO --- logins Created!
|
||||||
|
2025-04-20 19:15:37.300684 --- INFO --- sites Created!
|
||||||
|
2025-04-20 19:15:37.306214 --- INFO --- roles Created!
|
||||||
|
2025-04-20 19:15:37.310743 --- INFO --- units Created!
|
||||||
|
2025-04-20 19:15:37.320253 --- INFO --- cost_layers Created!
|
||||||
|
2025-04-20 19:15:37.328204 --- INFO --- linked_items Created!
|
||||||
|
2025-04-20 19:15:37.334340 --- INFO --- brands Created!
|
||||||
|
2025-04-20 19:15:37.341236 --- INFO --- food_info Created!
|
||||||
|
2025-04-20 19:15:37.348720 --- INFO --- item_info Created!
|
||||||
|
2025-04-20 19:15:37.358766 --- INFO --- zones Created!
|
||||||
|
2025-04-20 19:15:37.365313 --- INFO --- locations Created!
|
||||||
|
2025-04-20 19:15:37.373227 --- INFO --- logistics_info Created!
|
||||||
|
2025-04-20 19:15:37.379296 --- INFO --- transactions Created!
|
||||||
|
2025-04-20 19:15:37.387641 --- INFO --- item Created!
|
||||||
|
2025-04-20 19:15:37.395209 --- INFO --- vendors Created!
|
||||||
|
2025-04-20 19:15:37.402427 --- INFO --- groups Created!
|
||||||
|
2025-04-20 19:15:37.410391 --- INFO --- group_items Created!
|
||||||
|
2025-04-20 19:15:37.418421 --- INFO --- receipts Created!
|
||||||
|
2025-04-20 19:15:37.425524 --- INFO --- receipt_items Created!
|
||||||
|
2025-04-20 19:15:37.432240 --- INFO --- recipes Created!
|
||||||
|
2025-04-20 19:15:37.441206 --- INFO --- recipe_items Created!
|
||||||
|
2025-04-20 19:15:37.448373 --- INFO --- shopping_lists Created!
|
||||||
|
2025-04-20 19:15:37.456919 --- INFO --- shopping_list_items Created!
|
||||||
|
2025-04-20 19:15:37.465857 --- INFO --- item_locations Created!
|
||||||
|
2025-04-20 19:15:37.471974 --- INFO --- conversions Created!
|
||||||
|
2025-04-20 19:15:37.477332 --- INFO --- Admin User Created!
|
||||||
|
2025-04-20 19:15:37.488490 --- ERROR --- module 'MyDataclasses' has no attribute 'ZonePayload'
|
||||||
|
2025-04-20 19:16:04.245426 --- INFO --- logins Created!
|
||||||
|
2025-04-20 19:16:04.252369 --- INFO --- sites Created!
|
||||||
|
2025-04-20 19:16:04.257457 --- INFO --- roles Created!
|
||||||
|
2025-04-20 19:16:04.262528 --- INFO --- units Created!
|
||||||
|
2025-04-20 19:16:04.268813 --- INFO --- cost_layers Created!
|
||||||
|
2025-04-20 19:16:04.277403 --- INFO --- linked_items Created!
|
||||||
|
2025-04-20 19:16:04.282454 --- INFO --- brands Created!
|
||||||
|
2025-04-20 19:16:04.289349 --- INFO --- food_info Created!
|
||||||
|
2025-04-20 19:16:04.297588 --- INFO --- item_info Created!
|
||||||
|
2025-04-20 19:16:04.304872 --- INFO --- zones Created!
|
||||||
|
2025-04-20 19:16:04.312242 --- INFO --- locations Created!
|
||||||
|
2025-04-20 19:16:04.320032 --- INFO --- logistics_info Created!
|
||||||
|
2025-04-20 19:16:04.327526 --- INFO --- transactions Created!
|
||||||
|
2025-04-20 19:16:04.336107 --- INFO --- item Created!
|
||||||
|
2025-04-20 19:16:04.342583 --- INFO --- vendors Created!
|
||||||
|
2025-04-20 19:16:04.350295 --- INFO --- groups Created!
|
||||||
|
2025-04-20 19:16:04.357531 --- INFO --- group_items Created!
|
||||||
|
2025-04-20 19:16:04.365626 --- INFO --- receipts Created!
|
||||||
|
2025-04-20 19:16:04.372230 --- INFO --- receipt_items Created!
|
||||||
|
2025-04-20 19:16:04.379414 --- INFO --- recipes Created!
|
||||||
|
2025-04-20 19:16:04.387527 --- INFO --- recipe_items Created!
|
||||||
|
2025-04-20 19:16:04.394398 --- INFO --- shopping_lists Created!
|
||||||
|
2025-04-20 19:16:04.403440 --- INFO --- shopping_list_items Created!
|
||||||
|
2025-04-20 19:16:04.410510 --- INFO --- item_locations Created!
|
||||||
|
2025-04-20 19:16:04.418073 --- INFO --- conversions Created!
|
||||||
|
2025-04-20 19:16:04.423218 --- INFO --- Admin User Created!
|
||||||
|
2025-04-20 19:16:04.436197 --- ERROR --- module 'MyDataclasses' has no attribute 'ZonePayload'
|
||||||
|
2025-04-20 19:16:58.466588 --- INFO --- logins Created!
|
||||||
|
2025-04-20 19:16:58.472688 --- INFO --- sites Created!
|
||||||
|
2025-04-20 19:16:58.477737 --- INFO --- roles Created!
|
||||||
|
2025-04-20 19:16:58.481251 --- INFO --- units Created!
|
||||||
|
2025-04-20 19:16:58.489772 --- INFO --- cost_layers Created!
|
||||||
|
2025-04-20 19:16:58.497672 --- INFO --- linked_items Created!
|
||||||
|
2025-04-20 19:16:58.502626 --- INFO --- brands Created!
|
||||||
|
2025-04-20 19:16:58.509339 --- INFO --- food_info Created!
|
||||||
|
2025-04-20 19:16:58.516796 --- INFO --- item_info Created!
|
||||||
|
2025-04-20 19:16:58.524536 --- INFO --- zones Created!
|
||||||
|
2025-04-20 19:16:58.532376 --- INFO --- locations Created!
|
||||||
|
2025-04-20 19:16:58.540104 --- INFO --- logistics_info Created!
|
||||||
|
2025-04-20 19:16:58.547122 --- INFO --- transactions Created!
|
||||||
|
2025-04-20 19:16:58.555955 --- INFO --- item Created!
|
||||||
|
2025-04-20 19:16:58.562475 --- INFO --- vendors Created!
|
||||||
|
2025-04-20 19:16:58.570565 --- INFO --- groups Created!
|
||||||
|
2025-04-20 19:16:58.578991 --- INFO --- group_items Created!
|
||||||
|
2025-04-20 19:16:58.586669 --- INFO --- receipts Created!
|
||||||
|
2025-04-20 19:16:58.594838 --- INFO --- receipt_items Created!
|
||||||
|
2025-04-20 19:16:58.601902 --- INFO --- recipes Created!
|
||||||
|
2025-04-20 19:16:58.608915 --- INFO --- recipe_items Created!
|
||||||
|
2025-04-20 19:16:58.617684 --- INFO --- shopping_lists Created!
|
||||||
|
2025-04-20 19:16:58.625427 --- INFO --- shopping_list_items Created!
|
||||||
|
2025-04-20 19:16:58.635479 --- INFO --- item_locations Created!
|
||||||
|
2025-04-20 19:16:58.642372 --- INFO --- conversions Created!
|
||||||
|
2025-04-20 19:16:58.646415 --- INFO --- Admin User Created!
|
||||||
|
2025-04-20 19:16:58.668418 --- ERROR --- module 'MyDataclasses' has no attribute 'VendorPayload'
|
||||||
|
2025-04-20 19:17:15.674844 --- INFO --- logins Created!
|
||||||
|
2025-04-20 19:17:15.682368 --- INFO --- sites Created!
|
||||||
|
2025-04-20 19:17:15.687761 --- INFO --- roles Created!
|
||||||
|
2025-04-20 19:17:15.692281 --- INFO --- units Created!
|
||||||
|
2025-04-20 19:17:15.700205 --- INFO --- cost_layers Created!
|
||||||
|
2025-04-20 19:17:15.707736 --- INFO --- linked_items Created!
|
||||||
|
2025-04-20 19:17:15.714280 --- INFO --- brands Created!
|
||||||
|
2025-04-20 19:17:15.720500 --- INFO --- food_info Created!
|
||||||
|
2025-04-20 19:17:15.728902 --- INFO --- item_info Created!
|
||||||
|
2025-04-20 19:17:15.735575 --- INFO --- zones Created!
|
||||||
|
2025-04-20 19:17:15.743577 --- INFO --- locations Created!
|
||||||
|
2025-04-20 19:17:15.750537 --- INFO --- logistics_info Created!
|
||||||
|
2025-04-20 19:17:15.757739 --- INFO --- transactions Created!
|
||||||
|
2025-04-20 19:17:15.767727 --- INFO --- item Created!
|
||||||
|
2025-04-20 19:17:15.773470 --- INFO --- vendors Created!
|
||||||
|
2025-04-20 19:17:15.781078 --- INFO --- groups Created!
|
||||||
|
2025-04-20 19:17:15.791354 --- INFO --- group_items Created!
|
||||||
|
2025-04-20 19:17:15.799134 --- INFO --- receipts Created!
|
||||||
|
2025-04-20 19:17:15.806841 --- INFO --- receipt_items Created!
|
||||||
|
2025-04-20 19:17:15.813370 --- INFO --- recipes Created!
|
||||||
|
2025-04-20 19:17:15.821296 --- INFO --- recipe_items Created!
|
||||||
|
2025-04-20 19:17:15.828999 --- INFO --- shopping_lists Created!
|
||||||
|
2025-04-20 19:17:15.837757 --- INFO --- shopping_list_items Created!
|
||||||
|
2025-04-20 19:17:15.846351 --- INFO --- item_locations Created!
|
||||||
|
2025-04-20 19:17:15.853372 --- INFO --- conversions Created!
|
||||||
|
2025-04-20 19:17:15.856625 --- INFO --- Admin User Created!
|
||||||
|
2025-04-20 19:17:15.871202 --- ERROR --- module 'MyDataclasses' has no attribute 'VendorPayload'
|
||||||
|
2025-04-20 19:17:59.229086 --- INFO --- logins Created!
|
||||||
|
2025-04-20 19:17:59.236178 --- INFO --- sites Created!
|
||||||
|
2025-04-20 19:17:59.241179 --- INFO --- roles Created!
|
||||||
|
2025-04-20 19:17:59.244639 --- INFO --- units Created!
|
||||||
|
2025-04-20 19:17:59.252556 --- INFO --- cost_layers Created!
|
||||||
|
2025-04-20 19:17:59.258931 --- INFO --- linked_items Created!
|
||||||
|
2025-04-20 19:17:59.265640 --- INFO --- brands Created!
|
||||||
|
2025-04-20 19:17:59.271635 --- INFO --- food_info Created!
|
||||||
|
2025-04-20 19:17:59.281402 --- INFO --- item_info Created!
|
||||||
|
2025-04-20 19:17:59.289153 --- INFO --- zones Created!
|
||||||
|
2025-04-20 19:17:59.295595 --- INFO --- locations Created!
|
||||||
|
2025-04-20 19:17:59.303510 --- INFO --- logistics_info Created!
|
||||||
|
2025-04-20 19:17:59.310620 --- INFO --- transactions Created!
|
||||||
|
2025-04-20 19:17:59.318745 --- INFO --- item Created!
|
||||||
|
2025-04-20 19:17:59.325400 --- INFO --- vendors Created!
|
||||||
|
2025-04-20 19:17:59.333182 --- INFO --- groups Created!
|
||||||
|
2025-04-20 19:17:59.341367 --- INFO --- group_items Created!
|
||||||
|
2025-04-20 19:17:59.348914 --- INFO --- receipts Created!
|
||||||
|
2025-04-20 19:17:59.355674 --- INFO --- receipt_items Created!
|
||||||
|
2025-04-20 19:17:59.363428 --- INFO --- recipes Created!
|
||||||
|
2025-04-20 19:17:59.371858 --- INFO --- recipe_items Created!
|
||||||
|
2025-04-20 19:17:59.378939 --- INFO --- shopping_lists Created!
|
||||||
|
2025-04-20 19:17:59.388138 --- INFO --- shopping_list_items Created!
|
||||||
|
2025-04-20 19:17:59.395999 --- INFO --- item_locations Created!
|
||||||
|
2025-04-20 19:17:59.402528 --- INFO --- conversions Created!
|
||||||
|
2025-04-20 19:17:59.406967 --- INFO --- Admin User Created!
|
||||||
|
2025-04-20 19:43:36.914652 --- INFO --- logins Created!
|
||||||
|
2025-04-20 19:43:36.922436 --- INFO --- sites Created!
|
||||||
|
2025-04-20 19:43:36.927458 --- INFO --- roles Created!
|
||||||
|
2025-04-20 19:43:36.931555 --- INFO --- units Created!
|
||||||
|
2025-04-20 19:43:36.939094 --- INFO --- cost_layers Created!
|
||||||
|
2025-04-20 19:43:36.947628 --- INFO --- linked_items Created!
|
||||||
|
2025-04-20 19:43:36.953631 --- INFO --- brands Created!
|
||||||
|
2025-04-20 19:43:36.959852 --- INFO --- food_info Created!
|
||||||
|
2025-04-20 19:43:36.968778 --- INFO --- item_info Created!
|
||||||
|
2025-04-20 19:43:36.976558 --- INFO --- zones Created!
|
||||||
|
2025-04-20 19:43:36.983310 --- INFO --- locations Created!
|
||||||
|
2025-04-20 19:43:36.990780 --- INFO --- logistics_info Created!
|
||||||
|
2025-04-20 19:43:36.998082 --- INFO --- transactions Created!
|
||||||
|
2025-04-20 19:43:37.005780 --- INFO --- item Created!
|
||||||
|
2025-04-20 19:43:37.013460 --- INFO --- vendors Created!
|
||||||
|
2025-04-20 19:43:37.020215 --- INFO --- groups Created!
|
||||||
|
2025-04-20 19:43:37.028782 --- INFO --- group_items Created!
|
||||||
|
2025-04-20 19:43:37.036257 --- INFO --- receipts Created!
|
||||||
|
2025-04-20 19:43:37.043565 --- INFO --- receipt_items Created!
|
||||||
|
2025-04-20 19:43:37.049814 --- INFO --- recipes Created!
|
||||||
|
2025-04-20 19:43:37.057702 --- INFO --- recipe_items Created!
|
||||||
|
2025-04-20 19:43:37.065761 --- INFO --- shopping_lists Created!
|
||||||
|
2025-04-20 19:43:37.073370 --- INFO --- shopping_list_items Created!
|
||||||
|
2025-04-20 19:43:37.081053 --- INFO --- item_locations Created!
|
||||||
|
2025-04-20 19:43:37.088088 --- INFO --- conversions Created!
|
||||||
|
2025-04-20 19:43:37.093156 --- INFO --- Admin User Created!
|
||||||
|
2025-04-20 19:45:44.395265 --- INFO --- item_info DROPPED!
|
||||||
|
2025-04-20 19:45:44.405900 --- INFO --- items DROPPED!
|
||||||
|
2025-04-20 19:45:44.414804 --- INFO --- cost_layers DROPPED!
|
||||||
|
2025-04-20 19:45:44.422703 --- INFO --- linked_items DROPPED!
|
||||||
|
2025-04-20 19:45:44.430346 --- INFO --- transactions DROPPED!
|
||||||
|
2025-04-20 19:45:44.437350 --- INFO --- brands DROPPED!
|
||||||
|
2025-04-20 19:45:44.444569 --- INFO --- food_info DROPPED!
|
||||||
|
2025-04-20 19:45:44.452885 --- INFO --- logistics_info DROPPED!
|
||||||
|
2025-04-20 19:48:31.583108 --- INFO --- item_info DROPPED!
|
||||||
|
2025-04-20 19:48:31.591460 --- INFO --- items DROPPED!
|
||||||
|
2025-04-20 19:48:31.596408 --- INFO --- cost_layers DROPPED!
|
||||||
|
2025-04-20 19:48:31.601566 --- INFO --- linked_items DROPPED!
|
||||||
|
2025-04-20 19:48:31.607329 --- INFO --- transactions DROPPED!
|
||||||
|
2025-04-20 19:48:31.611822 --- INFO --- brands DROPPED!
|
||||||
|
2025-04-20 19:48:31.615984 --- INFO --- food_info DROPPED!
|
||||||
|
2025-04-20 19:48:31.621444 --- INFO --- logistics_info DROPPED!
|
||||||
|
2025-04-20 19:51:08.211394 --- INFO --- item_info DROPPED!
|
||||||
|
2025-04-20 19:51:08.219628 --- INFO --- items DROPPED!
|
||||||
|
2025-04-20 19:51:08.225163 --- INFO --- cost_layers DROPPED!
|
||||||
|
2025-04-20 19:51:08.231236 --- INFO --- linked_items DROPPED!
|
||||||
|
2025-04-20 19:51:08.236599 --- INFO --- transactions DROPPED!
|
||||||
|
2025-04-20 19:51:08.241802 --- INFO --- brands DROPPED!
|
||||||
|
2025-04-20 19:51:08.247341 --- INFO --- food_info DROPPED!
|
||||||
|
2025-04-20 19:51:08.251883 --- INFO --- logistics_info DROPPED!
|
||||||
|
2025-04-20 19:52:54.948592 --- INFO --- item_info DROPPED!
|
||||||
|
2025-04-20 19:52:54.956447 --- INFO --- items DROPPED!
|
||||||
|
2025-04-20 19:52:54.962023 --- INFO --- cost_layers DROPPED!
|
||||||
|
2025-04-20 19:52:54.967556 --- INFO --- linked_items DROPPED!
|
||||||
|
2025-04-20 19:52:54.973165 --- INFO --- transactions DROPPED!
|
||||||
|
2025-04-20 19:52:54.976632 --- INFO --- brands DROPPED!
|
||||||
|
2025-04-20 19:52:54.981398 --- INFO --- food_info DROPPED!
|
||||||
|
2025-04-20 19:52:54.985072 --- INFO --- logistics_info DROPPED!
|
||||||
|
2025-04-20 19:52:54.989456 --- ERROR --- DatabaseError(message='table "testsite_zones" does not exist', payload=DROP TABLE TestSite_zones CASCADE;, sql='zones')
|
||||||
|
2025-04-20 19:56:25.272595 --- INFO --- item_info DROPPED!
|
||||||
|
2025-04-20 19:56:25.282064 --- INFO --- items DROPPED!
|
||||||
|
2025-04-20 19:56:25.287581 --- INFO --- cost_layers DROPPED!
|
||||||
|
2025-04-20 19:56:25.293563 --- INFO --- linked_items DROPPED!
|
||||||
|
2025-04-20 19:56:25.298578 --- INFO --- transactions DROPPED!
|
||||||
|
2025-04-20 19:56:25.303670 --- INFO --- brands DROPPED!
|
||||||
|
2025-04-20 19:56:25.307742 --- INFO --- food_info DROPPED!
|
||||||
|
2025-04-20 19:56:25.313677 --- INFO --- logistics_info DROPPED!
|
||||||
|
2025-04-20 19:56:25.317716 --- INFO --- zones DROPPED!
|
||||||
|
2025-04-20 19:56:25.325855 --- INFO --- locations DROPPED!
|
||||||
|
2025-04-20 19:56:25.330990 --- INFO --- vendors DROPPED!
|
||||||
|
2025-04-20 19:56:25.337850 --- INFO --- group_items DROPPED!
|
||||||
|
2025-04-20 19:56:25.346723 --- INFO --- groups DROPPED!
|
||||||
|
2025-04-20 19:56:25.354480 --- INFO --- receipt_items DROPPED!
|
||||||
|
2025-04-20 19:56:25.362108 --- INFO --- receipts DROPPED!
|
||||||
|
2025-04-20 19:56:25.369684 --- INFO --- recipe_items DROPPED!
|
||||||
|
2025-04-20 19:56:25.377807 --- INFO --- recipes DROPPED!
|
||||||
|
2025-04-20 19:56:25.385620 --- INFO --- shopping_list_items DROPPED!
|
||||||
|
2025-04-20 19:56:25.393541 --- INFO --- shopping_lists DROPPED!
|
||||||
|
2025-04-20 19:56:25.401384 --- INFO --- item_locations DROPPED!
|
||||||
|
2025-04-20 19:56:25.405523 --- INFO --- conversions DROPPED!
|
||||||
|
2025-04-20 19:58:10.901757 --- INFO --- item_info DROPPED!
|
||||||
|
2025-04-20 19:58:10.911845 --- INFO --- items DROPPED!
|
||||||
|
2025-04-20 19:58:10.917872 --- INFO --- cost_layers DROPPED!
|
||||||
|
2025-04-20 19:58:10.922065 --- INFO --- linked_items DROPPED!
|
||||||
|
2025-04-20 19:58:10.928478 --- INFO --- transactions DROPPED!
|
||||||
|
2025-04-20 19:58:10.932582 --- INFO --- brands DROPPED!
|
||||||
|
2025-04-20 19:58:10.936689 --- INFO --- food_info DROPPED!
|
||||||
|
2025-04-20 19:58:10.941754 --- INFO --- logistics_info DROPPED!
|
||||||
|
2025-04-20 19:58:36.985976 --- INFO --- item_info DROPPED!
|
||||||
|
2025-04-20 19:58:36.996149 --- INFO --- items DROPPED!
|
||||||
|
2025-04-20 19:58:37.001166 --- INFO --- cost_layers DROPPED!
|
||||||
|
2025-04-20 19:58:37.006862 --- INFO --- linked_items DROPPED!
|
||||||
|
2025-04-20 19:58:37.012179 --- INFO --- transactions DROPPED!
|
||||||
|
2025-04-20 19:58:37.016694 --- INFO --- brands DROPPED!
|
||||||
|
2025-04-20 19:58:37.020758 --- INFO --- food_info DROPPED!
|
||||||
|
2025-04-20 19:58:37.026061 --- INFO --- logistics_info DROPPED!
|
||||||
|
2025-04-20 20:07:37.732426 --- INFO --- item_info DROPPED!
|
||||||
|
2025-04-20 20:07:37.740467 --- INFO --- items DROPPED!
|
||||||
|
2025-04-20 20:07:37.745042 --- INFO --- cost_layers DROPPED!
|
||||||
|
2025-04-20 20:07:37.750893 --- INFO --- linked_items DROPPED!
|
||||||
|
2025-04-20 20:07:37.754429 --- INFO --- transactions DROPPED!
|
||||||
|
2025-04-20 20:07:37.758729 --- INFO --- brands DROPPED!
|
||||||
|
2025-04-20 20:07:37.763189 --- INFO --- food_info DROPPED!
|
||||||
|
2025-04-20 20:07:37.767020 --- INFO --- logistics_info DROPPED!
|
||||||
|
2025-04-20 20:07:37.771055 --- INFO --- zones DROPPED!
|
||||||
|
2025-04-20 20:07:37.775628 --- INFO --- locations DROPPED!
|
||||||
|
2025-04-20 20:07:37.779735 --- INFO --- vendors DROPPED!
|
||||||
|
2025-04-20 20:07:37.784456 --- INFO --- group_items DROPPED!
|
||||||
|
2025-04-20 20:07:37.788915 --- INFO --- groups DROPPED!
|
||||||
|
2025-04-20 20:07:37.793395 --- INFO --- receipt_items DROPPED!
|
||||||
|
2025-04-20 20:07:37.798563 --- INFO --- receipts DROPPED!
|
||||||
|
2025-04-20 20:07:37.803691 --- INFO --- recipe_items DROPPED!
|
||||||
|
2025-04-20 20:07:37.808903 --- INFO --- recipes DROPPED!
|
||||||
|
2025-04-20 20:07:37.813005 --- INFO --- shopping_list_items DROPPED!
|
||||||
|
2025-04-20 20:07:37.818899 --- INFO --- shopping_lists DROPPED!
|
||||||
|
2025-04-20 20:07:37.823062 --- INFO --- item_locations DROPPED!
|
||||||
|
2025-04-20 20:07:37.827577 --- INFO --- conversions DROPPED!
|
||||||
|
2025-04-20 20:10:51.725827 --- INFO --- item_info DROPPED!
|
||||||
|
2025-04-20 20:10:51.735709 --- INFO --- items DROPPED!
|
||||||
|
2025-04-20 20:10:51.741311 --- INFO --- cost_layers DROPPED!
|
||||||
|
2025-04-20 20:10:51.745890 --- INFO --- linked_items DROPPED!
|
||||||
|
2025-04-20 20:10:51.750915 --- INFO --- transactions DROPPED!
|
||||||
|
2025-04-20 20:10:51.755599 --- INFO --- brands DROPPED!
|
||||||
|
2025-04-20 20:10:51.761188 --- INFO --- food_info DROPPED!
|
||||||
|
2025-04-20 20:10:51.765063 --- INFO --- logistics_info DROPPED!
|
||||||
|
2025-04-20 20:10:51.769585 --- INFO --- zones DROPPED!
|
||||||
|
2025-04-20 20:10:51.774893 --- INFO --- locations DROPPED!
|
||||||
|
2025-04-20 20:10:51.779100 --- INFO --- vendors DROPPED!
|
||||||
|
2025-04-20 20:10:51.784704 --- INFO --- group_items DROPPED!
|
||||||
|
2025-04-20 20:10:51.789211 --- INFO --- groups DROPPED!
|
||||||
|
2025-04-20 20:10:51.793357 --- INFO --- receipt_items DROPPED!
|
||||||
|
2025-04-20 20:10:51.799194 --- INFO --- receipts DROPPED!
|
||||||
|
2025-04-20 20:10:51.803289 --- INFO --- recipe_items DROPPED!
|
||||||
|
2025-04-20 20:10:51.808076 --- INFO --- recipes DROPPED!
|
||||||
|
2025-04-20 20:10:51.813010 --- INFO --- shopping_list_items DROPPED!
|
||||||
|
2025-04-20 20:10:51.817596 --- INFO --- shopping_lists DROPPED!
|
||||||
|
2025-04-20 20:10:51.821643 --- INFO --- item_locations DROPPED!
|
||||||
|
2025-04-20 20:10:51.826587 --- INFO --- conversions DROPPED!
|
||||||
|
2025-04-20 20:11:14.056575 --- INFO --- logins Created!
|
||||||
|
2025-04-20 20:11:14.063971 --- INFO --- sites Created!
|
||||||
|
2025-04-20 20:11:14.069365 --- INFO --- roles Created!
|
||||||
|
2025-04-20 20:11:14.073411 --- INFO --- units Created!
|
||||||
|
2025-04-20 20:11:14.082962 --- INFO --- cost_layers Created!
|
||||||
|
2025-04-20 20:11:14.091859 --- INFO --- linked_items Created!
|
||||||
|
2025-04-20 20:11:14.096907 --- INFO --- brands Created!
|
||||||
|
2025-04-20 20:11:14.104713 --- INFO --- food_info Created!
|
||||||
|
2025-04-20 20:11:14.110843 --- INFO --- item_info Created!
|
||||||
|
2025-04-20 20:11:14.119202 --- INFO --- zones Created!
|
||||||
|
2025-04-20 20:11:14.125756 --- INFO --- locations Created!
|
||||||
|
2025-04-20 20:11:14.131825 --- INFO --- logistics_info Created!
|
||||||
|
2025-04-20 20:11:14.138848 --- INFO --- transactions Created!
|
||||||
|
2025-04-20 20:11:14.146927 --- INFO --- item Created!
|
||||||
|
2025-04-20 20:11:14.152932 --- INFO --- vendors Created!
|
||||||
|
2025-04-20 20:11:14.159374 --- INFO --- groups Created!
|
||||||
|
2025-04-20 20:11:14.166932 --- INFO --- group_items Created!
|
||||||
|
2025-04-20 20:11:14.174750 --- INFO --- receipts Created!
|
||||||
|
2025-04-20 20:11:14.181847 --- INFO --- receipt_items Created!
|
||||||
|
2025-04-20 20:11:14.186633 --- INFO --- recipes Created!
|
||||||
|
2025-04-20 20:11:14.194852 --- INFO --- recipe_items Created!
|
||||||
|
2025-04-20 20:11:14.201475 --- INFO --- shopping_lists Created!
|
||||||
|
2025-04-20 20:11:14.209506 --- INFO --- shopping_list_items Created!
|
||||||
|
2025-04-20 20:11:14.217085 --- INFO --- item_locations Created!
|
||||||
|
2025-04-20 20:11:14.224012 --- INFO --- conversions Created!
|
||||||
|
2025-04-20 20:11:14.227800 --- INFO --- Admin User Created!
|
||||||
|
2025-04-20 20:11:14.240274 --- ERROR --- DatabaseError(message='tuple index out of range', payload=('main', ''), sql='INSERT INTO testsite_zones(name, description, site_id) VALUES (%s, %s, %s) RETURNING *;')
|
||||||
|
2025-04-20 20:12:29.007584 --- INFO --- logins Created!
|
||||||
|
2025-04-20 20:12:29.015493 --- INFO --- sites Created!
|
||||||
|
2025-04-20 20:12:29.021142 --- INFO --- roles Created!
|
||||||
|
2025-04-20 20:12:29.025026 --- INFO --- units Created!
|
||||||
|
2025-04-20 20:12:29.032140 --- INFO --- cost_layers Created!
|
||||||
|
2025-04-20 20:12:29.039496 --- INFO --- linked_items Created!
|
||||||
|
2025-04-20 20:12:29.046115 --- INFO --- brands Created!
|
||||||
|
2025-04-20 20:12:29.052237 --- INFO --- food_info Created!
|
||||||
|
2025-04-20 20:12:29.060538 --- INFO --- item_info Created!
|
||||||
|
2025-04-20 20:12:29.066956 --- INFO --- zones Created!
|
||||||
|
2025-04-20 20:12:29.073431 --- INFO --- locations Created!
|
||||||
|
2025-04-20 20:12:29.082564 --- INFO --- logistics_info Created!
|
||||||
|
2025-04-20 20:12:29.090000 --- INFO --- transactions Created!
|
||||||
|
2025-04-20 20:12:29.098688 --- INFO --- item Created!
|
||||||
|
2025-04-20 20:12:29.104873 --- INFO --- vendors Created!
|
||||||
|
2025-04-20 20:12:29.112717 --- INFO --- groups Created!
|
||||||
|
2025-04-20 20:12:29.120101 --- INFO --- group_items Created!
|
||||||
|
2025-04-20 20:12:29.128686 --- INFO --- receipts Created!
|
||||||
|
2025-04-20 20:12:29.135147 --- INFO --- receipt_items Created!
|
||||||
|
2025-04-20 20:12:29.141797 --- INFO --- recipes Created!
|
||||||
|
2025-04-20 20:12:29.151352 --- INFO --- recipe_items Created!
|
||||||
|
2025-04-20 20:12:29.158714 --- INFO --- shopping_lists Created!
|
||||||
|
2025-04-20 20:12:29.166933 --- INFO --- shopping_list_items Created!
|
||||||
|
2025-04-20 20:12:29.175124 --- INFO --- item_locations Created!
|
||||||
|
2025-04-20 20:12:29.181717 --- INFO --- conversions Created!
|
||||||
|
2025-04-20 20:12:29.186271 --- INFO --- Admin User Created!
|
||||||
|
2025-04-20 20:12:38.923616 --- INFO --- item_info DROPPED!
|
||||||
|
2025-04-20 20:12:38.932843 --- INFO --- items DROPPED!
|
||||||
|
2025-04-20 20:12:38.937372 --- INFO --- cost_layers DROPPED!
|
||||||
|
2025-04-20 20:12:38.942503 --- INFO --- linked_items DROPPED!
|
||||||
|
2025-04-20 20:12:38.946721 --- INFO --- transactions DROPPED!
|
||||||
|
2025-04-20 20:12:38.951727 --- INFO --- brands DROPPED!
|
||||||
|
2025-04-20 20:12:38.955969 --- INFO --- food_info DROPPED!
|
||||||
|
2025-04-20 20:12:38.961360 --- INFO --- logistics_info DROPPED!
|
||||||
|
2025-04-20 20:12:38.966848 --- INFO --- zones DROPPED!
|
||||||
|
2025-04-20 20:12:38.970884 --- INFO --- locations DROPPED!
|
||||||
|
2025-04-20 20:12:38.975885 --- INFO --- vendors DROPPED!
|
||||||
|
2025-04-20 20:12:38.980966 --- INFO --- group_items DROPPED!
|
||||||
|
2025-04-20 20:12:38.985746 --- INFO --- groups DROPPED!
|
||||||
|
2025-04-20 20:12:38.989709 --- INFO --- receipt_items DROPPED!
|
||||||
|
2025-04-20 20:12:38.994949 --- INFO --- receipts DROPPED!
|
||||||
|
2025-04-20 20:12:38.998971 --- INFO --- recipe_items DROPPED!
|
||||||
|
2025-04-20 20:12:39.004057 --- INFO --- recipes DROPPED!
|
||||||
|
2025-04-20 20:12:39.009013 --- INFO --- shopping_list_items DROPPED!
|
||||||
|
2025-04-20 20:12:39.013298 --- INFO --- shopping_lists DROPPED!
|
||||||
|
2025-04-20 20:12:39.017813 --- INFO --- item_locations DROPPED!
|
||||||
|
2025-04-20 20:12:39.022064 --- INFO --- conversions DROPPED!
|
||||||
|
2025-04-20 20:32:27.356365 --- INFO --- logins Created!
|
||||||
|
2025-04-20 20:32:27.364095 --- INFO --- sites Created!
|
||||||
|
2025-04-20 20:32:27.368637 --- INFO --- roles Created!
|
||||||
|
2025-04-20 20:32:27.374196 --- INFO --- units Created!
|
||||||
|
2025-04-20 20:32:27.381877 --- INFO --- cost_layers Created!
|
||||||
|
2025-04-20 20:32:27.389138 --- INFO --- linked_items Created!
|
||||||
|
2025-04-20 20:32:27.395362 --- INFO --- brands Created!
|
||||||
|
2025-04-20 20:32:27.402835 --- INFO --- food_info Created!
|
||||||
|
2025-04-20 20:32:27.409510 --- INFO --- item_info Created!
|
||||||
|
2025-04-20 20:32:27.417471 --- INFO --- zones Created!
|
||||||
|
2025-04-20 20:32:27.424663 --- INFO --- locations Created!
|
||||||
|
2025-04-20 20:32:27.433133 --- INFO --- logistics_info Created!
|
||||||
|
2025-04-20 20:32:27.439780 --- INFO --- transactions Created!
|
||||||
|
2025-04-20 20:32:27.448585 --- INFO --- item Created!
|
||||||
|
2025-04-20 20:32:27.455303 --- INFO --- vendors Created!
|
||||||
|
2025-04-20 20:32:27.462869 --- INFO --- groups Created!
|
||||||
|
2025-04-20 20:32:27.470653 --- INFO --- group_items Created!
|
||||||
|
2025-04-20 20:32:27.478653 --- INFO --- receipts Created!
|
||||||
|
2025-04-20 20:32:27.486804 --- INFO --- receipt_items Created!
|
||||||
|
2025-04-20 20:32:27.493141 --- INFO --- recipes Created!
|
||||||
|
2025-04-20 20:32:27.500870 --- INFO --- recipe_items Created!
|
||||||
|
2025-04-20 20:32:27.507862 --- INFO --- shopping_lists Created!
|
||||||
|
2025-04-20 20:32:27.515340 --- INFO --- shopping_list_items Created!
|
||||||
|
2025-04-20 20:32:27.524287 --- INFO --- item_locations Created!
|
||||||
|
2025-04-20 20:32:27.532196 --- INFO --- conversions Created!
|
||||||
|
2025-04-20 20:32:27.535331 --- INFO --- Admin User Created!
|
||||||
|
2025-04-20 20:33:25.173233 --- INFO --- item_info DROPPED!
|
||||||
|
2025-04-20 20:33:25.182788 --- INFO --- items DROPPED!
|
||||||
|
2025-04-20 20:33:25.187318 --- INFO --- cost_layers DROPPED!
|
||||||
|
2025-04-20 20:33:25.193746 --- INFO --- linked_items DROPPED!
|
||||||
|
2025-04-20 20:33:25.197263 --- INFO --- transactions DROPPED!
|
||||||
|
2025-04-20 20:33:25.202565 --- INFO --- brands DROPPED!
|
||||||
|
2025-04-20 20:33:25.207124 --- INFO --- food_info DROPPED!
|
||||||
|
2025-04-20 20:33:25.213012 --- INFO --- logistics_info DROPPED!
|
||||||
|
2025-04-20 20:33:25.218554 --- INFO --- zones DROPPED!
|
||||||
|
2025-04-20 20:33:25.222613 --- INFO --- locations DROPPED!
|
||||||
|
2025-04-20 20:33:25.227136 --- INFO --- vendors DROPPED!
|
||||||
|
2025-04-20 20:33:25.232695 --- INFO --- group_items DROPPED!
|
||||||
|
2025-04-20 20:33:25.237264 --- INFO --- groups DROPPED!
|
||||||
|
2025-04-20 20:33:25.241580 --- INFO --- receipt_items DROPPED!
|
||||||
|
2025-04-20 20:33:25.246657 --- INFO --- receipts DROPPED!
|
||||||
|
2025-04-20 20:33:25.251050 --- INFO --- recipe_items DROPPED!
|
||||||
|
2025-04-20 20:33:25.255068 --- INFO --- recipes DROPPED!
|
||||||
|
2025-04-20 20:33:25.260690 --- INFO --- shopping_list_items DROPPED!
|
||||||
|
2025-04-20 20:33:25.265769 --- INFO --- shopping_lists DROPPED!
|
||||||
|
2025-04-20 20:33:25.269793 --- INFO --- item_locations DROPPED!
|
||||||
|
2025-04-20 20:33:25.273824 --- INFO --- conversions DROPPED!
|
||||||
|
2025-04-20 20:34:25.344816 --- INFO --- logins Created!
|
||||||
|
2025-04-20 20:34:25.352155 --- INFO --- sites Created!
|
||||||
|
2025-04-20 20:34:25.357696 --- INFO --- roles Created!
|
||||||
|
2025-04-20 20:34:25.363010 --- INFO --- units Created!
|
||||||
|
2025-04-20 20:34:25.370030 --- INFO --- cost_layers Created!
|
||||||
|
2025-04-20 20:34:25.377554 --- INFO --- linked_items Created!
|
||||||
|
2025-04-20 20:34:25.383668 --- INFO --- brands Created!
|
||||||
|
2025-04-20 20:34:25.390875 --- INFO --- food_info Created!
|
||||||
|
2025-04-20 20:34:25.397424 --- INFO --- item_info Created!
|
||||||
|
2025-04-20 20:34:25.405761 --- INFO --- zones Created!
|
||||||
|
2025-04-20 20:34:25.412801 --- INFO --- locations Created!
|
||||||
|
2025-04-20 20:34:25.420564 --- INFO --- logistics_info Created!
|
||||||
|
2025-04-20 20:34:25.427949 --- INFO --- transactions Created!
|
||||||
|
2025-04-20 20:34:25.435344 --- INFO --- item Created!
|
||||||
|
2025-04-20 20:34:25.442389 --- INFO --- vendors Created!
|
||||||
|
2025-04-20 20:34:25.449534 --- INFO --- groups Created!
|
||||||
|
2025-04-20 20:34:25.457550 --- INFO --- group_items Created!
|
||||||
|
2025-04-20 20:34:25.465405 --- INFO --- receipts Created!
|
||||||
|
2025-04-20 20:34:25.471947 --- INFO --- receipt_items Created!
|
||||||
|
2025-04-20 20:34:25.478815 --- INFO --- recipes Created!
|
||||||
|
2025-04-20 20:34:25.486803 --- INFO --- recipe_items Created!
|
||||||
|
2025-04-20 20:34:25.495032 --- INFO --- shopping_lists Created!
|
||||||
|
2025-04-20 20:34:25.503567 --- INFO --- shopping_list_items Created!
|
||||||
|
2025-04-20 20:34:25.511863 --- INFO --- item_locations Created!
|
||||||
|
2025-04-20 20:34:25.517088 --- INFO --- conversions Created!
|
||||||
|
2025-04-20 20:34:25.522653 --- INFO --- Admin User Created!
|
||||||
|
2025-04-20 20:34:40.207702 --- INFO --- item_info DROPPED!
|
||||||
|
2025-04-20 20:34:40.217297 --- INFO --- items DROPPED!
|
||||||
|
2025-04-20 20:34:40.223866 --- INFO --- cost_layers DROPPED!
|
||||||
|
2025-04-20 20:34:40.228928 --- INFO --- linked_items DROPPED!
|
||||||
|
2025-04-20 20:34:40.233942 --- INFO --- transactions DROPPED!
|
||||||
|
2025-04-20 20:34:40.238435 --- INFO --- brands DROPPED!
|
||||||
|
2025-04-20 20:34:40.243588 --- INFO --- food_info DROPPED!
|
||||||
|
2025-04-20 20:34:40.248615 --- INFO --- logistics_info DROPPED!
|
||||||
|
2025-04-20 20:34:40.252808 --- INFO --- zones DROPPED!
|
||||||
|
2025-04-20 20:34:40.258575 --- INFO --- locations DROPPED!
|
||||||
|
2025-04-20 20:34:40.262926 --- INFO --- vendors DROPPED!
|
||||||
|
2025-04-20 20:34:40.267004 --- INFO --- group_items DROPPED!
|
||||||
|
2025-04-20 20:34:40.272103 --- INFO --- groups DROPPED!
|
||||||
|
2025-04-20 20:34:40.276648 --- INFO --- receipt_items DROPPED!
|
||||||
|
2025-04-20 20:34:40.281217 --- INFO --- receipts DROPPED!
|
||||||
|
2025-04-20 20:34:40.287034 --- INFO --- recipe_items DROPPED!
|
||||||
|
2025-04-20 20:34:40.291009 --- INFO --- recipes DROPPED!
|
||||||
|
2025-04-20 20:34:40.295365 --- INFO --- shopping_list_items DROPPED!
|
||||||
|
2025-04-20 20:34:40.301096 --- INFO --- shopping_lists DROPPED!
|
||||||
|
2025-04-20 20:34:40.305141 --- INFO --- item_locations DROPPED!
|
||||||
|
2025-04-20 20:34:40.310973 --- INFO --- conversions DROPPED!
|
||||||
|
2025-04-20 21:03:22.361206 --- INFO --- logins Created!
|
||||||
|
2025-04-20 21:03:22.368210 --- INFO --- sites Created!
|
||||||
|
2025-04-20 21:03:22.372737 --- INFO --- roles Created!
|
||||||
|
2025-04-20 21:03:22.376787 --- INFO --- units Created!
|
||||||
|
2025-04-20 21:03:22.384390 --- INFO --- cost_layers Created!
|
||||||
|
2025-04-20 21:03:22.392461 --- INFO --- linked_items Created!
|
||||||
|
2025-04-20 21:03:22.397727 --- INFO --- brands Created!
|
||||||
|
2025-04-20 21:03:22.403723 --- INFO --- food_info Created!
|
||||||
|
2025-04-20 21:03:22.411420 --- INFO --- item_info Created!
|
||||||
|
2025-04-20 21:03:22.418983 --- INFO --- zones Created!
|
||||||
|
2025-04-20 21:03:22.426220 --- INFO --- locations Created!
|
||||||
|
2025-04-20 21:03:22.433434 --- INFO --- logistics_info Created!
|
||||||
|
2025-04-20 21:03:22.440189 --- INFO --- transactions Created!
|
||||||
|
2025-04-20 21:03:22.448293 --- INFO --- item Created!
|
||||||
|
2025-04-20 21:03:22.455434 --- INFO --- vendors Created!
|
||||||
|
2025-04-20 21:03:22.462439 --- INFO --- groups Created!
|
||||||
|
2025-04-20 21:03:22.469950 --- INFO --- group_items Created!
|
||||||
|
2025-04-20 21:03:22.477434 --- INFO --- receipts Created!
|
||||||
|
2025-04-20 21:03:22.484434 --- INFO --- receipt_items Created!
|
||||||
|
2025-04-20 21:03:22.491212 --- INFO --- recipes Created!
|
||||||
|
2025-04-20 21:03:22.499338 --- INFO --- recipe_items Created!
|
||||||
|
2025-04-20 21:03:22.506939 --- INFO --- shopping_lists Created!
|
||||||
|
2025-04-20 21:03:22.514674 --- INFO --- shopping_list_items Created!
|
||||||
|
2025-04-20 21:03:22.522661 --- INFO --- item_locations Created!
|
||||||
|
2025-04-20 21:03:22.529320 --- INFO --- conversions Created!
|
||||||
|
2025-04-20 21:03:22.533343 --- INFO --- Admin User Created!
|
||||||
|
2025-04-20 21:07:46.103441 --- INFO --- item_info DROPPED!
|
||||||
|
2025-04-20 21:07:46.113647 --- INFO --- items DROPPED!
|
||||||
|
2025-04-20 21:07:46.119615 --- INFO --- cost_layers DROPPED!
|
||||||
|
2025-04-20 21:07:46.125899 --- INFO --- linked_items DROPPED!
|
||||||
|
2025-04-20 21:07:46.130623 --- INFO --- transactions DROPPED!
|
||||||
|
2025-04-20 21:07:46.134817 --- INFO --- brands DROPPED!
|
||||||
|
2025-04-20 21:07:46.139474 --- INFO --- food_info DROPPED!
|
||||||
|
2025-04-20 21:07:46.145048 --- INFO --- logistics_info DROPPED!
|
||||||
|
2025-04-20 21:07:46.150355 --- INFO --- zones DROPPED!
|
||||||
|
2025-04-20 21:07:46.155777 --- INFO --- locations DROPPED!
|
||||||
|
2025-04-20 21:07:46.160313 --- INFO --- vendors DROPPED!
|
||||||
|
2025-04-20 21:07:46.165662 --- INFO --- group_items DROPPED!
|
||||||
|
2025-04-20 21:07:46.169670 --- INFO --- groups DROPPED!
|
||||||
|
2025-04-20 21:07:46.174114 --- INFO --- receipt_items DROPPED!
|
||||||
|
2025-04-20 21:07:46.179634 --- INFO --- receipts DROPPED!
|
||||||
|
2025-04-20 21:07:46.184753 --- INFO --- recipe_items DROPPED!
|
||||||
|
2025-04-20 21:07:46.189291 --- INFO --- recipes DROPPED!
|
||||||
|
2025-04-20 21:07:46.193588 --- INFO --- shopping_list_items DROPPED!
|
||||||
|
2025-04-20 21:07:46.197995 --- INFO --- shopping_lists DROPPED!
|
||||||
|
2025-04-20 21:07:46.203577 --- INFO --- item_locations DROPPED!
|
||||||
|
2025-04-20 21:07:46.208195 --- INFO --- conversions DROPPED!
|
||||||
|
|||||||
21
process.py
21
process.py
@ -5,10 +5,12 @@ import postsqldb
|
|||||||
def dropSiteTables(conn, site_manager: MyDataclasses.SiteManager):
|
def dropSiteTables(conn, site_manager: MyDataclasses.SiteManager):
|
||||||
try:
|
try:
|
||||||
for table in site_manager.drop_order:
|
for table in site_manager.drop_order:
|
||||||
|
print(table)
|
||||||
database.__dropTable(conn, site_manager.site_name, table)
|
database.__dropTable(conn, site_manager.site_name, table)
|
||||||
with open("process.log", "a+") as file:
|
with open("process.log", "a+") as file:
|
||||||
file.write(f"{datetime.datetime.now()} --- INFO --- {table} DROPPED!\n")
|
file.write(f"{datetime.datetime.now()} --- INFO --- {table} DROPPED!\n")
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
|
print(error)
|
||||||
raise error
|
raise error
|
||||||
|
|
||||||
def setupSiteTables(conn, site_manager: MyDataclasses.SiteManager):
|
def setupSiteTables(conn, site_manager: MyDataclasses.SiteManager):
|
||||||
@ -72,6 +74,7 @@ def deleteSite(site_manager: MyDataclasses.SiteManager):
|
|||||||
|
|
||||||
site = database.deleteSitesTuple(conn, site_manager.site_name, (site['id'], ), convert=True)
|
site = database.deleteSitesTuple(conn, site_manager.site_name, (site['id'], ), convert=True)
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
with open("process.log", "a+") as file:
|
with open("process.log", "a+") as file:
|
||||||
file.write(f"{datetime.datetime.now()} --- ERROR --- {error}\n")
|
file.write(f"{datetime.datetime.now()} --- ERROR --- {error}\n")
|
||||||
@ -97,19 +100,19 @@ def addSite(site_manager: MyDataclasses.SiteManager):
|
|||||||
site_owner_id=admin_user['id']
|
site_owner_id=admin_user['id']
|
||||||
)
|
)
|
||||||
site = database.insertSitesTuple(conn, site.payload(), convert=True)
|
site = database.insertSitesTuple(conn, site.payload(), convert=True)
|
||||||
|
print("site", site)
|
||||||
role = MyDataclasses.RolePayload("Admin", f"Admin for {site['site_name']}", site['id'])
|
role = MyDataclasses.RolePayload("Admin", f"Admin for {site['site_name']}", site['id'])
|
||||||
role = database.insertRolesTuple(conn, role.payload(), convert=True)
|
role = database.insertRolesTuple(conn, role.payload(), convert=True)
|
||||||
|
print("role", role)
|
||||||
admin_user = database.updateAddLoginSitesRoles(conn, (site["id"], role["id"], admin_user["id"]), convert=True)
|
admin_user = database.updateAddLoginSitesRoles(conn, (site["id"], role["id"], admin_user["id"]), convert=True)
|
||||||
|
print('admin_user', admin_user)
|
||||||
default_zone = MyDataclasses.ZonePayload(site_manager.default_zone, site['id'])
|
default_zone = postsqldb.ZonesTable.Payload(site_manager.default_zone)
|
||||||
default_zone = database.insertZonesTuple(conn, site["site_name"], default_zone.payload(), convert=True)
|
default_zone = database.insertZonesTuple(conn, site["site_name"], default_zone.payload(), convert=True)
|
||||||
|
print('default_zone', default_zone)
|
||||||
uuid = f"{site_manager.default_zone}@{site_manager.default_location}"
|
uuid = f"{site_manager.default_zone}@{site_manager.default_location}"
|
||||||
default_location = MyDataclasses.LocationPayload(uuid, site_manager.default_location, default_zone['id'])
|
default_location = postsqldb.LocationsTable.Payload(uuid, site_manager.default_location, default_zone['id'])
|
||||||
default_location = database.insertLocationsTuple(conn, site['site_name'], default_location.payload(), convert=True)
|
default_location = database.insertLocationsTuple(conn, site['site_name'], default_location.payload(), convert=True)
|
||||||
|
print('default_location', default_location)
|
||||||
# need to update the default zones/locations for site.
|
# need to update the default zones/locations for site.
|
||||||
payload = {
|
payload = {
|
||||||
'id': site['id'],
|
'id': site['id'],
|
||||||
@ -120,8 +123,8 @@ def addSite(site_manager: MyDataclasses.SiteManager):
|
|||||||
database.__updateTuple(conn, site_manager.site_name, f"sites", payload)
|
database.__updateTuple(conn, site_manager.site_name, f"sites", payload)
|
||||||
|
|
||||||
|
|
||||||
blank_vendor = MyDataclasses.VendorPayload("None", admin_user['id'])
|
blank_vendor = postsqldb.VendorsTable.Payload("None", admin_user['id'])
|
||||||
blank_brand = MyDataclasses.BrandsPayload("None")
|
blank_brand = postsqldb.BrandsTable.Payload("None")
|
||||||
|
|
||||||
blank_vendor = database.insertVendorsTuple(conn, site['site_name'], blank_vendor.payload(), convert=True)
|
blank_vendor = database.insertVendorsTuple(conn, site['site_name'], blank_vendor.payload(), convert=True)
|
||||||
blank_brand = database.insertBrandsTuple(conn, site['site_name'], blank_brand.payload(), convert=True)
|
blank_brand = database.insertBrandsTuple(conn, site['site_name'], blank_brand.payload(), convert=True)
|
||||||
|
|||||||
0
scripts/__init__.py
Normal file
0
scripts/__init__.py
Normal file
BIN
scripts/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/__pycache__/postsqldb.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/postsqldb.cpython-312.pyc
Normal file
Binary file not shown.
2346
scripts/postsqldb.py
Normal file
2346
scripts/postsqldb.py
Normal file
File diff suppressed because it is too large
Load Diff
0
scripts/recipes/__init__.py
Normal file
0
scripts/recipes/__init__.py
Normal file
BIN
scripts/recipes/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
scripts/recipes/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/recipes/__pycache__/database_recipes.cpython-312.pyc
Normal file
BIN
scripts/recipes/__pycache__/database_recipes.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/recipes/__pycache__/recipes_api.cpython-312.pyc
Normal file
BIN
scripts/recipes/__pycache__/recipes_api.cpython-312.pyc
Normal file
Binary file not shown.
25
scripts/recipes/database_recipes.py
Normal file
25
scripts/recipes/database_recipes.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
from scripts import postsqldb
|
||||||
|
import config
|
||||||
|
import psycopg2
|
||||||
|
|
||||||
|
def getModalSKUs(site, payload, convert=True):
|
||||||
|
database_config = config.config()
|
||||||
|
with psycopg2.connect(**database_config) as conn:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
with open("scripts/recipes/sql/itemsModal.sql") as file:
|
||||||
|
sql = file.read().replace("%%site_name%%", site)
|
||||||
|
cur.execute(sql, payload)
|
||||||
|
rows = cur.fetchall()
|
||||||
|
|
||||||
|
if rows and convert:
|
||||||
|
rows = [postsqldb.tupleDictionaryFactory(cur.description, row) for row in rows]
|
||||||
|
|
||||||
|
with open("scripts/recipes/sql/itemsModalCount.sql") as file:
|
||||||
|
sql = file.read().replace("%%site_name%%", site)
|
||||||
|
|
||||||
|
cur.execute(sql)
|
||||||
|
count = cur.fetchone()[0]
|
||||||
|
|
||||||
|
if rows and count:
|
||||||
|
return rows, count
|
||||||
|
return [], 0
|
||||||
@ -5,6 +5,7 @@ from main import unfoldCostLayers
|
|||||||
from user_api import login_required
|
from user_api import login_required
|
||||||
import os
|
import os
|
||||||
import postsqldb, webpush
|
import postsqldb, webpush
|
||||||
|
from scripts.recipes import database_recipes
|
||||||
|
|
||||||
recipes_api = Blueprint('recipes_api', __name__)
|
recipes_api = Blueprint('recipes_api', __name__)
|
||||||
|
|
||||||
@ -82,12 +83,10 @@ def getItems():
|
|||||||
search_string = request.args.get('search_string', 10)
|
search_string = request.args.get('search_string', 10)
|
||||||
site_name = session['selected_site']
|
site_name = session['selected_site']
|
||||||
offset = (page - 1) * limit
|
offset = (page - 1) * limit
|
||||||
database_config = config()
|
recordset, count = database_recipes.getModalSKUs(site_name, (limit, offset))
|
||||||
with psycopg2.connect(**database_config) as conn:
|
print(recordset)
|
||||||
payload = (search_string, limit, offset)
|
return jsonify({"items":recordset, "end":math.ceil(count/limit), "error":False, "message":"items fetched succesfully!"})
|
||||||
recordset, count = database.getItemsWithQOH(conn, site_name, payload, convert=True)
|
return jsonify({"items":recordset, "end":math.ceil(count/limit), "error":True, "message":"There was an error with this GET statement"})
|
||||||
return jsonify({"items":recordset, "end":math.ceil(count['count']/limit), "error":False, "message":"items fetched succesfully!"})
|
|
||||||
return jsonify({"items":recordset, "end":math.ceil(count['count']/limit), "error":True, "message":"There was an error with this GET statement"})
|
|
||||||
|
|
||||||
|
|
||||||
@recipes_api.route('/recipe/postUpdate', methods=["POST"])
|
@recipes_api.route('/recipe/postUpdate', methods=["POST"])
|
||||||
2
scripts/recipes/sql/itemsModal.sql
Normal file
2
scripts/recipes/sql/itemsModal.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
SELECT item.id, item.barcode, item.item_name FROM %%site_name%%_items item
|
||||||
|
LIMIT %s OFFSET %s;
|
||||||
1
scripts/recipes/sql/itemsModalCount.sql
Normal file
1
scripts/recipes/sql/itemsModalCount.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
SELECT COUNT(item.*) FROM %%site_name%%_items item;
|
||||||
@ -2,9 +2,5 @@ CREATE TABLE IF NOT EXISTS %%site_name%%_zones(
|
|||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
name VARCHAR(32) NOT NULL,
|
name VARCHAR(32) NOT NULL,
|
||||||
description TEXT,
|
description TEXT,
|
||||||
site_id INTEGER NOT NULL,
|
UNIQUE(name)
|
||||||
UNIQUE(name),
|
|
||||||
CONSTRAINT fk_site
|
|
||||||
FOREIGN KEY(site_id)
|
|
||||||
REFERENCES sites(id)
|
|
||||||
);
|
);
|
||||||
|
|||||||
6
sql/INSERT/insertLoginsTupleTwo.sql
Normal file
6
sql/INSERT/insertLoginsTupleTwo.sql
Normal file
@ -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 *;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
INSERT INTO %%site_name%%_zones
|
INSERT INTO %%site_name%%_zones
|
||||||
(name, description, site_id)
|
(name, description)
|
||||||
VALUES (%s, %s, %s)
|
VALUES (%s, %s)
|
||||||
RETURNING *;
|
RETURNING *;
|
||||||
16
sql/admin/SELECT/selectLoginsUser.sql
Normal file
16
sql/admin/SELECT/selectLoginsUser.sql
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
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_roles
|
||||||
|
FROM cte_login login;
|
||||||
@ -1,28 +0,0 @@
|
|||||||
async function clickRoleRow(role_id){
|
|
||||||
const roleurl = new URL(`/admin/editRole/${role_id}`, window.location.origin);
|
|
||||||
window.location.href = roleurl.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchSites() {
|
|
||||||
const url = new URL('/admin/getSites', window.location.origin);
|
|
||||||
const response = await fetch(url);
|
|
||||||
const data = await response.json();
|
|
||||||
return data.sites;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchUsers(limit, page) {
|
|
||||||
const url = new URL('/admin/getUsers', window.location.origin);
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
limit: limit,
|
|
||||||
page: page
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const data = await response.json();
|
|
||||||
return data.users;
|
|
||||||
}
|
|
||||||
|
|
||||||
BIN
static/files/receipts/20250425_163102.jpg
Normal file
BIN
static/files/receipts/20250425_163102.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
489
static/handlers/adminHandler.js
Normal file
489
static/handlers/adminHandler.js
Normal file
@ -0,0 +1,489 @@
|
|||||||
|
var mode = false
|
||||||
|
async function toggleDarkMode() {
|
||||||
|
let darkMode = document.getElementById("dark-mode");
|
||||||
|
darkMode.disabled = !darkMode.disabled;
|
||||||
|
mode = !mode;
|
||||||
|
if(mode){
|
||||||
|
document.getElementById('modeToggle').innerHTML = "light_mode"
|
||||||
|
document.getElementById('main_html').classList.add('uk-light')
|
||||||
|
} else {
|
||||||
|
document.getElementById('modeToggle').innerHTML = "dark_mode"
|
||||||
|
document.getElementById('main_html').classList.remove('uk-light')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(session.user.flags.darkmode){
|
||||||
|
toggleDarkMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', async function() {
|
||||||
|
let sites = await fetchSites()
|
||||||
|
await updateSitesPagination()
|
||||||
|
await replenishSitesTable(sites)
|
||||||
|
|
||||||
|
let roles = await fetchRoles()
|
||||||
|
await updateRolesPagination()
|
||||||
|
await replenishRolesTable(roles)
|
||||||
|
|
||||||
|
let logins = await fetchLogins()
|
||||||
|
console.log(logins)
|
||||||
|
await updateLoginsPagination()
|
||||||
|
await replenishLoginsTable(logins)
|
||||||
|
})
|
||||||
|
|
||||||
|
async function openDeleteModal(item_name, item_type, site_id) {
|
||||||
|
document.getElementById('delete_item_name').innerHTML = item_name
|
||||||
|
|
||||||
|
if(item_type == "site"){
|
||||||
|
document.getElementById("deleteSubmitButton").onclick = async function() {
|
||||||
|
await postDeleteSite(site_id, item_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UIkit.modal(document.getElementById('deleteConfirmation')).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Site functions
|
||||||
|
var sites_current_page = 1
|
||||||
|
var sites_end_page = 10
|
||||||
|
var sites_limit = 25
|
||||||
|
async function fetchSites(){
|
||||||
|
const url = new URL('/admin/getSites', window.location.origin)
|
||||||
|
url.searchParams.append('page', sites_current_page)
|
||||||
|
url.searchParams.append('limit', sites_limit)
|
||||||
|
const response = await fetch(url)
|
||||||
|
data = await response.json()
|
||||||
|
sites_end_page = data.end
|
||||||
|
return data.sites
|
||||||
|
}
|
||||||
|
|
||||||
|
async function replenishSitesTable(sites){
|
||||||
|
let sitesTableBody = document.getElementById('sitesTableBody')
|
||||||
|
sitesTableBody.innerHTML = ""
|
||||||
|
|
||||||
|
for(let i=0; i < sites.length; i++){
|
||||||
|
let tableRow = document.createElement('tr')
|
||||||
|
|
||||||
|
|
||||||
|
let idCell = document.createElement('td')
|
||||||
|
idCell.innerHTML = `${sites[i].id}`
|
||||||
|
let nameCell = document.createElement('td')
|
||||||
|
nameCell.innerHTML = `${sites[i].site_name}`
|
||||||
|
let descriptionCell = document.createElement('td')
|
||||||
|
descriptionCell.innerHTML = `${sites[i].site_description}`
|
||||||
|
let opCell = document.createElement('td')
|
||||||
|
opCell.innerHTML = ``
|
||||||
|
|
||||||
|
let editOp = document.createElement('a')
|
||||||
|
editOp.setAttribute('class', 'uk-button uk-button-small uk-button-default')
|
||||||
|
editOp.innerHTML = "edit"
|
||||||
|
editOp.href = `/admin/site/${sites[i].id}`
|
||||||
|
|
||||||
|
let deleteOp = document.createElement('a')
|
||||||
|
deleteOp.setAttribute('class', 'uk-button uk-button-small uk-button-default')
|
||||||
|
deleteOp.innerHTML = "delete"
|
||||||
|
deleteOp.onclick = async function() {
|
||||||
|
await openDeleteModal(sites[i].site_name, "site", sites[i].id)
|
||||||
|
}
|
||||||
|
|
||||||
|
opCell.append(editOp, deleteOp)
|
||||||
|
tableRow.append(idCell, nameCell, descriptionCell, opCell)
|
||||||
|
sitesTableBody.append(tableRow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateSitesPagination() {
|
||||||
|
let paginationElement = document.getElementById("sitesPagination");
|
||||||
|
paginationElement.innerHTML = "";
|
||||||
|
// previous
|
||||||
|
let previousElement = document.createElement('li')
|
||||||
|
if(sites_current_page<=1){
|
||||||
|
previousElement.innerHTML = `<a><span uk-pagination-previous></span></a>`;
|
||||||
|
previousElement.classList.add('uk-disabled');
|
||||||
|
}else {
|
||||||
|
previousElement.innerHTML = `<a onclick="setSitesPage(${sites_current_page-1})"><span uk-pagination-previous></span></a>`;
|
||||||
|
}
|
||||||
|
paginationElement.append(previousElement)
|
||||||
|
|
||||||
|
//first
|
||||||
|
let firstElement = document.createElement('li')
|
||||||
|
if(sites_current_page<=1){
|
||||||
|
firstElement.innerHTML = `<a>1</a>`;
|
||||||
|
firstElement.classList.add('uk-disabled');
|
||||||
|
}else {
|
||||||
|
firstElement.innerHTML = `<a onclick="setSitesPage(1)">1</a>`;
|
||||||
|
}
|
||||||
|
paginationElement.append(firstElement)
|
||||||
|
|
||||||
|
// ...
|
||||||
|
if(sites_current_page-2>1){
|
||||||
|
let firstDotElement = document.createElement('li')
|
||||||
|
firstDotElement.classList.add('uk-disabled')
|
||||||
|
firstDotElement.innerHTML = `<span>…</span>`;
|
||||||
|
paginationElement.append(firstDotElement)
|
||||||
|
}
|
||||||
|
// last
|
||||||
|
if(sites_current_page-2>0){
|
||||||
|
let lastElement = document.createElement('li')
|
||||||
|
lastElement.innerHTML = `<a onclick=setSitesPage(${sites_current_page-1})>${sites_current_page-1}</a>`
|
||||||
|
paginationElement.append(lastElement)
|
||||||
|
}
|
||||||
|
// current
|
||||||
|
if(sites_current_page!=1 && sites_current_page != sites_end_page){
|
||||||
|
let currentElement = document.createElement('li')
|
||||||
|
currentElement.innerHTML = `<li class="uk-active"><span aria-current="page"><strong>${sites_current_page}</strong></span></li>`
|
||||||
|
paginationElement.append(currentElement)
|
||||||
|
}
|
||||||
|
// next
|
||||||
|
if(sites_current_page+2<sites_end_page+1){
|
||||||
|
let nextElement = document.createElement('li')
|
||||||
|
nextElement.innerHTML = `<a onclick=setSitesPage(${sites_current_page+1})>${sites_current_page+1}</a>`
|
||||||
|
paginationElement.append(nextElement)
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
if(sites_current_page+2<=sites_end_page){
|
||||||
|
let secondDotElement = document.createElement('li')
|
||||||
|
secondDotElement.classList.add('uk-disabled')
|
||||||
|
secondDotElement.innerHTML = `<span>…</span>`;
|
||||||
|
paginationElement.append(secondDotElement)
|
||||||
|
}
|
||||||
|
//end
|
||||||
|
let endElement = document.createElement('li')
|
||||||
|
if(sites_current_page>=sites_end_page){
|
||||||
|
endElement.innerHTML = `<a>${sites_end_page}</a>`;
|
||||||
|
endElement.classList.add('uk-disabled');
|
||||||
|
}else {
|
||||||
|
endElement.innerHTML = `<a onclick="setSitesPage(${sites_end_page})">${sites_end_page}</a>`;
|
||||||
|
}
|
||||||
|
paginationElement.append(endElement)
|
||||||
|
//next button
|
||||||
|
let nextElement = document.createElement('li')
|
||||||
|
if(sites_current_page>=sites_end_page){
|
||||||
|
nextElement.innerHTML = `<a><span uk-pagination-next></span></a>`;
|
||||||
|
nextElement.classList.add('uk-disabled');
|
||||||
|
}else {
|
||||||
|
nextElement.innerHTML = `<a onclick="setSitesPage(${sites_current_page+1})"><span uk-pagination-next></span></a>`;
|
||||||
|
}
|
||||||
|
paginationElement.append(nextElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setSitesPage(pageNumber){
|
||||||
|
sites_current_page = pageNumber;
|
||||||
|
let sites = await fetchSites()
|
||||||
|
await updateSitesPagination()
|
||||||
|
await replenishSitesTable(sites)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function postDeleteSite(site_id, item_name){
|
||||||
|
let valid = document.getElementById('delete_input')
|
||||||
|
if(valid.value==item_name){
|
||||||
|
valid.classList.remove('uk-form-danger')
|
||||||
|
const response = await fetch(`/admin/site/postDeleteSite`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
site_id: site_id
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
data = await response.json();
|
||||||
|
transaction_status = "success"
|
||||||
|
if (data.error){
|
||||||
|
transaction_status = "danger"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
valid.classList.add('uk-form-danger')
|
||||||
|
data = {'message': 'You did not confirm the item correctly!!'}
|
||||||
|
transaction_status = "danger"
|
||||||
|
}
|
||||||
|
|
||||||
|
UIkit.notification({
|
||||||
|
message: data.message,
|
||||||
|
status: transaction_status,
|
||||||
|
pos: 'top-right',
|
||||||
|
timeout: 5000
|
||||||
|
});
|
||||||
|
let sites = await fetchSites()
|
||||||
|
await updateSitesPagination()
|
||||||
|
await replenishSitesTable(sites)
|
||||||
|
UIkit.modal(document.getElementById('deleteConfirmation')).hide();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Roles functions
|
||||||
|
var roles_current_page = 1
|
||||||
|
var roles_end_page = 10
|
||||||
|
var roles_limit = 25
|
||||||
|
async function fetchRoles(){
|
||||||
|
const url = new URL('/admin/getRoles', window.location.origin)
|
||||||
|
url.searchParams.append('page', roles_current_page)
|
||||||
|
url.searchParams.append('limit', roles_limit)
|
||||||
|
const response = await fetch(url)
|
||||||
|
data = await response.json()
|
||||||
|
roles_end_page = data.end
|
||||||
|
return data.roles
|
||||||
|
}
|
||||||
|
|
||||||
|
async function replenishRolesTable(roles){
|
||||||
|
let rolesTableBody = document.getElementById('rolesTableBody')
|
||||||
|
rolesTableBody.innerHTML = ""
|
||||||
|
|
||||||
|
for(let i=0; i < roles.length; i++){
|
||||||
|
let tableRow = document.createElement('tr')
|
||||||
|
|
||||||
|
|
||||||
|
let idCell = document.createElement('td')
|
||||||
|
idCell.innerHTML = `${roles[i].id}`
|
||||||
|
let nameCell = document.createElement('td')
|
||||||
|
nameCell.innerHTML = `${roles[i].role_name}`
|
||||||
|
let descriptionCell = document.createElement('td')
|
||||||
|
descriptionCell.innerHTML = `${roles[i].role_description}`
|
||||||
|
let siteCell = document.createElement('td')
|
||||||
|
siteCell.innerHTML = `${roles[i].site.site_name}`
|
||||||
|
let opCell = document.createElement('td')
|
||||||
|
opCell.innerHTML = ``
|
||||||
|
|
||||||
|
let editOp = document.createElement('a')
|
||||||
|
editOp.setAttribute('class', 'uk-button uk-button-small uk-button-default')
|
||||||
|
editOp.innerHTML = "edit"
|
||||||
|
editOp.href = `/admin/role/${roles[i].id}`
|
||||||
|
|
||||||
|
let deleteOp = document.createElement('a')
|
||||||
|
deleteOp.setAttribute('class', 'uk-button uk-button-small uk-button-default')
|
||||||
|
deleteOp.innerHTML = "delete"
|
||||||
|
deleteOp.onclick = async function() {
|
||||||
|
await openDeleteModal(roles[i].role_name, "role", roles[i].id)
|
||||||
|
}
|
||||||
|
|
||||||
|
opCell.append(editOp, deleteOp)
|
||||||
|
tableRow.append(idCell, nameCell, descriptionCell, siteCell, opCell)
|
||||||
|
rolesTableBody.append(tableRow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateRolesPagination() {
|
||||||
|
let paginationElement = document.getElementById("rolesPagination");
|
||||||
|
paginationElement.innerHTML = "";
|
||||||
|
// previous
|
||||||
|
let previousElement = document.createElement('li')
|
||||||
|
if(roles_current_page<=1){
|
||||||
|
previousElement.innerHTML = `<a><span uk-pagination-previous></span></a>`;
|
||||||
|
previousElement.classList.add('uk-disabled');
|
||||||
|
}else {
|
||||||
|
previousElement.innerHTML = `<a onclick="setRolesPage(${roles_current_page-1})"><span uk-pagination-previous></span></a>`;
|
||||||
|
}
|
||||||
|
paginationElement.append(previousElement)
|
||||||
|
|
||||||
|
//first
|
||||||
|
let firstElement = document.createElement('li')
|
||||||
|
if(roles_current_page<=1){
|
||||||
|
firstElement.innerHTML = `<a>1</a>`;
|
||||||
|
firstElement.classList.add('uk-disabled');
|
||||||
|
}else {
|
||||||
|
firstElement.innerHTML = `<a onclick="setRolesPage(1)">1</a>`;
|
||||||
|
}
|
||||||
|
paginationElement.append(firstElement)
|
||||||
|
|
||||||
|
// ...
|
||||||
|
if(roles_current_page-2>1){
|
||||||
|
let firstDotElement = document.createElement('li')
|
||||||
|
firstDotElement.classList.add('uk-disabled')
|
||||||
|
firstDotElement.innerHTML = `<span>…</span>`;
|
||||||
|
paginationElement.append(firstDotElement)
|
||||||
|
}
|
||||||
|
// last
|
||||||
|
if(roles_current_page-2>0){
|
||||||
|
let lastElement = document.createElement('li')
|
||||||
|
lastElement.innerHTML = `<a onclick=setRolesPage(${roles_current_page-1})>${roles_current_page-1}</a>`
|
||||||
|
paginationElement.append(lastElement)
|
||||||
|
}
|
||||||
|
// current
|
||||||
|
if(roles_current_page!=1 && roles_current_page != roles_end_page){
|
||||||
|
let currentElement = document.createElement('li')
|
||||||
|
currentElement.innerHTML = `<li class="uk-active"><span aria-current="page"><strong>${roles_current_page}</strong></span></li>`
|
||||||
|
paginationElement.append(currentElement)
|
||||||
|
}
|
||||||
|
// next
|
||||||
|
if(roles_current_page+2<roles_end_page+1){
|
||||||
|
let nextElement = document.createElement('li')
|
||||||
|
nextElement.innerHTML = `<a onclick=setRolesPage(${roles_current_page+1})>${roles_current_page+1}</a>`
|
||||||
|
paginationElement.append(nextElement)
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
if(roles_current_page+2<=roles_end_page){
|
||||||
|
let secondDotElement = document.createElement('li')
|
||||||
|
secondDotElement.classList.add('uk-disabled')
|
||||||
|
secondDotElement.innerHTML = `<span>…</span>`;
|
||||||
|
paginationElement.append(secondDotElement)
|
||||||
|
}
|
||||||
|
//end
|
||||||
|
let endElement = document.createElement('li')
|
||||||
|
if(roles_current_page>=roles_end_page){
|
||||||
|
endElement.innerHTML = `<a>${roles_end_page}</a>`;
|
||||||
|
endElement.classList.add('uk-disabled');
|
||||||
|
}else {
|
||||||
|
endElement.innerHTML = `<a onclick="setRolesPage(${roles_end_page})">${roles_end_page}</a>`;
|
||||||
|
}
|
||||||
|
paginationElement.append(endElement)
|
||||||
|
//next button
|
||||||
|
let nextElement = document.createElement('li')
|
||||||
|
if(roles_current_page>=roles_end_page){
|
||||||
|
nextElement.innerHTML = `<a><span uk-pagination-next></span></a>`;
|
||||||
|
nextElement.classList.add('uk-disabled');
|
||||||
|
}else {
|
||||||
|
nextElement.innerHTML = `<a onclick="setRolesPage(${roles_current_page+1})"><span uk-pagination-next></span></a>`;
|
||||||
|
}
|
||||||
|
paginationElement.append(nextElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setRolesPage(pageNumber){
|
||||||
|
roles_current_page = pageNumber;
|
||||||
|
let roles = await fetchRoles()
|
||||||
|
await updateRolesPagination()
|
||||||
|
await replenishRolesTable(roles)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// users/devices functions
|
||||||
|
var logins_current_page = 1
|
||||||
|
var logins_end_page = 10
|
||||||
|
var logins_limit = 25
|
||||||
|
async function fetchLogins(){
|
||||||
|
const url = new URL('/admin/getLogins', window.location.origin)
|
||||||
|
url.searchParams.append('page', logins_current_page)
|
||||||
|
url.searchParams.append('limit', logins_limit)
|
||||||
|
const response = await fetch(url)
|
||||||
|
data = await response.json()
|
||||||
|
logins_end_page = data.end
|
||||||
|
return data.logins
|
||||||
|
}
|
||||||
|
|
||||||
|
async function replenishLoginsTable(logins){
|
||||||
|
let usersTableBody = document.getElementById('usersTableBody')
|
||||||
|
usersTableBody.innerHTML = ""
|
||||||
|
|
||||||
|
for(let i=0; i < logins.length; i++){
|
||||||
|
let tableRow = document.createElement('tr')
|
||||||
|
|
||||||
|
|
||||||
|
let idCell = document.createElement('td')
|
||||||
|
idCell.innerHTML = `${logins[i].id}`
|
||||||
|
let nameCell = document.createElement('td')
|
||||||
|
nameCell.innerHTML = `${logins[i].username}`
|
||||||
|
|
||||||
|
let emailCell = document.createElement('td')
|
||||||
|
emailCell.innerHTML = `${logins[i].email}`
|
||||||
|
|
||||||
|
let typeCell = document.createElement('td')
|
||||||
|
typeCell.innerHTML = `${logins[i].row_type}`
|
||||||
|
|
||||||
|
|
||||||
|
let opCell = document.createElement('td')
|
||||||
|
opCell.innerHTML = ``
|
||||||
|
|
||||||
|
let editOp = document.createElement('a')
|
||||||
|
editOp.setAttribute('class', 'uk-button uk-button-small uk-button-default')
|
||||||
|
editOp.innerHTML = "edit"
|
||||||
|
editOp.href = `/admin/user/${logins[i].id}`
|
||||||
|
|
||||||
|
let deleteOp = document.createElement('a')
|
||||||
|
deleteOp.setAttribute('class', 'uk-button uk-button-small uk-button-default')
|
||||||
|
deleteOp.innerHTML = "delete"
|
||||||
|
deleteOp.onclick = async function() {
|
||||||
|
await openDeleteModal(logins[i].username, "login", logins[i].id)
|
||||||
|
}
|
||||||
|
|
||||||
|
opCell.append(editOp, deleteOp)
|
||||||
|
tableRow.append(idCell, nameCell, emailCell, typeCell, opCell)
|
||||||
|
usersTableBody.append(tableRow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateLoginsPagination() {
|
||||||
|
let paginationElement = document.getElementById("usersPagination");
|
||||||
|
paginationElement.innerHTML = "";
|
||||||
|
// previous
|
||||||
|
let previousElement = document.createElement('li')
|
||||||
|
if(logins_current_page<=1){
|
||||||
|
previousElement.innerHTML = `<a><span uk-pagination-previous></span></a>`;
|
||||||
|
previousElement.classList.add('uk-disabled');
|
||||||
|
}else {
|
||||||
|
previousElement.innerHTML = `<a onclick="setLoginsPage(${logins_current_page-1})"><span uk-pagination-previous></span></a>`;
|
||||||
|
}
|
||||||
|
paginationElement.append(previousElement)
|
||||||
|
|
||||||
|
//first
|
||||||
|
let firstElement = document.createElement('li')
|
||||||
|
if(logins_current_page<=1){
|
||||||
|
firstElement.innerHTML = `<a>1</a>`;
|
||||||
|
firstElement.classList.add('uk-disabled');
|
||||||
|
}else {
|
||||||
|
firstElement.innerHTML = `<a onclick="setLoginsPage(1)">1</a>`;
|
||||||
|
}
|
||||||
|
paginationElement.append(firstElement)
|
||||||
|
|
||||||
|
// ...
|
||||||
|
if(logins_current_page-2>1){
|
||||||
|
let firstDotElement = document.createElement('li')
|
||||||
|
firstDotElement.classList.add('uk-disabled')
|
||||||
|
firstDotElement.innerHTML = `<span>…</span>`;
|
||||||
|
paginationElement.append(firstDotElement)
|
||||||
|
}
|
||||||
|
// last
|
||||||
|
if(logins_current_page-2>0){
|
||||||
|
let lastElement = document.createElement('li')
|
||||||
|
lastElement.innerHTML = `<a onclick=setLoginsPage(${logins_current_page-1})>${logins_current_page-1}</a>`
|
||||||
|
paginationElement.append(lastElement)
|
||||||
|
}
|
||||||
|
// current
|
||||||
|
if(logins_current_page!=1 && logins_current_page != logins_end_page){
|
||||||
|
let currentElement = document.createElement('li')
|
||||||
|
currentElement.innerHTML = `<li class="uk-active"><span aria-current="page"><strong>${logins_current_page}</strong></span></li>`
|
||||||
|
paginationElement.append(currentElement)
|
||||||
|
}
|
||||||
|
// next
|
||||||
|
if(logins_current_page+2<logins_end_page+1){
|
||||||
|
let nextElement = document.createElement('li')
|
||||||
|
nextElement.innerHTML = `<a onclick=setLoginsPage(${logins_current_page+1})>${logins_current_page+1}</a>`
|
||||||
|
paginationElement.append(nextElement)
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
if(logins_current_page+2<=logins_end_page){
|
||||||
|
let secondDotElement = document.createElement('li')
|
||||||
|
secondDotElement.classList.add('uk-disabled')
|
||||||
|
secondDotElement.innerHTML = `<span>…</span>`;
|
||||||
|
paginationElement.append(secondDotElement)
|
||||||
|
}
|
||||||
|
//end
|
||||||
|
let endElement = document.createElement('li')
|
||||||
|
if(logins_current_page>=logins_end_page){
|
||||||
|
endElement.innerHTML = `<a>${logins_end_page}</a>`;
|
||||||
|
endElement.classList.add('uk-disabled');
|
||||||
|
}else {
|
||||||
|
endElement.innerHTML = `<a onclick="setLoginsPage(${logins_end_page})">${logins_end_page}</a>`;
|
||||||
|
}
|
||||||
|
paginationElement.append(endElement)
|
||||||
|
//next button
|
||||||
|
let nextElement = document.createElement('li')
|
||||||
|
if(logins_current_page>=logins_end_page){
|
||||||
|
nextElement.innerHTML = `<a><span uk-pagination-next></span></a>`;
|
||||||
|
nextElement.classList.add('uk-disabled');
|
||||||
|
}else {
|
||||||
|
nextElement.innerHTML = `<a onclick="setLoginsPage(${logins_current_page+1})"><span uk-pagination-next></span></a>`;
|
||||||
|
}
|
||||||
|
paginationElement.append(nextElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setLoginsPage(pageNumber){
|
||||||
|
logins_current_page = pageNumber;
|
||||||
|
let logins = await fetchLogins()
|
||||||
|
await updateLoginsPagination()
|
||||||
|
await replenishLoginsTable(logins)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// uom functions
|
||||||
|
async function test() {
|
||||||
|
console.log('test')
|
||||||
|
}
|
||||||
281
templates/admin/index.html
Normal file
281
templates/admin/index.html
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" dir="ltr" id="main_html">
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
|
<title id="title"></title>
|
||||||
|
<!-- Material Icons -->
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
|
<!-- Material Symbols - Outlined Set -->
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
|
||||||
|
<!-- Material Symbols - Rounded Set -->
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded" rel="stylesheet" />
|
||||||
|
<!-- Material Symbols - Sharp Set -->
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/uikit.min.css') }}"/>
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/pantry.css') }}"/>
|
||||||
|
|
||||||
|
<link id="dark-mode" rel="stylesheet" href="{{ url_for('static', filename='css/dark-mode.css') }}" disabled/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div uk-sticky="sel-target: .uk-navbar-container; cls-active: uk-navbar-sticky">
|
||||||
|
<!-- to color the navbar i have to stlye this element the nav element -->
|
||||||
|
<nav id="navbar" class="uk-navbar-container">
|
||||||
|
<div class="uk-container uk-container-expand">
|
||||||
|
<div uk-navbar="dropbar: true">
|
||||||
|
<div id="offcanvas-slide" uk-offcanvas="mode: slide; overlay: true">
|
||||||
|
<div class="uk-offcanvas-bar uk-flex uk-flex-column">
|
||||||
|
<ul class="uk-nav uk-nav-secondary">
|
||||||
|
<img class="uk-align-center uk-border-circle" data-src="{{ url_for('static', filename='pictures/logo.jpg') }}" style="width: 150px; height: auto;" uk-img />
|
||||||
|
<li class="uk-nav-header">Apps</li>
|
||||||
|
<li><a href="/shopping-lists">Shopping Lists</a></li>
|
||||||
|
<li><a href="/recipes">Recipes</a></li>
|
||||||
|
<li class="uk-nav-header">Logistics</li>
|
||||||
|
<li><a href="/items">Items</a></li>
|
||||||
|
<li><a href="/transaction">Add Transaction</a></li>
|
||||||
|
<li>
|
||||||
|
<a href="/workshop">
|
||||||
|
<div class="uk-active">Workshop<div class="uk-nav-subtitle" disabled>Building in the workshop...</div>
|
||||||
|
</div></a>
|
||||||
|
</li>
|
||||||
|
<li><a href="/receipts">Receipts</a></li>
|
||||||
|
<li class="uk-nav-header">System Management</li>
|
||||||
|
<li class="uk-disabled" hidden><a><div>{{current_site}}<div class="uk-nav-subtitle">This is the current site you are viewing...</div></div></a>
|
||||||
|
<div uk-dropdown="mode: click">
|
||||||
|
<ul class="uk-nav uk-dropdown-nav">
|
||||||
|
{% for site in sites %}
|
||||||
|
{% if site == current_site %}
|
||||||
|
<li><a class="uk-disabled" href="#">{{site}}</a></li>
|
||||||
|
{% else %}
|
||||||
|
<li><a onclick="changeSite('{{site}}')">{{site}}</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% if system_admin %}
|
||||||
|
<li><a href="/admin">Administration</a></li>
|
||||||
|
{% endif %}
|
||||||
|
<li><a href="" class="">{{username}}</a></li>
|
||||||
|
</ul>
|
||||||
|
<button class="uk-button uk-margin-small uk-position-top-right" uk-icon="icon: close" href=""></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-navbar-left uk-margin-small">
|
||||||
|
<a href="#offcanvas-slide" class="uk-button uk-button-default uk-button-small" uk-icon="icon: menu" uk-toggle> Menu</a>
|
||||||
|
</div>
|
||||||
|
<div class="uk-navbar-center uk-margin-small uk-visible@s">
|
||||||
|
<ul class="uk-breadcrumb">
|
||||||
|
<li style="cursor: default;"><span><strong>Administration</strong></span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="uk-navbar-right">
|
||||||
|
<div>
|
||||||
|
<a onclick="toggleDarkMode()" class="uk-button uk-button-small"><span id="modeToggle" class="uk-flex material-symbols-outlined">dark_mode</span></a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a href="" class="" uk-icon="icon: user" uk-toggle>{{username}}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="uk-section uk-margin-left">
|
||||||
|
<div class="uk-child-width-1-1" uk-grid>
|
||||||
|
<div>
|
||||||
|
<div uk-grid>
|
||||||
|
<div class="uk-width-auto uk-margin-left">
|
||||||
|
<ul class="uk-tab-right" uk-tab="connect: #component-tab-left; animation: uk-animation-fade">
|
||||||
|
<li><a href="#">Site</a></li>
|
||||||
|
<li><a href="#">Roles</a></li>
|
||||||
|
<li><a href="#">Users/Devices</a></li>
|
||||||
|
<li><a href="#">Units of Measure</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-expand">
|
||||||
|
<div id="component-tab-left" class="uk-switcher">
|
||||||
|
<!-- sites -->
|
||||||
|
<div class="uk-container">
|
||||||
|
<div uk-grid>
|
||||||
|
<div class="uk-width-1-1 uk-flex uk-flex-center">
|
||||||
|
<nav aria-label="Pagination">
|
||||||
|
<ul id="sitesPagination" class="uk-pagination" uk-margin>
|
||||||
|
<li><a href="#"><span uk-pagination-previous></span></a></li>
|
||||||
|
<li><a href="#">1</a></li>
|
||||||
|
<li class="uk-disabled"><span>…</span></li>
|
||||||
|
<li><a href="#">5</a></li>
|
||||||
|
<li><a href="#">6</a></li>
|
||||||
|
<li class="uk-active"><span aria-current="page">7</span></li>
|
||||||
|
<li><a href="#">8</a></li>
|
||||||
|
<li><a href="#"><span uk-pagination-next></span></a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<caption class="uk-text-meta"></caption>
|
||||||
|
<table style="margin-bottom: 0px;" class="uk-table uk-table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Site Name</th>
|
||||||
|
<th>Site Description</th>
|
||||||
|
<th>Operations</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="sitesTableBody">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<a href="/admin/site/new" class="uk-button add-button"><i class="uk-flex-middle material-symbols-outlined" style="">add_circle</i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Roles -->
|
||||||
|
<div class="uk-container">
|
||||||
|
<div uk-grid>
|
||||||
|
<div class="uk-width-1-1 uk-flex uk-flex-center">
|
||||||
|
<nav aria-label="Pagination">
|
||||||
|
<ul id="rolesPagination" class="uk-pagination" uk-margin>
|
||||||
|
<li><a href="#"><span uk-pagination-previous></span></a></li>
|
||||||
|
<li><a href="#">1</a></li>
|
||||||
|
<li class="uk-disabled"><span>…</span></li>
|
||||||
|
<li><a href="#">5</a></li>
|
||||||
|
<li><a href="#">6</a></li>
|
||||||
|
<li class="uk-active"><span aria-current="page">7</span></li>
|
||||||
|
<li><a href="#">8</a></li>
|
||||||
|
<li><a href="#"><span uk-pagination-next></span></a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<caption class="uk-text-meta"></caption>
|
||||||
|
<table style="margin-bottom: 0px;" class="uk-table uk-table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Roles Name</th>
|
||||||
|
<th>Role Description</th>
|
||||||
|
<th>Site</th>
|
||||||
|
<th>Operations</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="rolesTableBody">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<a href="/admin/role/new" class="uk-button add-button"><i class="uk-flex-middle material-symbols-outlined" style="">add_circle</i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Users/Devices -->
|
||||||
|
<div class="uk-container">
|
||||||
|
<div uk-grid>
|
||||||
|
<div class="uk-width-1-1 uk-flex uk-flex-center">
|
||||||
|
<nav aria-label="Pagination">
|
||||||
|
<ul id="usersPagination" class="uk-pagination" uk-margin>
|
||||||
|
<li><a href="#"><span uk-pagination-previous></span></a></li>
|
||||||
|
<li><a href="#">1</a></li>
|
||||||
|
<li class="uk-disabled"><span>…</span></li>
|
||||||
|
<li><a href="#">5</a></li>
|
||||||
|
<li><a href="#">6</a></li>
|
||||||
|
<li class="uk-active"><span aria-current="page">7</span></li>
|
||||||
|
<li><a href="#">8</a></li>
|
||||||
|
<li><a href="#"><span uk-pagination-next></span></a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<caption class="uk-text-meta"></caption>
|
||||||
|
<table style="margin-bottom: 0px;" class="uk-table uk-table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Username</th>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Operations</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="usersTableBody">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<a href="/admin/user/new" class="uk-button add-button"><i class="uk-flex-middle material-symbols-outlined" style="">add_circle</i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Units of Measure -->
|
||||||
|
<div class="uk-container">
|
||||||
|
<div uk-grid>
|
||||||
|
<div class="uk-width-1-1 uk-flex uk-flex-center">
|
||||||
|
<nav aria-label="Pagination">
|
||||||
|
<ul id="uomPagination" class="uk-pagination" uk-margin>
|
||||||
|
<li><a href="#"><span uk-pagination-previous></span></a></li>
|
||||||
|
<li><a href="#">1</a></li>
|
||||||
|
<li class="uk-disabled"><span>…</span></li>
|
||||||
|
<li><a href="#">5</a></li>
|
||||||
|
<li><a href="#">6</a></li>
|
||||||
|
<li class="uk-active"><span aria-current="page">7</span></li>
|
||||||
|
<li><a href="#">8</a></li>
|
||||||
|
<li><a href="#"><span uk-pagination-next></span></a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<caption class="uk-text-meta"></caption>
|
||||||
|
<table style="margin-bottom: 0px;" class="uk-table uk-table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Fullname</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Operations</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="uomTableBody">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button onclick="" class="uk-button add-button"><i class="uk-flex-middle material-symbols-outlined" style="">add_circle</i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Modals -->
|
||||||
|
<!-- Sites Modal -->
|
||||||
|
<div id="deleteConfirmation" class="uk-modal-container" uk-modal>
|
||||||
|
<div class="uk-modal-dialog">
|
||||||
|
<button class="uk-modal-close-default" type="button" uk-close></button>
|
||||||
|
<div class="uk-modal-header">
|
||||||
|
<h2 class="uk-modal-title">DELETE</h2>
|
||||||
|
</div>
|
||||||
|
<div class="uk-modal-body">
|
||||||
|
<div uk-grid>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<p>You are attempting to delete something important, in order to ensure this is your intent, please type in the name of the
|
||||||
|
item you were going to delete</p>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<h3 id="delete_item_name"></h3>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div class="uk-margin">
|
||||||
|
<input id="delete_input" class="uk-input" type="text">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-modal-footer uk-text-right">
|
||||||
|
<button class="uk-button uk-button-default uk-modal-close" type="button">Cancel</button>
|
||||||
|
<button id="deleteSubmitButton" class="uk-button uk-button-danger" type="button">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
{% assets "js_all" %}
|
||||||
|
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||||
|
{% endassets %}
|
||||||
|
<script>const session = {{session|tojson}}</script>
|
||||||
|
<script src="{{ url_for('static', filename='handlers/adminHandler.js') }}"></script>
|
||||||
|
</html>
|
||||||
@ -1,130 +1,164 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en" dir="ltr">
|
<html lang="en" dir="ltr" id="main_html">
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title>Edit Role</title>
|
<title id="title"></title>
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/css/materialize.min.css" />
|
<!-- Material Icons -->
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" />
|
<!-- Material Symbols - Outlined Set -->
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/js/materialize.min.js"></script>
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
|
||||||
|
<!-- Material Symbols - Rounded Set -->
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded" rel="stylesheet" />
|
||||||
|
<!-- Material Symbols - Sharp Set -->
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/uikit.min.css') }}"/>
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/pantry.css') }}"/>
|
||||||
|
|
||||||
|
<link id="dark-mode" rel="stylesheet" href="{{ url_for('static', filename='css/dark-mode.css') }}" disabled/>
|
||||||
</head>
|
</head>
|
||||||
<style>
|
|
||||||
|
|
||||||
.dropdown-disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
.item :hover{
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: whitesmoke;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom_row:hover{
|
|
||||||
background-color: rgb(230, 230, 230) !important;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="uk-container uk-section">
|
||||||
<div class="section">
|
<div uk-grid>
|
||||||
<div class="row" style="gap: 1em;">
|
<div class="uk-width-1-1" >
|
||||||
<div class="col s12" style="padding-bottom: 10px;">
|
<a href="/admin" class="uk-button uk-button-small"><span class="uk-flex material-symbols-outlined">arrow_back</span></a>
|
||||||
<a href='{{ proto['referrer'] }}' class="left btn green lighten-4 black-text btn-flat"><i class="material-icons">arrow_back</i></a>
|
<a onclick="toggleDarkMode()" class="uk-button uk-button-small uk-align-right"><span id="modeToggle" class="uk-flex material-symbols-outlined">dark_mode</span></a>
|
||||||
<a href="/items" class="btn btn-flat right">Home</a>
|
|
||||||
<a href="/profile" class="btn btn-flat right">Profile</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="s12 m6 input-field">
|
<div class="uk-width-1-1">
|
||||||
<input id="role_name" type="text" placeholder=" " maxlength="20">
|
<h3 class="uk-text-center">Role Form</h3>
|
||||||
<label for="role_name">Name</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="input-field col s12">
|
<div class="uk-width-1-1">
|
||||||
<select id="role_site">
|
<p>Roles exist to harness a users/devices access to a sites specific apps and inputs. You use roles to better define a groups permissions
|
||||||
<option value="" disabled selected>Choose your option</option>
|
within the app in order to control who has access to what. Specific User/Device permissions will overwrite any of their roles, permissions.</p>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<hr class="uk-divider-icon">
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div class="uk-margin">
|
||||||
|
<label class="uk-form-label" for="role_name">Role Name</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<input id="role_name" class="uk-input" type="text">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<label class="uk-form-label" for="role_description">Role Description</label>
|
||||||
|
<div class="uk-margin">
|
||||||
|
<textarea id="role_description" class="uk-textarea" rows="5" aria-label="Textarea"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div class="uk-margin">
|
||||||
|
<label class="uk-form-label" for="site_id">Select</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<select id="site_id" class="uk-select">
|
||||||
|
{% for site in sites %}
|
||||||
|
<option value="{{site['id']}}">{{site['site_name']}}</option>
|
||||||
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<label for="role_site">Site</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="input-field col s12">
|
|
||||||
<textarea id="role_description" class="materialize-textarea" placeholder=" "></textarea>
|
|
||||||
<label for="role_description">Description</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col s12">
|
|
||||||
<h4>Permissions</h4>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col s12">
|
<div class="uk-width-1-1">
|
||||||
<h5>All</h5>
|
<hr class="uk-divider-icon">
|
||||||
</div>
|
|
||||||
<div class="col s12 m6">
|
|
||||||
<p>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" />
|
|
||||||
<span>Edit</span>
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" />
|
|
||||||
<span>Delete</span>
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="col s12 m6">
|
|
||||||
<p>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" />
|
|
||||||
<span>Create</span>
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" />
|
|
||||||
<span>View</span>
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="col s12">
|
|
||||||
<button class="btn btn-flat green lighten-4 right" onclick="updateRole()">Update</button>
|
|
||||||
<button class="btn btn-flat red lighten-4 right" style="margin-right: 10px;" onclick="deleteRole()">Delete</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<button id="SubmitButton" class="uk-button uk-button-primary uk-align-right">Save</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
<script src="{{ url_for('static', filename='adminHandler.js') }}"></script>
|
{% assets "js_all" %}
|
||||||
|
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||||
|
{% endassets %}
|
||||||
<script>
|
<script>
|
||||||
let role = {{role|tojson}}
|
let role = {{role|tojson}}
|
||||||
|
const session = {{session|tojson}}
|
||||||
|
const path = window.location.pathname
|
||||||
console.log(role)
|
console.log(role)
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', async function() {
|
document.addEventListener('DOMContentLoaded', async function() {
|
||||||
var elems = document.querySelectorAll('select');
|
let mode = "edit"
|
||||||
var instances = M.FormSelect.init(elems, {})
|
if(path == "/admin/role/new"){
|
||||||
await populateRoleForm()
|
mode = "new"
|
||||||
});
|
}
|
||||||
|
await replenishForm(role, mode)
|
||||||
|
})
|
||||||
|
|
||||||
async function populateRoleForm() {
|
var mode = false
|
||||||
document.getElementById("role_name").value = role[1];
|
async function toggleDarkMode() {
|
||||||
document.getElementById("role_description").value = role[2];
|
let darkMode = document.getElementById("dark-mode");
|
||||||
const selectElement = document.getElementById("role_site");
|
darkMode.disabled = !darkMode.disabled;
|
||||||
selectElement.innerHTML = "";
|
mode = !mode;
|
||||||
|
if(mode){
|
||||||
var sites = await fetchSites()
|
document.getElementById('modeToggle').innerHTML = "light_mode"
|
||||||
for (let i = 0; i < sites.length; i++){
|
document.getElementById('main_html').classList.add('uk-light')
|
||||||
let newOption = document.createElement("option");
|
} else {
|
||||||
newOption.value = sites[i][0];
|
document.getElementById('modeToggle').innerHTML = "dark_mode"
|
||||||
newOption.text = sites[i][1];
|
document.getElementById('main_html').classList.remove('uk-light')
|
||||||
selectElement.appendChild(newOption);
|
}
|
||||||
};
|
|
||||||
selectElement.value = role[3];
|
|
||||||
M.FormSelect.init(selectElement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteRole(){
|
if(session.user.flags.darkmode){
|
||||||
const url = new URL('/deleteRole', window.location.origin);
|
toggleDarkMode()
|
||||||
await fetch(url, {
|
}
|
||||||
|
|
||||||
|
async function replenishForm(role, mode){
|
||||||
|
document.getElementById('role_name').value = role.role_name
|
||||||
|
document.getElementById('role_description').value = role.role_description
|
||||||
|
document.getElementById('site_id').value = role.site_id
|
||||||
|
|
||||||
|
if(mode=="new"){
|
||||||
|
document.getElementById('site_id').classList.remove('uk-disabled')
|
||||||
|
document.getElementById('SubmitButton').innerHTML = "Add Role"
|
||||||
|
document.getElementById('SubmitButton').onclick = async function(){
|
||||||
|
await postAddRole()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
document.getElementById('site_id').classList.add('uk-disabled')
|
||||||
|
document.getElementById('SubmitButton').innerHTML = "Update Role"
|
||||||
|
document.getElementById('SubmitButton').onclick = async function(){
|
||||||
|
await postEditRole()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async function postAddRole(){
|
||||||
|
|
||||||
|
let payload = {
|
||||||
|
role_name: document.getElementById('role_name').value,
|
||||||
|
role_description: document.getElementById('role_description').value,
|
||||||
|
site_id: document.getElementById('site_id').value,
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`/admin/role/postAddRole`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {'Content-Type': 'application/json',},
|
headers: {
|
||||||
body: JSON.stringify({role_id: role[0],}),
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
payload: payload
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function postEditRole(){
|
||||||
|
|
||||||
|
let payload = {
|
||||||
|
id: role.id,
|
||||||
|
update: {role_name: document.getElementById('role_name').value, role_description: document.getElementById('role_description').value}
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`/admin/role/postEditRole`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
payload: payload
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
const adminurl = new URL(`/admin`, window.location.origin);
|
|
||||||
window.location.href = adminurl.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
240
templates/admin/site.html
Normal file
240
templates/admin/site.html
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" dir="ltr" id="main_html">
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
|
<title id="title"></title>
|
||||||
|
<!-- Material Icons -->
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
|
<!-- Material Symbols - Outlined Set -->
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
|
||||||
|
<!-- Material Symbols - Rounded Set -->
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded" rel="stylesheet" />
|
||||||
|
<!-- Material Symbols - Sharp Set -->
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/uikit.min.css') }}"/>
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/pantry.css') }}"/>
|
||||||
|
|
||||||
|
<link id="dark-mode" rel="stylesheet" href="{{ url_for('static', filename='css/dark-mode.css') }}" disabled/>
|
||||||
|
</head>
|
||||||
|
<body id="test">
|
||||||
|
<div class="uk-container uk-section uk-margin-remove-top">
|
||||||
|
<div uk-grid>
|
||||||
|
<div class="uk-width-1-1" >
|
||||||
|
<a href="/admin" class="uk-button uk-button-small"><span class="uk-flex material-symbols-outlined">arrow_back</span></a>
|
||||||
|
<a onclick="toggleDarkMode()" class="uk-button uk-button-small uk-align-right"><span id="modeToggle" class="uk-flex material-symbols-outlined">dark_mode</span></a>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<h3 class="uk-text-center">Site Form</h3>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<p>Sites are the main driving force of the system. They are essentially larger pools of items and apps that are segregated and only meet in cross-sites applications. Think of
|
||||||
|
sites as your house, your shed, or your car. When designing a site think about who has access and who will be using it the most often, what permissions might be needed, and
|
||||||
|
do you need all the apps active on the site. The more sites you have the more bloated the system can become so beware making tons of sites. Thats why Zones and Locations
|
||||||
|
exist.</p>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<hr class="uk-divider-icon">
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div class="uk-margin">
|
||||||
|
<label class="uk-form-label" for="site_name">Site Name</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<input class="uk-input" id="site_name" type="text" placeholder="Some text...">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div class="uk-margin">
|
||||||
|
<label class="uk-form-label" for="site_description">Site Description</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<textarea id="site_description" class="uk-textarea" rows="5" placeholder="Textarea" aria-label="Textarea"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div class="uk-margin">
|
||||||
|
<label class="uk-form-label" for="creation_date">Creation Date</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<input class="uk-input uk-disabled" id="creation_date" type="text" placeholder="Some text...">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div class="uk-margin">
|
||||||
|
<label class="uk-form-label" for="site_owner">Site Owner</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<input class="uk-input uk-disabled" id="site_owner" type="text" placeholder="Some text...">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="edit_locations" class="uk-width-1-1">
|
||||||
|
<div class="uk-width-large uk-grid-small" uk-grid>
|
||||||
|
<div class="uk-width-3-4 uk-margin-auto-top">
|
||||||
|
<label for="default_zone">Default Zone</label>
|
||||||
|
<input class="uk-input uk-disabled" id="default_zone" type="text">
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-4 uk-margin-auto-top">
|
||||||
|
<button onclick="" class="uk-button uk-button-default">Select</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-large uk-grid-small" uk-grid>
|
||||||
|
<div class="uk-width-3-4 uk-margin-auto-top">
|
||||||
|
<label for="default_auto_issue_location">Default Auto Issue Location</label>
|
||||||
|
<input class="uk-input uk-disabled" id="default_auto_issue_location" type="text">
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-4 uk-margin-auto-top">
|
||||||
|
<button onclick="" class="uk-button uk-button-default">Select</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-large uk-grid-small" uk-grid>
|
||||||
|
<div class="uk-width-3-4 uk-margin-auto-top">
|
||||||
|
<label for="default_primary_location">Default Primary Location</label>
|
||||||
|
<input class="uk-input uk-disabled" id="default_primary_location" type="text">
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-4 uk-margin-auto-top">
|
||||||
|
<button onclick="" class="uk-button uk-button-default">Select</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="new_locations" class="uk-width-1-1" uk-grid>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<caption> These are how the system will create the default locations and zones that get assigned to all new items by default.</caption>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div class="">
|
||||||
|
<label for="new_default_zone">Default Zone</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<input class="uk-input" id="new_default_zone" type="text" placeholder="Some text...">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div class="">
|
||||||
|
<label for="new_default_auto_issue_location">Default Auto Issue Location</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<input class="uk-input" id="new_default_auto_issue_location" type="text" placeholder="Some text...">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div class="">
|
||||||
|
<label for="new_default_primary_location">Default Primary Location</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<input class="uk-input" id="new_default_primary_location" type="text" placeholder="Some text...">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<hr class="uk-divider-icon">
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1 uk-margin-remove">
|
||||||
|
<button id="SubmitButton" class="uk-button uk-button-primary uk-align-right">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
{% assets "js_all" %}
|
||||||
|
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||||
|
{% endassets %}
|
||||||
|
<script>
|
||||||
|
const site = {{site|tojson}}
|
||||||
|
const session = {{session|tojson}}
|
||||||
|
const path = window.location.pathname
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', async function() {
|
||||||
|
let mode = "edit"
|
||||||
|
if(path == "/admin/site/new"){
|
||||||
|
mode = "new"
|
||||||
|
}
|
||||||
|
replenishForm(site, mode)
|
||||||
|
console.log(mode)
|
||||||
|
})
|
||||||
|
|
||||||
|
var mode = false
|
||||||
|
async function toggleDarkMode() {
|
||||||
|
let darkMode = document.getElementById("dark-mode");
|
||||||
|
darkMode.disabled = !darkMode.disabled;
|
||||||
|
mode = !mode;
|
||||||
|
if(mode){
|
||||||
|
document.getElementById('modeToggle').innerHTML = "light_mode"
|
||||||
|
document.getElementById('main_html').classList.add('uk-light')
|
||||||
|
} else {
|
||||||
|
document.getElementById('modeToggle').innerHTML = "dark_mode"
|
||||||
|
document.getElementById('main_html').classList.remove('uk-light')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(session.user.flags.darkmode){
|
||||||
|
toggleDarkMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function replenishForm(site, mode){
|
||||||
|
document.getElementById('site_name').value = site.site_name
|
||||||
|
document.getElementById('site_description').value = site.site_description
|
||||||
|
document.getElementById('creation_date').value = site.creation_date
|
||||||
|
document.getElementById('site_owner').value = site.site_owner_id
|
||||||
|
|
||||||
|
if(mode=="new"){
|
||||||
|
document.getElementById('edit_locations').hidden = true
|
||||||
|
document.getElementById('new_locations').hidden = false
|
||||||
|
document.getElementById('SubmitButton').innerHTML = "Add Site"
|
||||||
|
document.getElementById('SubmitButton').onclick = async function(){
|
||||||
|
await postAddSite()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
document.getElementById('edit_locations').hidden = false
|
||||||
|
document.getElementById('new_locations').hidden = true
|
||||||
|
document.getElementById('default_zone').value = site.default_zone
|
||||||
|
document.getElementById('default_auto_issue_location').value = site.default_auto_issue_location
|
||||||
|
document.getElementById('default_primary_location').value = site.default_primary_location
|
||||||
|
document.getElementById('SubmitButton').innerHTML = "Update Site"
|
||||||
|
document.getElementById('SubmitButton').onclick = async function(){
|
||||||
|
await postEditSite()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async function postAddSite(){
|
||||||
|
|
||||||
|
let payload = {
|
||||||
|
site_name: document.getElementById('site_name').value,
|
||||||
|
site_description: document.getElementById('site_description').value,
|
||||||
|
default_zone: document.getElementById('new_default_zone').value,
|
||||||
|
default_auto_issue_location: document.getElementById('new_default_auto_issue_location').value,
|
||||||
|
default_primary_location: document.getElementById('new_default_primary_location').value
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`/admin/site/postAddSite`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
payload: payload
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function postEditSite(){
|
||||||
|
|
||||||
|
let payload = {
|
||||||
|
id: site.id,
|
||||||
|
update: {site_name: document.getElementById('site_name').value,
|
||||||
|
site_description: document.getElementById('site_description').value}
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`/admin/site/postEditSite`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
payload: payload
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
367
templates/admin/user.html
Normal file
367
templates/admin/user.html
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" dir="ltr" id="main_html">
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
|
<title id="title">User</title>
|
||||||
|
<!-- Material Icons -->
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
|
<!-- Material Symbols - Outlined Set -->
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
|
||||||
|
<!-- Material Symbols - Rounded Set -->
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded" rel="stylesheet" />
|
||||||
|
<!-- Material Symbols - Sharp Set -->
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/uikit.min.css') }}"/>
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/pantry.css') }}"/>
|
||||||
|
|
||||||
|
<link id="dark-mode" rel="stylesheet" href="{{ url_for('static', filename='css/dark-mode.css') }}" disabled/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="uk-section">
|
||||||
|
<div class="uk-container uk-container-xsmall">
|
||||||
|
<div class="uk-grid-small" uk-grid>
|
||||||
|
<div class="uk-width-1-1" >
|
||||||
|
<a href="/admin" class="uk-button uk-button-small"><span class="uk-flex material-symbols-outlined">arrow_back</span></a>
|
||||||
|
<a onclick="toggleDarkMode()" class="uk-button uk-button-small uk-align-right"><span id="modeToggle" class="uk-flex material-symbols-outlined">dark_mode</span></a>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<h3 class="uk-text-center">User/Device Form</h3>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<p>Users and Devices, better known as access points, are credentials to acccess the system from specific areas. Users are intended to be
|
||||||
|
you basic login for actual people. Devices are created when you know a the credentials are to be used in one place and no where else.
|
||||||
|
Differentiating between the two is helpful to see who by/where in your setup transactions are happening.</p>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div class="uk-margin">
|
||||||
|
<label class="uk-form-label" for="login_name">Access Name</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<input class="uk-input" id="login_name" type="text">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div class="uk-margin">
|
||||||
|
<label class="uk-form-label" for="login_email">Access Email</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<input class="uk-input" id="login_email" type="text">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div class="uk-margin">
|
||||||
|
<label class="uk-form-label" for="login_type">Access Type</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<select class="uk-select" id="login_type">
|
||||||
|
<option value="user">User</option>
|
||||||
|
<option value="device">Device</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<hr class="uk-divider-icon">
|
||||||
|
</div>
|
||||||
|
<div id="new_password" class="uk-width-1-1">
|
||||||
|
<div class="uk-margin">
|
||||||
|
<label class="uk-form-label" for="new_login_password">Access Password</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<input class="uk-input" id="new_login_password" type="password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="new_password" class="uk-width-1-1">
|
||||||
|
<div class="uk-margin">
|
||||||
|
<label class="uk-form-label" for="new_login_password_retype">Access Retype Password</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<input class="uk-input" id="new_login_password_retype" type="password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="old_password" class="uk-width-1-1">
|
||||||
|
<div class="uk-margin">
|
||||||
|
<label class="uk-form-label" for="old_login_password">Current Access Password</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<input class="uk-input" id="old_login_password" type="password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="old_password" class="uk-width-1-1">
|
||||||
|
<div class="uk-margin">
|
||||||
|
<label class="uk-form-label" for="old_login_password_new">New Access Password</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<input class="uk-input" id="old_login_password_new" type="password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="old_password" class="uk-width-1-1">
|
||||||
|
<div class="uk-margin">
|
||||||
|
<label class="uk-form-label" for="old_login_password_new_retype">Retype New Access Password</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<input class="uk-input" id="old_login_password_new_retype" type="password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="old_password" class="uk-width-1-1">
|
||||||
|
<button onclick="postEditLoginPassword()" class="uk-button uk-button-primary uk-align-right">Update Password</button>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<hr class="uk-divider-icon">
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<caption>These are the Sites this access point has permission to see</caption>
|
||||||
|
<table class="uk-table uk-table-striped uk-table-small">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="uk-text-center">Site Name</th>
|
||||||
|
<th class="uk-text-center">Operations</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="sitesTableBody">
|
||||||
|
<tr>
|
||||||
|
<td class="uk-text-center">Test</td>
|
||||||
|
<td class="uk-text-center"><button class="uk-button uk-button-small uk-button-default">Remove</button></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="uk-text-center">Test</td>
|
||||||
|
<td class="uk-text-center"><button class="uk-button uk-button-small uk-button-default">Remove</button></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<caption>These are the roles that the access point has in each permissable site</caption>
|
||||||
|
<table class="uk-table uk-table-striped uk-table-small">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="uk-text-center">Site Role</th>
|
||||||
|
<th class="uk-text-center">Operations</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="siteRolesTableBody">
|
||||||
|
<tr>
|
||||||
|
<td class="uk-text-center">Test</td>
|
||||||
|
<td class="uk-text-center"><button class="uk-button uk-button-small uk-button-default">Remove</button></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="uk-text-center">Test</td>
|
||||||
|
<td class="uk-text-center"><button class="uk-button uk-button-small uk-button-default">Remove</button></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<hr class="uk-divider-icon">
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<button id="SubmitButton" class="uk-button uk-button-primary uk-align-right">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
{% assets "js_all" %}
|
||||||
|
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||||
|
{% endassets %}
|
||||||
|
<script>
|
||||||
|
const user = {{user|tojson}}
|
||||||
|
const session = {{session|tojson}}
|
||||||
|
const path = window.location.pathname
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
var update = {
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
row_type: "",
|
||||||
|
sites: [],
|
||||||
|
site_roles: [],
|
||||||
|
system_admin: false,
|
||||||
|
flags: {
|
||||||
|
permissions: {
|
||||||
|
sitename: {
|
||||||
|
items: {'nav': true, 'edit': false, 'view': true, 'delete': false},
|
||||||
|
item:{'nav': true, 'edit': false, 'view': true, 'delete': false},
|
||||||
|
recipes: {'nav': true, 'edit': false, 'view': true, 'delete': false},
|
||||||
|
receipts: {'nav': true, 'edit': false, 'view': true, 'delete': false},
|
||||||
|
shopping_lists: {'nav': true, 'edit': false, 'view': true, 'delete': false}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
darkmode: true, // prefers dark mode
|
||||||
|
solarized: false // preferse solarized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener('DOMContentLoaded', async function() {
|
||||||
|
let mode = "edit"
|
||||||
|
if(path == "/admin/user/new"){
|
||||||
|
mode = "new"
|
||||||
|
}
|
||||||
|
console.log(user)
|
||||||
|
await replenishForm(user, mode)
|
||||||
|
})
|
||||||
|
|
||||||
|
var mode = false
|
||||||
|
async function toggleDarkMode() {
|
||||||
|
let darkMode = document.getElementById("dark-mode");
|
||||||
|
darkMode.disabled = !darkMode.disabled;
|
||||||
|
mode = !mode;
|
||||||
|
if(mode){
|
||||||
|
document.getElementById('modeToggle').innerHTML = "light_mode"
|
||||||
|
document.getElementById('main_html').classList.add('uk-light')
|
||||||
|
} else {
|
||||||
|
document.getElementById('modeToggle').innerHTML = "dark_mode"
|
||||||
|
document.getElementById('main_html').classList.remove('uk-light')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(session.user.flags.darkmode){
|
||||||
|
toggleDarkMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function replenishForm(user, mode){
|
||||||
|
document.getElementById('login_name').value = user.username
|
||||||
|
document.getElementById('login_email').value = user.email
|
||||||
|
document.getElementById('login_type').value = user.row_type
|
||||||
|
|
||||||
|
if(mode=="new"){
|
||||||
|
document.querySelectorAll('#new_password').forEach(function(element) {
|
||||||
|
element.hidden = false;
|
||||||
|
});
|
||||||
|
document.querySelectorAll('#old_password').forEach(function(element) {
|
||||||
|
element.hidden = true;
|
||||||
|
});
|
||||||
|
document.getElementById('SubmitButton').innerHTML = "Add User"
|
||||||
|
document.getElementById('SubmitButton').onclick = async function(){
|
||||||
|
await postAddLogin()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
document.querySelectorAll('#new_password').forEach(function(element) {
|
||||||
|
element.hidden = true;
|
||||||
|
});
|
||||||
|
document.querySelectorAll('#old_password').forEach(function(element) {
|
||||||
|
element.hidden = false;
|
||||||
|
});
|
||||||
|
document.getElementById('SubmitButton').innerHTML = "Update User"
|
||||||
|
document.getElementById('SubmitButton').onclick = async function(){
|
||||||
|
await postEditLogin()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function postAddLogin(){
|
||||||
|
|
||||||
|
let payload = {
|
||||||
|
username: document.getElementById('login_name').value,
|
||||||
|
email: document.getElementById('login_email').value,
|
||||||
|
password: document.getElementById('new_login_password').value,
|
||||||
|
row_type: document.getElementById('login_type').value,
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`/admin/user/postAddLogin`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
payload: payload
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
data = await response.json();
|
||||||
|
let transaction_status = "success"
|
||||||
|
if (data.error){
|
||||||
|
transaction_status = "danger"
|
||||||
|
UIkit.notification({
|
||||||
|
message: data.message,
|
||||||
|
status: transaction_status,
|
||||||
|
pos: 'bottom-left',
|
||||||
|
timeout: 5000
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
window.location.href = `/admin/user/${data.user.id}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function postEditLoginPassword(){
|
||||||
|
let payload = {
|
||||||
|
id: user.id,
|
||||||
|
current_password: document.getElementById('old_login_password').value,
|
||||||
|
update: {password: document.getElementById('old_login_password_new').value}
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`/admin/user/postEditLoginPassword`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
payload: payload
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
data = await response.json();
|
||||||
|
transaction_status = "success"
|
||||||
|
if (data.error){
|
||||||
|
transaction_status = "danger"
|
||||||
|
UIkit.notification({
|
||||||
|
message: data.message,
|
||||||
|
status: transaction_status,
|
||||||
|
pos: 'bottom-left',
|
||||||
|
timeout: 5000
|
||||||
|
});
|
||||||
|
document.getElementById('old_login_password').classList.add('uk-form-danger')
|
||||||
|
document.getElementById('old_login_password_new').classList.add('uk-form-danger')
|
||||||
|
document.getElementById('old_login_password_new_retype').classList.add('uk-form-danger')
|
||||||
|
} else {
|
||||||
|
UIkit.notification({
|
||||||
|
message: data.message,
|
||||||
|
status: transaction_status,
|
||||||
|
pos: 'bottom-left',
|
||||||
|
timeout: 5000
|
||||||
|
});
|
||||||
|
document.getElementById('old_login_password').classList.remove('uk-form-danger')
|
||||||
|
document.getElementById('old_login_password_new').classList.remove('uk-form-danger')
|
||||||
|
document.getElementById('old_login_password_new_retype').classList.remove('uk-form-danger')
|
||||||
|
document.getElementById('old_login_password').value = ""
|
||||||
|
document.getElementById('old_login_password_new').value = ""
|
||||||
|
document.getElementById('old_login_password_new_retype').value = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function postEditLogin(){
|
||||||
|
let payload = {
|
||||||
|
id: user.id,
|
||||||
|
update: {
|
||||||
|
row_type: document.getElementById('login_type').value,
|
||||||
|
username: document.getElementById('login_name').value,
|
||||||
|
email: document.getElementById('login_email').value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`/admin/user/postEditLogin`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
payload: payload
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
data = await response.json();
|
||||||
|
if (data.error){
|
||||||
|
transaction_status = "danger"
|
||||||
|
UIkit.notification({
|
||||||
|
message: data.message,
|
||||||
|
status: transaction_status,
|
||||||
|
pos: 'bottom-left',
|
||||||
|
timeout: 5000
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
UIkit.notification({
|
||||||
|
message: data.message,
|
||||||
|
status: transaction_status,
|
||||||
|
pos: 'bottom-left',
|
||||||
|
timeout: 5000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
@ -1,17 +0,0 @@
|
|||||||
<div class="row" id="sites">
|
|
||||||
<div class="col s12">
|
|
||||||
<h1>Your Users</h1>
|
|
||||||
</div>
|
|
||||||
<div class="col s12">
|
|
||||||
<p class="flow-text">This is where all the users who have access to this instance!</p>
|
|
||||||
</div>
|
|
||||||
<div class="col s12">
|
|
||||||
%%userstable%%
|
|
||||||
</div>
|
|
||||||
<div class="col s12 center-align">
|
|
||||||
<span class="center-align"><button data-target="add_site"class="btn btn-flat center-align modal-trigger" style="width: 100%; border-radius: 10px;"><i class="large material-symbols-outlined" style="font-size: 2rem;">add_circle</i></button></span>
|
|
||||||
</div>
|
|
||||||
<div class="col s12">
|
|
||||||
%%pagination%%
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
11
test.py
11
test.py
@ -5,3 +5,14 @@ import random, uuid, csv, postsqldb
|
|||||||
import pdf2image, os, pymupdf, PIL
|
import pdf2image, os, pymupdf, PIL
|
||||||
|
|
||||||
from pywebpush import webpush, WebPushException
|
from pywebpush import webpush, WebPushException
|
||||||
|
|
||||||
|
|
||||||
|
site = MyDataclasses.SitePayload(
|
||||||
|
"testA",
|
||||||
|
"Test site A",
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
print("payload", site)
|
||||||
|
x = site.__dict__
|
||||||
|
print("dict", x)
|
||||||
@ -4,9 +4,16 @@ from config import config, sites_config, setFirstSetupDone
|
|||||||
from functools import wraps
|
from functools import wraps
|
||||||
from manage import create
|
from manage import create
|
||||||
from main import create_site, getUser, setSystemAdmin
|
from main import create_site, getUser, setSystemAdmin
|
||||||
|
import postsqldb
|
||||||
|
|
||||||
login_app = Blueprint('login', __name__)
|
login_app = Blueprint('login', __name__)
|
||||||
|
|
||||||
|
def update_session_user():
|
||||||
|
database_config = config()
|
||||||
|
with psycopg2.connect(**database_config) as conn:
|
||||||
|
user = postsqldb.LoginsTable.get_washed_tuple(conn, (session['user_id'],))
|
||||||
|
session['user'] = user
|
||||||
|
|
||||||
def login_required(func):
|
def login_required(func):
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
import celery.schedules
|
import celery.schedules
|
||||||
from flask import Flask, render_template, session, request, redirect, jsonify
|
from flask import Flask, render_template, session, request, redirect, jsonify
|
||||||
from flask_assets import Environment, Bundle
|
from flask_assets import Environment, Bundle
|
||||||
import api, config, user_api, psycopg2, main, admin, item_API, receipts_API, shopping_list_API, group_api, recipes_api
|
import api, config, user_api, psycopg2, main, api_admin, item_API, receipts_API, shopping_list_API, group_api
|
||||||
from user_api import login_required
|
from user_api import login_required, update_session_user
|
||||||
from external_API import external_api
|
from external_API import external_api
|
||||||
from workshop_api import workshop_api
|
from workshop_api import workshop_api
|
||||||
import database
|
import database
|
||||||
import postsqldb
|
import postsqldb
|
||||||
from webpush import trigger_push_notifications_for_subscriptions
|
from webpush import trigger_push_notifications_for_subscriptions
|
||||||
|
from scripts.recipes import recipes_api
|
||||||
|
|
||||||
app = Flask(__name__, instance_relative_config=True)
|
app = Flask(__name__, instance_relative_config=True)
|
||||||
UPLOAD_FOLDER = 'static/pictures'
|
UPLOAD_FOLDER = 'static/pictures'
|
||||||
@ -21,7 +22,7 @@ assets = Environment(app)
|
|||||||
app.secret_key = '11gs22h2h1a4h6ah8e413a45'
|
app.secret_key = '11gs22h2h1a4h6ah8e413a45'
|
||||||
app.register_blueprint(api.database_api)
|
app.register_blueprint(api.database_api)
|
||||||
app.register_blueprint(user_api.login_app)
|
app.register_blueprint(user_api.login_app)
|
||||||
app.register_blueprint(admin.admin)
|
app.register_blueprint(api_admin.admin_api)
|
||||||
app.register_blueprint(item_API.items_api)
|
app.register_blueprint(item_API.items_api)
|
||||||
app.register_blueprint(external_api)
|
app.register_blueprint(external_api)
|
||||||
app.register_blueprint(workshop_api)
|
app.register_blueprint(workshop_api)
|
||||||
@ -90,6 +91,7 @@ def transaction():
|
|||||||
@app.route("/items")
|
@app.route("/items")
|
||||||
@login_required
|
@login_required
|
||||||
def items():
|
def items():
|
||||||
|
update_session_user()
|
||||||
sites = [site[1] for site in main.get_sites(session['user']['sites'])]
|
sites = [site[1] for site in main.get_sites(session['user']['sites'])]
|
||||||
return render_template("items/index.html",
|
return render_template("items/index.html",
|
||||||
current_site=session['selected_site'],
|
current_site=session['selected_site'],
|
||||||
@ -116,6 +118,7 @@ def subscribe():
|
|||||||
@app.route("/")
|
@app.route("/")
|
||||||
@login_required
|
@login_required
|
||||||
def home():
|
def home():
|
||||||
|
update_session_user()
|
||||||
sites = [site[1] for site in main.get_sites(session['user']['sites'])]
|
sites = [site[1] for site in main.get_sites(session['user']['sites'])]
|
||||||
session['selected_site'] = sites[0]
|
session['selected_site'] = sites[0]
|
||||||
return redirect("/items")
|
return redirect("/items")
|
||||||
|
|||||||
@ -10,6 +10,7 @@ workshop_api = Blueprint('workshop_api', __name__)
|
|||||||
@workshop_api.route("/workshop")
|
@workshop_api.route("/workshop")
|
||||||
@login_required
|
@login_required
|
||||||
def workshop():
|
def workshop():
|
||||||
|
print(session['user'])
|
||||||
sites = [site[1] for site in main.get_sites(session['user']['sites'])]
|
sites = [site[1] for site in main.get_sites(session['user']['sites'])]
|
||||||
print(session.get('user')['system_admin'])
|
print(session.get('user')['system_admin'])
|
||||||
if not session.get('user')['system_admin']:
|
if not session.get('user')['system_admin']:
|
||||||
@ -117,7 +118,6 @@ def postAddZone():
|
|||||||
site_id = cur.fetchone()[0]
|
site_id = cur.fetchone()[0]
|
||||||
zone = postsqldb.ZonesTable.Payload(
|
zone = postsqldb.ZonesTable.Payload(
|
||||||
request.get_json()['name'],
|
request.get_json()['name'],
|
||||||
site_id,
|
|
||||||
request.get_json()['description']
|
request.get_json()['description']
|
||||||
)
|
)
|
||||||
postsqldb.ZonesTable.insert_tuple(conn, site_name, zone.payload())
|
postsqldb.ZonesTable.insert_tuple(conn, site_name, zone.payload())
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user