diff --git a/.air.toml b/.air.toml new file mode 100644 index 0000000..3562e34 --- /dev/null +++ b/.air.toml @@ -0,0 +1,51 @@ +root = "." +testdata_dir = "testdata" +tmp_dir = "tmp" + +[build] + args_bin = [] + bin = "./tmp/main" + cmd = "go build -tags=\"dev\" -o ./tmp/main ." + delay = 1000 + exclude_dir = ["assets", "views", "tmp", "vendor", "testdata"] + exclude_file = [] + exclude_regex = ["_test.go"] + exclude_unchanged = false + follow_symlink = false + full_bin = "" + include_dir = [] + include_ext = ["go", "tpl", "tmpl", "html"] + include_file = [] + kill_delay = "0s" + log = "build-errors.log" + poll = false + poll_interval = 0 + post_cmd = [] + pre_cmd = [] + rerun = false + rerun_delay = 500 + send_interrupt = false + stop_on_error = false + +[color] + app = "" + build = "yellow" + main = "magenta" + runner = "green" + watcher = "cyan" + +[log] + main_only = false + time = false + +[misc] + clean_on_exit = false + +[proxy] + app_port = 0 + enabled = false + proxy_port = 0 + +[screen] + clear_on_rebuild = false + keep_scroll = true diff --git a/controllers/controller.go b/controllers/controller.go index a6b366f..07ebdc1 100644 --- a/controllers/controller.go +++ b/controllers/controller.go @@ -1,4 +1,4 @@ -package controller +package controllers import ( "net/http" diff --git a/controllers/years.go b/controllers/years.go new file mode 100644 index 0000000..21e6872 --- /dev/null +++ b/controllers/years.go @@ -0,0 +1,21 @@ +package controllers + +import ( + "github.com/Theodor-Springmann-Stiftung/kgpz_web/app" + "github.com/gofiber/fiber/v2" +) + +func GetYear(kgpz *app.KGPZ) fiber.Handler { + return func(c *fiber.Ctx) error { + y := c.Params("year", "1764") + if len(y) != 4 { + return c.SendStatus(fiber.StatusBadRequest) + } + + issues := kgpz.Library.Issues.GetYear(y) + if len(issues.Issues) == 0 { + return c.SendStatus(fiber.StatusNotFound) + } + return c.Render("/", fiber.Map{"model": issues}) + } +} diff --git a/helpers/watcher.go b/helpers/watcher.go index 3c8cb6a..c2adb25 100644 --- a/helpers/watcher.go +++ b/helpers/watcher.go @@ -31,7 +31,6 @@ type FileWatcher struct { } func NewFileWatcher() (*FileWatcher, error) { - fw := &FileWatcher{mu: sync.Mutex{}} fw.Watch() return fw, nil @@ -96,8 +95,8 @@ func (fw *FileWatcher) Watch() error { if !ok { return } - log.Println("event:", event) if !event.Has(fsnotify.Chmod) { + log.Println("event:", event) fw.mu.Lock() for _, wf := range fw.wf { wf(event.Name) diff --git a/kgpz_web.go b/kgpz_web.go index 65ba7b7..54d7204 100644 --- a/kgpz_web.go +++ b/kgpz_web.go @@ -37,7 +37,7 @@ func main() { kgpz := app.NewKGPZ(cfg) kgpz.Init() - server := server.Start(kgpz, cfg) + server := server.Create(kgpz, cfg) Start(kgpz, server) } diff --git a/models/notes.md b/models/notes.md deleted file mode 100644 index 5516282..0000000 --- a/models/notes.md +++ /dev/null @@ -1,5 +0,0 @@ -# Notes on xml reading - -The models expect certain files and a certain file XML structure and throw quite often if the structure is not as expected. -This is by design, as the models are supposed to be used in a controlled environment. Changes in the XML schema should be reflected in the models. -All collections in the models should be kept thread-safe. diff --git a/providers/issues.go b/providers/issues.go index a73bb5c..53c0778 100644 --- a/providers/issues.go +++ b/providers/issues.go @@ -11,7 +11,31 @@ type IssueProvider struct { type Issues struct { XMLName xml.Name `xml:"stuecke"` - Issue []Issue `xml:"stueck"` + Issues []Issue `xml:"stueck"` +} + +func (i *IssueProvider) GetYear(year string) YearViewModel { + res := YearViewModel{Year: year} + last := "" + for _, issue := range i.Items.Issues { + if len(issue.Datum.When) < 4 { + continue + } + + date := issue.Datum.When[0:4] + if date != last { + res.PushAvailable(date) + last = date + } + + if date == year { + res.PushIssue(issue) + } + } + + res.SortAvailableYears() + + return res } type Issue struct { @@ -39,13 +63,13 @@ type Additional struct { } func (i Issues) Append(data Issues) Issues { - i.Issue = append(i.Issue, data.Issue...) + i.Issues = append(i.Issues, data.Issues...) return i } func (i Issues) String() string { var res []string - for _, issue := range i.Issue { + for _, issue := range i.Issues { res = append(res, issue.String()) } diff --git a/providers/xmlcommon.go b/providers/xmlcommon.go index 59f5824..728dc22 100644 --- a/providers/xmlcommon.go +++ b/providers/xmlcommon.go @@ -81,5 +81,5 @@ type Reference struct { } type Value struct { - Value string `xml:",chardata"` + Chardata string `xml:",chardata"` } diff --git a/providers/year.go b/providers/year.go new file mode 100644 index 0000000..6015276 --- /dev/null +++ b/providers/year.go @@ -0,0 +1,84 @@ +package providers + +import ( + "slices" + "strconv" + "time" +) + +const TLAYOUT = "2006-01-02" + +var TRANSLM = map[string][]string{ + "January": {"Januar", "Jan"}, + "February": {"Februar", "Feb"}, + "March": {"März", "Mär"}, + "April": {"April", "Apr"}, + "May": {"Mai", "Mai"}, + "June": {"Juni", "Jun"}, + "July": {"Juli", "Jul"}, + "August": {"August", "Aug"}, + "September": {"September", "Sep"}, + "October": {"Oktober", "Okt"}, + "November": {"November", "Nov"}, + "December": {"Dezember", "Dez"}, +} + +var TRANSLD = map[string][]string{ + "Monday": {"Montag", "Mo"}, + "Tuesday": {"Dienstag", "Di"}, + "Wednesday": {"Mittwoch", "Mi"}, + "Thursday": {"Donnerstag", "Do"}, + "Friday": {"Freitag", "Fr"}, + "Saturday": {"Samstag", "Sa"}, + "Sunday": {"Sonntag", "So"}, +} + +type IssueViewModel struct { + Issue + Weekday []string + Day int + Month []string +} + +func NewIssueView(i Issue) (IssueViewModel, error) { + t, err := time.Parse(TLAYOUT, i.Datum.When) + if err != nil { + return IssueViewModel{}, err + } + + return IssueViewModel{ + Issue: i, + Weekday: append(TRANSLD[t.Weekday().String()], t.Weekday().String()), + Day: t.Day(), + Month: append(TRANSLM[t.Month().String()], i.Datum.When[5:7]), + }, nil +} + +type YearViewModel struct { + Year string + AvailableYears []int + Issues []IssueViewModel +} + +func (y *YearViewModel) PushIssue(i Issue) { + iv, err := NewIssueView(i) + if err != nil { + return + } + y.Issues = append(y.Issues, iv) +} + +func (y *YearViewModel) PushAvailable(s string) { + i, err := strconv.Atoi(s) + if err != nil { + return + } + + if !slices.Contains(y.AvailableYears, i) { + y.AvailableYears = append(y.AvailableYears, i) + } +} + +func (y *YearViewModel) SortAvailableYears() { + slices.Sort(y.AvailableYears) +} diff --git a/server/server.go b/server/server.go index d2b16d5..a440d41 100644 --- a/server/server.go +++ b/server/server.go @@ -7,6 +7,7 @@ import ( "time" "github.com/Theodor-Springmann-Stiftung/kgpz_web/app" + "github.com/Theodor-Springmann-Stiftung/kgpz_web/controllers" "github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers" "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers" "github.com/Theodor-Springmann-Stiftung/kgpz_web/templating" @@ -40,27 +41,45 @@ const ( // - we invalidate all caches if data is valid // - we reload all clients // - if data validity catastrophically fails, we restart the router to map error pages. -// TODO: Use fiber type Server struct { Config *providers.ConfigProvider running chan bool shutdown *sync.WaitGroup cache *memory.Storage + mu sync.Mutex watcher *helpers.FileWatcher + + kgpz *app.KGPZ } -func Start(k *app.KGPZ, c *providers.ConfigProvider) *Server { +func Create(k *app.KGPZ, c *providers.ConfigProvider) *Server { + if c == nil || k == nil { + log.Println("Error creating server") + return nil + } + return &Server{ Config: c, + kgpz: k, } } +// INFO: hot reloading for poor people +// BUG: unable to close the old file watcher here, since the process gets aborted in the middle of creating the new one. func (s *Server) Watcher() error { + s.mu.Lock() + defer s.mu.Unlock() + watcher, err := helpers.NewFileWatcher() + if err != nil { + return err + } + s.watcher = watcher s.watcher.Append(func(path string) { log.Println("Restarting server") + time.Sleep(200 * time.Millisecond) s.Restart() }) @@ -111,6 +130,7 @@ func (s *Server) Start() { srv.Use(recover.New()) // TODO: Dont cache static assets, bc storage gets huge + // INFO: Maybe fiber does this already? if s.Config.Debug { srv.Use(cache.New(cache.Config{ Next: func(c *fiber.Ctx) bool { @@ -133,16 +153,15 @@ func (s *Server) Start() { srv.Use(STATIC_PREFIX, static(&views.StaticFS)) - srv.Get("/", func(c *fiber.Ctx) error { - return c.Render("/", fiber.Map{}) - }) + srv.Get("/:year?", controllers.GetYear(s.kgpz)) s.runner(srv) if s.Config.Debug { err := s.Watcher() if err != nil { - fmt.Println(err) + log.Println("Error watching files") + log.Println(err) } } } @@ -194,8 +213,6 @@ func (s *Server) runner(srv *fiber.App) { defer cleanup.Done() clean := <-s.running - srv.Server().CloseOnShutdown = true - if clean { if err := srv.ShutdownWithTimeout(SERVER_TIMEOUT); err != nil { fmt.Println(err) diff --git a/techstack.md b/techstack.md index 37b2b38..508436b 100644 --- a/techstack.md +++ b/techstack.md @@ -73,9 +73,6 @@ - Was ist "Link auf seite teilen"? -> Permalink ## Überarbeiten -- Undifferenzierte Angabe von "Akteur" von Werken in der Kurzangabe, Differenzieren -- Jahr -- Kurztitel, Titel - Sekundärüberlieferung in Stück/Beitrag - Fehlende Daten in Stück/Beitrag - Kurzinformation Überlieferung diff --git a/tmp/build-errors.log b/tmp/build-errors.log new file mode 100644 index 0000000..67fc100 --- /dev/null +++ b/tmp/build-errors.log @@ -0,0 +1 @@ +exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1 \ No newline at end of file diff --git a/tmp/main b/tmp/main new file mode 100755 index 0000000..df291b8 Binary files /dev/null and b/tmp/main differ diff --git a/views/routes/body.tmpl b/views/routes/body.tmpl index 2d49a94..c15210b 100644 --- a/views/routes/body.tmpl +++ b/views/routes/body.tmpl @@ -1,3 +1,19 @@ {{ define "body" }} -

Change! Hello from body

+ {{ range $year := .model.AvailableYears }} + {{ $year }} + {{ end }} + {{ range $issue := .model.Issues }} +
+
+ {{ $issue.Number.Chardata }} +
+
+ {{ index $issue.Month 1 }} +
+
+ {{ index $issue.Weekday 1 }} +
+
{{ $issue.Day }}.{{ index $issue.Month 2 }}.
+
+ {{ end }} {{ end }}