affinity-sdk 0.9.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. affinity/__init__.py +139 -0
  2. affinity/cli/__init__.py +7 -0
  3. affinity/cli/click_compat.py +27 -0
  4. affinity/cli/commands/__init__.py +1 -0
  5. affinity/cli/commands/_entity_files_dump.py +219 -0
  6. affinity/cli/commands/_list_entry_fields.py +41 -0
  7. affinity/cli/commands/_v1_parsing.py +77 -0
  8. affinity/cli/commands/company_cmds.py +2139 -0
  9. affinity/cli/commands/completion_cmd.py +33 -0
  10. affinity/cli/commands/config_cmds.py +540 -0
  11. affinity/cli/commands/entry_cmds.py +33 -0
  12. affinity/cli/commands/field_cmds.py +413 -0
  13. affinity/cli/commands/interaction_cmds.py +875 -0
  14. affinity/cli/commands/list_cmds.py +3152 -0
  15. affinity/cli/commands/note_cmds.py +433 -0
  16. affinity/cli/commands/opportunity_cmds.py +1174 -0
  17. affinity/cli/commands/person_cmds.py +1980 -0
  18. affinity/cli/commands/query_cmd.py +444 -0
  19. affinity/cli/commands/relationship_strength_cmds.py +62 -0
  20. affinity/cli/commands/reminder_cmds.py +595 -0
  21. affinity/cli/commands/resolve_url_cmd.py +127 -0
  22. affinity/cli/commands/session_cmds.py +84 -0
  23. affinity/cli/commands/task_cmds.py +110 -0
  24. affinity/cli/commands/version_cmd.py +29 -0
  25. affinity/cli/commands/whoami_cmd.py +36 -0
  26. affinity/cli/config.py +108 -0
  27. affinity/cli/context.py +749 -0
  28. affinity/cli/csv_utils.py +195 -0
  29. affinity/cli/date_utils.py +42 -0
  30. affinity/cli/decorators.py +77 -0
  31. affinity/cli/errors.py +28 -0
  32. affinity/cli/field_utils.py +355 -0
  33. affinity/cli/formatters.py +551 -0
  34. affinity/cli/help_json.py +283 -0
  35. affinity/cli/logging.py +100 -0
  36. affinity/cli/main.py +261 -0
  37. affinity/cli/options.py +53 -0
  38. affinity/cli/paths.py +32 -0
  39. affinity/cli/progress.py +183 -0
  40. affinity/cli/query/__init__.py +163 -0
  41. affinity/cli/query/aggregates.py +357 -0
  42. affinity/cli/query/dates.py +194 -0
  43. affinity/cli/query/exceptions.py +147 -0
  44. affinity/cli/query/executor.py +1236 -0
  45. affinity/cli/query/filters.py +248 -0
  46. affinity/cli/query/models.py +333 -0
  47. affinity/cli/query/output.py +331 -0
  48. affinity/cli/query/parser.py +619 -0
  49. affinity/cli/query/planner.py +430 -0
  50. affinity/cli/query/progress.py +270 -0
  51. affinity/cli/query/schema.py +439 -0
  52. affinity/cli/render.py +1589 -0
  53. affinity/cli/resolve.py +222 -0
  54. affinity/cli/resolvers.py +249 -0
  55. affinity/cli/results.py +308 -0
  56. affinity/cli/runner.py +218 -0
  57. affinity/cli/serialization.py +65 -0
  58. affinity/cli/session_cache.py +276 -0
  59. affinity/cli/types.py +70 -0
  60. affinity/client.py +771 -0
  61. affinity/clients/__init__.py +19 -0
  62. affinity/clients/http.py +3664 -0
  63. affinity/clients/pipeline.py +165 -0
  64. affinity/compare.py +501 -0
  65. affinity/downloads.py +114 -0
  66. affinity/exceptions.py +615 -0
  67. affinity/filters.py +1128 -0
  68. affinity/hooks.py +198 -0
  69. affinity/inbound_webhooks.py +302 -0
  70. affinity/models/__init__.py +163 -0
  71. affinity/models/entities.py +798 -0
  72. affinity/models/pagination.py +513 -0
  73. affinity/models/rate_limit_snapshot.py +48 -0
  74. affinity/models/secondary.py +413 -0
  75. affinity/models/types.py +663 -0
  76. affinity/policies.py +40 -0
  77. affinity/progress.py +22 -0
  78. affinity/py.typed +0 -0
  79. affinity/services/__init__.py +42 -0
  80. affinity/services/companies.py +1286 -0
  81. affinity/services/lists.py +1892 -0
  82. affinity/services/opportunities.py +1330 -0
  83. affinity/services/persons.py +1348 -0
  84. affinity/services/rate_limits.py +173 -0
  85. affinity/services/tasks.py +193 -0
  86. affinity/services/v1_only.py +2445 -0
  87. affinity/types.py +83 -0
  88. affinity_sdk-0.9.5.dist-info/METADATA +622 -0
  89. affinity_sdk-0.9.5.dist-info/RECORD +92 -0
  90. affinity_sdk-0.9.5.dist-info/WHEEL +4 -0
  91. affinity_sdk-0.9.5.dist-info/entry_points.txt +2 -0
  92. affinity_sdk-0.9.5.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,413 @@
