miscreaders package

Supported readers:

Loop Habit Tracker

Loop Habit Tracker is an open-source Android app for tracking habits.

References

class miscreaders.loophabit.LoophabitDbReader(db_path: str | PathLike[Any])

Reader for SQLite database exported from Loop Habit Tracker

get_connection() Connection

Return read-only connection to the database

get_habit_df() DataFrame

Return dataframe with habit definition

See also

HabitType for meaning of the type column

Example

>>> from miscreaders.loophabit import LoophabitDbReader
>>> reader = LoophabitDbReader(path_to_db)
>>> reader.get_habit_df().head(2)
shape: (2, 18)
┌─────┬──────────┬───────┬─────────────┬───┬──────────────┬──────┬──────────┬──────────────────────┐
│ id  ┆ archived ┆ color ┆ description ┆ … ┆ target_value ┆ unit ┆ question ┆ uuid                 │
│ --- ┆ ---      ┆ ---   ┆ ---         ┆   ┆ ---          ┆ ---  ┆ ---      ┆ ---                  │
│ i64 ┆ i64      ┆ i64   ┆ str         ┆   ┆ f64          ┆ str  ┆ str      ┆ str                  │
╞═════╪══════════╪═══════╪═════════════╪═══╪══════════════╪══════╪══════════╪══════════════════════╡
│ 1   ┆ 0        ┆ 4     ┆             ┆ … ┆ 0.0          ┆      ┆          ┆ cwvnot6vdjvkwzu55s19 │
│     ┆          ┆       ┆             ┆   ┆              ┆      ┆          ┆ gbgdeer84i…          │
│ 2   ┆ 0        ┆ 0     ┆             ┆ … ┆ 0.0          ┆      ┆          ┆ owhf7kceexi8cghlbqks │
│     ┆          ┆       ┆             ┆   ┆              ┆      ┆          ┆ 687kj3w8j9…          │
└─────┴──────────┴───────┴─────────────┴───┴──────────────┴──────┴──────────┴──────────────────────┘
get_repetition_df() DataFrame

Return dataframe with daily data for habits

See also

EntryValue for meaning of the value column

Example

>>> from miscreaders.loophabit import LoophabitDbReader
>>> reader = LoophabitDbReader(path_to_db)
>>> reader.get_repetition_df()
shape: (10, 4)
┌──────┬────────────┬───────┬───────┐
│ name ┆ timestamp  ┆ value ┆ notes │
│ ---  ┆ ---        ┆ ---   ┆ ---   │
│ enum ┆ date       ┆ i64   ┆ str   │
╞══════╪════════════╪═══════╪═══════╡
│ a    ┆ 2014-02-21 ┆ 2     ┆       │
│ b    ┆ 2014-02-22 ┆ 2     ┆       │
│ c    ┆ 2014-02-23 ┆ 2     ┆       │
│ d    ┆ 2014-02-24 ┆ 2     ┆       │
│ a    ┆ 2014-02-25 ┆ 0     ┆       │
│ a    ┆ 2014-02-26 ┆ 2     ┆       │
│ a    ┆ 2014-02-27 ┆ 2     ┆       │
│ a    ┆ 2014-02-28 ┆ 2     ┆       │
│ a    ┆ 2014-03-01 ┆ 2     ┆       │
│ a    ┆ 2014-03-02 ┆ 2     ┆       │
└──────┴────────────┴───────┴───────┘
class miscreaders.loophabit.HabitType

Values in column type in habit table

YES_NO

Habit value has interpretation per EntryValue

NUMERICAL

Habit value has numerical interpretation

References

class miscreaders.loophabit.EntryValue

Values in column value in repetition table for habits of type HabitType.YES_NO

SKIP

Value indicating that the habit is not applicable for this timestamp.

YES_MANUAL

Value indicating that the user has performed the habit at this timestamp.

YES_AUTO

Value indicating that the user did not perform the habit, but they were not expected to, because of the frequency of the habit.

NO

Value indicating that the user did not perform the habit, even though they were expected to perform it.

UNKNOWN

Value indicating that no data is available for the given timestamp.

References

Moonwatch.rs

Moonwatch.rs is a privacy-oriented tool for tracking application usage on Windows and Linux.

References

class miscreaders.moonwatch.MoonwatchLogReader(jsonl_path: str | PathLike[Any])

Reader for Moonwatch.rs log file

get_df() DataFrame

Return dataframe with log content

Example

