introduced very static OIDC through authentik

This commit is contained in:
Jadowyne Ulve 2025-08-03 08:08:23 -05:00
parent fe34ddafe2
commit 324415ffb3
21 changed files with 489 additions and 1234 deletions

View File

View File

@ -1,12 +1,18 @@
from flask import Blueprint, request, render_template, redirect, session, url_for, jsonify
import hashlib, psycopg2, process, MyDataclasses
from config import config, sites_config, setFirstSetupDone
from authlib.integrations.flask_client import OAuth
import hashlib, psycopg2
from config import config, sites_config
from functools import wraps
from manage import create
from main import create_site, getUser, setSystemAdmin
import postsqldb
import requests
from application.access_module import access_database
from outh import oauth
access_api = Blueprint('access_api', __name__, template_folder="templates", static_folder="static")
login_app = Blueprint('login', __name__)
def update_session_user():
database_config = config()
@ -18,45 +24,44 @@ def login_required(func):
@wraps(func)
def wrapper(*args, **kwargs):
if 'user' not in session or session['user'] == None:
return redirect(url_for('login.login'))
return redirect(url_for('access_api.login'))
return func(*args, **kwargs)
return wrapper
@login_app.route('/setup', methods=['GET', 'POST'])
def first_time_setup():
if request.method == "POST":
database_address = request.form['database_address']
database_port = request.form['database_port']
database_name = request.form['database_name']
database_user = request.form['database_user']
database_password = request.form['database_address']
site_manager = MyDataclasses.SiteManager(
site_name=request.form['site_name'],
admin_user=(request.form['username'], hashlib.sha256(request.form['password'].encode()).hexdigest(), request.form['email']),
default_zone=request.form['site_default_zone'],
default_location=request.form['site_default_location'],
description=request.form['site_description']
)
process.addSite(site_manager)
setFirstSetupDone()
return redirect("/login")
return render_template("setup.html")
@login_app.route('/logout', methods=['GET'])
@access_api.route('/logout', methods=['GET'])
@login_required
def logout():
if 'user' in session.keys():
session['user'] = None
return redirect('/login')
return redirect('/access/login')
@login_app.route('/login', methods=['POST', 'GET'])
@access_api.route('/auth')
def auth():
token = oauth.authentik.authorize_access_token()
access_token = token['access_token']
userinfo_endpoint="https://auth.treehousefullofstars.com/application/o/userinfo/"
headers = {
'Authorization': f'Bearer {access_token}',
}
response = requests.get(userinfo_endpoint, headers=headers)
if response.status_code == 200:
user_email = response.json()['email']
user = access_database.selectUserByEmail((user_email,))
user = access_database.washUserDictionary(user)
session['user_id'] = user['id']
session['user'] = user
session['login_type'] = 'External'
return redirect('/')
else:
print("Failed to fetch user info:", response.status_code, response.text)
return redirect('/access/login')
@access_api.route('/login/oidc')
def oidc_login():
redirect_uri = url_for('access_api.auth', _external=True)
return oauth.authentik.authorize_redirect(redirect_uri)
@access_api.route('/login', methods=['POST', 'GET'])
def login():
session.clear()
instance_config = sites_config()
@ -83,6 +88,7 @@ def login():
if user and user[2] == password:
session['user_id'] = user[0]
session['user'] = {'id': user[0], 'username': user[1], 'sites': user[13], 'site_roles': user[14], 'system_admin': user[15], 'flags': user[16]}
session['login_type'] = 'Internal'
return jsonify({'error': False, 'message': 'Logged In Sucessfully!'})
else:
return jsonify({'error': True, 'message': 'Username or Password was incorrect!'})
@ -91,9 +97,15 @@ def login():
if 'user' not in session.keys():
session['user'] = None
return render_template("other/login.html")
return render_template("login.html")
@login_app.route('/signup', methods=['POST', 'GET'])
@access_api.route('/dashboard')
def dashboard():
if 'user' not in session:
return redirect('/')
return f"Hello, {session['user']['name']}! <a href='/logout'>Logout</a>"
@access_api.route('/signup', methods=['POST', 'GET'])
def signup():
instance_config = sites_config()
if not instance_config['signup_enabled']:

View File

@ -0,0 +1,42 @@
import psycopg2
import config
from application import postsqldb
def washUserDictionary(user):
return {
'id': user['id'],
'username': user['username'],
'sites': user['sites'],
'site_roles': user['site_roles'],
'system_admin': user['system_admin'],
'flags': user['flags']
}
def selectUserByEmail(payload, convert=True, conn=None):
""" payload = (email,)"""
self_conn = False
user = ()
sql = f"SELECT * FROM logins WHERE email=%s;"
try:
if not conn:
database_config = config.config()
conn = psycopg2.connect(**database_config)
conn.autocommit = True
self_conn = True
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
user = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
user = rows
if self_conn:
conn.commit()
conn.close()
return user
except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql)

View File

@ -8,7 +8,7 @@ async function loginUser() {
let username = document.getElementById('login_username').value
let password = document.getElementById('login_password').value
const response = await fetch(`/login`, {
const response = await fetch(`/access/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@ -97,7 +97,7 @@ async function signupUser() {
let user_email = document.getElementById('signup_email').value
let password = document.getElementById('signup_password').value
let username = document.getElementById('signup_username').value
const response = await fetch(`/signup`, {
const response = await fetch(`/access/signup`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',

View File

@ -35,6 +35,9 @@
<div class="uk-switcher">
<div class="uk-grid-small" uk-grid>
<div class="uk-width-1-1">
<a href="/access/login/oidc" class="uk-button uk-button-primary">Login with Authentik</a>
</div>
<div class="uk-width-1-1">
<div class="uk-margin">
<label class="uk-form-label" for="login_username">Username</label>
@ -98,5 +101,5 @@
</div>
</div>
</body>
<script src="{{ url_for('static', filename='handlers/loginHandler.js') }}"></script>
<script src="{{ url_for('access_api.static', filename='js/loginHandler.js') }}"></script>
</html>

View File

@ -1,13 +1,14 @@
# 3RD PARTY IMPORTS
from flask import (Blueprint, request, render_template, session, jsonify)
from flask import (
Blueprint, request, render_template, session, jsonify, redirect
)
import math
import hashlib
# APPLICATION IMPORTS
from application.access_module import access_api
from application.administration import administration_database, administration_processes
from application import database_payloads, postsqldb
from user_api import login_required
admin_api = Blueprint('admin_api', __name__, template_folder="templates", static_folder="static")
@ -15,13 +16,13 @@ admin_api = Blueprint('admin_api', __name__, template_folder="templates", static
# ROOT TEMPLATE ROUTES
@admin_api.route('/')
@access_api.login_required
def admin_index():
sites = [site[1] for site in postsqldb.get_sites(session['user']['sites'])]
return render_template("admin_index.html", current_site=session['selected_site'], sites=sites)
# Added to Database
@admin_api.route('/site/<id>')
@login_required
@access_api.login_required
def adminSites(id):
if id == "new":
new_site_payload = database_payloads.SitePayload("", "", session['user_id'])
@ -30,9 +31,8 @@ def adminSites(id):
site = administration_database.selectSitesTuple((id,))
return render_template('site.html', site=site)
# Added to database
@admin_api.route('/role/<id>')
@login_required
@access_api.login_required
def adminRoles(id):
sites = administration_database.selectSitesTuples()
if id == "new":
@ -42,9 +42,8 @@ def adminRoles(id):
role = administration_database.selectRolesTuple((id,))
return render_template('role.html', role=role, sites=sites)
# Added to database
@admin_api.route('/user/<id>')
@login_required
@access_api.login_required
def adminUser(id):
if id == "new":
new_user_payload = database_payloads.LoginsPayload("", "", "", "")
@ -53,10 +52,32 @@ def adminUser(id):
user = administration_database.selectLoginsTuple((int(id),))
return render_template('user.html', user=user)
@admin_api.route('/setup', methods=['GET', 'POST'])
def first_time_setup():
if request.method == "POST":
database_address = request.form['database_address']
database_port = request.form['database_port']
database_name = request.form['database_name']
database_user = request.form['database_user']
database_password = request.form['database_address']
payload = {
"site_name" : request.form['site_name'],
"admin_user": (request.form['username'], hashlib.sha256(request.form['password'].encode()).hexdigest(), request.form['email']),
"default_zone": request.form['site_default_zone'],
"default_primary_location": request.form['site_default_location'],
"site_description": request.form['site_description']
}
administration_processes.addSite(payload)
return redirect("/login")
return render_template("setup.html")
# API ROUTES
# add to database
@admin_api.route('/api/getSites', methods=['GET'])
@login_required
@access_api.login_required
def getSites():
if request.method == "GET":
records = []
@ -68,9 +89,8 @@ def getSites():
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!'})
# Added to database
@admin_api.route('/api/getRoles', methods=['GET'])
@login_required
@access_api.login_required
def getRoles():
if request.method == "GET":
records = []
@ -82,9 +102,8 @@ def getRoles():
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!'})
# Added to Database
@admin_api.route('/api/getLogins', methods=['GET'])
@login_required
@access_api.login_required
def getLogins():
if request.method == "GET":
records = []
@ -96,8 +115,8 @@ def getLogins():
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!'})
# Added to database and Processses.
@admin_api.route('/api/site/postDeleteSite', methods=["POST"])
@access_api.login_required
def postDeleteSite():
if request.method == "POST":
site_id = request.get_json()['site_id']
@ -115,8 +134,8 @@ def postDeleteSite():
return jsonify({'error': False, 'message': f""})
return jsonify({'error': True, 'message': f""})
# Added to Database and Processes
@admin_api.route('/api/site/postAddSite', methods=["POST"])
@access_api.login_required
def postAddSite():
if request.method == "POST":
payload = request.get_json()['payload']
@ -131,8 +150,8 @@ def postAddSite():
return jsonify({'error': False, 'message': f"Zone added to {site_name}."})
return jsonify({'error': True, 'message': f"These was an error with adding this Zone to {site_name}."})
# Added to Database
@admin_api.route('/api/site/postEditSite', methods=["POST"])
@access_api.login_required
def postEditSite():
if request.method == "POST":
payload = request.get_json()['payload']
@ -140,8 +159,8 @@ def postEditSite():
return jsonify({'error': False, 'message': f"Site updated."})
return jsonify({'error': True, 'message': f"These was an error with updating Site."})
# Added to Database
@admin_api.route('/api/role/postAddRole', methods=["POST"])
@access_api.login_required
def postAddRole():
if request.method == "POST":
payload = request.get_json()['payload']
@ -155,8 +174,8 @@ def postAddRole():
return jsonify({'error': False, 'message': f"Role added."})
return jsonify({'error': True, 'message': f"These was an error with adding this Role."})
# Added to Database
@admin_api.route('/api/role/postEditRole', methods=["POST"])
@access_api.login_required
def postEditRole():
if request.method == "POST":
payload = request.get_json()['payload']
@ -164,8 +183,8 @@ def postEditRole():
return jsonify({'error': False, 'message': f"Role updated."})
return jsonify({'error': True, 'message': f"These was an error with updating this Role."})
# Added to database
@admin_api.route('/api/user/postAddLogin', methods=["POST"])
@access_api.login_required
def postAddLogin():
if request.method == "POST":
payload = request.get_json()['payload']
@ -180,8 +199,8 @@ def postAddLogin():
return jsonify({'user': user, 'error': False, 'message': f"User added."})
return jsonify({'user': user, 'error': True, 'message': f"These was an error with adding this User."})
# Added to database
@admin_api.route('/api/user/postEditLogin', methods=["POST"])
@access_api.login_required
def postEditLogin():
if request.method == "POST":
payload = request.get_json()['payload']
@ -189,8 +208,8 @@ def postEditLogin():
return jsonify({'error': False, 'message': f"User was Added Successfully."})
return jsonify({'error': True, 'message': f"These was an error with adding this user."})
# Added to Database
@admin_api.route('/api/user/postEditLoginPassword', methods=["POST"])
@access_api.login_required
def postEditLoginPassword():
if request.method == "POST":
payload = request.get_json()['payload']

View File

@ -1,19 +1,14 @@
from application import postsqldb
import config
# 3RD PARTY IMPORTS
import psycopg2
import datetime
# APPLICATION IMPORTS
from application import postsqldb
import config
def getTransactions(site:str, payload: tuple, convert:bool=True):
""" Page through a sites Transactions by passing a logistics id, limit, and offset through a payload
Args:
site (str): _description_
payload (tuple): (logistics_id, limit, offset)
convert (bool, optional): _description_. Defaults to True.
Returns:
_type_: _description_
"""
""" payload (tuple): (logistics_id, limit, offset) """
database_config = config.config()
sql = f"SELECT * FROM {site}_transactions WHERE logistics_info_id=%s LIMIT %s OFFSET %s;"
sql_count = f"SELECT COUNT(*) FROM {site}_transactions WHERE logistics_info_id=%s;"
@ -241,48 +236,25 @@ def getItemLocations(site, payload, convert=True, conn=None):
raise postsqldb.DatabaseError(error, payload, sql)
def getItemInfoTuple(site:str, payload:tuple, convert=True):
"""_summary_
Args:
conn (_type_): _description_
site (_type_): _description_
payload (_type_): (item_info_id,)
convert (bool, optional): _description_. Defaults to True.
Raises:
DatabaseError: _description_
Returns:
_type_: _description_
"""
selected = ()
database_config = config.config()
sql = f"SELECT * FROM {site}_item_info WHERE id=%s;"
try:
with psycopg2.connect(**database_config) as conn:
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
selected = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
selected = rows
return selected
except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql)
""" payload (_type_): (item_info_id,) """
selected = ()
database_config = config.config()
sql = f"SELECT * FROM {site}_item_info WHERE id=%s;"
try:
with psycopg2.connect(**database_config) as conn:
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
selected = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
selected = rows
return selected
except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql)
def selectItemLocationsTuple(site_name, payload, convert=True):
"""select a single tuple from ItemLocations table for site_name
Args:
conn (_T_connector@connect):
site_name (str):
payload (tuple): [item_id, location_id]
convert (bool): defaults to False, used to determine return of tuple/dict
Returns:
tuple: the row that was returned from the table
"""
""" payload (tuple): [item_id, location_id] """
item_locations = ()
database_config = config.config()
select_item_location_sql = f"SELECT * FROM {site_name}_item_locations WHERE part_id = %s AND location_id = %s;"
@ -300,17 +272,7 @@ def selectItemLocationsTuple(site_name, payload, convert=True):
return error
def selectCostLayersTuple(site_name, payload, convert=True):
"""select a single or series of cost layers from the database for site_name
Args:
conn (_T_connector@connect):
site_name (str):
payload (tuple): (item_locations_id, )
convert (bool): defaults to False, used for determining return as tuple/dict
Returns:
list: list of tuples/dict from the cost_layers table for site_name
"""
""" payload (tuple): (item_locations_id, ) """
cost_layers = ()
database_config = config.config()
select_cost_layers_sql = f"SELECT cl.* FROM {site_name}_item_locations il JOIN {site_name}_cost_layers cl ON cl.id = ANY(il.cost_layers) where il.id=%s;"
@ -329,19 +291,7 @@ def selectCostLayersTuple(site_name, payload, convert=True):
return error
def selectSiteTuple(payload, convert=True):
"""Select a single Site from sites using site_name
Args:
conn (_T_connector@connect): Postgresql Connector
payload (tuple): (site_name,)
convert (bool, optional): determines if to return tuple as dictionary. Defaults to False.
Raises:
DatabaseError:
Returns:
tuple or dict: selected tuples
"""
""" payload (tuple): (site_name,) """
site = ()
database_config = config.config()
select_site_sql = f"SELECT * FROM sites WHERE site_name = %s;"
@ -381,24 +331,24 @@ def paginateZonesBySku(site: str, payload: tuple, convert=True):
raise postsqldb.DatabaseError(error, payload, sql)
def paginateLocationsWithZone(site:str, payload:tuple, convert:bool=True):
recordset, count = (), 0
database_config = config.config()
with open(f"application/items/sql/getLocationsWithZone.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
with psycopg2.connect(**database_config) as conn:
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchall()
if rows and convert:
recordset = [postsqldb.tupleDictionaryFactory(cur.description, row) for row in rows]
elif rows and not convert:
recordset = rows
cur.execute(f"SELECT COUNT(*) FROM {site}_locations;")
count = cur.fetchone()[0]
return recordset, count
except Exception as error:
raise postsqldb.DatabaseError(error, (), sql)
recordset, count = (), 0
database_config = config.config()
with open(f"application/items/sql/getLocationsWithZone.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
with psycopg2.connect(**database_config) as conn:
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchall()
if rows and convert:
recordset = [postsqldb.tupleDictionaryFactory(cur.description, row) for row in rows]
elif rows and not convert:
recordset = rows
cur.execute(f"SELECT COUNT(*) FROM {site}_locations;")
count = cur.fetchone()[0]
return recordset, count
except Exception as error:
raise postsqldb.DatabaseError(error, (), sql)
def paginateLocationsBySkuZone(site: str, payload: tuple, convert=True):
database_config = config.config()
@ -471,20 +421,7 @@ def insertCostLayersTuple(site, payload, convert=True, conn=None):
raise postsqldb.DatabaseError(error, payload, sql)
def insertItemLocationsTuple(site, payload, convert=True, conn=None):
"""insert payload into item_locations table for site
Args:
conn (_T_connector@connect): Postgresql Connector
site (str):
payload (tuple): (part_id[int], location_id[int], quantity_on_hand[float], cost_layers[lst2pgarr])
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
Raises:
DatabaseError:
Returns:
tuple or dict: inserted tuple
"""
""" payload (tuple): (part_id[int], location_id[int], quantity_on_hand[float], cost_layers[lst2pgarr]) """
location = ()
self_conn = False
database_config = config.config()
@ -514,21 +451,8 @@ def insertItemLocationsTuple(site, payload, convert=True, conn=None):
raise postsqldb.DatabaseError(error, payload, sql)
def insertLogisticsInfoTuple(site, payload, convert=True, conn=None):
"""insert payload into logistics_info table for site
Args:
conn (_T_connector@connect): Postgresql Connector
site (str):
payload (tuple): (barcode[str], primary_location[str], auto_issue_location[str], dynamic_locations[jsonb],
location_data[jsonb], quantity_on_hand[float])
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
Raises:
DatabaseError:
Returns:
tuple or dict: inserted tuple
"""
""" payload (tuple): (barcode[str], primary_location[str], auto_issue_location[str], dynamic_locations[jsonb],
location_data[jsonb], quantity_on_hand[float]) """
logistics_info = ()
self_conn = False
@ -559,21 +483,8 @@ def insertLogisticsInfoTuple(site, payload, convert=True, conn=None):
raise postsqldb.DatabaseError(error, payload, sql)
def insertItemInfoTuple(site, payload, convert=True, conn=None):
"""inserts payload into the item_info table of site
Args:
conn (_T_connector@connect): Postgresql Connector
site_name (str):
payload (tuple): (barcode[str], linked_items[lst2pgarr], shopping_lists[lst2pgarr], recipes[lst2pgarr], groups[lst2pgarr],
packaging[str], uom[str], cost[float], safety_stock[float], lead_time_days[float], ai_pick[bool])
convert (bool optional): Determines if to return tuple as dictionary. DEFAULTS to False.
Raises:
DatabaseError:
Returns:
tuple or dict: inserted tuple
"""
""" payload (tuple): (barcode[str], linked_items[lst2pgarr], shopping_lists[lst2pgarr], recipes[lst2pgarr], groups[lst2pgarr],
packaging[str], uom[str], cost[float], safety_stock[float], lead_time_days[float], ai_pick[bool]) """
item_info = ()
self_conn = False
with open(f"application/items/sql/insertItemInfoTuple.sql", "r+") as file:
@ -601,20 +512,7 @@ def insertItemInfoTuple(site, payload, convert=True, conn=None):
raise postsqldb.DatabaseError(error, payload, sql)
def insertFoodInfoTuple(site, payload, convert=True, conn=None):
"""insert payload into food_info table for site
Args:
conn (_T_connector@connect): Postgresql Connector
site (str):
payload (_type_): (ingrediants[lst2pgarr], food_groups[lst2pgarr], nutrients[jsonstr], expires[bool])
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
Raises:
DatabaseError:
Returns:
tuple or dict: inserted tuple
"""
""" payload (_type_): (ingrediants[lst2pgarr], food_groups[lst2pgarr], nutrients[jsonstr], expires[bool]) """
food_info = ()
self_conn = False
with open(f"application/items/sql/insertFoodInfoTuple.sql", "r+") as file:
@ -643,22 +541,9 @@ def insertFoodInfoTuple(site, payload, convert=True, conn=None):
raise postsqldb.DatabaseError(error, payload, sql)
def insertItemTuple(site, payload, convert=True, conn=None):
"""insert payload into items table for site
Args:
conn (_T_connector@connect): Postgresql Connector
site (str):
payload (tuple): (barcode[str], item_name[str], brand[int], description[str],
""" payload (tuple): (barcode[str], item_name[str], brand[int], description[str],
tags[lst2pgarr], links[jsonb], item_info_id[int], logistics_info_id[int],
food_info_id[int], row_type[str], item_type[str], search_string[str])
convert (bool, optional): Determines if to return tuple as a dictionary. Defaults to False.
Raises:
DatabaseError:
Returns:
tuple or dict: inserted tuple
"""
food_info_id[int], row_type[str], item_type[str], search_string[str]) """
item = ()
self_conn = False
with open(f"application/items/sql/insertItemTuple.sql", "r+") as file:
@ -687,99 +572,66 @@ def insertItemTuple(site, payload, convert=True, conn=None):
raise postsqldb.DatabaseError(error, payload, sql)
def insertSKUPrefixtuple(site:str, payload:tuple, convert=True, conn=None):
"""insert payload into zones table of site
""" payload (tuple): (name[str],) """
prefix = ()
self_conn = False
with open(f"application/items/sql/insertSKUPrefixTuple.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
if not conn:
database_config = config.config()
conn = psycopg2.connect(**database_config)
conn.autocommit = True
self_conn = True
Args:
conn (_T_connector@connect): Postgresql Connector
site (str):
payload (tuple): (name[str],)
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
prefix = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
prefix = rows
Raises:
DatabaseError:
if self_conn:
conn.commit()
conn.close()
Returns:
tuple or dict: inserted tuple
"""
prefix = ()
self_conn = False
with open(f"application/items/sql/insertSKUPrefixTuple.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
if not conn:
database_config = config.config()
conn = psycopg2.connect(**database_config)
conn.autocommit = True
self_conn = True
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
prefix = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
prefix = rows
if self_conn:
conn.commit()
conn.close()
return prefix
except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql)
return prefix
except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql)
def insertConversionTuple(site: str, payload: list, convert=True, conn=None):
"""insert into recipes table for site
""" payload (tuple): (item_id, uom_id, conversion_factor) """
record = ()
self_conn = False
with open(f"sql/INSERT/insertConversionsTuple.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
if not conn:
database_config = config.config()
conn = psycopg2.connect(**database_config)
conn.autocommit = True
self_conn = True
Args:
conn (_T_connector@connect): Postgresql Connector
site (stre):
payload (tuple): (item_id, uom_id, conversion_factor)
convert (bool, optional): Determines if to return tuple as a dictionary. Defaults to False.
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
record = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
record = rows
Raises:
DatabaseError:
if self_conn:
conn.commit()
conn.close()
Returns:
tuple or dict: inserted tuple
"""
record = ()
self_conn = False
with open(f"sql/INSERT/insertConversionsTuple.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
if not conn:
database_config = config.config()
conn = psycopg2.connect(**database_config)
conn.autocommit = True
self_conn = True
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
record = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
record = rows
if self_conn:
conn.commit()
conn.close()
return record
except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql)
return record
except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql)
def postDeleteCostLayer(site_name, payload, convert=True, conn=None):
"""
payload (tuple): (table_to_delete_from, tuple_id)
Raises:
DatabaseError:
Returns:
tuple or dict: deleted tuple
"""
""" payload (tuple): (table_to_delete_from, tuple_id) """
deleted = ()
self_conn = False
sql = f"WITH deleted_rows AS (DELETE FROM {site_name}_cost_layers WHERE id IN ({','.join(['%s'] * len(payload))}) RETURNING *) SELECT * FROM deleted_rows;"
@ -808,21 +660,7 @@ def postDeleteCostLayer(site_name, payload, convert=True, conn=None):
raise postsqldb.DatabaseError(error, payload, sql)
def deleteConversionTuple(site_name: str, payload: tuple, convert=True, conn=None):
"""This is a basic funtion to delete a tuple from a table in site with an id. All
tables in this database has id's associated with them.
Args:
conn (_T_connector@connect): Postgresql Connector
site_name (str):
payload (tuple): (tuple_id,...)
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
Raises:
DatabaseError:
Returns:
tuple or dict: deleted tuple
"""
""" payload (tuple): (tuple_id,...) """
deleted = ()
self_conn = False
sql = f"WITH deleted_rows AS (DELETE FROM {site_name}_conversions WHERE id IN ({','.join(['%s'] * len(payload))}) RETURNING *) SELECT * FROM deleted_rows;"
@ -851,93 +689,65 @@ def deleteConversionTuple(site_name: str, payload: tuple, convert=True, conn=Non
raise postsqldb.DatabaseError(error, payload, sql)
def updateConversionTuple(site:str, payload: dict, convert=True, conn=None):
"""_summary_
""" payload (dict): {'id': row_id, 'update': {... column_to_update: value_to_update_to...}} """
updated = ()
self_conn = False
set_clause, values = postsqldb.updateStringFactory(payload['update'])
values.append(payload['id'])
sql = f"UPDATE {site}_conversions SET {set_clause} WHERE id=%s RETURNING *;"
try:
if not conn:
database_config = config.config()
conn = psycopg2.connect(**database_config)
conn.autocommit = False
self_conn = True
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.
with conn.cursor() as cur:
cur.execute(sql, values)
rows = cur.fetchone()
if rows and convert:
updated = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
updated = rows
if self_conn:
conn.commit()
conn.close()
Raises:
DatabaseError:
Returns:
tuple or dict: updated tuple
"""
updated = ()
self_conn = False
set_clause, values = postsqldb.updateStringFactory(payload['update'])
values.append(payload['id'])
sql = f"UPDATE {site}_conversions SET {set_clause} WHERE id=%s RETURNING *;"
try:
if not conn:
database_config = config.config()
conn = psycopg2.connect(**database_config)
conn.autocommit = False
self_conn = True
with conn.cursor() as cur:
cur.execute(sql, values)
rows = cur.fetchone()
if rows and convert:
updated = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
updated = rows
if self_conn:
conn.commit()
conn.close()
return updated
except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql)
return updated
except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql)
def updateItemInfoTuple(site:str, payload: dict, convert=True, conn=None):
"""_summary_
""" payload (dict): {'id': row_id, 'update': {... column_to_update: value_to_update_to...}} """
updated = ()
self_conn = False
set_clause, values = postsqldb.updateStringFactory(payload['update'])
values.append(payload['id'])
sql = f"UPDATE {site}_item_info SET {set_clause} WHERE id=%s RETURNING *;"
try:
if not conn:
database_config = config.config()
conn = psycopg2.connect(**database_config)
conn.autocommit = False
self_conn = True
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.
with conn.cursor() as cur:
cur.execute(sql, values)
rows = cur.fetchone()
if rows and convert:
updated = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
updated = rows
if self_conn:
conn.commit()
conn.close()
Raises:
DatabaseError:
return updated
Returns:
tuple or dict: updated tuple
"""
updated = ()
self_conn = False
set_clause, values = postsqldb.updateStringFactory(payload['update'])
values.append(payload['id'])
sql = f"UPDATE {site}_item_info SET {set_clause} WHERE id=%s RETURNING *;"
try:
if not conn:
database_config = config.config()
conn = psycopg2.connect(**database_config)
conn.autocommit = False
self_conn = True
with conn.cursor() as cur:
cur.execute(sql, values)
rows = cur.fetchone()
if rows and convert:
updated = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
updated = rows
if self_conn:
conn.commit()
conn.close()
return updated
except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql)
except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql)
def postUpdateItemLocation(site: str, payload: tuple, conn=None):
@ -968,11 +778,7 @@ def postUpdateItemLocation(site: str, payload: tuple, conn=None):
# TODO: This should be in the item's process module
def postUpdateItem(site:str, payload:dict):
""" POST and update to an item
Args:
site (str): name of the site the item exists in.
payload (dict): STRICT FORMAT
""" payload (dict): STRICT FORMAT
{id: item_id, data: SEE BELOW, user_id: updater}
data is complex structure
@ -1060,14 +866,10 @@ def postUpdateItem(site:str, payload:dict):
postAddTransaction(conn, site, trans.payload())
except Exception as error:
raise postsqldb.DatabaseError(error, payload, "MULTICALL!")
def postUpdateItemLink(site: str, payload: dict):
""" POST update to ItemLink
Args:
site (str): _description_
payload (dict): {id, update, old_conv_factor, user_id}
"""
# TODO: This should be in the item's process module
def postUpdateItemLink(site: str, payload: dict):
""" payload (dict): {id, update, old_conv_factor, user_id} """
def postUpdateData(conn, table, payload, convert=True):
updated = ()
set_clause, values = postsqldb.updateStringFactory(payload['update'])
@ -1123,21 +925,7 @@ def postUpdateItemLink(site: str, payload: dict):
postAddTransaction(conn, site, transaction.payload())
def postUpdateCostLayer(site, payload, convert=True, conn=None):
"""_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
"""
""" payload (dict): {'id': row_id, 'update': {... column_to_update: value_to_update_to...}} """
updated = ()
self_conn = False
@ -1168,47 +956,34 @@ def postUpdateCostLayer(site, payload, convert=True, conn=None):
raise postsqldb.DatabaseError(error, payload, sql)
def postAddTransaction(site, payload, convert=False, conn=None):
transaction = ()
self_conn = False
transaction = ()
self_conn = False
with open(f"application/items/sql/insertTransactionsTuple.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
if not conn:
database_config = config.config()
conn = psycopg2.connect(**database_config)
conn.autocommit = False
self_conn = True
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
transaction = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
transaction = rows
if self_conn:
conn.commit()
conn.close()
return transaction
except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql)
with open(f"application/items/sql/insertTransactionsTuple.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
if not conn:
database_config = config.config()
conn = psycopg2.connect(**database_config)
conn.autocommit = False
self_conn = True
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
transaction = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
transaction = rows
if self_conn:
conn.commit()
conn.close()
return transaction
except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql)
def postInsertItemLink(site, payload, convert=True, conn=None):
"""insert payload into itemlinks table of site
Args:
conn (_T_connector@connect): Postgresql Connector
site (str):
payload (tuple): (barcode[str], link[int], data[jsonb], conv_factor[float])
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
Raises:
DatabaseError:
Returns:
tuple or dict: inserted tuple
"""
""" payload (tuple): (barcode[str], link[int], data[jsonb], conv_factor[float]) """
link = ()
self_conn = False
@ -1238,21 +1013,7 @@ def postInsertItemLink(site, payload, convert=True, conn=None):
raise postsqldb.DatabaseError(error, payload, sql)
def postUpdateItemByID(site, payload, convert=True, conn=None):
""" high level update of an item specific data, none of its relationships
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
"""
""" payload (dict): {'id': row_id, 'update': {... column_to_update: value_to_update_to...}} """
updated = ()
self_conn = False
set_clause, values = postsqldb.updateStringFactory(payload['update'])

View File

@ -7,7 +7,7 @@ import math
# APPLICATION IMPORTS
from config import config
from user_api import login_required
from application.access_module import access_api
import application.postsqldb as db
from application.items import database_items
from application.items import items_processes
@ -15,15 +15,15 @@ import application.database_payloads as dbPayloads
items_api = Blueprint('items_api', __name__, template_folder="templates", static_folder="static")
def update_session_user():
database_config = config()
with psycopg2.connect(**database_config) as conn:
user = db.LoginsTable.get_washed_tuple(conn, (session['user_id'],))
session['user'] = user
database_config = config()
with psycopg2.connect(**database_config) as conn:
user = db.LoginsTable.get_washed_tuple(conn, (session['user_id'],))
session['user'] = user
# ROOT TEMPLATE ROUTES
@items_api.route("/")
@login_required
@access_api.login_required
def items():
update_session_user()
sites = [site[1] for site in db.get_sites(session['user']['sites'])]
@ -32,7 +32,7 @@ def items():
sites=sites)
@items_api.route("/<id>")
@login_required
@access_api.login_required
def item(id):
sites = [site[1] for site in db.get_sites(session['user']['sites'])]
database_config = config()
@ -41,47 +41,30 @@ def item(id):
return render_template("item_new.html", id=id, units=units, current_site=session['selected_site'], sites=sites)
@items_api.route("/transaction")
@login_required
@access_api.login_required
def transaction():
sites = [site[1] for site in db.get_sites(session['user']['sites'])]
database_config = config()
with psycopg2.connect(**database_config) as conn:
units = db.UnitsTable.getAll(conn)
return render_template("transaction.html", units=units, current_site=session['selected_site'], sites=sites, proto={'referrer': request.referrer})
sites = [site[1] for site in db.get_sites(session['user']['sites'])]
database_config = config()
with psycopg2.connect(**database_config) as conn:
units = db.UnitsTable.getAll(conn)
return render_template("transaction.html", units=units, current_site=session['selected_site'], sites=sites, proto={'referrer': request.referrer})
@items_api.route("/transactions/<id>")
@login_required
@access_api.login_required
def transactions(id):
"""This is the main endpoint to reach the webpage for an items transaction history
---
parameters:
- name: id
in: path
type: integer
required: true
default: all
responses:
200:
description: Returns the transactions.html webpage for the item with passed ID
"""
sites = [site[1] for site in db.get_sites(session['user']['sites'])]
return render_template("transactions.html", id=id, current_site=session['selected_site'], sites=sites)
sites = [site[1] for site in db.get_sites(session['user']['sites'])]
return render_template("transactions.html", id=id, current_site=session['selected_site'], sites=sites)
@items_api.route("/<parent_id>/itemLink/<id>")
@login_required
@access_api.login_required
def itemLink(parent_id, id):
sites = [site[1] for site in db.get_sites(session['user']['sites'])]
return render_template("itemlink.html", current_site=session['selected_site'], sites=sites, proto={'referrer': request.referrer}, id=id)
sites = [site[1] for site in db.get_sites(session['user']['sites'])]
return render_template("itemlink.html", current_site=session['selected_site'], sites=sites, proto={'referrer': request.referrer}, id=id)
# API CALLS
@items_api.route("/getTransactions", methods=["GET"])
@login_required
@access_api.login_required
def getTransactions():
""" GET a subquery of transactions by passing a logistics_info_id, limit, and page
---
responses:
200:
description: transactions received successfully.
"""
if request.method == "GET":
recordset = []
count = 0
@ -95,23 +78,8 @@ def getTransactions():
return jsonify({"transactions": recordset, "end": math.ceil(count/limit), "error": True, "message": f"method {request.method} is not allowed."})
@items_api.route("/getTransaction", methods=["GET"])
@login_required
@access_api.login_required
def getTransaction():
""" GET a transaction from the system by passing an ID
---
parameters:
- in: query
name: id
schema:
type: integer
minimum: 1
default: 1
required: true
description: The transaction.id
responses:
200:
description: Transaction Object received successfully.
"""
transaction = ()
if request.method == "GET":
id = int(request.args.get('id', 1))
@ -121,22 +89,8 @@ def getTransaction():
return jsonify({"transaction": transaction, "error": True, "message": f"method {request.method} is not allowed."})
@items_api.route("/getItem", methods=["GET"])
@login_required
@access_api.login_required
def get_item():
""" GET item from system by passing its ID
---
parameters:
- in: query
name: id
schema:
type: integer
minimum: 1
default: 1
description: item.id
responses:
200:
description: Item.id received successfully!
"""
if request.method == "GET":
id = int(request.args.get('id', 1))
site_name = session['selected_site']
@ -146,46 +100,8 @@ def get_item():
return jsonify({'item': item, 'error': True, 'message': f'method {request.method} not allowed.'})
@items_api.route("/getItemsWithQOH", methods=['GET'])
@login_required
@access_api.login_required
def pagninate_items():
""" GET items from the system by passing a page, limit, search_string, sort, and order
---
parameters:
- in: query
name: page
schema:
type: integer
default: 1
description: page number for offset
- in: query
name: limit
schema:
type: integer
default: 50
description: number of records to grab
- in: query
name: search_string
schema:
type: string
default: ''
description: string to look for in column search_string
- in: query
name: sort
schema:
type: string
default: ''
description: items table column to sort by
- in: query
name: order
schema:
type: string
enum: ['ASC', 'DESC']
default: 'ASC'
description: Order to sort items table sort parameter by
responses:
200:
description: Items received successfully.
"""
items = []
count = 0
if request.method == "GET":
@ -207,33 +123,8 @@ def pagninate_items():
return jsonify({'items': items, "end": math.ceil(count/limit), 'error':True, 'message': 'There was a problem loading the items!'})
@items_api.route('/getModalItems', methods=["GET"])
@login_required
@access_api.login_required
def getModalItems():
""" GET items from the system by passing a page, limit, search_string. For select modals
---
parameters:
- in: query
name: page
schema:
type: integer
default: 1
description: page number for offset
- in: query
name: limit
schema:
type: integer
default: 25
description: number of records to grab
- in: query
name: search_string
schema:
type: string
default: ''
description: string to look for in column search_string
responses:
200:
description: Items received successfully.
"""
recordset, count = tuple(), 0
if request.method == "GET":
page = int(request.args.get('page', 1))
@ -247,29 +138,8 @@ def getModalItems():
return jsonify({"items":recordset, "end":math.ceil(count/limit), "error":True, "message": f"method {request.method} is not allowed."})
@items_api.route('/getPrefixes', methods=["GET"])
@login_required
@access_api.login_required
def getModalPrefixes():
""" GET prefixes from the system by passing page and limit.
---
parameters:
- in: query
name: page
schema:
type: integer
minimum: 1
default: 1
description: page of the database records
- in: query
name: limit
schema:
type: integer
minimum: 1
default: 10
description: number of database records to GET
responses:
200:
description: Prefixes received from the system successfully!
"""
recordset = []
count = 0
if request.method == "GET":
@ -283,36 +153,8 @@ def getModalPrefixes():
return jsonify({"prefixes":recordset, "end":math.ceil(count/limit), "error":True, "message":f"method {request.method} is not allowed!"})
@items_api.route('/getZonesBySku', methods=["GET"])
@login_required
@access_api.login_required
def getZonesbySku():
""" GET zones by sku by passing page, limit, item_id
---
parameters:
- in: query
name: page
schema:
type: integer
minimum: 1
default: 1
description: page of the records to GET
- in: query
name: limit
schema:
type: integer
minimum: 1
default: 10
description: number of records to grab from the system
- in: query
name: item_id
schema:
type: integer
minimum: 1
default: 1
description: item_id to pull zones for
responses:
200:
description: Zones received successfully.
"""
zones, count = [], 0
if request.method == "GET":
page = int(request.args.get('page', 1))
@ -325,43 +167,8 @@ def getZonesbySku():
return jsonify({'zones': zones, 'endpage': math.ceil(count/limit), 'error':False, 'message': f'method {request.method} not allowed.'})
@items_api.route('/getLocationsBySkuZone', methods=['GET'])
@login_required
@access_api.login_required
def getLocationsBySkuZone():
""" GET locations by sku by passing page, limit, item_id, zone_id
---
parameters:
- in: query
name: page
schema:
type: integer
minimum: 1
default: 1
description: page of the records to GET
- in: query
name: limit
schema:
type: integer
minimum: 1
default: 10
description: number of records to grab from the system
- in: query
name: item_id
schema:
type: integer
minimum: 1
default: 1
description: item_id to pull locations for zone_id
- in: query
name: zone_id
schema:
type: integer
minimum: 1
default: 1
description: zone_id to pull locations for item_id
responses:
200:
description: Locations received successfully.
"""
locations, count = [], 0
if request.method == "GET":
zone_id = int(request.args.get('zone_id', 1))
@ -375,29 +182,8 @@ def getLocationsBySkuZone():
return jsonify({'locations': locations, 'endpage': math.ceil(count/limit), 'error': True, 'message': f'method {request.method} is not allowed.'})
@items_api.route('/getBrands', methods=['GET'])
@login_required
@access_api.login_required
def getBrands():
""" GET brands from the system by passing page, limit
---
parameters:
- in: query
name: page
schema:
type: integer
minimum: 1
default: 1
description: page of the records to GET
- in: query
name: limit
schema:
type: integer
minimum: 1
default: 10
description: number of records to grab from the system
responses:
200:
description: Brands received successfully.
"""
brands, count = [], 0
if request.method == "GET":
page = int(request.args.get('page', 1))
@ -409,25 +195,8 @@ def getBrands():
return jsonify({'brands': brands, 'endpage': math.ceil(count/limit), 'error': True, 'message': f'method {request.method} is not allowed.'})
@items_api.route('/updateItem', methods=['POST'])
@login_required
@access_api.login_required
def updateItem():
""" POST update to item in the system by passing item_id, data
---
parameters:
- in: query
name: item_id
schema:
type: integer
minimum: 1
default: 1
description: item_id that the POST targets
- in: header
name: data
description: data to update in system
responses:
200:
description: item updated successfully.
"""
if request.method == "POST":
id = request.get_json()['id']
data = request.get_json()['data']
@ -437,41 +206,8 @@ def updateItem():
return jsonify({'error': True, 'message': f'method {request.method} is not allowed!'})
@items_api.route('/updateItemLink', methods=['POST'])
@login_required
@access_api.login_required
def updateItemLink():
""" UPDATE item link by passing id, conv_factor, barcode, old_conv
---
parameters:
- in: query
name: id
schema:
type: integer
minimum: 1
default: 1
required: true
description: Id of item link to update
- in: query
name: conv_factor
schema:
type: integer
required: true
description: new conversion factor of item_link id
- in: query
name: barcode
schema:
type: string
required: true
description: barcode of item_link id
- in: query
name: old_conv
schema:
type: integer
required: true
description: old conversion factor of item_link id
responses:
200:
description: Item Link updated successfully.
"""
if request.method == "POST":
id = request.get_json()['id']
conv_factor = request.get_json()['conv_factor']
@ -484,29 +220,8 @@ def updateItemLink():
return jsonify({'error': True, 'message': f"method {request.method} not allowed."})
@items_api.route('/getPossibleLocations', methods=["GET"])
@login_required
@access_api.login_required
def getPossibleLocations():
""" GET locations with zones by passing a page and limit
---
parameters:
- in: query
name: page
schema:
type: interger
minimum: 1
default: 1
description: page in the records to GET
- in: query
name: limit
schema:
type: interger
minimum: 1
default: 1
description: number of records to GET
responses:
200:
description: Locations GET successful.
"""
locations, count = (), 0
if request.method == "GET":
page = int(request.args.get('page', 1))
@ -518,22 +233,8 @@ def getPossibleLocations():
return jsonify({'locations': locations, 'end':math.ceil(count/limit), 'error':True, 'message': f'method {request.method} not allowed.'})
@items_api.route('/getLinkedItem', methods=["GET"])
@login_required
@access_api.login_required
def getLinkedItem():
""" GET itemlink from system by passing an ID
---
parameters:
- in: query
name: id
schema:
type: integer
default: 1
required: true
description: item link to get from the system
responses:
200:
description: Item Link GET successful.
"""
linked_item = {}
if request.method == "GET":
id = int(request.args.get('id', 1))
@ -543,36 +244,8 @@ def getLinkedItem():
return jsonify({'linked_item': linked_item, 'error': True, 'message': f'method {request.method} not allowed'})
@items_api.route('/addLinkedItem', methods=["POST"])
@login_required
@access_api.login_required
def addLinkedItem():
""" POST a link between items by passing a parent_id, a child_id, conv_factor
---
parameters:
- in: query
name: parent_id
schema:
type: integer
default: 1
required: true
description: id to linked list item
- in: query
name: child_id
schema:
type: integer
default: 1
required: true
description: id to item to be linked to list.
- in: query
name: conv_factor
schema:
type: integer
default: 1
required: true
description: integer factor between child id to parent id.
responses:
200:
description: Items linked successfully.
"""
if request.method == "POST":
parent_id = request.get_json()['parent_id']
child_id = request.get_json()['child_id']
@ -591,35 +264,8 @@ def addLinkedItem():
return jsonify({'error': True, 'message': 'These was an error with adding to the linked list!'})
@items_api.route('/addBlankItem', methods=["POST"])
@access_api.login_required
def addBlankItem():
""" POST new Blank item to the system given a barcode, item_name, subtype
---
parameters:
- in: query
name: barcode
schema:
type: string
default: 1
required: true
description: barcode for the item
- in: query
name: item_name
schema:
type: string
default: 1
required: true
description: name of the blank item
- in: query
name: subtype
schema:
type: string
default: 1
required: true
description: type of item this is categorized to be.
responses:
200:
description: Item added successfully.
"""
if request.method == "POST":
data = {
'barcode': request.get_json()['barcode'],
@ -635,35 +281,8 @@ def addBlankItem():
return jsonify({'error': True, 'message': 'These was an error with adding Item!'})
@items_api.route('/addSKUPrefix', methods=["POST"])
@access_api.login_required
def addSKUPrefix():
""" POST new SKU Prefix to the system given a uuid, name, description
---
parameters:
- in: query
name: uuid
schema:
type: string
default: 1
required: true
description: uuid for the sku which will be attached to items
- in: query
name: name
schema:
type: string
default: 1
required: true
description: name of the Prefix
- in: query
name: description
schema:
type: string
default: 1
required: true
description: description of the Prefix.
responses:
200:
description: Prefix added successfully.
"""
if request.method == "POST":
site_name = session['selected_site']
prefix = dbPayloads.SKUPrefixPayload(
@ -676,35 +295,9 @@ def addSKUPrefix():
return jsonify({'error': True, 'message': 'These was an error with adding this Prefix!'})
@items_api.route('/addConversion', methods=['POST'])
@access_api.login_required
def addConversion():
""" POST new conversion to the system given a item_id, uom_id, conv_factor
---
parameters:
- in: header
name: item_id
schema:
type: integer
default: 1
required: true
description: item_id the conversion applies to
- in: header
name: uom_id
schema:
type: integer
default: 1
required: true
description: uom_id to match item_id uom to convert to
- in: header
name: conv_factor
schema:
type: float
default: 1
required: true
description: item_id.uom -> uom_id amount
responses:
200:
description: Prefix added successfully.
"""
if request.method == "POST":
item_id = request.get_json()['parent_id']
uom_id = request.get_json()['uom_id']
@ -721,21 +314,8 @@ def addConversion():
return jsonify(error=True, message="Unable to save this conversion, ERROR!")
@items_api.route('/deleteConversion', methods=['POST'])
@access_api.login_required
def deleteConversion():
""" POST delete conversion to the system given a conversion_id
---
parameters:
- in: header
name: conversion_id
schema:
type: integer
default: 1
required: true
description: conversion_id to be deleted
responses:
200:
description: Prefix added successfully.
"""
if request.method == "POST":
conversion_id = request.get_json()['conversion_id']
site_name = session['selected_site']
@ -744,28 +324,8 @@ def deleteConversion():
return jsonify(error=True, message="Unable to delete this conversion, ERROR!")
@items_api.route('/updateConversion', methods=['POST'])
@access_api.login_required
def updateConversion():
""" POST update conversion to the system given a conversion_id, update dictionary
---
parameters:
- in: header
name: conversion_id
schema:
type: integer
default: 1
required: true
description: conversion_id to be deleted
- in: header
name: update
schema:
type: dict
default: 1
required: true
description: data to update in key=column, value=update_data
responses:
200:
description: conversion updated successfully.
"""
if request.method == "POST":
conversion_id = request.get_json()['conversion_id']
update_dictionary = request.get_json()['update']
@ -775,28 +335,8 @@ def updateConversion():
return jsonify(error=True, message="Unable to save this conversion, ERROR!")
@items_api.route('/addPrefix', methods=['POST'])
@access_api.login_required
def addPrefix():
""" POST add prefix to the system given a item_info_id and prefix_id
---
parameters:
- in: header
name: item_info_id
schema:
type: integer
default: 1
required: true
description: item_info_id to be updated
- in: header
name: prefix_id
schema:
type: integer
default: 1
required: true
description: prefix_id to be added
responses:
200:
description: conversion updated successfully.
"""
if request.method == "POST":
item_info_id = request.get_json()['parent_id']
prefix_id = request.get_json()['prefix_id']
@ -808,28 +348,8 @@ def addPrefix():
return jsonify(error=True, message="Unable to save this prefix, ERROR!")
@items_api.route('/deletePrefix', methods=['POST'])
@access_api.login_required
def deletePrefix():
""" POST delete prefix from the system given a item_info_id and prefix_id
---
parameters:
- in: header
name: item_info_id
schema:
type: integer
default: 1
required: true
description: item_info_id to be updated
- in: header
name: prefix_id
schema:
type: integer
default: 1
required: true
description: prefix_id to be added
responses:
200:
description: conversion updated successfully.
"""
if request.method == "POST":
item_info_id = request.get_json()['item_info_id']
prefix_id = request.get_json()['prefix_id']
@ -841,21 +361,8 @@ def deletePrefix():
return jsonify(error=True, message="Unable to delete this prefix, ERROR!")
@items_api.route('/refreshSearchString', methods=['POST'])
@access_api.login_required
def refreshSearchString():
""" POST update search_string to the system given a item_id
---
parameters:
- in: header
name: item_info_id
schema:
type: integer
default: 1
required: true
description: item_id to be updated
responses:
200:
description: conversion updated successfully.
"""
if request.method == "POST":
item_id = request.get_json()['item_id']
site_name = session['selected_site']
@ -866,28 +373,8 @@ def refreshSearchString():
return jsonify(error=True, message="Unable to update this search string, ERROR!")
@items_api.route('/postNewItemLocation', methods=['POST'])
@access_api.login_required
def postNewItemLocation():
""" POST add itemlocation to the system given a item_id and location_id
---
parameters:
- in: header
name: item_id
schema:
type: integer
default: 1
required: true
description: item_id to be attached location_id to
- in: header
name: item_id
schema:
type: integer
default: 1
required: true
description: location_id to attach item_id to
responses:
200:
description: conversion updated successfully.
"""
if request.method == "POST":
item_id = request.get_json()['item_id']
location_id = request.get_json()['location_id']
@ -898,6 +385,7 @@ def postNewItemLocation():
return jsonify(error=True, message="Unable to save this location, ERROR!")
@items_api.route("/getItemLocations", methods=["GET"])
@access_api.login_required
def getItemLocations():
recordset = []
count = 0
@ -911,8 +399,8 @@ def getItemLocations():
return jsonify({"locations":recordset, "end":math.ceil(count/limit), "error":False, "message":"item fetched succesfully!"})
return jsonify({"locations":recordset, "end": math.ceil(count/limit), "error":True, "message":"There was an error with this GET statement"})
@items_api.route('/postTransaction', methods=["POST"])
@access_api.login_required
def post_transaction():
if request.method == "POST":
result = items_processes.postAdjustment(

View File

@ -1,9 +1,9 @@
# 3rd party imports
# 3RD PARTY IMPORTS
import datetime
import psycopg2
import json
# applications imports
# APPLICATION IMPORTS
from application.items import database_items
import application.postsqldb as db
import application.database_payloads as dbPayloads

View File

@ -1,12 +1,12 @@
# 3rd Party imports
# 3RD PARTY IMPORTS
from flask import (
Blueprint, request, render_template, redirect, session, url_for, send_file, jsonify, Response
)
import psycopg2
# applications imports
# APPLICATION IMPORTS
from config import config
from user_api import login_required
from application.access_module import access_api
from application.poe import poe_processes, poe_database
from application import postsqldb
@ -15,14 +15,14 @@ point_of_ease = Blueprint('poe', __name__, template_folder="templates", static_f
@point_of_ease.route('/scanner', methods=["GET"])
@login_required
@access_api.login_required
def scannerEndpoint():
sites = [site[1] for site in postsqldb.get_sites(session['user']['sites'])]
return render_template('scanner.html', current_site=session['selected_site'],
sites=sites)
@point_of_ease.route('/receipts', methods=["GET"])
@login_required
@access_api.login_required
def receiptsEndpoint():
sites = [site[1] for site in postsqldb.get_sites(session['user']['sites'])]
database_config = config()
@ -32,7 +32,7 @@ def receiptsEndpoint():
sites=sites, units=units)
@point_of_ease.route('/getItem/barcode', methods=["GET"])
@login_required
@access_api.login_required
def getItemBarcode():
record = {}
if request.method == "GET":
@ -46,9 +46,8 @@ def getItemBarcode():
return jsonify({"item":record, "error":False, "message":"item fetched succesfully!"})
return jsonify({"item":record, "error":True, "message":"There was an error with this GET statement"})
@point_of_ease.route('/postTransaction', methods=["POST"])
@login_required
@access_api.login_required
def post_transaction():
if request.method == "POST":
result = poe_processes.postTransaction(
@ -59,9 +58,8 @@ def post_transaction():
return jsonify(result)
return jsonify({"error":True, "message":"There was an error with this POST statement"})
@point_of_ease.route('/postReceipt', methods=["POST"])
@login_required
@access_api.login_required
def post_receipt():
if request.method == "POST":
site_name = session['selected_site']

View File

@ -1,4 +1,7 @@
# 3RD PARTY IMPORTS
import psycopg2
# APPLICATION IMPORTS
import config
from application import postsqldb
@ -210,8 +213,7 @@ def selectItemAllByBarcode(site, payload, convert=True, conn=None):
return item
except (Exception, psycopg2.DatabaseError) as error:
raise postsqldb.DatabaseError(error, payload, getItemAllByBarcode_sql)
def insertCostLayersTuple(site, payload, convert=True, conn=None):
cost_layer = ()
self_conn = False
@ -389,7 +391,6 @@ def updateItemLocation(site, payload, convert=True, conn=None):
except Exception as error:
return error
def deleteCostLayersTuple(site, payload, convert=True, conn=None):
deleted = ()
self_conn = False

View File

@ -1,8 +1,8 @@
# 3rd Party imports
# 3RD PARTY IMPORTS
import datetime
import psycopg2
# applications imports
# APPLICATION IMPORTS
from application import postsqldb, database_payloads
from application.poe import poe_database
import config
@ -12,9 +12,8 @@ point of ease module. """
def postTransaction(site_name, user_id, data: dict, conn=None):
'''Takes a set of data as a dictionary and inserts them into the system for passed site_name. '''
#dict_keys(['item_id', 'logistics_info_id', 'barcode', 'item_name', 'transaction_type',
# 'quantity', 'description', 'cost', 'vendor', 'expires', 'location_id'])
""" dict_keys(['item_id', 'logistics_info_id', 'barcode', 'item_name', 'transaction_type',
'quantity', 'description', 'cost', 'vendor', 'expires', 'location_id'])"""
def quantityFactory(quantity_on_hand:float, quantity:float, transaction_type:str):
if transaction_type == "Adjust In":
quantity_on_hand += quantity
@ -101,8 +100,6 @@ def postTransaction(site_name, user_id, data: dict, conn=None):
return {"error": False, "message":f"Transaction Successful!"}
def post_receipt(site_name, user_id, data: dict, conn=None):
'''Takes a list of items and opens and creates a SIR (SCANNED IN RECEIPT) into the system with the items linked
to said receipt.'''
# data = {'items': items}
self_conn = False
items = data['items']

View File

@ -1,5 +1,7 @@
# 3RD PARTY IMPORTS
from flask import (Blueprint, request, render_template, session, jsonify, current_app, send_from_directory)
from flask import (
Blueprint, request, render_template, session, jsonify, current_app, send_from_directory
)
import math
import postsqldb
import mimetypes
@ -7,7 +9,7 @@ import os
# APPLICATION IMPORTS
import webpush
from user_api import login_required
from application.access_module import access_api
from application import postsqldb, database_payloads
from application.receipts import receipts_processes, receipts_database
@ -17,13 +19,13 @@ receipt_api = Blueprint('receipt_api', __name__, template_folder='templates', st
# ROOT TEMPLATE ROUTES
@receipt_api.route("/")
@login_required
@access_api.login_required
def receipts():
sites = [site[1] for site in postsqldb.get_sites(session['user']['sites'])]
return render_template("receipts_index.html", current_site=session['selected_site'], sites=sites)
@receipt_api.route("/<id>")
@login_required
@access_api.login_required
def receipt(id):
sites = [site[1] for site in postsqldb.get_sites(session['user']['sites'])]
units = postsqldb.get_units_of_measure()
@ -31,8 +33,8 @@ def receipt(id):
# API ROUTES
# Added to Database
@receipt_api.route('/api/getItems', methods=["GET"])
@access_api.login_required
def getItems():
recordset = []
count = {'count': 0}
@ -47,8 +49,8 @@ def getItems():
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"})
# Added to Database
@receipt_api.route('/api/getVendors', methods=["GET"])
@access_api.login_required
def getVendors():
recordset = []
count = 0
@ -61,8 +63,8 @@ def getVendors():
return jsonify({"vendors":recordset, "end":math.ceil(count/limit), "error":False, "message":"items fetched succesfully!"})
return jsonify({"vendors":recordset, "end":math.ceil(count/limit), "error":True, "message":"There was an error with this GET statement"})
# Added to Database
@receipt_api.route('/api/getLinkedLists', methods=["GET"])
@access_api.login_required
def getLinkedLists():
recordset = []
count = 0
@ -75,8 +77,8 @@ def getLinkedLists():
return jsonify({"items":recordset, "end":math.ceil(count/limit), "error":False, "message":"items fetched succesfully!"})
return jsonify({"items":recordset, "end":math.ceil(count/limit), "error":True, "message":"There was an error with this GET statement"})
# Added to database
@receipt_api.route('/api/getReceipts', methods=["GET"])
@access_api.login_required
def getReceipts():
recordset = []
if request.method == "GET":
@ -88,8 +90,8 @@ def getReceipts():
return jsonify({'receipts':recordset, "end": math.ceil(count/limit), 'error': False, "message": "Get Receipts Successful!"})
return jsonify({'receipts': recordset, "end": math.ceil(count/limit), 'error': True, "message": "Something went wrong while getting receipts!"})
# Added to database
@receipt_api.route('/api/getReceipt', methods=["GET"])
@access_api.login_required
def getReceipt():
receipt = []
if request.method == "GET":
@ -99,8 +101,8 @@ def getReceipt():
return jsonify({'receipt': receipt, 'error': False, "message": "Get Receipts Successful!"})
return jsonify({'receipt': receipt, 'error': True, "message": "Something went wrong while getting receipts!"})
# added to database
@receipt_api.route('/api/addReceipt', methods=["POST", "GET"])
@access_api.login_required
def addReceipt():
if request.method == "GET":
user_id = session['user_id']
@ -113,8 +115,8 @@ def addReceipt():
return jsonify({'error': False, "message": "Receipt Added Successful!"})
return jsonify({'error': True, "message": "Something went wrong while adding receipt!"})
# Added to Database
@receipt_api.route('/api/addSKULine', methods=["POST"])
@access_api.login_required
def addSKULine():
if request.method == "POST":
item_id = int(request.get_json()['item_id'])
@ -139,8 +141,8 @@ def addSKULine():
return jsonify({'error': False, "message": "Line added Succesfully"})
return jsonify({'error': True, "message": "Something went wrong while add SKU line!"})
# Added to Database
@receipt_api.route('/api/deleteLine', methods=["POST"])
@access_api.login_required
def deleteLine():
if request.method == "POST":
line_id = int(request.get_json()['line_id'])
@ -149,8 +151,8 @@ def deleteLine():
return jsonify({'error': False, "message": "Line Deleted Succesfully"})
return jsonify({'error': True, "message": "Something went wrong while deleting line!"})
# Added to Database
@receipt_api.route('/api/denyLine', methods=["POST"])
@access_api.login_required
def denyLine():
if request.method == "POST":
line_id = int(request.get_json()['line_id'])
@ -159,8 +161,8 @@ def denyLine():
return jsonify({'error': False, "message": "Line Denied Succesfully"})
return jsonify({'error': True, "message": "Something went wrong while denying line!"})
# Added to database
@receipt_api.route('/api/saveLine', methods=["POST"])
@access_api.login_required
def saveLine():
if request.method == "POST":
line_id = int(request.get_json()['line_id'])
@ -173,8 +175,8 @@ def saveLine():
return jsonify({'error': False, "message": "Line Saved Succesfully"})
return jsonify({'error': True, "message": "Something went wrong while saving line!"})
# Added to Process and database!
@receipt_api.route('/api/postLinkedItem', methods=["POST"])
@access_api.login_required
def postLinkedItem():
if request.method == "POST":
receipt_item_id = int(request.get_json()['receipt_item_id'])
@ -194,8 +196,9 @@ def postLinkedItem():
return jsonify({'error': False, "message": "Line Saved Succesfully"})
return jsonify({'error': True, "message": "Something went wrong while saving line!"})
# Added to processes and Database
@receipt_api.route('/api/resolveLine', methods=["POST"])
@access_api.login_required
def resolveLine():
if request.method == "POST":
line_id = int(request.get_json()['line_id'])
@ -206,8 +209,8 @@ def resolveLine():
return jsonify({'error': False, "message": "Line Saved Succesfully"})
return jsonify({'error': True, "message": "Something went wrong while saving line!"})
# add to database
@receipt_api.route('/api/postVendorUpdate', methods=["POST"])
@access_api.login_required
def postVendorUpdate():
if request.method == "POST":
receipt_id = int(request.get_json()['receipt_id'])
@ -217,8 +220,8 @@ def postVendorUpdate():
return jsonify({'error': False, "message": "Line Saved Succesfully"})
return jsonify({'error': True, "message": "Something went wrong while saving line!"})
# added to database
@receipt_api.route('/api/resolveReceipt', methods=["POST"])
@access_api.login_required
def resolveReceipt():
if request.method == "POST":
receipt_id = int(request.get_json()['receipt_id'])
@ -229,8 +232,8 @@ def resolveReceipt():
return jsonify({'error': False, "message": "Line Saved Succesfully"})
return jsonify({'error': True, "message": "Something went wrong while saving line!"})
# added to database
@receipt_api.route('/api/uploadfile/<receipt_id>', methods=["POST"])
@access_api.login_required
def uploadFile(receipt_id):
file = request.files['file']
file_path = current_app.config['FILES_FOLDER'] + f"/receipts/{file.filename.replace(" ", "_")}"
@ -249,15 +252,15 @@ def uploadFile(receipt_id):
receipts_database.updateReceiptsTuple(site_name, {'id': receipt_id, 'update': {'files': receipt_files}})
return jsonify({})
# Does not need to be added to Database
@receipt_api.route('/api/getFile/<file_name>')
@access_api.login_required
def getFile(file_name):
path_ = current_app.config['FILES_FOLDER'] + "/receipts"
print(path_)
return send_from_directory(path_, file_name)
# Added to database
@receipt_api.route('/api/checkAPI', methods=["POST"])
@access_api.login_required
def checkAPI():
if request.method == "POST":
line_id = int(request.get_json()['line_id'])

View File

@ -1,9 +1,12 @@
from application import postsqldb
import config
# 3RD PARTY APPLICATIONS
import psycopg2
import random
import string
# APPLICATION IMPORTS
from application import postsqldb
import config
def getUUID(n):
random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=n))
return random_string

View File

@ -1,53 +1,29 @@
# 3rd party imports
# 3RD PARTY IMPORTS
from flask import (
Blueprint, request, render_template, session, jsonify, current_app, send_from_directory
)
import math
# application imports
# APPLICATION IMPORTS
import main
from user_api import login_required
import webpush
from application.access_module import access_api
from application.recipes import database_recipes
from application import postsqldb as db
recipes_api = Blueprint('recipes_api', __name__, template_folder="templates", static_folder="static")
@recipes_api.route("/")
@login_required
@access_api.login_required
def recipes():
"""This is the main endpoint to reach the webpage for a list of all recipes
---
responses:
200:
description: returns recipes/index.html with sites, current_site.
"""
sites = [site[1] for site in main.get_sites(session['user']['sites'])]
return render_template("recipes_index.html",
current_site=session['selected_site'],
sites=sites)
@recipes_api.route("/<mode>/<id>")
@login_required
@access_api.login_required
def recipe(id, mode='view'):
"""This is the main endpoint to reach the webpage for a recipe's view or edit mode.
---
parameters:
- name: mode
in: path
type: string
required: true
default: view
- name: id
in: path
type: integer
required: true
default: all
responses:
200:
description: Respondes with either the Edit or View webpage for the recipe.
"""
units = database_recipes.getUnits()
print("woot")
if mode == "edit":
@ -56,14 +32,8 @@ def recipe(id, mode='view'):
return render_template("recipe_view.html", recipe_id=id, current_site=session['selected_site'])
@recipes_api.route('/getRecipes', methods=["GET"])
@login_required
@access_api.login_required
def getRecipes():
""" Get a subquery of recipes from the database by passing a page, limit
---
responses:
200:
description: limit of rows passed returned to requester
"""
recipes = []
count=0
if request.method == "GET":
@ -76,15 +46,8 @@ def getRecipes():
return jsonify({'recipes': recipes, 'end': math.ceil(count/limit), 'error': True, 'message': f'method is not allowed: {request.method}'})
@recipes_api.route('/getRecipe', methods=["GET"])
@login_required
@access_api.login_required
def getRecipe():
""" Get a query for recipe id from database by passing an id
---
responses:
200:
description: id queried successfully!
"""
recipe = {}
if request.method == "GET":
id = int(request.args.get('id', 1))
@ -94,14 +57,8 @@ def getRecipe():
return jsonify({'recipe': recipe, 'error': True, 'message': f'method {request.method} not allowed'})
@recipes_api.route('/addRecipe', methods=["POST"])
@login_required
@access_api.login_required
def addRecipe():
""" post a new recipe into the database by passing a recipe_name and recipe description
---
responses:
200:
description: Recipe was added successfully into the system
"""
if request.method == "POST":
recipe_name = request.get_json()['recipe_name']
recipe_description = request.get_json()['recipe_description']
@ -118,14 +75,8 @@ def addRecipe():
return jsonify({'recipe': recipe, 'error': True, 'message': f'method {request.method}'})
@recipes_api.route('/getItems', methods=["GET"])
@login_required
@access_api.login_required
def getItems():
""" Pass along a page, limit, and search strings to get a pagination of items from the system
---
responses:
200:
description: Items were returned successfully!
"""
recordset = []
count = {'count': 0}
if request.method == "GET":
@ -139,17 +90,8 @@ def getItems():
return jsonify({"items":recordset, "end":math.ceil(count/limit), "error":True, "message":"There was an error with this GET statement"})
@recipes_api.route('/postUpdate', methods=["POST"])
@login_required
@access_api.login_required
def postUpdate():
""" This is an endpoint for updating an RecipeTuple in the sites recipes table
---
responses:
200:
description: The time was updated successfully!
Returns:
dict: returns a dictionary containing the updated recipe object, error status, and a message to post for notifications
"""
recipe = {}
if request.method == "POST":
recipe_id = int(request.get_json()['recipe_id'])
@ -160,14 +102,8 @@ def postUpdate():
return jsonify({'recipe': recipe, 'error': True, 'message': 'Update of Recipe unsuccessful!'})
@recipes_api.route('/postCustomItem', methods=["POST"])
@login_required
@access_api.login_required
def postCustomItem():
""" post a recipe item to the database by passing a uuid, recipe_id, item_type, item_name, uom, qty, and link
---
responses:
200:
description: Recipe Item posted successfully!
"""
recipe = {}
if request.method == "POST":
site_name = session['selected_site']
@ -187,14 +123,8 @@ def postCustomItem():
return jsonify({'recipe': recipe, 'error': True, 'message': f'method {request.method} not allowed!'})
@recipes_api.route('/postSKUItem', methods=["POST"])
@login_required
@access_api.login_required
def postSKUItem():
""" post a recipe item to the database by passing a recipe_id and item_id
---
responses:
200:
description: recipe item was posted successfully!
"""
recipe = {}
if request.method == "POST":
recipe_id = int(request.get_json()['recipe_id'])
@ -217,20 +147,8 @@ def postSKUItem():
return jsonify({'recipe': recipe, 'error': True, 'message': f'method {request.method} is not allowed!'})
@recipes_api.route('/postImage/<recipe_id>', methods=["POST"])
@login_required
@access_api.login_required
def uploadImage(recipe_id):
""" post an image for a recipe into the database and files by passing the recipe_id and picture_path
---
parameters:
- name: recipe_id
in: path
required: true
schema:
type: integer
responses:
200:
description: image uploaded succesfully!
"""
file = request.files['file']
file_path = current_app.config['UPLOAD_FOLDER'] + f"/recipes/{file.filename.replace(" ", "_")}"
file.save(file_path)
@ -239,33 +157,15 @@ def uploadImage(recipe_id):
return jsonify({'error': False, 'message': 'Recipe was updated successfully!'})
@recipes_api.route('/getImage/<recipe_id>')
@login_required
@access_api.login_required
def get_image(recipe_id):
""" get the picture path for a recipe by passing teh recipe id in the path
---
parameters:
- name: recipe_id
in: path
required: true
schema:
type: integer
responses:
200:
description: image fetched succesfully!
"""
site_name = session['selected_site']
picture_path = database_recipes.getPicturePath(site_name, (recipe_id,))
return send_from_directory('static/pictures/recipes', picture_path)
@recipes_api.route('/deleteRecipeItem', methods=["POST"])
@login_required
@access_api.login_required
def deleteRecipeItem():
""" delete recipe item from database by passing the recipe item ID
---
responses:
200:
description: recipe item deleted successfully.
"""
recipe = {}
if request.method == "POST":
id = int(request.get_json()['id'])
@ -276,15 +176,8 @@ def deleteRecipeItem():
return jsonify({'recipe': recipe, 'error': True, 'message': f'method {request.method} is not allowed!'})
@recipes_api.route('/saveRecipeItem', methods=["POST"])
@login_required
@access_api.login_required
def saveRecipeItem():
""" post an update to a recipe item in the database by passing the recipe item ID and an update
payload.
---
responses:
200:
description: recipe item updated successfully.
"""
recipe = {}
if request.method == "POST":
id = int(request.get_json()['id'])

View File

@ -6,7 +6,7 @@ import math
# APPLICATION IMPORTS
from application import postsqldb, database_payloads
from user_api import login_required
from application.access_module import access_api
from application.shoppinglists import shoplist_database
shopping_list_api = Blueprint('shopping_list_API', __name__, template_folder="templates", static_folder="static")
@ -14,13 +14,13 @@ shopping_list_api = Blueprint('shopping_list_API', __name__, template_folder="te
# ROOT TEMPLATE ROUTES
@shopping_list_api.route("/")
@login_required
@access_api.login_required
def shopping_lists():
sites = [site[1] for site in postsqldb.get_sites(session['user']['sites'])]
return render_template("lists.html", current_site=session['selected_site'], sites=sites)
@shopping_list_api.route("/<mode>/<id>")
@login_required
@access_api.login_required
def shopping_list(mode, id):
sites = [site[1] for site in postsqldb.get_sites(session['user']['sites'])]
if mode == "view":
@ -29,10 +29,10 @@ def shopping_list(mode, id):
return render_template("edit.html", id=id, current_site=session['selected_site'], sites=sites)
return redirect("/")
# API CALLS
# Added to Database
@shopping_list_api.route('/api/addList', methods=["POST"])
@access_api.login_required
def addList():
if request.method == "POST":
list_name = request.get_json()['list_name']
@ -52,6 +52,7 @@ def addList():
# Added to Database
@shopping_list_api.route('/api/getLists', methods=["GET"])
@access_api.login_required
def getShoppingLists():
lists = []
if request.method == "GET":
@ -86,6 +87,7 @@ def getShoppingLists():
# Added to Database
@shopping_list_api.route('/api/getList', methods=["GET"])
@access_api.login_required
def getShoppingList():
if request.method == "GET":
sl_id = int(request.args.get('id', 1))
@ -95,6 +97,7 @@ def getShoppingList():
# Added to Database
@shopping_list_api.route('/api/getListItem', methods=["GET"])
@access_api.login_required
def getShoppingListItem():
list_item = {}
if request.method == "GET":
@ -106,6 +109,7 @@ def getShoppingListItem():
# Added to database
@shopping_list_api.route('/api/getItems', methods=["GET"])
@access_api.login_required
def getItems():
recordset = []
count = {'count': 0}
@ -123,6 +127,7 @@ def getItems():
# Added to database
@shopping_list_api.route('/api/postListItem', methods=["POST"])
@access_api.login_required
def postListItem():
if request.method == "POST":
data = request.get_json()['data']
@ -143,6 +148,7 @@ def postListItem():
# Added to Database
@shopping_list_api.route('/api/deleteListItem', methods=["POST"])
@access_api.login_required
def deleteListItem():
if request.method == "POST":
sli_id = request.get_json()['sli_id']
@ -153,6 +159,7 @@ def deleteListItem():
# Added to Database
@shopping_list_api.route('/api/saveListItem', methods=["POST"])
@access_api.login_required
def saveListItem():
if request.method == "POST":
sli_id = request.get_json()['sli_id']
@ -164,6 +171,7 @@ def saveListItem():
# Added to Database
@shopping_list_api.route('/api/getSKUItemsFull', methods=["GET"])
@access_api.login_required
def getSKUItemsFull():
items = []
count = {'count': 0}

View File

@ -5,15 +5,15 @@ from flask import (
import math
# APPLICATION IMPORTS
from user_api import login_required
from application import postsqldb, database_payloads
from application.access_module import access_api
from application.site_management import site_management_database
site_management_api = Blueprint('site_management_api', __name__, template_folder="templates", static_folder="static")
# ROOT TEMPLATE ROUTES
@site_management_api.route("/")
@login_required
@access_api.login_required
def site_management_index():
sites = [site[1] for site in postsqldb.get_sites(session['user']['sites'])]
if not session.get('user')['system_admin']:
@ -25,7 +25,7 @@ def site_management_index():
# API CALLS
# added to database
@site_management_api.route('/api/getZones', methods=['GET'])
@login_required
@access_api.login_required
def getZones():
if request.method == "GET":
records = []
@ -40,7 +40,7 @@ def getZones():
# added to database
@site_management_api.route('/api/getLocations', methods=['GET'])
@login_required
@access_api.login_required
def getLocations():
if request.method == "GET":
records = []
@ -55,7 +55,7 @@ def getLocations():
# added to database
@site_management_api.route('/api/getVendors', methods=['GET'])
@login_required
@access_api.login_required
def getVendors():
if request.method == "GET":
records = []
@ -70,7 +70,7 @@ def getVendors():
# added to database
@site_management_api.route('/api/getBrands', methods=['GET'])
@login_required
@access_api.login_required
def getBrands():
if request.method == "GET":
records = []
@ -85,7 +85,7 @@ def getBrands():
# added to database
@site_management_api.route('/api/getPrefixes', methods=['GET'])
@login_required
@access_api.login_required
def getPrefixes():
if request.method == "GET":
records = []
@ -100,6 +100,7 @@ def getPrefixes():
# added to database
@site_management_api.route('/api/postAddZone', methods=["POST"])
@access_api.login_required
def postAddZone():
if request.method == "POST":
site_name = session['selected_site']
@ -110,6 +111,7 @@ def postAddZone():
# added to database
@site_management_api.route('/api/postEditZone', methods=["POST"])
@access_api.login_required
def postEditZone():
if request.method == "POST":
site_name = session['selected_site']
@ -120,6 +122,7 @@ def postEditZone():
# added to database
@site_management_api.route('/api/postAddLocation', methods=["POST"])
@access_api.login_required
def postAddLocation():
if request.method == "POST":
site_name = session['selected_site']
@ -130,6 +133,7 @@ def postAddLocation():
# added to database
@site_management_api.route('/api/postAddVendor', methods=["POST"])
@access_api.login_required
def postAddVendor():
if request.method == "POST":
site_name = session['selected_site']
@ -146,6 +150,7 @@ def postAddVendor():
# added to database
@site_management_api.route('/api/postEditVendor', methods=["POST"])
@access_api.login_required
def postEditVendor():
if request.method == "POST":
site_name = session['selected_site']
@ -156,6 +161,7 @@ def postEditVendor():
# added to database
@site_management_api.route('/api/postAddBrand', methods=["POST"])
@access_api.login_required
def postAddBrand():
if request.method == "POST":
site_name = session['selected_site']
@ -166,6 +172,7 @@ def postAddBrand():
# added to database
@site_management_api.route('/api/postEditBrand', methods=["POST"])
@access_api.login_required
def postEditBrand():
if request.method == "POST":
site_name = session['selected_site']
@ -176,6 +183,7 @@ def postEditBrand():
# added to database
@site_management_api.route('/api/postAddPrefix', methods=["POST"])
@access_api.login_required
def postAddPrefix():
if request.method == "POST":
site_name = session['selected_site']
@ -186,6 +194,7 @@ def postAddPrefix():
# added to database
@site_management_api.route('/api/postEditPrefix', methods=["POST"])
@access_api.login_required
def postEditPrefix():
if request.method == "POST":
site_name = session['selected_site']

4
outh.py Normal file
View File

@ -0,0 +1,4 @@
from authlib.integrations.flask_client import OAuth
# Initialize OAuth instance
oauth = OAuth()

View File

@ -1,10 +1,11 @@
from flask import Flask, render_template, session, request, redirect, jsonify
from flask_assets import Environment, Bundle
import config, user_api, psycopg2, main
from user_api import login_required, update_session_user
from authlib.integrations.flask_client import OAuth
import config, psycopg2, main
import database
from webpush import trigger_push_notifications_for_subscriptions
from application.administration import administration_api
from application.access_module import access_api
from application.site_management import site_management_api
from application.recipes import recipes_api
from application.items import items_API
@ -12,9 +13,10 @@ from application.poe import poe_api
from application.shoppinglists import shoplist_api
from application.receipts import receipts_api
from flasgger import Swagger
from outh import oauth
app = Flask(__name__, instance_relative_config=True)
oauth.init_app(app)
swagger = Swagger(app)
UPLOAD_FOLDER = 'static/pictures'
FILES_FOLDER = 'static/files'
@ -22,10 +24,22 @@ app.config.from_pyfile('application.cfg.py')
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['FILES_FOLDER'] = FILES_FOLDER
oauth.register(
name='authentik',
client_id='gh8rLyXC6hfI7W5mDX26OJFGHxmU0nMzeYl3B04G',
client_secret='aRHyAkDDeU22s69Ig0o7f46Xn3HCnB8guZoMHuA23B7x1e2YL8FhAqZbu1f3naiaLyTLi9ICIiBc6dxOp5eIO4fEI9paL2NwKXmqYCRmzNzWAfwmcsIh2qTlQfAfsh6e',
access_token_url="https://auth.treehousefullofstars.com/application/o/token/",
authorize_url="https://auth.treehousefullofstars.com/application/o/authorize/",
userinfo_endpoint="https://auth.treehousefullofstars.com/application/o/userinfo/",
api_base_url="https://auth.treehousefullofstars.com/application/o/",
jwks_uri="https://auth.treehousefullofstars.com/application/o/pantry/jwks/",
client_kwargs={'scope': 'openid profile email'},
)
assets = Environment(app)
app.secret_key = '11gs22h2h1a4h6ah8e413a45'
app.register_blueprint(user_api.login_app)
app.register_blueprint(access_api.access_api, url_prefix="/access")
app.register_blueprint(administration_api.admin_api, url_prefix='/admin')
app.register_blueprint(items_API.items_api, url_prefix='/items')
app.register_blueprint(poe_api.point_of_ease, url_prefix='/poe')
@ -89,9 +103,9 @@ def subscribe():
return render_template("subscribe.html")
@app.route("/")
@login_required
@access_api.login_required
def home():
update_session_user()
access_api.update_session_user()
sites = [site[1] for site in main.get_sites(session['user']['sites'])]
session['selected_site'] = sites[0]
return redirect("/items")