keith / reminders-cli

A simple CLI for interacting with macOS reminders
MIT License
663 stars 52 forks source link

How to mimic the 'today' view? (and similar) #71

Open 0xdevalias opened 9 months ago

0xdevalias commented 9 months ago

I was hoping I could mimic the 'today' view to get access to my reminders from CLI, but having a quick look through the help, i'm not sure if this is possible currently.

I tried show-lists, but it only shows the actual underlying reminder lists (not the meta categories for 'Today', 'Scheduled', 'All', 'Flagged', 'Completed')

Since show requires me to pass a <list-name>, that wouldn't let me mimic the 'Today' view, as it gets from all my lists.

I tried show-all --due-date today, but that only showed me a single reminder, presumably with the due date set to exactly todays date; whereas I currently have 130 reminders in the 'Today' view.

Potential Solutions

I haven't thought too deeply about this yet, but off the top of my head, some potential solutions:

See Also

This might be tangentially related to:

And if the Reminders app counts these 'meta categories' as 'smart lists', then it may also be related to:

Additional Context

OVERVIEW: Interact with macOS Reminders from the command line

USAGE: reminders <subcommand>

OPTIONS:
  -h, --help              Show help information.

SUBCOMMANDS:
  add                     Add a reminder to a list
  complete                Complete a reminder
  uncomplete              Uncomplete a reminder
  delete                  Delete a reminder
  edit                    Edit the text of a reminder
  show                    Print the items on the given list
  show-lists              Print the name of lists to pass to other commands
  new-list                Create a new list
  show-all                Print all reminders

  See 'reminders help <subcommand>' for detailed help.
OVERVIEW: Print the items on the given list

USAGE: reminders show <list-name> [--only-completed] [--include-completed] [--sort <sort>] [--sort-order <sort-order>] [--due-date <due-date>] [--format <format>]

ARGUMENTS:
  <list-name>             The list to print items from, see 'show-lists' for
                          names

OPTIONS:
  --only-completed        Show completed items only
  --include-completed     Include completed items in output
  -s, --sort <sort>       Show the reminders in a specific order, one of: none,
                          creation-date, due-date (default: none)
  -o, --sort-order <sort-order>
                          How the sort order should be applied, one of:
                          ascending, descending (default: ascending)
  -d, --due-date <due-date>
                          Show only reminders due on this date
  -f, --format <format>   format, either of 'plain' or 'json' (default: plain)
  -h, --help              Show help information.
OVERVIEW: Print all reminders

USAGE: reminders show-all [--only-completed] [--include-completed] [--due-date <due-date>] [--format <format>]

OPTIONS:
  --only-completed        Show completed items only
  --include-completed     Include completed items in output
  -d, --due-date <due-date>
                          Show only reminders due on this date
  -f, --format <format>   format, either of 'plain' or 'json' (default: plain)
  -h, --help              Show help information.
0xdevalias commented 9 months ago

This seems to be a limitation of the Apple Reminders API itself not including Smart Lists.

I haven't verified this, but ChatGPT seems to suggest that they may need to be manually implemented as filters:

Full ChatGPT Response > To access smart lists using the Apple Reminders API, you'll need to interact with the API through Apple's frameworks, such as EventKit for iOS and macOS. Smart lists in Apple Reminders are special lists like "Today", "Scheduled", "All", etc., that automatically categorize reminders based on certain criteria. > > Here's a general approach to accessing smart lists: > > 1. **Import EventKit**: First, you need to import the EventKit framework into your project. This framework provides the necessary classes and methods to interact with calendar and reminders data. > > 2. **Request Access**: Request access to the user's reminders. You need to ensure your app has the appropriate permissions to access reminder data. > > 3. **Fetch Reminders**: Once you have access, you can fetch reminders. However, Apple's EventKit does not provide a direct way to access smart lists by their usual names like "Today" or "Scheduled". Instead, you have to manually filter the reminders based on the criteria that define these smart lists. For example, for the "Today" smart list, you would filter reminders whose due dates are today. > > Here's a basic outline of how you might do this in Swift (for iOS/macOS): > > ```swift > import EventKit > > let eventStore = EKEventStore() > > // Request access to reminders > eventStore.requestAccess(to: .reminder) { (granted, error) in > if granted && error == nil { > // Fetch all reminders > let predicate = eventStore.predicateForReminders(in: nil) > eventStore.fetchReminders(matching: predicate) { (reminders) in > // Filter reminders based on your criteria for a smart list > let todayReminders = reminders?.filter { > guard let dueDate = $0.dueDateComponents?.date else { return false } > return Calendar.current.isDateInToday(dueDate) > } > > // Do something with the filtered reminders > } > } else { > // Handle the error or the case where access is not granted > } > } > ``` > > In this code snippet, we: > > - Import `EventKit`. > - Request access to reminders. > - Fetch all reminders and filter them to match the "Today" smart list criteria. > > Remember that this is just a basic example. You'll need to tailor your filtering logic to match the specific criteria of the smart list you're interested in. Additionally, always handle permissions and potential errors appropriately in a production environment. > > For a more comprehensive solution, you might consider creating custom functions or classes to encapsulate the logic for different smart lists.

