ADM-Purchasing-Tools / app /web_app.py
abdulsalam2121
Refactor default settings in load_saved_settings to remove environment variable dependencies
38a89cd
Raw
History Blame Contribute Delete
5.92 kB
import json
import os
import logging
import threading
import webbrowser
from pathlib import Path
from typing import Any, Dict
from flask import Flask, jsonify, make_response, render_template, request, send_file
from services.run_bot import AutomationController
APP_DIR = Path(__file__).resolve().parent
ROOT_DIR = APP_DIR.parent
CONFIG_PATH = APP_DIR / "config.json"
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s: %(message)s")
app = Flask(
__name__,
template_folder=str(APP_DIR / "templates"),
static_folder=str(APP_DIR / "static"),
static_url_path="/static",
)
controller = AutomationController()
def load_saved_settings() -> Dict[str, Any]:
default_settings = {
"username": "",
"password": "",
"studio": "",
"min_price": 6.0,
}
if not CONFIG_PATH.exists():
return default_settings
try:
data = json.loads(CONFIG_PATH.read_text(encoding="utf-8"))
return {
"username": data.get("username", default_settings["username"]),
"password": data.get("password", default_settings["password"]),
"studio": data.get("studio", default_settings["studio"]),
"min_price": data.get("min_price", default_settings["min_price"]),
}
except Exception:
return default_settings
def save_settings(payload: Dict[str, Any]) -> None:
data = {
"username": payload.get("username", ""),
"password": payload.get("password", ""),
"studio": payload.get("studio", ""),
"min_price": payload.get("min_price", 6.0),
}
CONFIG_PATH.write_text(json.dumps(data, indent=2), encoding="utf-8")
@app.get("/")
def index():
return render_template("index.html", settings=load_saved_settings())
@app.post("/start")
def start():
payload = request.get_json(silent=True) or {}
username = str(payload.get("username", "")).strip()
password = str(payload.get("password", ""))
studio = str(payload.get("studio", "")).strip()
min_price = payload.get("min_price", "")
if not username or not password or not studio:
return jsonify({"ok": False, "error": "Username, password, and studio are required"}), 400
try:
min_price_value = float(min_price)
except Exception:
return jsonify({"ok": False, "error": "Minimum price must be numeric"}), 400
result = controller.start(username=username, password=password, studio=studio, min_price=min_price_value)
status_code = 200 if result.get("ok") else 400
return jsonify(result), status_code
@app.post("/stop")
def stop():
return jsonify(controller.stop())
@app.get("/status")
def status():
return jsonify(controller.snapshot())
@app.get("/screenshot")
def screenshot_route():
# Use absolute path to logs directory (project root)
logs_dir = ROOT_DIR / "logs"
latest = logs_dir / "screenshot_latest.png"
# normalize
latest = latest.resolve()
logging.info(f"Screenshot request: ROOT_DIR={ROOT_DIR}, logs_dir={logs_dir}, latest={latest}, exists={latest.exists()}")
if not latest.exists():
# List what's in the logs directory for debugging
if logs_dir.exists():
files = list(logs_dir.glob("*.png"))
logging.warning(f"Screenshot not found at {latest}. Files in {logs_dir}: {files}")
else:
logging.warning(f"Logs directory doesn't exist: {logs_dir}")
return ("", 204)
try:
logging.info(f"Sending screenshot: {latest}")
response = make_response(send_file(str(latest), mimetype="image/png"))
response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0"
response.headers["Pragma"] = "no-cache"
response.headers["Expires"] = "0"
return response
except Exception as e:
logging.error(f"Screenshot send failed: {e}")
return ("", 500)
@app.get("/download")
def download():
snapshot = controller.snapshot()
csv_path_raw = snapshot.get("last_csv_path") or str(ROOT_DIR / "exports" / "latest.csv")
path = Path(csv_path_raw)
# Normalize relative paths after project restructuring.
if not path.is_absolute():
candidates = [
ROOT_DIR / path,
APP_DIR / path,
Path.cwd() / path,
]
resolved = next((p for p in candidates if p.exists()), None)
path = resolved or (ROOT_DIR / "exports" / "latest.csv")
path = path.resolve()
if not path.exists() or not path.is_file():
return jsonify({"ok": False, "error": "No CSV file is available yet"}), 404
try:
return send_file(str(path), as_attachment=True, download_name=path.name, mimetype="text/csv")
except Exception as exc:
logging.exception("Download failed")
return jsonify({"ok": False, "error": f"Download failed: {exc}"}), 500
@app.post("/save-settings")
def save_settings_route():
payload = request.get_json(silent=True) or {}
username = str(payload.get("username", "")).strip()
password = str(payload.get("password", ""))
studio = str(payload.get("studio", "")).strip()
min_price = payload.get("min_price", 6.0)
try:
min_price_value = float(min_price)
except Exception:
return jsonify({"ok": False, "error": "Minimum price must be numeric"}), 400
save_settings({
"username": username,
"password": password,
"studio": studio,
"min_price": min_price_value,
})
return jsonify({"ok": True, "message": "Settings saved"})
if __name__ == "__main__":
port = int(os.environ.get("PORT", "5000"))
host = "0.0.0.0" if "PORT" in os.environ else "127.0.0.1"
if "PORT" not in os.environ:
threading.Timer(1.0, lambda: webbrowser.open_new(f"http://localhost:{port}")).start()
app.run(host=host, port=port, debug=False, threaded=True)