NEWS
togglr 1.0.0
Breaking changes
- Reports come from the Reports API v3 (#76):
get_summary_report() items no
longer give cur, sum and rate (NA); entries without a visible project
are grouped under an NA id and name instead of "Private Project";
the user_agent argument of the report functions is ignored. The columns
and types of get_detailled_report() are unchanged. The rounding set in the
Toggl profile is never applied (rounding = 0 in every request), as in
Reports v2.
get_workspace_id() returns the default workspace of the account, no longer
always the first one (#23).
get_time_entries() fails before any request for a period starting more
than 3 months ago (the API refused it anyway) (#13).
create_client() returns the created client (a list), no longer the httr
response (#54); toggl_update_entries() sends one PUT per entry and returns
the updated entry, or a list of them (#56).
- A write refused by the API raises an error (#54);
toggl_start() and
toggl_create() return NA, with a warning, when the API accepted the
entry but its answer could not be read.
get_toggl_api_token() fails, outside an interactive session, instead of
prompting, and never deletes the stored key (#51).
set_toggl_api_token() returns the token invisibly (#84).
simplify() is rewritten on the public API of jsonlite (#69).
get_detailled_report(): cache = FALSE by default, max_page and
memoise_cache_dir deprecated (#60, #67); a 7th positional argument now
goes to cache.
Other changes
get_detailled_report() reads Reports API v3 (/search/time_entries,
enriched with the names of users, projects, clients and tags) instead of
Reports v2, with the same columns and types. Pages of 1000 entries
(options(togglr.page_size)) instead of 50: a month of a team of 20 is read
in one or two requests. The period is checked against the total duration of
/search/time_entries/totals, equal before and after the pagination and,
up to the rounding of the API (1 s per entry), to the sum of the entries.
New argument user_ids; user_agent is ignored (#76).
- The completeness checks of
get_detailled_report() also treat as a movement
any entry received that was edited after the read started: the pages are
numbered by row, so a deletion followed by an insertion of the same duration
shifts the rows without changing the totals. This replaces the total_grand
tolerance for running timers, which could hide such a movement (#77). A
running timer may only grow by the time between the two totals, measured by
the clock of the server from its own start, not by the time spent waiting
before the first total. One movement still escapes: an entry deleted and
replaced by one of exactly the same duration that is not in the pages
received.
get_summary_report(), get_weekly_report() and get_dashboard() read
Reports API v3 too, with the output of Reports v2. Reports v3 only returns
ids: the names of users, projects and clients come from the Track API v9,
archived projects included. get_weekly_report() returns an empty tibble
for a week without any entry. An unreadable answer to a report request is an
error, never an empty report.
The amounts of the summary items (cur, sum, rate) are no longer given
(NA). Entries without a visible project are grouped with an NA id and name
(Reports v2 called them "Private Project") (#76).
- With a shared state file, an exhausted quota, or a spread of the requests,
recorded by another process while this one was waiting is no longer erased:
it is waited for in turn, or fails within
max_wait (#78).
- The requests of Reports v3 (
/reports/api/v3/workspace/<id>/) count against
the quota of their workspace, like the requests of the API v9
(/workspaces/<id>/): an exhausted Reports quota no longer blocks /me.
get_time_entries() is never truncated: /me/time_entries returns at most
1000 entries, so a full answer splits the period in two, recursively. A
period starting more than 3 months ago (the limit of the API) fails before
any request, pointing to get_detailled_report(user_ids = ) (#13). Its
output gains a client_name column; project_name and client_name cover
the archived projects too (#14). get_project_id_and_name() gains active
(NULL, "false" or "both") and returns 0 rows, not an error, for a
workspace without any matching project. since and until may be text
dates again.
get_workspace_id() returns the default workspace of the account
(default_workspace_id of /me), and the first workspace only when no
default is set; it used to be always the first one. New get_workspaces()
lists every workspace, the default one flagged (#23).
get_dashboard() and get_all_project_names() gain user_ids, to keep the
projects some team members worked on (#12).
- New
get_project_data(): the data of one project from its id (#28).
toggl_start(tags = ) sends its tags, now covered by a test (#16).
set_toggl_api_token() returns the token invisibly (it was printed when
called at top level, e.g. in a script whose output is logged), and checks
the new token before deleting the stored one. update_toggl_api_token()
asks first: a cancelled input keeps the stored token (#84).
- Tests: coverage from 85 % to 93 %, a real multi-process test of
togglr.state_file, no test depending on covr's instrumentation; new
workflow measuring the coverage (#84, #32, #33).
- DESCRIPTION declares
R (>= 4.1.0) and the minimal versions of its
dependencies; the obsolete inst/dependencies.R is no longer shipped (#83).
- The API token is no longer printed:
create_client() and
toggl_create_project() used httr::verbose(), which shows the
Authorization header (#50).
- Every request times out after
options(togglr.timeout) seconds (120 by
default), and network errors (timeout, DNS, connection reset) are retried
like a 5xx (#45).
get_detailled_report() returns fixed column types (e.g. task is always
text, tid integer), whatever the content of the page: periods over a year
with an empty year no longer fail to combine (#46).
- New option
togglr.state_file: the pacing of the reports calls and the
knowledge of an exhausted quota are stored in that file, under a lock, and
shared by every R process using it (callr / future / targets workers, cron
jobs). Without it, the state stays private to the session (#48).
- A 402 without quota headers (a feature of a paid plan) now stops at once with
the message of the API, instead of blocking every request for an hour. An
exhausted quota is remembered per token, as a hash: the token is never
stored (#49).
- The
api_token and workspace_id given to a function are now used by every
call it makes: project, client and dashboard helpers, get_time_entries()
(new workspace_id argument), toggl_start(), toggl_stop(),
toggl_create(), toggl_create_project() no longer fall back to the keyring
token nor to an extra /workspaces request. client_name_to_id() and
client_id_to_name() gain a workspace_id argument (#51).
get_toggl_api_token() no longer deletes the stored key when the keyring
cannot be read (e.g. locked under cron); in a non interactive session it
fails with a clear message instead of prompting (#51).
toggl_start() and toggl_create() send the start time converted to UTC,
instead of removing one hour: entries were one hour off in summer, and more
outside Paris (#52, from #44 by @bradyrippon).
get_current_duration() counts from the start of the running entry, not
from its last edit (at); get_project_total() and
get_current_project_task_detail() were affected (#53).
- Every write (POST, PUT, PATCH) goes through
toggl_request(), with the
policy of the reads: an error of the API is raised with its message (no more
"$ operator is invalid for atomic vectors"), the quota and 429 are respected.
A POST is never retried after a 5xx or a network error, as it may have been
processed. toggl_start() / toggl_create() notify then raise the error;
toggl_stop() only notifies STOP once the API accepted it. create_client()
now returns the created client (a list) instead of the raw httr response
(#54).
toggl_create_project() works again: no crash without client, an existing
client is reused instead of being created again, its client_id is sent,
and the body follows the v9 format (private, color, active are no
longer ignored) (#55).
toggl_update_entries() sends one PUT per entry (the v9 API takes a single
id per PUT), sends stop (it was accepted then dropped), sends the project as
project_id, and no longer sends the read-only at. It returns the updated
entry, or a list of them for several ids (#56).
get_time_entries() sends its dates in UTC (...Z): the + of a time zone
offset was sent unencoded in the url, and read as a space by the server
(#57). Entries with several tags are covered by a test (#30).
get_detailled_report() splits by calendar year as soon as a period spans
365 days or more across a new year: a 367 days period (bounds included) was
sent as is, beyond the one year limit of the reports API v2 (#58).
- The pagination of
get_all_projects() is bounded: it stops with an error if
the API returns the same page twice, or after options(togglr.max_pages)
pages (100 by default), instead of spending the quota in an endless loop
(#59).
get_detailled_report(max_page = ) now warns that the argument is
deprecated and ignored (#60).
- The reports functions (
get_detailled_report(), get_summary_report(),
get_weekly_report(), get_dashboard()) accept since / until as a
Date, a date-time (its day in its own time zone) or a text "YYYY-MM-DD",
and fail with a clear message, before any request, on anything else or on
since after until (#61).
- Default periods ("one year ago") are computed with
%m-%: they were NA
on February 29th (#62).
options(togglr.min_interval) (1 s by default) now spaces every request,
reads and writes, retries included, not only the pages of the detailled
report (#64).
- A period that keeps moving during the pagination is read up to
options(togglr.report_attempts) times (3 by default, 2 before) (#65).
- An entry seen twice during the pagination keeps its most recently updated
version, not the first one read (#66).
- The reports cache (
cache = TRUE) lives in its own folder, the reports
sub-folder of rappdirs::user_cache_dir("togglr") or
options(togglr.cache_dir), apart from the memoise files of 0.2.x. New
argument cache_dir and function clean_cache(); memoise_cache_dir and
clean_memoise_cache() are deprecated (#67).
- The quota announced by every answer (
x-toggl-quota-remaining) is used: at
0, the next request waits for the reset (or fails within max_wait) instead
of receiving a 402; with fewer than options(togglr.quota_spread) (10)
requests left, they are spread until the reset (#68).
simplify() no longer calls an internal function of jsonlite
(getFromNamespace()): it uses the public toJSON() / fromJSON(), at full
precision, with the same result (#69).
globalVariables() no longer declares function names (case_when,
full_join, tribble) nor unused names, which could hide a missing
import (#70).
- Every request sends the user agent
togglr/<version> in its header;
get_dashboard() no longer sends a hardcoded api_test (#71). The
user_agent argument of the report functions is kept for compatibility and
ignored.
get_workspace_id() fails with its message when the account has no
workspace, instead of "subscript out of bounds". New tests cover the edge
cases of the pagination, of the quota and of the cache (#73).
- Package metadata and documentation: new title and description, no more
LazyData without data, targeted imports only (no more whole dplyr,
httr, keyring, assertthat, getPass), get_detailled_report() has
its own help page, the vignette no longer runs install.packages() during
R CMD check. R CMD check is clean: 0 error,
0 warning, 0 note (#74).
toggl_create_project() (and so toggl_start() on a new project) works
again in a workspace without any client: get_all_client_info() returns
the id and name columns, even with no rows (review of #75).
- The reports cache is kept per token (as a hash): a period fetched with one
token is no longer returned to another token of the same workspace (review of #75).
- The spreading of the last requests of the quota waits at most
max_wait,
with a message, and only slows down the token that is short of quota (review of #75).
- The quota is remembered per token and per scope (the workspace or
organization of the url, or the user for
/me and /workspaces): an
exhausted workspace no longer blocks /me or another workspace (review of #75).
- A request processed by the API is returned even if its quota cannot be
recorded in the state file (warning): a time entry is no longer reported
as not started, then created twice (review of #75).
- One pass on the state file per request instead of three, and a read no
longer rewrites it (review of #75).
- A time entry accepted by the API but whose answer cannot be read is
reported as started (with a warning, id
NA), not as NOT STARTED (review of #75).
- A state file that cannot be replaced is an error instead of a silently
lost update, and the temporary file is removed (review of #75).
- Network errors are no longer retried when the backoff exceeds
max_wait (review of #75).
togglr 0.3.0
- Every read of the API goes through
toggl_GET(): HTTP errors are no longer
turned into empty results. 429 and 5xx are retried, waiting what the API asks
(within options(togglr.max_wait), 60 s by default), any other error stops.
get_detailled_report() no longer returns silently truncated periods: the
number of pages comes from total_count, entries are de-duplicated by id,
the period is fetched again if entries move during pagination, and an error is
raised if the result is still incomplete.
get_detailled_report() no longer memoises pages. The new cache argument
(default FALSE) stores a whole period, only once it is complete and checked;
reserve it to closed periods. max_page is deprecated and ignored.
- Quota exhaustion (HTTP 402) is remembered for the session: no request is
sent before the reset (waited for within
max_wait, otherwise an error).
A 429 without retry-after backs off for a minute.
- Calls to the reports API are spaced by at least one second, or by
options(togglr.min_interval) seconds to spread a long extraction.
togglr 0.1.99.9002
togglr 0.1.34.9000
get_time_entries() can now handle multiple tags, stored in a list column (#21, @pat-s)
togglr 0.1.34
- Added pkgdown documentation
- Added a
NEWS.md file to track changes to the package.