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 from flask import Blueprint, request, render_template, redirect, session, url_for, jsonify
import hashlib, psycopg2, process, MyDataclasses from authlib.integrations.flask_client import OAuth
from config import config, sites_config, setFirstSetupDone import hashlib, psycopg2
from config import config, sites_config
from functools import wraps from functools import wraps
from manage import create
from main import create_site, getUser, setSystemAdmin
import postsqldb 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(): def update_session_user():
database_config = config() database_config = config()
@ -18,45 +24,44 @@ def login_required(func):
@wraps(func) @wraps(func)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
if 'user' not in session or session['user'] == None: 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 func(*args, **kwargs)
return wrapper return wrapper
@access_api.route('/logout', methods=['GET'])
@login_app.route('/setup', methods=['GET', 'POST']) @login_required
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'])
def logout(): def logout():
if 'user' in session.keys(): if 'user' in session.keys():
session['user'] = None 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(): def login():
session.clear() session.clear()
instance_config = sites_config() instance_config = sites_config()
@ -83,6 +88,7 @@ def login():
if user and user[2] == password: if user and user[2] == password:
session['user_id'] = user[0] 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['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!'}) return jsonify({'error': False, 'message': 'Logged In Sucessfully!'})
else: else:
return jsonify({'error': True, 'message': 'Username or Password was incorrect!'}) return jsonify({'error': True, 'message': 'Username or Password was incorrect!'})
@ -91,9 +97,15 @@ def login():
if 'user' not in session.keys(): if 'user' not in session.keys():
session['user'] = None 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(): def signup():
instance_config = sites_config() instance_config = sites_config()
if not instance_config['signup_enabled']: 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 username = document.getElementById('login_username').value
let password = document.getElementById('login_password').value let password = document.getElementById('login_password').value
const response = await fetch(`/login`, { const response = await fetch(`/access/login`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -97,7 +97,7 @@ async function signupUser() {
let user_email = document.getElementById('signup_email').value let user_email = document.getElementById('signup_email').value
let password = document.getElementById('signup_password').value let password = document.getElementById('signup_password').value
let username = document.getElementById('signup_username').value let username = document.getElementById('signup_username').value
const response = await fetch(`/signup`, { const response = await fetch(`/access/signup`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',

View File

@ -35,6 +35,9 @@
<div class="uk-switcher"> <div class="uk-switcher">
<div class="uk-grid-small" uk-grid> <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-width-1-1">
<div class="uk-margin"> <div class="uk-margin">
<label class="uk-form-label" for="login_username">Username</label> <label class="uk-form-label" for="login_username">Username</label>
@ -98,5 +101,5 @@
</div> </div>
</div> </div>
</body> </body>
<script src="{{ url_for('static', filename='handlers/loginHandler.js') }}"></script> <script src="{{ url_for('access_api.static', filename='js/loginHandler.js') }}"></script>
</html> </html>

View File

@ -1,13 +1,14 @@
# 3RD PARTY IMPORTS # 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 math
import hashlib import hashlib
# APPLICATION IMPORTS # APPLICATION IMPORTS
from application.access_module import access_api
from application.administration import administration_database, administration_processes from application.administration import administration_database, administration_processes
from application import database_payloads, postsqldb from application import database_payloads, postsqldb
from user_api import login_required
admin_api = Blueprint('admin_api', __name__, template_folder="templates", static_folder="static") 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 # ROOT TEMPLATE ROUTES
@admin_api.route('/') @admin_api.route('/')
@access_api.login_required
def admin_index(): def admin_index():
sites = [site[1] for site in postsqldb.get_sites(session['user']['sites'])] 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) return render_template("admin_index.html", current_site=session['selected_site'], sites=sites)
# Added to Database
@admin_api.route('/site/<id>') @admin_api.route('/site/<id>')
@login_required @access_api.login_required
def adminSites(id): def adminSites(id):
if id == "new": if id == "new":
new_site_payload = database_payloads.SitePayload("", "", session['user_id']) new_site_payload = database_payloads.SitePayload("", "", session['user_id'])
@ -30,9 +31,8 @@ def adminSites(id):
site = administration_database.selectSitesTuple((id,)) site = administration_database.selectSitesTuple((id,))
return render_template('site.html', site=site) return render_template('site.html', site=site)
# Added to database
@admin_api.route('/role/<id>') @admin_api.route('/role/<id>')
@login_required @access_api.login_required
def adminRoles(id): def adminRoles(id):
sites = administration_database.selectSitesTuples() sites = administration_database.selectSitesTuples()
if id == "new": if id == "new":
@ -42,9 +42,8 @@ def adminRoles(id):
role = administration_database.selectRolesTuple((id,)) role = administration_database.selectRolesTuple((id,))
return render_template('role.html', role=role, sites=sites) return render_template('role.html', role=role, sites=sites)
# Added to database
@admin_api.route('/user/<id>') @admin_api.route('/user/<id>')
@login_required @access_api.login_required
def adminUser(id): def adminUser(id):
if id == "new": if id == "new":
new_user_payload = database_payloads.LoginsPayload("", "", "", "") new_user_payload = database_payloads.LoginsPayload("", "", "", "")
@ -53,10 +52,32 @@ def adminUser(id):
user = administration_database.selectLoginsTuple((int(id),)) user = administration_database.selectLoginsTuple((int(id),))
return render_template('user.html', user=user) 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 # API ROUTES
# add to database
@admin_api.route('/api/getSites', methods=['GET']) @admin_api.route('/api/getSites', methods=['GET'])
@login_required @access_api.login_required
def getSites(): def getSites():
if request.method == "GET": if request.method == "GET":
records = [] 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':False, 'message': 'Sites Loaded Successfully!'})
return jsonify({'sites': records, "end": math.ceil(count/limit), 'error':True, 'message': 'There was a problem loading Sites!'}) 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']) @admin_api.route('/api/getRoles', methods=['GET'])
@login_required @access_api.login_required
def getRoles(): def getRoles():
if request.method == "GET": if request.method == "GET":
records = [] 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':False, 'message': 'Roles Loaded Successfully!'})
return jsonify({'roles': records, "end": math.ceil(count/limit), 'error':True, 'message': 'There was a problem loading Roles!'}) 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']) @admin_api.route('/api/getLogins', methods=['GET'])
@login_required @access_api.login_required
def getLogins(): def getLogins():
if request.method == "GET": if request.method == "GET":
records = [] 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':False, 'message': 'logins Loaded Successfully!'})
return jsonify({'logins': records, "end": math.ceil(count/limit), 'error':True, 'message': 'There was a problem loading logins!'}) 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"]) @admin_api.route('/api/site/postDeleteSite', methods=["POST"])
@access_api.login_required
def postDeleteSite(): def postDeleteSite():
if request.method == "POST": if request.method == "POST":
site_id = request.get_json()['site_id'] site_id = request.get_json()['site_id']
@ -115,8 +134,8 @@ def postDeleteSite():
return jsonify({'error': False, 'message': f""}) return jsonify({'error': False, 'message': f""})
return jsonify({'error': True, 'message': f""}) return jsonify({'error': True, 'message': f""})
# Added to Database and Processes
@admin_api.route('/api/site/postAddSite', methods=["POST"]) @admin_api.route('/api/site/postAddSite', methods=["POST"])
@access_api.login_required
def postAddSite(): def postAddSite():
if request.method == "POST": if request.method == "POST":
payload = request.get_json()['payload'] 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': 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}."}) 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"]) @admin_api.route('/api/site/postEditSite', methods=["POST"])
@access_api.login_required
def postEditSite(): def postEditSite():
if request.method == "POST": if request.method == "POST":
payload = request.get_json()['payload'] payload = request.get_json()['payload']
@ -140,8 +159,8 @@ def postEditSite():
return jsonify({'error': False, 'message': f"Site updated."}) return jsonify({'error': False, 'message': f"Site updated."})
return jsonify({'error': True, 'message': f"These was an error with updating Site."}) return jsonify({'error': True, 'message': f"These was an error with updating Site."})
# Added to Database
@admin_api.route('/api/role/postAddRole', methods=["POST"]) @admin_api.route('/api/role/postAddRole', methods=["POST"])
@access_api.login_required
def postAddRole(): def postAddRole():
if request.method == "POST": if request.method == "POST":
payload = request.get_json()['payload'] payload = request.get_json()['payload']
@ -155,8 +174,8 @@ def postAddRole():
return jsonify({'error': False, 'message': f"Role added."}) return jsonify({'error': False, 'message': f"Role added."})
return jsonify({'error': True, 'message': f"These was an error with adding this Role."}) 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"]) @admin_api.route('/api/role/postEditRole', methods=["POST"])
@access_api.login_required
def postEditRole(): def postEditRole():
if request.method == "POST": if request.method == "POST":
payload = request.get_json()['payload'] payload = request.get_json()['payload']
@ -164,8 +183,8 @@ def postEditRole():
return jsonify({'error': False, 'message': f"Role updated."}) return jsonify({'error': False, 'message': f"Role updated."})
return jsonify({'error': True, 'message': f"These was an error with updating this Role."}) 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"]) @admin_api.route('/api/user/postAddLogin', methods=["POST"])
@access_api.login_required
def postAddLogin(): def postAddLogin():
if request.method == "POST": if request.method == "POST":
payload = request.get_json()['payload'] 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': False, 'message': f"User added."})
return jsonify({'user': user, 'error': True, 'message': f"These was an error with adding this User."}) 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"]) @admin_api.route('/api/user/postEditLogin', methods=["POST"])
@access_api.login_required
def postEditLogin(): def postEditLogin():
if request.method == "POST": if request.method == "POST":
payload = request.get_json()['payload'] payload = request.get_json()['payload']
@ -189,8 +208,8 @@ def postEditLogin():
return jsonify({'error': False, 'message': f"User was Added Successfully."}) return jsonify({'error': False, 'message': f"User was Added Successfully."})
return jsonify({'error': True, 'message': f"These was an error with adding this user."}) 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"]) @admin_api.route('/api/user/postEditLoginPassword', methods=["POST"])
@access_api.login_required
def postEditLoginPassword(): def postEditLoginPassword():
if request.method == "POST": if request.method == "POST":
payload = request.get_json()['payload'] payload = request.get_json()['payload']

View File

@ -1,19 +1,14 @@
from application import postsqldb # 3RD PARTY IMPORTS
import config
import psycopg2 import psycopg2
import datetime import datetime
# APPLICATION IMPORTS
from application import postsqldb
import config
def getTransactions(site:str, payload: tuple, convert:bool=True): 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 """ payload (tuple): (logistics_id, limit, offset) """
Args:
site (str): _description_
payload (tuple): (logistics_id, limit, offset)
convert (bool, optional): _description_. Defaults to True.
Returns:
_type_: _description_
"""
database_config = config.config() database_config = config.config()
sql = f"SELECT * FROM {site}_transactions WHERE logistics_info_id=%s LIMIT %s OFFSET %s;" 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;" 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) raise postsqldb.DatabaseError(error, payload, sql)
def getItemInfoTuple(site:str, payload:tuple, convert=True): def getItemInfoTuple(site:str, payload:tuple, convert=True):
"""_summary_ """ payload (_type_): (item_info_id,) """
selected = ()
Args: database_config = config.config()
conn (_type_): _description_ sql = f"SELECT * FROM {site}_item_info WHERE id=%s;"
site (_type_): _description_ try:
payload (_type_): (item_info_id,) with psycopg2.connect(**database_config) as conn:
convert (bool, optional): _description_. Defaults to True. with conn.cursor() as cur:
cur.execute(sql, payload)
Raises: rows = cur.fetchone()
DatabaseError: _description_ if rows and convert:
selected = postsqldb.tupleDictionaryFactory(cur.description, rows)
Returns: elif rows and not convert:
_type_: _description_ selected = rows
""" return selected
selected = () except Exception as error:
database_config = config.config() raise postsqldb.DatabaseError(error, payload, sql)
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): def selectItemLocationsTuple(site_name, payload, convert=True):
"""select a single tuple from ItemLocations table for site_name """ payload (tuple): [item_id, location_id] """
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
"""
item_locations = () item_locations = ()
database_config = config.config() database_config = config.config()
select_item_location_sql = f"SELECT * FROM {site_name}_item_locations WHERE part_id = %s AND location_id = %s;" 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 return error
def selectCostLayersTuple(site_name, payload, convert=True): def selectCostLayersTuple(site_name, payload, convert=True):
"""select a single or series of cost layers from the database for site_name """ payload (tuple): (item_locations_id, ) """
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
"""
cost_layers = () cost_layers = ()
database_config = config.config() 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;" 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 return error
def selectSiteTuple(payload, convert=True): def selectSiteTuple(payload, convert=True):
"""Select a single Site from sites using site_name """ payload (tuple): (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
"""
site = () site = ()
database_config = config.config() database_config = config.config()
select_site_sql = f"SELECT * FROM sites WHERE site_name = %s;" 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) raise postsqldb.DatabaseError(error, payload, sql)
def paginateLocationsWithZone(site:str, payload:tuple, convert:bool=True): def paginateLocationsWithZone(site:str, payload:tuple, convert:bool=True):
recordset, count = (), 0 recordset, count = (), 0
database_config = config.config() database_config = config.config()
with open(f"application/items/sql/getLocationsWithZone.sql", "r+") as file: with open(f"application/items/sql/getLocationsWithZone.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site) sql = file.read().replace("%%site_name%%", site)
try: try:
with psycopg2.connect(**database_config) as conn: with psycopg2.connect(**database_config) as conn:
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute(sql, payload) cur.execute(sql, payload)
rows = cur.fetchall() rows = cur.fetchall()
if rows and convert: if rows and convert:
recordset = [postsqldb.tupleDictionaryFactory(cur.description, row) for row in rows] recordset = [postsqldb.tupleDictionaryFactory(cur.description, row) for row in rows]
elif rows and not convert: elif rows and not convert:
recordset = rows recordset = rows
cur.execute(f"SELECT COUNT(*) FROM {site}_locations;") cur.execute(f"SELECT COUNT(*) FROM {site}_locations;")
count = cur.fetchone()[0] count = cur.fetchone()[0]
return recordset, count return recordset, count
except Exception as error: except Exception as error:
raise postsqldb.DatabaseError(error, (), sql) raise postsqldb.DatabaseError(error, (), sql)
def paginateLocationsBySkuZone(site: str, payload: tuple, convert=True): def paginateLocationsBySkuZone(site: str, payload: tuple, convert=True):
database_config = config.config() database_config = config.config()
@ -471,20 +421,7 @@ def insertCostLayersTuple(site, payload, convert=True, conn=None):
raise postsqldb.DatabaseError(error, payload, sql) raise postsqldb.DatabaseError(error, payload, sql)
def insertItemLocationsTuple(site, payload, convert=True, conn=None): def insertItemLocationsTuple(site, payload, convert=True, conn=None):
"""insert payload into item_locations table for site """ payload (tuple): (part_id[int], location_id[int], quantity_on_hand[float], cost_layers[lst2pgarr]) """
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
"""
location = () location = ()
self_conn = False self_conn = False
database_config = config.config() database_config = config.config()
@ -514,21 +451,8 @@ def insertItemLocationsTuple(site, payload, convert=True, conn=None):
raise postsqldb.DatabaseError(error, payload, sql) raise postsqldb.DatabaseError(error, payload, sql)
def insertLogisticsInfoTuple(site, payload, convert=True, conn=None): def insertLogisticsInfoTuple(site, payload, convert=True, conn=None):
"""insert payload into logistics_info table for site """ payload (tuple): (barcode[str], primary_location[str], auto_issue_location[str], dynamic_locations[jsonb],
location_data[jsonb], quantity_on_hand[float]) """
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
"""
logistics_info = () logistics_info = ()
self_conn = False self_conn = False
@ -559,21 +483,8 @@ def insertLogisticsInfoTuple(site, payload, convert=True, conn=None):
raise postsqldb.DatabaseError(error, payload, sql) raise postsqldb.DatabaseError(error, payload, sql)
def insertItemInfoTuple(site, payload, convert=True, conn=None): def insertItemInfoTuple(site, payload, convert=True, conn=None):
"""inserts payload into the item_info table of site """ 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]) """
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
"""
item_info = () item_info = ()
self_conn = False self_conn = False
with open(f"application/items/sql/insertItemInfoTuple.sql", "r+") as file: 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) raise postsqldb.DatabaseError(error, payload, sql)
def insertFoodInfoTuple(site, payload, convert=True, conn=None): def insertFoodInfoTuple(site, payload, convert=True, conn=None):
"""insert payload into food_info table for site """ payload (_type_): (ingrediants[lst2pgarr], food_groups[lst2pgarr], nutrients[jsonstr], expires[bool]) """
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
"""
food_info = () food_info = ()
self_conn = False self_conn = False
with open(f"application/items/sql/insertFoodInfoTuple.sql", "r+") as file: 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) raise postsqldb.DatabaseError(error, payload, sql)
def insertItemTuple(site, payload, convert=True, conn=None): def insertItemTuple(site, payload, convert=True, conn=None):
"""insert payload into items table for site """ payload (tuple): (barcode[str], item_name[str], brand[int], description[str],
Args:
conn (_T_connector@connect): Postgresql Connector
site (str):
payload (tuple): (barcode[str], item_name[str], brand[int], description[str],
tags[lst2pgarr], links[jsonb], item_info_id[int], logistics_info_id[int], 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]) 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
"""
item = () item = ()
self_conn = False self_conn = False
with open(f"application/items/sql/insertItemTuple.sql", "r+") as file: 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) raise postsqldb.DatabaseError(error, payload, sql)
def insertSKUPrefixtuple(site:str, payload:tuple, convert=True, conn=None): 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: with conn.cursor() as cur:
conn (_T_connector@connect): Postgresql Connector cur.execute(sql, payload)
site (str): rows = cur.fetchone()
payload (tuple): (name[str],) if rows and convert:
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False. prefix = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
prefix = rows
Raises: if self_conn:
DatabaseError: conn.commit()
conn.close()
Returns: return prefix
tuple or dict: inserted tuple except Exception as error:
""" raise postsqldb.DatabaseError(error, payload, sql)
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)
def insertConversionTuple(site: str, payload: list, convert=True, conn=None): 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: with conn.cursor() as cur:
conn (_T_connector@connect): Postgresql Connector cur.execute(sql, payload)
site (stre): rows = cur.fetchone()
payload (tuple): (item_id, uom_id, conversion_factor) if rows and convert:
convert (bool, optional): Determines if to return tuple as a dictionary. Defaults to False. record = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
record = rows
Raises: if self_conn:
DatabaseError: conn.commit()
conn.close()
Returns: return record
tuple or dict: inserted tuple
""" except Exception as error:
record = () raise postsqldb.DatabaseError(error, payload, sql)
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)
def postDeleteCostLayer(site_name, payload, convert=True, conn=None): def postDeleteCostLayer(site_name, payload, convert=True, conn=None):
""" """ payload (tuple): (table_to_delete_from, tuple_id) """
payload (tuple): (table_to_delete_from, tuple_id)
Raises:
DatabaseError:
Returns:
tuple or dict: deleted tuple
"""
deleted = () deleted = ()
self_conn = False 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;" 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) raise postsqldb.DatabaseError(error, payload, sql)
def deleteConversionTuple(site_name: str, payload: tuple, convert=True, conn=None): 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 """ payload (tuple): (tuple_id,...) """
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
"""
deleted = () deleted = ()
self_conn = False 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;" 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) raise postsqldb.DatabaseError(error, payload, sql)
def updateConversionTuple(site:str, payload: dict, convert=True, conn=None): 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: with conn.cursor() as cur:
conn (_T_connector@connect): Postgresql Connector cur.execute(sql, values)
site (str): rows = cur.fetchone()
table (str): if rows and convert:
payload (dict): {'id': row_id, 'update': {... column_to_update: value_to_update_to...}} updated = postsqldb.tupleDictionaryFactory(cur.description, rows)
convert (bool, optional): determines if to return tuple as dictionary. Defaults to False. elif rows and not convert:
updated = rows
if self_conn:
conn.commit()
conn.close()
Raises: return updated
DatabaseError: except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql)
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)
def updateItemInfoTuple(site:str, payload: dict, convert=True, conn=None): 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: with conn.cursor() as cur:
conn (_T_connector@connect): Postgresql Connector cur.execute(sql, values)
site (str): rows = cur.fetchone()
table (str): if rows and convert:
payload (dict): {'id': row_id, 'update': {... column_to_update: value_to_update_to...}} updated = postsqldb.tupleDictionaryFactory(cur.description, rows)
convert (bool, optional): determines if to return tuple as dictionary. Defaults to False. elif rows and not convert:
updated = rows
if self_conn:
conn.commit()
conn.close()
Raises: return updated
DatabaseError:
Returns: except Exception as error:
tuple or dict: updated tuple raise postsqldb.DatabaseError(error, payload, sql)
"""
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)
def postUpdateItemLocation(site: str, payload: tuple, conn=None): 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 # TODO: This should be in the item's process module
def postUpdateItem(site:str, payload:dict): def postUpdateItem(site:str, payload:dict):
""" POST and update to an item """ payload (dict): STRICT FORMAT
Args:
site (str): name of the site the item exists in.
payload (dict): STRICT FORMAT
{id: item_id, data: SEE BELOW, user_id: updater} {id: item_id, data: SEE BELOW, user_id: updater}
data is complex structure data is complex structure
@ -1060,14 +866,10 @@ def postUpdateItem(site:str, payload:dict):
postAddTransaction(conn, site, trans.payload()) postAddTransaction(conn, site, trans.payload())
except Exception as error: except Exception as error:
raise postsqldb.DatabaseError(error, payload, "MULTICALL!") raise postsqldb.DatabaseError(error, payload, "MULTICALL!")
def postUpdateItemLink(site: str, payload: dict):
""" POST update to ItemLink
Args: # TODO: This should be in the item's process module
site (str): _description_ def postUpdateItemLink(site: str, payload: dict):
payload (dict): {id, update, old_conv_factor, user_id} """ payload (dict): {id, update, old_conv_factor, user_id} """
"""
def postUpdateData(conn, table, payload, convert=True): def postUpdateData(conn, table, payload, convert=True):
updated = () updated = ()
set_clause, values = postsqldb.updateStringFactory(payload['update']) set_clause, values = postsqldb.updateStringFactory(payload['update'])
@ -1123,21 +925,7 @@ def postUpdateItemLink(site: str, payload: dict):
postAddTransaction(conn, site, transaction.payload()) postAddTransaction(conn, site, transaction.payload())
def postUpdateCostLayer(site, payload, convert=True, conn=None): def postUpdateCostLayer(site, payload, convert=True, conn=None):
"""_summary_ """ payload (dict): {'id': row_id, 'update': {... column_to_update: value_to_update_to...}} """
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 = () updated = ()
self_conn = False self_conn = False
@ -1168,47 +956,34 @@ def postUpdateCostLayer(site, payload, convert=True, conn=None):
raise postsqldb.DatabaseError(error, payload, sql) raise postsqldb.DatabaseError(error, payload, sql)
def postAddTransaction(site, payload, convert=False, conn=None): def postAddTransaction(site, payload, convert=False, conn=None):
transaction = () transaction = ()
self_conn = False self_conn = False
with open(f"application/items/sql/insertTransactionsTuple.sql", "r+") as file: with open(f"application/items/sql/insertTransactionsTuple.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site) sql = file.read().replace("%%site_name%%", site)
try: try:
if not conn: if not conn:
database_config = config.config() database_config = config.config()
conn = psycopg2.connect(**database_config) conn = psycopg2.connect(**database_config)
conn.autocommit = False conn.autocommit = False
self_conn = True self_conn = True
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute(sql, payload) cur.execute(sql, payload)
rows = cur.fetchone() rows = cur.fetchone()
if rows and convert: if rows and convert:
transaction = postsqldb.tupleDictionaryFactory(cur.description, rows) transaction = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert: elif rows and not convert:
transaction = rows transaction = rows
if self_conn: if self_conn:
conn.commit() conn.commit()
conn.close() conn.close()
return transaction return transaction
except Exception as error: except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql) raise postsqldb.DatabaseError(error, payload, sql)
def postInsertItemLink(site, payload, convert=True, conn=None): def postInsertItemLink(site, payload, convert=True, conn=None):
"""insert payload into itemlinks table of site """ payload (tuple): (barcode[str], link[int], data[jsonb], conv_factor[float]) """
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
"""
link = () link = ()
self_conn = False self_conn = False
@ -1238,21 +1013,7 @@ def postInsertItemLink(site, payload, convert=True, conn=None):
raise postsqldb.DatabaseError(error, payload, sql) raise postsqldb.DatabaseError(error, payload, sql)
def postUpdateItemByID(site, payload, convert=True, conn=None): def postUpdateItemByID(site, payload, convert=True, conn=None):
""" high level update of an item specific data, none of its relationships """ payload (dict): {'id': row_id, 'update': {... column_to_update: value_to_update_to...}} """
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 = () updated = ()
self_conn = False self_conn = False
set_clause, values = postsqldb.updateStringFactory(payload['update']) set_clause, values = postsqldb.updateStringFactory(payload['update'])

View File

@ -7,7 +7,7 @@ import math
# APPLICATION IMPORTS # APPLICATION IMPORTS
from config import config from config import config
from user_api import login_required from application.access_module import access_api
import application.postsqldb as db import application.postsqldb as db
from application.items import database_items from application.items import database_items
from application.items import items_processes 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") items_api = Blueprint('items_api', __name__, template_folder="templates", static_folder="static")
def update_session_user(): def update_session_user():
database_config = config() database_config = config()
with psycopg2.connect(**database_config) as conn: with psycopg2.connect(**database_config) as conn:
user = db.LoginsTable.get_washed_tuple(conn, (session['user_id'],)) user = db.LoginsTable.get_washed_tuple(conn, (session['user_id'],))
session['user'] = user session['user'] = user
# ROOT TEMPLATE ROUTES
@items_api.route("/") @items_api.route("/")
@login_required @access_api.login_required
def items(): def items():
update_session_user() update_session_user()
sites = [site[1] for site in db.get_sites(session['user']['sites'])] sites = [site[1] for site in db.get_sites(session['user']['sites'])]
@ -32,7 +32,7 @@ def items():
sites=sites) sites=sites)
@items_api.route("/<id>") @items_api.route("/<id>")
@login_required @access_api.login_required
def item(id): def item(id):
sites = [site[1] for site in db.get_sites(session['user']['sites'])] sites = [site[1] for site in db.get_sites(session['user']['sites'])]
database_config = config() 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) return render_template("item_new.html", id=id, units=units, current_site=session['selected_site'], sites=sites)
@items_api.route("/transaction") @items_api.route("/transaction")
@login_required @access_api.login_required
def transaction(): def transaction():
sites = [site[1] for site in db.get_sites(session['user']['sites'])] sites = [site[1] for site in db.get_sites(session['user']['sites'])]
database_config = config() database_config = config()
with psycopg2.connect(**database_config) as conn: with psycopg2.connect(**database_config) as conn:
units = db.UnitsTable.getAll(conn) units = db.UnitsTable.getAll(conn)
return render_template("transaction.html", units=units, current_site=session['selected_site'], sites=sites, proto={'referrer': request.referrer}) return render_template("transaction.html", units=units, current_site=session['selected_site'], sites=sites, proto={'referrer': request.referrer})
@items_api.route("/transactions/<id>") @items_api.route("/transactions/<id>")
@login_required @access_api.login_required
def transactions(id): def transactions(id):
"""This is the main endpoint to reach the webpage for an items transaction history 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)
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)
@items_api.route("/<parent_id>/itemLink/<id>") @items_api.route("/<parent_id>/itemLink/<id>")
@login_required @access_api.login_required
def itemLink(parent_id, id): def itemLink(parent_id, id):
sites = [site[1] for site in db.get_sites(session['user']['sites'])] 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) 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"]) @items_api.route("/getTransactions", methods=["GET"])
@login_required @access_api.login_required
def getTransactions(): 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": if request.method == "GET":
recordset = [] recordset = []
count = 0 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."}) 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"]) @items_api.route("/getTransaction", methods=["GET"])
@login_required @access_api.login_required
def getTransaction(): 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 = () transaction = ()
if request.method == "GET": if request.method == "GET":
id = int(request.args.get('id', 1)) 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."}) return jsonify({"transaction": transaction, "error": True, "message": f"method {request.method} is not allowed."})
@items_api.route("/getItem", methods=["GET"]) @items_api.route("/getItem", methods=["GET"])
@login_required @access_api.login_required
def get_item(): 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": if request.method == "GET":
id = int(request.args.get('id', 1)) id = int(request.args.get('id', 1))
site_name = session['selected_site'] 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.'}) return jsonify({'item': item, 'error': True, 'message': f'method {request.method} not allowed.'})
@items_api.route("/getItemsWithQOH", methods=['GET']) @items_api.route("/getItemsWithQOH", methods=['GET'])
@login_required @access_api.login_required
def pagninate_items(): 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 = [] items = []
count = 0 count = 0
if request.method == "GET": 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!'}) 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"]) @items_api.route('/getModalItems', methods=["GET"])
@login_required @access_api.login_required
def getModalItems(): 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 recordset, count = tuple(), 0
if request.method == "GET": if request.method == "GET":
page = int(request.args.get('page', 1)) 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."}) 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"]) @items_api.route('/getPrefixes', methods=["GET"])
@login_required @access_api.login_required
def getModalPrefixes(): 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 = [] recordset = []
count = 0 count = 0
if request.method == "GET": 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!"}) 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"]) @items_api.route('/getZonesBySku', methods=["GET"])
@login_required @access_api.login_required
def getZonesbySku(): 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 zones, count = [], 0
if request.method == "GET": if request.method == "GET":
page = int(request.args.get('page', 1)) 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.'}) return jsonify({'zones': zones, 'endpage': math.ceil(count/limit), 'error':False, 'message': f'method {request.method} not allowed.'})
@items_api.route('/getLocationsBySkuZone', methods=['GET']) @items_api.route('/getLocationsBySkuZone', methods=['GET'])
@login_required @access_api.login_required
def getLocationsBySkuZone(): 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 locations, count = [], 0
if request.method == "GET": if request.method == "GET":
zone_id = int(request.args.get('zone_id', 1)) 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.'}) 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']) @items_api.route('/getBrands', methods=['GET'])
@login_required @access_api.login_required
def getBrands(): 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 brands, count = [], 0
if request.method == "GET": if request.method == "GET":
page = int(request.args.get('page', 1)) 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.'}) 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']) @items_api.route('/updateItem', methods=['POST'])
@login_required @access_api.login_required
def updateItem(): 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": if request.method == "POST":
id = request.get_json()['id'] id = request.get_json()['id']
data = request.get_json()['data'] data = request.get_json()['data']
@ -437,41 +206,8 @@ def updateItem():
return jsonify({'error': True, 'message': f'method {request.method} is not allowed!'}) return jsonify({'error': True, 'message': f'method {request.method} is not allowed!'})
@items_api.route('/updateItemLink', methods=['POST']) @items_api.route('/updateItemLink', methods=['POST'])
@login_required @access_api.login_required
def updateItemLink(): 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": if request.method == "POST":
id = request.get_json()['id'] id = request.get_json()['id']
conv_factor = request.get_json()['conv_factor'] conv_factor = request.get_json()['conv_factor']
@ -484,29 +220,8 @@ def updateItemLink():
return jsonify({'error': True, 'message': f"method {request.method} not allowed."}) return jsonify({'error': True, 'message': f"method {request.method} not allowed."})
@items_api.route('/getPossibleLocations', methods=["GET"]) @items_api.route('/getPossibleLocations', methods=["GET"])
@login_required @access_api.login_required
def getPossibleLocations(): 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 locations, count = (), 0
if request.method == "GET": if request.method == "GET":
page = int(request.args.get('page', 1)) 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.'}) return jsonify({'locations': locations, 'end':math.ceil(count/limit), 'error':True, 'message': f'method {request.method} not allowed.'})
@items_api.route('/getLinkedItem', methods=["GET"]) @items_api.route('/getLinkedItem', methods=["GET"])
@login_required @access_api.login_required
def getLinkedItem(): 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 = {} linked_item = {}
if request.method == "GET": if request.method == "GET":
id = int(request.args.get('id', 1)) 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'}) return jsonify({'linked_item': linked_item, 'error': True, 'message': f'method {request.method} not allowed'})
@items_api.route('/addLinkedItem', methods=["POST"]) @items_api.route('/addLinkedItem', methods=["POST"])
@login_required @access_api.login_required
def addLinkedItem(): 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": if request.method == "POST":
parent_id = request.get_json()['parent_id'] parent_id = request.get_json()['parent_id']
child_id = request.get_json()['child_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!'}) return jsonify({'error': True, 'message': 'These was an error with adding to the linked list!'})
@items_api.route('/addBlankItem', methods=["POST"]) @items_api.route('/addBlankItem', methods=["POST"])
@access_api.login_required
def addBlankItem(): 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": if request.method == "POST":
data = { data = {
'barcode': request.get_json()['barcode'], 'barcode': request.get_json()['barcode'],
@ -635,35 +281,8 @@ def addBlankItem():
return jsonify({'error': True, 'message': 'These was an error with adding Item!'}) return jsonify({'error': True, 'message': 'These was an error with adding Item!'})
@items_api.route('/addSKUPrefix', methods=["POST"]) @items_api.route('/addSKUPrefix', methods=["POST"])
@access_api.login_required
def addSKUPrefix(): 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": if request.method == "POST":
site_name = session['selected_site'] site_name = session['selected_site']
prefix = dbPayloads.SKUPrefixPayload( prefix = dbPayloads.SKUPrefixPayload(
@ -676,35 +295,9 @@ def addSKUPrefix():
return jsonify({'error': True, 'message': 'These was an error with adding this Prefix!'}) return jsonify({'error': True, 'message': 'These was an error with adding this Prefix!'})
@items_api.route('/addConversion', methods=['POST']) @items_api.route('/addConversion', methods=['POST'])
@access_api.login_required
def addConversion(): 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": if request.method == "POST":
item_id = request.get_json()['parent_id'] item_id = request.get_json()['parent_id']
uom_id = request.get_json()['uom_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!") return jsonify(error=True, message="Unable to save this conversion, ERROR!")
@items_api.route('/deleteConversion', methods=['POST']) @items_api.route('/deleteConversion', methods=['POST'])
@access_api.login_required
def deleteConversion(): 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": if request.method == "POST":
conversion_id = request.get_json()['conversion_id'] conversion_id = request.get_json()['conversion_id']
site_name = session['selected_site'] site_name = session['selected_site']
@ -744,28 +324,8 @@ def deleteConversion():
return jsonify(error=True, message="Unable to delete this conversion, ERROR!") return jsonify(error=True, message="Unable to delete this conversion, ERROR!")
@items_api.route('/updateConversion', methods=['POST']) @items_api.route('/updateConversion', methods=['POST'])
@access_api.login_required
def updateConversion(): 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": if request.method == "POST":
conversion_id = request.get_json()['conversion_id'] conversion_id = request.get_json()['conversion_id']
update_dictionary = request.get_json()['update'] update_dictionary = request.get_json()['update']
@ -775,28 +335,8 @@ def updateConversion():
return jsonify(error=True, message="Unable to save this conversion, ERROR!") return jsonify(error=True, message="Unable to save this conversion, ERROR!")
@items_api.route('/addPrefix', methods=['POST']) @items_api.route('/addPrefix', methods=['POST'])
@access_api.login_required
def addPrefix(): 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": if request.method == "POST":
item_info_id = request.get_json()['parent_id'] item_info_id = request.get_json()['parent_id']
prefix_id = request.get_json()['prefix_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!") return jsonify(error=True, message="Unable to save this prefix, ERROR!")
@items_api.route('/deletePrefix', methods=['POST']) @items_api.route('/deletePrefix', methods=['POST'])
@access_api.login_required
def deletePrefix(): 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": if request.method == "POST":
item_info_id = request.get_json()['item_info_id'] item_info_id = request.get_json()['item_info_id']
prefix_id = request.get_json()['prefix_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!") return jsonify(error=True, message="Unable to delete this prefix, ERROR!")
@items_api.route('/refreshSearchString', methods=['POST']) @items_api.route('/refreshSearchString', methods=['POST'])
@access_api.login_required
def refreshSearchString(): 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": if request.method == "POST":
item_id = request.get_json()['item_id'] item_id = request.get_json()['item_id']
site_name = session['selected_site'] site_name = session['selected_site']
@ -866,28 +373,8 @@ def refreshSearchString():
return jsonify(error=True, message="Unable to update this search string, ERROR!") return jsonify(error=True, message="Unable to update this search string, ERROR!")
@items_api.route('/postNewItemLocation', methods=['POST']) @items_api.route('/postNewItemLocation', methods=['POST'])
@access_api.login_required
def postNewItemLocation(): 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": if request.method == "POST":
item_id = request.get_json()['item_id'] item_id = request.get_json()['item_id']
location_id = request.get_json()['location_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!") return jsonify(error=True, message="Unable to save this location, ERROR!")
@items_api.route("/getItemLocations", methods=["GET"]) @items_api.route("/getItemLocations", methods=["GET"])
@access_api.login_required
def getItemLocations(): def getItemLocations():
recordset = [] recordset = []
count = 0 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":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"}) 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"]) @items_api.route('/postTransaction', methods=["POST"])
@access_api.login_required
def post_transaction(): def post_transaction():
if request.method == "POST": if request.method == "POST":
result = items_processes.postAdjustment( result = items_processes.postAdjustment(

View File

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

View File

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

View File

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

View File

@ -1,8 +1,8 @@
# 3rd Party imports # 3RD PARTY IMPORTS
import datetime import datetime
import psycopg2 import psycopg2
# applications imports # APPLICATION IMPORTS
from application import postsqldb, database_payloads from application import postsqldb, database_payloads
from application.poe import poe_database from application.poe import poe_database
import config import config
@ -12,9 +12,8 @@ point of ease module. """
def postTransaction(site_name, user_id, data: dict, conn=None): 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',
#dict_keys(['item_id', 'logistics_info_id', 'barcode', 'item_name', 'transaction_type', 'quantity', 'description', 'cost', 'vendor', 'expires', 'location_id'])"""
# 'quantity', 'description', 'cost', 'vendor', 'expires', 'location_id'])
def quantityFactory(quantity_on_hand:float, quantity:float, transaction_type:str): def quantityFactory(quantity_on_hand:float, quantity:float, transaction_type:str):
if transaction_type == "Adjust In": if transaction_type == "Adjust In":
quantity_on_hand += quantity 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!"} return {"error": False, "message":f"Transaction Successful!"}
def post_receipt(site_name, user_id, data: dict, conn=None): 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} # data = {'items': items}
self_conn = False self_conn = False
items = data['items'] items = data['items']

View File

@ -1,5 +1,7 @@
# 3RD PARTY IMPORTS # 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 math
import postsqldb import postsqldb
import mimetypes import mimetypes
@ -7,7 +9,7 @@ import os
# APPLICATION IMPORTS # APPLICATION IMPORTS
import webpush import webpush
from user_api import login_required from application.access_module import access_api
from application import postsqldb, database_payloads from application import postsqldb, database_payloads
from application.receipts import receipts_processes, receipts_database 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 # ROOT TEMPLATE ROUTES
@receipt_api.route("/") @receipt_api.route("/")
@login_required @access_api.login_required
def receipts(): def receipts():
sites = [site[1] for site in postsqldb.get_sites(session['user']['sites'])] 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) return render_template("receipts_index.html", current_site=session['selected_site'], sites=sites)
@receipt_api.route("/<id>") @receipt_api.route("/<id>")
@login_required @access_api.login_required
def receipt(id): def receipt(id):
sites = [site[1] for site in postsqldb.get_sites(session['user']['sites'])] sites = [site[1] for site in postsqldb.get_sites(session['user']['sites'])]
units = postsqldb.get_units_of_measure() units = postsqldb.get_units_of_measure()
@ -31,8 +33,8 @@ def receipt(id):
# API ROUTES # API ROUTES
# Added to Database
@receipt_api.route('/api/getItems', methods=["GET"]) @receipt_api.route('/api/getItems', methods=["GET"])
@access_api.login_required
def getItems(): def getItems():
recordset = [] recordset = []
count = {'count': 0} 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":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"}) 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"]) @receipt_api.route('/api/getVendors', methods=["GET"])
@access_api.login_required
def getVendors(): def getVendors():
recordset = [] recordset = []
count = 0 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":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"}) 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"]) @receipt_api.route('/api/getLinkedLists', methods=["GET"])
@access_api.login_required
def getLinkedLists(): def getLinkedLists():
recordset = [] recordset = []
count = 0 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":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"}) 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"]) @receipt_api.route('/api/getReceipts', methods=["GET"])
@access_api.login_required
def getReceipts(): def getReceipts():
recordset = [] recordset = []
if request.method == "GET": 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': False, "message": "Get Receipts Successful!"})
return jsonify({'receipts': recordset, "end": math.ceil(count/limit), 'error': True, "message": "Something went wrong while getting receipts!"}) 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"]) @receipt_api.route('/api/getReceipt', methods=["GET"])
@access_api.login_required
def getReceipt(): def getReceipt():
receipt = [] receipt = []
if request.method == "GET": 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': False, "message": "Get Receipts Successful!"})
return jsonify({'receipt': receipt, 'error': True, "message": "Something went wrong while getting receipts!"}) return jsonify({'receipt': receipt, 'error': True, "message": "Something went wrong while getting receipts!"})
# added to database
@receipt_api.route('/api/addReceipt', methods=["POST", "GET"]) @receipt_api.route('/api/addReceipt', methods=["POST", "GET"])
@access_api.login_required
def addReceipt(): def addReceipt():
if request.method == "GET": if request.method == "GET":
user_id = session['user_id'] user_id = session['user_id']
@ -113,8 +115,8 @@ def addReceipt():
return jsonify({'error': False, "message": "Receipt Added Successful!"}) return jsonify({'error': False, "message": "Receipt Added Successful!"})
return jsonify({'error': True, "message": "Something went wrong while adding receipt!"}) return jsonify({'error': True, "message": "Something went wrong while adding receipt!"})
# Added to Database
@receipt_api.route('/api/addSKULine', methods=["POST"]) @receipt_api.route('/api/addSKULine', methods=["POST"])
@access_api.login_required
def addSKULine(): def addSKULine():
if request.method == "POST": if request.method == "POST":
item_id = int(request.get_json()['item_id']) 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': False, "message": "Line added Succesfully"})
return jsonify({'error': True, "message": "Something went wrong while add SKU line!"}) return jsonify({'error': True, "message": "Something went wrong while add SKU line!"})
# Added to Database
@receipt_api.route('/api/deleteLine', methods=["POST"]) @receipt_api.route('/api/deleteLine', methods=["POST"])
@access_api.login_required
def deleteLine(): def deleteLine():
if request.method == "POST": if request.method == "POST":
line_id = int(request.get_json()['line_id']) 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': False, "message": "Line Deleted Succesfully"})
return jsonify({'error': True, "message": "Something went wrong while deleting line!"}) return jsonify({'error': True, "message": "Something went wrong while deleting line!"})
# Added to Database
@receipt_api.route('/api/denyLine', methods=["POST"]) @receipt_api.route('/api/denyLine', methods=["POST"])
@access_api.login_required
def denyLine(): def denyLine():
if request.method == "POST": if request.method == "POST":
line_id = int(request.get_json()['line_id']) 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': False, "message": "Line Denied Succesfully"})
return jsonify({'error': True, "message": "Something went wrong while denying line!"}) return jsonify({'error': True, "message": "Something went wrong while denying line!"})
# Added to database
@receipt_api.route('/api/saveLine', methods=["POST"]) @receipt_api.route('/api/saveLine', methods=["POST"])
@access_api.login_required
def saveLine(): def saveLine():
if request.method == "POST": if request.method == "POST":
line_id = int(request.get_json()['line_id']) 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': False, "message": "Line Saved Succesfully"})
return jsonify({'error': True, "message": "Something went wrong while saving line!"}) return jsonify({'error': True, "message": "Something went wrong while saving line!"})
# Added to Process and database!
@receipt_api.route('/api/postLinkedItem', methods=["POST"]) @receipt_api.route('/api/postLinkedItem', methods=["POST"])
@access_api.login_required
def postLinkedItem(): def postLinkedItem():
if request.method == "POST": if request.method == "POST":
receipt_item_id = int(request.get_json()['receipt_item_id']) 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': False, "message": "Line Saved Succesfully"})
return jsonify({'error': True, "message": "Something went wrong while saving line!"}) return jsonify({'error': True, "message": "Something went wrong while saving line!"})
# Added to processes and Database
@receipt_api.route('/api/resolveLine', methods=["POST"]) @receipt_api.route('/api/resolveLine', methods=["POST"])
@access_api.login_required
def resolveLine(): def resolveLine():
if request.method == "POST": if request.method == "POST":
line_id = int(request.get_json()['line_id']) 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': False, "message": "Line Saved Succesfully"})
return jsonify({'error': True, "message": "Something went wrong while saving line!"}) return jsonify({'error': True, "message": "Something went wrong while saving line!"})
# add to database
@receipt_api.route('/api/postVendorUpdate', methods=["POST"]) @receipt_api.route('/api/postVendorUpdate', methods=["POST"])
@access_api.login_required
def postVendorUpdate(): def postVendorUpdate():
if request.method == "POST": if request.method == "POST":
receipt_id = int(request.get_json()['receipt_id']) 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': False, "message": "Line Saved Succesfully"})
return jsonify({'error': True, "message": "Something went wrong while saving line!"}) return jsonify({'error': True, "message": "Something went wrong while saving line!"})
# added to database
@receipt_api.route('/api/resolveReceipt', methods=["POST"]) @receipt_api.route('/api/resolveReceipt', methods=["POST"])
@access_api.login_required
def resolveReceipt(): def resolveReceipt():
if request.method == "POST": if request.method == "POST":
receipt_id = int(request.get_json()['receipt_id']) 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': False, "message": "Line Saved Succesfully"})
return jsonify({'error': True, "message": "Something went wrong while saving line!"}) return jsonify({'error': True, "message": "Something went wrong while saving line!"})
# added to database
@receipt_api.route('/api/uploadfile/<receipt_id>', methods=["POST"]) @receipt_api.route('/api/uploadfile/<receipt_id>', methods=["POST"])
@access_api.login_required
def uploadFile(receipt_id): def uploadFile(receipt_id):
file = request.files['file'] file = request.files['file']
file_path = current_app.config['FILES_FOLDER'] + f"/receipts/{file.filename.replace(" ", "_")}" 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}}) receipts_database.updateReceiptsTuple(site_name, {'id': receipt_id, 'update': {'files': receipt_files}})
return jsonify({}) return jsonify({})
# Does not need to be added to Database
@receipt_api.route('/api/getFile/<file_name>') @receipt_api.route('/api/getFile/<file_name>')
@access_api.login_required
def getFile(file_name): def getFile(file_name):
path_ = current_app.config['FILES_FOLDER'] + "/receipts" path_ = current_app.config['FILES_FOLDER'] + "/receipts"
print(path_) print(path_)
return send_from_directory(path_, file_name) return send_from_directory(path_, file_name)
# Added to database
@receipt_api.route('/api/checkAPI', methods=["POST"]) @receipt_api.route('/api/checkAPI', methods=["POST"])
@access_api.login_required
def checkAPI(): def checkAPI():
if request.method == "POST": if request.method == "POST":
line_id = int(request.get_json()['line_id']) line_id = int(request.get_json()['line_id'])

