This commit is contained in:
livindra 2026-04-28 21:29:34 +07:00
parent 910e929394
commit ff92f07645
1 changed files with 50 additions and 8 deletions

View File

@ -17,19 +17,31 @@ from sqlalchemy.exc import SQLAlchemyError
import pytz import pytz
TZ_INDONESIA = pytz.timezone('Asia/Jakarta') TZ_INDONESIA = pytz.timezone('Asia/Jakarta')
DATABASE_URL = os.environ.get('DATABASE_URL') DATABASE_URL = os.environ.get('DATABASE_URL', '').strip()
def _normalize_port(value, default='3306'):
"""Return a numeric MySQL port string or a safe default."""
if value is None:
return default
value = str(value).strip()
return value if value.isdigit() else default
def _build_database_uri(): def _build_database_uri():
"""Build a SQLAlchemy database URI from Railway or local environment variables.""" """Build a SQLAlchemy database URI from Railway or local environment variables."""
if DATABASE_URL: if DATABASE_URL:
try:
url = make_url(DATABASE_URL) url = make_url(DATABASE_URL)
if url.drivername == 'mysql': if url.drivername == 'mysql':
url = url.set(drivername='mysql+pymysql') url = url.set(drivername='mysql+pymysql')
return str(url) return str(url)
except Exception as e:
print(f"[MYSQL] Ignoring invalid DATABASE_URL: {e}")
db_host = os.environ.get('DB_HOST') or os.environ.get('MYSQLHOST', 'localhost') db_host = os.environ.get('DB_HOST') or os.environ.get('MYSQLHOST', 'localhost')
db_port = os.environ.get('DB_PORT') or os.environ.get('MYSQLPORT', '3306') db_port = _normalize_port(os.environ.get('DB_PORT') or os.environ.get('MYSQLPORT'))
db_user = os.environ.get('DB_USER') or os.environ.get('MYSQLUSER', 'root') db_user = os.environ.get('DB_USER') or os.environ.get('MYSQLUSER', 'root')
db_password = os.environ.get('DB_PASSWORD') or os.environ.get('MYSQLPASSWORD', '') db_password = os.environ.get('DB_PASSWORD') or os.environ.get('MYSQLPASSWORD', '')
db_name = os.environ.get('DB_NAME') or os.environ.get('MYSQLDATABASE', 'deteksi_pmk') db_name = os.environ.get('DB_NAME') or os.environ.get('MYSQLDATABASE', 'deteksi_pmk')
@ -43,9 +55,14 @@ def _build_database_uri():
DATABASE_URI = _build_database_uri() DATABASE_URI = _build_database_uri()
# Create engine # Create engine
try:
engine = create_engine(DATABASE_URI, echo=False, pool_pre_ping=True) engine = create_engine(DATABASE_URI, echo=False, pool_pre_ping=True)
except Exception as e:
print(f"[MYSQL] Database engine unavailable: {e}")
engine = None
Base = declarative_base() Base = declarative_base()
Session = sessionmaker(bind=engine) Session = sessionmaker(bind=engine) if engine is not None else sessionmaker()
def _prediction_source_from_features(features): def _prediction_source_from_features(features):
@ -285,6 +302,10 @@ def get_engine():
def init_mysql_tables(): def init_mysql_tables():
"""Initialize database tables""" """Initialize database tables"""
if engine is None:
print("[MYSQL] Skipping table initialization because database engine is unavailable")
return False
try: try:
Base.metadata.create_all(engine) Base.metadata.create_all(engine)
migrate_diagnosis_history_schema() migrate_diagnosis_history_schema()
@ -292,6 +313,7 @@ def init_mysql_tables():
seed_expert_knowledge(force=False) seed_expert_knowledge(force=False)
sync_expert_rule_symptom_relations() sync_expert_rule_symptom_relations()
print("Database tables initialized successfully") print("Database tables initialized successfully")
return True
except SQLAlchemyError as e: except SQLAlchemyError as e:
print(f"Error initializing database tables: {e}") print(f"Error initializing database tables: {e}")
raise raise
@ -299,6 +321,9 @@ def init_mysql_tables():
def migrate_diagnosis_history_schema(): def migrate_diagnosis_history_schema():
"""Remove legacy diagnosis_history columns that are no longer used.""" """Remove legacy diagnosis_history columns that are no longer used."""
if engine is None:
return
try: try:
inspector = inspect(engine) inspector = inspect(engine)
table_names = inspector.get_table_names() table_names = inspector.get_table_names()
@ -318,6 +343,9 @@ def migrate_diagnosis_history_schema():
def migrate_expert_rule_disease_fk(): def migrate_expert_rule_disease_fk():
"""Add the missing foreign key from expert_rules.result_disease_code to expert_diseases.code.""" """Add the missing foreign key from expert_rules.result_disease_code to expert_diseases.code."""
if engine is None:
return
try: try:
inspector = inspect(engine) inspector = inspect(engine)
if 'expert_rules' not in inspector.get_table_names() or 'expert_diseases' not in inspector.get_table_names(): if 'expert_rules' not in inspector.get_table_names() or 'expert_diseases' not in inspector.get_table_names():
@ -342,6 +370,9 @@ def migrate_expert_rule_disease_fk():
def sync_expert_rule_symptom_relations(): def sync_expert_rule_symptom_relations():
"""Backfill the expert_rules_expert_symptoms join table from stored symptom codes.""" """Backfill the expert_rules_expert_symptoms join table from stored symptom codes."""
if engine is None:
return
session = Session() session = Session()
try: try:
session.execute(expert_rules_expert_symptoms.delete()) session.execute(expert_rules_expert_symptoms.delete())
@ -377,6 +408,14 @@ def sync_expert_rule_symptom_relations():
def seed_expert_knowledge(force=False): def seed_expert_knowledge(force=False):
"""Isi data default gejala, penyakit, dan aturan sistem pakar.""" """Isi data default gejala, penyakit, dan aturan sistem pakar."""
if engine is None:
return {
'seeded': False,
'symptoms': 0,
'diseases': 0,
'rules': 0,
}
session = Session() session = Session()
try: try:
existing_symptoms = session.query(ExpertSymptom).count() existing_symptoms = session.query(ExpertSymptom).count()
@ -440,6 +479,9 @@ def seed_expert_knowledge(force=False):
def get_expert_knowledge_mysql(): def get_expert_knowledge_mysql():
"""Ambil knowledge base sistem pakar (gejala, penyakit, aturan) dari MySQL.""" """Ambil knowledge base sistem pakar (gejala, penyakit, aturan) dari MySQL."""
if engine is None:
return None
session = Session() session = Session()
try: try:
symptoms = session.query(ExpertSymptom).order_by(ExpertSymptom.display_order.asc(), ExpertSymptom.code.asc()).all() symptoms = session.query(ExpertSymptom).order_by(ExpertSymptom.display_order.asc(), ExpertSymptom.code.asc()).all()