import gspread import os import json from google.oauth2.service_account import Credentials from datetime import datetime FIELDNAMES = [ "timestamp", "face_ratio", "jaw_ratio", "jaw_to_height", "eye_ratio", "eye_height", "lip_ratio", "nose_position", "lower_face_ratio", "chin_prominence", "symmetry", "upper_third", "middle_third", "lower_third", "mid_lower_ratio", "thirds_balance", "quality_score", "rec_1", "rec_2", "rec_3", "rating", "comment", ] VOTE_FIELDS = [ "timestamp", "style_name", "vote", "face_ratio", "jaw_ratio", "jaw_to_height", "eye_ratio", "eye_height", "lip_ratio", "nose_position", "lower_face_ratio", "chin_prominence", "symmetry", "upper_third", "middle_third", "lower_third", "mid_lower_ratio", "thirds_balance", "gender" ] def _get_sheet(sheet_name): creds_json = os.environ.get("GCP_SERVICE_ACCOUNT") spreadsheet_id = os.environ.get("SPREADSHEET_ID") if not creds_json: raise RuntimeError("GCP_SERVICE_ACCOUNT env var not set") if not spreadsheet_id: raise RuntimeError("SPREADSHEET_ID env var not set") creds_dict = json.loads(creds_json) creds = Credentials.from_service_account_info( creds_dict, scopes=["https://www.googleapis.com/auth/spreadsheets"], ) client = gspread.authorize(creds) spreadsheet = client.open_by_key(spreadsheet_id) try: return spreadsheet.worksheet(sheet_name) except gspread.WorksheetNotFound: return spreadsheet.add_worksheet(sheet_name, rows=1000, cols=20) def _ensure_header(sheet, headers): existing = sheet.row_values(1) if existing and existing[0] != "timestamp": return if not existing: sheet.insert_row(headers, 1) def save_session(features, quality_score, recs, rating=None, comment=""): try: sheet = _get_sheet("feedback") _ensure_header(sheet, FIELDNAMES) top_styles = recs["top_styles"] row = [ datetime.now().isoformat(), round(features.get("face_ratio", 0), 4), round(features.get("jaw_ratio", 0), 4), round(features.get("jaw_to_height", 0), 4), round(features.get("eye_ratio", 0), 4), round(features.get("eye_height", 0), 4), round(features.get("lip_ratio", 0), 4), round(features.get("nose_position", 0), 4), round(features.get("lower_face_ratio", 0), 4), round(features.get("chin_prominence", 0), 4), round(features.get("symmetry", 0), 4), round(features.get("upper_third", 0), 4), round(features.get("middle_third", 0), 4), round(features.get("lower_third", 0), 4), round(features.get("mid_lower_ratio", 0), 4), round(features.get("thirds_balance", 0), 4), round(quality_score, 4), top_styles[0]["name"] if len(top_styles) > 0 else "", top_styles[1]["name"] if len(top_styles) > 1 else "", top_styles[2]["name"] if len(top_styles) > 2 else "", rating or "", comment, ] sheet.append_row(row) except Exception as e: print(f"Google Sheets save_session error: {repr(e)}") raise def save_vote(style_name: str, vote: str, features: dict, gender: str = ""): try: sheet = _get_sheet("votes") _ensure_header(sheet, VOTE_FIELDS) row = [ datetime.now().isoformat(), style_name, vote, round(features.get("face_ratio", 0), 4), round(features.get("jaw_ratio", 0), 4), round(features.get("jaw_to_height", 0), 4), round(features.get("eye_ratio", 0), 4), round(features.get("eye_height", 0), 4), round(features.get("lip_ratio", 0), 4), round(features.get("nose_position", 0), 4), round(features.get("lower_face_ratio", 0), 4), round(features.get("chin_prominence", 0), 4), round(features.get("symmetry", 0), 4), round(features.get("upper_third", 0), 4), round(features.get("middle_third", 0), 4), round(features.get("lower_third", 0), 4), round(features.get("mid_lower_ratio", 0), 4), round(features.get("thirds_balance", 0), 4), gender, ] sheet.append_row(row) except Exception as e: print(f"Google Sheets save_vote error: {repr(e)}") raise def load_feedback(): try: sheet = _get_sheet("feedback") return sheet.get_all_records() except Exception: return []