430 lines
13 KiB
Python
430 lines
13 KiB
Python
|
|
import json
|
|||
|
|
import uuid
|
|||
|
|
from datetime import datetime
|
|||
|
|
|
|||
|
|
import requests
|
|||
|
|
from flask import Flask, request, send_from_directory
|
|||
|
|
from flask_cors import CORS, cross_origin
|
|||
|
|
|
|||
|
|
from util import (
|
|||
|
|
check_existing_token,
|
|||
|
|
convert_to_mysql_query,
|
|||
|
|
get_category_id,
|
|||
|
|
get_data_from_db,
|
|||
|
|
add_or_update_to_db,
|
|||
|
|
delete_from_db,
|
|||
|
|
get_store_id,
|
|||
|
|
load_conf,
|
|||
|
|
load_db_conn,
|
|||
|
|
delete_receipt,
|
|||
|
|
update_server_config,
|
|||
|
|
convert_pdf_to_png
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
app = Flask(
|
|||
|
|
__name__,
|
|||
|
|
static_url_path="",
|
|||
|
|
static_folder="../webroot",
|
|||
|
|
)
|
|||
|
|
cors = CORS(app, resources={r"/api/upload/*": {"origins": "*"}})
|
|||
|
|
app.config["CORS_HEADERS"] = "Content-Type"
|
|||
|
|
|
|||
|
|
cfg = None
|
|||
|
|
api_token = None
|
|||
|
|
|
|||
|
|
|
|||
|
|
@app.before_first_request
|
|||
|
|
def first():
|
|||
|
|
global cfg
|
|||
|
|
global api_token
|
|||
|
|
cfg = load_conf()
|
|||
|
|
api_token = check_existing_token()
|
|||
|
|
|
|||
|
|
@app.before_request
|
|||
|
|
def before_request():
|
|||
|
|
if request.endpoint in ('static', 'index'):
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
if not request.args:
|
|||
|
|
return "No token provided! Add &token= to URL", 401
|
|||
|
|
|
|||
|
|
if api_token != request.args["token"]:
|
|||
|
|
return "Unauthorized", 401
|
|||
|
|
|
|||
|
|
global cfg
|
|||
|
|
cfg = load_conf()
|
|||
|
|
if ((
|
|||
|
|
not cfg["dbMode"]
|
|||
|
|
or not cfg["sqlDatabase"]
|
|||
|
|
or not cfg["sqlPassword"]
|
|||
|
|
or not cfg["sqlServerIP"]
|
|||
|
|
or not cfg["sqlUsername"]
|
|||
|
|
)
|
|||
|
|
and request.endpoint != 'updateConfig'
|
|||
|
|
and request.endpoint != 'getBackendConfig'
|
|||
|
|
):
|
|||
|
|
return "Settings incomplete!", 512
|
|||
|
|
|
|||
|
|
@app.route("/", methods=["GET"])
|
|||
|
|
def index():
|
|||
|
|
return send_from_directory(app.static_folder, "index.html")
|
|||
|
|
|
|||
|
|
@app.route("/api/getBackendConfig", methods=["GET", "OPTIONS"])
|
|||
|
|
@cross_origin(origin="*", headers=["Content-Type"])
|
|||
|
|
def getBackendConfig():
|
|||
|
|
return json.dumps(cfg)
|
|||
|
|
|
|||
|
|
@app.route("/api/updateConfig", methods=["POST", "OPTIONS"])
|
|||
|
|
@cross_origin(origin="*", headers=["Content-Type"])
|
|||
|
|
def updateConfig():
|
|||
|
|
post_string = json.dumps(request.get_json())
|
|||
|
|
post_json = json.loads(post_string)
|
|||
|
|
|
|||
|
|
update_server_config(post_json)
|
|||
|
|
|
|||
|
|
return "Config updated", 200
|
|||
|
|
|
|||
|
|
@app.route("/api/upload", methods=["POST", "OPTIONS"])
|
|||
|
|
@cross_origin(origin="*", headers=["Content-Type"])
|
|||
|
|
def upload():
|
|||
|
|
file = request.files["file"]
|
|||
|
|
file_name = file.filename
|
|||
|
|
gaussian_blur = True
|
|||
|
|
|
|||
|
|
if file.content_type == "application/pdf":
|
|||
|
|
file = convert_pdf_to_png(file.read())
|
|||
|
|
file_name = file_name.split(".")[0] + ".png"
|
|||
|
|
gaussian_blur = False
|
|||
|
|
|
|||
|
|
if not file:
|
|||
|
|
return "No valid file found", 500
|
|||
|
|
|
|||
|
|
url = (
|
|||
|
|
"http://"
|
|||
|
|
+ str(cfg["parserIP"])
|
|||
|
|
+ ":"
|
|||
|
|
+ str(cfg["parserPort"])
|
|||
|
|
+ "/api/upload?access_token="
|
|||
|
|
+ str(cfg["parserToken"])
|
|||
|
|
+ "&legacy_parser=True"
|
|||
|
|
+ "&grayscale_image=True"
|
|||
|
|
+ "&rotate_image=True"
|
|||
|
|
+ "&gaussian_blur=" + str(gaussian_blur)
|
|||
|
|
+ "&median_blur=True"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
receipt_upload = requests.post(url, files={"file": (file_name, file)})
|
|||
|
|
|
|||
|
|
if receipt_upload.status_code == 200:
|
|||
|
|
upload_response = json.dumps(receipt_upload.content.decode("utf8"))
|
|||
|
|
response_json = json.loads(upload_response)
|
|||
|
|
response_json = json.loads(response_json)
|
|||
|
|
|
|||
|
|
# Replace " in Date
|
|||
|
|
if '"' in response_json["receiptDate"]:
|
|||
|
|
response_json["receiptDate"] = response_json["receiptDate"].replace('"', "")
|
|||
|
|
|
|||
|
|
# Create 4 digit year
|
|||
|
|
if response_json["receiptDate"] != "null":
|
|||
|
|
year_string = response_json["receiptDate"].split(".")
|
|||
|
|
if len(year_string[2]) < 4:
|
|||
|
|
year_string[2] = "20" + year_string[2]
|
|||
|
|
response_json["receiptDate"] = (
|
|||
|
|
year_string[0] + "." + year_string[1] + "." + year_string[2]
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
conn, cursor = load_db_conn()
|
|||
|
|
for idx, article in enumerate(response_json["receiptItems"]):
|
|||
|
|
article = article[0]
|
|||
|
|
|
|||
|
|
splitted_articles = article.split(" ")
|
|||
|
|
|
|||
|
|
for article in splitted_articles:
|
|||
|
|
if len(article) > 3:
|
|||
|
|
|
|||
|
|
if cfg["dbMode"] == "mysql":
|
|||
|
|
sql_query = "SELECT category FROM purchaseData where article_name like %s order by timestamp desc limit 1"
|
|||
|
|
else:
|
|||
|
|
sql_query = "SELECT TOP 1 category FROM purchaseData where article_name like ? order by timestamp desc"
|
|||
|
|
|
|||
|
|
cursor.execute(sql_query, [f"%{article}%"])
|
|||
|
|
row = cursor.fetchone()
|
|||
|
|
|
|||
|
|
if row:
|
|||
|
|
if cfg["dbMode"] == "mysql":
|
|||
|
|
found_cat = row[0]
|
|||
|
|
else:
|
|||
|
|
found_cat = row.category
|
|||
|
|
|
|||
|
|
copy_array = response_json["receiptItems"][idx]
|
|||
|
|
copy_array.insert(2, found_cat)
|
|||
|
|
|
|||
|
|
response_json["receiptItems"][idx] = copy_array
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
conn.close()
|
|||
|
|
return json.dumps(response_json)
|
|||
|
|
|
|||
|
|
else:
|
|||
|
|
return "Error on upload", receipt_upload.status_code
|
|||
|
|
|
|||
|
|
|
|||
|
|
@app.route("/api/getHistory", methods=["GET", "OPTIONS"])
|
|||
|
|
@cross_origin(origin="*", headers=["Content-Type"])
|
|||
|
|
def get_history():
|
|||
|
|
conn, cursor = load_db_conn()
|
|||
|
|
history_json = []
|
|||
|
|
|
|||
|
|
cursor.execute(
|
|||
|
|
"select SUM(total) as totalSum, location, id, timestamp from purchaseData \
|
|||
|
|
where id is not null \
|
|||
|
|
GROUP BY timestamp, id, location \
|
|||
|
|
ORDER BY timestamp desc"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
rows = cursor.fetchall()
|
|||
|
|
|
|||
|
|
for row in rows:
|
|||
|
|
if cfg["dbMode"] == "mysql":
|
|||
|
|
add_json = {
|
|||
|
|
"location": row[1],
|
|||
|
|
"totalSum": str(row[0]),
|
|||
|
|
"timestamp": str(row[3]),
|
|||
|
|
"id": row[2],
|
|||
|
|
}
|
|||
|
|
else:
|
|||
|
|
add_json = {
|
|||
|
|
"location": row.location,
|
|||
|
|
"totalSum": str(row.totalSum),
|
|||
|
|
"timestamp": str(row.timestamp),
|
|||
|
|
"id": row.id,
|
|||
|
|
}
|
|||
|
|
history_json.append(add_json)
|
|||
|
|
|
|||
|
|
conn.close()
|
|||
|
|
|
|||
|
|
return json.dumps(history_json)
|
|||
|
|
|
|||
|
|
|
|||
|
|
@app.route("/api/getHistoryDetails", methods=["GET", "OPTIONS"])
|
|||
|
|
@cross_origin(origin="*", headers=["Content-Type"])
|
|||
|
|
def get_history_details():
|
|||
|
|
purchase_id = request.args["purchaseID"]
|
|||
|
|
|
|||
|
|
conn, cursor = load_db_conn()
|
|||
|
|
sql_query = (
|
|||
|
|
"SELECT storeName, total, date, purchaseId " +
|
|||
|
|
"FROM receipts re " +
|
|||
|
|
"JOIN stores st ON re.storeId = st.id " +
|
|||
|
|
"where re.id = ?"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if cfg["dbMode"] == "mysql":
|
|||
|
|
sql_query = convert_to_mysql_query(sql_query)
|
|||
|
|
|
|||
|
|
cursor.execute(sql_query, [purchase_id])
|
|||
|
|
row = cursor.fetchone()
|
|||
|
|
|
|||
|
|
if row:
|
|||
|
|
if cfg["dbMode"] == "mysql":
|
|||
|
|
store_name = row[0]
|
|||
|
|
receipt_total = row[1]
|
|||
|
|
receipt_date = row[2]
|
|||
|
|
db_purchase_id = row[3]
|
|||
|
|
else:
|
|||
|
|
store_name = row.storeName
|
|||
|
|
receipt_total = row.total
|
|||
|
|
receipt_date = row.date
|
|||
|
|
db_purchase_id = row.purchaseId
|
|||
|
|
|
|||
|
|
receipt_date = receipt_date.strftime("%d.%m.%Y")
|
|||
|
|
|
|||
|
|
purchase_details = {
|
|||
|
|
"storeName": store_name,
|
|||
|
|
"receiptTotal": str(receipt_total),
|
|||
|
|
"receiptDate": receipt_date,
|
|||
|
|
"purchaseID": db_purchase_id,
|
|||
|
|
"receiptItems": [],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
sql_query = "select article_name, total, category from purchaseData where id = ?"
|
|||
|
|
if cfg["dbMode"] == "mysql":
|
|||
|
|
sql_query = convert_to_mysql_query(sql_query)
|
|||
|
|
|
|||
|
|
cursor.execute(sql_query, [purchase_id])
|
|||
|
|
rows = cursor.fetchall()
|
|||
|
|
|
|||
|
|
for row in rows:
|
|||
|
|
if cfg["dbMode"] == "mysql":
|
|||
|
|
add_json = [row[0], str(row[1]), row[2]]
|
|||
|
|
else:
|
|||
|
|
add_json = [row.article_name, str(row.total), row.category]
|
|||
|
|
|
|||
|
|
purchase_details["receiptItems"].append(add_json)
|
|||
|
|
|
|||
|
|
conn.close()
|
|||
|
|
|
|||
|
|
return json.dumps(purchase_details)
|
|||
|
|
else:
|
|||
|
|
return "Purchase not found!", 500
|
|||
|
|
|
|||
|
|
|
|||
|
|
@app.route("/api/getValue", methods=["GET", "OPTIONS"])
|
|||
|
|
@cross_origin(origin="*", headers=["Content-Type"])
|
|||
|
|
def get_categories():
|
|||
|
|
get_values_from = request.args["getValuesFrom"]
|
|||
|
|
|
|||
|
|
ret_json = get_data_from_db(get_values_from)
|
|||
|
|
|
|||
|
|
return json.dumps(ret_json, ensure_ascii=False)
|
|||
|
|
|
|||
|
|
|
|||
|
|
@app.route("/api/addValue", methods=["POST", "OPTIONS"])
|
|||
|
|
@cross_origin(origin="*", headers=["Content-Type"])
|
|||
|
|
def add_value():
|
|||
|
|
to_add_array = request.args["toAddArray"]
|
|||
|
|
to_add_value = request.args["toAddValue"]
|
|||
|
|
item_id = request.args["id"]
|
|||
|
|
|
|||
|
|
add_or_update_to_db(to_add_array, item_id, to_add_value)
|
|||
|
|
|
|||
|
|
return "Done!"
|
|||
|
|
|
|||
|
|
|
|||
|
|
@app.route("/api/deleteValue", methods=["POST", "OPTIONS"])
|
|||
|
|
@cross_origin(origin="*", headers=["Content-Type"])
|
|||
|
|
def delete_value():
|
|||
|
|
table_name = request.args["tableName"]
|
|||
|
|
item_id = request.args["id"]
|
|||
|
|
|
|||
|
|
delete_from_db(table_name, item_id)
|
|||
|
|
|
|||
|
|
return "Done!"
|
|||
|
|
|
|||
|
|
@app.route("/api/deleteReceiptFromDB", methods=["POST", "OPTIONS"])
|
|||
|
|
@cross_origin(origin="*", headers=["Content-Type"])
|
|||
|
|
def delete_receipt_from_db():
|
|||
|
|
receipt_id = request.args["purchaseID"]
|
|||
|
|
|
|||
|
|
delete_receipt(receipt_id)
|
|||
|
|
|
|||
|
|
return "Done!"
|
|||
|
|
|
|||
|
|
@app.route("/api/updateReceiptToDB", methods=["POST", "OPTIONS"])
|
|||
|
|
@cross_origin(origin="*", headers=["Content-Type"])
|
|||
|
|
def update_receipt_to_db():
|
|||
|
|
post_string = json.dumps(request.get_json())
|
|||
|
|
post_json = json.loads(post_string)
|
|||
|
|
|
|||
|
|
conn, cursor = load_db_conn()
|
|||
|
|
|
|||
|
|
store_id = get_store_id(post_json["storeName"])
|
|||
|
|
receipt_date = post_json["receiptDate"]
|
|||
|
|
receipt_total = post_json["receiptTotal"]
|
|||
|
|
receipt_id = post_json["purchaseID"]
|
|||
|
|
|
|||
|
|
receipt_date = datetime.strptime(receipt_date, "%d.%m.%Y")
|
|||
|
|
receipt_date = receipt_date.strftime("%m-%d-%Y")
|
|||
|
|
|
|||
|
|
# Clean old db data
|
|||
|
|
delete_receipt(receipt_id)
|
|||
|
|
|
|||
|
|
# Write article positions
|
|||
|
|
for article in post_json["receiptItems"]:
|
|||
|
|
article_name = article[1]
|
|||
|
|
article_sum = article[2]
|
|||
|
|
article_id = int(str(uuid.uuid1().int)[:8])
|
|||
|
|
article_category_id = get_category_id(article[3])
|
|||
|
|
|
|||
|
|
sql_query = "INSERT INTO items values (?,?,?,?)"
|
|||
|
|
if cfg["dbMode"] == "mysql":
|
|||
|
|
sql_query = convert_to_mysql_query(sql_query)
|
|||
|
|
cursor.execute(
|
|||
|
|
sql_query, [article_id, article_name, article_sum, article_category_id]
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
sql_query = "INSERT INTO purchasesArticles values (?,?)"
|
|||
|
|
if cfg["dbMode"] == "mysql":
|
|||
|
|
sql_query = convert_to_mysql_query(sql_query)
|
|||
|
|
cursor.execute(sql_query, [receipt_id, article_id])
|
|||
|
|
|
|||
|
|
# Write receipt summary
|
|||
|
|
if cfg["dbMode"] == "mysql":
|
|||
|
|
sql_query = (
|
|||
|
|
"INSERT INTO receipts values (%s,%s,STR_TO_DATE(%s,'%m-%d-%Y'),%s,%s,%s)"
|
|||
|
|
)
|
|||
|
|
else:
|
|||
|
|
sql_query = "INSERT INTO receipts values (?,?,?,?,?,?)"
|
|||
|
|
|
|||
|
|
cursor.execute(
|
|||
|
|
sql_query, [receipt_id, store_id, receipt_date, receipt_total, None, receipt_id]
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
conn.commit()
|
|||
|
|
conn.close()
|
|||
|
|
|
|||
|
|
return "Done!"
|
|||
|
|
|
|||
|
|
|
|||
|
|
@app.route("/api/writeReceiptToDB", methods=["POST", "OPTIONS"])
|
|||
|
|
@cross_origin(origin="*", headers=["Content-Type"])
|
|||
|
|
def write_receipt_to_db():
|
|||
|
|
post_string = json.dumps(request.get_json())
|
|||
|
|
post_json = json.loads(post_string)
|
|||
|
|
|
|||
|
|
conn, cursor = load_db_conn()
|
|||
|
|
|
|||
|
|
store_id = get_store_id(post_json["storeName"])
|
|||
|
|
receipt_date = post_json["receiptDate"]
|
|||
|
|
receipt_total = post_json["receiptTotal"]
|
|||
|
|
receipt_id = int(str(uuid.uuid1().int)[:6])
|
|||
|
|
|
|||
|
|
receipt_date = datetime.strptime(receipt_date, "%d.%m.%Y")
|
|||
|
|
receipt_date = receipt_date.strftime("%m-%d-%Y")
|
|||
|
|
|
|||
|
|
# Write article positions
|
|||
|
|
for article in post_json["receiptItems"]:
|
|||
|
|
article_id = int(str(uuid.uuid1().int)[:8])
|
|||
|
|
article_name = article[1]
|
|||
|
|
article_sum = article[2]
|
|||
|
|
article_category_id = get_category_id(article[3])
|
|||
|
|
|
|||
|
|
if not article_category_id:
|
|||
|
|
return "Category id for category: " + article[3] + " not found", 500
|
|||
|
|
|
|||
|
|
sql_query = "INSERT INTO items values (?,?,?,?)"
|
|||
|
|
if cfg["dbMode"] == "mysql":
|
|||
|
|
sql_query = convert_to_mysql_query(sql_query)
|
|||
|
|
cursor.execute(
|
|||
|
|
sql_query, [article_id, article_name, article_sum, article_category_id]
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
sql_query = "INSERT INTO purchasesArticles values (?,?)"
|
|||
|
|
if cfg["dbMode"] == "mysql":
|
|||
|
|
sql_query = convert_to_mysql_query(sql_query)
|
|||
|
|
cursor.execute(sql_query, [receipt_id, article_id])
|
|||
|
|
|
|||
|
|
# Write receipt summary
|
|||
|
|
if cfg["dbMode"] == "mysql":
|
|||
|
|
sql_query = (
|
|||
|
|
"INSERT INTO receipts values (%s,%s,STR_TO_DATE(%s,'%m-%d-%Y'),%s,%s,%s)"
|
|||
|
|
)
|
|||
|
|
else:
|
|||
|
|
sql_query = "INSERT INTO receipts values (?,?,?,?,?,?)"
|
|||
|
|
|
|||
|
|
cursor.execute(
|
|||
|
|
sql_query, [receipt_id, store_id, receipt_date, receipt_total, None, receipt_id]
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
conn.commit()
|
|||
|
|
conn.close()
|
|||
|
|
|
|||
|
|
return "Done!"
|
|||
|
|
|
|||
|
|
@app.after_request
|
|||
|
|
def add_header(r):
|
|||
|
|
r.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
|
|||
|
|
r.headers["Pragma"] = "no-cache"
|
|||
|
|
r.headers["Expires"] = "0"
|
|||
|
|
r.headers['Cache-Control'] = 'public, max-age=0'
|
|||
|
|
return r
|