delete Go files

This commit is contained in:
Muhan Li 2023-11-06 13:05:10 +08:00
parent 8ea57de154
commit 869a9de697
20 changed files with 14 additions and 3694 deletions

View File

@ -32,16 +32,19 @@ jobs:
shell: bash
run: python crawler.py
- name: Setup Go environment
uses: actions/setup-go@v4
- name: Setup Haskell and Cabal
uses: haskell-actions/setup@v2
with:
go-version-file: go.mod
ghc-version: 9.2.8
cabal-version: 3.10.1.0
- name: Go Build
run: go build -o . main.go
- name: Configure and build
run: |
cabal configure --enable-tests --enable-benchmarks --disable-documentation
cabal build all
- name: Generate ICS files
run: ./main
run: cabal run
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v4

View File

@ -97,19 +97,6 @@ jobs:
path: parser/
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:
name: Pylint
runs-on: ubuntu-latest

File diff suppressed because it is too large Load Diff

10
go.mod
View File

@ -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
View File

@ -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
View File

@ -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()
}

View File

@ -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()
}

View File

@ -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)
}

View File

@ -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] }

View File

@ -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
}

View File

@ -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
}

View File

@ -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])
}
}
})
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
},
)
}

View File

@ -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)
}
})
}
}

View File

@ -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)
}

View File

@ -24,7 +24,7 @@ description: Calendar of Public Holidays in China
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.1.1.0
version: 0.1.2.0
-- A short (one-line) description of the package.
-- synopsis:

View File

@ -16,12 +16,12 @@ parseByFile :: (FilePath, String) -> (String, [Date], [Date])
parseByFile (file, content) = (year, rest, work)
where
year = takeBaseName file
rest = parse year content Rest
work = parse year content Work
rest = parse content Rest
work = parse content Work
-- Convert data to Date
parse :: String -> String -> DateType -> [Date]
parse year content flag = concatMap constructor $ zip (map head raw) dates
parse :: String -> DateType -> [Date]
parse content flag = concatMap constructor $ zip (map head raw) dates
where
constructor (name, dates) = constructDate name flag <$> dates
dates = parseDate <$> map (!! indexDateType flag) raw