diff --git a/app.log b/app.log new file mode 100644 index 0000000..8b32a01 --- /dev/null +++ b/app.log @@ -0,0 +1,25 @@ +nohup: ignoring input + * Serving Flask app 'app' + * Debug mode: on +WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:8781 + * Running on http://192.168.0.132:8781 +Press CTRL+C to quit + * Restarting with stat + * Debugger is active! + * Debugger PIN: 869-443-054 +172.18.0.2 - - [07/Oct/2025 22:03:14] "GET /quality HTTP/1.1" 200 - +172.18.0.2 - - [07/Oct/2025 22:03:19] "GET /logout HTTP/1.1" 302 - +172.18.0.2 - - [07/Oct/2025 22:03:19] "GET / HTTP/1.1" 200 - +172.18.0.2 - - [07/Oct/2025 22:03:19] "GET /static/logo_login.jpg HTTP/1.1" 304 - +172.18.0.2 - - [07/Oct/2025 22:03:24] "POST / HTTP/1.1" 302 - +172.18.0.2 - - [07/Oct/2025 22:03:24] "GET /dashboard HTTP/1.1" 200 - +172.18.0.2 - - [07/Oct/2025 22:03:30] "GET /logout HTTP/1.1" 302 - +172.18.0.2 - - [07/Oct/2025 22:03:30] "GET / HTTP/1.1" 200 - +172.18.0.2 - - [07/Oct/2025 22:03:34] "POST / HTTP/1.1" 302 - +172.18.0.2 - - [07/Oct/2025 22:03:34] "GET /dashboard HTTP/1.1" 200 - +172.18.0.2 - - [07/Oct/2025 22:03:38] "GET /logout HTTP/1.1" 302 - +172.18.0.2 - - [07/Oct/2025 22:03:38] "GET / HTTP/1.1" 200 - +172.18.0.2 - - [07/Oct/2025 22:03:56] "POST / HTTP/1.1" 302 - +172.18.0.2 - - [07/Oct/2025 22:03:56] "GET /dashboard HTTP/1.1" 200 - diff --git a/py_app/app/__pycache__/__init__.cpython-311.pyc b/py_app/app/__pycache__/__init__.cpython-311.pyc old mode 100755 new mode 100644 index d24961c..d43ed28 Binary files a/py_app/app/__pycache__/__init__.cpython-311.pyc and b/py_app/app/__pycache__/__init__.cpython-311.pyc differ diff --git a/py_app/app/__pycache__/models.cpython-311.pyc b/py_app/app/__pycache__/models.cpython-311.pyc old mode 100755 new mode 100644 index 9ed0bde..1b9f37e Binary files a/py_app/app/__pycache__/models.cpython-311.pyc and b/py_app/app/__pycache__/models.cpython-311.pyc differ diff --git a/py_app/app/__pycache__/routes.cpython-311.pyc b/py_app/app/__pycache__/routes.cpython-311.pyc old mode 100755 new mode 100644 index a2bc35c..decbc86 Binary files a/py_app/app/__pycache__/routes.cpython-311.pyc and b/py_app/app/__pycache__/routes.cpython-311.pyc differ diff --git a/py_app/app/db_create_scripts/recreate_order_for_labels_table.py b/py_app/app/db_create_scripts/recreate_order_for_labels_table.py index ef090c6..163d58d 100755 --- a/py_app/app/db_create_scripts/recreate_order_for_labels_table.py +++ b/py_app/app/db_create_scripts/recreate_order_for_labels_table.py @@ -2,10 +2,10 @@ import mariadb # Database connection credentials DB_CONFIG = { - "user": "trasabilitate", - "password": "Initial01!", + "user": "sa", + "password": "12345678", "host": "localhost", - "database": "trasabilitate_database" + "database": "recticel" } def recreate_order_for_labels_table(): diff --git a/py_app/app/routes.py b/py_app/app/routes.py index 827527c..5b5b7b6 100755 --- a/py_app/app/routes.py +++ b/py_app/app/routes.py @@ -27,84 +27,8 @@ from .print_module import get_unprinted_orders_data bp = Blueprint('main', __name__) warehouse_bp = Blueprint('warehouse', __name__) -def format_cell_data(cell): - """Helper function to format cell data, especially dates and times""" - # Import date and datetime at the top of the function - from datetime import datetime, date - - if isinstance(cell, datetime): - # Format datetime as dd/mm/yyyy - return cell.strftime('%d/%m/%Y') - elif isinstance(cell, date): - # Format date as dd/mm/yyyy - return cell.strftime('%d/%m/%Y') - elif isinstance(cell, timedelta): - # Convert timedelta to HH:MM:SS format - total_seconds = int(cell.total_seconds()) - hours, remainder = divmod(total_seconds, 3600) - minutes, seconds = divmod(remainder, 60) - return f"{hours:02d}:{minutes:02d}:{seconds:02d}" - elif isinstance(cell, str): - # Handle string dates in yyyy-mm-dd or yyyy-mm-dd HH:MM:SS - import re - match = re.match(r'^(\d{4})-(\d{2})-(\d{2})(.*)$', cell) - if match: - year, month, day, rest = match.groups() - formatted = f"{day}/{month}/{year}" - if rest.strip(): - # If there is a time part, keep it after the date - formatted += rest - return formatted - return cell - else: - return cell -@bp.route('/store_articles') -def store_articles(): - return render_template('store_articles.html') - -@bp.route('/get_pairing_keys') -def get_pairing_keys(): - """Return all pairing keys as JSON for client selection.""" - keys_path = os.path.join(current_app.instance_path, 'pairing_keys.json') - try: - if os.path.exists(keys_path): - with open(keys_path, 'r') as f: - keys = json.load(f) - else: - keys = [] - except Exception as e: - print(f"Error loading pairing keys: {e}") - return jsonify([]), 200 - return jsonify(keys) - -@bp.route('/warehouse_reports') -def warehouse_reports(): - return render_template('warehouse_reports.html') - -def get_db_connection(): - """Reads the external_server.conf file and returns a MariaDB database connection.""" - settings_file = os.path.join(current_app.instance_path, 'external_server.conf') - if not os.path.exists(settings_file): - raise FileNotFoundError("The external_server.conf file is missing in the instance folder.") - - # Read settings from the configuration file - settings = {} - with open(settings_file, 'r') as f: - for line in f: - key, value = line.strip().split('=', 1) - settings[key] = value - - # Create a database connection - return mariadb.connect( - user=settings['username'], - password=settings['password'], - host=settings['server_domain'], - port=int(settings['port']), - database=settings['database_name'] - ) - -@bp.route('/login', methods=['GET', 'POST']) +@bp.route('/', methods=['GET', 'POST']) def login(): import sqlite3 if request.method == 'POST': @@ -190,6 +114,83 @@ def login(): flash('Invalid credentials. Please try again.') return render_template('login.html') +def format_cell_data(cell): + """Helper function to format cell data, especially dates and times""" + # Import date and datetime at the top of the function + from datetime import datetime, date + + if isinstance(cell, datetime): + # Format datetime as dd/mm/yyyy + return cell.strftime('%d/%m/%Y') + elif isinstance(cell, date): + # Format date as dd/mm/yyyy + return cell.strftime('%d/%m/%Y') + elif isinstance(cell, timedelta): + # Convert timedelta to HH:MM:SS format + total_seconds = int(cell.total_seconds()) + hours, remainder = divmod(total_seconds, 3600) + minutes, seconds = divmod(remainder, 60) + return f"{hours:02d}:{minutes:02d}:{seconds:02d}" + elif isinstance(cell, str): + # Handle string dates in yyyy-mm-dd or yyyy-mm-dd HH:MM:SS + import re + match = re.match(r'^(\d{4})-(\d{2})-(\d{2})(.*)$', cell) + if match: + year, month, day, rest = match.groups() + formatted = f"{day}/{month}/{year}" + if rest.strip(): + # If there is a time part, keep it after the date + formatted += rest + return formatted + return cell + else: + return cell + +@bp.route('/store_articles') +def store_articles(): + return render_template('store_articles.html') + +@bp.route('/get_pairing_keys') +def get_pairing_keys(): + """Return all pairing keys as JSON for client selection.""" + keys_path = os.path.join(current_app.instance_path, 'pairing_keys.json') + try: + if os.path.exists(keys_path): + with open(keys_path, 'r') as f: + keys = json.load(f) + else: + keys = [] + except Exception as e: + print(f"Error loading pairing keys: {e}") + return jsonify([]), 200 + return jsonify(keys) + +@bp.route('/warehouse_reports') +def warehouse_reports(): + return render_template('warehouse_reports.html') + +def get_db_connection(): + """Reads the external_server.conf file and returns a MariaDB database connection.""" + settings_file = os.path.join(current_app.instance_path, 'external_server.conf') + if not os.path.exists(settings_file): + raise FileNotFoundError("The external_server.conf file is missing in the instance folder.") + + # Read settings from the configuration file + settings = {} + with open(settings_file, 'r') as f: + for line in f: + key, value = line.strip().split('=', 1) + settings[key] = value + + # Create a database connection + return mariadb.connect( + user=settings['username'], + password=settings['password'], + host=settings['server_domain'], + port=int(settings['port']), + database=settings['database_name'] + ) + @bp.route('/dashboard') def dashboard(): print("Session user:", session.get('user'), session.get('role')) @@ -203,21 +204,21 @@ def settings(): @bp.route('/quality') def quality(): - if 'role' not in session or session['role'] not in ['superadmin', 'quality']: + if 'role' not in session or session['role'] not in ['superadmin', 'administrator', 'quality']: flash('Access denied: Quality users only.') return redirect(url_for('main.dashboard')) return render_template('quality.html') @bp.route('/warehouse') def warehouse(): - if 'role' not in session or session['role'] not in ['superadmin', 'warehouse']: + if 'role' not in session or session['role'] not in ['superadmin', 'administrator', 'admin', 'warehouse']: flash('Access denied: Warehouse users only.') return redirect(url_for('main.dashboard')) return render_template('main_page_warehouse.html') @bp.route('/scan', methods=['GET', 'POST']) def scan(): - if 'role' not in session or session['role'] not in ['superadmin', 'scan']: + if 'role' not in session or session['role'] not in ['superadmin', 'administrator', 'admin', 'scan']: flash('Access denied: Scan users only.') return redirect(url_for('main.dashboard')) @@ -952,14 +953,14 @@ def test_database(): @bp.route('/etichete') def etichete(): - if 'role' not in session or session['role'] not in ['superadmin', 'etichete']: + if 'role' not in session or session['role'] not in ['superadmin', 'admin', 'administrator', 'etichete']: flash('Access denied: Etichete users only.') return redirect(url_for('main.dashboard')) return render_template('main_page_etichete.html') @bp.route('/upload_data') def upload_data(): - return render_template('upload_data.html') + return render_template('upload_orders.html') @bp.route('/print_module') def print_module(): @@ -1817,7 +1818,7 @@ def create_template(): @bp.route('/get_database_tables', methods=['GET']) def get_database_tables(): """Get list of database tables for template creation""" - if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']: + if 'role' not in session or session['role'] not in ['superadmin', 'admin', 'administrator', 'warehouse_manager', 'etichete']: return jsonify({'error': 'Access denied'}), 403 try: @@ -1867,7 +1868,7 @@ def get_database_tables(): @bp.route('/get_table_columns/', methods=['GET']) def get_table_columns(table_name): """Get column names and descriptions for a specific table""" - if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']: + if 'role' not in session or session['role'] not in ['superadmin', 'admin', 'administrator', 'warehouse_manager', 'etichete']: return jsonify({'error': 'Access denied'}), 403 try: @@ -2002,41 +2003,7 @@ def generate_pdf(): return jsonify({'message': 'PDF generated successfully!', 'pdf_path': f'/static/label_templates/label_template.pdf'}) # Order Labels Upload Module Routes -@bp.route('/upload_orders', methods=['GET', 'POST']) -def upload_orders(): - """Route for uploading orders CSV files for label generation""" - if 'role' not in session or session['role'] not in ['superadmin', 'warehouse', 'warehouse_manager']: - flash('Access denied: Warehouse management permissions required.') - return redirect(url_for('main.dashboard')) - - from app.order_labels import upload_orders_handler - return upload_orders_handler() - -@bp.route('/view_orders') -def view_orders(): - """Route for viewing uploaded orders""" - if 'role' not in session or session['role'] not in ['superadmin', 'warehouse', 'warehouse_manager', 'warehouse_worker']: - flash('Access denied: Warehouse access required.') - return redirect(url_for('main.dashboard')) - - from app.order_labels import get_orders_from_database - orders = get_orders_from_database(200) # Get last 200 orders - return render_template('view_orders.html', orders=orders) - -@bp.route('/print_lost_labels') -def print_lost_labels(): - """Route for printing lost labels - allows searching and reprinting specific orders""" - if 'role' not in session or session['role'] not in ['superadmin', 'warehouse', 'warehouse_manager', 'warehouse_worker']: - flash('Access denied: Warehouse access required.') - return redirect(url_for('main.dashboard')) - - from app.order_labels import get_orders_from_database - orders = get_orders_from_database(500) # Get more orders for searching - return render_template('print_lost_labels.html', orders=orders) - -@bp.route('/db_test') -def db_test(): - """Simple database test page""" + # ...existing code... return render_template('db_test.html') @bp.route('/get_unprinted_orders', methods=['GET']) @@ -2067,9 +2034,9 @@ def generate_labels_pdf(order_id, paper_saving_mode='true'): """Generate PDF labels for a specific order""" print(f"DEBUG: generate_labels_pdf called for order_id: {order_id}") - if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']: + if 'role' not in session or session['role'] not in ['superadmin', 'admin', 'administrator', 'warehouse_manager', 'etichete']: print(f"DEBUG: Access denied for role: {session.get('role')}") - return jsonify({'error': 'Access denied. Required roles: superadmin, warehouse_manager, etichete'}), 403 + return jsonify({'error': 'Access denied. Required roles: superadmin, admin, administrator, warehouse_manager, etichete'}), 403 try: from .pdf_generator import generate_order_labels_pdf, update_order_printed_status @@ -2148,8 +2115,8 @@ def generate_labels_pdf(order_id, paper_saving_mode='true'): def generate_label_pdf(): """Generate a single label PDF for thermal printing via QZ Tray""" - if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']: - return jsonify({'error': 'Access denied. Required roles: superadmin, warehouse_manager, etichete'}), 403 + if 'role' not in session or session['role'] not in ['superadmin', 'admin', 'administrator', 'warehouse_manager', 'etichete']: + return jsonify({'error': 'Access denied. Required roles: superadmin, admin, administrator, warehouse_manager, etichete'}), 403 try: from .pdf_generator import LabelPDFGenerator @@ -2197,9 +2164,9 @@ def update_printed_status(order_id): """Update printed status for direct printing (without PDF generation)""" print(f"DEBUG: update_printed_status called for order_id: {order_id}") - if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']: + if 'role' not in session or session['role'] not in ['superadmin', 'admin', 'administrator', 'warehouse_manager', 'etichete']: print(f"DEBUG: Access denied for role: {session.get('role')}") - return jsonify({'error': 'Access denied. Required roles: superadmin, warehouse_manager, etichete'}), 403 + return jsonify({'error': 'Access denied. Required roles: superadmin, admin, administrator, warehouse_manager, etichete'}), 403 try: from .pdf_generator import update_order_printed_status @@ -2312,7 +2279,7 @@ def get_order_data(order_id): """Get specific order data for preview""" print(f"DEBUG: get_order_data called for order_id: {order_id}") - if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']: + if 'role' not in session or session['role'] not in ['superadmin', 'admin', 'administrator', 'warehouse_manager', 'etichete']: return jsonify({'error': 'Access denied'}), 403 try: diff --git a/py_app/app/templates/main_page_etichete.html b/py_app/app/templates/main_page_etichete.html index 86994e5..25083fc 100755 --- a/py_app/app/templates/main_page_etichete.html +++ b/py_app/app/templates/main_page_etichete.html @@ -14,8 +14,8 @@

