Skip to content

Note

Report endpoints are not cached locally.

toggl_api.reports.ReportBody dataclass

Bases: BaseBody

Body for summary endpoint which turns into a JSON body.

Source code in toggl_api/reports/reports.py
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
@dataclass
class ReportBody(BaseBody):
    """Body for summary endpoint which turns into a JSON body."""

    start_date: Optional[date] = field(default=None)
    """Start date, example time.DateOnly. Should be less than End date."""

    end_date: Optional[date] = field(default=None)
    """End date, example time. DateOnly. Should be greater than Start date."""

    client_ids: list[int | None] = field(default_factory=list)
    """Client IDs, optional, filtering attribute. To filter records with no clients, use [None]."""

    description: Optional[str] = field(default=None)
    """Description, optional, filtering attribute."""

    group_ids: list[int] = field(default_factory=list)
    """Group IDs, optional, filtering attribute."""

    grouping: Optional[str] = field(
        default=None,
        metadata={
            "endpoints": frozenset(
                (
                    "summary_time_entries",
                    "summary_report_pdf",
                    "summary_report_csv",
                ),
            ),
        },
    )
    """Grouping option, optional."""

    grouped: bool = field(
        default=False,
        metadata={
            "endpoints": frozenset(
                (
                    "detail_search_time",
                    "detail_report_pdf",
                    "detail_report_csv",
                    "detail_totals",
                ),
            ),
        },
    )
    """Whether time entries should be grouped, optional, default false."""

    include_time_entry_ids: bool = field(
        default=True,  # NOTE: API default is False. Wrapper sets it as True.
        metadata={
            "endpoints": frozenset(
                (
                    "summary_time_entries",
                    "summary_report_pdf",
                    "summary_report_csv",
                ),
            ),
        },
    )
    """Whether time entry IDs should be included in the results, optional, default true. Not applicable for export."""

    max_duration_seconds: Optional[int] = field(default=None)
    """Max duration seconds, optional, filtering attribute. Time Audit only,
    should be greater than min_duration_seconds."""

    min_duration_seconds: Optional[int] = field(default=None)
    """Min duration seconds, optional, filtering attribute. Time Audit only,
    should be less than max_duration_seconds."""

    project_ids: list[int | None] = field(default_factory=list)
    """Project IDs, optional, filtering attribute. To filter records with no projects, use [None]."""

    rounding: Optional[int] = field(default=None)
    """Whether time should be rounded, optional, default from user preferences."""

    rounding_minutes: Optional[Literal[0, 1, 5, 6, 10, 12, 15, 30, 60, 240]] = field(default=None)
    """Rounding minutes value, optional, default from user preferences.
    Should be 0, 1, 5, 6, 10, 12, 15, 30, 60 or 240."""

    sub_grouping: Optional[str] = field(
        default=None,
        metadata={
            "endpoints": frozenset(
                (
                    "summary_time_entries",
                    "summary_report_pdf",
                    "summary_report_csv",
                ),
            ),
        },
    )
    """SubGrouping option, optional."""

    tag_ids: list[int | None] = field(default_factory=list)
    """Tag IDs, optional, filtering attribute. To filter records with no tags, use [None]."""

    time_entry_ids: list[int] = field(default_factory=list)
    """TimeEntryIDs filters by time entries."""

    user_ids: list[int] = field(default_factory=list)
    """User IDs, optional, filtering attribute."""

    date_format: Literal[
        "MM/DD/YYYY",
        "DD-MM-YYYY",
        "MM-DD-YYYY",
        "YYYY-MM-DD",
        "DD/MM/YYYY",
        "DD.MM.YYYY",
    ] = field(
        default="YYYY-MM-DD",  # NOTE: API Default is 'MM/DD/YYYY'
        metadata={
            "endpoints": frozenset(
                (
                    "summary_report_pdf",
                    "detail_report_pdf",
                    "weekly_report_pdf",
                ),
            ),
        },
    )
    """Date format, optional, default 'YYYY-MM-DD'."""

    duration_format: Literal["classic", "decimal", "improved"] = field(
        default="classic",
        metadata={
            "endpoints": frozenset(
                (
                    "summary_report_pdf",
                    "summary_report_csv",
                    "detailed_report_pdf",
                    "detailed_report_csv",
                    "weekly_report_pdf",
                ),
            ),
        },
    )
    """Duration format, optional, default "classic". Can be "classic", "decimal" or "improved"."""

    order_by: Optional[Literal["title", "duration"]] = field(
        default=None,
        metadata={
            "endpoints": frozenset(
                (
                    "summary_report_pdf",
                    "summary_report_csv",
                    "detail_search_time",
                    "detail_report_pdf",
                    "detail_report_csv",
                ),
            ),
        },
    )
    """Order by option, optional, default title. Can be title or duration."""

    order_dir: Optional[Literal["ASC", "DESC"]] = field(
        default=None,
        metadata={
            "endpoints": frozenset(
                (
                    "summary_report_pdf",
                    "summary_report_csv",
                    "detail_search_time",
                    "detail_report_pdf",
                    "detail_report_csv",
                ),
            ),
        },
    )
    """Order direction, optional. Can be ASC or DESC."""

    resolution: Optional[str] = field(
        default=None,
        metadata={
            "endpoints": frozenset(
                (
                    "summary_report_pdf",
                    "detail_totals",
                ),
            ),
        },
    )
    """Graph resolution, optional. Allow clients to explicitly request a resolution."""

    enrich_response: bool = field(
        default=False,
        metadata={
            "endpoints": frozenset(
                (
                    "detail_search_time",
                    "detail_report",
                ),
            ),
        },
    )
    """It will force the detailed report to return as much information as possible, as it does for the export."""

    def format(self, endpoint: str, **body: Any) -> dict[str, Any]:  # noqa: PLR0912
        body.update(
            {
                "client_ids": self.client_ids,
                "project_ids": self.project_ids,
                "tag_ids": self.tag_ids,
                "time_entry_ids": self.time_entry_ids,
                "user_ids": self.user_ids,
            },
        )

        if self.start_date:
            body["start_date"] = format_iso(self.start_date)
            if self.end_date is not None and self.start_date > self.end_date:
                msg = "Start date needs to be on or before the end date!"
                raise AttributeError(msg)

        if self.end_date:
            body["end_date"] = format_iso(self.end_date)

        if self.verify_endpoint_parameter("date_format", endpoint):
            body["date_format"] = self.date_format

        if self.verify_endpoint_parameter("duration_format", endpoint):
            body["duration_format"] = self.duration_format

        if self.include_time_entry_ids and self.verify_endpoint_parameter("include_time_entry_ids", endpoint):
            body["include_time_entry_ids"] = self.include_time_entry_ids

        if self.description is not None:
            body["description"] = self.description

        if self.group_ids:
            body["group_ids"] = self.group_ids

        if self.grouping and self.verify_endpoint_parameter("grouping", endpoint):
            body["grouping"] = self.grouping

        if self.grouped and self.verify_endpoint_parameter("grouped", endpoint):
            body["grouped"] = self.grouped

        if isinstance(self.max_duration_seconds, int):
            body["max_duration_seconds"] = self.max_duration_seconds

        if isinstance(self.min_duration_seconds, int):
            body["min_duration_seconds"] = self.min_duration_seconds

        if isinstance(self.rounding, int):
            body["rounding"] = self.rounding

        if isinstance(self.rounding_minutes, int):
            body["rounding_minutes"] = self.rounding_minutes

        if self.sub_grouping is not None and self.verify_endpoint_parameter("sub_grouping", endpoint):
            body["sub_grouping"] = self.sub_grouping

        if self.order_by is not None and self.verify_endpoint_parameter("order_by", endpoint):
            body["order_by"] = self.order_by

        if self.order_dir is not None and self.verify_endpoint_parameter("order_dir", endpoint):
            body["order_dir"] = self.order_dir

        if self.resolution is not None and self.verify_endpoint_parameter("resolution", endpoint):
            body["resolution"] = self.resolution

        if self.enrich_response and self.verify_endpoint_parameter("enrich_response", endpoint):
            body["enrich_response"] = self.enrich_response

        return body

