calendar
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-09-26 14:00:59 +09:00
parent 7651c01245
commit 0724018895
2 changed files with 107 additions and 98 deletions

View File

@@ -771,5 +771,7 @@ app.openapi = custom_openapi
if __name__ == "__main__":
import uvicorn
import os
uvicorn.run(app, host="0.0.0.0", port=8000)
port = int(os.environ.get("PORT", 8000))
uvicorn.run(app, host="0.0.0.0", port=port)

View File

@@ -1,16 +1,17 @@
from datetime import date, datetime, timedelta
from enum import Enum
from typing import List, Optional
from typing import Dict, List, Optional
from fastapi import Depends, FastAPI, HTTPException, Query
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field
from pydantic import BaseModel
from sqlalchemy import and_, desc, select
from sqlalchemy.ext.asyncio import AsyncSession
from services.calendar_service.models import CalendarEntry, CycleData, HealthInsights
from services.user_service.main import get_current_user
from services.user_service.models import User
from services.calendar_service.schemas import (CalendarEntryCreate, CalendarEntryResponse,
CycleDataResponse, CycleOverview, EntryType,
FlowIntensity, HealthInsightResponse, MoodType)
from shared.auth import get_current_user_from_token as get_current_user
from shared.config import settings
from shared.database import get_db
@@ -32,66 +33,6 @@ async def health_check():
return {"status": "healthy", "service": "calendar_service"}
class EntryType(str, Enum):
PERIOD = "period"
OVULATION = "ovulation"
SYMPTOMS = "symptoms"
MEDICATION = "medication"
MOOD = "mood"
EXERCISE = "exercise"
APPOINTMENT = "appointment"
class FlowIntensity(str, Enum):
LIGHT = "light"
MEDIUM = "medium"
HEAVY = "heavy"
SPOTTING = "spotting"
class MoodType(str, Enum):
HAPPY = "happy"
SAD = "sad"
ANXIOUS = "anxious"
IRRITATED = "irritated"
ENERGETIC = "energetic"
TIRED = "tired"
class CalendarEntryCreate(BaseModel):
entry_date: date
entry_type: EntryType
flow_intensity: Optional[FlowIntensity] = None
period_symptoms: Optional[str] = Field(None, max_length=500)
mood: Optional[MoodType] = None
energy_level: Optional[int] = Field(None, ge=1, le=5)
sleep_hours: Optional[int] = Field(None, ge=0, le=24)
symptoms: Optional[str] = Field(None, max_length=1000)
medications: Optional[str] = Field(None, max_length=500)
notes: Optional[str] = Field(None, max_length=1000)
class CalendarEntryResponse(BaseModel):
id: int
uuid: str
entry_date: date
entry_type: str
flow_intensity: Optional[str]
period_symptoms: Optional[str]
mood: Optional[str]
energy_level: Optional[int]
sleep_hours: Optional[int]
symptoms: Optional[str]
medications: Optional[str]
notes: Optional[str]
is_predicted: bool
confidence_score: Optional[int]
created_at: datetime
class Config:
from_attributes = True
class CycleDataResponse(BaseModel):
id: int
cycle_start_date: date
@@ -186,7 +127,7 @@ async def calculate_predictions(user_id: int, db: AsyncSession):
@app.post("/api/v1/entries", response_model=CalendarEntryResponse)
async def create_calendar_entry(
entry_data: CalendarEntryCreate,
current_user: User = Depends(get_current_user),
current_user: Dict = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""Create new calendar entry"""
@@ -195,7 +136,7 @@ async def create_calendar_entry(
existing = await db.execute(
select(CalendarEntry).filter(
and_(
CalendarEntry.user_id == current_user.id,
CalendarEntry.user_id == current_user["user_id"],
CalendarEntry.entry_date == entry_data.entry_date,
CalendarEntry.entry_type == entry_data.entry_type.value,
)
@@ -206,29 +147,37 @@ async def create_calendar_entry(
status_code=400, detail="Entry already exists for this date and type"
)
db_entry = CalendarEntry(
user_id=current_user.id,
entry_date=entry_data.entry_date,
entry_type=entry_data.entry_type.value,
flow_intensity=entry_data.flow_intensity.value
if entry_data.flow_intensity
else None,
period_symptoms=entry_data.period_symptoms,
mood=entry_data.mood.value if entry_data.mood else None,
energy_level=entry_data.energy_level,
sleep_hours=entry_data.sleep_hours,
symptoms=entry_data.symptoms,
medications=entry_data.medications,
notes=entry_data.notes,
)
try:
db_entry = CalendarEntry(
user_id=current_user["user_id"],
entry_date=entry_data.entry_date,
entry_type=entry_data.entry_type.value,
flow_intensity=entry_data.flow_intensity.value
if entry_data.flow_intensity
else None,
period_symptoms=entry_data.period_symptoms,
mood=entry_data.mood.value if entry_data.mood else None,
energy_level=entry_data.energy_level,
sleep_hours=entry_data.sleep_hours,
symptoms=entry_data.symptoms,
medications=entry_data.medications,
notes=entry_data.notes,
)
db.add(db_entry)
await db.commit()
await db.refresh(db_entry)
db.add(db_entry)
await db.commit()
await db.refresh(db_entry)
import logging
logging.info(f"Created calendar entry: {db_entry.id}")
except Exception as e:
import logging
logging.error(f"Error creating calendar entry: {str(e)}")
raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
# If this is a period entry, update cycle data
if entry_data.entry_type == EntryType.PERIOD:
await update_cycle_data(current_user.id, entry_data.entry_date, db)
await update_cycle_data(current_user["user_id"], entry_data.entry_date, db)
return CalendarEntryResponse.model_validate(db_entry)
@@ -269,7 +218,7 @@ async def update_cycle_data(user_id: int, period_date: date, db: AsyncSession):
@app.get("/api/v1/entries", response_model=List[CalendarEntryResponse])
async def get_calendar_entries(
current_user: User = Depends(get_current_user),
current_user: Dict = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
start_date: Optional[date] = Query(None),
end_date: Optional[date] = Query(None),
@@ -278,7 +227,7 @@ async def get_calendar_entries(
):
"""Get calendar entries with optional filtering"""
query = select(CalendarEntry).filter(CalendarEntry.user_id == current_user.id)
query = select(CalendarEntry).filter(CalendarEntry.user_id == current_user["user_id"])
if start_date:
query = query.filter(CalendarEntry.entry_date >= start_date)
@@ -297,14 +246,14 @@ async def get_calendar_entries(
@app.get("/api/v1/cycle-overview", response_model=CycleOverview)
async def get_cycle_overview(
current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)
current_user: Dict = Depends(get_current_user), db: AsyncSession = Depends(get_db)
):
"""Get current cycle overview and predictions"""
# Get current cycle
current_cycle = await db.execute(
select(CycleData)
.filter(CycleData.user_id == current_user.id)
.filter(CycleData.user_id == current_user["user_id"])
.order_by(desc(CycleData.cycle_start_date))
.limit(1)
)
@@ -338,7 +287,7 @@ async def get_cycle_overview(
# Calculate regularity
cycles = await db.execute(
select(CycleData)
.filter(CycleData.user_id == current_user.id)
.filter(CycleData.user_id == current_user["user_id"])
.order_by(desc(CycleData.cycle_start_date))
.limit(6)
)
@@ -370,7 +319,7 @@ async def get_cycle_overview(
@app.get("/api/v1/insights", response_model=List[HealthInsightResponse])
async def get_health_insights(
current_user: User = Depends(get_current_user),
current_user: Dict = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
limit: int = Query(10, ge=1, le=50),
):
@@ -379,7 +328,7 @@ async def get_health_insights(
result = await db.execute(
select(HealthInsights)
.filter(
HealthInsights.user_id == current_user.id,
HealthInsights.user_id == current_user["user_id"],
HealthInsights.is_dismissed == False,
)
.order_by(desc(HealthInsights.created_at))
@@ -395,13 +344,13 @@ async def get_all_calendar_entries(
start_date: Optional[date] = None,
end_date: Optional[date] = None,
entry_type: Optional[EntryType] = None,
current_user: User = Depends(get_current_user),
current_user: Dict = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
limit: int = Query(100, ge=1, le=500),
):
"""Get all calendar entries for the current user"""
query = select(CalendarEntry).filter(CalendarEntry.user_id == current_user.id)
query = select(CalendarEntry).filter(CalendarEntry.user_id == current_user["user_id"])
if start_date:
query = query.filter(CalendarEntry.entry_date >= start_date)
@@ -418,17 +367,75 @@ async def get_all_calendar_entries(
return [CalendarEntryResponse.model_validate(entry) for entry in entries]
@app.post("/api/v1/calendar/entries", response_model=CalendarEntryResponse, status_code=201)
async def create_calendar_entry(
entry_data: CalendarEntryCreate,
current_user: Dict = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""Create a new calendar entry"""
# Debug prints
import logging
logging.info(f"Current user: {current_user}")
logging.info(f"Entry data: {entry_data}")
try:
# Check if entry already exists for this date and type
existing = await db.execute(
select(CalendarEntry).filter(
and_(
CalendarEntry.user_id == current_user["user_id"],
CalendarEntry.entry_date == entry_data.entry_date,
CalendarEntry.entry_type == entry_data.entry_type.value,
)
)
)
if existing.scalars().first():
raise HTTPException(
status_code=400, detail="Entry already exists for this date and type"
)
except Exception as e:
logging.error(f"Error checking for existing entry: {str(e)}")
raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
# Create new calendar entry
new_entry = CalendarEntry(
user_id=current_user["user_id"],
entry_date=entry_data.entry_date,
entry_type=entry_data.entry_type.value,
flow_intensity=entry_data.flow_intensity.value if entry_data.flow_intensity else None,
period_symptoms=entry_data.period_symptoms,
mood=entry_data.mood.value if entry_data.mood else None,
energy_level=entry_data.energy_level,
sleep_hours=entry_data.sleep_hours,
symptoms=entry_data.symptoms,
medications=entry_data.medications,
notes=entry_data.notes,
)
db.add(new_entry)
await db.commit()
await db.refresh(new_entry)
# If this is a period entry, update cycle data
if entry_data.entry_type == EntryType.PERIOD:
await update_cycle_data(current_user["user_id"], entry_data.entry_date, db)
return CalendarEntryResponse.model_validate(new_entry)
@app.delete("/api/v1/entries/{entry_id}")
async def delete_calendar_entry(
entry_id: int,
current_user: User = Depends(get_current_user),
current_user: Dict = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""Delete calendar entry"""
result = await db.execute(
select(CalendarEntry).filter(
and_(CalendarEntry.id == entry_id, CalendarEntry.user_id == current_user.id)
and_(CalendarEntry.id == entry_id, CalendarEntry.user_id == current_user["user_id"])
)
)
entry = result.scalars().first()