from datetime import date from enum import Enum from typing import Optional, Dict, Any, List from pydantic import BaseModel, Field from .schemas import ( EntryType, FlowIntensity, MoodType, CalendarEntryCreate, CalendarEntryResponse ) class MobileFlowIntensity(int, Enum): """Flow intensity values used in the mobile app (1-5)""" SPOTTING = 1 LIGHT = 2 MEDIUM = 3 HEAVY = 4 VERY_HEAVY = 5 @classmethod def to_server_intensity(cls, value: int) -> Optional[str]: """Convert mobile app intensity value to server format""" if value is None: return None mapping = { cls.SPOTTING.value: FlowIntensity.SPOTTING.value, cls.LIGHT.value: FlowIntensity.LIGHT.value, cls.MEDIUM.value: FlowIntensity.MEDIUM.value, cls.HEAVY.value: FlowIntensity.HEAVY.value, cls.VERY_HEAVY.value: FlowIntensity.HEAVY.value, # Server has no "very heavy" } return mapping.get(value) @classmethod def from_server_intensity(cls, value: Optional[str]) -> Optional[int]: """Convert server intensity value to mobile app format""" if value is None: return None mapping = { FlowIntensity.SPOTTING.value: cls.SPOTTING.value, FlowIntensity.LIGHT.value: cls.LIGHT.value, FlowIntensity.MEDIUM.value: cls.MEDIUM.value, FlowIntensity.HEAVY.value: cls.HEAVY.value, } return mapping.get(value) class MobileMood(str, Enum): """Mood values used in the mobile app""" HAPPY = "HAPPY" SAD = "SAD" ANXIOUS = "ANXIOUS" IRRITABLE = "IRRITABLE" ENERGETIC = "ENERGETIC" TIRED = "TIRED" @classmethod def to_server_mood(cls, value: str) -> Optional[MoodType]: """Convert mobile app mood value to server format""" if value is None: return None mapping = { cls.HAPPY: MoodType.HAPPY, cls.SAD: MoodType.SAD, cls.ANXIOUS: MoodType.ANXIOUS, cls.IRRITABLE: MoodType.IRRITATED, # Different spelling cls.ENERGETIC: MoodType.ENERGETIC, cls.TIRED: MoodType.TIRED, } return mapping.get(MobileMood(value)) @classmethod def from_server_mood(cls, value: Optional[str]) -> Optional[str]: """Convert server mood value to mobile app format""" if value is None: return None # Convert string value to MoodType enum try: mood_type = MoodType(value) except ValueError: return None mapping = { MoodType.HAPPY: cls.HAPPY, MoodType.SAD: cls.SAD, MoodType.ANXIOUS: cls.ANXIOUS, MoodType.IRRITATED: cls.IRRITABLE, # Different spelling MoodType.ENERGETIC: cls.ENERGETIC, MoodType.TIRED: cls.TIRED, } return mapping.get(mood_type) class MobileAppCalendarEntryCreate(BaseModel): """Schema for creating calendar entries from mobile app""" entry_date: date entry_type: str # Will be mapped to EntryType flow_intensity: Optional[int] = None # 1-5 scale symptoms: Optional[str] = None mood: Optional[str] = None # Mobile mood enum as string notes: Optional[str] = None class MobileAppCalendarEntryResponse(BaseModel): """Schema for calendar entry responses to mobile app""" id: int user_id: int entry_date: date entry_type: str flow_intensity: Optional[int] = None symptoms: Optional[str] = None mood: Optional[str] = None notes: Optional[str] = None created_at: date updated_at: date def convert_mobile_app_format_to_server(mobile_entry: MobileAppCalendarEntryCreate) -> CalendarEntryCreate: """Convert mobile app format to server format""" # Map entry type try: entry_type = EntryType(mobile_entry.entry_type.lower()) except ValueError: # Handle case where entry type doesn't match directly if mobile_entry.entry_type == "MENSTRUATION": entry_type = EntryType.PERIOD elif mobile_entry.entry_type == "SPOTTING": entry_type = EntryType.PERIOD else: # Default to symptoms if no direct mapping entry_type = EntryType.SYMPTOMS # Map flow intensity (1-5 scale to text values) flow_intensity_str = None if mobile_entry.flow_intensity is not None: flow_intensity_str = MobileFlowIntensity.to_server_intensity(mobile_entry.flow_intensity) # Map mood mood_str = None if mobile_entry.mood is not None: try: mobile_mood = MobileMood(mobile_entry.mood) mood_type = MobileMood.to_server_mood(mobile_entry.mood) if mood_type: mood_str = mood_type.value except ValueError: # If mood doesn't match any enum value, leave it as None pass return CalendarEntryCreate( entry_date=mobile_entry.entry_date, entry_type=entry_type, flow_intensity=flow_intensity_str, symptoms=mobile_entry.symptoms or "", mood=mood_str, notes=mobile_entry.notes or "", # Default values for server required fields that mobile app doesn't provide period_symptoms="", energy_level=0, sleep_hours=0, medications="", ) def convert_server_response_to_mobile_app( server_response: CalendarEntryResponse ) -> MobileAppCalendarEntryResponse: """Convert server response to mobile app format""" # Map flow intensity back to 1-5 scale flow_intensity = None if server_response.flow_intensity is not None: flow_intensity = MobileFlowIntensity.from_server_intensity(server_response.flow_intensity) # Map mood back to mobile format mood = None if server_response.mood is not None: mood = MobileMood.from_server_mood(server_response.mood) # Upper case the entry type entry_type = server_response.entry_type.upper() if server_response.entry_type else "" return MobileAppCalendarEntryResponse( id=server_response.id, user_id=getattr(server_response, "user_id", 0), # Handle if field doesn't exist entry_date=server_response.entry_date, entry_type=entry_type, flow_intensity=flow_intensity, symptoms=server_response.symptoms, mood=mood, notes=server_response.notes, created_at=getattr(server_response, "created_at", date.today()), updated_at=getattr(server_response, "created_at", date.today()), # Fallback to created_at )