start_date: Optional[date] = field(default=None) class-attribute instance-attribute

Start date, example time.DateOnly. Should be less than End date.

end_date: Optional[date] = field(default=None) class-attribute instance-attribute

End date, example time. DateOnly. Should be greater than Start date.

client_ids: list[int | None] = field(default_factory=list) class-attribute instance-attribute

Client IDs, optional, filtering attribute. To filter records with no clients, use [None].

description: Optional[str] = field(default=None) class-attribute instance-attribute

Description, optional, filtering attribute.

group_ids: list[int] = field(default_factory=list) class-attribute instance-attribute

Group IDs, optional, filtering attribute.

grouping: Optional[str] = field(default=None, metadata={'endpoints': frozenset(('summary_time_entries', 'summary_report_pdf', 'summary_report_csv'))}) class-attribute instance-attribute

Grouping option, optional.

grouped: bool = field(default=False, metadata={'endpoints': frozenset(('detail_search_time', 'detail_report_pdf', 'detail_report_csv', 'detail_totals'))}) class-attribute instance-attribute

Whether time entries should be grouped, optional, default false.

include_time_entry_ids: bool = field(default=True, metadata={'endpoints': frozenset(('summary_time_entries', 'summary_report_pdf', 'summary_report_csv'))}) class-attribute instance-attribute

Whether time entry IDs should be included in the results, optional, default true. Not applicable for export.

max_duration_seconds: Optional[int] = field(default=None) class-attribute instance-attribute

Max duration seconds, optional, filtering attribute. Time Audit only, should be greater than min_duration_seconds.

min_duration_seconds: Optional[int] = field(default=None) class-attribute instance-attribute

Min duration seconds, optional, filtering attribute. Time Audit only, should be less than max_duration_seconds.

