-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patharticle.go
156 lines (139 loc) · 4.21 KB
/
article.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package citygraph
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net/url"
"os"
"path/filepath"
"strings"
"time"
"github.com/google/uuid"
"github.com/geomodulus/citygraph/pb"
)
var (
ArticleType = &pb.Identifier{Value: "news-contributor-article"}
IsRelated = pb.Identifier{Value: "is-related"}
)
type Duration struct {
time.Duration
}
func (d Duration) MarshalJSON() ([]byte, error) {
return json.Marshal(d.String())
}
func (d *Duration) UnmarshalJSON(b []byte) error {
var v interface{}
if err := json.Unmarshal(b, &v); err != nil {
return err
}
switch value := v.(type) {
case float64:
d.Duration = time.Duration(value)
return nil
case string:
var err error
d.Duration, err = time.ParseDuration(value)
if err != nil {
return err
}
return nil
default:
return errors.New("invalid duration")
}
}
type Article struct {
LoadedFrom string `json:"-"`
ID string `json:"id"`
Name string `json:"display_name"`
PastNames []string `json:"past_names"`
Headline string `json:"headline_html,omitempty"`
Description string `json:"h2"`
FeatureImage string `json:"img_url"`
IsLive bool `json:"is_live"`
Categories []string `json:"categories"`
Authors []string `json:"authors"`
Pitch float64 `json:"pitch,omitempty"`
Camera map[string]interface{} `json:"camera,omitempty"`
Promo string `json:"promo,omitempty"`
PubDate string `json:"pub_date"`
LastUpdated string `json:"last_updated,omitempty"`
CodeCredit string `json:"code_credit"`
PromoAt time.Time `json:"promo_start"`
PromoUntil time.Time `json:"promo_until"`
PromoWait Duration `json:"promo_wait"`
PromoExpires time.Time `json:"promo_expires,omitempty"`
Slug string `json:"slug,omitempty"`
// Loc citygraph.LngLat `json:"loc,omitempty"`
// Zoom float64 `json:"zoom,omitempty"`
Related []string `json:"related,omitempty"`
GeoJSONDatasets []*GeoJSONDataset `json:"geojson_datasets,omitempty"`
Teaser map[string]interface{} `json:"teaser,omitempty"`
Format string `json:"format,omitempty"`
}
func (a *Article) UUID() (uuid.UUID, error) {
id, err := uuid.Parse(a.ID)
if err != nil {
return uuid.UUID{}, err
}
return id, nil
}
func (a *Article) SlugID() (string, error) {
id, err := a.UUID()
if err != nil {
return "", err
}
idBytes, err := id.MarshalBinary()
if err != nil {
return "", err
}
var b strings.Builder
if _, err := b.WriteString(base64.URLEncoding.EncodeToString(idBytes)[:22]); err != nil {
return "", fmt.Errorf("base64 encode: %v", err)
}
return b.String(), err
}
func (a *Article) LoadBodyText() (string, error) {
body, err := os.ReadFile(filepath.Join(a.LoadedFrom, "article.html"))
if err != nil {
return "", fmt.Errorf("error reading article body: %v", err)
}
return string(body), nil
}
func makeSlugTitle(title string) string {
titleParts := strings.Split(title, " ")
if len(titleParts) > 5 {
titleParts = titleParts[:5]
}
cutAndMashed := strings.ToLower(strings.Join(titleParts, "-"))
sanitized := strings.TrimRight(
url.PathEscape(punctRE.ReplaceAllString(cutAndMashed, "")),
".",
)
return sanitized
}
func (a *Article) SlugTitle() string {
return makeSlugTitle(a.Name)
}
// AllSlugTitles includes past slug titles form former headlines that may still be out there as
// valid links.
func (a *Article) AllSlugTitles() (slugs []string) {
for _, name := range append(a.PastNames, a.Name) {
slugs = append(slugs, makeSlugTitle(name))
}
return
}
func (a *Article) Path() (string, error) {
slugID, err := a.SlugID()
if err != nil {
return "", fmt.Errorf("slug id: %v", err)
}
return fmt.Sprintf("/articles/%s/%s", slugID, a.SlugTitle()), nil
}
func (a *Article) VertexQuery() (*pb.VertexQuery, error) {
id, err := a.UUID()
if err != nil {
return nil, err
}
return NewSpecificVertexQuery(UUID(id)), nil
}