1
+ """
2
+ Additional models for interactions, notes, reminders, webhooks, and files.
3
+
4
+ These entities are primarily accessed through the V1 API for write operations.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any
10
+
11
+ from pydantic import Field, model_validator
12
+
13
+ from .entities import AffinityModel, PersonSummary
14
+ from .types import (
15
+ CompanyId,
16
+ FileId,
17
+ InteractionDirection,
18
+ InteractionId,
19
+ InteractionType,
20
+ ISODatetime,
21
+ NoteId,
22
+ NoteType,
23
+ OpportunityId,
24
+ PersonId,
25
+ ReminderIdType,
26
+ ReminderResetType,
27
+ ReminderStatus,
28
+ ReminderType,
29
+ TenantId,
30
+ UserId,
31
+ WebhookEvent,
32
+ WebhookId,
33
+ )
34
+
35
+ # =============================================================================
36
+ # Note Models
37
+ # =============================================================================
38
+
39
+
40
+ class Note(AffinityModel):
41
+ """
42
+ A note attached to one or more entities.
43
+
44
+ Notes can be plain text, HTML, or AI-generated meeting summaries.
45
+ """
46
+
47
+ id: NoteId
48
+ creator_id: UserId = Field(alias="creatorId")
49
+ content: str | None = None
50
+ type: NoteType = NoteType.PLAIN_TEXT
51
+
52
+ # Associated entities
53
+ person_ids: list[PersonId] = Field(default_factory=list, alias="personIds")
54
+ associated_person_ids: list[PersonId] = Field(default_factory=list, alias="associatedPersonIds")
55
+ interaction_person_ids: list[PersonId] = Field(
56
+ default_factory=list, alias="interactionPersonIds"
57
+ )
58
+ mentioned_person_ids: list[PersonId] = Field(default_factory=list, alias="mentionedPersonIds")
59
+ company_ids: list[CompanyId] = Field(default_factory=list, alias="organizationIds")
60
+ opportunity_ids: list[OpportunityId] = Field(default_factory=list, alias="opportunityIds")
61
+
62
+ # Interaction association
63
+ interaction_id: int | None = Field(None, alias="interactionId")
64
+ interaction_type: InteractionType | None = Field(None, alias="interactionType")
65
+ is_meeting: bool = Field(False, alias="isMeeting")
66
+
67
+ # Thread support
68
+ parent_id: NoteId | None = Field(None, alias="parentId")
69
+
70
+ # Timestamps
71
+ created_at: ISODatetime = Field(alias="createdAt")
72
+ updated_at: ISODatetime | None = Field(None, alias="updatedAt")
73
+
74
+
75
+ class NoteCreate(AffinityModel):
76
+ """Data for creating a new note (V1 API)."""
77
+
78
+ content: str
79
+ type: NoteType = NoteType.PLAIN_TEXT
80
+ person_ids: list[PersonId] = Field(default_factory=list)
81
+ company_ids: list[CompanyId] = Field(default_factory=list, alias="organization_ids")
82
+ opportunity_ids: list[OpportunityId] = Field(default_factory=list)
83
+ parent_id: NoteId | None = None # For reply notes
84
+ creator_id: UserId | None = None
85
+ created_at: ISODatetime | None = None
86
+
87
+
88
+ class NoteUpdate(AffinityModel):
89
+ """Data for updating a note (V1 API)."""
90
+
91
+ content: str
92
+
93
+
94
+ # V2 Note response format
95
+ class NoteContent(AffinityModel):
96
+ """Note content in V2 format."""
97
+
98
+ html: str | None = None
99
+
100
+
101
+ class NoteV2(AffinityModel):
102
+ """V2 API note format."""
103
+
104
+ id: NoteId
105
+ type: str # "user_root_note", "user_reply_note", etc.
106
+ content: NoteContent
107
+ creator: PersonSummary
108
+ created_at: ISODatetime = Field(alias="createdAt")
109
+ updated_at: ISODatetime | None = Field(None, alias="updatedAt")
110
+
111
+ # Parent for replies
112
+ parent: dict[str, Any] | None = None
113
+
114
+ # Mentions
115
+ mentions: list[dict[str, Any]] = Field(default_factory=list)
116
+
117
+
118
+ # =============================================================================
119
+ # Interaction Models (V1 only for CRUD)
120
+ # =============================================================================
121
+
122
+
123
+ class Interaction(AffinityModel):
124
+ """
125
+ An interaction (email, meeting, call, or chat message).
126
+
127
+ Different interaction types have different fields available.
128
+ """
129
+
130
+ id: InteractionId
131
+ type: InteractionType
132
+ date: ISODatetime
133
+
134
+ # Associated persons
135
+ persons: list[PersonSummary] = Field(default_factory=list)
136
+ attendees: list[str] = Field(default_factory=list)
137
+
138
+ # Meeting/Call specific
139
+ title: str | None = None
140
+ start_time: ISODatetime | None = Field(None, alias="startTime")
141
+ end_time: ISODatetime | None = Field(None, alias="endTime")
142
+
143
+ # Email specific
144
+ subject: str | None = None
145
+
146
+ # Chat/Email direction
147
+ direction: InteractionDirection | None = None
148
+
149
+ # Notes attached to this interaction
150
+ notes: list[NoteId] = Field(default_factory=list)
151
+
152
+ # Manual logging info
153
+ manual_creator_id: UserId | None = Field(None, alias="manualCreatorId")
154
+ updated_at: ISODatetime | None = Field(None, alias="updatedAt")
155
+
156
+
157
+ class InteractionCreate(AffinityModel):
158
+ """Data for creating an interaction (V1 API)."""
159
+
160
+ type: InteractionType
161
+ person_ids: list[PersonId]
162
+ content: str
163
+ date: ISODatetime
164
+ direction: InteractionDirection | None = None
165
+
166
+
167
+ class InteractionUpdate(AffinityModel):
168
+ """Data for updating an interaction (V1 API)."""
169
+
170
+ person_ids: list[PersonId] | None = None
171
+ content: str | None = None
172
+ date: ISODatetime | None = None
173
+ direction: InteractionDirection | None = None
174
+
175
+
176
+ # =============================================================================
177
+ # Reminder Models (V1 only)
178
+ # =============================================================================
179
+
180
+
181
+ class Reminder(AffinityModel):
182
+ """A reminder attached to an entity."""
183
+
184
+ id: ReminderIdType
185
+ type: ReminderType
186
+ status: ReminderStatus
187
+ content: str | None = None
188
+
189
+ # Due date and recurrence
190
+ due_date: ISODatetime = Field(alias="dueDate")
191
+ reset_type: ReminderResetType | None = Field(None, alias="resetType")
192
+ reminder_days: int | None = Field(None, alias="reminderDays")
193
+
194
+ # Persons involved
195
+ creator: PersonSummary | None = None
196
+ owner: PersonSummary | None = None
197
+ completer: PersonSummary | None = None
198
+
199
+ # Associated entity (one of these)
200
+ person: PersonSummary | None = None
201
+ company: dict[str, Any] | None = Field(None, alias="organization")
202
+ opportunity: dict[str, Any] | None = None
203
+
204
+ # Timestamps
205
+ created_at: ISODatetime = Field(alias="createdAt")
206
+ completed_at: ISODatetime | None = Field(None, alias="completedAt")
207
+
208
+
209
+ class ReminderCreate(AffinityModel):
210
+ """Data for creating a reminder (V1 API)."""
211
+
212
+ owner_id: UserId
213
+ type: ReminderType
214
+ content: str | None = None
215
+ due_date: ISODatetime | None = None # Required for one-time
216
+ reset_type: ReminderResetType | None = None # Required for recurring
217
+ reminder_days: int | None = None # Required for recurring
218
+
219
+ # Associate with one entity
220
+ person_id: PersonId | None = None
221
+ company_id: CompanyId | None = Field(None, alias="organization_id")
222
+ opportunity_id: OpportunityId | None = None
223
+
224
+
225
+ class ReminderUpdate(AffinityModel):
226
+ """Data for updating a reminder (V1 API)."""
227
+
228
+ owner_id: UserId | None = None
229
+ type: ReminderType | None = None
230
+ content: str | None = None
231
+ due_date: ISODatetime | None = None
232
+ reset_type: ReminderResetType | None = None
233
+ reminder_days: int | None = None
234
+ is_completed: bool | None = None
235
+
236
+
237
+ # =============================================================================
238
+ # Webhook Models (V1 only)
239
+ # =============================================================================
240
+
241
+
242
+ class WebhookSubscription(AffinityModel):
243
+ """A webhook subscription for real-time events."""
244
+
245
+ id: WebhookId
246
+ webhook_url: str = Field(alias="webhookUrl")
247
+ subscriptions: list[WebhookEvent] = Field(default_factory=list)
248
+ disabled: bool = False
249
+ created_by: UserId = Field(alias="createdBy")
250
+
251
+
252
+ class WebhookCreate(AffinityModel):
253
+ """Data for creating a webhook subscription (V1 API)."""
254
+
255
+ webhook_url: str
256
+ subscriptions: list[WebhookEvent] = Field(default_factory=list)
257
+
258
+
259
+ class WebhookUpdate(AffinityModel):
260
+ """Data for updating a webhook subscription (V1 API)."""
261
+
262
+ webhook_url: str | None = None
263
+ subscriptions: list[WebhookEvent] | None = None
264
+ disabled: bool | None = None
265
+
266
+
267
+ # =============================================================================
268
+ # Entity File Models (V1 only)
269
+ # =============================================================================
270
+
271
+
272
+ class EntityFile(AffinityModel):
273
+ """A file attached to an entity."""
274
+
275
+ id: FileId
276
+ name: str
277
+ size: int
278
+ # Observed missing in some V1 responses; treat as optional for robustness.
279
+ content_type: str | None = Field(None, alias="contentType")
280
+
281
+ # Associated entity
282
+ person_id: PersonId | None = Field(None, alias="personId")
283
+ company_id: CompanyId | None = Field(None, alias="organizationId")
284
+ opportunity_id: OpportunityId | None = Field(None, alias="opportunityId")
285
+
286
+ # Uploader
287
+ uploader_id: UserId = Field(alias="uploaderId")
288
+
289
+ # Timestamps
290
+ created_at: ISODatetime = Field(alias="createdAt")
291
+
292
+
293
+ # =============================================================================
294
+ # Relationship Strength Models (V1 only)
295
+ # =============================================================================
296
+
297
+
298
+ class RelationshipStrength(AffinityModel):
299
+ """Relationship strength between internal and external persons."""
300
+
301
+ internal_id: UserId = Field(alias="internalId")
302
+ external_id: PersonId = Field(alias="externalId")
303
+ strength: float # 0.0 to 1.0
304
+
305
+
306
+ # =============================================================================
307
+ # Auth/Whoami Models
308
+ # =============================================================================
309
+
310
+
311
+ class Tenant(AffinityModel):
312
+ """Affinity tenant (organization/team) information."""
313
+
314
+ id: TenantId
315
+ name: str
316
+ subdomain: str
317
+
318
+
319
+ class User(AffinityModel):
320
+ """Current user information."""
321
+
322
+ id: UserId
323
+ first_name: str = Field(alias="firstName")
324
+ last_name: str | None = Field(alias="lastName")
325
+ email: str = Field(alias="emailAddress")
326
+
327
+
328
+ class Grant(AffinityModel):
329
+ """API key grant information."""
330
+
331
+ type: str
332
+ scopes: list[str] = Field(default_factory=list)
333
+ created_at: ISODatetime = Field(alias="createdAt")
334
+
335
+ @model_validator(mode="before")
336
+ @classmethod
337
+ def _coerce_scope_to_scopes(cls, value: Any) -> Any:
338
+ if not isinstance(value, dict):
339
+ return value
340
+ if "scopes" in value:
341
+ return value
342
+ scope = value.get("scope")
343
+ if isinstance(scope, str):
344
+ updated = dict(value)
345
+ updated["scopes"] = [scope]
346
+ return updated
347
+ return value
348
+
349
+ @property
350
+ def scope(self) -> str | None:
351
+ """
352
+ Backwards-compatible convenience for older response shapes.
353
+
354
+ Returns the first scope string when present, otherwise None.
355
+ """
356
+ return self.scopes[0] if self.scopes else None
357
+
358
+
359
+ class WhoAmI(AffinityModel):
360
+ """Response from whoami endpoint."""
361
+
362
+ tenant: Tenant
363
+ user: User
364
+ grant: Grant
365
+
366
+
367
+ # =============================================================================
368
+ # Rate Limit Models
369
+ # =============================================================================
370
+
371
+
372
+ class RateLimitInfo(AffinityModel):
373
+ """Rate limit information for an API key."""
374
+
375
+ limit: int
376
+ remaining: int
377
+ reset: int # Seconds until reset
378
+ used: int
379
+
380
+
381
+ class RateLimits(AffinityModel):
382
+ """Current rate limit status."""
383
+
384
+ org_monthly: RateLimitInfo = Field(alias="orgMonthly")
385
+ api_key_per_minute: RateLimitInfo = Field(alias="apiKeyPerMinute")
386
+
387
+
388
+ # =============================================================================
389
+ # Merge Task Models (V2 BETA)
390
+ # =============================================================================
391
+
392
+
393
+ class MergeResultsSummary(AffinityModel):
394
+ """Summary of merge operation results."""
395
+
396
+ total: int
397
+ success: int
398
+ failed: int
399
+ in_progress: int = Field(alias="inProgress")
400
+
401
+
402
+ class MergeTask(AffinityModel):
403
+ """Async merge task status."""
404
+
405
+ id: str
406
+ status: str # pending, in_progress, success, failed
407
+ results_summary: MergeResultsSummary | None = Field(None, alias="resultsSummary")
408
+
409
+
410
+ class MergeResponse(AffinityModel):
411
+ """Response from initiating a merge."""
412
+
413
+ task_url: str = Field(alias="taskUrl")