project_ids: list[int | None] = field(default_factory=list) class-attribute instance-attribute

Project IDs, optional, filtering attribute. To filter records with no projects, use [None].

rounding: Optional[int] = field(default=None) class-attribute instance-attribute

Whether time should be rounded, optional, default from user preferences.

rounding_minutes: Optional[Literal[0, 1, 5, 6, 10, 12, 15, 30, 60, 240]] = field(default=None) class-attribute instance-attribute

Rounding minutes value, optional, default from user preferences. Should be 0, 1, 5, 6, 10, 12, 15, 30, 60 or 240.

sub_grouping: Optional[str] = field(default=None, metadata={'endpoints': frozenset(('summary_time_entries', 'summary_report_pdf', 'summary_report_csv'))}) class-attribute instance-attribute

SubGrouping option, optional.

tag_ids: list[int | None] = field(default_factory=list) class-attribute instance-attribute

Tag IDs, optional, filtering attribute. To filter records with no tags, use [None].

time_entry_ids: list[int] = field(default_factory=list) class-attribute instance-attribute

TimeEntryIDs filters by time entries.

user_ids: list[int] = field(default_factory=list) class-attribute instance-attribute

User IDs, optional, filtering attribute.

date_format: Literal['MM/DD/YYYY', 'DD-MM-YYYY', 'MM-DD-YYYY', 'YYYY-MM-DD', 'DD/MM/YYYY', 'DD.MM.YYYY'] = field(default='YYYY-MM-DD', metadata={'endpoints': frozenset(('summary_report_pdf', 'detail_report_pdf', 'weekly_report_pdf'))}) class-attribute instance-attribute

Date format, optional, default 'YYYY-MM-DD'.

duration_format: Literal['classic', 'decimal', 'improved'] = field(default='classic', metadata={'endpoints': frozenset(('summary_report_pdf', 'summary_report_csv', 'detailed_report_pdf', 'detailed_report_csv', 'weekly_report_pdf'))}) class-attribute instance-attribute

Duration format, optional, default "classic". Can be "classic", "decimal" or "improved".

order_by: Optional[Literal['title', 'duration']] = field(default=None, metadata={'endpoints': frozenset(('summary_report_pdf', 'summary_report_csv', 'detail_search_time', 'detail_report_pdf', 'detail_report_csv'))}) class-attribute instance-attribute

Order by option, optional, default title. Can be title or duration.

order_dir: Optional[Literal['ASC', 'DESC']] = field(default=None, metadata={'endpoints': frozenset(('summary_report_pdf', 'summary_report_csv', 'detail_search_time', 'detail_report_pdf', 'detail_report_csv'))}) class-attribute instance-attribute

Order direction, optional. Can be ASC or DESC.

resolution: Optional[str] = field(default=None, metadata={'endpoints': frozenset(('summary_report_pdf', 'detail_totals'))}) class-attribute instance-attribute

Graph resolution, optional. Allow clients to explicitly request a resolution.

enrich_response: bool = field(default=False, metadata={'endpoints': frozenset(('detail_search_time', 'detail_report'))}) class-attribute instance-attribute

It will force the detailed report to return as much information as possible, as it does for the export.

toggl_api.reports.ReportEndpoint

Bases: TogglEndpoint

Abstract baseclass for the reports endpoint that overrides BASE_ENDPOINT.


Summary Reports

toggl_api.reports.SummaryReportEndpoint

Bases: ReportEndpoint

Summary reports endpoints.

Official Documentation