View File

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

View File

@ -1,53 +1,29 @@
# 3rd party imports # 3RD PARTY IMPORTS
from flask import ( from flask import (
Blueprint, request, render_template, session, jsonify, current_app, send_from_directory Blueprint, request, render_template, session, jsonify, current_app, send_from_directory
) )
import math import math
# application imports # APPLICATION IMPORTS
import main import main
from user_api import login_required
import webpush import webpush
from application.access_module import access_api
from application.recipes import database_recipes from application.recipes import database_recipes
from application import postsqldb as db from application import postsqldb as db
recipes_api = Blueprint('recipes_api', __name__, template_folder="templates", static_folder="static") recipes_api = Blueprint('recipes_api', __name__, template_folder="templates", static_folder="static")
@recipes_api.route("/") @recipes_api.route("/")
@login_required @access_api.login_required
def recipes(): 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'])] sites = [site[1] for site in main.get_sites(session['user']['sites'])]
return render_template("recipes_index.html", return render_template("recipes_index.html",
current_site=session['selected_site'], current_site=session['selected_site'],
sites=sites) sites=sites)
@recipes_api.route("/<mode>/<id>") @recipes_api.route("/<mode>/<id>")
@login_required @access_api.login_required
def recipe(id, mode='view'): 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() units = database_recipes.getUnits()
print("woot") print("woot")
if mode == "edit": 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']) return render_template("recipe_view.html", recipe_id=id, current_site=session['selected_site'])
@recipes_api.route('/getRecipes', methods=["GET"]) @recipes_api.route('/getRecipes', methods=["GET"])
@login_required @access_api.login_required
def getRecipes(): 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 = [] recipes = []
count=0 count=0
if request.method == "GET": 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}'}) 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"]) @recipes_api.route('/getRecipe', methods=["GET"])
@login_required @access_api.login_required
def getRecipe(): def getRecipe():
""" Get a query for recipe id from database by passing an id
---
responses:
200:
description: id queried successfully!
"""
recipe = {} recipe = {}
if request.method == "GET": if request.method == "GET":
id = int(request.args.get('id', 1)) 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'}) return jsonify({'recipe': recipe, 'error': True, 'message': f'method {request.method} not allowed'})
@recipes_api.route('/addRecipe', methods=["POST"]) @recipes_api.route('/addRecipe', methods=["POST"])
@login_required @access_api.login_required
def addRecipe(): 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": if request.method == "POST":
recipe_name = request.get_json()['recipe_name'] recipe_name = request.get_json()['recipe_name']
recipe_description = request.get_json()['recipe_description'] recipe_description = request.get_json()['recipe_description']
@ -118,14 +75,8 @@ def addRecipe():
return jsonify({'recipe': recipe, 'error': True, 'message': f'method {request.method}'}) return jsonify({'recipe': recipe, 'error': True, 'message': f'method {request.method}'})
@recipes_api.route('/getItems', methods=["GET"]) @recipes_api.route('/getItems', methods=["GET"])
@login_required @access_api.login_required
def getItems(): 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 = [] recordset = []
count = {'count': 0} count = {'count': 0}
if request.method == "GET": 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"}) 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"]) @recipes_api.route('/postUpdate', methods=["POST"])
@login_required @access_api.login_required
def postUpdate(): 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 = {} recipe = {}
if request.method == "POST": if request.method == "POST":
recipe_id = int(request.get_json()['recipe_id']) 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!'}) return jsonify({'recipe': recipe, 'error': True, 'message': 'Update of Recipe unsuccessful!'})
@recipes_api.route('/postCustomItem', methods=["POST"]) @recipes_api.route('/postCustomItem', methods=["POST"])
@login_required @access_api.login_required
def postCustomItem(): 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 = {} recipe = {}
if request.method == "POST": if request.method == "POST":
site_name = session['selected_site'] site_name = session['selected_site']
@ -187,14 +123,8 @@ def postCustomItem():
return jsonify({'recipe': recipe, 'error': True, 'message': f'method {request.method} not allowed!'}) return jsonify({'recipe': recipe, 'error': True, 'message': f'method {request.method} not allowed!'})
@recipes_api.route('/postSKUItem', methods=["POST"]) @recipes_api.route('/postSKUItem', methods=["POST"])
@login_required @access_api.login_required
def postSKUItem(): 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 = {} recipe = {}
if request.method == "POST": if request.method == "POST":
recipe_id = int(request.get_json()['recipe_id']) 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!'}) return jsonify({'recipe': recipe, 'error': True, 'message': f'method {request.method} is not allowed!'})
@recipes_api.route('/postImage/<recipe_id>', methods=["POST"]) @recipes_api.route('/postImage/<recipe_id>', methods=["POST"])
@login_required @access_api.login_required
def uploadImage(recipe_id): 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 = request.files['file']
file_path = current_app.config['UPLOAD_FOLDER'] + f"/recipes/{file.filename.replace(" ", "_")}" file_path = current_app.config['UPLOAD_FOLDER'] + f"/recipes/{file.filename.replace(" ", "_")}"
file.save(file_path) file.save(file_path)
@ -239,33 +157,15 @@ def uploadImage(recipe_id):
return jsonify({'error': False, 'message': 'Recipe was updated successfully!'}) return jsonify({'error': False, 'message': 'Recipe was updated successfully!'})
@recipes_api.route('/getImage/<recipe_id>') @recipes_api.route('/getImage/<recipe_id>')
@login_required @access_api.login_required
def get_image(recipe_id): 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'] site_name = session['selected_site']
picture_path = database_recipes.getPicturePath(site_name, (recipe_id,)) picture_path = database_recipes.getPicturePath(site_name, (recipe_id,))
return send_from_directory('static/pictures/recipes', picture_path) return send_from_directory('static/pictures/recipes', picture_path)
@recipes_api.route('/deleteRecipeItem', methods=["POST"]) @recipes_api.route('/deleteRecipeItem', methods=["POST"])
@login_required @access_api.login_required
def deleteRecipeItem(): def deleteRecipeItem():
""" delete recipe item from database by passing the recipe item ID
---
responses:
200:
description: recipe item deleted successfully.
"""
recipe = {} recipe = {}
if request.method == "POST": if request.method == "POST":
id = int(request.get_json()['id']) 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!'}) return jsonify({'recipe': recipe, 'error': True, 'message': f'method {request.method} is not allowed!'})
@recipes_api.route('/saveRecipeItem', methods=["POST"]) @recipes_api.route('/saveRecipeItem', methods=["POST"])
@login_required @access_api.login_required
def saveRecipeItem(): 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 = {} recipe = {}
if request.method == "POST": if request.method == "POST":
id = int(request.get_json()['id']) id = int(request.get_json()['id'])

