mirror of
https://github.com/Theodor-Springmann-Stiftung/lenz-web.git
synced 2025-10-28 16:55:32 +00:00
Index functions
This commit is contained in:
@@ -8,6 +8,9 @@ import (
|
|||||||
const DEFAULT_YEAR = 1765
|
const DEFAULT_YEAR = 1765
|
||||||
|
|
||||||
func GetIndex(fiber *fiber.Ctx) error {
|
func GetIndex(fiber *fiber.Ctx) error {
|
||||||
_ = xmlmodels.Get()
|
lib := xmlmodels.Get()
|
||||||
return fiber.Render("/", nil)
|
// Years
|
||||||
|
years, yearmap := lib.Years()
|
||||||
|
|
||||||
|
return fiber.Render("/", map[string]any{"years": years, "yearmap": yearmap})
|
||||||
}
|
}
|
||||||
|
|||||||
4
lenz.go
4
lenz.go
@@ -43,12 +43,14 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// INFO: the lib, engine and storage objects passed to the server should never be recreated.
|
// INFO: the lib, engine and storage objects passed to the server should never be recreated.
|
||||||
err = xmlmodels.New(dir, commit.Hash)
|
lib, err := xmlmodels.Parse(dir, commit.Hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
engine := templating.New(&views.LayoutFS, &views.RoutesFS)
|
engine := templating.New(&views.LayoutFS, &views.RoutesFS)
|
||||||
|
engine.AddFuncs(lib.FuncMap())
|
||||||
|
|
||||||
storage := memory.New(memory.Config{
|
storage := memory.New(memory.Config{
|
||||||
GCInterval: 24 * time.Hour,
|
GCInterval: 24 * time.Hour,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/Theodor-Springmann-Stiftung/lenz-web/helpers/functions"
|
"github.com/Theodor-Springmann-Stiftung/lenz-web/helpers/functions"
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"golang.org/x/net/websocket"
|
"golang.org/x/net/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -180,9 +179,17 @@ func (e *Engine) AddFunc(name string, fn any) {
|
|||||||
e.FuncMap[name] = fn
|
e.FuncMap[name] = fn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Engine) AddFuncs(funcs template.FuncMap) {
|
||||||
|
e.mu.Lock()
|
||||||
|
defer e.mu.Unlock()
|
||||||
|
for k, v := range funcs {
|
||||||
|
e.FuncMap[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Engine) Render(out io.Writer, path string, data any, layout ...string) error {
|
func (e *Engine) Render(out io.Writer, path string, data any, layout ...string) error {
|
||||||
e.mu.RLock()
|
e.mu.RLock()
|
||||||
ld := data.(fiber.Map)
|
ld := data.(map[string]any)
|
||||||
if e.GlobalData != nil {
|
if e.GlobalData != nil {
|
||||||
maps.Copy(ld, e.GlobalData)
|
maps.Copy(ld, e.GlobalData)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,15 @@
|
|||||||
Hello from body! What!
|
{{ $model := . }}
|
||||||
|
|
||||||
|
{{- if .years -}}
|
||||||
|
{{- range $y := .years -}}
|
||||||
|
<div>
|
||||||
|
{{- printf "%d " $y -}}
|
||||||
|
{{- $letters := index $model.yearmap $y -}}
|
||||||
|
{{- len $letters }}
|
||||||
|
{{ range $l := $letters -}}
|
||||||
|
<div>{{ $l.Letter }}</div>
|
||||||
|
<div>{{ $l.Earliest.Text -}}</div>
|
||||||
|
{{- end -}}
|
||||||
|
</div>
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
|||||||
@@ -89,13 +89,13 @@ func (p *XMLParser[T]) Serialize(dataholder XMLRootElement[T], path string, late
|
|||||||
// It deletes all items that have not been parsed in the last commit,
|
// It deletes all items that have not been parsed in the last commit,
|
||||||
// and whose filepath has not been marked as failed.
|
// and whose filepath has not been marked as failed.
|
||||||
func (p *XMLParser[T]) Cleanup(latest ParseMeta) {
|
func (p *XMLParser[T]) Cleanup(latest ParseMeta) {
|
||||||
todelete := make([]string, 0)
|
todelete := make([]any, 0)
|
||||||
toappend := make([]*T, 0)
|
toappend := make([]*T, 0)
|
||||||
p.Infos.Range(func(key, value interface{}) bool {
|
p.Infos.Range(func(key, value interface{}) bool {
|
||||||
info := value.(ItemInfo)
|
info := value.(ItemInfo)
|
||||||
if !info.Parse.Equals(latest) {
|
if !info.Parse.Equals(latest) {
|
||||||
if !latest.Failed(info.Source) {
|
if !latest.Failed(info.Source) {
|
||||||
todelete = append(todelete, key.(string))
|
todelete = append(todelete, key)
|
||||||
} else {
|
} else {
|
||||||
item, ok := p.Items.Load(key)
|
item, ok := p.Items.Load(key)
|
||||||
if ok {
|
if ok {
|
||||||
@@ -165,7 +165,7 @@ func (p *XMLParser[T]) Info(id string) ItemInfo {
|
|||||||
return info.(ItemInfo)
|
return info.(ItemInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *XMLParser[T]) Item(id string) *T {
|
func (p *XMLParser[T]) Item(id any) *T {
|
||||||
item, ok := p.Items.Load(id)
|
item, ok := p.Items.Load(id)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -231,17 +231,17 @@ func (xsdd XSDDate) Type() XSDDatetype {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (xsdd *XSDDate) Validate() bool {
|
func (xsdd *XSDDate) Validate() bool {
|
||||||
if xsdd.error {
|
if xsdd.error || len(xsdd.base) == 0 {
|
||||||
xsdd.state = Invalid
|
xsdd.state = Invalid
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
xsdd.state = xsdd.inferState()
|
xsdd.state = xsdd.inferState()
|
||||||
if xsdd.state != Invalid {
|
if xsdd.state == Invalid {
|
||||||
return true
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (xsdd *XSDDate) parseError(s string) error {
|
func (xsdd *XSDDate) parseError(s string) error {
|
||||||
@@ -309,6 +309,56 @@ func (xsdd XSDDate) inferState() XSDDatetype {
|
|||||||
return Invalid
|
return Invalid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (xsdd XSDDate) Before(other XSDDate) bool {
|
||||||
|
if xsdd.Year < other.Year {
|
||||||
|
return true
|
||||||
|
} else if xsdd.Year > other.Year {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if xsdd.Month < other.Month {
|
||||||
|
return true
|
||||||
|
} else if xsdd.Month > other.Month {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if xsdd.Day < other.Day {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xsddate *XSDDate) Compare(other *XSDDate) int {
|
||||||
|
if !xsddate.Validate() {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if !other.Validate() {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if xsddate.Year < other.Year {
|
||||||
|
return -1
|
||||||
|
} else if xsddate.Year > other.Year {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if xsddate.Month < other.Month {
|
||||||
|
return -1
|
||||||
|
} else if xsddate.Month > other.Month {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if xsddate.Day < other.Day {
|
||||||
|
return -1
|
||||||
|
} else if xsddate.Day > other.Day {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
func validDay(i int) bool {
|
func validDay(i int) bool {
|
||||||
if i < 1 || i > 31 {
|
if i < 1 || i > 31 {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -17,3 +17,23 @@ type Date struct {
|
|||||||
Cert string `xml:"cert,attr"`
|
Cert string `xml:"cert,attr"`
|
||||||
Text string `xml:",chardata"`
|
Text string `xml:",chardata"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Date) Sort() *xmlparsing.XSDDate {
|
||||||
|
if d.NotBefore.Validate() {
|
||||||
|
return &d.NotBefore
|
||||||
|
}
|
||||||
|
if d.From.Validate() {
|
||||||
|
return &d.From
|
||||||
|
}
|
||||||
|
if d.When.Validate() {
|
||||||
|
return &d.When
|
||||||
|
}
|
||||||
|
if d.To.Validate() {
|
||||||
|
return &d.To
|
||||||
|
}
|
||||||
|
if d.NotAfter.Validate() {
|
||||||
|
return &d.NotAfter
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ package xmlmodels
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"html/template"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"maps"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -30,6 +33,8 @@ type Library struct {
|
|||||||
Letters *xmlparsing.XMLParser[Letter]
|
Letters *xmlparsing.XMLParser[Letter]
|
||||||
Traditions *xmlparsing.XMLParser[Tradition]
|
Traditions *xmlparsing.XMLParser[Tradition]
|
||||||
Metas *xmlparsing.XMLParser[Meta]
|
Metas *xmlparsing.XMLParser[Meta]
|
||||||
|
|
||||||
|
cache sync.Map
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Library) String() string {
|
func (l *Library) String() string {
|
||||||
@@ -89,6 +94,7 @@ func (l *Library) Parse(source xmlparsing.ParseSource, baseDir, commit string) e
|
|||||||
// INFO: this lock prevents multiple parses from happening at the same time.
|
// INFO: this lock prevents multiple parses from happening at the same time.
|
||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
defer l.mu.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
l.cache.Clear()
|
||||||
|
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
meta := xmlparsing.ParseMeta{
|
meta := xmlparsing.ParseMeta{
|
||||||
@@ -233,3 +239,73 @@ func (l *Library) cleanup(meta xmlparsing.ParseMeta) {
|
|||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Library) Years() ([]int, map[int][]Meta) {
|
||||||
|
if years, ok := l.cache.Load("years"); ok {
|
||||||
|
if yearmap, ok := l.cache.Load("yearmap"); ok {
|
||||||
|
return years.([]int), yearmap.(map[int][]Meta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mapYears := make(map[int][]Meta)
|
||||||
|
for item := range l.Metas.Iterate() {
|
||||||
|
earliest := item.Earliest()
|
||||||
|
if earliest != nil {
|
||||||
|
mapYears[earliest.Sort().Year] = append(mapYears[earliest.Sort().Year], item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := slices.Collect(maps.Keys(mapYears))
|
||||||
|
slices.Sort(ret)
|
||||||
|
|
||||||
|
for _, items := range mapYears {
|
||||||
|
slices.SortFunc(items, func(a, b Meta) int {
|
||||||
|
return a.Earliest().Sort().Compare(b.Earliest().Sort())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
l.cache.Store("years", ret)
|
||||||
|
l.cache.Store("yearmap", mapYears)
|
||||||
|
return ret, mapYears
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Library) LettersForYear(year int) (ret []Meta) {
|
||||||
|
for l := range l.Metas.Filter(func(item Meta) bool {
|
||||||
|
return item.Earliest().Sort().Year == year
|
||||||
|
}) {
|
||||||
|
ret = append(ret, l)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Library) Person(id int) (ret *PersonDef) {
|
||||||
|
ret = l.Persons.Item(id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Library) Place(id int) (ret *LocationDef) {
|
||||||
|
ret = l.Places.Item(id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Library) GetPersons(id []int) (ret []*PersonDef) {
|
||||||
|
for _, i := range id {
|
||||||
|
ret = append(ret, l.Person(i))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Library) GetPlaces(id []int) (ret []*LocationDef) {
|
||||||
|
for _, i := range id {
|
||||||
|
ret = append(ret, l.Place(i))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Library) FuncMap() template.FuncMap {
|
||||||
|
return template.FuncMap{
|
||||||
|
"Person": l.Person,
|
||||||
|
"Place": l.Place,
|
||||||
|
"Persons": l.GetPersons,
|
||||||
|
"Places": l.GetPlaces,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package xmlmodels
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
"iter"
|
||||||
|
|
||||||
xmlparsing "github.com/Theodor-Springmann-Stiftung/lenz-web/xml"
|
xmlparsing "github.com/Theodor-Springmann-Stiftung/lenz-web/xml"
|
||||||
)
|
)
|
||||||
@@ -17,6 +18,28 @@ type Meta struct {
|
|||||||
Recieved []Action `xml:"recieved"`
|
Recieved []Action `xml:"recieved"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Meta) Earliest() *Date {
|
||||||
|
var earliest *Date
|
||||||
|
|
||||||
|
for _, action := range m.Sent {
|
||||||
|
if earliest == nil || action.Earliest().Sort().Before(*earliest.Sort()) {
|
||||||
|
earliest = action.Earliest()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if earliest != nil {
|
||||||
|
return earliest
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, action := range m.Recieved {
|
||||||
|
if earliest == nil || action.Earliest().Sort().Before(*earliest.Sort()) {
|
||||||
|
earliest = action.Earliest()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return earliest
|
||||||
|
}
|
||||||
|
|
||||||
func (m Meta) Keys() []any {
|
func (m Meta) Keys() []any {
|
||||||
return []any{m.Letter}
|
return []any{m.Letter}
|
||||||
}
|
}
|
||||||
@@ -33,8 +56,30 @@ func (m Meta) String() string {
|
|||||||
return string(json)
|
return string(json)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m Meta) SendRecieved() iter.Seq2[*Action, *Action] {
|
||||||
|
return func(yield func(*Action, *Action) bool) {
|
||||||
|
for i, sent := range m.Sent {
|
||||||
|
var rec *Action
|
||||||
|
if i < len(m.Recieved) {
|
||||||
|
rec = &m.Recieved[i]
|
||||||
|
}
|
||||||
|
if !yield(&sent, rec) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Action struct {
|
type Action struct {
|
||||||
Dates []Date `xml:"date,attr"`
|
Dates []Date `xml:"date"`
|
||||||
Places []RefElement `xml:"place"`
|
Places []RefElement `xml:"place"`
|
||||||
Persons []RefElement `xml:"person"`
|
Persons []RefElement `xml:"person"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Action) Earliest() *Date {
|
||||||
|
if len(a.Dates) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &a.Dates[0]
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,14 +27,14 @@ func Get() *Library {
|
|||||||
return lib
|
return lib
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(dir, hash string) error {
|
func Parse(dir, hash string) (*Library, error) {
|
||||||
Set(NewLibrary())
|
if lib == nil {
|
||||||
return Parse(dir, hash)
|
Set(NewLibrary())
|
||||||
}
|
|
||||||
|
|
||||||
func Parse(dir, hash string) error {
|
|
||||||
if hash == "" {
|
|
||||||
return lib.Parse(xmlparsing.Path, dir, hash)
|
|
||||||
}
|
}
|
||||||
return lib.Parse(xmlparsing.Commit, dir, hash)
|
|
||||||
|
if hash == "" {
|
||||||
|
return Get(), lib.Parse(xmlparsing.Path, dir, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Get(), lib.Parse(xmlparsing.Commit, dir, hash)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user