miscreaders package
Supported readers:
Loop Habit Tracker
Loop Habit Tracker is an open-source Android app for tracking habits.
References
Note
Tested with version 2.2.1 of Loop Habit Tracker.
- 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
HabitTypefor meaning of thetypecolumnExample
>>> 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
EntryValuefor meaning of thevaluecolumnExample
>>> 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
typein habit table- YES_NO
Habit value has interpretation per
EntryValue
- NUMERICAL
Habit value has numerical interpretation
References
- class miscreaders.loophabit.EntryValue
Values in column
valuein repetition table for habits of typeHabitType.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
Note
Tested with version 0.1.0 of Moonwatch.rs.
- 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.See also
- 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
Note
Tested with version 18.8.4 of the Android app.
- class miscreaders.stayfree.StayfreeXlsReader(xls_path: str | PathLike[Any])
Reader for StayFree XLS export file
This parser reads file that can be manually exported from the Android app (as of 18.8.4) by the slightly confusing way of: Settings > Storage > Export to CSV. Despite the menu mentioning CSV, the exported file is actually XLS (old-style Excel file).
See also
StayfreeBackupReaderfor a more readily automated way of getting the data.- 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"intodatetime.timedeltaExample
>>> 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
- class miscreaders.stayfree.StayfreeBackupReader(backup_path: str | PathLike[Any])
Reader for StayFree app data backup file on Google Drive
This parser can extract data from StayFree internal data backup that can be scheduled or triggered manually at Settings > Storage > Backup & Restore. The backup gets stored as application-specific data on your Google Drive.
The problem with this is that there is no blessed way of accessing such data (other than deleting them) for the user; it is only meant to be accessed by the app. For a slightly cursed way of getting access, see https://github.com/albertored/google-drive-app-data.
See also
StayfreeXlsReaderfor reader of the official file export format.- get_connection(db_name: str) Generator[Connection, None, None]
Return read-only connection to the database extracted from the backup archive to temporary directory
- get_usage_time_df(lookup_app_name: bool = True) DataFrame
Return dataframe with app usage time
- Parameters:
lookup_app_name – Translate package names (eg.
"com.google.android.youtube") into human-readable app names (eg."YouTube").
Example
>>> from miscreaders.stayfree import StayfreeBackupReader >>> reader = StayfreeBackupReader("stayfree-dailybackup_PhoneName.backup-xxxxxx") >>> reader.get_usage_time_df() shape: (6_222, 4) ┌────────────┬─────────────┬──────────────┐ │ date ┆ app ┆ duration │ │ --- ┆ --- ┆ --- │ │ date ┆ str ┆ duration[μs] │ ╞════════════╪═════════════╪══════════════╡ │ 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 │ └────────────┴─────────────┴──────────────┘