brestok commited on
Commit
a7ed1bd
·
1 Parent(s): e090583
cbh/api/account/dto.py CHANGED
@@ -29,6 +29,6 @@ class CoachOpportunity(Enum):
29
  Coach opportunity.
30
  """
31
 
32
- PRIVATE = 1
33
  BUSINESS = 2
34
  BOTH = 3
 
29
  Coach opportunity.
30
  """
31
 
32
+ GENERAL = 1
33
  BUSINESS = 2
34
  BOTH = 3
cbh/api/availability/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
  from fastapi import APIRouter
2
 
3
- availability_router = APIRouter(prefix="/availability", tags=["availability"])
4
 
5
  from . import views
 
1
  from fastapi import APIRouter
2
 
3
+ availability_router = APIRouter(prefix="/api/availability", tags=["availability"])
4
 
5
  from . import views
cbh/api/availability/db_requests.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from cbh.api.availability.schemas import UpdateAvailabilityRequest
2
+ from cbh.api.availability.models import AvailabilityModel
3
+ from cbh.api.common.db_requests import get_obj_by_id
4
+ from cbh.core.config import settings
5
+
6
+
7
+ async def update_availability_obj(
8
+ availability: UpdateAvailabilityRequest, coach_id: str
9
+ ) -> AvailabilityModel:
10
+ """
11
+ Update availability object.
12
+ """
13
+ availability_obj = await get_obj_by_id(
14
+ AvailabilityModel, None, additional_filter={"coach.id": coach_id}
15
+ )
16
+ availability_obj.weeklySchedule = availability.weeklySchedule
17
+ await settings.DB_CLIENT.availabilities.update_one(
18
+ {"coach.id": coach_id},
19
+ {"$set": availability_obj.to_mongo()},
20
+ )
21
+ return availability_obj
cbh/api/availability/models.py CHANGED
@@ -1,6 +1,5 @@
1
  from cbh.api.account.models import AccountShorten
2
  from cbh.api.availability.dto import DaySchedule
3
- from cbh.api.timezone.models import TimezoneModel
4
  from cbh.core.database import MongoBaseModel
5
 
6
 
@@ -10,6 +9,4 @@ class AvailabilityModel(MongoBaseModel):
10
  """
11
 
12
  coach: AccountShorten
13
- timezone: TimezoneModel
14
-
15
  weeklySchedule: list[DaySchedule] | None = None
 
1
  from cbh.api.account.models import AccountShorten
2
  from cbh.api.availability.dto import DaySchedule
 
3
  from cbh.core.database import MongoBaseModel
4
 
5
 
 
9
  """
10
 
11
  coach: AccountShorten
 
 
12
  weeklySchedule: list[DaySchedule] | None = None
cbh/api/availability/schemas.py CHANGED
@@ -1,7 +1,5 @@
1
  from pydantic import BaseModel
2
  from cbh.api.availability.dto import DaySchedule
3
- from cbh.api.availability.models import AvailabilityModel
4
- from cbh.api.events.models import EventShorten
5
 
6
 
7
  class UpdateAvailabilityRequest(BaseModel):
@@ -9,14 +7,4 @@ class UpdateAvailabilityRequest(BaseModel):
9
  Update availability request.
10
  """
11
 
12
- timezoneId: str
13
  weeklySchedule: list[DaySchedule]
14
-
15
-
16
- class ExtendedAvailabilityResponse(BaseModel):
17
- """
18
- Extended availability response.
19
- """
20
-
21
- availability: AvailabilityModel
22
- events: list[EventShorten]
 
1
  from pydantic import BaseModel
2
  from cbh.api.availability.dto import DaySchedule
 
 
3
 
4
 
5
  class UpdateAvailabilityRequest(BaseModel):
 
7
  Update availability request.
8
  """
9
 
 
10
  weeklySchedule: list[DaySchedule]
 
 
 
 
 
 
 
 
 
cbh/api/availability/views.py CHANGED
@@ -1,9 +1,10 @@
1
  from fastapi import Depends
2
  from cbh.api.account.models import AccountModel
 
3
  from cbh.api.availability.schemas import (
4
  UpdateAvailabilityRequest,
5
- ExtendedAvailabilityResponse,
6
  )
 
7
  from cbh.core.security import PermissionDependency
8
  from cbh.api.account.dto import AccountType