Originally posted by @0xdevalias in https://github.com/keith/reminders-cli/issues/72#issuecomment-1828898918

0xdevalias commented 5 months ago

Following on from my above research, looking at the sqlite DB that contains the reminders data:


In /Users/devalias/Library/Reminders/Container_v1/Stores/Data-SOME-UUID-TYPE-THING.sqlite:

  • In the ZREMCDHASHTAGLABEL table:
    • Z_ENT: seems to be 3 for all of the hashtag entries
    • ZNAME / ZCANONICALNAME: seem to contain the text of my hashtags
  • In the ZREMCDREMINDER table:
    • Z_PK: ?reminder primary key?
    • Z_ENT: seems to be 32 for all of the reminder entries
    • ZCOMPLETED: 1 for completed, 0 for not completed
    • ZFLAGGED: ?probably 1 if flagged, otherwise 0?
    • ZPRIORITY: ?reminder priority?
    • ZLIST: ?ID of list the reminder relates to?
    • ZTITLE: Main reminder text
    • ZNOTES: Reminder notes
    • etc
  • In the ZREMCDOBJECT table, columns such as the following look potentially useful:
    • ZCKIDENTIFIER: ?some sort of UUID?
    • ZREMINDERIDENTIFIER: ?some sort of UUID?
    • ZREMINDER3: ?might contain the ID of the reminder the row relates to?
    • ZHASHTAGLABEL: seems to contain the PK of the hashtag from ZREMCDHASHTAGLABEL
    • ZDATECOMPONENTSDATA: ?stuff related to the reminder date?
    • Z_ENT:
    • 30: seems to correlate to the smart lists
      • Z_FOK_PARENTLIST1: ?Foreign key for the associated parent list?
      • ZBADGEEMBLEM1: Contains data like: {"Emoji" : "πŸŽ₯"} that I assigned to the smartlists
      • ZNAME3: This seems to contain the name I assigned to the smartlists
      • ZSMARTLISTTYPE: seems to contain things like:
      • com.apple.reminders.smartlist.today
      • com.apple.reminders.smartlist.assigned
      • com.apple.reminders.smartlist.custom
      • com.apple.reminders.smartlist.flagged
      • ZSORTINGSTYLE1: eg. manual
      • ZFILTEREDDATA: json data relating to the smartlist config; eg:
      • {"hashtags":{"hashtags":["health"]}}
      • {"date":{"relativeRange":["inNext","1","week"]}}
      • {"hashtags":{"hashtags":{"include":["to-watch","youtube-aaa","youtube-bbb","youtube-ccc","youtube-ddd"],"exclude":[],"operation":"or"}},"date":{"any":""},"operation":"and"}
    • 26: ???
      • ZLISTID: seems to contain things like:
      • com.apple.reminders.smartlist.today
      • Or sometimes a UUID (eg. in my data: BA189B19-F050-43FD-A76F-115415ED91A2 / 5C703F5C-7ED3-4AC3-B064-7F9C1E01AA95)
    • 25: seems to correlate to normal lists
      • ZBADGEEMBLEM seems to be the image I assigned to the normal lists (eg. {"Emoji" : "πŸ’²"})
      • ZNAME2 seems to be the name of the normal list
      • ZSORTINGSTYLE: eg. manual
    • 24: seems to (at least partially?) correlate to hashtags for reminders
      • ZNAME1: seems to contain the text of a hashtag for the reminder
    • 22: ???
    • 21: ???
    • 18: ?URL related?
      • Looks like ZUTI (eg. public.url), ZURL, etc; may be related to this..
      • ZURL: reminder URL field
    • 17: ?attachment related?
      • Looks like ZUTI (eg. public.jpeg), ZFILENAME (eg. 24B9CB35-CC7F-45F0-B52F-8BED9C2F2769-732-00055908B19E5135.jpeg), ZSHA512SUM, etc; may be related to this..
    • 10: ?location related?
      • Looks like ZLATITUDE / ZLONGITUDE / ZADDRESS / ZLOCATIONUID / ZTITLE etc are related to this
    • 9: ?reminder date/time related?
      • Looks like ZALARM / ZDATECOMPONENTSDATA / etc may be related to this
    • 7: ???
      • Looks like ZREMINDER / ZTRIGGER / Z8TRIGGER / etc may be related to this
    • 6: ?settings/flags related?
      • I only seem to have a single entry for this type, and it seems to correlate with fields like ZDAALLOWSCALENDARADDDELETEMODIFY / ZDASUPPORTSSHAREDCALENDARS / etc (seemingly at least 10 fields like this seem to correlate with it), as well as ZCKUSERRECORDNAME, ZNAME (iCloud), ZPERSONID (PRIMARY-CLOUDKIT)

