├── .flake8 ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── notionscripts ├── __init__.py ├── config.py ├── disable_cache.py ├── notion_api.py └── tasks_transition_updater.py ├── requirements.txt └── setup.py /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 160 3 | max-complexity = 10 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [v0.2.1](https://github.com/kevinjalbert/notion-scripts/tree/v0.2.1) (2019-11-27) 4 | 5 | [Full Changelog](https://github.com/kevinjalbert/notion-scripts/compare/v0.2.0...v0.2.1) 6 | 7 | **Merged pull requests:** 8 | 9 | - feat: add ability to append text to a block [\#1](https://github.com/kevinjalbert/notion-scripts/pull/1) ([kevinjalbert](https://github.com/kevinjalbert)) 10 | 11 | ## [v0.2.0](https://github.com/kevinjalbert/notion-scripts/tree/v0.2.0) (2019-09-10) 12 | 13 | [Full Changelog](https://github.com/kevinjalbert/notion-scripts/compare/v0.1.0...v0.2.0) 14 | 15 | ## [v0.1.0](https://github.com/kevinjalbert/notion-scripts/tree/v0.1.0) (2019-08-18) 16 | 17 | [Full Changelog](https://github.com/kevinjalbert/notion-scripts/compare/0b0e456a642da61732361bd2a190227790d0434f...v0.1.0) 18 | 19 | 20 | 21 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Kevin Jalbert 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This repository is ⚰️ ARCHIVED ⚰️ 2 | 3 | `notion-scripts` as an individual project has been sunsetted and merged into [`notion-toolbox`](https://github.com/kevinjalbert/notion-toolbox). In `notion-toolbox` is where this project lives on. The merge happened on December 27, 2019. 4 | 5 | --- 6 | 7 | ## Install 8 | 9 | This package is using Python 3. 10 | 11 | ```sh 12 | pip install git+https://github.com/kevinjalbert/notion-scripts 13 | ``` 14 | 15 | ## Usage 16 | 17 | Some quick usage is shown here: 18 | 19 | ```python 20 | from notionscripts.notion_api import NotionApi 21 | 22 | notion_api = NotionApi() 23 | notion_api.current_day() 24 | notion_api.append_to_current_day_notes("title of a note") 25 | ``` 26 | 27 | For more detailed usage I would recommend taking a look at [`alfred-notion`](https://github.com/kevinjalbert/alfred-notion) and [`notion-heroku`](https://github.com/kevinjalbert/notion-heroku). 28 | 29 | A more completed set of features and instructions will be provided soon. 30 | 31 | ## Author 32 | 33 | 👤 **Kevin Jalbert** 34 | 35 | * Twitter: [@KevinJalbert](https://twitter.com/KevinJalbert) 36 | * Github: [@kevinjalbert](https://github.com/kevinjalbert) 37 | 38 | ## 🤝 Contributing 39 | 40 | Contributions, issues and feature requests are welcome!
Feel free to check [issues page](https://github.com/kevinjalbert/notion-scripts/issues). 41 | 42 | ## Show your support 43 | 44 | Give a ⭐️ if this project helped you! 45 | 46 | ## 📝 License 47 | 48 | Copyright © 2019 [Kevin Jalbert](https://github.com/kevinjalbert).
49 | This project is [MIT](https://github.com/kevinjalbert/notion-scripts/blob/master/LICENSE) licensed. 50 | 51 | *** 52 | _This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_ 53 | -------------------------------------------------------------------------------- /notionscripts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinjalbert/notion-scripts/27f909ca7c53a99651a52fe76c6aa062b8ed034c/notionscripts/__init__.py -------------------------------------------------------------------------------- /notionscripts/config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S PATH="${PATH}:/usr/local/bin" python3 2 | 3 | import os 4 | from cachetools import cached 5 | 6 | 7 | class Config(): 8 | @cached(cache={}) 9 | def notion_token(self): 10 | return os.environ.get('NOTION_TOKEN') 11 | 12 | @cached(cache={}) 13 | def tasks_database_url(self): 14 | return os.environ.get('TASKS_DATABASE_URL') 15 | 16 | @cached(cache={}) 17 | def tags_database_url(self): 18 | return os.environ.get('TAGS_DATABASE_URL') 19 | 20 | @cached(cache={}) 21 | def wins_database_url(self): 22 | return os.environ.get('WINS_DATABASE_URL') 23 | 24 | @cached(cache={}) 25 | def year_page_url(self): 26 | return os.environ.get('YEAR_PAGE_URL') 27 | 28 | @cached(cache={}) 29 | def imported_tag_url(self): 30 | return os.environ.get('IMPORTED_TAG_URL') 31 | -------------------------------------------------------------------------------- /notionscripts/disable_cache.py: -------------------------------------------------------------------------------- 1 | from notion.store import RecordStore 2 | 3 | 4 | def no_save_cache(self, attribute): 5 | return() 6 | 7 | 8 | def no_load_cache(self, attributes=("_values", "_role", "_collection_row_ids")): 9 | return() 10 | 11 | 12 | # Prevents the cache from being written and loaded 13 | # Provides a massive speed boost for short lived commands 14 | RecordStore._save_cache = no_save_cache 15 | RecordStore._load_cache = no_load_cache 16 | -------------------------------------------------------------------------------- /notionscripts/notion_api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S PATH="${PATH}:/usr/local/bin" python3 2 | 3 | from cachetools import cached 4 | from datetime import datetime 5 | 6 | from notion.client import NotionClient 7 | from notion.block import DividerBlock, TextBlock 8 | 9 | from notionscripts.config import Config 10 | from notionscripts.tasks_transition_updater import TasksTransitionUpdater 11 | import notionscripts.disable_cache # noqa: F401 12 | 13 | 14 | class NotionApi(): 15 | def __init__(self, config=Config()): 16 | self.config = config 17 | 18 | @cached(cache={}) 19 | def client(self): 20 | return NotionClient(token_v2=self.config.notion_token(), monitor=False) 21 | 22 | @cached(cache={}) 23 | def tags_database(self): 24 | return self.client().get_collection_view(self.config.tags_database_url()) 25 | 26 | @cached(cache={}) 27 | def tasks_database(self): 28 | return self.client().get_collection_view(self.config.tasks_database_url()) 29 | 30 | @cached(cache={}) 31 | def wins_database(self): 32 | return self.client().get_collection_view(self.config.wins_database_url()) 33 | 34 | def get_block(self, id): 35 | return self.client().get_block(id) 36 | 37 | def append_text_to_block(self, block, text): 38 | return block.children.add_new(TextBlock, title=text) 39 | 40 | @cached(cache={}) 41 | def current_year(self): 42 | return self.client().get_block(self.config.year_page_url()) 43 | 44 | @cached(cache={}) 45 | def current_week(self): 46 | found_week = None 47 | current_date = datetime.now() 48 | 49 | # Sunday Starts the week 50 | week_number = str(current_date.isocalendar()[ 51 | 1] + (current_date.isoweekday() == 7)) 52 | 53 | for week_page in self.current_year().children: 54 | if week_page.title.startswith("Week " + week_number): 55 | found_week = week_page 56 | break 57 | else: 58 | next 59 | 60 | return found_week 61 | 62 | @cached(cache={}) 63 | def current_day(self): 64 | found_day = None 65 | current_date = datetime.now() 66 | 67 | day_number = str(current_date.day) 68 | month_name = current_date.strftime("%B") 69 | days_page = self.current_week().children[1].children[1] 70 | 71 | for day_page in days_page.children: 72 | if day_page.title.startswith(month_name + " " + day_number): 73 | found_day = day_page 74 | break 75 | else: 76 | next 77 | 78 | return found_day 79 | 80 | def append_to_current_day_notes(self, content): 81 | # Get the divider block that signifies the end of the notes for the current day 82 | divider_block = [x for x in self.current_day().children if type(x) == DividerBlock][0] 83 | 84 | # Add note to end of the page, then move it to before the divider 85 | note_block = self.current_day().children.add_new(TextBlock, title=content) 86 | note_block.move_to(divider_block, "before") 87 | 88 | return note_block 89 | 90 | def get_current_tasks(self): 91 | filter_params = [ 92 | { 93 | "property": "status", 94 | "comparator": "enum_is", 95 | "value": "Current" 96 | } 97 | ] 98 | current_tasks_query = self.tasks_database().build_query(filter=filter_params) 99 | return current_tasks_query.execute() 100 | 101 | def transition_tasks(self): 102 | TasksTransitionUpdater(self).process() 103 | -------------------------------------------------------------------------------- /notionscripts/tasks_transition_updater.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S PATH="${PATH}:/usr/local/bin" python3 2 | 3 | from datetime import datetime 4 | from time import sleep 5 | from notion.block import ToggleBlock, TextBlock 6 | 7 | 8 | class TasksTransitionUpdater(): 9 | def __init__(self, notion_api): 10 | self.notion_api = notion_api 11 | 12 | def process(self): 13 | try: 14 | for row in self._build_task_query().execute(): 15 | sleep(0.5) 16 | 17 | task = self.notion_api.client().get_block(row.id) 18 | 19 | print("Processing '{}'".format(task.title)) 20 | 21 | # Find toggle block (or create one if it doesn't exist) 22 | toggle_block = self._find_toggle_block(task) 23 | 24 | # Create initial transition if it doesn't exist 25 | if len(toggle_block.children) == 0: 26 | print("-> Initial Transition Detected") 27 | self._create_transition(toggle_block, task) 28 | 29 | # Find last transition 30 | last_transition_block = toggle_block.children[-1] 31 | 32 | # Add next transition if task status is different 33 | if task.status not in last_transition_block.title: 34 | print("-> Transition Change Detected") 35 | self._create_transition(toggle_block, task) 36 | 37 | except Exception as e: 38 | print(e) 39 | 40 | def _build_task_query(self): 41 | filter_params = [ 42 | { 43 | "property": "completed_on", 44 | "comparator": "date_is", 45 | "value_type": "today" 46 | }, 47 | { 48 | "property": "completed_on", 49 | "comparator": "is_empty", 50 | "value_type": "exact_date" 51 | } 52 | ] 53 | return self.notion_api.tasks_database().build_query(filter=filter_params, filter_operator="or") 54 | 55 | def _find_toggle_block(self, task): 56 | toggle_block = None 57 | 58 | if len(task.children) == 0: 59 | toggle_block = task.children.add_new(ToggleBlock) 60 | 61 | if type(task.children[0]) == ToggleBlock: 62 | toggle_block = task.children[0] 63 | else: 64 | toggle_block = task.children.add_new(ToggleBlock) 65 | toggle_block.move_to(task.children[0], "before") 66 | 67 | toggle_block.title = "⏲️ Task Transitions ⏲️" 68 | 69 | return toggle_block 70 | 71 | def _create_transition(self, toggle_block, task): 72 | transition_string = "{}, {}".format(task.status, self._format_date_time(datetime.now())) 73 | toggle_block.children.add_new(TextBlock, title=transition_string) 74 | 75 | def _format_date_time(self, datetime): 76 | return datetime.strftime("%Y-%m-%d %H:%M:%S") 77 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | cachetools==3.1.0 2 | notion==0.0.21 3 | flake8==3.7.7 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup(name='notion-scripts', 4 | version='0.2.1', 5 | description='Shared collection of common Notion Scripts used in my projects', 6 | url='http://github.com/kevinjalbert/notion-scripts', 7 | author='Kevin Jalbert', 8 | author_email='kevin.j.jalbert@gmail.com', 9 | license='MIT', 10 | packages=['notionscripts'], 11 | zip_safe=False) 12 | --------------------------------------------------------------------------------