TIF_E41253368/api/app/__init__.py

96 lines
4.0 KiB
Python

import json
import logging
from pathlib import Path
from flask import Flask, jsonify, request
from app.api.routes import api
from app.config import Config
from app.data.repository import CsvPriceRepository, MySQLPriceRepository
from app.errors import register_error_handlers
from app.services.model_registry import ModelRegistry
from app.services.prediction_service import PredictionService
from app.services.siskaperbapo_scraper import SiskaperbapoScraper
def create_app(config_class=Config) -> Flask:
app = Flask(__name__)
app.config.from_object(config_class)
logging.basicConfig(level=logging.INFO)
origins = app.config["CORS_ORIGINS"]
allowed_origins = [value.strip() for value in origins.split(",") if value.strip()]
@app.after_request
def add_cors_credentials(response):
origin = request.headers.get("Origin")
if origin and (origins == "*" or origin in allowed_origins):
response.headers["Access-Control-Allow-Origin"] = origin
response.headers["Access-Control-Allow-Credentials"] = "true"
response.headers["Access-Control-Allow-Headers"] = "Content-Type"
response.headers["Access-Control-Allow-Methods"] = "GET,POST,DELETE,OPTIONS"
response.headers["Vary"] = "Origin"
return response
model_registry = ModelRegistry(app.config["MODELS_DIR"])
if app.config["DATA_SOURCE"] == "mysql":
repository = MySQLPriceRepository(app.config)
elif app.config["DATA_SOURCE"] == "csv":
repository = CsvPriceRepository(app.config["DATA_DIR"], app.config["PRICE_UNIT_SCALE"])
else:
raise ValueError("DATA_SOURCE harus bernilai 'mysql' atau 'csv'.")
app.extensions["model_registry"] = model_registry
app.extensions["prediction_service"] = PredictionService(model_registry, repository, app.config["STALE_DATA_DAYS"], app.config["TREND_THRESHOLD_PCT"])
if hasattr(repository, "_connect"):
connection = repository._connect()
try:
with connection.cursor() as cursor:
cursor.execute(
"""
SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE()
AND table_name = 'price_history'
AND column_name = 'scraping_log_id'
"""
)
if cursor.fetchone()[0] == 0:
cursor.execute(
"ALTER TABLE price_history ADD COLUMN scraping_log_id BIGINT UNSIGNED NULL AFTER source"
)
cursor.execute(
"""
ALTER TABLE price_history
ADD KEY idx_price_history_scraping_log (scraping_log_id),
ADD CONSTRAINT fk_price_history_scraping_log
FOREIGN KEY (scraping_log_id) REFERENCES scraping_logs(id)
ON UPDATE CASCADE ON DELETE CASCADE
"""
)
cursor.execute(
"""
UPDATE price_history ph
JOIN (
SELECT MAX(id) AS id FROM scraping_logs WHERE source = 'siskaperbapo'
) latest ON 1 = 1
SET ph.scraping_log_id = latest.id
WHERE ph.source = 'siskaperbapo' AND ph.scraping_log_id IS NULL
"""
)
connection.commit()
finally:
connection.close()
app.extensions["siskaperbapo_scraper"] = SiskaperbapoScraper(
app.config["SISKAPERBAPO_URL"],
app.config["SISKAPERBAPO_COMMODITY_PARAM"],
app.config["SISKAPERBAPO_TIMEOUT"],
)
markets_path = Path(__file__).parent / "data" / "markets.json"
app.extensions["markets_config"] = json.loads(markets_path.read_text(encoding="utf-8"))
app.register_blueprint(api)
register_error_handlers(app)
@app.get("/")
def root():
return jsonify({"message": "API Prediksi Harga Cabai aktif", "status": "ok"})
return app