# db_schema.py import os import yaml from sqlalchemy import Table, Column, Date, Integer, String, Float, Text, MetaData, UniqueConstraint, DateTime, Time, PrimaryKeyConstraint, Index from sqlalchemy.sql import func BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) CONFIG_PATH = os.path.join(BASE_DIR, 'conf', 'config.yaml') with open(CONFIG_PATH, 'r', encoding='utf-8') as f: cfg = yaml.safe_load(f) table_prefix = cfg.get('table_prefix', '') tables = cfg.get('tables', {}) metadata = MetaData() default_mysql_opts = dict(mysql_engine='InnoDB', mysql_charset='utf8mb4') def get_full_table_name(key): # config.yaml에서 tables: 항목의 실제 테이블명 가져오기 name_in_cfg = tables.get(key, '') if name_in_cfg: # 이미 prefix 포함된 이름일 수 있으니 확인 if name_in_cfg.startswith(table_prefix): return name_in_cfg else: return table_prefix + name_in_cfg else: # 기본은 prefix + key return table_prefix + key # 테이블 변수명은 prefix 제외한 key를 그대로 사용 ga4_by_date = Table( get_full_table_name('ga4_by_date'), metadata, Column('date', Date, primary_key=True), Column('activeUsers', Integer), Column('screenPageViews', Integer), Column('sessions', Integer), ) ga4_by_source = Table( get_full_table_name('ga4_by_source'), metadata, Column('date', Date, primary_key=True), Column('sessionSource', String(255), primary_key=True), Column('sessions', Integer), ) ga4_by_medium = Table( get_full_table_name('ga4_by_medium'), metadata, Column('date', Date, primary_key=True), Column('sessionMedium', String(255), primary_key=True), Column('sessions', Integer), ) ga4_by_device = Table( get_full_table_name('ga4_by_device'), metadata, Column('date', Date, primary_key=True), Column('deviceCategory', String(255), primary_key=True), Column('activeUsers', Integer), ) ga4_by_country = Table( get_full_table_name('ga4_by_country'), metadata, Column('date', Date, primary_key=True), Column('country', String(255), primary_key=True), Column('activeUsers', Integer), ) ga4_by_city = Table( get_full_table_name('ga4_by_city'), metadata, Column('date', Date, primary_key=True), Column('city', String(255), primary_key=True), Column('activeUsers', Integer), ) air = Table( get_full_table_name('air'), metadata, Column('date', Date, primary_key=True, nullable=False), Column('station', String(32), nullable=False), Column('pm25', Float), Column('pm10', Float), Column('so2', Float), Column('co', Float), Column('no2', Float), Column('o3', Float), ) weather = Table( get_full_table_name('weather'), metadata, Column('date', Date, primary_key=True, nullable=False), Column('stnId', Integer, nullable=False), Column('avgTa', Float), Column('minTa', Float), Column('minTaHrmt', String(4)), Column('maxTa', Float), Column('maxTaHrmt', String(4)), Column('sumRnDur', Float), Column('mi10MaxRn', Float), Column('mi10MaxRnHrmt', String(4)), Column('hr1MaxRn', Float), Column('hr1MaxRnHrmt', String(4)), Column('sumRn', Float), Column('maxInsWs', Float), Column('maxInsWsWd', Integer), Column('maxInsWsHrmt', String(4)), Column('maxWs', Float), Column('maxWsWd', Integer), Column('maxWsHrmt', String(4)), Column('avgWs', Float), Column('hr24SumRws', Float), Column('maxWd', Integer), Column('avgTd', Float), Column('minRhm', Float), Column('minRhmHrmt', String(4)), Column('avgRhm', Float), Column('avgPv', Float), Column('avgPa', Float), Column('maxPs', Float), Column('maxPsHrmt', String(4)), Column('minPs', Float), Column('minPsHrmt', String(4)), Column('avgPs', Float), Column('ssDur', Float), Column('sumSsHr', Float), Column('hr1MaxIcsrHrmt', String(4)), Column('hr1MaxIcsr', Float), Column('sumGsr', Float), Column('ddMefs', Float), Column('ddMefsHrmt', String(4)), Column('ddMes', Float), Column('ddMesHrmt', String(4)), Column('sumDpthFhsc', Float), Column('avgTca', Float), Column('avgLmac', Float), Column('avgTs', Float), Column('minTg', Float), Column('avgCm5Te', Float), Column('avgCm10Te', Float), Column('avgCm20Te', Float), Column('avgCm30Te', Float), Column('avgM05Te', Float), Column('avgM10Te', Float), Column('avgM15Te', Float), Column('avgM30Te', Float), Column('avgM50Te', Float), Column('sumLrgEv', Float), Column('sumSmlEv', Float), Column('n99Rn', Float), Column('iscs', Text), Column('sumFogDur', Float), ) ga4 = Table( get_full_table_name('ga4'), metadata, Column('date', Date, primary_key=True), Column('source', String(255), primary_key=True), Column('medium', String(255), primary_key=True), Column('deviceCategory', String(50), primary_key=True), Column('country', String(100), primary_key=True), Column('city', String(100), primary_key=True), Column('activeUsers', Integer), Column('screenPageViews', Integer), mysql_engine='InnoDB', mysql_charset='utf8mb4' ) holiday = Table( get_full_table_name('holiday'), metadata, Column('date', String(8), primary_key=True, comment='날짜 (YYYYMMDD)'), Column('name', String(50), nullable=False, comment='휴일명'), Column('created_at', DateTime, server_default=func.now(), comment='등록일시'), Column('updated_at', DateTime, server_default=func.now(), onupdate=func.now(), comment='수정일시'), comment='한국천문연구원 특일정보' ) pos = Table( get_full_table_name('pos'), metadata, Column('idx', Integer, primary_key=True, autoincrement=True), Column('date', Date, nullable=False), Column('ca01', String(50), nullable=False), Column('ca02', String(50), nullable=False), Column('ca03', String(50), nullable=False), Column('barcode', Integer, nullable=False), Column('name', String(100), nullable=False), Column('qty', Integer, nullable=False), Column('tot_amount', Integer, nullable=False), Column('tot_discount', Integer, nullable=False), Column('actual_amount', Integer, nullable=False), UniqueConstraint('date', 'ca01', 'ca02', 'ca03', 'name', 'barcode', name='uniq_pos_composite') ) pos_billdata = Table( get_full_table_name('pos_billdata'), metadata, Column('sale_date', Date, nullable=False), Column('shop_cd', String(20), nullable=False), Column('pos_no', Integer, nullable=False), Column('bill_no', Integer, nullable=False), Column('product_cd', String(20), nullable=False), Column('division', String(10)), Column('table_no', String(20)), Column('order_time', Time), Column('pay_time', Time), Column('barcode', String(20)), Column('product_name', String(100)), Column('qty', Integer), Column('tot_sale_amt', Integer), Column('erp_cd', String(50)), Column('remark', Text), Column('dc_amt', Integer), Column('dc_type', String(50)), Column('dcm_sale_amt', Integer), Column('net_amt', Integer), Column('vat_amt', Integer), PrimaryKeyConstraint('sale_date', 'shop_cd', 'pos_no', 'bill_no', 'product_cd') ) pos_ups_billdata = Table( get_full_table_name('pos_ups_billdata'), metadata, Column('sale_date', DateTime, nullable=False), Column('shop_name', String(100), nullable=False), Column('pos_no', String(20), nullable=False), Column('bill_no', String(20), nullable=False), Column('product_cd', String(20), nullable=False), Column('ca01', String(50)), Column('ca02', String(50)), Column('ca03', String(50)), Column('product_name', String(100)), Column('barcode', String(20)), Column('amt', Integer), Column('qty', Integer), Column('tot_sale_amt', Integer), Column('dc_amt', Integer), Column('dcm_sale_amt', Integer), Column('net_amt', Integer), Column('vat_amt', Integer), Column('cash_receipt', Integer), Column('card', Integer), # PrimaryKeyConstraint 생략 mysql_engine='InnoDB', mysql_charset='utf8mb4' ) # 인덱스 추가 Index('idx_sale_shop_pos_product', pos_ups_billdata.c.sale_date, pos_ups_billdata.c.shop_name, pos_ups_billdata.c.pos_no, pos_ups_billdata.c.product_cd) Index('idx_category', pos_ups_billdata.c.ca01, pos_ups_billdata.c.ca02, pos_ups_billdata.c.ca03) Index('idx_product_barcode', pos_ups_billdata.c.product_name, pos_ups_billdata.c.barcode) pos_shop_name = Table( get_full_table_name('pos_shop_name'), metadata, Column('shop_cd', String(20), primary_key=True, nullable=False), Column('shop_name', String(100), nullable=False), Column('used', Integer, nullable=False, default=1, comment='사용여부 (1=사용, 0=미사용)'), Column('created_at', DateTime, server_default=func.current_timestamp(), comment='등록일시'), Column('updated_at', DateTime, server_default=func.current_timestamp(), onupdate=func.current_timestamp(), comment='수정일시'), mysql_engine='InnoDB', mysql_charset='utf8mb4', )