├── .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 |
--------------------------------------------------------------------------------