Source code in toggl_api/reports/reports.py
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
class SummaryReportEndpoint(ReportEndpoint):
    """Summary reports endpoints.

    [Official Documentation](https://engineering.toggl.com/docs/reports/summary_reports)
    """

    def project_summary(
        self,
        project: TogglProject | int,
        start_date: date | str,
        end_date: date | str,
    ) -> list[dict[str, int]]:
        """Returns a specific projects summary within the parameters provided.

        [Official Documentation](https://engineering.toggl.com/docs/reports/summary_reports#post-load-project-summary)

        Args:
            project: Project to retrieve summaries about.
            start_date: The date to gather project summary data from.
            end_date: The date to gather project summary data to.

        Returns:
            A list of dictionary with the summary data.
        """

        return self.request(
            f"projects/{project.id if isinstance(project, TogglProject) else project}/summary",
            method=RequestMethod.POST,
            body={
                "start_date": format_iso(start_date),
                "end_date": format_iso(end_date),
            },
        )

    def project_summaries(
        self,
        start_date: date | str,
        end_date: date | str,
    ) -> list[dict[str, int]]:
        """Returns a summary of user projects according to parameters provided.

        [Official Documentation](https://engineering.toggl.com/docs/reports/summary_reports#post-list-project-users)

        Args:
            start_date: The date to gather project summaries from.
            end_date: The date to gather project summaries data to.

        Returns:
            A list of dictionary with the summary data.
        """

        return self.request(
            "projects/summary",
            method=RequestMethod.POST,
            body={
                "start_date": format_iso(start_date),
                "end_date": format_iso(end_date),
            },
        )

    def search_time_entries(self, body: ReportBody) -> list[dict[str, int]]:
        """Returns a list of time entries within the parameters specified.

        [Official Documentation](https://engineering.toggl.com/docs/reports/summary_reports#post-search-time-entries)

        Args:
            body: Body parameters to filter time entries by.

        Returns:
            A list of dictionaries with the filtered tracker data.
        """
        return self.request(
            "summary/time_entries",
            method=RequestMethod.POST,
            body=body.format(
                "summary_time_entries",
                workspace_id=self.workspace_id,
            ),
        )

    def time_entries(self, body: ReportBody) -> list[dict[str, int]]:
        """Returns a list of time entries within the parameters specified.

        DEPRECATED: Use ['search_time_entries'][toggl_api.reports.SummaryReportEndpoint.search_time_entries] instead.

        [Official Documentation](https://engineering.toggl.com/docs/reports/summary_reports#post-search-time-entries)

        Args:
            body: Body parameters to filter time entries by.

        Returns:
            A list of dictionaries with the filtered tracker data.
        """
        warnings.warn(
            "Deprecated: Use 'search_time_entries' instead.",
            DeprecationWarning,
            stacklevel=3,
        )
        return self.search_time_entries(body)

    def export_report(
        self,
        body: ReportBody,
        extension: REPORT_FORMATS,
        *,
        collapse: bool = False,
    ) -> bytes:
        """Downloads summary report in the specified in the specified format: csv or pdf.

        [Official Documentation](https://engineering.toggl.com/docs/reports/summary_reports#post-export-summary-report)

        Args:
            body: Body parameters to filter the report by.
            extension: What format to use for the report. CSV or PDF.
            collapse: Whether collapse others. Inserted into body.

        Raises:
            ValueError: If extension is not pdf or csv.

        Returns:
            A format ready to be saved as a file or used for further processing.
        """
        _validate_extension(extension)

        return self.request(
            f"summary/time_entries.{extension}",
            method=RequestMethod.POST,
            body=body.format(
                f"summary_report_{extension}",
                workspace_id=self.workspace_id,
                collapse=collapse,
            ),
            raw=True,
        ).content

    def export_summary(
        self,
        body: ReportBody,
        extension: REPORT_FORMATS,
        *,
        collapse: bool = False,
    ) -> bytes:
        """Downloads summary report in the specified in the specified format: csv or pdf.

        DEPRECATED: Use ['export_report'][toggl_api.reports.SummaryReportEndpoint.export_report] instead.

        [Official Documentation](https://engineering.toggl.com/docs/reports/summary_reports#post-export-summary-report)

        Args:
            body: Body parameters to filter the report by.
            extension: What format to use for the report. CSV or PDF.
            collapse: Whether collapse others. Inserted into body.

        Raises:
            ValueError: If extension is not pdf or csv.

        Returns:
            A format ready to be saved as a file or used for further processing.
        """
        warnings.warn(
            "Deprecated: Use 'export_report' instead.",
            DeprecationWarning,
            stacklevel=3,
        )
        return self.export_report(body, extension, collapse=collapse)

    @property
    def endpoint(self) -> str:
        return self.BASE_ENDPOINT + f"workspace/{self.workspace_id}/"

project_summary(project: TogglProject | int, start_date: date | str, end_date: date | str) -> list[dict[str, int]]

Returns a specific projects summary within the parameters provided.

Official Documentation

Parameters:

  • project (TogglProject | int) –

    Project to retrieve summaries about.

  • start_date (date | str) –

    The date to gather project summary data from.

  • end_date (date | str) –

    The date to gather project summary data to.

Returns:

  • list[dict[str, int]]

    A list of dictionary with the summary data.

Source code in toggl_api/reports/reports.py
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
def project_summary(
    self,
    project: TogglProject | int,
    start_date: date | str,
    end_date: date | str,
) -> list[dict[str, int]]:
    """Returns a specific projects summary within the parameters provided.

    [Official Documentation](https://engineering.toggl.com/docs/reports/summary_reports#post-load-project-summary)

    Args:
        project: Project to retrieve summaries about.
        start_date: The date to gather project summary data from.
        end_date: The date to gather project summary data to.

    Returns:
        A list of dictionary with the summary data.
    """

    return self.request(
        f"projects/{project.id if isinstance(project, TogglProject) else project}/summary",
        method=RequestMethod.POST,
        body={
            "start_date": format_iso(start_date),
            "end_date": format_iso(end_date),
        },
    )

project_summaries(start_date: date | str, end_date: date | str) -> list[dict[str, int]]

Returns a summary of user projects according to parameters provided.

Official Documentation

Parameters:

  • start_date (date | str) –

    The date to gather project summaries from.

  • end_date (date | str) –

    The date to gather project summaries data to.

Returns:

  • list[dict[str, int]]

    A list of dictionary with the summary data.

