-
-
Save gauchy/3866fa059eecd2081ec92bea07a4a29f to your computer and use it in GitHub Desktop.
import requests, json | |
#Notion's token and databaseId | |
token = 'XXX' | |
databaseId = 'XXX' | |
#Notion's headers | |
headers = { | |
"Authorization": "Bearer " + token, | |
"Content-Type": "application/json", | |
"Notion-Version": "2021-05-13" | |
} | |
#Habitica's headers | |
headersHabitica = { | |
"x-api-user": "XXX", | |
"x-api-key": "XXX" | |
} | |
#Completed Status | |
def completed(): | |
return 'Completed'; | |
#Prefix to the task when created in Habitica | |
def prefixChar(): | |
return "N2H_" | |
def readDatabaseOfNotion(databaseId, headers): | |
readUrl = f"https://api.notion.com/v1/databases/{databaseId}/query" | |
res = requests.request("POST", readUrl, headers=headers) | |
data = res.json() | |
print(res.status_code) | |
# print(res.text) | |
with open('./notion.json', 'w', encoding='utf8') as f: | |
json.dump(data, f, ensure_ascii=False) | |
def readHabiticaData(headersHabitica): | |
url = "https://habitica.com/api/v3/tasks/user?type=todos" | |
res = requests.request("GET", url, headers=headersHabitica) | |
data = res.json() | |
print(res.status_code) | |
# print(res.text) | |
with open('./habitica.json', 'w', encoding='utf8') as f: | |
json.dump(data, f, ensure_ascii=False) | |
def readHabiticaDoneData(headersHabitica): | |
url = "https://habitica.com/api/v3/tasks/user?type=completedTodos" | |
res = requests.request("GET", url, headers=headersHabitica) | |
data = res.json() | |
print(res.status_code) | |
# print(res.text) | |
with open('./habitica_done.json', 'w', encoding='utf8') as f: | |
json.dump(data, f, ensure_ascii=False) | |
def createTodoInHabitica(name, headersHabitica): | |
name = prefixN2H(name) | |
url = "https://habitica.com/api/v3/tasks/user" | |
res = requests.post(url, headers=headersHabitica, json={"text": name, "type": "todo", "priority":"2"}) | |
data = res.json() | |
#print(res.text) | |
def scoreTaskInHabitica(id): | |
url = f"https://habitica.com/api/v3/tasks/{id}/score/up" | |
res = requests.post(url, headers=headersHabitica) | |
print(res.status_code) | |
def scoreTaskInNotion(name, headers): | |
url = f"https://api.notion.com/v1/pages/{name}" | |
res = requests.patch(url, headers=headers, json={"properties": {"Status" : {"select" :{"name": completed()}}}}) | |
data = res.json() | |
#print(res.text) | |
print(res.status_code) | |
def prefixN2H(taskName): | |
return prefixChar()+taskName | |
def isAbsentInHabitica(taskName, habiticaList): | |
taskName = prefixN2H(taskName) | |
for i in habiticaList: | |
if taskName == i['name']: | |
return False | |
return True | |
def getHabiticaList(habitica_file): | |
lst = [] | |
f = open(habitica_file,encoding="utf8") | |
data = json.load(f) | |
for i in data['data']: | |
name = i['text'] | |
id = i['id'] | |
dict = {'name':name , 'id':id} | |
lst.append(dict) | |
f.close() | |
return lst | |
def getNotionList(condn): | |
lst = [] | |
f = open("notion.json") | |
data = json.load(f) | |
for i in data['results']: | |
name = i['properties']['Name']['title'][0]['text']['content'] | |
status = i['properties']['Status']['select']['name'] | |
id = i['id'] | |
if condn(status): | |
dict = {'name':name , 'id':id} | |
lst.append(dict) | |
f.close() | |
return lst | |
def notionDoneCondn(status): | |
return status == completed() | |
def notionNotDoneCondn(status): | |
return status != completed() | |
def getDoneListOfNotion(): | |
return getNotionList(notionDoneCondn) | |
def getNotDoneListOfNotion(): | |
return getNotionList(notionNotDoneCondn) | |
def getTaskId(name, list): | |
for i in list: | |
if name == i['name']: | |
return i['id'] | |
def syncNotionToHabitica(): | |
print('==========================') | |
print('Syncing Notion to Habitica') | |
print('==========================') | |
habiticaList = getHabiticaList("habitica.json") | |
notionDoneList = getDoneListOfNotion() | |
for task in notionDoneList: | |
print('Processing completed Notion Task in Habitica ' + task['name']) | |
name = prefixN2H(task['name']) | |
habiticaid = getTaskId(name,habiticaList) | |
if habiticaid is not None: | |
print('Scoring in Habitica for ' + name + ':' + habiticaid) | |
scoreTaskInHabitica(habiticaid) | |
notionNotDoneList = getNotDoneListOfNotion() | |
for task in notionNotDoneList: | |
print('Processing Incomplete Notion Task ' + task['name']) | |
if isAbsentInHabitica(task['name'],habiticaList): | |
print('Missing in Habitica, Creating '+ task['name'] ) | |
createTodoInHabitica(task['name'], headersHabitica) | |
def syncHabiticaToNotion(): | |
print('==========================') | |
print('Syncing Habitica to Notion') | |
print('==========================') | |
notionNotDoneList = getNotDoneListOfNotion() | |
habiticaDoneList = getHabiticaList("habitica_done.json") | |
for task in notionNotDoneList: | |
try: | |
print('Processing Notion task ' + task['name']) | |
habitica_name = prefixN2H(task['name']) | |
for i in habiticaDoneList : | |
if habitica_name == i['name']: | |
print('Scoring in Notion for ' + habitica_name + ':' + task['id']) | |
scoreTaskInNotion(task['id'], headers) | |
except Exception as e: | |
#print(e) | |
print('Cannot score : Old or absent Habitica todo ' + task['name']) | |
def readDB(): | |
print('==========================') | |
print('Reading data') | |
print('==========================') | |
print('Reading Notion Data') | |
readDatabaseOfNotion(databaseId, headers) | |
print('Reading Habitica Data') | |
readHabiticaData(headersHabitica) | |
print('Reading Habitica Done Data') | |
readHabiticaDoneData(headersHabitica) | |
readDB() | |
syncNotionToHabitica() | |
syncHabiticaToNotion() | |
great work mate, will give this a try tonight !
This was great! One change that I had to make (and I suspect I won't be alone) is in line 95 I had to change the open to f = open("notion.json", encoding='utf-8')
. This was to handle the emojis that Notion often has in their default templates (such as Roadmap).
thanks, updated!
Fantastic! Thanks for making this, it was exactly what I needed.
Thank you very much. Everything works. But what's the point if there is no backward compatibility? We moved the tasks to Habotika, marked them as done, but nothing has been updated in Notion
What's the point of doing this?
May I suggest you replace status = i['properties']['Status']['select']['name']
by status = i['properties']['Status']['status']['name']
in line 100 ? I guess the Notion API changed its nomenclature since you wrote this script.
To add to what Hydrock said, do you think it could even be possible to sync back, Habitica2Notion ? I've got no experience with their API.
@elliotfontaine - i see according to the official documentation this is right -> status = i['properties']['Status']['select']['name']
It has been a long time since i update this gist. I will try to create both way syncing between notion and habitica, perhaps within a week from now.
@gauchy Well, it didn't work with 'select', I had to change it to 'status' for the script to work. What led me to the answer was the structure of the json files created by the script.
EDIT: I would be so glad if you managed to make two-way syncing! For me the best way would be to change the name of the new task to sync on both databases, adding your current N2H_ prefix. Then, if you detect one of the two is missing (either by keeping track of the tasks in a json or simply by checking if there is a lonely N2H_ task), you would ✅ the task on both DB.
I have no experience with web services yet, but I'm eager to learn so if you need any help with this little project just ask me :)
@elliotfontaine - did a few changes for two way sync, have a look if it works.
Why status is working for you instead of select is perhaps because the property type you have chosen is of 'status' type not 'select'. See here - https://developers.notion.com/reference/property-object
The following is machine translation, English is not my native language.
Great project 👍
two way sync is work, but if the task name is duplicated, there will be problems.
I made some small modifications 62 - 190
- Notion create new property "HabiticaID" type "text" => Two-way sync key
syncNotionToHabitica
function check "habitica.json" and "habitica_done.json" for absences
def createTodoInHabitica(name, headersHabitica):
name = prefixN2H(name)
url = "https://habitica.com/api/v3/tasks/user"
res = requests.post(url, headers=headersHabitica, json={"text": name,"type": "todo", "priority":'2'})
data = res.json()
# print(res.text)
return data['data']['id']
def updateNotionInHabiticaID(pageId,habiticaId,headers):
url = f"https://api.notion.com/v1/pages/{pageId}"
res = requests.patch(url, headers=headers, json={"properties": {
"HabiticaID":{"rich_text":[
{"type":"text","text":{"content":habiticaId}}
]}
}})
data =res.json()
print(res.status_code)
def scoreTaskInHabitica(id):
url = f"https://habitica.com/api/v3/tasks/{id}/score/up"
res = requests.post(url, headers=headersHabitica)
print(res.status_code)
def scoreTaskInNotion(name, headers):
url = f"https://api.notion.com/v1/pages/{name}"
res = requests.patch(url, headers=headers, json={"properties": {"Status" : {"select" :{"name": completed()}}}})
data = res.json()
#print(res.text)
print(res.status_code)
def prefixN2H(taskName):
return prefixChar()+taskName
def isAbsentInHabitica(habiticaId, habiticaList,habiticaDoneList):
isAbsent = True
for i in habiticaList:
if habiticaId == i['id']:
isAbsent = False
for i in habiticaDoneList:
if habiticaId == i['id']:
isAbsent = False
return isAbsent
def getHabiticaList(habitica_file):
lst = []
f = open(habitica_file,encoding="utf8")
data = json.load(f)
for i in data['data']:
name = i['text']
id = i['id']
dict = {'name':name , 'id':id}
lst.append(dict)
f.close()
return lst
def getNotionList(condn):
lst = []
f = open("notion.json")
data = json.load(f)
for i in data['results']:
name = i['properties']['Name']['title'][0]['text']['content']
status = i['properties']['Status']['select']['name']
temp = i['properties']['HabiticaID']['rich_text']
if not temp:
habiticaId = ''
else:
habiticaId = temp[0]['text']['content']
id = i['id']
if condn(status):
dict = {'name':name , 'id':id, 'HabiticaID':habiticaId}
lst.append(dict)
f.close()
return lst
def notionDoneCondn(status):
return status == completed()
def notionNotDoneCondn(status):
return status != completed()
def getDoneListOfNotion():
return getNotionList(notionDoneCondn)
def getNotDoneListOfNotion():
return getNotionList(notionNotDoneCondn)
def getTaskId(habiticaId, list):
for i in list:
if habiticaId == i['id']:
return i['id']
def syncNotionToHabitica():
print('==========================')
print('Syncing Notion to Habitica')
print('==========================')
habiticaList = getHabiticaList("habitica.json")
habiticaDoneList = getHabiticaList("habitica_done.json")
notionDoneList = getDoneListOfNotion()
for task in notionDoneList:
print('Processing completed Notion Task in Habitica ' + task['name'])
name = prefixN2H(task['name'])
habiticaId = getTaskId(task['HabiticaID'],habiticaList)
if habiticaId is not None:
print('Scoring in Habitica for ' + name + ':' + habiticaId)
scoreTaskInHabitica(habiticaId)
notionNotDoneList = getNotDoneListOfNotion()
for task in notionNotDoneList:
print('Processing Incomplete Notion Task ' + task['name'])
if isAbsentInHabitica(task['HabiticaID'],habiticaList,habiticaDoneList):
print('Missing in Habitica, Creating '+ task['name'] )
habiticaId = createTodoInHabitica(task['name'], headersHabitica)
print(f'updated HabiticaID in Notion, '+ task['name'] )
updateNotionInHabiticaID(task['id'], habiticaId, headers)
def syncHabiticaToNotion():
print('==========================')
print('Syncing Habitica to Notion')
print('==========================')
notionNotDoneList = getNotDoneListOfNotion()
habiticaDoneList = getHabiticaList("habitica_done.json")
for task in notionNotDoneList:
try:
print('Processing Notion task ' + task['name'])
habitica_name = prefixN2H(task['name'])
habiticaId = task['HabiticaID']
for i in habiticaDoneList :
if habiticaId == i['id'] :
print('Scoring in Notion for ' + habitica_name + ':' + task['id'])
scoreTaskInNotion(task['id'], headers)
except Exception as e:
#print(e)
print('Cannot score : Old or absent Habitica todo ' + task['name'])
Hi there, I updated some steps of the tutorial like adding the integration and add few code inside like the difficulty of the task in Habitica depends on the priority in Notion or removing the prefix.
https://github.com/vandaref/from_notion_to_habitica/tree/main
My bad I forgot to make a fork of your work
Description:
Steps needed in Notion -
You will find good documentation on all of the above steps here - https://developers.notion.com/docs/getting-started#share-a-database-with-your-integration
Steps needed in Habitica
Note: Habitica userId is UUID and not same has user handle.
Assumptions in the script
The script makes two assumptions -
Note1: if your database has different field than Name/Status then you can change function getNotionList() and scoreTaskInNotion() to set field names of your choice. Make sure that the field type (for e.g. status is 'select' type in the above script) is set correctly if you are changing field name & type. More can be read about this here - https://developers.notion.com/reference/database
Note2: if you wish to set a different 'status' that 'Completed' that should be used to determine if a task is done or not then change the function "completed()" to set the final state of your choice.
Setup
How to run
python notion2Habitica.py
Further integration
Windows task scheduler or Linux CRON can be used to run the script at regular interval say every 1 hour.