mirror of
https://github.com/muhac/chinese-holidays-calendar.git
synced 2024-11-21 01:01:50 +08:00
delete Go files
This commit is contained in:
parent
8ea57de154
commit
869a9de697
15
.github/workflows/cd.yml
vendored
15
.github/workflows/cd.yml
vendored
@ -32,16 +32,19 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: python crawler.py
|
run: python crawler.py
|
||||||
|
|
||||||
- name: Setup Go environment
|
- name: Setup Haskell and Cabal
|
||||||
uses: actions/setup-go@v4
|
uses: haskell-actions/setup@v2
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
ghc-version: 9.2.8
|
||||||
|
cabal-version: 3.10.1.0
|
||||||
|
|
||||||
- name: Go Build
|
- name: Configure and build
|
||||||
run: go build -o . main.go
|
run: |
|
||||||
|
cabal configure --enable-tests --enable-benchmarks --disable-documentation
|
||||||
|
cabal build all
|
||||||
|
|
||||||
- name: Generate ICS files
|
- name: Generate ICS files
|
||||||
run: ./main
|
run: cabal run
|
||||||
|
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
uses: stefanzweifel/git-auto-commit-action@v4
|
uses: stefanzweifel/git-auto-commit-action@v4
|
||||||
|
13
.github/workflows/ci.yml
vendored
13
.github/workflows/ci.yml
vendored
@ -97,19 +97,6 @@ jobs:
|
|||||||
path: parser/
|
path: parser/
|
||||||
fail-on: warning
|
fail-on: warning
|
||||||
|
|
||||||
golint:
|
|
||||||
name: Golint
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: golangci-lint
|
|
||||||
uses: golangci/golangci-lint-action@v3
|
|
||||||
with:
|
|
||||||
version: latest
|
|
||||||
working-directory: .
|
|
||||||
|
|
||||||
pylint:
|
pylint:
|
||||||
name: Pylint
|
name: Pylint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
2962
docs/holiday.ics
2962
docs/holiday.ics
File diff suppressed because it is too large
Load Diff
10
go.mod
10
go.mod
@ -1,10 +0,0 @@
|
|||||||
module main
|
|
||||||
|
|
||||||
go 1.19
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/google/uuid v1.3.0
|
|
||||||
github.com/samber/lo v1.28.2
|
|
||||||
)
|
|
||||||
|
|
||||||
require golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect
|
|
11
go.sum
11
go.sum
@ -1,11 +0,0 @@
|
|||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/samber/lo v1.28.2 h1:f1gctelJ5YQk336wCN+Elr90FyhZ6ArhelD5kjhNTz4=
|
|
||||||
github.com/samber/lo v1.28.2/go.mod h1:it33p9UtPMS7z72fP4gw/EIfQB2eI8ke7GR2wc6+Rhg=
|
|
||||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
|
||||||
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
|
|
||||||
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM=
|
|
||||||
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
16
main.go
16
main.go
@ -1,16 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"main/parse/app"
|
|
||||||
"main/parse/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
holidays := app.Data().Read(`^20\d\d`).From("data").Parse().Sort().Get().Print("==== HOLIDAYS ====")
|
|
||||||
|
|
||||||
app.Data(holidays).Write("index.html").To("docs").Title("节假日").Set()
|
|
||||||
app.Data(holidays).Write("holiday.ics").To("docs").Title("节假日").Set()
|
|
||||||
|
|
||||||
app.Data(holidays.Select(core.Rest)).Write("rest.ics").To("docs").Title("节假日(假期)").Set()
|
|
||||||
app.Data(holidays.Select(core.Work)).Write("work.ics").To("docs").Title("节假日(补班)").Set()
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package app
|
|
||||||
|
|
||||||
import "main/parse/core"
|
|
||||||
|
|
||||||
func Data(optional ...core.Holidays) Handler {
|
|
||||||
return newHandler(optional...)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Handler interface {
|
|
||||||
Read(filename string) setDirIn
|
|
||||||
Write(filename string) setDirOut
|
|
||||||
}
|
|
||||||
|
|
||||||
type setDirIn interface {
|
|
||||||
From(directory string) readData
|
|
||||||
}
|
|
||||||
|
|
||||||
type readData interface {
|
|
||||||
Parse() getData
|
|
||||||
}
|
|
||||||
|
|
||||||
type getData interface {
|
|
||||||
Sort() getData
|
|
||||||
Get() core.Holidays
|
|
||||||
}
|
|
||||||
|
|
||||||
type setDirOut interface {
|
|
||||||
To(directory string) setTitle
|
|
||||||
}
|
|
||||||
|
|
||||||
type setTitle interface {
|
|
||||||
Title(name string) writeData
|
|
||||||
}
|
|
||||||
|
|
||||||
type writeData interface {
|
|
||||||
Set()
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"main/parse/core"
|
|
||||||
"main/parse/data"
|
|
||||||
"main/parse/data/input"
|
|
||||||
"main/parse/data/output"
|
|
||||||
"main/parse/data/read"
|
|
||||||
"main/parse/data/write"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newHandler(optionalData ...core.Holidays) Handler {
|
|
||||||
if len(optionalData) == 0 {
|
|
||||||
return handler{}
|
|
||||||
}
|
|
||||||
return handler{data: optionalData[0]}
|
|
||||||
}
|
|
||||||
|
|
||||||
type handler struct {
|
|
||||||
data core.Holidays
|
|
||||||
|
|
||||||
reader data.Reader
|
|
||||||
writer data.Writer
|
|
||||||
filename string
|
|
||||||
|
|
||||||
input data.Input
|
|
||||||
output data.Output
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h handler) Read(filename string) setDirIn {
|
|
||||||
h.filename = filename
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h handler) From(directory string) readData {
|
|
||||||
h.reader = read.NewReader(directory, h.filename)
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h handler) Parse() getData {
|
|
||||||
h.input = h.reader.Read()
|
|
||||||
h.data = input.NewParser().Parse(h.input)
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h handler) Sort() getData {
|
|
||||||
sort.Sort(h.data)
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h handler) Write(filename string) setDirOut {
|
|
||||||
h.filename = filename
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h handler) To(directory string) setTitle {
|
|
||||||
h.writer = write.NewWriter(directory, h.filename)
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h handler) Title(name string) writeData {
|
|
||||||
h.output = output.NewFormatter(name).Format(h.data)
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h handler) Get() core.Holidays {
|
|
||||||
return h.data
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h handler) Set() {
|
|
||||||
h.writer.Write(h.output)
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/samber/lo"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Status string
|
|
||||||
|
|
||||||
const (
|
|
||||||
Rest Status = "rest" // 假日
|
|
||||||
Work Status = "work" // 补班
|
|
||||||
)
|
|
||||||
|
|
||||||
// Holidays data
|
|
||||||
type Holidays []Holiday
|
|
||||||
|
|
||||||
// Holiday data per day
|
|
||||||
type Holiday struct {
|
|
||||||
Group string
|
|
||||||
Date time.Time
|
|
||||||
Name string
|
|
||||||
Type Status
|
|
||||||
Nth int
|
|
||||||
Total int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h Holidays) Select(t Status) Holidays {
|
|
||||||
return lo.Filter(h, func(d Holiday, _ int) bool { return d.Type == t })
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h Holidays) Print(titles ...string) Holidays {
|
|
||||||
lo.ForEach(titles, func(title string, _ int) { log.Println(title) })
|
|
||||||
lo.ForEach(h, func(day Holiday, _ int) { log.Printf("%+v\n", day) })
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h Holidays) Len() int { return len(h) }
|
|
||||||
func (h Holidays) Less(i, j int) bool { return h[i].Date.Before(h[j].Date) }
|
|
||||||
func (h Holidays) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
|
@ -1,63 +0,0 @@
|
|||||||
package input
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"main/parse/core"
|
|
||||||
"main/parse/data"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewParser() data.Parser {
|
|
||||||
return parser{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type parser struct{}
|
|
||||||
|
|
||||||
func (p parser) Parse(raw data.Input) (result core.Holidays) {
|
|
||||||
for _, year := range raw {
|
|
||||||
days, _ := parse(year)
|
|
||||||
result = append(result, days...)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse(raw data.InputRaw) (result core.Holidays, err error) {
|
|
||||||
dayCount := make(map[string]map[core.Status]int)
|
|
||||||
|
|
||||||
for group, holiday := range raw.Data {
|
|
||||||
groupName := fmt.Sprintf("%04d%02d", raw.Year, group+1)
|
|
||||||
dayCount[groupName] = make(map[core.Status]int)
|
|
||||||
info := strings.Split(holiday, ";")
|
|
||||||
|
|
||||||
for i, day := range holidays(raw.Year, info[1]) {
|
|
||||||
restDay := core.Holiday{
|
|
||||||
Group: groupName,
|
|
||||||
Name: info[0],
|
|
||||||
Nth: i + 1,
|
|
||||||
Date: day,
|
|
||||||
Type: core.Rest,
|
|
||||||
}
|
|
||||||
result = append(result, restDay)
|
|
||||||
dayCount[restDay.Group][restDay.Type]++
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, day := range holidays(raw.Year, info[2]) {
|
|
||||||
workDay := core.Holiday{
|
|
||||||
Group: groupName,
|
|
||||||
Name: info[0],
|
|
||||||
Nth: i + 1,
|
|
||||||
Date: day,
|
|
||||||
Type: core.Work,
|
|
||||||
}
|
|
||||||
result = append(result, workDay)
|
|
||||||
dayCount[workDay.Group][workDay.Type]++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, holiday := range result {
|
|
||||||
result[i].Total = dayCount[holiday.Group][holiday.Type]
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package input
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func date(year int, date string) (result time.Time) {
|
|
||||||
input := fmt.Sprintf("%04d-%s", year, date)
|
|
||||||
result, _ = time.Parse("2006-1.2", input)
|
|
||||||
|
|
||||||
if date[0] == '0' { // => 0001-1.1
|
|
||||||
delta, _ := strconv.Atoi(date[2:]) // days before
|
|
||||||
result = result.AddDate(year-1, 0, -delta)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func holidays(year int, days string) (result []time.Time) {
|
|
||||||
if days == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, day := range strings.Split(days, ",") {
|
|
||||||
if strings.Contains(day, "-") {
|
|
||||||
period := strings.Split(day, "-")
|
|
||||||
d := date(year, period[0])
|
|
||||||
for !d.After(date(year, period[1])) {
|
|
||||||
result = append(result, d)
|
|
||||||
d = d.AddDate(0, 0, 1)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result = append(result, date(year, day))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
package input
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_date(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
year int
|
|
||||||
date string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantResult time.Time
|
|
||||||
}{
|
|
||||||
{"1", args{2001, "1.1"}, time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC)},
|
|
||||||
{"2", args{2002, "1.11"}, time.Date(2002, 1, 11, 0, 0, 0, 0, time.UTC)},
|
|
||||||
{"3", args{2003, "11.1"}, time.Date(2003, 11, 1, 0, 0, 0, 0, time.UTC)},
|
|
||||||
{"4", args{2004, "11.11"}, time.Date(2004, 11, 11, 0, 0, 0, 0, time.UTC)},
|
|
||||||
{"5", args{2005, "1.41"}, time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)},
|
|
||||||
{"6", args{2006, "0.1"}, time.Date(2005, 12, 31, 0, 0, 0, 0, time.UTC)},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if gotResult := date(tt.args.year, tt.args.date); !reflect.DeepEqual(gotResult, tt.wantResult) {
|
|
||||||
t.Errorf("date() = %v, want %v", gotResult, tt.wantResult)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_holidays(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
year int
|
|
||||||
daysRaw string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantResult []string
|
|
||||||
}{
|
|
||||||
{"1", args{1, "1.1"}, []string{"01.1.1"}},
|
|
||||||
{"2", args{1, "1.1,2.2"}, []string{"01.1.1", "01.2.2"}},
|
|
||||||
{"3", args{1, "1.1-1.3"}, []string{"01.1.1", "01.1.2", "01.1.3"}},
|
|
||||||
{"4", args{1, "1.1-1.3,2.2,3.3-3.4"}, []string{"01.1.1", "01.1.2", "01.1.3", "01.2.2", "01.3.3", "01.3.4"}},
|
|
||||||
{"5", args{1, "1.31-2.2"}, []string{"01.1.31", "01.2.1", "01.2.2"}},
|
|
||||||
{"6", args{1, "0.2"}, []string{"00.12.30"}},
|
|
||||||
{"7", args{1, "0.4-0.1"}, []string{"00.12.28", "00.12.29", "00.12.30", "00.12.31"}},
|
|
||||||
{"8", args{1, "0.2-1.2"}, []string{"00.12.30", "00.12.31", "01.1.1", "01.1.2"}},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
gotResult := holidays(tt.args.year, tt.args.daysRaw)
|
|
||||||
for idx, result := range gotResult{
|
|
||||||
if !reflect.DeepEqual(result.Format("06.1.2"), tt.wantResult[idx]) {
|
|
||||||
t.Errorf("holidays() = %v, want %v", result, tt.wantResult[idx])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
package data
|
|
||||||
|
|
||||||
import "main/parse/core"
|
|
||||||
|
|
||||||
type Reader interface {
|
|
||||||
Read() Input
|
|
||||||
}
|
|
||||||
|
|
||||||
type Parser interface {
|
|
||||||
Parse(Input) core.Holidays
|
|
||||||
}
|
|
||||||
|
|
||||||
type Formatter interface {
|
|
||||||
Format(core.Holidays) Output
|
|
||||||
}
|
|
||||||
|
|
||||||
type Writer interface {
|
|
||||||
Write(Output)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Input data
|
|
||||||
type Input []InputRaw
|
|
||||||
|
|
||||||
// InputRaw per year
|
|
||||||
type InputRaw struct {
|
|
||||||
Year int
|
|
||||||
Data []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Output struct {
|
|
||||||
Prefix string
|
|
||||||
Body []string
|
|
||||||
Suffix string
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
package output
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"hash/crc32"
|
|
||||||
"math/rand"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/samber/lo"
|
|
||||||
|
|
||||||
"main/parse/core"
|
|
||||||
"main/parse/data"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewFormatter(name string) data.Formatter {
|
|
||||||
return formatter{name}
|
|
||||||
}
|
|
||||||
|
|
||||||
type formatter struct {
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f formatter) Format(info core.Holidays) (result data.Output) {
|
|
||||||
result.Prefix = fmt.Sprintf(icsHead, f.name)
|
|
||||||
result.Suffix = icsTail
|
|
||||||
|
|
||||||
uuid.SetRand(rand.New(rand.NewSource(int64(crc32.ChecksumIEEE([]byte(f.name))))))
|
|
||||||
|
|
||||||
result.Body = lo.Map(info, func(day core.Holiday, i int) string {
|
|
||||||
return event{
|
|
||||||
id: uuid.NewString(),
|
|
||||||
group: day.Group,
|
|
||||||
title: getTitle(day),
|
|
||||||
date: day.Date,
|
|
||||||
desc: getDesc(day),
|
|
||||||
}.Ics()
|
|
||||||
})
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
package output
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"main/parse/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
icsHead = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//Rank Technology//Chinese Holidays//EN\nX-WR-CALNAME:%s"
|
|
||||||
icsEvent = "BEGIN:VEVENT\nUID:%s\nDTSTART;VALUE=DATE:%s\nSUMMARY:%s\nDESCRIPTION:%s\nEND:VEVENT"
|
|
||||||
icsTail = "END:VCALENDAR"
|
|
||||||
)
|
|
||||||
|
|
||||||
// event data
|
|
||||||
type event struct {
|
|
||||||
id string
|
|
||||||
group string
|
|
||||||
title string
|
|
||||||
date time.Time
|
|
||||||
desc string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d event) Ics() string {
|
|
||||||
return fmt.Sprintf(
|
|
||||||
icsEvent,
|
|
||||||
d.id,
|
|
||||||
d.date.Format("20060102"),
|
|
||||||
d.title,
|
|
||||||
d.desc,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getStatusName(status core.Status) string {
|
|
||||||
name := map[core.Status]string{
|
|
||||||
core.Rest: "假期",
|
|
||||||
core.Work: "补班",
|
|
||||||
}
|
|
||||||
return name[status]
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTitle(item core.Holiday) string {
|
|
||||||
return fmt.Sprintf("%s%s", item.Name, getStatusName(item.Type))
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDesc(item core.Holiday) string {
|
|
||||||
return fmt.Sprintf("%s 第%d天/共%d天", getStatusName(item.Type), item.Nth, item.Total)
|
|
||||||
}
|
|
@ -1,116 +0,0 @@
|
|||||||
package read
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/samber/lo"
|
|
||||||
|
|
||||||
"main/parse/data"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewReader(dir, file string) data.Reader {
|
|
||||||
return dataReader{Dir: "./" + dir + "/", File: file}
|
|
||||||
}
|
|
||||||
|
|
||||||
type dataReader struct {
|
|
||||||
Dir string
|
|
||||||
File string
|
|
||||||
}
|
|
||||||
|
|
||||||
type fileInfo struct {
|
|
||||||
Name string
|
|
||||||
Year int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dw dataReader) Read() (result data.Input) {
|
|
||||||
resultChan := make(chan data.InputRaw)
|
|
||||||
wg := new(sync.WaitGroup)
|
|
||||||
|
|
||||||
for _, f := range dw.fileList() {
|
|
||||||
wg.Add(1)
|
|
||||||
|
|
||||||
go func(file fileInfo) {
|
|
||||||
defer wg.Done()
|
|
||||||
raw, err := dw.load(file.Name)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error loading %s: %s\n", file.Name, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
res := data.InputRaw{
|
|
||||||
Year: file.Year,
|
|
||||||
Data: lines(raw),
|
|
||||||
}
|
|
||||||
if len(res.Data) == 0 {
|
|
||||||
log.Printf("No data in %s\n", file.Name)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resultChan <- res
|
|
||||||
}(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
wg.Wait()
|
|
||||||
close(resultChan)
|
|
||||||
}()
|
|
||||||
|
|
||||||
for content := range resultChan {
|
|
||||||
result = append(result, content)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dw dataReader) fileList() (result []fileInfo) {
|
|
||||||
files, err := os.ReadDir(dw.Dir)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return lo.FilterMap(files, func(file os.DirEntry, _ int) (fileInfo, bool) {
|
|
||||||
yr, e := year(file.Name(), dw.File)
|
|
||||||
isFile := e == nil && !file.IsDir()
|
|
||||||
return fileInfo{Name: file.Name(), Year: yr}, isFile
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dw dataReader) load(filename string) (result string, err error) {
|
|
||||||
content, err := os.ReadFile(dw.Dir + filename)
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
return string(content), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func year(filename, format string) (result int, err error) {
|
|
||||||
regex := regexp.MustCompile(format)
|
|
||||||
if !regex.MatchString(filename) {
|
|
||||||
return 0, fmt.Errorf("%s is not a valid filename", filename)
|
|
||||||
}
|
|
||||||
return strconv.Atoi(filename[:4])
|
|
||||||
}
|
|
||||||
|
|
||||||
func lines(data string) (result []string) {
|
|
||||||
var (
|
|
||||||
dateSingle = `(\d?\d.\d?\d)`
|
|
||||||
dateRange = fmt.Sprintf(`(%s-%s)`, dateSingle, dateSingle)
|
|
||||||
dateFormat = fmt.Sprintf(`(%s|%s)`, dateSingle, dateRange)
|
|
||||||
dateInputs = fmt.Sprintf(`(%s,)*%s`, dateFormat, dateFormat)
|
|
||||||
dateAccept = fmt.Sprintf(`(|%s)`, dateInputs)
|
|
||||||
dateRegex = regexp.MustCompile(fmt.Sprintf(`^[^;]+;%s;%s$`, dateAccept, dateAccept))
|
|
||||||
)
|
|
||||||
|
|
||||||
return lo.FilterMap(strings.Split(data, "\n"),
|
|
||||||
func(line string, _ int) (string, bool) {
|
|
||||||
line = strings.Split(line, "//")[0]
|
|
||||||
line = strings.TrimSpace(line)
|
|
||||||
return line, dateRegex.MatchString(line)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
package read
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_year(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
filename string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantResult int
|
|
||||||
wantErr error
|
|
||||||
}{
|
|
||||||
{"2018", args{"2018.txt"}, 2018, nil},
|
|
||||||
{"2019", args{"2019.avi"}, 0, fmt.Errorf("%s", "invalid year")},
|
|
||||||
{"2020", args{"zero.txt"}, 0, fmt.Errorf("%s", "invalid year")},
|
|
||||||
{"2021", args{"2021.txt"}, 2021, nil},
|
|
||||||
{"2022", args{"2022.txt"}, 2022, nil},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
gotResult, err := year(tt.args.filename, `^\d{4}\.txt$`)
|
|
||||||
if err == nil && err != tt.wantErr || err != nil && tt.wantErr == nil {
|
|
||||||
t.Errorf("year() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if gotResult != tt.wantResult {
|
|
||||||
t.Errorf("year() gotResult = %v, want %v", gotResult, tt.wantResult)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_lines(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
data string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantResult []string
|
|
||||||
}{
|
|
||||||
{"1", args{"// none"}, []string{}},
|
|
||||||
{"2", args{";1.1;2.2"}, []string{}},
|
|
||||||
{"3", args{"3;1.1;2.2"}, []string{"3;1.1;2.2"}},
|
|
||||||
{"4", args{"4;1.1;"}, []string{"4;1.1;"}},
|
|
||||||
{"5", args{"5;1.1;2.2,3.3"}, []string{"5;1.1;2.2,3.3"}},
|
|
||||||
{"6", args{"6;1.1,2.2;3.3,4.4"}, []string{"6;1.1,2.2;3.3,4.4"}},
|
|
||||||
{"7", args{"7;1.1,2.2;3.3,4.4-5.5"}, []string{"7;1.1,2.2;3.3,4.4-5.5"}},
|
|
||||||
{"8", args{"8;1.1;2.2;"}, []string{}},
|
|
||||||
{"9", args{"9;,1.1;2.2"}, []string{}},
|
|
||||||
{"10", args{"10;1.1"}, []string{}},
|
|
||||||
{"11", args{"11;1.1;2.2,"}, []string{}},
|
|
||||||
{"12", args{"// 13;1.1;2.2 "}, []string{}},
|
|
||||||
{"13", args{"13;1.1;2.2 // none"}, []string{"13;1.1;2.2"}},
|
|
||||||
{"14", args{"14;1.1;2.2 "}, []string{"14;1.1;2.2"}},
|
|
||||||
{"15", args{" 15;1.1;2.2"}, []string{"15;1.1;2.2"}},
|
|
||||||
{"16", args{" 16;1.1;2.2 "}, []string{"16;1.1;2.2"}},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if gotResult := lines(tt.args.data); !reflect.DeepEqual(gotResult, tt.wantResult) {
|
|
||||||
t.Errorf("lines() = %v, want %v", gotResult, tt.wantResult)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
package write
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"main/parse/data"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewWriter(dir, file string) data.Writer {
|
|
||||||
return dataWriter{File: "./" + dir + "/" + file}
|
|
||||||
}
|
|
||||||
|
|
||||||
type dataWriter struct {
|
|
||||||
File string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dw dataWriter) Write(data data.Output) {
|
|
||||||
output := strings.Join(
|
|
||||||
[]string{
|
|
||||||
data.Prefix,
|
|
||||||
strings.Join(data.Body, "\n\n"),
|
|
||||||
data.Suffix,
|
|
||||||
},
|
|
||||||
"\n\n\n",
|
|
||||||
)
|
|
||||||
|
|
||||||
f, err := os.Create(dw.File)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() { _ = f.Close() }()
|
|
||||||
|
|
||||||
n, err := f.WriteString(output)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("write", n, "bytes to", dw.File)
|
|
||||||
}
|
|
@ -24,7 +24,7 @@ description: Calendar of Public Holidays in China
|
|||||||
-- PVP summary: +-+------- breaking API changes
|
-- PVP summary: +-+------- breaking API changes
|
||||||
-- | | +----- non-breaking API additions
|
-- | | +----- non-breaking API additions
|
||||||
-- | | | +--- code changes with no API change
|
-- | | | +--- code changes with no API change
|
||||||
version: 0.1.1.0
|
version: 0.1.2.0
|
||||||
|
|
||||||
-- A short (one-line) description of the package.
|
-- A short (one-line) description of the package.
|
||||||
-- synopsis:
|
-- synopsis:
|
||||||
|
@ -16,12 +16,12 @@ parseByFile :: (FilePath, String) -> (String, [Date], [Date])
|
|||||||
parseByFile (file, content) = (year, rest, work)
|
parseByFile (file, content) = (year, rest, work)
|
||||||
where
|
where
|
||||||
year = takeBaseName file
|
year = takeBaseName file
|
||||||
rest = parse year content Rest
|
rest = parse content Rest
|
||||||
work = parse year content Work
|
work = parse content Work
|
||||||
|
|
||||||
-- Convert data to Date
|
-- Convert data to Date
|
||||||
parse :: String -> String -> DateType -> [Date]
|
parse :: String -> DateType -> [Date]
|
||||||
parse year content flag = concatMap constructor $ zip (map head raw) dates
|
parse content flag = concatMap constructor $ zip (map head raw) dates
|
||||||
where
|
where
|
||||||
constructor (name, dates) = constructDate name flag <$> dates
|
constructor (name, dates) = constructDate name flag <$> dates
|
||||||
dates = parseDate <$> map (!! indexDateType flag) raw
|
dates = parseDate <$> map (!! indexDateType flag) raw
|
||||||
|
Loading…
Reference in New Issue
Block a user