{"id":17823370,"url":"https://github.com/purarue/calcurse-load","last_synced_at":"2025-03-18T16:30:28.399Z","repository":{"id":94609852,"uuid":"294601904","full_name":"purarue/calcurse-load","owner":"purarue","description":"Hooks/scripts for calcurse. Sources events for calcurse from Google Calendar and todo.txt","archived":false,"fork":false,"pushed_at":"2025-02-06T20:57:21.000Z","size":56,"stargazers_count":6,"open_issues_count":4,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-28T10:44:39.713Z","etag":null,"topics":["appointments","calcurse","calendar","cli","google-calendar","googlecalendar","hook","todotxt"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/calcurse-load","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/purarue.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-09-11T05:23:22.000Z","updated_at":"2025-02-25T00:41:24.000Z","dependencies_parsed_at":null,"dependency_job_id":"a3f67094-17b2-4732-9a64-b4e3df7f525d","html_url":"https://github.com/purarue/calcurse-load","commit_stats":null,"previous_names":["purarue/calcurse-load"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/purarue%2Fcalcurse-load","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/purarue%2Fcalcurse-load/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/purarue%2Fcalcurse-load/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/purarue%2Fcalcurse-load/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/purarue","download_url":"https://codeload.github.com/purarue/calcurse-load/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243940058,"owners_count":20372044,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["appointments","calcurse","calendar","cli","google-calendar","googlecalendar","hook","todotxt"],"created_at":"2024-10-27T17:57:47.825Z","updated_at":"2025-03-18T16:30:28.392Z","avatar_url":"https://github.com/purarue.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"Hooks/scripts for loading data for calcurse. This integrates [`calcurse`](https://github.com/lfos/calcurse) with Google Calendar, and [`todo.txt`](http://todotxt.org/).\n\n- pre-load:\n  - Looks at the locally indexed Google Calendar JSON dump, adds events as `calcurse` appointments; adds summary/HTML links as appointment notes.\n  - Replace `calcurse`s todos with my current [`todo.txt`](http://todotxt.org/), converting priorities accordingly.\n- post-save\n  - If any new todos are added, write those back to my `todo.txt` file.\n\nThis doesn't write back to Google Calendar, its only used to source events.\n\nShould be mentioned that deleting a todo in calcurse does nothing, because the corresponding `todotxt` still exists. Only reason for me to load my todos into `calcurse` is to remind me what they are, and to possibly add new ones. I have [other ways](https://purarue.xyz/d/todo-prompt?dark) I mark todos as done.\n\nOther than the extensions provided here, you can also define completely custom behaviour by creating your own extensions, see [extension reference](#calcurse_load-reference)\n\nAs a general warning, if there's any output from the hooks, calcurse fails to load, so you could do something like this in your `pre-load` script:\n\n```python\npython3 -m calcurse_load --pre-load gcal \u003e\u003e/tmp/calcurse_load.log 2\u003e\u00261\n```\n\n## Setup\n\n```bash\ngit clone https://github.com/purarue/calcurse-load \u0026\u0026 cd ./calcurse-load\n# copy over calcurse hooks\n# assuming its not overwriting any hooks, else youd have to manually copy in parts of the scripts\ncp ./hooks/* ~/.config/calcurse/hooks/\npip install .  # install current directory with pip\n```\n\nThis installs 2 python scripts/modules, `gcal_index`, and `calcurse_load`.\n\n`gcal_index` has nothing to do with calcurse inherently, it could be used on its own to export all your current data from Google Calendar.\n\nThe data for calcurse is typically kept in `$XDG_DATA_HOME/calcurse` (`$HOME/.local/share/calcurse`). If you want to override that for some reason, this allows you to set the `$CALCURSE_DIR` environment variable. That's not something `calcurse` recognizes, but you could setup an alias:\n\n```bash\nexport CALCURSE_DIR=\"$HOME/Documents/calcurse\"\nalias calcurse='calcurse --datadir \"$CALCURSE_DIR\" --confdir ~/.config/calcurse \"$@\"'\n```\n\nIn addition to that, this maintains a data directory in `$XDG_DATA_HOME/calcurse_load` (you can overwrite this with `$CALCURSE_LOAD_DIR`), where it stores data for `gcal_index`.\n\n## About\n\nIf you wanted to disable one of the `todotxt` or `gcal` extensions, you could remove or rename the corresponding scripts in the `hooks` directory.\n\n## gcal pre-load\n\nThe `gcal` calcurse hook tries to read any `gcal_index`-created JSON files in the `$XDG_DATA_HOME/calcurse_load/gcal/` directory. If there's description/extra information for events from Google Calendar, this attaches corresponding notes to each calcurse event. Specifically, it:\n\n- Loads the calcurse appointments file\n- Removes any Google Calendar events (which are tagged with `[gcal]`)\n- Generates Google Calendar events from the JSON\n- Adds the newly created events and writes back to the appointments file.\n\n### gcal update example\n\n`gcal_index` saves an index of Google Calendar events for a Google Account locally as a JSON file.\n\nTo setup credentials, see [here](https://google-calendar-simple-api.readthedocs.io/en/latest/getting_started.html).\n\nPut the downloaded credentials in `~/.credentials/`, and specify the location with the `--credential-file`. I'd recommend wrapping in a script, and then setting up a job to run in the background, to update the local JSON index of Google Calendar events.\n\nIts possible to put the command to update the local JSON index in your `pre-load` hook as well, before the call to `python3 -m calcurse_load`, but that would cause some noticeable lag on calcurse start-up.\n\n```\nUsage: python -m gcal_index [OPTIONS]\n\n  Export Google Calendar events\n\nOptions:\n  --email TEXT            Google Email to export  [required]\n  --credential-file TEXT  Google credential file  [required]\n  --end-days INTEGER      Specify how many days into the future to get events\n                          for (if we went forever, repeating events would be\n                          there in 2050)  [default: 90]\n  --calendar TEXT         Specify which calendar to export from  [default:\n                          primary]\n  --help                  Show this message and exit.\n```\n\nPrints the JSON dump to STDOUT; example:\n\n`python3 -m gcal_index --email \u003cyour_email\u003e --credential-file ~/.credentials/\u003ccredential\u003e.json`\n\nFor an example script one might put under cron, see [`example_update_google_cal`](./example_update_google_cal)\n\n## todotxt\n\nThe `pre-load`/`post-save` `todotxt` hook converts the `calcurse` todos back to `todotxt` todos, and updates the `todotxt` file if any todos were added. A `todo.txt` is searched for in one of the common locations:\n\n- `$TODOTXT_FILE`\n- `$TODO_DIR/todo.txt`\n- `$XDG_CONFIG/todo/todo.txt`\n- `~/.config/todo/todo.txt`\n- `~/.todo/todo.txt`\n\n### Todo.txt Priority Conversion\n\n| Todo.txt | Calcurse |\n| -------- | -------- |\n| (A)      | 1 - 3    |\n| (B)      | 4 - 6    |\n| (C)      | 7 - 9    |\n| None     | 0        |\n\n## calcurse_load reference\n\n`calcurse_load` accepts one, or multiple pre/post hooks, with an extension name. There are individual [`hooks`](./hooks) for for each extension (`gcal`/`todotxt`)\n\nYou could instead just add the single line you want into your `pre-load` script, like: `python3 -m calcurse_load --pre-load todotxt --pre-load gcal`\n\n```\nUsage: calcurse_load [OPTIONS]\n\n  A CLI for loading data for calcurse\n\nOptions:\n  --pre-load gcal|todotxt|custom.module.name.Extension\n                                  Execute the preload action for the extension\n  --post-save gcal|todotxt|custom.module.name.Extension\n                                  Execute the postsave action for the\n                                  extension\n  --help                          Show this message and exit.\n```\n\nIf you want to use this for other purposes; there is a `Extension` base class in `calcurse_load.ext.abstract`.\n\nTo load a custom extension, you can point this at the fully qualified path to an Extension (module name + class name). This works with both absolute and relative imports.\n\n### Relative\n\nWith relative paths, the easiest way is to put the extension in a `myextension.py` file in your hooks directory:\n\n```bash\n.\n├── gcal.enabled\n├── myextension.py\n├── post-save\n├── pre-load\n└── todotxt.enabled\n\n1 directory, 5 files\n```\n\nAs an example:\n\n```python\nimport os\nfrom calcurse_load.ext.abstract import Extension\n\n\nclass Notifier(Extension):\n    \"\"\"\n    Sends a notification letting you know how many appointments were loaded\n    \"\"\"\n\n    def pre_load(self):\n        appointments = self.config.calcurse_dir / \"apts\"\n        with open(appointments, \"r\") as f:\n            lines = [l for l in f.readlines() if l.strip()]\n\n        os.system(f\"notify-send 'Loaded {len(lines)} appointments'\")\n\n    def post_save(self):\n        # do nothing\n        pass\n```\n\nThen, for example, at the top of your `pre-load`, just be sure to change the directory to the current one, and call your custom extension:\n\n```bash\n#!/bin/sh\n\ncd \"$(dirname \"$0\")\" || exit 1\n\npython3 -m calcurse_load --pre-load myextension.Notifier\n```\n\n### Absolute\n\nIf you had a wrote your own package and like `my_custom_calcurse` installed into your python environment, and\ninside that file you have a class called `MyCustomExtension`, you can\nload that extension by passing `my_custom_calcurse.MyCustomExtension` to\nthe `--pre-load` or `--post-save` options.\n\nAs another example, to use it with the gcal extension, you could also provide the fully qualified path:\n\n```bash\npython3 -m calcurse_load --pre-load calcurse_load.ext.gcal.gcal_ext\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpurarue%2Fcalcurse-load","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpurarue%2Fcalcurse-load","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpurarue%2Fcalcurse-load/lists"}