Source code in toggl_api/reports/reports.py
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
def project_summaries(
    self,
    start_date: date | str,
    end_date: date | str,
) -> list[dict[str, int]]:
    """Returns a summary of user projects according to parameters provided.

    [Official Documentation](https://engineering.toggl.com/docs/reports/summary_reports#post-list-project-users)

    Args:
        start_date: The date to gather project summaries from.
        end_date: The date to gather project summaries data to.

    Returns:
        A list of dictionary with the summary data.
    """

    return self.request(
        "projects/summary",
        method=RequestMethod.POST,
        body={
            "start_date": format_iso(start_date),
            "end_date": format_iso(end_date),
        },
    )

search_time_entries(body: ReportBody) -> list[dict[str, int]]

Returns a list of time entries within the parameters specified.

Official Documentation

Parameters:

  • body (ReportBody) –

    Body parameters to filter time entries by.

Returns:

  • list[dict[str, int]]

    A list of dictionaries with the filtered tracker data.

Source code in toggl_api/reports/reports.py
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
def search_time_entries(self, body: ReportBody) -> list[dict[str, int]]:
    """Returns a list of time entries within the parameters specified.

    [Official Documentation](https://engineering.toggl.com/docs/reports/summary_reports#post-search-time-entries)

    Args:
        body: Body parameters to filter time entries by.

    Returns:
        A list of dictionaries with the filtered tracker data.
    """
    return self.request(
        "summary/time_entries",
        method=RequestMethod.POST,
        body=body.format(
            "summary_time_entries",
            workspace_id=self.workspace_id,
        ),
    )

time_entries(body: ReportBody) -> list[dict[str, int]]

Returns a list of time entries within the parameters specified.

Deprecated

Use 'search_time_entries' instead.

Official Documentation

Parameters:

  • body (ReportBody) –

    Body parameters to filter time entries by.

Returns:

  • list[dict[str, int]]

    A list of dictionaries with the filtered tracker data.

Source code in toggl_api/reports/reports.py
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
def time_entries(self, body: ReportBody) -> list[dict[str, int]]:
    """Returns a list of time entries within the parameters specified.

    DEPRECATED: Use ['search_time_entries'][toggl_api.reports.SummaryReportEndpoint.search_time_entries] instead.

    [Official Documentation](https://engineering.toggl.com/docs/reports/summary_reports#post-search-time-entries)

    Args:
        body: Body parameters to filter time entries by.

    Returns:
        A list of dictionaries with the filtered tracker data.
    """
    warnings.warn(
        "Deprecated: Use 'search_time_entries' instead.",
        DeprecationWarning,
        stacklevel=3,
    )
    return self.search_time_entries(body)

export_report(body: ReportBody, extension: REPORT_FORMATS, *, collapse: bool = False) -> bytes

Downloads summary report in the specified in the specified format: csv or pdf.

Official Documentation

Parameters:

  • body (ReportBody) –

    Body parameters to filter the report by.

  • extension (REPORT_FORMATS) –

    What format to use for the report. CSV or PDF.

  • collapse (bool, default: False ) –

    Whether collapse others. Inserted into body.

Raises:

  • ValueError

    If extension is not pdf or csv.

Returns:

  • bytes

    A format ready to be saved as a file or used for further processing.

Source code in toggl_api/reports/reports.py
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
def export_report(
    self,
    body: ReportBody,
    extension: REPORT_FORMATS,
    *,
    collapse: bool = False,
) -> bytes:
    """Downloads summary report in the specified in the specified format: csv or pdf.

    [Official Documentation](https://engineering.toggl.com/docs/reports/summary_reports#post-export-summary-report)

    Args:
        body: Body parameters to filter the report by.
        extension: What format to use for the report. CSV or PDF.
        collapse: Whether collapse others. Inserted into body.

    Raises:
        ValueError: If extension is not pdf or csv.

    Returns:
        A format ready to be saved as a file or used for further processing.
    """
    _validate_extension(extension)

    return self.request(
        f"summary/time_entries.{extension}",
        method=RequestMethod.POST,
        body=body.format(
            f"summary_report_{extension}",
            workspace_id=self.workspace_id,
            collapse=collapse,
        ),
        raw=True,
    ).content

export_summary(body: ReportBody, extension: REPORT_FORMATS, *, collapse: bool = False) -> bytes

Downloads summary report in the specified in the specified format: csv or pdf.

Deprecated

Use 'export_report' instead.

Official Documentation

Parameters:

  • body (ReportBody) –

    Body parameters to filter the report by.

  • extension (REPORT_FORMATS) –

    What format to use for the report. CSV or PDF.

  • collapse (bool, default: False ) –

    Whether collapse others. Inserted into body.

Raises:

  • ValueError

    If extension is not pdf or csv.

Returns:

  • bytes

    A format ready to be saved as a file or used for further processing.

