为什么用 Notion
最开始想尝试的是 Notion AI,想着把所有记录都放到 Notion 里,能不能通过 AI 发掘一些自己没有意识到的地方,去更好地了解自己,结果效果很一般。
使用三个月后的感受
今年9月初开始正式使用,目前感觉 Notion 有以下一些优点。
- 开箱即用,除了科学上网以外不需要太多的折腾。
- 数据库功能强大、易用。
- 界面简洁美观。
- 手机端方便好用,数据同步快速。
- API 使用方便。
记录的内容及我自己的一些使用方法
日常所见所想
把日常的记录、待办事项都放到这个 Memos 的数据库里,用时间线去罗列。可以通过数据库的各种属性去展示和筛选。
快捷输入,主要依赖于 Notion API 及苹果系统的快捷指令。快捷指令参考 https://www.youtube.com/watch?v=mKgi6CWedeg
点击桌面上的 “New Memos”,键盘输入,长句子也可以通过苹果键盘自带的语音输入快速记录。
记录就会插入 Memos 这个数据库。
锁屏界面也可以点击锁屏小组件调用快捷指令来快捷输入。
macOS 上结合 Alfred,可以实现类似手机端的快捷输入。
先准备一个"3 合 1"的快捷指令,然后用 Aflred 的 workflow 去调用它。
Apple Watch上也可以调用快捷指令。
晨间日记
通过日期列的筛选还可以实现"那年今日"的功能。
还可以使用自定义的模板。
年终回顾的时候,可以用特定的筛选条件来过滤今年的以及还没有 review 过的记录。
待办事项
用来取代 Things,让完成的事项也有迹可寻。
每天重复的待办事项罗列得清楚。
通过 Notion 自动化,把完成的项目自动添加到 Memos 数据库。
其他重复的待办事项也一目了然,并且有自动化加持。
完成某一项待办事项的时候点击 Button,可以把当前日期附加到 Records 这一列当中,当作历史记录。同时会根据 Interval 和 Unit 计算下一次待办事项的日期,更新 NextTime 列。例如现在点一下 Button,结果如下:
Mac mini 上设置一个 cronjob 每天去检查 NextTime 列的日期,如果某条事项的日期是当天的话就更新 Text 列为当天的时间。当 Text 列被编辑,又会自动调用 Notion 的自动化去发出一个提醒到 Inbox 里,手机上也会有消息推送。
# Notion update for nexttime - 12/19/2024
30 05 * * * /Users/jinhuaiyao/Dropbox/my_config/Mac_Script/notion_update_for_nexttime.py >> /Users/jinhuaiyao/Log/notion_update_for_nexttime.txt 2>&1
jinhuaiyao@huaiyaos-mac-mini ~ % cat /Users/jinhuaiyao/Dropbox/my_config/Mac_Script/notion_update_for_nexttime.py
#!/Library/Frameworks/Python.framework/Versions/3.12/bin/python3.12
import requests
import json
from datetime import datetime, timedelta
# API Token and Database ID
NOTION_TOKEN = "ntn_xxxx"
DATABASE_ID = "13c41d2xxxxx"
# Headers
headers = {
"Authorization": f"Bearer {NOTION_TOKEN}",
"Content-Type": "application/json",
"Notion-Version": "2022-06-28"
}
# Today's date in ISO format
current_date = datetime.utcnow().strftime("%Y-%m-%d")
# UTC 0am, local 8 or 9am
local_am_today = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0).isoformat()
# Fetch database items
url = f"https://api.notion.com/v1/databases/{DATABASE_ID}/query"
response = requests.post(url, headers=headers)
data = response.json()
if "results" not in data:
print("Error fetching data from Notion:", data)
exit()
# Iterate over database entries
for page in data["results"]:
properties = page.get("properties", {})
# Safely access NextTime property
next_time_property = properties.get("NextTime", None)
if next_time_property and isinstance(next_time_property, dict):
next_time_date = next_time_property.get("date", None)
if next_time_date and "start" in next_time_date:
next_time = next_time_date.get("start")
if next_time == current_date:
page_id = page["id"]
# Prepare payload to update Text property
text_payload = {
"properties": {
"Text": {
"rich_text": [
{
"type": "mention",
"mention": {
"type": "date",
"date": {
"start": local_am_today,
"end": None,
"time_zone": None
}
},
"annotations": {
"bold": False,
"italic": False,
"strikethrough": False,
"underline": False,
"code": False,
"color": "default"
},
"plain_text": local_am_today
}
]
}
}
}
# Update the page
update_url = f"https://api.notion.com/v1/pages/{page_id}"
update_response = requests.patch(update_url, headers=headers, json=text_payload)
if update_response.status_code == 200:
print(f"Successfully updated Text property for page: {page_id}")
else:
print(f"Failed to update page {page_id}:", update_response.json())
jinhuaiyao@huaiyaos-mac-mini ~ % /Users/jinhuaiyao/Dropbox/my_config/Mac_Script/notion_update_for_nexttime.py
Successfully updated Text property for page: 13c41d22-a693-8009-8515-e0ce0d4629f8
效果如下:
等等的每日一照
英文学习
可以标注并且添加注释。
文章剪藏
电脑端 https://www.notion.com/web-clipper
手机端,发送到Notion。
桌面小组件展示
利用 scriptable 这个 app 来调用 notion api 展示记录。
比如即将来临的待办事项显示在手机负一屏。
// 配置参数
max_lines = 15;
let notionData = await loadDataFromNotion();
// 按照 plan_date 排序
notionData.sort((a, b) => new Date(a.time) - new Date(b.time));
if (config.runsInWidget) {
let widget = await createWidget(notionData);
Script.setWidget(widget);
} else if (config.runsWithSiri) {
let table = createTable(notionData);
await QuickLook.present(table);
} else {
let table = createTable(notionData);
await QuickLook.present(table);
}
Script.complete();
async function createWidget(notionData) {
// notionData 是一个数组,包含多个 {title, time}
let gradient = new LinearGradient();
gradient.locations = [0, 1];
gradient.colors = [
new Color("#ffffff80"),
new Color("#ffffff80")
];
let widget = new ListWidget();
widget.backgroundColor = new Color("#ffffff80");
widget.backgroundGradient = gradient;
// 显示所有记录
notionData.forEach(data => {
let timeElement = widget.addText(data.time);
timeElement.font = Font.mediumSystemFont(12);
timeElement.textColor = Color.black();
timeElement.lineLimit = 1;
let titleElement = widget.addText(data.title);
titleElement.font = Font.boldSystemFont(16);
titleElement.textColor = Color.black();
titleElement.minimumScaleFactor = 0.75;
titleElement.lineLimit = 1;
widget.addSpacer(8); // 在标题行下方增加间隔
});
return widget;
}
function createTable(notionData) {
let table = new UITable();
// 显示所有记录
notionData.forEach(data => {
// 时间行
let row_time = new UITableRow();
row_time.addText(data.time);
row_time.cellSpacing = 0; // 消除间隙
table.addRow(row_time);
// 标题行
let row_title = new UITableRow();
row_title.addText(data.title);
row_title.cellSpacing = 0; // 消除间隙
row_title.height = 40; // 增加标题行下方的高度
table.addRow(row_title);
});
return table;
}
async function loadDataFromNotion() {
let url = 'https://api.notion.com/v1/databases/xxxx/query';
let req = new Request(url);
req.method = 'POST';
req.headers = {
'Authorization': 'Bearer ntn_xxxx',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
};
// 获取当前时间,并格式化为 ISO 8601 字符串
let currentTime = new Date().toISOString();
let filterPayload = {
"filter": {
"and": [
{
"property": "Tags",
"multi_select": {
"contains": "Todo"
}
},
{
"property": "PlanDate",
"date": {
"is_not_empty": true
}
},
{
"property": "Status",
"status": {
"equals": "Not Started"
}
}
]
}
};
req.body = JSON.stringify(filterPayload);
let json = await req.loadJSON();
let results = json.results || []; // 确保 results 存在
// 提取所有符合条件的记录
let notionData = results.map(result => {
let title_content = result.properties.Content.title[0].text.content;
let plan_date_content = result.properties.PlanDate.date.start;
// 格式化时间为中国时区
let time = formatTimeToChina(plan_date_content);
return {
title: title_content,
time: time
};
});
return notionData;
}
function formatTimeToChina(time_content) {
let utc_time = new Date(time_content);
let options = {
timeZone: "Asia/Shanghai",
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit'
};
return utc_time.toLocaleString('zh-CN', options);
}
又比如一些重要的提醒循环显示在手机桌面。
数据同步到 2024 打卡记录
Memos 数据库里特定 tag 的记录定期更新到打卡记录。
# upload heatmap data - 05/07/2024
55 23 * * * /bin/bash /Users/jinhuaiyao/Dropbox/my_config/Mac_Script/upload_heatmap_data.sh >/Users/jinhuaiyao/Log/upload_heatmap_data.txt 2>&1
jinhuaiyao@huaiyaos-mac-mini .tmp % cat /Users/jinhuaiyao/Dropbox/my_config/Mac_Script/upload_heatmap_data.sh
cd /Users/jinhuaiyao/.tmp/
DT=`date +%Y_%m_%d`
for file in `ls *.txt`
do
cp $file /Users/jinhuaiyao/OneDrive/Backup/backup_heatmap/${file}.${DT}
done
bash workout.sh
bash running.sh
bash reading.sh
bash vocabulary.sh
bash englishpod.sh
cd /Users/jinhuaiyao/.tmp/
scp -P 10086 *.txt [email protected]:/home/xx/www/webpage
jinhuaiyao@huaiyaos-mac-mini .tmp % cat workout.sh
/Library/Frameworks/Python.framework/Versions/3.12/bin/python3 workout.py > workout.1
awk -F' ' '{print $1 " |1| " substr($0, index($0,$3))}' workout.1 > workout.2
awk -F'|' '
{
key = $1 FS $2;
if (key in data) {
data[key] = data[key] ", " $3;
} else {
data[key] = $3;
}
}
END {
for (key in data) {
print key " |" data[key];
}
}' workout.2 |sort >workout.3
cat workout.txt.base workout.3 > workout.txt
jinhuaiyao@huaiyaos-mac-mini .tmp % cat workout.py
import requests
import json
import pytz
from datetime import datetime
# API 令牌
NOTION_TOKEN = "ntn_xxxx"
# 数据库ID
DATABASE_ID = "4bd4xxxx"
# 请求头
headers = {
"Authorization": f"Bearer {NOTION_TOKEN}",
"Content-Type": "application/json",
"Notion-Version": "2022-06-28"
}
# 定义过滤条件,筛选带有 "Workout" 标签的项目
filter_payload = {
"filter": {
"property": "Tags", # 属性名称,假设为 "Tags"
"multi_select": {
"contains": "Workout" # 标签内容
}
}
}
# 查询数据库的API URL
url = f"https://api.notion.com/v1/databases/{DATABASE_ID}/query"
# 发送带有过滤器的请求
response = requests.post(url, headers=headers, json=filter_payload)
# 解析并打印响应结果
data = response.json()
# 提取并解码内容
for result in data["results"]:
# 假设 Content 在 properties 里面,获取 title 属性中的内容
title_content = result["properties"]["Content"]["title"][0]["text"]["content"]
time_content = result["properties"]["Time"]["created_time"]
utc_format = "%Y-%m-%dT%H:%M:%S.%fZ"
utc_time = datetime.strptime(time_content, utc_format)
# 设置时区:UTC 和 东八区
utc = pytz.timezone('UTC')
cn_tz = pytz.timezone('Asia/Shanghai')
utc_time = utc.localize(utc_time)
cn_time = utc_time.astimezone(cn_tz)
# 打印解码后的标题内容
print(f"{cn_time}: {title_content}")
jinhuaiyao@huaiyaos-mac-mini .tmp % tail workout.txt
2024-09-23 |1 | 跑步5km
2024-10-02 |1 | 跑步5km
2024-10-03 |1 | 跑步5.7km
2024-10-05 |1 | 跑步6.1km
2024-10-13 |1 | 顾村公园快走
2024-10-20 |1 | 陪等等散步
2024-11-02 |1 | 跳绳
2024-11-09 |1 | 和等等踢球
2024-11-11 |1 | 跳绳300个
2024-11-30 |1 | 跳绳600