>>> from miscreaders.moonwatch import MoonwatchLogReader
>>> reader = MoonwatchLogReader(path_to_jsonl)
>>> reader.get_df()
shape: (5, 8)
┌────────────┬────────────┬────────────┬───────────┬───────────┬───────────┬───────────┬───────────┐
│ type       ┆ time       ┆ duration   ┆ hostname  ┆ username  ┆ idle_for  ┆ process_p ┆ tags      │
│ ---        ┆ ---        ┆ ---        ┆ ---       ┆ ---       ┆ ---       ┆ ath       ┆ ---       │
│ enum       ┆ datetime[μ ┆ duration[μ ┆ str       ┆ str       ┆ duration[ ┆ ---       ┆ list[str] │
│            ┆ s]         ┆ s]         ┆           ┆           ┆ μs]       ┆ str       ┆           │
╞════════════╪════════════╪════════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╡
│ ActiveWind ┆ 2025-04-02 ┆ 15s        ┆ test-host ┆ test-user ┆ 1m 22s    ┆ C:\test-p ┆ []        │
│ owEvent    ┆ 21:40:24.5 ┆            ┆ name      ┆           ┆           ┆ rogram.ex ┆           │
│            ┆ 43149      ┆            ┆           ┆           ┆           ┆ e         ┆           │
│ ActiveWind ┆ 2025-04-02 ┆ 15s        ┆ test-host ┆ test-user ┆ 3m 38s    ┆ C:\other- ┆ ["tag-a"] │
│ owEvent    ┆ 00:20:12.3 ┆            ┆ name      ┆           ┆           ┆ program.e ┆           │
│            ┆ 26975      ┆            ┆           ┆           ┆           ┆ xe        ┆           │
│ ActiveWind ┆ 2025-04-02 ┆ 15s        ┆ test-host ┆ test-user ┆ 3m 23s    ┆ C:\test-p ┆ []        │
│ owEvent    ┆ 00:19:57.3 ┆            ┆ name      ┆           ┆           ┆ rogram.ex ┆           │
│            ┆ 17296      ┆            ┆           ┆           ┆           ┆ e         ┆           │
│ ActiveWind ┆ 2025-04-02 ┆ 15s        ┆ test-host ┆ test-user ┆ 3m 8s     ┆ C:\test-p ┆ []        │
│ owEvent    ┆ 00:19:42.3 ┆            ┆ name      ┆           ┆           ┆ rogram.ex ┆           │
│            ┆ 14747      ┆            ┆           ┆           ┆           ┆ e         ┆           │
│ ActiveWind ┆ 2025-04-02 ┆ 15s        ┆ test-host ┆ test-user ┆ 2m 53s    ┆ C:\other- ┆ ["tag-a", │
│ owEvent    ┆ 00:19:27.3 ┆            ┆ name      ┆           ┆           ┆ program.e ┆ "tag-b"]  │
│            ┆ 06595      ┆            ┆           ┆           ┆           ┆ xe        ┆           │
└────────────┴────────────┴────────────┴───────────┴───────────┴───────────┴───────────┴───────────┘
class miscreaders.moonwatch.MoonwatchLogDirectoryReader(dir_path: str | PathLike[Any])

Helper class for batch reading of Moonwatch.rs logs

You can use this to read entire log directory (eg. ~/.moonwatch-rs/log) into one dataframe.

get_df(rechunk: bool = True) DataFrame

Return concatenated dataframe of all logs in directory

Parameters:

rechunk – Reallocate data into contiguous memory for better performance (on by default)

Example