View Orders

Upload new orders or view existing orders and manage label data for printing.

- Upload Orders - View Orders + Upload Orders + View Orders
@@ -25,7 +25,7 @@

Access the print module to print labels.

Launch Printing Module - Launch lost labels printing module + Launch lost labels printing module
diff --git a/py_app/instance/external_server.conf b/py_app/instance/external_server.conf index 271cc7b..a03a8b9 100755 --- a/py_app/instance/external_server.conf +++ b/py_app/instance/external_server.conf @@ -1,5 +1,5 @@ server_domain=localhost port=3602 -database_name=trasabilitate_database -username=trasabilitate -password=Initial01! +database_name=recticel +username=sa +password=12345678 diff --git a/py_app/run.py b/py_app/run.py index a3fdaf3..21b7df3 100755 --- a/py_app/run.py +++ b/py_app/run.py @@ -3,4 +3,4 @@ from app import create_app app = create_app() if __name__ == '__main__': - app.run(debug=True) + app.run(debug=True, port=8781, host='0.0.0.0') diff --git a/recticel-app.service b/recticel-app.service new file mode 100644 index 0000000..46b9361 --- /dev/null +++ b/recticel-app.service @@ -0,0 +1,15 @@ +[Unit] +Description=Recticel Quality App +After=network.target mariadb.service + +[Service] +Type=simple +User=ske087 +WorkingDirectory=/home/ske087/quality_recticel +Environment=PATH=/home/ske087/quality_recticel/recticel/bin +ExecStart=/home/ske087/quality_recticel/recticel/bin/python py_app/run.py +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/to_do_install.txt b/to_do_install.txt new file mode 100644 index 0000000..22a570a --- /dev/null +++ b/to_do_install.txt @@ -0,0 +1,27 @@ + +# Steps to Prepare Environment for Installing Python Requirements + +1. Change ownership of the project directory (if needed): + sudo chown -R $USER:$USER /home/ske087/quality_recticel + +2. Install Python venv module: + sudo apt install -y python3-venv + +3. Create and activate the virtual environment: + python3 -m venv recticel + source recticel/bin/activate + +4. Install MariaDB server and development libraries: + sudo apt install -y mariadb-server libmariadb-dev + +5. Create MariaDB database and user: + sudo mysql -e "CREATE DATABASE recticel; CREATE USER 'sa'@'localhost' IDENTIFIED BY '12345678'; GRANT ALL PRIVILEGES ON recticel.* TO 'sa'@'localhost'; FLUSH PRIVILEGES;" + +6. Install build tools (for compiling Python packages): + sudo apt install -y build-essential + +7. Install Python development headers: + sudo apt install -y python3-dev + +8. Install Python requirements: + pip install -r py_app/requirements.txt