unique-quartr 0.2.6__tar.gz

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.
@@ -0,0 +1,576 @@
1
+ Metadata-Version: 2.3
2
+ Name: unique-quartr
3
+ Version: 0.2.6
4
+ Summary: Unique Quartr API client
5
+ Author: Rami Azouz
6
+ Author-email: Rami Azouz <rami.ext@unique.ch>
7
+ License: Proprietary
8
+ Requires-Dist: unique-toolkit>=1.50.4
9
+ Requires-Dist: pydantic>=2.12.4
10
+ Requires-Dist: pydantic-settings>=2.10.1
11
+ Requires-Dist: jinja2>=3.1.0
12
+ Requires-Dist: httpx>=0.28.0
13
+ Requires-Dist: requests>=2.32.0
14
+ Requires-Python: >=3.12, <4
15
+ Description-Content-Type: text/markdown
16
+
17
+ # Unique Quartr Connector
18
+
19
+ A Python connector library for the [Quartr API](https://quartr.com), providing easy access to company events, documents, and financial data.
20
+
21
+ ## Features
22
+
23
+ - 🔍 Fetch company earnings calls and events
24
+ - 📄 Retrieve event documents (transcripts, reports, slides, etc.)
25
+ - 📝 Parse and export earnings call transcripts to markdown
26
+ - 🎯 Type-safe API with Pydantic models
27
+ - 🔄 Automatic pagination handling
28
+ - 📊 Support for multiple document and event types
29
+ - 🔐 Secure API key authentication with base64 encoding
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ poetry add unique_quartr
35
+ ```
36
+
37
+ Or using pip:
38
+
39
+ ```bash
40
+ pip install unique_quartr
41
+ ```
42
+
43
+ ## Configuration
44
+
45
+ ### Environment Variables
46
+
47
+ Create a `.env` file in your project root with your Quartr API credentials:
48
+
49
+ ```env
50
+ # Base64-encoded JSON credentials
51
+ QUARTR_API_CREDS='xxxxxxxxxxx'
52
+ QUARTR_API_ACTIVATED_COMPANIES='["company_id_1", "company_id_2"]'
53
+ ```
54
+
55
+ **Note**: The `QUARTR_API_CREDS` should be a base64-encoded JSON string containing:
56
+ ```json
57
+ {"api_key": "your_api_key_here", "valid_to": "2025-12-31"}
58
+ ```
59
+
60
+ You can encode your credentials using:
61
+ ```python
62
+ import base64
63
+ import json
64
+
65
+ creds = {"api_key": "your_api_key_here", "valid_to": "2025-12-31"}
66
+ encoded = base64.b64encode(json.dumps(creds).encode()).decode()
67
+ print(encoded)
68
+ ```
69
+
70
+ ### Test Environment
71
+
72
+ For testing, create `tests/test.env`:
73
+
74
+ ```env
75
+ QUARTR_API_CREDS='xxxxxxxxxxx'
76
+ QUARTR_API_ACTIVATED_COMPANIES='["test_company"]'
77
+ ```
78
+
79
+ ## Quick Start
80
+
81
+ ### Basic Usage
82
+
83
+ ```python
84
+ from unique_quartr.service import QuartrService
85
+ from unique_quartr.constants.event_types import EventType
86
+ from unique_quartr.constants.document_types import DocumentType
87
+ from unique_toolkit._common.experimental.endpoint_requestor import RequestorType
88
+
89
+ # Initialize the service
90
+ service = QuartrService(
91
+ company_id="your_company_id",
92
+ requestor_type=RequestorType.REQUESTS,
93
+ )
94
+
95
+ # Define the event types you want to fetch
96
+ event_types = [EventType.EARNINGS_CALL]
97
+ event_ids = service.get_event_subtype_ids_from_event_types(event_types)
98
+
99
+ # Fetch company events
100
+ events = service.fetch_company_events(
101
+ ticker="AAPL",
102
+ exchange="NasdaqGS",
103
+ country="US",
104
+ event_ids=event_ids,
105
+ start_date="2024-01-01",
106
+ end_date="2024-12-31",
107
+ )
108
+
109
+ print(f"Found {len(events.data)} events")
110
+ for event in events.data:
111
+ print(f"Event: {event.title} - {event.date}")
112
+ ```
113
+
114
+ ### Fetching Documents
115
+
116
+ ```python
117
+ # Get event IDs from the events you fetched
118
+ event_ids = [event.id for event in events.data]
119
+
120
+ # Define document types you want
121
+ document_types = [DocumentType.TRANSCRIPT, DocumentType.SLIDES]
122
+ document_ids = service.get_document_ids_from_document_types(document_types)
123
+
124
+ # Fetch documents for these events
125
+ documents = service.fetch_event_documents(
126
+ event_ids=event_ids,
127
+ document_ids=document_ids,
128
+ )
129
+
130
+ print(f"Found {len(documents.data)} documents")
131
+ for doc in documents.data:
132
+ print(f"Document: {doc.type_id} - {doc.file_url}")
133
+ ```
134
+
135
+ ### Working with Earnings Call Transcripts
136
+
137
+ ```python
138
+ from unique_quartr.transcripts.earnings_call import QuartrEarningsCallTranscript
139
+
140
+ # Fetch transcript from Quartr URL
141
+ transcript_url = "https://quartr.com/transcript/12345.json"
142
+ transcript = QuartrEarningsCallTranscript.from_quartr_transcript_url(transcript_url)
143
+
144
+ # Or use async version
145
+ transcript = await QuartrEarningsCallTranscript.from_quartr_transcript_url_async(transcript_url)
146
+
147
+ # Export to markdown
148
+ markdown_content = transcript.to_markdown()
149
+ print(markdown_content)
150
+
151
+ # Access transcript data
152
+ print(f"Event ID: {transcript.event_id}")
153
+ print(f"Company ID: {transcript.company_id}")
154
+ print(f"Number of speakers: {transcript.transcript.number_of_speakers}")
155
+
156
+ # Iterate through paragraphs
157
+ for paragraph in transcript.transcript.paragraphs:
158
+ speaker_name = transcript._get_speaker_name(paragraph.speaker)
159
+ print(f"{speaker_name}: {paragraph.text[:100]}...")
160
+ ```
161
+
162
+ ## Event Types
163
+
164
+ The library supports the following event types:
165
+
166
+ - **Earnings Call**: Q1, Q2, Q3, Q4, H1, H2
167
+ - **Analyst Day**
168
+ - **Annual General Meeting**: AGM, Scheme Meeting
169
+ - **Business Combination**
170
+ - **C-level Sitdown**: C-level Sitdown, CEO Sitdown
171
+ - **Capital Markets Day**
172
+ - **Capital Raise**
173
+ - **Conference**
174
+ - **Extraordinary General Meeting**
175
+ - **FDA Announcement**
176
+ - **Fireside Chat**
177
+ - **Investor Day**
178
+ - **M&A Announcement**
179
+ - **Outlook / Guidance Update**
180
+ - **Partnerships / Collaborations**: Partnership, Collaboration
181
+ - **Product / Service Launch**: Product Launch, Service Launch
182
+ - **Slides**: Investor Presentation, Corporate Presentation, Company Presentation
183
+ - **Trading Update**
184
+ - **Update / Briefing**: Status Update, Investor Update, ESG Update, Study Update, Study Result, KOL Event
185
+
186
+ ### Example: Getting Earnings Call Event IDs
187
+
188
+ ```python
189
+ from unique_quartr.constants.event_types import EventType
190
+
191
+ event_types = [EventType.EARNINGS_CALL]
192
+ event_ids = QuartrService.get_event_subtype_ids_from_event_types(event_types)
193
+ # Returns: [26, 27, 28, 29, 35, 36] (Q1, Q2, Q3, Q4, H1, H2)
194
+ ```
195
+
196
+ ## Document Types
197
+
198
+ The library supports various document types:
199
+
200
+ - **Slides** 📊
201
+ - **Report** 📄
202
+ - **Quarterly Report (10-Q)** 📑
203
+ - **Earnings Release (8-K)** 📢
204
+ - **Annual Report (10-K)** 📘
205
+ - **Annual Report (20-F)** 📙
206
+ - **Annual Report (40-F)** 📕
207
+ - **Earnings Release (6-K)** 📣
208
+ - **Transcript** 🗒️
209
+ - **Interim Report** 📜
210
+ - **Press Release** 🗞️
211
+ - **Earnings Release** 💰
212
+ - **In-house Transcript** 🎤
213
+
214
+ ### Example: Getting Document Type Information
215
+
216
+ ```python
217
+ from unique_quartr.constants.document_types import DocumentType
218
+
219
+ doc_type = DocumentType.QUARTERLY_REPORT_10Q
220
+
221
+ print(doc_type.name) # "Quarterly report"
222
+ print(doc_type.form) # "10-Q"
223
+ print(doc_type.emoji) # "📑"
224
+ print(doc_type.get_file_prefix()) # "Quarterly report (10-Q)"
225
+ ```
226
+
227
+ ## Advanced Usage
228
+
229
+ ### Custom Pagination
230
+
231
+ Control pagination parameters for large datasets:
232
+
233
+ ```python
234
+ events = service.fetch_company_events(
235
+ ticker="AAPL",
236
+ exchange="NasdaqGS",
237
+ country="US",
238
+ event_ids=event_ids,
239
+ limit=100, # Items per page (max 500)
240
+ max_iteration=10, # Maximum number of pages
241
+ )
242
+ ```
243
+
244
+ ### Filtering by Date Range
245
+
246
+ ```python
247
+ events = service.fetch_company_events(
248
+ ticker="AAPL",
249
+ exchange="NasdaqGS",
250
+ country="US",
251
+ event_ids=event_ids,
252
+ start_date="2024-01-01T00:00:00Z", # ISO format
253
+ end_date="2024-03-31T23:59:59Z",
254
+ )
255
+ ```
256
+
257
+ ### Fetching Multiple Event Types
258
+
259
+ ```python
260
+ from unique_quartr.constants.event_types import EventType
261
+
262
+ # Combine multiple event types
263
+ event_types = [
264
+ EventType.EARNINGS_CALL,
265
+ EventType.ANALYST_DAY,
266
+ EventType.INVESTOR_DAY,
267
+ ]
268
+
269
+ event_ids = QuartrService.get_event_subtype_ids_from_event_types(event_types)
270
+
271
+ events = service.fetch_company_events(
272
+ ticker="AAPL",
273
+ exchange="NasdaqGS",
274
+ country="US",
275
+ event_ids=event_ids,
276
+ )
277
+ ```
278
+
279
+ ## API Reference
280
+
281
+ ### QuartrService
282
+
283
+ ```python
284
+ class QuartrService:
285
+ def __init__(
286
+ self,
287
+ *,
288
+ company_id: str,
289
+ requestor_type: RequestorType,
290
+ ):
291
+ """
292
+ Initialize the Quartr service.
293
+
294
+ Args:
295
+ company_id: Company identifier for API access
296
+ requestor_type: Type of requestor (SYNC or ASYNC)
297
+ """
298
+ ```
299
+
300
+ #### Methods
301
+
302
+ ##### fetch_company_events
303
+
304
+ ```python
305
+ # Method signature (overloaded)
306
+ def fetch_company_events(
307
+ self,
308
+ *,
309
+ company_ids: list[int | float] | None = None,
310
+ ticker: str | None = None,
311
+ exchange: str | None = None,
312
+ country: str | None = None,
313
+ event_ids: list[int] | None = None,
314
+ start_date: str | None = None,
315
+ end_date: str | None = None,
316
+ limit: int = 500,
317
+ max_iteration: int = 20,
318
+ ) -> EventResults:
319
+ """
320
+ Retrieve events for a given company.
321
+
322
+ Args:
323
+ company_ids: List of company IDs (alternative to ticker/exchange/country)
324
+ ticker: Company ticker symbol (e.g., 'AAPL', 'AMZN')
325
+ exchange: Exchange code (e.g., 'NasdaqGS', 'NYSE')
326
+ country: Country code (e.g., 'US', 'CA')
327
+ event_ids: List of event type IDs to filter
328
+ start_date: Optional start date in ISO format
329
+ end_date: Optional end date in ISO format
330
+ limit: Items per request (max 500)
331
+ max_iteration: Maximum number of pagination iterations
332
+
333
+ Returns:
334
+ EventResults object with .data containing list of EventDto objects
335
+
336
+ Note:
337
+ Either company_ids OR (ticker, exchange, country) must be provided
338
+ """
339
+ ```
340
+
341
+ **Example with company IDs:**
342
+ ```python
343
+ events = service.fetch_company_events(
344
+ company_ids=[4742, 5025],
345
+ event_ids=event_ids,
346
+ start_date="2024-01-01",
347
+ )
348
+ ```
349
+
350
+ **Example with ticker/exchange:**
351
+ ```python
352
+ events = service.fetch_company_events(
353
+ ticker="AAPL",
354
+ exchange="NasdaqGS",
355
+ country="US",
356
+ event_ids=event_ids,
357
+ )
358
+ ```
359
+
360
+ ##### fetch_event_documents
361
+
362
+ ```python
363
+ def fetch_event_documents(
364
+ self,
365
+ event_ids: list[int],
366
+ document_ids: list[int],
367
+ limit: int = 500,
368
+ max_iteration: int = 20,
369
+ ) -> DocumentResults:
370
+ """
371
+ Retrieve documents for a list of events.
372
+
373
+ Args:
374
+ event_ids: List of event IDs
375
+ document_ids: List of document type IDs to filter
376
+ limit: Items per request (max 500)
377
+ max_iteration: Maximum number of pagination iterations
378
+
379
+ Returns:
380
+ DocumentResults object with .data containing list of DocumentDto objects
381
+ """
382
+ ```
383
+
384
+ ##### Static Methods
385
+
386
+ ```python
387
+ @staticmethod
388
+ def get_event_subtype_ids_from_event_types(
389
+ event_types: list[EventType],
390
+ ) -> list[int]:
391
+ """
392
+ Convert EventType enums to Quartr API event subtype IDs.
393
+
394
+ Args:
395
+ event_types: List of EventType enums
396
+
397
+ Returns:
398
+ List of event subtype IDs
399
+ """
400
+
401
+ @staticmethod
402
+ def get_document_ids_from_document_types(
403
+ document_types: list[DocumentType],
404
+ ) -> list[int]:
405
+ """
406
+ Convert DocumentType enums to Quartr API document type IDs.
407
+
408
+ Args:
409
+ document_types: List of DocumentType enums
410
+
411
+ Returns:
412
+ List of document type IDs
413
+ """
414
+ ```
415
+
416
+ ## Response Models
417
+
418
+ ### EventResults
419
+
420
+ Wrapper object returned by `fetch_company_events`:
421
+
422
+ ```python
423
+ class EventResults:
424
+ data: list[EventDto] # Access events via .data attribute
425
+ ```
426
+
427
+ ### EventDto
428
+
429
+ ```python
430
+ class EventDto:
431
+ company_id: float
432
+ date: datetime
433
+ id: float
434
+ title: str
435
+ type_id: float
436
+ fiscal_year: float | None
437
+ fiscal_period: str | None
438
+ backlink_url: str
439
+ updated_at: datetime
440
+ created_at: datetime
441
+ ```
442
+
443
+ ### DocumentResults
444
+
445
+ Wrapper object returned by `fetch_event_documents`:
446
+
447
+ ```python
448
+ class DocumentResults:
449
+ data: list[DocumentDto] # Access documents via .data attribute
450
+ ```
451
+
452
+ ### DocumentDto
453
+
454
+ ```python
455
+ class DocumentDto:
456
+ company_id: float | None
457
+ event_id: float | None
458
+ file_url: str
459
+ id: float
460
+ type_id: float
461
+ updated_at: datetime
462
+ created_at: datetime
463
+ ```
464
+
465
+ ### QuartrEarningsCallTranscript
466
+
467
+ Complete earnings call transcript with speaker mapping:
468
+
469
+ ```python
470
+ class QuartrEarningsCallTranscript:
471
+ version: str
472
+ event_id: int
473
+ company_id: int
474
+ transcript: Transcript
475
+ speaker_mapping: list[SpeakerMapping]
476
+
477
+ # Methods
478
+ def to_markdown(self) -> str
479
+ @classmethod
480
+ def from_quartr_transcript_url(cls, url: str) -> Self
481
+ @classmethod
482
+ async def from_quartr_transcript_url_async(cls, url: str) -> Self
483
+ ```
484
+
485
+ ## Testing
486
+
487
+ Run the test suite:
488
+
489
+ ```bash
490
+ poetry run pytest
491
+ ```
492
+
493
+ Run with coverage:
494
+
495
+ ```bash
496
+ poetry run pytest --cov=unique_quartr --cov-report=html
497
+ ```
498
+
499
+ Run specific test files:
500
+
501
+ ```bash
502
+ poetry run pytest tests/test_service.py
503
+ poetry run pytest tests/test_constants.py
504
+ ```
505
+
506
+ ## Error Handling
507
+
508
+ The library will raise exceptions in the following cases:
509
+
510
+ - **Missing API Credentials**: `ValueError` when `QUARTR_API_CREDS` is not set
511
+ - **Company Not Activated**: `ValueError` when the company_id is not in `QUARTR_API_ACTIVATED_COMPANIES`
512
+ - **API Errors**: Various HTTP errors from the Quartr API
513
+
514
+ ### Example Error Handling
515
+
516
+ ```python
517
+ from unique_quartr.service import QuartrService
518
+
519
+ try:
520
+ service = QuartrService(
521
+ company_id="invalid_company",
522
+ requestor_type=RequestorType.SYNC,
523
+ )
524
+ except ValueError as e:
525
+ print(f"Configuration error: {e}")
526
+ ```
527
+
528
+ ## Development
529
+
530
+ ### Setup Development Environment
531
+
532
+ ```bash
533
+ # Clone the repository
534
+ git clone <repository_url>
535
+ cd unique_quartr
536
+
537
+ # Install dependencies
538
+ poetry install
539
+
540
+ # Run linting
541
+ poetry run ruff check .
542
+
543
+ # Run formatting
544
+ poetry run ruff format .
545
+ ```
546
+
547
+ ### Project Structure
548
+
549
+ ```
550
+ unique_quartr/
551
+ ├── constants/
552
+ │ ├── document_types.py # Document type enums and mappings
553
+ │ └── event_types.py # Event type enums and mappings
554
+ ├── endpoints/
555
+ │ ├── api.py # API endpoint definitions
556
+ │ └── schemas.py # Pydantic models for API requests/responses
557
+ ├── transcripts/
558
+ │ └── earnings_call/
559
+ │ ├── __init__.py # Transcript module exports
560
+ │ ├── schema.py # Transcript data models
561
+ │ └── transcript_template.j2 # Jinja2 template for markdown export
562
+ ├── service.py # Main service class
563
+ └── settings.py # Configuration and settings
564
+ ```
565
+
566
+ ## License
567
+
568
+ Proprietary
569
+
570
+ ## Authors
571
+
572
+ - Rami Azouz <rami.ext@unique.ch>
573
+
574
+ ## Support
575
+
576
+ For issues and questions, please contact the maintainers or refer to the [Quartr API documentation](https://quartr.dev/api-reference).