>>> from miscreaders.moonwatch import MoonwatchLogDirectoryReader
>>> reader = MoonwatchLogDirectoryReader(path_to_dir)
>>> reader.get_df()
shape: (15, 8)
┌────────────┬────────────┬────────────┬───────────┬───────────┬───────────┬───────────┬───────────┐
│ type       ┆ time       ┆ duration   ┆ hostname  ┆ username  ┆ idle_for  ┆ process_p ┆ tags      │
│ ---        ┆ ---        ┆ ---        ┆ ---       ┆ ---       ┆ ---       ┆ ath       ┆ ---       │
│ enum       ┆ datetime[μ ┆ duration[μ ┆ str       ┆ str       ┆ duration[ ┆ ---       ┆ list[str] │
│            ┆ s]         ┆ s]         ┆           ┆           ┆ μs]       ┆ str       ┆           │
╞════════════╪════════════╪════════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╡
│ ActiveWind ┆ 2025-04-02 ┆ 15s        ┆ test-host ┆ test-user ┆ 57s       ┆ C:\test-p ┆ []        │
│ owEvent    ┆ 00:12:57.1 ┆            ┆ name      ┆           ┆           ┆ rogram.ex ┆           │
│            ┆ 11274      ┆            ┆           ┆           ┆           ┆ e         ┆           │
│ ActiveWind ┆ 2025-04-02 ┆ 15s        ┆ test-host ┆ test-user ┆ 42s       ┆ C:\test-p ┆ []        │
│ owEvent    ┆ 00:12:42.1 ┆            ┆ name      ┆           ┆           ┆ rogram.ex ┆           │
│            ┆ 10712      ┆            ┆           ┆           ┆           ┆ e         ┆           │
│ ActiveWind ┆ 2025-04-02 ┆ 15s        ┆ test-host ┆ test-user ┆ 27s       ┆ C:\test-p ┆ []        │
│ owEvent    ┆ 00:12:27.1 ┆            ┆ name      ┆           ┆           ┆ rogram.ex ┆           │
│            ┆ 08317      ┆            ┆           ┆           ┆           ┆ e         ┆           │
│ ActiveWind ┆ 2025-04-02 ┆ 15s        ┆ test-host ┆ test-user ┆ 12s       ┆ C:\test-p ┆ []        │
│ owEvent    ┆ 00:12:12.0 ┆            ┆ name      ┆           ┆           ┆ rogram.ex ┆           │
│            ┆ 97326      ┆            ┆           ┆           ┆           ┆ e         ┆           │
│ ActiveWind ┆ 2025-04-02 ┆ 15s        ┆ test-host ┆ test-user ┆ 0µs       ┆ C:\test-p ┆ []        │
│ owEvent    ┆ 00:11:57.0 ┆            ┆ name      ┆           ┆           ┆ rogram.ex ┆           │
│            ┆ 94459      ┆            ┆           ┆           ┆           ┆ e         ┆           │
│ …          ┆ …          ┆ …          ┆ …         ┆ …         ┆ …         ┆ …         ┆ …         │
│ ActiveWind ┆ 2025-04-02 ┆ 15s        ┆ test-host ┆ test-user ┆ 1m 22s    ┆ C:\test-p ┆ []        │
│ owEvent    ┆ 21:40:24.5 ┆            ┆ name      ┆           ┆           ┆ rogram.ex ┆           │
│            ┆ 43149      ┆            ┆           ┆           ┆           ┆ e         ┆           │
│ ActiveWind ┆ 2025-04-02 ┆ 15s        ┆ test-host ┆ test-user ┆ 3m 38s    ┆ C:\other- ┆ ["tag-a"] │
│ owEvent    ┆ 00:20:12.3 ┆            ┆ name      ┆           ┆           ┆ program.e ┆           │
│            ┆ 26975      ┆            ┆           ┆           ┆           ┆ xe        ┆           │
│ ActiveWind ┆ 2025-04-02 ┆ 15s        ┆ test-host ┆ test-user ┆ 3m 23s    ┆ C:\test-p ┆ []        │
│ owEvent    ┆ 00:19:57.3 ┆            ┆ name      ┆           ┆           ┆ rogram.ex ┆           │
│            ┆ 17296      ┆            ┆           ┆           ┆           ┆ e         ┆           │
│ ActiveWind ┆ 2025-04-02 ┆ 15s        ┆ test-host ┆ test-user ┆ 3m 8s     ┆ C:\test-p ┆ []        │
│ owEvent    ┆ 00:19:42.3 ┆            ┆ name      ┆           ┆           ┆ rogram.ex ┆           │
│            ┆ 14747      ┆            ┆           ┆           ┆           ┆ e         ┆           │
│ ActiveWind ┆ 2025-04-02 ┆ 15s        ┆ test-host ┆ test-user ┆ 2m 53s    ┆ C:\other- ┆ ["tag-a", │
│ owEvent    ┆ 00:19:27.3 ┆            ┆ name      ┆           ┆           ┆ program.e ┆ "tag-b"]  │
│            ┆ 06595      ┆            ┆           ┆           ┆           ┆ xe        ┆           │
└────────────┴────────────┴────────────┴───────────┴───────────┴───────────┴───────────┴───────────┘
iter_dfs() Iterator[DataFrame]

Iterate over dataframes from individual log files

StayFree

StayFree is a productivity app for mobile, desktop and web.

References

class miscreaders.stayfree.StayfreeXlsReader(xls_path: str | PathLike[Any])

