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.
- affinity/__init__.py +139 -0
- affinity/cli/__init__.py +7 -0
- affinity/cli/click_compat.py +27 -0
- affinity/cli/commands/__init__.py +1 -0
- affinity/cli/commands/_entity_files_dump.py +219 -0
- affinity/cli/commands/_list_entry_fields.py +41 -0
- affinity/cli/commands/_v1_parsing.py +77 -0
- affinity/cli/commands/company_cmds.py +2139 -0
- affinity/cli/commands/completion_cmd.py +33 -0
- affinity/cli/commands/config_cmds.py +540 -0
- affinity/cli/commands/entry_cmds.py +33 -0
- affinity/cli/commands/field_cmds.py +413 -0
- affinity/cli/commands/interaction_cmds.py +875 -0
- affinity/cli/commands/list_cmds.py +3152 -0
- affinity/cli/commands/note_cmds.py +433 -0
- affinity/cli/commands/opportunity_cmds.py +1174 -0
- affinity/cli/commands/person_cmds.py +1980 -0
- affinity/cli/commands/query_cmd.py +444 -0
- affinity/cli/commands/relationship_strength_cmds.py +62 -0
- affinity/cli/commands/reminder_cmds.py +595 -0
- affinity/cli/commands/resolve_url_cmd.py +127 -0
- affinity/cli/commands/session_cmds.py +84 -0
- affinity/cli/commands/task_cmds.py +110 -0
- affinity/cli/commands/version_cmd.py +29 -0
- affinity/cli/commands/whoami_cmd.py +36 -0
- affinity/cli/config.py +108 -0
- affinity/cli/context.py +749 -0
- affinity/cli/csv_utils.py +195 -0
- affinity/cli/date_utils.py +42 -0
- affinity/cli/decorators.py +77 -0
- affinity/cli/errors.py +28 -0
- affinity/cli/field_utils.py +355 -0
- affinity/cli/formatters.py +551 -0
- affinity/cli/help_json.py +283 -0
- affinity/cli/logging.py +100 -0
- affinity/cli/main.py +261 -0
- affinity/cli/options.py +53 -0
- affinity/cli/paths.py +32 -0
- affinity/cli/progress.py +183 -0
- affinity/cli/query/__init__.py +163 -0
- affinity/cli/query/aggregates.py +357 -0
- affinity/cli/query/dates.py +194 -0
- affinity/cli/query/exceptions.py +147 -0
- affinity/cli/query/executor.py +1236 -0
- affinity/cli/query/filters.py +248 -0
- affinity/cli/query/models.py +333 -0
- affinity/cli/query/output.py +331 -0
- affinity/cli/query/parser.py +619 -0
- affinity/cli/query/planner.py +430 -0
- affinity/cli/query/progress.py +270 -0
- affinity/cli/query/schema.py +439 -0
- affinity/cli/render.py +1589 -0
- affinity/cli/resolve.py +222 -0
- affinity/cli/resolvers.py +249 -0
- affinity/cli/results.py +308 -0
- affinity/cli/runner.py +218 -0
- affinity/cli/serialization.py +65 -0
- affinity/cli/session_cache.py +276 -0
- affinity/cli/types.py +70 -0
- affinity/client.py +771 -0
- affinity/clients/__init__.py +19 -0
- affinity/clients/http.py +3664 -0
- affinity/clients/pipeline.py +165 -0
- affinity/compare.py +501 -0
- affinity/downloads.py +114 -0
- affinity/exceptions.py +615 -0
- affinity/filters.py +1128 -0
- affinity/hooks.py +198 -0
- affinity/inbound_webhooks.py +302 -0
- affinity/models/__init__.py +163 -0
- affinity/models/entities.py +798 -0
- affinity/models/pagination.py +513 -0
- affinity/models/rate_limit_snapshot.py +48 -0
- affinity/models/secondary.py +413 -0
- affinity/models/types.py +663 -0
- affinity/policies.py +40 -0
- affinity/progress.py +22 -0
- affinity/py.typed +0 -0
- affinity/services/__init__.py +42 -0
- affinity/services/companies.py +1286 -0
- affinity/services/lists.py +1892 -0
- affinity/services/opportunities.py +1330 -0
- affinity/services/persons.py +1348 -0
- affinity/services/rate_limits.py +173 -0
- affinity/services/tasks.py +193 -0
- affinity/services/v1_only.py +2445 -0
- affinity/types.py +83 -0
- affinity_sdk-0.9.5.dist-info/METADATA +622 -0
- affinity_sdk-0.9.5.dist-info/RECORD +92 -0
- affinity_sdk-0.9.5.dist-info/WHEEL +4 -0
- affinity_sdk-0.9.5.dist-info/entry_points.txt +2 -0
- 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")
|