Source code in toggl_api/reports/reports.py
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
def export_summary(
    self,
    body: ReportBody,
    extension: REPORT_FORMATS,
    *,
    collapse: bool = False,
) -> bytes:
    """Downloads summary report in the specified in the specified format: csv or pdf.

    DEPRECATED: Use ['export_report'][toggl_api.reports.SummaryReportEndpoint.export_report] instead.

    [Official Documentation](https://engineering.toggl.com/docs/reports/summary_reports#post-export-summary-report)

    Args:
        body: Body parameters to filter the report by.
        extension: What format to use for the report. CSV or PDF.
        collapse: Whether collapse others. Inserted into body.

    Raises:
        ValueError: If extension is not pdf or csv.

    Returns:
        A format ready to be saved as a file or used for further processing.
    """
    warnings.warn(
        "Deprecated: Use 'export_report' instead.",
        DeprecationWarning,
        stacklevel=3,
    )
    return self.export_report(body, extension, collapse=collapse)

Detailed Reports

toggl_api.reports.reports.PaginatedResult dataclass

Bases: Generic[T]

Generic dataclass for paginated results.

Source code in toggl_api/reports/reports.py
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@dataclass
class PaginatedResult(Generic[T]):
    """Generic dataclass for paginated results."""

    result: T = field()
    next_id: Optional[int] = field(default=None)
    next_row: Optional[int] = field(default=None)

    def __post_init__(self) -> None:
        # NOTE: Header types are strings so post init converts to integer.
        if self.next_id:
            self.next_id = int(self.next_id)
        if self.next_row:
            self.next_row = int(self.next_row)

    def next_options(self, page_size: int = 50) -> PaginationOptions:
        return PaginationOptions(page_size, self.next_id, self.next_row)

toggl_api.reports.PaginationOptions dataclass

Dataclass for paginate endpoints.

Source code in toggl_api/reports/reports.py
21
22
23
24
25
26
27
@dataclass(frozen=True)
class PaginationOptions:
    """Dataclass for paginate endpoints."""

    page_size: int = field(default=50)
    next_id: Optional[int] = field(default=None)
    next_row: Optional[int] = field(default=None)

toggl_api.reports.DetailedReportEndpoint

Bases: ReportEndpoint

Detailed reports endpoint.

Official Documentation

Source code in toggl_api/reports/reports.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
class DetailedReportEndpoint(ReportEndpoint):
    """Detailed reports endpoint.

    [Official Documentation](https://engineering.toggl.com/docs/reports/detailed_reports)
    """

    @staticmethod
    def _paginate(request: Response, *, raw: bool = False) -> PaginatedResult:
        return PaginatedResult(
            request.content if raw else request.json(),
            request.headers.get("x-next-id"),
            request.headers.get("x-next-row-number"),
        )

    @staticmethod
    def _paginate_body(
        body: dict[str, Any],
        pagination: PaginationOptions,
    ) -> dict[str, Any]:
        body["page_size"] = pagination.page_size
        if pagination.next_id is not None and pagination.next_row is not None:
            body["first_id"] = pagination.next_id
            body["first_row_number"] = pagination.next_row

        return body

    def search_time_entries(
        self,
        body: ReportBody,
        pagination: Optional[PaginationOptions] = None,
        *,
        hide_amounts: bool = False,
    ) -> PaginatedResult[list]:
        """Returns time entries for detailed report according to the given filters.

        [Official Documentation](https://engineering.toggl.com/docs/reports/detailed_reports#post-search-time-entries)

        Args:
            body: JSON body with filters for time entries.
            pagination: Pagination options containing page size, next_id and next_row.
            hide_amounts: Whether amounts should be hidden.

        Returns:
            Data with pagination information if required.
        """

        pagination = pagination or PaginationOptions()

        request: Response = self.request(
            "",
            body=self._paginate_body(
                body.format(
                    "detail_search_time",
                    workspace_id=self.workspace_id,
                    hide_amounts=hide_amounts,
                ),
                pagination,
            ),
            method=RequestMethod.POST,
            raw=True,
        )

        return self._paginate(request)

    def export_report(
        self,
        body: ReportBody,
        extension: REPORT_FORMATS,
        pagination: Optional[PaginationOptions] = None,
        *,
        hide_amounts: bool = False,
    ) -> PaginatedResult[bytes]:
        """Downloads detailed report in pdf or csv format.

        [Official Documentation](https://engineering.toggl.com/docs/reports/detailed_reports#post-export-detailed-report)

        Args:
            body: JSON body for formatting and filtering the report.
            extension: Format of the exported report. PDF or CSV.
            pagination: Pagination options containing page size, next_id and next_row.
            hide_amounts: Whether amounts should be hidden.

        Raises:
            ValueError: If extension is not pdf or csv.
            HTTPStatusError: If the request is not a success.

        Returns:
            Report ready to be saved or further processed in python.
        """
        _validate_extension(extension)

        pagination = pagination or PaginationOptions()
        request = self.request(
            f".{extension}",
            body=self._paginate_body(
                body.format(
                    f"detail_report_{extension}",
                    workspace_id=self.workspace_id,
                    hide_amounts=hide_amounts,
                ),
                pagination,
            ),
            method=RequestMethod.POST,
            raw=True,
        )
        return self._paginate(request, raw=True)

    def totals_report(
        self,
        body: ReportBody,
        *,
        granularity: Literal["day", "week", "month"] = "day",
        with_graph: bool = False,
    ) -> dict[str, int]:
        """Returns totals sums for detailed report.

        [Official Documentation](https://engineering.toggl.com/docs/reports/detailed_reports#post-load-totals-detailed-report)

        Args:
            body: JSON body for filtering the report.
            granularity: Totals granularity, optional, overrides resolution values.
            with_graph: Whether Graph information should be loaded.

        Returns:
            With the totals relevant to the provided filters.
        """
        return self.request(
            "/totals",
            body=body.format(
                "detail_totals",
                workspace_id=self.workspace_id,
                granularity=granularity,
                with_graph=with_graph,
            ),
            method=RequestMethod.POST,
        )

    @property
    def endpoint(self) -> str:
        return self.BASE_ENDPOINT + f"workspace/{self.workspace_id}/search/time_entries"

