194 lines
5.5 KiB
Python
194 lines
5.5 KiB
Python
import re
|
|
import os
|
|
import mysql.connector
|
|
|
|
from cleanup_dummy_data import db_config
|
|
|
|
|
|
DATASET_PRODUCTS = {
|
|
'Baking Powder',
|
|
'Baking Powder 45gr',
|
|
'Cokelat Bubuk 250gr',
|
|
'Gula Pasir 1kg',
|
|
'Keju Parut 250gr',
|
|
'Mentega 500gr',
|
|
'Susu Bubuk',
|
|
'Susu Bubuk 27gr',
|
|
'Telur 1kg',
|
|
'Tepung Terigu 1kg',
|
|
}
|
|
|
|
RESTORE_TABLES = [
|
|
'predictions',
|
|
'products',
|
|
'recipes',
|
|
'recipe_ingredients',
|
|
'stock in',
|
|
'stock_usage_history',
|
|
'stok_keluar',
|
|
'transactions',
|
|
]
|
|
|
|
|
|
def extract_insert_statements(sql_text):
|
|
pattern = re.compile(
|
|
r"INSERT INTO `([^`]+)` .*?;\n",
|
|
flags=re.IGNORECASE | re.DOTALL,
|
|
)
|
|
for match in pattern.finditer(sql_text):
|
|
table_name = match.group(1)
|
|
statement = match.group(0)
|
|
if table_name in RESTORE_TABLES:
|
|
yield table_name, statement
|
|
|
|
|
|
def table_exists(cursor, table_name):
|
|
cursor.execute("SHOW TABLES LIKE %s", (table_name,))
|
|
return cursor.fetchone() is not None
|
|
|
|
|
|
def delete_table(cursor, table_name):
|
|
if not table_exists(cursor, table_name):
|
|
return
|
|
escaped = f"`{table_name.replace('`', '``')}`"
|
|
cursor.execute(f"DELETE FROM {escaped}")
|
|
cursor.execute(f"ALTER TABLE {escaped} AUTO_INCREMENT = 1")
|
|
|
|
|
|
def get_existing_column(cursor, table_name, candidates):
|
|
for column in candidates:
|
|
cursor.execute(f"SHOW COLUMNS FROM `{table_name}` LIKE %s", (column,))
|
|
if cursor.fetchone():
|
|
return column
|
|
return None
|
|
|
|
|
|
def placeholders(values):
|
|
return ', '.join(['%s'] * len(values))
|
|
|
|
|
|
def delete_not_in_dataset(cursor, table_name, column_name):
|
|
if not table_exists(cursor, table_name):
|
|
return 0
|
|
product_list = sorted(DATASET_PRODUCTS)
|
|
escaped_table = f"`{table_name.replace('`', '``')}`"
|
|
escaped_column = f"`{column_name.replace('`', '``')}`"
|
|
cursor.execute(
|
|
f"DELETE FROM {escaped_table} WHERE {escaped_column} NOT IN ({placeholders(product_list)})",
|
|
tuple(product_list),
|
|
)
|
|
return cursor.rowcount
|
|
|
|
|
|
def normalize_dataset_product_rows(cursor):
|
|
if not table_exists(cursor, 'products'):
|
|
return
|
|
|
|
updates = {
|
|
'Baking Powder': ('Bahan Tambahan', 8180, '45gr'),
|
|
'Baking Powder 45gr': ('Bahan Tambahan', 8180, '45gr'),
|
|
'Cokelat Bubuk 250gr': ('Cokelat', 21036, '250gr'),
|
|
'Gula Pasir 1kg': ('Gula', 14608, '1kg'),
|
|
'Keju Parut 250gr': ('Keju', 23373, '250gr'),
|
|
'Mentega 500gr': ('Mentega', 17529, '500gr'),
|
|
'Susu Bubuk': ('Susu', 17529, '27gr'),
|
|
'Susu Bubuk 27gr': ('Susu', 17529, '27gr'),
|
|
'Telur 1kg': ('Telur', 26878, '1kg'),
|
|
'Tepung Terigu 1kg': ('Tepung', 11687, '1kg'),
|
|
}
|
|
|
|
unit_col = get_existing_column(cursor, 'products', ['unit'])
|
|
for name, (category, price, unit) in updates.items():
|
|
if unit_col:
|
|
cursor.execute(
|
|
f"""
|
|
UPDATE products
|
|
SET category = %s, price = %s, {unit_col} = %s
|
|
WHERE name = %s
|
|
""",
|
|
(category, price, unit, name),
|
|
)
|
|
else:
|
|
cursor.execute(
|
|
"UPDATE products SET category = %s, price = %s WHERE name = %s",
|
|
(category, price, name),
|
|
)
|
|
|
|
|
|
def cleanup_to_backend_recipe_data(cursor):
|
|
deleted = {}
|
|
|
|
# Jangan hapus products/recipe_ingredients yang tidak ada di dataset model.
|
|
# Random Forest memang hanya memakai bahan dataset sebagai bahan acuan,
|
|
# tetapi kalkulasi kebutuhan resep tetap membutuhkan bahan pendukung seperti
|
|
# Santan, Pewarna Makanan, Kelapa Parut, Selai Nanas, dan bahan lain dari backend.
|
|
deleted['products'] = 0
|
|
deleted['recipe_ingredients'] = 0
|
|
deleted['stock in'] = 0
|
|
deleted['stock_usage_history'] = 0
|
|
deleted['transactions'] = 0
|
|
|
|
if table_exists(cursor, 'stok_keluar') and table_exists(cursor, 'products'):
|
|
cursor.execute(
|
|
"""
|
|
DELETE sk
|
|
FROM stok_keluar sk
|
|
LEFT JOIN products p ON p.id = sk.bahan_id
|
|
WHERE p.id IS NULL
|
|
"""
|
|
)
|
|
deleted['stok_keluar'] = cursor.rowcount
|
|
|
|
if table_exists(cursor, 'recipes') and table_exists(cursor, 'recipe_ingredients'):
|
|
cursor.execute(
|
|
"""
|
|
DELETE r
|
|
FROM recipes r
|
|
LEFT JOIN recipe_ingredients ri ON ri.recipe_id = r.id
|
|
WHERE ri.id IS NULL
|
|
"""
|
|
)
|
|
deleted['recipes_without_dataset_ingredients'] = cursor.rowcount
|
|
|
|
normalize_dataset_product_rows(cursor)
|
|
return deleted
|
|
|
|
|
|
def main():
|
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
sql_path = os.path.join(script_dir, 'prediksi_stok_db.sql')
|
|
with open(sql_path, 'r', encoding='utf-8') as sql_file:
|
|
sql_text = sql_file.read()
|
|
|
|
connection = mysql.connector.connect(**db_config())
|
|
cursor = connection.cursor()
|
|
|
|
restored = []
|
|
try:
|
|
cursor.execute("SET FOREIGN_KEY_CHECKS = 0")
|
|
|
|
for table in reversed(RESTORE_TABLES):
|
|
delete_table(cursor, table)
|
|
|
|
for table, statement in extract_insert_statements(sql_text):
|
|
cursor.execute(statement)
|
|
restored.append(table)
|
|
|
|
deleted = cleanup_to_backend_recipe_data(cursor)
|
|
|
|
cursor.execute("SET FOREIGN_KEY_CHECKS = 1")
|
|
connection.commit()
|
|
except Exception:
|
|
connection.rollback()
|
|
raise
|
|
finally:
|
|
cursor.close()
|
|
connection.close()
|
|
|
|
print("Restored insert blocks:", ", ".join(restored))
|
|
print("Deleted unsupported rows:", deleted)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|