That seems to be enough basic info to figure out resolving both this issue, and maybe also #72

Would just need to figure out how to match up the ID/data that the API is currently able to provide, with an ID that can be looked up in the sqlite database; and then extract the associated hashtags/etc.

I was thinking that maybe externalId from reminders show Reminders --sort creation-date --sort-order ascending --format json might have worked.. but it only seems to show up in ZREMCDOBJECT within some fields with LOTS of other IDs in them, so doesn't seem ideal; though ZREMCDREMINDER seems to have a single row match with the ID being in ZCKIDENTIFIER / ZDACALENDARITEMUNIQUEIDENTIFIER, so maybe we can do it that way in like 2 steps..

Originally posted by @0xdevalias in https://github.com/keith/reminders-cli/issues/74#issuecomment-2029147628


Based on that, it should be possible to get a list of all of the normal lists from the ZREMCDOBJECT table like follows:

-- SELECT *
SELECT
  Z_PK,
  Z_ENT,
  ZBADGEEMBLEM,
  ZNAME2,
  ZSORTINGSTYLE
FROM ZREMCDOBJECT
WHERE Z_ENT = '25'

Which for my data, returns 9 entries:

  • 7 active lists
  • 1 (recently) deleted list
  • 1 folder
β‡’ sqlite3 -readonly -json /Users/devalias/Library/Reminders/Container_v1/Stores/Data-5070B790-D66D-40F7-8F4A-EC8E0FA88F3A.sqlite "
  SELECT
    Z_PK,
    Z_ENT,
    ZBADGEEMBLEM,
    ZNAME2,
    ZSORTINGSTYLE
  FROM ZREMCDOBJECT
  WHERE Z_ENT = '25'
"
[{"Z_PK":8,"Z_ENT":25,"ZBADGEEMBLEM":null,"ZNAME2":"Reminders","ZSORTINGSTYLE":"manual"},
{"Z_PK":2936,"Z_ENT":25,"ZBADGEEMBLEM":"{\"Emoji\" : \"πŸ’²\"}","ZNAME2":"To Buy","ZSORTINGSTYLE":"manual"},
{"Z_PK":2965,"Z_ENT":25,"ZBADGEEMBLEM":"{\"Emoji\" : \"πŸŽ“\"}","ZNAME2":"Learning","ZSORTINGSTYLE":"manual"},
{"Z_PK":2966,"Z_ENT":25,"ZBADGEEMBLEM":null,"ZNAME2":"Adulting","ZSORTINGSTYLE":"manual"},
{"Z_PK":2967,"Z_ENT":25,"ZBADGEEMBLEM":null,"ZNAME2":"Quality of Life","ZSORTINGSTYLE":"manual"},
{"Z_PK":3001,"Z_ENT":25,"ZBADGEEMBLEM":null,"ZNAME2":"People","ZSORTINGSTYLE":"manual"},
{"Z_PK":5060,"Z_ENT":25,"ZBADGEEMBLEM":"{\"Emoji\" : \"πŸ› οΈ\"}","ZNAME2":"Projects","ZSORTINGSTYLE":"manual"},
{"Z_PK":7176,"Z_ENT":25,"ZBADGEEMBLEM":null,"ZNAME2":"Smart Lists","ZSORTINGSTYLE":"manual"},
{"Z_PK":9740,"Z_ENT":25,"ZBADGEEMBLEM":"{\"Emoji\" : \"β˜€οΈ\"}","ZNAME2":"Daily","ZSORTINGSTYLE":"manual"}]

And then I can access my smart lists as follows:

-- SELECT *
SELECT
  Z_PK,
  Z_ENT,
  ZBADGEEMBLEM1,
  ZNAME3,
  ZSORTINGSTYLE1,
  ZSMARTLISTTYPE,
  ZFILTERDATA
FROM ZREMCDOBJECT
WHERE Z_ENT = '30'

Which for my data, returns 10 entries:

  • 1 com.apple.reminders.smartlist.today
  • 1 com.apple.reminders.smartlist.flagged
  • 1 com.apple.reminders.smartlist.assigned
  • 7 com.apple.reminders.smartlist.custom
β‡’ sqlite3 -readonly -json /Users/devalias/Library/Reminders/Container_v1/Stores/Data-5070B790-D66D-40F7-8F4A-EC8E0FA88F3A.sqlite "
  SELECT
    Z_PK,
    Z_ENT,
    ZBADGEEMBLEM1,
    ZNAME3,
    ZSORTINGSTYLE1,
    ZSMARTLISTTYPE,
    ZFILTERDATA
  FROM ZREMCDOBJECT
  WHERE Z_ENT = '30'