search_time_entries(body: ReportBody, pagination: Optional[PaginationOptions] = None, *, hide_amounts: bool = False) -> PaginatedResult[list]

Returns time entries for detailed report according to the given filters.

Official Documentation

Parameters:

  • body (ReportBody) –

    JSON body with filters for time entries.

  • pagination (Optional[PaginationOptions], default: None ) –

    Pagination options containing page size, next_id and next_row.

  • hide_amounts (bool, default: False ) –

    Whether amounts should be hidden.

Returns:

Source code in toggl_api/reports/reports.py
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
def search_time_entries(
    self,
    body: ReportBody,
    pagination: Optional[PaginationOptions] = None,
    *,
    hide_amounts: bool = False,
) -> PaginatedResult[list]:
    """Returns time entries for detailed report according to the given filters.

    [Official Documentation](https://engineering.toggl.com/docs/reports/detailed_reports#post-search-time-entries)

    Args:
        body: JSON body with filters for time entries.
        pagination: Pagination options containing page size, next_id and next_row.
        hide_amounts: Whether amounts should be hidden.

    Returns:
        Data with pagination information if required.
    """

    pagination = pagination or PaginationOptions()

    request: Response = self.request(
        "",
        body=self._paginate_body(
            body.format(
                "detail_search_time",
                workspace_id=self.workspace_id,
                hide_amounts=hide_amounts,
            ),
            pagination,
        ),
        method=RequestMethod.POST,
        raw=True,
    )

    return self._paginate(request)

export_report(body: ReportBody, extension: REPORT_FORMATS, pagination: Optional[PaginationOptions] = None, *, hide_amounts: bool = False) -> PaginatedResult[bytes]

Downloads detailed report in pdf or csv format.

Official Documentation

Parameters:

  • body (ReportBody) –

    JSON body for formatting and filtering the report.

  • extension (REPORT_FORMATS) –

    Format of the exported report. PDF or CSV.

  • pagination (Optional[PaginationOptions], default: None ) –

    Pagination options containing page size, next_id and next_row.

  • hide_amounts (bool, default: False ) –

    Whether amounts should be hidden.

Raises:

  • ValueError

    If extension is not pdf or csv.

  • HTTPStatusError

    If the request is not a success.

Returns:

  • PaginatedResult[bytes]

    Report ready to be saved or further processed in python.

Source code in toggl_api/reports/reports.py
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
def export_report(
    self,
    body: ReportBody,
    extension: REPORT_FORMATS,
    pagination: Optional[PaginationOptions] = None,
    *,
    hide_amounts: bool = False,
) -> PaginatedResult[bytes]:
    """Downloads detailed report in pdf or csv format.

    [Official Documentation](https://engineering.toggl.com/docs/reports/detailed_reports#post-export-detailed-report)

    Args:
        body: JSON body for formatting and filtering the report.
        extension: Format of the exported report. PDF or CSV.
        pagination: Pagination options containing page size, next_id and next_row.
        hide_amounts: Whether amounts should be hidden.

    Raises:
        ValueError: If extension is not pdf or csv.
        HTTPStatusError: If the request is not a success.

    Returns:
        Report ready to be saved or further processed in python.
    """
    _validate_extension(extension)

    pagination = pagination or PaginationOptions()
    request = self.request(
        f".{extension}",
        body=self._paginate_body(
            body.format(
                f"detail_report_{extension}",
                workspace_id=self.workspace_id,
                hide_amounts=hide_amounts,
            ),
            pagination,
        ),
        method=RequestMethod.POST,
        raw=True,
    )
    return self._paginate(request, raw=True)

totals_report(body: ReportBody, *, granularity: Literal['day', 'week', 'month'] = 'day', with_graph: bool = False) -> dict[str, int]

Returns totals sums for detailed report.

Official Documentation

Parameters:

  • body (ReportBody) –

    JSON body for filtering the report.

  • granularity (Literal['day', 'week', 'month'], default: 'day' ) –

    Totals granularity, optional, overrides resolution values.

  • with_graph (bool, default: False ) –

    Whether Graph information should be loaded.

