Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite with ScriptingBridge (was Rewrite with PyXA) #16

Open
RhetTbull opened this issue Dec 22, 2022 · 4 comments
Open

Rewrite with ScriptingBridge (was Rewrite with PyXA) #16

RhetTbull opened this issue Dec 22, 2022 · 4 comments
Labels
enhancement New feature or request

Comments

@RhetTbull
Copy link
Owner

This looks like a much better way of interacting with Notes: PyXA

As an example:

>>> import PyXA
>>> app = PyXA.Application("Notes")
>>> print(app.notes().name())

Prints titles of all notes instantly

@RhetTbull RhetTbull added the enhancement New feature or request label Dec 22, 2022
@RhetTbull
Copy link
Owner Author

Actually, might be able to implement this with ScriptingBridge without the overhead of PyXA (which looks awesome but still immature and has some weird dependencies: python==3.11, numpy, all of pyobjc)

python
^[[APython 3.11.0 (main, Dec 25 2022, 14:21:02) [Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import ScriptingBridge
>>> from ScriptingBridge import SBElementArray
>>> app = ScriptingBridge.SBApplication.alloc().initWithBundleIdentifier_("com.apple.Notes")
>>> notes = app.accounts()[1].notes()
>>> len(notes)
394
>>> import AppKit
>>> predicate = AppKit.NSPredicate.predicateWithFormat_("name contains[cd] %@", "cocktail")
>>> predicate
name CONTAINS[cd] "cocktail"
>>> results = notes.filteredArrayUsingPredicate_(predicate)
>>> len(results)
4
>>> names = [results.arrayByApplyingSelector_("name")]
>>> names
[(
    "You\U2019ve Never Heard of One of the Bahamas\U2019 Most Popular Cocktails",
    "Watermelon Mint Mojito Cocktails - Creative Culinary",
    "Gin Basil Smash cocktail",
    "Tequila Sunrise Cocktail"
)]
>>> ids = [results.arrayByApplyingSelector_("id")]
>>> ids
[(
    "x-coredata://BA47E88C-3599-48BE-88DD-5CBC118E9CE6/ICNote/p1038",
    "x-coredata://BA47E88C-3599-48BE-88DD-5CBC118E9CE6/ICNote/p1030",
    "x-coredata://BA47E88C-3599-48BE-88DD-5CBC118E9CE6/ICNote/p659",
    "x-coredata://BA47E88C-3599-48BE-88DD-5CBC118E9CE6/ICNote/p661"
)]
>>> note = notes.filteredArrayUsingPredicate_(AppKit.NSPredicate.predicateWithFormat_("id == %@", ids[0][0]))
>>> len(note)
1
>>> note[0].name()
'You’ve Never Heard of One of the Bahamas’ Most Popular Cocktails'
>>>

@RhetTbull
Copy link
Owner Author

Making progress on this but looks like I can't get rid of the AppleScript completely.

There are some conditions (e.g. using selection on Catalina or using a predicate) where the ScriptingBridge sets the object ID to 0. I haven't been able to figure out why but in this case, the id can be determined by examining the string representation of the object which looks like this:<SBObject @0x7fd721544690: <class ''> id "x-coredata://19B82A76-B3FE-4427-9C5E-5107C1E3CA57/IMAPNote/p87" of application "Notes" (55036)>

Using this, I can get the Note ID then use the existing AppleScript calls to get the note details. This isn't elegant but works in all cases I've been able to test.

@RhetTbull RhetTbull changed the title Rewrite with PyXA Rewrite with ScriptingBridge (was Rewrite with PyXA) Dec 27, 2022
RhetTbull added a commit that referenced this issue Dec 27, 2022
RhetTbull added a commit that referenced this issue Dec 27, 2022
RhetTbull added a commit that referenced this issue Dec 31, 2022
@SKaplanOfficial
Copy link

SKaplanOfficial commented Jan 1, 2023

Making progress on this but looks like I can't get rid of the AppleScript completely.

There are some conditions (e.g. using selection on Catalina or using a predicate) where the ScriptingBridge sets the object ID to 0.

I just looked into this for PyXA (thanks for the feedback btw!). It seems to be an issue with how PyObjC's lazy evaluation of SBObjects works when using predicates -- it ends up evaluating the object at its index within the result of the filtered array. E.g., if filtered_array is the result after applying a predicate, then filtered_array[0] will effectively evaluate as [the object with ID 0 of [filtered array]]. HOWEVER, there is a workaround! Call get() on the filtered array prior to retrieving a specific index. This will force evaluation of the list but (to my knowledge) not the SBObjects within it. Then filtered_array[0] effectively becomes [the first SBObject in [list of SBObjects]].

@RhetTbull
Copy link
Owner Author

@SKaplanOfficial Thanks. I saw that you did call get() in PyXA and tried it but wasn't able to reliably make it work. Part of that may be that I do some of my development on Catalina (because I still use an old MB Pro laptop that won't run newer OS) and Catalina's Notes AppleScript interface is horribly broken. I may drop Catalina support if I can make this work reliably on newer versions of macOS. In the meantime, I built a very hacky but working work-around that evaluates the string representation of the SBObject (which will include the ID in form: <SBObject @0x7fd721544690: <class ''> id "x-coredata://19B82A76-B3FE-4427-9C5E-5107C1E3CA57/IMAPNote/p87" of application "Notes" (55036)> from which I can extract the ID with a regex. Not elegant but it works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants