Jump to content

deanishe

Member
  • Posts

    8,759
  • Joined

  • Last visited

  • Days Won

    522

Everything posted by deanishe

  1. If you have added a huge directory, it'd be better to replace it with multiple, more precise entries and/or fiddle with the depth parameter. It's not ideal to have the workflow crawl half the HD every few hours.
  2. It depends on how big/deep the directories you specify are. As noted in the OP, it uses the find command, so if you try to search a huge directory, like ~/, it will take a very, very long time.
  3. What shows up in the debugger when you run the workflow? (Set it to log "All information".)
  4. It would definitely be handy to have a "version" entry in info.plist (or wherever). The problem is, workflow authors are not exactly great at remembering to change the version number of their workflows when they update them. This has caused numerous problems with Packal (and is something I've done many times, and even Shawn has ballsed up in this regard). Speaking from experience, version numbers won't be updated consistently by developers due to laziness/forgetfulness/the desire to hide a silly bug, unless it's enforced.
  5. Cheeky update today. It's very handy being able to open your Git repos in your editor, terminal, Finder and SCM app from Alfred. Now you can open the repo in all of them at once! Just set an app_N option to a list of applications. Also fixed a bug with app_6 / fn+↩ not working.
  6. I don't have the app, but by the looks of it, GET is sufficient. You just need to create your URL and then you can open it with cURL (or the HTTP libraries built into any scripting language). Instead of using an Open URL action with {query} as the URL, use a Run Script action with "/bin/bash" as the language: curl -LsS "{query}" Be sure to set the escaping options correctly (Backquotes, Double Quotes, Backslashes, Dollars).
  7. Alfred Git Repos Workflow Browse, search and open Git repositories from within Alfred. Download Get the workflow from GitHub or Packal. Usage This workflow requires some configuration before use. See Configuration for details. repos [<query>] — Show a list of your Git repos filtered by <query> ↩ — Open selected repo in app_1 (see configuration) ⌘+↩ — Open selected repo in app_2 (see configuration) ⌥+↩ — Open selected repo in app_3 (requires configuration) ^+↩ — Open selected repo in app_4 (requires configuration) ⇧+↩ — Open selected repo in app_5 (requires configuration) fn+↩ — Open selected repo in app_6 (requires configuration) reposettings — Open settings.json in default JSON editor reposupdate — Force workflow to update its cached list of repositories. (By default, the list will only be updated every 3 hours.) reposhelp — Open this file in your browser Configuration Before you can use this workflow, you have to configure one or more folders in which the workflow should search for Git repos. The workflow uses find to search for .git directories, so you shouldn't add huge directory trees to it, and use the depth option to restrict the search depth. Typically, a depth of 2 will be what you want (i.e. search within subdirectories of specified directory, but no lower). Add directories to search to the search_dir array in settings.json (see below). The default settings.json file looks like this: { "app_1": "Finder", // ↩ to open in this/these app(s) "app_2": "Terminal", // ⌘+↩ to open in this/these app(s) "app_3": null, // ⌥+↩ to open in this/these app(s) "app_4": null, // ^+↩ to open in this/these app(s) "app_5": null, // ⇧+↩ to open in this/these app(s) "app_6": null, // fn+↩ to open in this/these app(s) "global_exclude_patterns": [], // Exclude from all searches "search_dirs": [ { "path": "~/delete/this/example", // Path to search. ~/ is expanded "depth": 2, // Search subdirs of `path` "name_for_parent": 1, // Name Alfred entry after parent of `.git`. 2 = grandparent of `.git` etc. "excludes": [ // Excludes specific to this path "tmp", // Directories named `tmp` "bad/smell/*" // Subdirs of `bad/smell` directory ] } ] } This is my settings.json: { "app_1": "Finder", "app_2": ["Finder", "Sublime Text", "SourceTree", "iTerm"], "app_3": "Sublime Text", "app_4": "SourceTree", "app_5": "iTerm", "app_6": "GitHub", "global_exclude_patterns": [], "search_dirs": [ { "path": "~/Code" }, { "path": "~/Sites" } ] } Search Directories Each entry in the search_dirs list must be a mapping. Only path is required. depth will default to 2 if not specified. excludes are globbing patterns, like in .gitignore. name_for_parent defaults to 1, which means the entry in Alfred's results should be named after the directory containing the .git directory. If you want Alfred to show the name of the grandparent, set name_for_parent to 2 etc. This is useful if your projects are structured, for example, like this and src is the actual repo: Code Project_1 src other_stuff Project_2 src other_stuff … … Open in Applications The applications specified by the app_N options are all called using open -a AppName path/to/directory. You can configure any application that can open a directory in this manner. Some recommendations are Sublime Text, SourceTree, GitHub or iTerm. Note: As you can see from my settings.json, you can also set an app_N value to a list of applications to open the selected repo in more than one app at once: … "app_2": ["Finder", "Sublime Text", "SourceTree", "iTerm"], … You can also use → on a result to access Alfred's default File Actions menu. License, Thanks This workflow is released under the MIT Licence. It uses the Alfred-Workflow and docopt libraries (both MIT Licence). The icon is by Jason Long, from git-scm.com, released under the Creative Commons Attribution 3.0 Unported Licence.
  8. If the data is already in an SQLite database, you might consider just leaving it there. As you can see, the performance is ridiculous compared to messing around with JSON and Python. (SQLlite is pure C and super-optimised for exactly this kind of stuff—it's what CoreData is based on.) I must admit, my MailTo workflow does pull data from SQLite databases and cache them in JSON, but the JSON is essentially search indices, and I would have used an SQLite cache if the performance weren't acceptable. If you're creating an FTS virtual table (FTS3 only—FTS4 isn't supported by the system Python), you just need to insert a unique id (as a reference to the original full dataset) and the fields you want to search on. In the demo workflow, I included the id for demonstration purposes (it isn't used), but set its "rank" to zero, so it is ignored when ranking the search results. If you really don't want to mess around with writing SQL queries, you can use an ORM like SQLAlchemy or Peewee. That's how most Python developers use SQL databases, tbh. They allow you to treat database tables/rows as classes/instances. Very pleasant to use. I suspect this might mean a serious restructuring of ZotQuery, but IMO the performance is compelling. It all depends on what the typical dataset size is. You can't search thousands of items with Alfred-Workflow's filter() function, but a JSON-based data cache (properly keyed) should be just fine for at least 10,000 items if combined with a more efficient search mechanism. Moving entirely to using the original SQLite db might be more work than it's worth, but I reckon re-implementing the search in SQLite is well worth it. WRT Alfred-Workflow, I've been thinking all day about a useful abstraction that could use SQLite for search. The user would, in any case, have to specify a schema. But how do I go about indexing/updating the search database? Does the user call an index() function with all the data, or specify a callback that the indexer can call if it's out of date? Should the indexer return just the ids (and rank?) of the search results, or require a callback to retrieve the full dataset, so it can return the complete data like filter()?
  9. Here's some sample log output from the above workflow to give you a concrete idea of exactly how fast sqlite full-text search is: 11:10:53 background.py:220 DEBUG Executing task `indexer` in background... 11:10:53 index.py:43 INFO Creating index database 11:10:53 index.py:56 INFO Updating index database 11:10:53 books.py:110 INFO 0 results for `im` in 0.001 seconds 11:10:53 books.py:110 INFO 0 results for `imm` in 0.001 seconds 11:10:53 books.py:110 INFO 0 results for `imma` in 0.001 seconds 11:10:55 index.py:73 INFO 44549 items added/updated in 2.19 seconds 11:10:55 books.py:110 INFO 0 results for `imman` in 1.710 seconds 11:10:55 index.py:80 INFO Index database update finished 11:10:55 background.py:270 DEBUG Task `indexer` finished 11:10:55 books.py:110 INFO 15 results for `immanuel` in 0.002 seconds 11:10:58 books.py:110 INFO 100 results for `p` in 0.017 seconds 11:10:59 books.py:110 INFO 4 results for `ph` in 0.002 seconds 11:10:59 books.py:110 INFO 0 results for `phi` in 0.002 seconds 11:11:00 books.py:110 INFO 9 results for `phil` in 0.002 seconds 11:11:00 books.py:110 INFO 3 results for `philo` in 0.002 seconds 11:11:00 books.py:110 INFO 0 results for `philos` in 0.001 seconds 11:11:00 books.py:110 INFO 0 results for `philosp` in 0.001 seconds 11:11:01 books.py:110 INFO 0 results for `philospo` in 0.001 seconds 11:11:01 books.py:110 INFO 0 results for `philosp` in 0.001 seconds 11:11:02 books.py:110 INFO 0 results for `philos` in 0.002 seconds 11:11:02 books.py:110 INFO 0 results for `philoso` in 0.001 seconds 11:11:02 books.py:110 INFO 0 results for `philosoh` in 0.003 seconds 11:11:02 books.py:110 INFO 0 results for `philosohp` in 0.002 seconds 11:11:02 books.py:110 INFO 0 results for `philosohpy` in 0.002 seconds 11:11:03 books.py:110 INFO 0 results for `philosohp` in 0.002 seconds 11:11:03 books.py:110 INFO 0 results for `philosoh` in 0.001 seconds 11:11:03 books.py:110 INFO 0 results for `philoso` in 0.001 seconds 11:11:03 books.py:110 INFO 0 results for `philosop` in 0.001 seconds 11:11:03 books.py:110 INFO 0 results for `philosopj` in 0.001 seconds 11:11:03 books.py:110 INFO 0 results for `philosopjy` in 0.002 seconds 11:11:04 books.py:110 INFO 0 results for `philosopj` in 0.002 seconds 11:11:04 books.py:110 INFO 0 results for `philosop` in 0.002 seconds 11:11:04 books.py:110 INFO 0 results for `philosoph` in 0.002 seconds 11:11:04 books.py:110 INFO 100 results for `philosophy` in 0.012 seconds 11:11:08 books.py:110 INFO 100 results for `philosophy ` in 0.007 seconds 11:11:09 books.py:110 INFO 2 results for `philosophy t` in 0.002 seconds 11:11:09 books.py:110 INFO 0 results for `philosophy ti` in 0.002 seconds 11:11:10 books.py:110 INFO 0 results for `philosophy tit` in 0.002 seconds 11:11:11 books.py:110 INFO 0 results for `philosophy titl` in 0.002 seconds 11:11:11 books.py:110 INFO 0 results for `philosophy title` in 0.002 seconds 11:11:11 books.py:110 INFO 100 results for `philosophy title:` in 0.007 seconds 11:11:11 books.py:110 INFO 0 results for `philosophy title:t` in 0.002 seconds 11:11:11 books.py:110 INFO 0 results for `philosophy title:th` in 0.002 seconds 11:11:11 books.py:110 INFO 72 results for `philosophy title:the` in 0.010 seconds 11:11:12 books.py:110 INFO 40 results for `philosophy a` in 0.006 seconds 11:11:13 books.py:110 INFO 0 results for `philosophy au` in 0.002 seconds 11:11:13 books.py:110 INFO 0 results for `philosophy aut` in 0.002 seconds 11:11:13 books.py:110 INFO 0 results for `philosophy auth` in 0.002 seconds 11:11:13 books.py:110 INFO 0 results for `philosophy autho` in 0.002 seconds 11:11:13 books.py:110 INFO 0 results for `philosophy author` in 0.002 seconds 11:11:14 books.py:110 INFO 100 results for `philosophy author:` in 0.009 seconds 11:11:14 books.py:110 INFO 0 results for `philosophy author:k` in 0.002 seconds 11:11:14 books.py:110 INFO 0 results for `philosophy author:ka` in 0.002 seconds 11:11:14 books.py:110 INFO 0 results for `philosophy author:kan` in 0.002 seconds 11:11:15 books.py:110 INFO 0 results for `philosophy author:kant` in 0.002 seconds 11:11:18 books.py:110 INFO 3 results for `philosophy author:a` in 0.003 seconds 11:11:18 books.py:110 INFO 0 results for `philosophy author:ar` in 0.002 seconds 11:11:19 books.py:110 INFO 0 results for `philosophy author:ari` in 0.002 seconds 11:11:19 books.py:110 INFO 0 results for `philosophy author:aris` in 0.002 seconds 11:11:20 books.py:110 INFO 0 results for `philosophy author:arist` in 0.002 seconds 11:11:20 books.py:110 INFO 0 results for `philosophy author:aristo` in 0.002 seconds 11:11:20 books.py:110 INFO 0 results for `philosophy author:aristot` in 0.002 seconds 11:11:20 books.py:110 INFO 0 results for `philosophy author:aristotl` in 0.002 seconds 11:11:20 books.py:110 INFO 0 results for `philosophy author:aristotle` in 0.002 seconds 11:11:22 books.py:110 INFO 15 results for `author:aristotle` in 0.002 seconds
  10. No idea, tbh. Haven't used it in a long time. I'd imagine it isn't possible to nest data because that's just not how most search engines work. To prove my point re search indices, I've spent my evening writing a demo workflow showing how to use sqlite3 for a search index. Here's the workflow. Here's the GitHub repo. Hopefully, it's sufficiently well commented to be useful as a starting point. (If not, ask away.) The dataset is the (almost) complete list of Project Gutenberg ebooks. There are 45,000 entries (author, title, URL). Now tell me this doesn't totally rock Note: If you want to use "AND" or "OR", they have to be in capitals. (So sue me…) Man, I should've used this from the start in Alfred-Workflow By all means use whoosh, but I reckon sqlite3 is eminently suitable for ZotQuery's needs (especially as whoosh is designed for searching separate documents, not a single monolithic data file) and is included with the system Python. A bit of SQL is something every developer should know.
  11. I have used it once or twice. It's pretty damn good. You might want to try it out for ZotQuery. It's a much better fit than Alfred-Workflow. It's probably the simplest solution if you don't want to start messing around with SQL. TBH, creating an index is probably simpler than caching queries. You don't have all these thorny questions about which queries to cache, how to keep cache size under control, when to delete cached queries etc. It's also trivial to update the index in the background (using background.py). You can also do "abc AND xyz" with sqlite's full-text search.
  12. An inverted index or a forward index. Exactly how you structure it depends on which search semantics you want to use. You might want to parse the search keys (in filter() terms) for capital letters or the first letters of words to maintain "of"->"OmniFocus" style searching, or just store the entire key in the DB and rely on sqlite's (awesome) search capabilities. I don't know how ZotQuery users typically search. If you're doing any additional processing, you'd probably want to store the search key in the database as a way of determining if an item has been updated and needs re-indexing.
  13. You shouldn't directly use the query as the filename, as this would break if someone enters "/" in a query, for example. You could use a slugify function or give the cache files random names and keep an index. You should load a cache if query starts with the query of the cached data. So, say you have a cache for the query "cun", that would be loaded for the new query "cunning". Whether you then save another cache for "cunning" is a tricky question (presumably, you'd also already have caches for "cunn", "cunni", "cunnin"). You might want to consider the number of results when deciding whether to cache them. If the parent set only contains, say, 300 entries, it's probably not worth creating another cache. You'd probably want to purge all caches when the underlying data is updated. I wouldn't worry too much about the cache size: instead of duplicating all the data, you can just cache the search keys and some form of ID with which you can look up the actual data in your main dataset. (Unless that's so big it significantly impacts performance.) You could occasionally sweep the cache for old cache files (by last access time, not modification time). Personally, I think creating a proper search index is a much better solution. You can use the background.py module to update the index whenever the data has changed (indexing would be a lot slower than filtering). If you put the index data in an sqlite database, actual search would be super fast.
  14. That's what happens when an author updates a workflow on Packal but forgets to bump the version number. Which is also a bug, but it's with Packal.
  15. That would be the right way to go about it. You'd also need to write a function (that gets run first) to delete any old caches or you'll end up with dozens of copies of the data.
  16. Well, I'd like to take issue with that. Not in a it's-not-my-fault way, but in a Shawn-did-99.9%-of-the-work way. My contribution has been distinctly minimal. This is his (great) idea and his work and any credit belongs to him. Let's compare it to the guy who designs and builds a race car and the guy who tightens the nuts.
  17. Deeply cool. 102 workflows Shawn? How the hell do you remember all the keywords? EDIT: Oh…
  18. It shows me Alfred's birthday and Jesus's birthday.
  19. It does start a new search whenever you change the query. That's how Alfred works. It calls the workflow anew every time you press a key instead of interacting with a longer-running process (that could hold onto its filtered set of results). Ideally, a workflow would create a search index for that particular set of data, but I haven't looked into that yet (I wrote one of the underlying libraries that ZotQuery uses and which provides the filtering functionality). The filter function uses a relatively complex search algorithm (to rate "of", for example, as a better match for "OmniFocus" than for "Duke of York", which is in turn better than "Smirnoff"), and as a result isn't capable of rapidly searching thousands of data (complex code, no search index, relatively slow programming language) unless you turn off some of the filter options. You could try "working with" the search algorithm, i.e. "km" will match "Kant Metaphysics", or if you're prepared to get your hands dirty, you could look at the search options and tinker with ZotQuery's source here and here to whittle down the applied rules to achieve an acceptable compromise between flexibility and performance. I can look into the feasibility of caching "partial" searches and/or creating a search index, but fundamentally, the library on which ZotQuery is based wasn't designed for searching thousands of data without disabling a lot of the "intelligence" of the search algorithm.
  20. Yeah, double-checked the format, deleted the settings file and re-installed before posting. I'm on Mavericks, so Python 2.7. FWIW, python will always refer to the system Python in Alfred, as it doesn't use your PATH. Unless someone was dumb enough to overwrite /usr/bin/python…
  21. And another thing: the workflow description on Packal says "A very basic date calculator". Basic? Premium more like.
  22. It doesn't work for me I followed the instructions above, but "dcalcset list add dean xx-xx-xxxx" give me an "Invalid command" error "dcalcset format" now appears to be an unknown command You also appear to have removed the download link from the OP. It's got links to all the stuff you used to make the workflow (apart from Smirnoff), but there doesn't appear to be one to the actual workflow any more!
  23. AppleScript is utterly insane. I'm so happy they've added JavaScript in Yosemite. I don't like JavaScript at all, but hey, it ain't AppleScript
  24. Good thinking, dfay! Especially sending it to Fantastical. I think Apple Calendar can handle simpler dates, too, but I'm not sure. One minor gripe: yyyy-mm-dd is an international date format, not a US one. Everyone uses it.
×
×
  • Create New...