A wrapper around notion api allowing you to create objects for your databases/pages and easily perform add/update/delete/filter operations in a more readable way
Project description
notion-api-py
A wrapper around Notion Api allowing you to create objects for your databases/pages and easily perform add/update/delete/filter operations in a more readable way
There are many libraries, official/unofficial out there. However the motivation behind creating this is to have a more simplistic approach to the most basic operations to perform over a Notion database
and Notion pages
. Also, allowing for filtering
, handling pagination amoung query results and **Applying filters on a database view on Notion UI via code (Yes, the coolest unofficial thing so far 🥳) Refer section Collection view filter for more details.
Prerequiste: Generate Notion Api token and integration
Before using this Api, you will need a API token from Notion
Please follow the below guide to know how to generate a api token for your Notion account, as well as how to share databases
and pages
with your created integration
https://www.codingwithmiszu.com/2021/12/28/how-to-generate-a-notion-api-token-easily/
https://daily-dev-tips.com/posts/getting-started-with-the-notion-api/
To know more about NotionApi from the official sources
https://www.notion.so/help/create-integrations-with-the-notion-api
https://developers.notion.com/docs/authorization
Usage
Via the ‘Releases’ section of this repository to get the latest and greatest version of the library
Via pip
python3 -m pip install notion-api-py
RECOMMENDED - Setting up the secrets_file.py
Its recommened that all sensitive data be added to a secrets_file.py
file, including the token, version, database ids and cookie related informationand not committed to the repository(add to .gitignore
)
# secrets_file.py
token = "secret_XXXXXXXXX"
version = "2021-08-16"
database1_id = "AAAAAAAAAAAAAAAAAAAAA"
database2_id = "BBBBBBBBBBBBBBBBBBBB"
# used for notion Collection view filter filers - NOT OFFICIAL API
notion_client_version = ''
x_notion_active_user_header = ''
cookie = '__cookie_string__'
Notion Database
A wrapper class containing basic operations like add_page
, update_page
, delete_page
and filter
around a basic Notion database.
Example usage:
To create an object of Tasks
database (example), all you need to do is relay the database_id as database_id='XXXXXXX-XXXX-XXXX-XXXXXXXXX'
OR database_id=secrets_file.master_task_database_id
to the extended NotionDatabase
class. The other add
, update
, delete
can simply be relayed to parent NotionDatabase
class and it will take care of it.
class Tasks(NotionDatabase):
def __init__(self):
self.log = logging.getLogger(self.__class__.__name__)
NotionDatabase.__init__(self, token=secrets_file.token, version=secrets_file.version,
database_id=secrets_file.master_task_database_id)
def add(self,icon=None,properties=None):
self.log.info("--------- Preparing to Add tasks ---------")
return self.add_page(icon=icon, properties=properties)
def update(self, task_id=None, icon=None, properties=None):
self.log.info("--------- Preparing to Update tasks ---------")
return self.update_page(page_id=task_id, icon=icon, properties=properties)
def delete(self, task_id):
return self.delete_page(page_id=task_id)
def filter(self, filter):
return super().filter(filter)
Notion Filters
The NotionFilters
class provides a range of filters you can apply to the databases.
Example usage
incomplete_task_filter = NotionFilter(
NotionFilterAnd(NotionRelationFilter("Release").contains("My Custom Release")
, NotionTextFilter("Name").contains("Test Name")
, NotionCheckboxFilter("✅ ?").does_not_equal(True)).build()).build()
filtered_incomplete_tasks = Tasks().filter(incomplete_feature_filter)
NotionFilter
This is wrapper/base that encapsulates all Notion filters. You can either give it a simple single filter (any one of the Single Filter,listed below) or a compund filter
Compund filters
NotionFilterAnd
NotionFilterOr
These filter encapsulates the Single Filters listed below
Single Filters
NotionRelationFilter
- apply filter on Relations
fields
NotionCheckboxFilter
- apply filter on Checkbox
fields
NotionTextFilter
- apply filter on Text
fields
NotionDateFilter
- apply filter on Date
fields
NotionSelectFilter
- apply filter on Select
fields
NotionMultiSelecFilter
- apply filter on MultiSelect
fields
NotionNumberFilter
apply filter on Number
fields
NotionFormulaFilter
- apply filter on Formula
fields. While dealing with Number, Text, Date, Checkbox
within NotionFormulaFilter
use the NotionFormulaNumberFilter
NotionFormulaTextFilter
NotionFormulaDateFilter
NotionFormulaCheckboxFilter
NotionFilesFilter
- apply filter on Files
fields
NotionPeopleFilter
- apply filter on People
fields
Notion Page
A wrapper class containing basic operations like add_page
, update_page
, build_properties
around a basic Notion page
.
Example Usage:
To represent a Tasks
page in the above Tasks
database, we can create a TasksPage
object extending the libraries NotionPage
class. By virtue, the TasksPage
class now inherits all basic operations - add_page
, update_page
, build_properties
from NotionPage
reducing the code.
class TasksPage(NotionPage):
def __init__(self, page_id=None, existing_properties=None):
NotionPage.__init__(self, token=secrets_file.token, version=secrets_file.version
,page_id=page_id, properties=existing_properties)
By default, page_id
and existing_properties
are None. However, we need either the page_id
or existing_properties
. When we are performing a filtering operation, the Notions filter results have all the information including the properties of the pages that come in the filtered result. In this case, we can simply pass the properties from the filtered results into the object while instantiating
This helps reduce the number of api calls to Notion to fetch properties details later
# Example
incomplete_task_filter = NotionFilter(
NotionFilterAnd(NotionRelationFilter("Release").contains("My Custom Release")
, NotionTextFilter("Name").contains("Test Name")
, NotionCheckboxFilter("✅ ?").does_not_equal(True)).build()).build()
filtered_incomplete_tasks = Tasks().filter(incomplete_feature_filter)
for each_task in filtered_incomplete_tasks:
# print("each_task", each_task)
each_incomplete_task = TasksPage(existing_properties=each_task)
#The properties of each_incomplete_task can then be retrieved by below
logging.debug("Incomplete task -> %s with status %s", each_incomplete_task.get_property("Name")
,each_incomplete_task.get_property("Status"))
However, if we dont have properties, then page_id
is mandatory as a api request would be made to fetch the page properties
existing_task = TasksPage(page_id="87ec7b33-7932-bg35-d7fa7580cc63")
Dynamic Notion Request builder
The build_properties function is unique automatic dynamic request builder which auto creates the json required for update page
existing_task = TasksPage(page_id="87ec7b33-7932-bg35-d7fa7580cc63")
planned_days_relation = Relations().create("e9ec7b33-7932-bg35-d7fa7580cc63").append_to_existing(existing_task.get_property("Planned Day"))
planned_week_relation = Relations().create("303d3e79-9000-3000-b942-34ffd349af75").append_to_existing(existing_task.get_property("Planned Week"))
planned_month_relation = Relations().create("7896ty6r-344e-4763-819a-b7c7c405e4f0").append_to_existing(existing_task.get_property("Planned Month"))
# The 'Tasks' page might have several properties, but we are interesting only in updating the "Planned Day","Planned Week","Planned Month"
existing_task.update_page(icon="🔺",
properties={
"Planned Day": planned_days_relation,
"Planned Week": planned_week_relation,
"Planned Month": planned_month_relation
})
would translate to a RequestBody
in json
something like
{
"icon":
{
"type": "emoji",
"emoji": "🔺"
},
"archived": false,
"properties":
{
"Planned Day":
{
"type": "relation",
"relation":
[
{
"id": "e9ec7b33-7932-bg35-d7fa7580cc63"
}
]
},
"Planned Week":
{
"type": "relation",
"relation":
[
{
"id": "303d3e79-9000-3000-b942-34ffd349af75"
}
]
},
"Planned Month":
{
"type": "relation",
"relation":
[
{
"id": "7896ty6r-344e-4763-819a-b7c7c405e4f0"
}
]
}
}
}
Another example with date
fields (which is slightly different)
Tasks().add(icon="✅",
properties = {"Name": task_title,
"Task": task_relation,
"Hours spent": distributed_hours,
"Journal Date": Relations().create(journal_date_id).overwrite(),
"Time (Date)": {"start": "2021-12-31"
,"end": None
,"time_zone":None}
})
Notion Properties
This class is responsible for interpreting the properties
object for any page
or database
from the results of Notion requests.
Its used implicitly within the NotionDatabase
and NotionPage
classes
Its basically used to either read a specific property value from a json result or marshall a set of properties into a json request body
Notion relations
This class is responsible for handling relations
in databases, which is the basis of creating relation (much like foreign key) between different databases.
#overwrites an existing relation with the new_relation
Relations().create(new_relation).overwrite()
#appends the new_relation to an existing list of relations
Relations().create(new_relation).append_to_existing(existing_relation)
Practical usage example
Say, extending the example above, if we use overwrite()
#Before
{
"icon":
{
"type": "emoji",
"emoji": "🔺"
},
"archived": false,
"properties":
{
"Planned Day":
{
"type": "relation",
"relation":
[
{
"id": "e9ec7b33-7932-bg35-d7fa7580cc63"
}
]
},
}
}
# Updating the Planned day relation with overwrite
existing_task = TasksPage(page_id="87ec7b33-7932-bg35-d7fa7580cc63")
planned_days_relation = Relations().create("5555555-7932-bg35-d7fa7580cc63").overwrite()
existing_task.update_page(icon="🔺",
properties={
"Planned Day": planned_days_relation,
})
#After update
{
"icon":
{
"type": "emoji",
"emoji": "🔺"
},
"archived": false,
"properties":
{
"Planned Day":
{
"type": "relation",
"relation":
[
{
"id": "5555555-7932-bg35-d7fa7580cc63"
}
]
},
}
}
With the append_to_existing
option, it would add to existing relation, if already present
#Before
{
"icon":
{
"type": "emoji",
"emoji": "🔺"
},
"archived": false,
"properties":
{
"Planned Day":
{
"type": "relation",
"relation":
[
{
"id": "e9ec7b33-7932-bg35-d7fa7580cc63"
}
]
},
}
}
# Updating the Planned day relation with overwrite
existing_task = TasksPage(page_id="87ec7b33-7932-bg35-d7fa7580cc63")
planned_days_relation = Relations().create("5555555-7932-bg35-d7fa7580cc63").append_to_existing(existing_task.get_property("Planned Day"))
existing_task.update_page(icon="🔺",
properties={
"Planned Day": planned_days_relation,
})
#After update
{
"icon":
{
"type": "emoji",
"emoji": "🔺"
},
"archived": false,
"properties":
{
"Planned Day":
{
"type": "relation",
"relation":
[
{
"id": "e9ec7b33-7932-bg35-d7fa7580cc63"
},
{
"id": "5555555-7932-bg35-d7fa7580cc63"
}
]
},
}
}
More examples, can anyways seen in the code example of previous sections.
Notion sorts
Not available at the moment. This is on the wishlist
Collection view filter
This is an exciting option, which allows you auto set filters on particular view
in Notion Databases
To know more about Notion database view, refer https://www.notion.so/help/guides/using-database-views
While the official Notion Api provides an option to query
databases - https://developers.notion.com/reference/post-database-query
it doesnt provide an ability to actually “set” the filters on the UI.
Example use cases
Use case 1: where I needed this. I have Planned Days
, Planned Months
and Planned Weeks
as relation pages and I want that every morning, the filter automatically changes to reflect the correct date/week/month so I can plan my tasks accordingly
Use case 2: I use Notion for project management where I create Project → Features → Tasks. I have templates for each of these. So whenever I create a new feature using the template, it brings in the linked task database with the Related Features
filter applied. However, I would also like “auto” apply the Tags
, Projects
and Release
relations, as I know them based on the Feature page we are on. As of this writing, Notion provides no way of doing this.
I reverse engineered the api calls from Notion to thier server to see what they are doing when we apply filter operations to a view
Based on my experiments, I designed wrapper classes around it
Please note this is unofficial api call, hence you will need a logged in session cookie to make this to work
There are ‘n’ number of tutorials/chrome extensions on the net, how to get the stored cookies for a site.Currenly this is not covered here.
Once, you have the cookie details, we could use something like the below
#Applying filters on UI for Use case 1
query2filter = NotionWebQuery2(
notion_web_filter=NotionWebQuery2Filter("and"
, NotionWebDbSimpleFilter("exact", day_page_id,
"relation_contains",
task_property_id_map.get("Planned Day"))
, NotionWebDbSimpleFilter("exact", week_page_id,
"relation_contains",
task_property_id_map.get("Planned Week"))
, NotionWebDbSimpleFilter("exact", month_page_id,
"relation_contains",
task_property_id_map.get("Planned Month"))
)
#Optional, only if you require to provide aggregate operations on columns
# In my use case, wanted to provide aggregate on time spent for tasks as well as number of tasks
, notion_web_aggregations=NotionWebDbAggregations(
NotionWebDbAggregation(task_property_id_map.get("Actual time spent")).sum()
, NotionWebDbAggregation(task_property_id_map.get("Name")).count()
)
).generate()
Above, will generate the json
RequestBody for the request to be sent
Further, you pass this filter to generate_collection_view_filter_body
followed by send_collection_view_filter_request
# the collection_id and collection_view_id can be fetched from url of the view
# if you right click on a database view and use the "Copy link" you should get a url as
# https://www.notion.so/XXXXXXXXXXX?v=YYYYYYYYY
# Here the collection_id=XXXXXXXXXXX and collection_view_id="YYYYYYYYY"
payload=generate_collection_view_filter_body(collection_id, collection_view_id,
int(datetime.now().timestamp()), query2filter)
# notion_client_version, x_notion_active_user_header, cookie
# These details can be fetched by inspecting the Chrome devtools(Network) and the cookie
send_collection_view_filter_request("https://www.notion.so/api/v3/saveTransactions"
,payload
,secrets_file.notion_client_version
,secrets_file.x_notion_active_user_header
,secrets_file.cookie)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file notion-api-py-1.0.2.tar.gz
.
File metadata
- Download URL: notion-api-py-1.0.2.tar.gz
- Upload date:
- Size: 19.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.0 CPython/3.9.12
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 85aeaa443d0bb833e1277339525997e76f3f6df440868dff5bf357df7333f0ae |
|
MD5 | 639137bef8d8f7d292329e2c135fec0c |
|
BLAKE2b-256 | 3aa87770fcf24d5ff83e7764fdbc63fb4eb2b7584bdd66525dff01047bbc2dda |
File details
Details for the file notion_api_py-1.0.2-py3-none-any.whl
.
File metadata
- Download URL: notion_api_py-1.0.2-py3-none-any.whl
- Upload date:
- Size: 16.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.0 CPython/3.9.12
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 0c0f101fa6f1286a5dee36a2a56485f6c5e3f1f4f9633ce9ada383314819afe8 |
|
MD5 | 3770ebb82713302278786517e8dd8b50 |
|
BLAKE2b-256 | 94f5ffe5701d90c6e4420cd3c7952f1383e5709090fd9df6a9070fca478f715a |