hr-eval-api-v2 / database /config_manager.py
KarenYYH
Initial commit - HR Evaluation API v2
c8b1f17
import json
import time
from typing import Dict, List, Any, Optional
from sqlalchemy.orm import Session
from .models import ConfigItem, SensitiveWord, WhitelistItem, SessionLocal
class ConfigManager:
_instance = None
_cache = {}
_last_refresh = 0
CACHE_TTL = 60 # 1 minute cache
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._refresh_cache_if_needed(force=True)
return cls._instance
def get_db(self):
db = SessionLocal()
try:
yield db
finally:
db.close()
def _refresh_cache_if_needed(self, force=False):
if force or time.time() - self._last_refresh > self.CACHE_TTL:
self.refresh_cache()
def refresh_cache(self):
# print("Refreshing config cache from DB...")
db = SessionLocal()
try:
# 1. Refresh Sensitive Words
words = db.query(SensitiveWord).filter(SensitiveWord.is_active == True).all()
sensitive_dict = {}
# Reconstruct nested structure: category -> subcategory -> list of words
for w in words:
if w.category not in sensitive_dict:
sensitive_dict[w.category] = {}
# If subcategory is empty/null, use 'default' or handle appropriately
# Original JSON structure: "discrimination": {"age": [...], "gender": [...]}
sub = w.subcategory if w.subcategory else "general"
if sub not in sensitive_dict[w.category]:
sensitive_dict[w.category][sub] = []
sensitive_dict[w.category][sub].append(w.word)
# 2. Refresh Whitelist
whitelist = db.query(WhitelistItem).filter(WhitelistItem.is_active == True).all()
whitelist_list = [w.word for w in whitelist]
# 3. Refresh Configs
configs = db.query(ConfigItem).filter(ConfigItem.is_active == True).all()
config_dict = {}
for c in configs:
try:
if c.type in ['json', 'list', 'dict', 'bool']:
val = json.loads(c.value)
elif c.type == 'int':
val = int(c.value)
elif c.type == 'float':
val = float(c.value)
else:
val = c.value
config_dict[c.key] = val
except Exception as e:
print(f"Error parsing config {c.key}: {e}")
config_dict[c.key] = c.value
self._cache = {
"sensitive_words": sensitive_dict,
"whitelist": whitelist_list,
"configs": config_dict
}
self._last_refresh = time.time()
except Exception as e:
# Silently handle database not initialized yet (first startup)
# Tables will be created in main.py lifespan
if "no such table" not in str(e):
print(f"Error refreshing cache: {e}")
finally:
db.close()
def get_sensitive_words(self) -> Dict:
self._refresh_cache_if_needed()
return self._cache.get("sensitive_words", {})
def get_whitelist(self) -> List[str]:
self._refresh_cache_if_needed()
return self._cache.get("whitelist", [])
def get_config(self, key: str, default: Any = None) -> Any:
self._refresh_cache_if_needed()
if key == "all":
return self._cache.get("configs", {})
return self._cache.get("configs", {}).get(key, default)
def get_all_configs(self) -> Dict:
self._refresh_cache_if_needed()
return self._cache.get("configs", {})
# CRUD Operations (Direct DB access, clears cache)
def add_sensitive_word(self, word: str, category: str, subcategory: Optional[str] = None, severity: str = 'medium'):
db = SessionLocal()
try:
# Check if exists to update or add
existing = db.query(SensitiveWord).filter(
SensitiveWord.word == word,
SensitiveWord.category == category,
SensitiveWord.subcategory == subcategory
).first()
if existing:
existing.severity = severity
existing.is_active = True
else:
item = SensitiveWord(word=word, category=category, subcategory=subcategory, severity=severity)
db.add(item)
db.commit()
self._last_refresh = 0 # Invalidate cache
return True
except Exception as e:
db.rollback()
print(f"Error adding sensitive word: {e}")
return False
finally:
db.close()
def add_whitelist_item(self, word: str, category: str = 'general'):
db = SessionLocal()
try:
existing = db.query(WhitelistItem).filter(WhitelistItem.word == word).first()
if existing:
existing.is_active = True
else:
item = WhitelistItem(word=word, category=category)
db.add(item)
db.commit()
self._last_refresh = 0
return True
except Exception as e:
db.rollback()
print(f"Error adding whitelist item: {e}")
return False
finally:
db.close()
def remove_sensitive_word(self, word: str) -> bool:
db = SessionLocal()
try:
item = db.query(SensitiveWord).filter(SensitiveWord.word == word).first()
if item:
item.is_active = False # Soft delete
db.commit()
self._last_refresh = 0
return True
return False
except Exception as e:
db.rollback()
return False
finally:
db.close()
def update_config(self, key: str, value: Any, type_: str = 'string', group: str = 'general') -> bool:
db = SessionLocal()
try:
str_val = value
if type_ in ['json', 'list', 'dict', 'bool']:
str_val = json.dumps(value, ensure_ascii=False)
elif type_ in ['int', 'float']:
str_val = str(value)
item = db.query(ConfigItem).filter(ConfigItem.key == key).first()
if item:
item.value = str_val
item.type = type_
# item.group_name = group # Optional: update group
else:
item = ConfigItem(key=key, value=str_val, type=type_, group_name=group)
db.add(item)
db.commit()
self._last_refresh = 0
return True
except Exception as e:
db.rollback()
print(f"Error updating config: {e}")
return False
finally:
db.close()
config_manager = ConfigManager()