9
  from cbh.core.wrappers import CbhResponseWrapper
@@ -16,21 +17,25 @@ async def get_own_availability(
16
  account: AccountModel = Depends(PermissionDependency([AccountType.COACH])),
17
  ) -> CbhResponseWrapper[AvailabilityModel]:
18
 
19
- availability = await get_availability_obj(account)
 
 
20
  return CbhResponseWrapper(data=availability)
21
 
22
 
23
  @availability_router.get("/{coachId}")
24
- async def get_extended_availability(
25
  coachId: str,
26
- account: AccountModel = Depends(
27
  PermissionDependency([AccountType.ADMIN, AccountType.USER])
28
  ),
29
- ) -> CbhResponseWrapper[ExtendedAvailabilityResponse]:
30
  """
31
  Get availability by coach ID.
32
  """
33
- availability = await get_extended_availability_obj(coachId)
 
 
34
  return CbhResponseWrapper(data=availability)
35
 
36
 
@@ -42,5 +47,18 @@ async def update_own_availability(
42
  """
43
  Update own availability.
44
  """
45
- availability = await update_own_availability_obj(availability, account)
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  return CbhResponseWrapper(data=availability)
 
1
  from fastapi import Depends
2
  from cbh.api.account.models import AccountModel
3
+ from cbh.api.availability.db_requests import update_availability_obj
4
  from cbh.api.availability.schemas import (
5
  UpdateAvailabilityRequest,
 
6
  )
7
+ from cbh.api.common.db_requests import get_obj_by_id
8
  from cbh.core.security import PermissionDependency
9
  from cbh.api.account.dto import AccountType
10
  from cbh.core.wrappers import CbhResponseWrapper
 
17
  account: AccountModel = Depends(PermissionDependency([AccountType.COACH])),
18
  ) -> CbhResponseWrapper[AvailabilityModel]:
19
 
20
+ availability = await get_obj_by_id(
21
+ AvailabilityModel, None, additional_filter={"coach.id": account.id}
22
+ )
23
  return CbhResponseWrapper(data=availability)
24
 
25
 
26
  @availability_router.get("/{coachId}")
27
+ async def get_availability(
28
  coachId: str,
29
+ _: AccountModel = Depends(
30
  PermissionDependency([AccountType.ADMIN, AccountType.USER])
31
  ),
32
+ ) -> CbhResponseWrapper[AvailabilityModel]:
33
  """
34
  Get availability by coach ID.
35
  """
36
+ availability = await get_obj_by_id(
37
+ AvailabilityModel, None, additional_filter={"coach.id": coachId}
38
+ )
39
  return CbhResponseWrapper(data=availability)
40
 
41
 
 
47
  """
48
  Update own availability.
49
  """
50
+ availability = await update_availability_obj(availability, account.id)
51
+ return CbhResponseWrapper(data=availability)
52
+
53
+
54
+ @availability_router.put("/{coachId}")
55
+ async def update_availability(
56
+ coachId: str,
57
+ availability: UpdateAvailabilityRequest,
58
+ _: AccountModel = Depends(PermissionDependency([AccountType.ADMIN])),
59
+ ) -> CbhResponseWrapper[AvailabilityModel]:
60
+ """
61
+ Update availability.
62
+ """
63
+ availability = await update_availability_obj(availability, coachId)
64
  return CbhResponseWrapper(data=availability)
cbh/api/calls/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
  from fastapi import APIRouter
2
 
3
- calls_router = APIRouter(prefix="/calls", tags=["calls"])
4
 
5
  from . import views
 
1
  from fastapi import APIRouter
2
 
3
+ calls_router = APIRouter(prefix="/api/calls", tags=["calls"])
4
 
5
  from . import views
cbh/api/calls/db_requests.py CHANGED
@@ -3,8 +3,8 @@ from datetime import datetime, timedelta
3
 
4
  from fastapi import HTTPException
5
 
6
- from cbh.api.account.models import AccountModel, AccountShorten
7
  from cbh.api.account.dto import AccountStatus, AccountType, CoachOpportunity
 
8
  from cbh.api.calls.dto import CallAvailabilityOption, CallStatus
9
  from cbh.api.calls.models import CallModel