View File

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

View File

@ -5,15 +5,15 @@ from flask import (
import math import math
# APPLICATION IMPORTS # APPLICATION IMPORTS
from user_api import login_required
from application import postsqldb, database_payloads from application import postsqldb, database_payloads
from application.access_module import access_api
from application.site_management import site_management_database from application.site_management import site_management_database
site_management_api = Blueprint('site_management_api', __name__, template_folder="templates", static_folder="static") site_management_api = Blueprint('site_management_api', __name__, template_folder="templates", static_folder="static")
# ROOT TEMPLATE ROUTES # ROOT TEMPLATE ROUTES
@site_management_api.route("/") @site_management_api.route("/")
@login_required @access_api.login_required
def site_management_index(): def site_management_index():
sites = [site[1] for site in postsqldb.get_sites(session['user']['sites'])] sites = [site[1] for site in postsqldb.get_sites(session['user']['sites'])]
if not session.get('user')['system_admin']: if not session.get('user')['system_admin']:
@ -25,7 +25,7 @@ def site_management_index():
# API CALLS # API CALLS
# added to database # added to database
@site_management_api.route('/api/getZones', methods=['GET']) @site_management_api.route('/api/getZones', methods=['GET'])
@login_required @access_api.login_required
def getZones(): def getZones():
if request.method == "GET": if request.method == "GET":
records = [] records = []
@ -40,7 +40,7 @@ def getZones():
# added to database # added to database
@site_management_api.route('/api/getLocations', methods=['GET']) @site_management_api.route('/api/getLocations', methods=['GET'])
@login_required @access_api.login_required
def getLocations(): def getLocations():
if request.method == "GET": if request.method == "GET":
records = [] records = []
@ -55,7 +55,7 @@ def getLocations():
# added to database # added to database
@site_management_api.route('/api/getVendors', methods=['GET']) @site_management_api.route('/api/getVendors', methods=['GET'])
@login_required @access_api.login_required
def getVendors(): def getVendors():
if request.method == "GET": if request.method == "GET":
records = [] records = []
@ -70,7 +70,7 @@ def getVendors():
# added to database # added to database
@site_management_api.route('/api/getBrands', methods=['GET']) @site_management_api.route('/api/getBrands', methods=['GET'])
@login_required @access_api.login_required
def getBrands(): def getBrands():
if request.method == "GET": if request.method == "GET":
records = [] records = []
@ -85,7 +85,7 @@ def getBrands():
# added to database # added to database
@site_management_api.route('/api/getPrefixes', methods=['GET']) @site_management_api.route('/api/getPrefixes', methods=['GET'])
@login_required @access_api.login_required
def getPrefixes(): def getPrefixes():
if request.method == "GET": if request.method == "GET":
records = [] records = []
@ -100,6 +100,7 @@ def getPrefixes():
# added to database # added to database
@site_management_api.route('/api/postAddZone', methods=["POST"]) @site_management_api.route('/api/postAddZone', methods=["POST"])
@access_api.login_required
def postAddZone(): def postAddZone():
if request.method == "POST": if request.method == "POST":
site_name = session['selected_site'] site_name = session['selected_site']
@ -110,6 +111,7 @@ def postAddZone():
# added to database # added to database
@site_management_api.route('/api/postEditZone', methods=["POST"]) @site_management_api.route('/api/postEditZone', methods=["POST"])
@access_api.login_required
def postEditZone(): def postEditZone():
if request.method == "POST": if request.method == "POST":
site_name = session['selected_site'] site_name = session['selected_site']
@ -120,6 +122,7 @@ def postEditZone():
# added to database # added to database
@site_management_api.route('/api/postAddLocation', methods=["POST"]) @site_management_api.route('/api/postAddLocation', methods=["POST"])
@access_api.login_required
def postAddLocation(): def postAddLocation():
if request.method == "POST": if request.method == "POST":
site_name = session['selected_site'] site_name = session['selected_site']
@ -130,6 +133,7 @@ def postAddLocation():
# added to database # added to database
@site_management_api.route('/api/postAddVendor', methods=["POST"]) @site_management_api.route('/api/postAddVendor', methods=["POST"])
@access_api.login_required
def postAddVendor(): def postAddVendor():
if request.method == "POST": if request.method == "POST":
site_name = session['selected_site'] site_name = session['selected_site']
@ -146,6 +150,7 @@ def postAddVendor():
# added to database # added to database
@site_management_api.route('/api/postEditVendor', methods=["POST"]) @site_management_api.route('/api/postEditVendor', methods=["POST"])
@access_api.login_required
def postEditVendor(): def postEditVendor():
if request.method == "POST": if request.method == "POST":
site_name = session['selected_site'] site_name = session['selected_site']
@ -156,6 +161,7 @@ def postEditVendor():
# added to database # added to database
@site_management_api.route('/api/postAddBrand', methods=["POST"]) @site_management_api.route('/api/postAddBrand', methods=["POST"])
@access_api.login_required
def postAddBrand(): def postAddBrand():
if request.method == "POST": if request.method == "POST":
site_name = session['selected_site'] site_name = session['selected_site']
@ -166,6 +172,7 @@ def postAddBrand():
# added to database # added to database
@site_management_api.route('/api/postEditBrand', methods=["POST"]) @site_management_api.route('/api/postEditBrand', methods=["POST"])
@access_api.login_required
def postEditBrand(): def postEditBrand():
if request.method == "POST": if request.method == "POST":
site_name = session['selected_site'] site_name = session['selected_site']
@ -176,6 +183,7 @@ def postEditBrand():
# added to database # added to database
@site_management_api.route('/api/postAddPrefix', methods=["POST"]) @site_management_api.route('/api/postAddPrefix', methods=["POST"])
@access_api.login_required
def postAddPrefix(): def postAddPrefix():
if request.method == "POST": if request.method == "POST":
site_name = session['selected_site'] site_name = session['selected_site']
@ -186,6 +194,7 @@ def postAddPrefix():
# added to database # added to database
@site_management_api.route('/api/postEditPrefix', methods=["POST"]) @site_management_api.route('/api/postEditPrefix', methods=["POST"])
@access_api.login_required
def postEditPrefix(): def postEditPrefix():
if request.method == "POST": if request.method == "POST":
site_name = session['selected_site'] 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 import Flask, render_template, session, request, redirect, jsonify
from flask_assets import Environment, Bundle from flask_assets import Environment, Bundle
import config, user_api, psycopg2, main from authlib.integrations.flask_client import OAuth
from user_api import login_required, update_session_user import config, psycopg2, main
import database import database
from webpush import trigger_push_notifications_for_subscriptions from webpush import trigger_push_notifications_for_subscriptions
from application.administration import administration_api from application.administration import administration_api
from application.access_module import access_api
from application.site_management import site_management_api from application.site_management import site_management_api
from application.recipes import recipes_api from application.recipes import recipes_api
from application.items import items_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.shoppinglists import shoplist_api
from application.receipts import receipts_api from application.receipts import receipts_api
from flasgger import Swagger from flasgger import Swagger
from outh import oauth
app = Flask(__name__, instance_relative_config=True) app = Flask(__name__, instance_relative_config=True)
oauth.init_app(app)
swagger = Swagger(app) swagger = Swagger(app)
UPLOAD_FOLDER = 'static/pictures' UPLOAD_FOLDER = 'static/pictures'
FILES_FOLDER = 'static/files' FILES_FOLDER = 'static/files'
@ -22,10 +24,22 @@ app.config.from_pyfile('application.cfg.py')
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['FILES_FOLDER'] = FILES_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) assets = Environment(app)
app.secret_key = '11gs22h2h1a4h6ah8e413a45' 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(administration_api.admin_api, url_prefix='/admin')
app.register_blueprint(items_API.items_api, url_prefix='/items') app.register_blueprint(items_API.items_api, url_prefix='/items')
app.register_blueprint(poe_api.point_of_ease, url_prefix='/poe') app.register_blueprint(poe_api.point_of_ease, url_prefix='/poe')
@ -89,9 +103,9 @@ def subscribe():
return render_template("subscribe.html") return render_template("subscribe.html")
@app.route("/") @app.route("/")
@login_required @access_api.login_required
def home(): def home():
update_session_user() access_api.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")