Reader for StayFree XLS export file

get_device_unlocks_df() DataFrame

Return dataframe with device unlock counts

Example

>>> from miscreaders.stayfree import StayfreeXlsReader
>>> reader = StayfreeXlsReader(path_to_xls)
>>> reader.get_device_unlocks_df()
shape: (122, 2)
┌────────────┬───────┐
│ date       ┆ count │
│ ---        ┆ ---   │
│ date       ┆ i32   │
╞════════════╪═══════╡
│ 2023-11-10 ┆ 4     │
│ 2023-11-11 ┆ 26    │
│ 2023-11-12 ┆ 22    │
│ 2023-11-13 ┆ 19    │
│ 2023-11-14 ┆ 21    │
│ …          ┆ …     │
│ 2024-03-06 ┆ 39    │
│ 2024-03-07 ┆ 35    │
│ 2024-03-08 ┆ 26    │
│ 2024-03-09 ┆ 10    │
│ 2024-03-10 ┆ 0     │
└────────────┴───────┘
get_usage_count_df() DataFrame

Return dataframe with app open counts

Example

>>> from miscreaders.stayfree import StayfreeXlsReader
>>> reader = StayfreeXlsReader(path_to_xls)
>>> reader.get_usage_count_df()
shape: (6_222, 4)
┌────────────┬─────────────┬───────┬────────┐
│ date       ┆ app         ┆ count ┆ device │
│ ---        ┆ ---         ┆ ---   ┆ ---    │
│ date       ┆ str         ┆ i32   ┆ str    │
╞════════════╪═════════════╪═══════╪════════╡
│ 2023-11-10 ┆ addititious ┆ 0     ┆        │
│ 2023-11-11 ┆ addititious ┆ 0     ┆        │
│ 2023-11-12 ┆ addititious ┆ 0     ┆        │
│ 2023-11-13 ┆ addititious ┆ 0     ┆        │
│ 2023-11-14 ┆ addititious ┆ 1     ┆        │
│ …          ┆ …           ┆ …     ┆ …      │
│ 2024-03-06 ┆ xylometer   ┆ 3     ┆        │
│ 2024-03-07 ┆ xylometer   ┆ 0     ┆        │
│ 2024-03-08 ┆ xylometer   ┆ 2     ┆        │
│ 2024-03-09 ┆ xylometer   ┆ 0     ┆        │
│ 2024-03-10 ┆ xylometer   ┆ 0     ┆        │
└────────────┴─────────────┴───────┴────────┘
get_usage_time_df() DataFrame

Return dataframe with app usage time

Example

>>> from miscreaders.stayfree import StayfreeXlsReader
>>> reader = StayfreeXlsReader(path_to_xls)
>>> reader.get_usage_time_df()
shape: (6_222, 4)
┌────────────┬─────────────┬──────────────┬────────┐
│ date       ┆ app         ┆ duration     ┆ device │
│ ---        ┆ ---         ┆ ---          ┆ ---    │
│ date       ┆ str         ┆ duration[μs] ┆ str    │
╞════════════╪═════════════╪══════════════╪════════╡
│ 2023-11-10 ┆ addititious ┆ 0µs          ┆        │
│ 2023-11-11 ┆ addititious ┆ 0µs          ┆        │
│ 2023-11-12 ┆ addititious ┆ 0µs          ┆        │
│ 2023-11-13 ┆ addititious ┆ 0µs          ┆        │
│ 2023-11-14 ┆ addititious ┆ 6s           ┆        │
│ …          ┆ …           ┆ …            ┆ …      │
│ 2024-03-06 ┆ xylometer   ┆ 53m 8s       ┆        │
│ 2024-03-07 ┆ xylometer   ┆ 0µs          ┆        │
│ 2024-03-08 ┆ xylometer   ┆ 30m 54s      ┆        │
│ 2024-03-09 ┆ xylometer   ┆ 0µs          ┆        │
│ 2024-03-10 ┆ xylometer   ┆ 0µs          ┆        │
└────────────┴─────────────┴──────────────┴────────┘
static parse_duration(text: str) timedelta

Convert "1h 10m 26s" into datetime.timedelta

Example

>>> from miscreaders.stayfree import StayfreeXlsReader
>>> print(StayfreeXlsReader.parse_duration("1h 10m 26s"))
1:10:26
>>> print(StayfreeXlsReader.parse_duration("10m"))
0:10:00
>>> print(StayfreeXlsReader.parse_duration("10h 26s"))
10:00:26