"

[{"Z_PK":18,"Z_ENT":30,"ZBADGEEMBLEM1":null,"ZNAME3":null,"ZSORTINGSTYLE1":"manual","ZSMARTLISTTYPE":"com.apple.reminders.smartlist.today","ZFILTERDATA":null},
{"Z_PK":2932,"Z_ENT":30,"ZBADGEEMBLEM1":"{\"Emoji\" : \"πŸ₯\"}","ZNAME3":"Health","ZSORTINGSTYLE1":"manual","ZSMARTLISTTYPE":"com.apple.reminders.smartlist.custom","ZFILTERDATA":"{\"hashtags\":{\"hashtags\":[\"health\"]}}"},
{"Z_PK":3125,"Z_ENT":30,"ZBADGEEMBLEM1":"{\"Emoji\" : \"⏱\"}","ZNAME3":"This Week","ZSORTINGSTYLE1":null,"ZSMARTLISTTYPE":"com.apple.reminders.smartlist.custom","ZFILTERDATA":"{\"date\":{\"relativeRange\":[\"inNext\",\"1\",\"week\"]}}"},
{"Z_PK":4293,"Z_ENT":30,"ZBADGEEMBLEM1":"{\"Emoji\" : \"🎫\"}","ZNAME3":"Events","ZSORTINGSTYLE1":null,"ZSMARTLISTTYPE":"com.apple.reminders.smartlist.custom","ZFILTERDATA":"{\"hashtags\":{\"hashtags\":[\"event\"]}}"},
{"Z_PK":7157,"Z_ENT":30,"ZBADGEEMBLEM1":"{\"Emoji\" : \"πŸ“š\"}","ZNAME3":"To Read","ZSORTINGSTYLE1":"manual","ZSMARTLISTTYPE":"com.apple.reminders.smartlist.custom","ZFILTERDATA":"{\"hashtags\":{\"hashtags\":[\"to-read\"]}}"},
{"Z_PK":7177,"Z_ENT":30,"ZBADGEEMBLEM1":"{\"Emoji\" : \"πŸŽ₯\"}","ZNAME3":"To Watch","ZSORTINGSTYLE1":null,"ZSMARTLISTTYPE":"com.apple.reminders.smartlist.custom","ZFILTERDATA":"{\"hashtags\":{\"hashtags\":{\"include\":[\"to-watch\",\"youtube-aitrepreneur\",\"youtube-crypto-crew-university\",\"youtube-daveshapiro\",\"youtube-mattvidpro\"],\"exclude\":[],\"operation\":\"or\"}},\"date\":{\"any\":\"\"},\"operation\":\"and\"}"},
{"Z_PK":14539,"Z_ENT":30,"ZBADGEEMBLEM1":"{\"Emoji\" : \"πŸ’Ύ\"}","ZNAME3":"Backup","ZSORTINGSTYLE1":"manual","ZSMARTLISTTYPE":"com.apple.reminders.smartlist.custom","ZFILTERDATA":"{\"hashtags\":{\"hashtags\":[\"backup\"]}}"},
{"Z_PK":25188,"Z_ENT":30,"ZBADGEEMBLEM1":"{\"Emoji\" : \"πŸŽ₯\"}","ZNAME3":"To Watch - Dave Shapiro","ZSORTINGSTYLE1":null,"ZSMARTLISTTYPE":"com.apple.reminders.smartlist.custom","ZFILTERDATA":"{\"hashtags\":{\"hashtags\":{\"exclude\":[],\"operation\":\"or\",\"include\":[\"youtube-daveshapiro\"]}},\"operation\":\"and\"}"},
{"Z_PK":25191,"Z_ENT":30,"ZBADGEEMBLEM1":null,"ZNAME3":null,"ZSORTINGSTYLE1":null,"ZSMARTLISTTYPE":"com.apple.reminders.smartlist.flagged","ZFILTERDATA":null},
{"Z_PK":25192,"Z_ENT":30,"ZBADGEEMBLEM1":null,"ZNAME3":null,"ZSORTINGSTYLE1":null,"ZSMARTLISTTYPE":"com.apple.reminders.smartlist.assigned","ZFILTERDATA":null}]

(Edit: Collated/cross-posted the above on the following gist for future reference: https://gist.github.com/0xdevalias/ccc2b083ff58b52aa701462f2cfb3cc8#accessing--exporting-apples-reminders-data-on-macos)

Originally posted by @0xdevalias in https://github.com/keith/reminders-cli/issues/72#issuecomment-2029363287

ap commented 2 months ago

--due-on-or-before-date

Maybe --due-by-date for that one.