Returns:

  • dict[str, int]

    With the totals relevant to the provided filters.

Source code in toggl_api/reports/reports.py
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
def totals_report(
    self,
    body: ReportBody,
    *,
    granularity: Literal["day", "week", "month"] = "day",
    with_graph: bool = False,
) -> dict[str, int]:
    """Returns totals sums for detailed report.

    [Official Documentation](https://engineering.toggl.com/docs/reports/detailed_reports#post-load-totals-detailed-report)

    Args:
        body: JSON body for filtering the report.
        granularity: Totals granularity, optional, overrides resolution values.
        with_graph: Whether Graph information should be loaded.

    Returns:
        With the totals relevant to the provided filters.
    """
    return self.request(
        "/totals",
        body=body.format(
            "detail_totals",
            workspace_id=self.workspace_id,
            granularity=granularity,
            with_graph=with_graph,
        ),
        method=RequestMethod.POST,
    )

Weekly Reports

toggl_api.reports.WeeklyReportEndpoint

Bases: ReportEndpoint

Weekly reports endpoint.

Official Documentation

Source code in toggl_api/reports/reports.py
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
class WeeklyReportEndpoint(ReportEndpoint):
    """Weekly reports endpoint.

    [Official Documentation](https://engineering.toggl.com/docs/reports/weekly_reports)
    """

    def search_time_entries(self, body: ReportBody) -> list[dict[str, Any]]:
        """Returns time entries for weekly report according to the given filters.

        [Official Documentation](https://engineering.toggl.com/docs/reports/detailed_reports#post-search-time-entries)

        Args:
            body: JSON body for filtering time entries.

        Returns:
            A List of time entries filted by the formatted body.
        """
        return self.request(
            "",
            body=body.format(
                "weekly_time_entries",
                workspace_id=self.workspace_id,
            ),
            method=RequestMethod.POST,
        )

    def export_report(self, body: ReportBody, extension: REPORT_FORMATS) -> bytes:
        """Downloads weekly report in pdf or csv format.

        [Official Documentation](https://engineering.toggl.com/docs/reports/weekly_reports#post-export-weekly-report)

        Args:
            body: JSON body for filtering time entries.
            extension: extension: Format of the exported report. PDF or CSV.

        Raises:
            ValueError: If extension is not pdf or csv.

        Returns:
            Report ready to be saved or further processed in python.
        """
        _validate_extension(extension)
        return self.request(
            f".{extension}",
            body=body.format(
                f"weekly_report_{extension}",
                workspace_id=self.workspace_id,
            ),
            method=RequestMethod.POST,
            raw=True,
        ).content

    @property
    def endpoint(self) -> str:
        return self.BASE_ENDPOINT + f"workspace/{self.workspace_id}/weekly/time_entries"

search_time_entries(body: ReportBody) -> list[dict[str, Any]]

Returns time entries for weekly report according to the given filters.

Official Documentation

Parameters:

  • body (ReportBody) –

    JSON body for filtering time entries.

Returns:

  • list[dict[str, Any]]

    A List of time entries filted by the formatted body.

Source code in toggl_api/reports/reports.py
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
def search_time_entries(self, body: ReportBody) -> list[dict[str, Any]]:
    """Returns time entries for weekly report according to the given filters.

    [Official Documentation](https://engineering.toggl.com/docs/reports/detailed_reports#post-search-time-entries)

    Args:
        body: JSON body for filtering time entries.

    Returns:
        A List of time entries filted by the formatted body.
    """
    return self.request(
        "",
        body=body.format(
            "weekly_time_entries",
            workspace_id=self.workspace_id,
        ),
        method=RequestMethod.POST,
    )

export_report(body: ReportBody, extension: REPORT_FORMATS) -> bytes

Downloads weekly report in pdf or csv format.

Official Documentation

Parameters:

  • body (ReportBody) –

    JSON body for filtering time entries.

  • extension (REPORT_FORMATS) –

    extension: Format of the exported report. PDF or CSV.

Raises:

  • ValueError

    If extension is not pdf or csv.

Returns:

  • bytes

    Report ready to be saved or further processed in python.

Source code in toggl_api/reports/reports.py
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
def export_report(self, body: ReportBody, extension: REPORT_FORMATS) -> bytes:
    """Downloads weekly report in pdf or csv format.

    [Official Documentation](https://engineering.toggl.com/docs/reports/weekly_reports#post-export-weekly-report)

    Args:
        body: JSON body for filtering time entries.
        extension: extension: Format of the exported report. PDF or CSV.

    Raises:
        ValueError: If extension is not pdf or csv.

    Returns:
        Report ready to be saved or further processed in python.
    """
    _validate_extension(extension)
    return self.request(
        f".{extension}",
        body=body.format(
            f"weekly_report_{extension}",
            workspace_id=self.workspace_id,
        ),
        method=RequestMethod.POST,
        raw=True,
    ).content