Coverage for modules\email\schemas.py: 100%
135 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-05 09:25 +0800
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-05 09:25 +0800
1from pydantic import BaseModel, EmailStr, Field
2from typing import Optional, List
3from uuid import UUID
4from datetime import datetime
7class EmailAccountOut(BaseModel):
8 id: UUID
9 provider: str
10 connection_type: str
11 email_address: str
12 display_name: str
13 is_primary: bool
14 sync_enabled: bool
15 last_sync_at: Optional[datetime] = None
16 is_verified: bool
17 unread_inbox_count: int = 0
19 class Config:
20 from_attributes = True
23class EmailAccountCreateIn(BaseModel):
24 """Connect email via IMAP/SMTP (Outlook/Thunderbird style)."""
25 provider: str = Field(default='other')
26 email_address: EmailStr
27 display_name: str = ""
28 imap_host: str = Field(default="", description="IMAP server address")
29 imap_port: int = Field(default=993)
30 smtp_host: str = Field(default="", description="SMTP server address")
31 smtp_port: int = Field(default=587)
32 password: str = Field(description="Email password or app password")
33 use_ssl: bool = True
36class EmailAccountUpdateIn(BaseModel):
37 display_name: Optional[str] = None
38 is_primary: Optional[bool] = None
39 sync_enabled: Optional[bool] = None
40 sync_interval: Optional[int] = None
43class EmailFolderOut(BaseModel):
44 id: str # Can be UUID string or integer as string
45 name: str
46 folder_type: str
47 unread_count: int
49 class Config:
50 from_attributes = True
53class EmailFolderCreateIn(BaseModel):
54 """Create a custom email folder."""
55 name: str = Field(max_length=128)
56 folder_type: str = Field(default='custom') # custom is the only user-creatable type
59class EmailFilterCondition(BaseModel):
60 """A single condition for email filtering."""
61 field: str = Field(description="Field to match: from, to, subject, body, cc")
62 operator: str = Field(description="Operator: contains, equals, starts_with, ends_with, regex")
63 value: str = Field(description="Value to match against")
66class EmailFilterCreateIn(BaseModel):
67 """Create an email filter/rule."""
68 name: str = Field(max_length=128)
69 conditions: List[EmailFilterCondition] = Field(description="List of conditions (all must match)")
70 action_type: str = Field(description="Action: move_to_folder, mark_read, star, delete")
71 action_folder_id: Optional[UUID] = None
72 is_active: bool = True
73 priority: int = 0
76class EmailFilterOut(BaseModel):
77 id: UUID
78 name: str
79 conditions: dict
80 action_type: str
81 action_folder_id: Optional[UUID] = None
82 is_active: bool
83 priority: int
85 class Config:
86 from_attributes = True
89class EmailAddress(BaseModel):
90 email: str
91 name: Optional[str] = None
94class EmailMessageOut(BaseModel):
95 id: UUID
96 message_id: str
97 subject: str
98 snippet: str
99 from_address: str
100 from_name: Optional[str] = None
101 to_addresses: List[EmailAddress]
102 cc_addresses: Optional[List[EmailAddress]] = None
103 is_read: bool
104 is_starred: bool
105 is_draft: bool
106 is_sent: bool
107 sent_at: Optional[datetime] = None
108 received_at: Optional[datetime] = None
109 folder_id: Optional[UUID] = None
110 account_id: Optional[UUID] = None
112 class Config:
113 from_attributes = True
116class EmailMessageDetailOut(EmailMessageOut):
117 body_text: Optional[str] = None
118 body_html: Optional[str] = None
119 attachments: List['EmailAttachmentOut'] = []
121 class Config:
122 from_attributes = True
125class EmailAttachmentOut(BaseModel):
126 id: UUID
127 file_name: str
128 content_type: str
129 size_bytes: int
130 file_path: Optional[str] = None
132 class Config:
133 from_attributes = True
136class EmailSendIn(BaseModel):
137 """Send an email."""
138 to: List[EmailStr]
139 cc: Optional[List[EmailStr]] = None
140 bcc: Optional[List[EmailStr]] = None
141 subject: str
142 body: str
143 is_html: bool = False
146class EmailDraftIn(BaseModel):
147 """Save a draft."""
148 to: Optional[List[EmailStr]] = None
149 cc: Optional[List[EmailStr]] = None
150 bcc: Optional[List[EmailStr]] = None
151 subject: str = ""
152 body: str = ""
155class EmailMessageUpdateIn(BaseModel):
156 is_read: Optional[bool] = None
157 is_starred: Optional[bool] = None
158 is_flagged: Optional[bool] = None
161class InternalEmailOut(BaseModel):
162 id: UUID
163 subject: str
164 body: str
165 sender_name: str
166 sender_email: str
167 recipients: List[EmailAddress]
168 sent_at: datetime
169 is_read: bool
171 class Config:
172 from_attributes = True
175class InternalEmailSendIn(BaseModel):
176 """Send internal ERP email."""
177 to: List[UUID] # User IDs
178 subject: str = Field(..., max_length=512)
179 body: str
182class InternalEmailCountOut(BaseModel):
183 unread_count: int
184 total_count: int
187# Update forward references
188EmailMessageDetailOut.model_rebuild()