10
  from cbh.api.calls.schemas import (
@@ -24,12 +24,12 @@ async def create_call_obj(
24
  event: EventModel,
25
  coach: AccountShorten,
26
  customer: AccountModel,
27
- is_code_valid: bool,
28
  ) -> CallModel:
29
  """
30
  Create a new call.
31
  """
32
- status = CallStatus.SCHEDULED if is_code_valid else CallStatus.CREATED
33
  call = CallModel(
34
  event=EventShorten(**event.model_dump()),
35
  customer=AccountShorten(**customer.model_dump()),
@@ -200,7 +200,7 @@ async def calculate_call_availabilities(
200
  opportunity_filter = (
201
  [CoachOpportunity.BUSINESS.value, CoachOpportunity.BOTH.value]
202
  if isBusiness
203
- else [CoachOpportunity.PRIVATE.value, CoachOpportunity.BOTH.value]
204
  )
205
 
206
  pipeline = [
@@ -332,17 +332,12 @@ async def calculate_call_availabilities(
332
  for day_schedule in weekly_schedule:
333
  if day_schedule.get("dayOfWeek") == slot_day_of_week:
334
  for time_slot in day_schedule.get("slots", []):
335
- slot_start_time = time_slot.get("startTime")
336
- slot_end_time = time_slot.get("endTime")
337
-
338
- if isinstance(slot_start_time, str):
339
- slot_start_time = datetime.strptime(
340
- slot_start_time, "%H:%M:%S"
341
- ).time()
342
- if isinstance(slot_end_time, str):
343
- slot_end_time = datetime.strptime(
344
- slot_end_time, "%H:%M:%S"
345
- ).time()
346
 
347
  if slot_start_time <= slot_time and slot_end_time >= slot_end:
348
  is_available = True
 
3
 
4
  from fastapi import HTTPException
5
 
 
6
  from cbh.api.account.dto import AccountStatus, AccountType, CoachOpportunity
7
+ from cbh.api.account.models import AccountModel, AccountShorten
8
  from cbh.api.calls.dto import CallAvailabilityOption, CallStatus
9
  from cbh.api.calls.models import CallModel
10
  from cbh.api.calls.schemas import (
 
24
  event: EventModel,
25
  coach: AccountShorten,
26
  customer: AccountModel,
27
+ is_business: bool,
28
  ) -> CallModel:
29
  """
30
  Create a new call.
31
  """
32
+ status = CallStatus.SCHEDULED if is_business else CallStatus.CREATED
33
  call = CallModel(
34
  event=EventShorten(**event.model_dump()),
35
  customer=AccountShorten(**customer.model_dump()),
 
200
  opportunity_filter = (
201
  [CoachOpportunity.BUSINESS.value, CoachOpportunity.BOTH.value]
202
  if isBusiness
203
+ else [CoachOpportunity.GENERAL.value, CoachOpportunity.BOTH.value]
204
  )
205
 
206
  pipeline = [
 
332
  for day_schedule in weekly_schedule:
333
  if day_schedule.get("dayOfWeek") == slot_day_of_week:
334
  for time_slot in day_schedule.get("slots", []):
335
+ slot_start_time = datetime.strptime(
336
+ time_slot.get("startTime"), "%H:%M:%S.%f%z"
337
+ ).time()
338
+ slot_end_time = datetime.strptime(
339
+ time_slot.get("endTime"), "%H:%M:%S.%f%z"
340
+ ).time()
 
 
 
 
 
341
 
342
  if slot_start_time <= slot_time and slot_end_time >= slot_end:
343
  is_available = True
cbh/api/calls/schemas.py CHANGED
@@ -38,12 +38,12 @@ class CallsFilter(BaseModel):
38
  Calls filter.
39
  """
40
 
41
- customerIds: list[str]
42
- coachIds: list[str]
43
- statuses: list[int]
44
- minDate: datetime
45
- maxDate: datetime
46
- searchTerm: str
47
 
48
 
49
  class CallAvailabilityResponse(BaseModel):
@@ -51,5 +51,5 @@ class CallAvailabilityResponse(BaseModel):
51
  Call availability response.
52
  """
53
 
54
- closestOption: CallAvailabilityOption
55
  options: list[CallAvailabilityOption]
 
38
  Calls filter.
39
  """
40
 
41
+ customerIds: list[str] | None = None
42
+ coachIds: list[str] | None = None
43
+ statuses: list[int] | None = None
44
+ minDate: datetime | None = None
45
+ maxDate: datetime | None = None
46
+ searchTerm: str | None = None
47
 
48
 
49
  class CallAvailabilityResponse(BaseModel):
 
51
  Call availability response.
52
  """
53
 
54
+ closestOption: CallAvailabilityOption | None
55
  options: list[CallAvailabilityOption]
cbh/api/calls/services/stripe.py CHANGED
@@ -21,25 +21,28 @@ async def create_stripe_session(
21
  }
22
 
23
  checkout_session = await settings.STRIPE_CLIENT.checkout.sessions.create_async(
24
- payment_method_types=["card", "ideal"],
25
- line_items=[
26
- {
27
- "price_data": {
28
- "currency": "usd",
29
- "unit_amount": 150,
30
- "product_data": {
31
- "name": f"Call ({call.duration} minutes)",
32
- "description": f"Payment for a call of {call.duration} minutes",
 
 
33
  },
 
34
  },
35
- "quantity": 1,
36
- },
37
- ],
38
- mode="payment",
39
- success_url=f"{settings.Issuer}/api/calls/stripe/callback",
40
- cancel_url=f"{settings.Issuer}/api/calls/stripe/callback",
41
- metadata=metadata,
42
- expires_at=int(time.time()) + 1800,
43
  )
44
  return checkout_session.url
45
 
 
21
  }
22
 
23
  checkout_session = await settings.STRIPE_CLIENT.checkout.sessions.create_async(
24
+ params={
25
+ "customer_email": call.customer.email,
26
+ "line_items": [
27
+ {
28
+ "price_data": {
29
+ "currency": "usd",
30
+ "unit_amount": 150 * 100,
31
+ "product_data": {
32
+ "name": "Coaching Session",
33
+ "description": f"1-on-1 coaching session with {call.coach.name}",
34
+ },
35
  },
36
+ "quantity": 1,
37
  },
38
+ ],
39
+ "mode": "payment",
40
+ "success_url": f"{settings.Issuer}/payment/success?callId={call.id}",
41
+ "cancel_url": f"{settings.Issuer}/payment/cancel",
42
+ "metadata": metadata,
43
+ "expires_at": int(time.time()) + 1800,
44
+ },
45
+ options={"api_key": settings.STRIPE_API_KEY}
46
  )
47
  return checkout_session.url
48
 
cbh/api/calls/views.py CHANGED
@@ -34,7 +34,6 @@ from cbh.api.calls.utils import can_edit_call
34
  from cbh.api.common.db_requests import get_obj_by_id
35
  from cbh.api.common.dto import Paging
36
  from cbh.api.common.schemas import AllObjectsResponse, FilterRequest
37
- from cbh.api.discountcode.models import DiscountCodeModel
38
  from cbh.core.security import PermissionDependency
39
  from cbh.core.wrappers import CbhResponseWrapper
40
 
@@ -47,17 +46,17 @@ async def purchase_call(
47
  """
48
  Purchase a call.
49
  """
50
- coach, is_code_valid = await asyncio.gather(
51
  get_obj_by_id(
52
  AccountShorten, request.coachId, projection=AccountShorten.to_mongo_fields()
53
  ),
54
  verify_discount_code(request.discountCode),
55
  )
56
  event = await create_event_obj(request, coach)
57
- call = await create_call_obj(event, coach, account)
58
 
59
  session_url = None
60
- if is_code_valid:
61
  session_url = await create_stripe_session(call)
62
  return CbhResponseWrapper(data=StripeSessionResponse(sessionUrl=session_url))
63
 
 
34
  from cbh.api.common.db_requests import get_obj_by_id
35
  from cbh.api.common.dto import Paging
36
  from cbh.api.common.schemas import AllObjectsResponse, FilterRequest
 
37
  from cbh.core.security import PermissionDependency
38
  from cbh.core.wrappers import CbhResponseWrapper
39
 
 
46
  """
47
  Purchase a call.
48
  """
49
+ coach, is_business = await asyncio.gather(
50
  get_obj_by_id(
51
  AccountShorten, request.coachId, projection=AccountShorten.to_mongo_fields()
52
  ),
53
  verify_discount_code(request.discountCode),
54
  )
55
  event = await create_event_obj(request, coach)
56
+ call = await create_call_obj(event, coach, account, is_business)
57
 
58
  session_url = None
59
+ if not is_business:
60
  session_url = await create_stripe_session(call)
61
  return CbhResponseWrapper(data=StripeSessionResponse(sessionUrl=session_url))
62
 
cbh/api/common/db_requests.py CHANGED
@@ -20,28 +20,12 @@ T = TypeVar("T", bound=BaseModel)
20
  collection_map = {
21
  "AccountModel": "accounts",
22
  "AccountShorten": "accounts",
23
- "ScenarioModel": "scenarios",
24
- "ScenarioShorten": "scenarios",
25
- "SessionReportModel": "sessionreports",
26
- "SessionReportShorten": "sessionreports",
27
- "TeamModel": "teams",
28
- "TeamModelShorten": "teams",
29
- "OrganizationModel": "organizations",
30
- "OrganizationShorten": "organizations",
31
- "OrganizationDocumentModel": "organizationdocuments",
32
- "OrganizationSettingsModel": "organizationsettings",
33
- "UserInsightModel": "userinsights",
34
- "UserInsightShorten": "userinsights",
35
- "VoiceModel": "voices",
36
- "VoiceModelShorten": "voices",
37
- "ActivityLogModel": "activitylogs",
38
- "AccountNotificationModel": "accountnotifications",
39
- "FeedbackModel": "generalfeedbacks",
40
- "FeedbackExtendedModel": "extendedfeedbacks",
41
- "ScenarioChangeModel": "scenariochanges",
42
- "WaitlistModel": "waitlists",
43
- "NotificationModel": "notifications",
44
- "NotificationModelShorten": "notifications",
45
  }
46
 
47
 
 
20
  collection_map = {
21
  "AccountModel": "accounts",
22
  "AccountShorten": "accounts",
23
+ "AvailabilityModel": "availabilities",
24
+ "CallModel": "calls",
25
+ "DiscountCodeModel": "discountcodes",
26
+ "EventModel": "events",
27
+ "EventShorten": "events",
28
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  }
30
 
31
 
cbh/api/discountcode/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
  from fastapi import APIRouter
2
 
3
- discountcode_router = APIRouter(prefix="/discountcodes", tags=["discount-code"])
4
 
5
  from . import views
 
1
  from fastapi import APIRouter
2
 
3
+ discountcode_router = APIRouter(prefix="/api/discountcodes", tags=["discount-code"])
4
 
5
  from . import views
cbh/api/events/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
  from fastapi import APIRouter
2
 
3
- events_router = APIRouter(prefix="/events", tags=["events"])
4
 
5
  from . import views
 
1
  from fastapi import APIRouter
2
 
3
+ events_router = APIRouter(prefix="/api/events", tags=["events"])
4
 
5
  from . import views
cbh/api/security/db_requests.py CHANGED
@@ -2,13 +2,15 @@
2
  Database requests module for security functionality.
3
  """
4
 
 
5
  from datetime import datetime
6
 
7
  from fastapi import HTTPException
8
- from passlib.context import CryptContext
9
  from pydantic import EmailStr
10
 
 
11
  from cbh.api.account.models import AccountModel, AccountShorten
 
12
  from cbh.api.security.schemas import (
13
  LoginAccountRequest,
14
  RegisterAccountRequest,
@@ -38,17 +40,35 @@ async def save_account(data: RegisterAccountRequest) -> AccountModel:
38
  Create a new user account in the database.
39
  """
40
  await check_unique_fields_existence("email", data.email)
 
 
 
 
 
41
  account = AccountModel(
42
  name=f"{data.firstName} {data.lastName}",
43
  email=data.email,
44
  password=data.password,
45
  datetimeUpdated=datetime.now(),
46
  accountType=data.accountType,
 
47
  )
48
  await settings.DB_CLIENT.accounts.insert_one(account.to_mongo())
 
 
 
49
  return account
50
 
51
 
 
 
 
 
 
 
 
 
 
52
  async def authenticate_account(data: LoginAccountRequest) -> AccountModel:
53
  """
54
  Authenticate a user account using mail and password.
 
2
  Database requests module for security functionality.
3
  """
4
 
5
+ import asyncio
6
  from datetime import datetime
7
 
8
  from fastapi import HTTPException
 
9
  from pydantic import EmailStr
10
 
11
+ from cbh.api.account.dto import AccountType, CoachOpportunity
12
  from cbh.api.account.models import AccountModel, AccountShorten
13
+ from cbh.api.availability.models import AvailabilityModel
14
  from cbh.api.security.schemas import (
15
  LoginAccountRequest,
16
  RegisterAccountRequest,
 
40
  Create a new user account in the database.
41
  """
42
  await check_unique_fields_existence("email", data.email)
43
+
44
+ opportunity = None
45
+ if data.accountType == AccountType.COACH:
46
+ opportunity = CoachOpportunity.GENERAL
47
+
48
  account = AccountModel(
49
  name=f"{data.firstName} {data.lastName}",
50
  email=data.email,
51
  password=data.password,
52
  datetimeUpdated=datetime.now(),
53
  accountType=data.accountType,
54
+ opportunity=opportunity,
55
  )
56
  await settings.DB_CLIENT.accounts.insert_one(account.to_mongo())
57
+
58
+ if account.accountType == AccountType.COACH:
59
+ asyncio.create_task(create_availability_obj(account))
60
  return account
61
 
62
 
63
+ async def create_availability_obj(account: AccountModel) -> AvailabilityModel:
64
+ """
65
+ Create a new availability object.
66
+ """
67
+ availability = AvailabilityModel(coach=AccountShorten(**account.to_mongo()))
68
+ await settings.DB_CLIENT.availabilities.insert_one(availability.to_mongo())
69
+ return availability
70
+
71
+
72
  async def authenticate_account(data: LoginAccountRequest) -> AccountModel:
73
  """
74
  Authenticate a user account using mail and password.
cbh/core/config.py CHANGED
@@ -30,6 +30,7 @@ class BaseConfig:
30
  ).spark
31
 
32
  STRIPE_CLIENT = StripeClient(api_key=os.getenv("STRIPE_API_KEY"))
 
33
  STRIPE_WEBHOOK_SECRET = os.getenv("STRIPE_WEBHOOK_SECRET")
34
 
35
  @staticmethod
@@ -78,9 +79,8 @@ class DevelopmentConfig(BaseConfig):
78
  Development environment configuration settings.
79
  """
80
 
81
- Issuer = "https://trainwitharena.com"
82
- Audience = "https://trainwitharena.com"
83
- Domain = "trainwitharena.com"
84
 
85
 
86
  class ProductionConfig(BaseConfig):
@@ -88,9 +88,8 @@ class ProductionConfig(BaseConfig):
88
  Production environment configuration settings.
89
  """
90
 
91
- Issuer = "https://trainwitharena.com"
92
- Audience = "https://trainwitharena.com"
93
- Domain = "trainwitharena.com"
94
 
95
 
96
  @lru_cache()
 
30
  ).spark
31
 
32
  STRIPE_CLIENT = StripeClient(api_key=os.getenv("STRIPE_API_KEY"))
33
+ STRIPE_API_KEY = os.getenv("STRIPE_API_KEY")
34
  STRIPE_WEBHOOK_SECRET = os.getenv("STRIPE_WEBHOOK_SECRET")
35
 
36
  @staticmethod
 
79
  Development environment configuration settings.
80
  """
81
 
82
+ Issuer = "http://localhost:8000"
83
+ Audience = "http://localhost:3000"
 
84
 
85
 
86
  class ProductionConfig(BaseConfig):
 
88
  Production environment configuration settings.
89
  """
90
 
91
+ Issuer = "https://brestok-spark.hf.space"
92
+ Audience = "http://localhost:3000"
 
93
 
94
 
95
  @lru_cache()
cbh/core/database.py CHANGED
@@ -2,7 +2,7 @@
2
  Database utilities for ClipboardHealthAI application.
3
  """
4
 
5
- from datetime import datetime
6
  from enum import Enum
7
  import re
8
  from typing import Any, Dict, Type, TypeVar
@@ -122,7 +122,7 @@ class MongoBaseModel(BaseModel):
122
  doc[key] = [model_to_dict(item) for item in value] # type: ignore
123
  elif value and isinstance(value, Enum):
124
  doc[key] = value.value
125
- elif isinstance(value, datetime):
126
  doc[key] = value.isoformat() # type: ignore
127
  elif value and isinstance(value, AnyUrl):
128
  doc[key] = str(value) # type: ignore
 
2
  Database utilities for ClipboardHealthAI application.
3
  """
4
 
5
+ from datetime import datetime, time, date
6
  from enum import Enum
7
  import re
8
  from typing import Any, Dict, Type, TypeVar
 
122
  doc[key] = [model_to_dict(item) for item in value] # type: ignore
123
  elif value and isinstance(value, Enum):
124
  doc[key] = value.value
125
+ elif isinstance(value, (datetime, time, date)):
126
  doc[key] = value.isoformat() # type: ignore
127
  elif value and isinstance(value, AnyUrl):
128
  doc[key] = str(value) # type: ignore
requirements.txt CHANGED
@@ -1,77 +1,135 @@
1
- aiosmtplib==5.1.0
2
- annotated-doc==0.0.4
 
 
3
  annotated-types==0.7.0
4
- anyio==4.12.1
5
- black==25.12.0
6
- certifi==2026.1.4
7
- charset-normalizer==3.4.4
8
- click==8.3.1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  distro==1.9.0
10
- dnspython==2.8.0
11
- ecdsa==0.19.1
12
- email-validator==2.3.0
13
- fastapi==0.128.0
14
- h11==0.16.0
15
- httpcore==1.0.9
16
- httptools==0.7.1
 
 
 
 
 
 
17
  httpx==0.28.1
18
- idna==3.11
19
- jiter==0.12.0
 
 
 
 
20
  jsonpatch==1.33
21
  jsonpointer==3.0.0
22
- langchain==1.2.3
23
- langchain-classic==1.0.1
24
- langchain-core==1.2.7
25
- langchain-mongodb==0.10.0
26
- langchain-openai==1.1.7
27
- langchain-text-splitters==1.1.0
28
- langgraph==1.0.5
29
- langgraph-checkpoint==3.0.1
30
- langgraph-prebuilt==1.0.5
31
- langgraph-sdk==0.3.2
32
- langsmith==0.6.2
33
- lark==1.3.1
34
  motor==3.7.1
 
 
35
  mypy_extensions==1.1.0
36
- numpy==2.4.1
37
- openai==2.15.0
38
- orjson==3.11.5
39
- ormsgpack==1.12.1
40
- packaging==25.0
41
  passlib==1.7.4
42
- pathspec==1.0.3
43
- pillow==12.1.0
44
- platformdirs==4.5.1
45
- pyasn1==0.6.2
46
- pydantic==2.12.5
47
- pydantic_core==2.41.5
 
 
 
 
 
 
48
  pydash==8.0.5
49
- pymongo==4.16.0
50
- pymongo-search-utils==0.1.0
51
- python-dotenv==1.2.1
52
- python-jose==3.5.0
53
- python-multipart==0.0.22
54
- pytokens==0.3.0
55
- PyYAML==6.0.3
56
- regex==2025.11.3
57
- requests==2.32.5
 
 
 
 
 
 
 
58
  requests-toolbelt==1.0.0
59
- rsa==4.9.1
 
 
 
 
 
60
  six==1.17.0
 
61
  sniffio==1.3.1
62
- SQLAlchemy==2.0.45
63
- starlette==0.50.0
64
- stripe==14.1.0
 
 
 
65
  tenacity==9.1.2
66
- tiktoken==0.12.0
 
67
  tqdm==4.67.1
68
- typing-inspection==0.4.2
69
- typing_extensions==4.15.0
70
- urllib3==2.6.3
71
- uuid_utils==0.13.0
72
- uvicorn==0.40.0
73
- uvloop==0.22.1
74
- watchfiles==1.1.1
75
- websockets==16.0
76
- xxhash==3.6.0
77
- zstandard==0.25.0
 
 
 
 
 
 
 
 
 
1
+ aiohappyeyeballs==2.6.1
2
+ aiohttp==3.11.16
3
+ aiosignal==1.3.2
4
+ aiosmtplib==3.0.2
5
  annotated-types==0.7.0
6
+ anyio==4.8.0
7
+ APScheduler==3.11.0
8
+ asn1crypto==1.5.1
9
+ assemblyai==0.40.2
10
+ astroid==3.3.10
11
+ attrs==25.3.0
12
+ bandit==1.8.3
13
+ bcrypt==4.2.1
14
+ beautifulsoup4==4.13.4
15
+ black==24.3.0
16
+ boto3==1.35.97
17
+ botocore==1.35.99
18
+ cachetools==6.2.0
19
+ certifi==2025.1.31
20
+ cffi==1.17.1
21
+ chardet==5.2.0
22
+ charset-normalizer==3.4.1
23
+ click==8.1.8
24
+ cryptography==44.0.1
25
+ cssselect==1.3.0
26
+ cssutils==2.11.1
27
+ dateutils==0.6.12
28
+ dill==0.4.0
29
  distro==1.9.0
30
+ dnspython==2.7.0
31
+ ecdsa==0.19.0
32
+ email_validator==2.2.0
33
+ emails==0.6
34
+ fastapi==0.115.8
35
+ filelock==3.17.0
36
+ flake8==7.0.0
37
+ frozenlist==1.5.0
38
+ git-filter-repo==2.47.0
39
+ h11==0.14.0
40
+ html2text==2025.4.15
41
+ httpcore==1.0.7
42
+ httptools==0.6.4
43
  httpx==0.28.1
44
+ idna==3.10
45
+ isodate==0.7.2
46
+ isort==6.0.1
47
+ Jinja2==3.1.6
48
+ jiter==0.8.2
49
+ jmespath==1.0.1
50
  jsonpatch==1.33
51
  jsonpointer==3.0.0
52
+ lark==1.2.2
53
+ lxml==6.0.0
54
+ markdown-it-py==3.0.0
55
+ MarkupSafe==3.0.2
56
+ mccabe==0.7.0
57
+ mdurl==0.1.2
58
+ more-itertools==10.7.0
 
 
 
 
 
59
  motor==3.7.1
60
+ multidict==6.4.3
61
+ mypy==1.15.0
62
  mypy_extensions==1.1.0
63
+ numpy==2.2.3
64
+ orjson==3.10.16
65
+ packaging==24.2
66
+ pandas==2.2.3
67
+ pandas-stubs==2.2.3.250308
68
  passlib==1.7.4
69
+ pathspec==0.12.1
70
+ pbr==6.1.1
71
+ pillow==11.3.0
72
+ platformdirs==4.3.6
73
+ premailer==3.10.0
74
+ propcache==0.3.1
75
+ pyasn1==0.4.8
76
+ pycodestyle==2.11.1
77
+ pycparser==2.22
78
+ pydantic==2.11.7
79
+ pydantic-mongo==3.1.0
80
+ pydantic_core==2.33.2
81
  pydash==8.0.5
82
+ pydub==0.25.1
83
+ pyflakes==3.2.0
84
+ Pygments==2.19.1
85
+ PyJWT==2.10.1
86
+ pylint==3.3.7
87
+ pymongo==4.15.2
88
+ pyOpenSSL==24.3.0
89
+ python-dateutil==2.9.0.post0
90
+ python-dotenv==1.0.1
91
+ python-jose==3.4.0
92
+ python-multipart==0.0.20
93
+ pytz==2025.1
94
+ PyYAML==6.0.2
95
+ regex==2024.11.6
96
+ requests==2.32.3
97
+ requests-file==2.1.0
98
  requests-toolbelt==1.0.0
99
+ rich==14.0.0
100
+ rsa==4.9
101
+ ruff==0.11.9
102
+ s3transfer==0.10.4
103
+ setuptools==80.8.0
104
+ simple-salesforce==1.12.6
105
  six==1.17.0
106
+ slack_sdk==3.35.0
107
  sniffio==1.3.1
108
+ snowflake-connector-python==3.13.2
109
+ sortedcontainers==2.4.0
110
+ soupsieve==2.7
111
+ SQLAlchemy==2.0.40
112
+ starlette==0.45.3
113
+ stevedore==5.4.1
114
  tenacity==9.1.2
115
+ tiktoken==0.9.0
116
+ tomlkit==0.13.2
117
  tqdm==4.67.1
118
+ types-passlib==1.7.7.20250408
119
+ types-pyasn1==0.6.0.20250208
120
+ types-python-dateutil==2.9.0.20241206
121
+ types-python-jose==3.4.0.20250224
122
+ types-pytz==2025.2.0.20250326
123
+ types-requests==2.32.0.20250328
124
+ typing-inspection==0.4.0
125
+ typing_extensions==4.12.2
126
+ tzdata==2025.1
127
+ tzlocal==5.3.1
128
+ urllib3==2.3.0
129
+ uvicorn==0.34.0
130
+ uvloop==0.21.0
131
+ watchfiles==1.0.4
132
+ websockets==14.2
133
+ yarl==1.19.0
134
+ zeep==4.3.1
135
+ zstandard==0.23.0