Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 1fedeeb

Browse files
committed
feat: update record when record is existed
1 parent d8c1e05 commit 1fedeeb

File tree

4 files changed

+198
-8
lines changed

4 files changed

+198
-8
lines changed

‎cmd/sync/main.go‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ func Run(lc *leetcode.Leetcode, isNotion bool) {
2121
nc := notion.NewNotion(lc.Config.Notion.Token).
2222
WithConfig("", lc.Config.Notion.DatabaseID)
2323

24+
err := nc.Init()
25+
if err != nil {
26+
log.Fatalf("notion init failed: %v", err)
27+
}
28+
2429
g, _ := errgroup.WithContext(context.TODO())
2530
nThread := 5
2631

@@ -37,7 +42,7 @@ func Run(lc *leetcode.Leetcode, isNotion bool) {
3742
for i := 0; i < nThread; i++ {
3843
g.Go(func() error {
3944
for record := range in {
40-
err := nc.Insert(record)
45+
err := nc.InsertOrUpdate(record)
4146
if err != nil {
4247
return err
4348
}
@@ -72,8 +77,9 @@ func MetaToRecord(e *meta.Meta) *notion.Record {
7277
}
7378

7479
fields := []*notion.Field{
75-
{Type: "title", Name: "ID", Content: e.Index},
76-
{Type: "text", Name: "Name", Content: e.Title},
80+
{Type: "text", Name: "_id", Content: e.ID},
81+
{Type: "text", Name: "ID", Content: e.Index},
82+
{Type: "title", Name: "Name", Content: e.Title},
7783
{Type: "url", Name: "Link", Content: e.Link},
7884
{Type: "select", Name: "Difficulty", Content: e.Difficulty},
7985
{Type: "multi_select", Name: "Tags", Content: e.Tags},

‎internal/meta/meta.go‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package meta
22

33
import (
4+
"crypto/sha256"
5+
"encoding/hex"
46
"github.com/bmatcuk/doublestar/v2"
57
"github.com/ppsteven/leetcode-tool/internal/helper"
68
"io/ioutil"
@@ -24,6 +26,7 @@ var (
2426
)
2527

2628
type Meta struct {
29+
ID string
2730
Index string
2831
Title string
2932
Difficulty string
@@ -120,6 +123,7 @@ func findMeta(content []byte, fp string) *Meta {
120123
}
121124

122125
return &Meta{
126+
ID: GetMetaID(fp),
123127
Index: findTag(content, indexRegex),
124128
Title: findTag(content, titleRegex),
125129
Difficulty: findTag(content, difficultyRegex),
@@ -140,3 +144,9 @@ func genCompleted(isCompleted bool, ext string) string {
140144
}
141145
return ext[1:] + " ❌"
142146
}
147+
148+
func GetMetaID(fp string) string {
149+
hash := sha256.New()
150+
hash.Write([]byte(fp))
151+
return hex.EncodeToString(hash.Sum(nil))
152+
}

‎internal/notion/notion.go‎

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,24 @@ package notion
22

33
import (
44
"context"
5+
"fmt"
56
"github.com/jomei/notionapi"
7+
"log"
8+
"strings"
69
)
710

11+
type PageUID string
12+
13+
type SigAndID struct {
14+
Signature string
15+
PageID notionapi.PageID
16+
}
17+
818
type Notion struct {
919
DatabaseID notionapi.DatabaseID
1020
PageID notionapi.PageID
1121
client *notionapi.Client
22+
PageSig map[PageUID]*SigAndID
1223
}
1324

1425
type Field struct {
@@ -69,6 +80,77 @@ func (r *Record) MakeProperties() notionapi.Properties {
6980
return properties
7081
}
7182

83+
type PageData struct {
84+
PageID notionapi.PageID
85+
Data map[string]string
86+
}
87+
88+
func ParsePage(page *notionapi.Page) (PageUID, *PageData) {
89+
var pageUID PageUID
90+
pageID := GetPageID(page.ID)
91+
data := make(map[string]string)
92+
for name, property := range page.Properties {
93+
data[name] = ParseProperty(property)
94+
}
95+
if v, ok := data["_id"]; ok {
96+
pageUID = PageUID(v)
97+
} else {
98+
log.Fatalf("_id not found: %v", data)
99+
}
100+
return pageUID, &PageData{PageID: notionapi.PageID(pageID), Data: data}
101+
}
102+
103+
func ParseProperty(property notionapi.Property) string {
104+
switch property.GetType() {
105+
case "title":
106+
p, ok := property.(*notionapi.TitleProperty)
107+
if !ok {
108+
log.Fatalf("title parsed failed: %v", property)
109+
}
110+
if len(p.Title) == 0 {
111+
return ""
112+
}
113+
return p.Title[0].Text.Content
114+
case "rich_text":
115+
p, ok := property.(*notionapi.RichTextProperty)
116+
if !ok {
117+
log.Fatalf("rich_text parsed failed: %v", property)
118+
}
119+
if len(p.RichText) == 0 {
120+
return ""
121+
}
122+
return p.RichText[0].Text.Content
123+
case "url":
124+
p, ok := property.(*notionapi.URLProperty)
125+
if !ok {
126+
log.Fatalf("url parsed failed: %v", property)
127+
}
128+
return p.URL
129+
case "select":
130+
p, ok := property.(*notionapi.SelectProperty)
131+
if !ok {
132+
log.Fatalf("select parsed failed: %v", property)
133+
}
134+
return p.Select.Name
135+
case "multi_select":
136+
p, ok := property.(*notionapi.MultiSelectProperty)
137+
if !ok {
138+
log.Fatalf("multi_select parsed failed: %v", property)
139+
}
140+
optionStr := ""
141+
for _, option := range p.MultiSelect {
142+
optionStr += option.Name + ","
143+
}
144+
if len(optionStr) > 0 {
145+
optionStr = optionStr[:len(optionStr)-1]
146+
}
147+
return optionStr
148+
default:
149+
log.Fatalf("%s not supported current", property.GetType())
150+
}
151+
return ""
152+
}
153+
72154
func NewNotion(token string) *Notion {
73155
client := notionapi.NewClient(notionapi.Token(token))
74156
return &Notion{
@@ -77,11 +159,25 @@ func NewNotion(token string) *Notion {
77159
}
78160

79161
func (n *Notion) WithConfig(pageID, databaseID string) *Notion {
80-
n.DatabaseID = notionapi.DatabaseID(databaseID)
81162
n.PageID = notionapi.PageID(pageID)
163+
n.DatabaseID = notionapi.DatabaseID(databaseID)
82164
return n
83165
}
84166

167+
func (n *Notion) Init() error {
168+
if n.DatabaseID != "" {
169+
records, err := n.Query()
170+
if err != nil {
171+
return fmt.Errorf("query failed: %v", err)
172+
}
173+
n.PageSig = make(map[PageUID]*SigAndID)
174+
for pageUID, pageData := range records {
175+
n.PageSig[pageUID] = &SigAndID{PageID: pageData.PageID, Signature: GetPageSig(pageData.Data)}
176+
}
177+
}
178+
return nil
179+
}
180+
85181
func (n *Notion) Insert(record *Record) error {
86182
ctx := context.Background()
87183

@@ -95,3 +191,56 @@ func (n *Notion) Insert(record *Record) error {
95191
_, err := n.client.Page.Create(ctx, pageReq)
96192
return err
97193
}
194+
195+
func (n *Notion) Update(pageID notionapi.PageID, record *Record) error {
196+
ctx := context.Background()
197+
198+
updateReq := &notionapi.PageUpdateRequest{
199+
Properties: record.MakeProperties(),
200+
}
201+
_, err := n.client.Page.Update(ctx, pageID, updateReq)
202+
203+
return err
204+
}
205+
206+
func (n *Notion) Query() (records map[PageUID]*PageData, err error) {
207+
ctx := context.Background()
208+
209+
dbQueryReq := &notionapi.DatabaseQueryRequest{}
210+
211+
queryResp, err := n.client.Database.Query(ctx, n.DatabaseID, dbQueryReq)
212+
213+
if err != nil {
214+
return nil, err
215+
}
216+
217+
records = make(map[PageUID]*PageData)
218+
for _, result := range queryResp.Results {
219+
pageUID, pageData := ParsePage(&result)
220+
records[pageUID] = pageData
221+
}
222+
return records, nil
223+
}
224+
225+
func (n *Notion) InsertOrUpdate(record *Record) error {
226+
pageUID, pageData := ParsePage(&notionapi.Page{Properties: record.MakeProperties()})
227+
228+
if pageSig, ok := n.PageSig[pageUID]; ok {
229+
// 签名不一致,更新行信息
230+
if pageSig.Signature != GetPageSig(pageData.Data) {
231+
return n.Update(n.PageSig[pageUID].PageID, record)
232+
} else {
233+
return nil
234+
}
235+
}
236+
237+
return n.Insert(record)
238+
}
239+
240+
func GetPageSig(v map[string]string) string {
241+
return fmt.Sprintf("%s_%s_%s_%s_%s_%s", v["Difficulty"], v["ID"], v["Link"], v["Name"], v["Solved"], v["Tags"])
242+
}
243+
244+
func GetPageID(objectID notionapi.ObjectID) string {
245+
return strings.ReplaceAll(objectID.String(), "-", "")
246+
}

‎internal/notion/notion_test.go‎

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import (
88

99
func TestNotion(t *testing.T) {
1010
notion := NewNotion("secret_xxxxxx")
11-
notion.WithConfig("", "xxxx")
11+
notion.WithConfig("xxx", "xxxx")
1212

1313
m := &meta.Meta{
14+
"465b111ca3e74113a0fc382aa1d3dfa6",
1415
"1",
1516
"Binary Search",
1617
"Medium",
@@ -24,10 +25,33 @@ func TestNotion(t *testing.T) {
2425
""}
2526
record := MetaToRecord(m)
2627

27-
t.Run("", func(t *testing.T) {
28+
t.Run("Insert", func(t *testing.T) {
2829
err := notion.Insert(record)
2930
assert.NoError(t, err)
3031
})
32+
33+
t.Run("Query", func(t *testing.T) {
34+
ret, err := notion.Query()
35+
assert.NoError(t, err)
36+
t.Log(ret)
37+
})
38+
39+
t.Run("Init", func(t *testing.T) {
40+
err := notion.Init()
41+
assert.NoError(t, err)
42+
})
43+
44+
t.Run("Update", func(t *testing.T) {
45+
err := notion.Update("465b111ca3e74113a0fc382aa1d3dfa6", MetaToRecord(m))
46+
assert.NoError(t, err)
47+
})
48+
t.Run("InsertOrUpdate", func(t *testing.T) {
49+
_ = notion.Init()
50+
51+
m.Title = "test"
52+
err := notion.InsertOrUpdate(MetaToRecord(m))
53+
assert.NoError(t, err)
54+
})
3155
}
3256

3357
func MetaToRecord(e *meta.Meta) *Record {
@@ -39,8 +63,9 @@ func MetaToRecord(e *meta.Meta) *Record {
3963
}
4064

4165
fields := []*Field{
42-
{Type: "title", Name: "ID", Content: e.Index},
43-
{Type: "text", Name: "Name", Content: e.Title},
66+
{Type: "text", Name: "_id", Content: e.ID},
67+
{Type: "text", Name: "ID", Content: e.Index},
68+
{Type: "title", Name: "Name", Content: e.Title},
4469
{Type: "url", Name: "Link", Content: e.Link},
4570
{Type: "select", Name: "Difficulty", Content: e.Difficulty},
4671
{Type: "multi_select", Name: "Tags", Content: e.Tags},

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /