Akteure beginning

This commit is contained in:
Simon Martens
2024-12-26 01:26:51 +01:00
parent 4de0eab443
commit 86152bd46d
47 changed files with 61845 additions and 23644 deletions

View File

@@ -8,15 +8,15 @@ full_bin = "export KGPZ_WATCH=false; ./tmp/main"
cmd = "go build -tags=\"dev\" -o ./tmp/main ."
delay = 400
exclude_dir = [
"views/public",
"views/node_modules",
"views/transform",
"tmp",
"vendor",
"testdata",
"data_git",
"cache_gnd",
"cache_geonames",
"views/public",
"views/node_modules",
"views/transform",
"tmp",
"vendor",
"testdata",
"data_git",
"cache_gnd",
"cache_geonames",
]
exclude_file = []
exclude_regex = ["_test.go"]
@@ -30,7 +30,7 @@ log = "build-errors.log"
poll = false
poll_interval = 0
post_cmd = []
pre_cmd = ["npm --prefix views/ run tailwind"]
pre_cmd = ["npm --prefix views/ run build"]
rerun = false
rerun_delay = 250
send_interrupt = true

View File

@@ -22,6 +22,9 @@ func GetAgents(kgpz *app.KGPZ) fiber.Handler {
logging.Error(nil, "No agents found for letter or id: "+a)
return c.SendStatus(fiber.StatusNotFound)
}
return c.Render("/akteure/", fiber.Map{"model": fiber.Map{"agents": agents, "search": a}, "title": "Akteure"})
return c.Render(
"/akteure/",
fiber.Map{"model": agents},
)
}
}

View File

@@ -1,5 +1,11 @@
package functions
import (
"strconv"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/xsdtime"
)
type Month struct {
Full string
Short string
@@ -48,6 +54,28 @@ var TRANSLD = []Weekday{
{"Sonntag", "So", 7},
}
func HRDateShort(date string) string {
xsdt, err := xsdtime.New(date)
if err != nil {
return ""
}
t := xsdt.Type()
if t == xsdtime.GYear {
return strconv.Itoa(xsdt.Year)
}
if t == xsdtime.GYearMonth {
return strconv.Itoa(xsdt.Month) + "." + strconv.Itoa(xsdt.Year)
}
if t == xsdtime.Date {
return strconv.Itoa(xsdt.Day) + "." + strconv.Itoa(xsdt.Month) + "." + strconv.Itoa(xsdt.Year)
}
return ""
}
func MonthName(i int) Month {
if i > 12 || i < 1 {
return TRANSLM[0]

1
functions/slices.go Normal file
View File

@@ -0,0 +1 @@
package functions

8
functions/string.go Normal file
View File

@@ -0,0 +1,8 @@
package functions
func FirstLetter(s string) string {
if len(s) == 0 {
return ""
}
return string(s[:1])
}

View File

@@ -1 +0,0 @@
package providers

View File

@@ -1 +0,0 @@
package providers

View File

@@ -4,6 +4,7 @@ import (
"encoding/xml"
"fmt"
"strconv"
"strings"
"github.com/google/uuid"
)
@@ -60,6 +61,15 @@ func (p Piece) ReferencesIssue(y, no int) (*IssueRef, bool) {
return nil, false
}
func (p Piece) ReferencesAgent(a string) (*AgentRef, bool) {
for _, i := range p.AgentRefs {
if strings.HasPrefix(i.Ref, a) {
return &i, true
}
}
return nil, false
}
// TODO: We can make this fast depending on which category to look for
// but we'll have to define rules for every single category (~35 of them)
func (p Piece) IsCat(k string) bool {

View File

@@ -3,6 +3,7 @@ package xmlprovider
import (
"encoding/xml"
"fmt"
"strings"
)
type Work struct {
@@ -10,11 +11,20 @@ type Work struct {
URLs []URL `xml:"url"`
Citation Citation `xml:"zitation"`
PreferredTitle string `xml:"preferred"`
Akteur []AgentRef `xml:"akteur"`
AgentRefs []AgentRef `xml:"akteur"`
Identifier
AnnotationNote
}
func (p Work) ReferencesAgent(a string) (*AgentRef, bool) {
for _, i := range p.AgentRefs {
if strings.HasPrefix(i.Ref, a) {
return &i, true
}
}
return nil, false
}
type Citation struct {
XMLName xml.Name `xml:"zitation"`
Title string `xml:"title"`
@@ -24,5 +34,5 @@ type Citation struct {
}
func (w Work) String() string {
return fmt.Sprintf("URLs: %v, Citation: %v, PreferredTitle: %s, Akteur: %v, Identifier: %v, AnnotationNote: %v\n", w.URLs, w.Citation, w.PreferredTitle, w.Akteur, w.Identifier, w.AnnotationNote)
return fmt.Sprintf("URLs: %v, Citation: %v, PreferredTitle: %s, Akteur: %v, Identifier: %v, AnnotationNote: %v\n", w.URLs, w.Citation, w.PreferredTitle, w.AgentRefs, w.Identifier, w.AnnotationNote)
}

View File

@@ -142,25 +142,15 @@ func (p *XMLProvider[T]) Item(id string) *T {
return i
}
func (p *XMLProvider[T]) Find(fn func(*T) bool) []*T {
var items []*T
p.Items.Range(func(key, value interface{}) bool {
if fn(value.(*T)) {
items = append(items, value.(*T))
func (p *XMLProvider[T]) Find(fn func(*T) bool) []T {
p.mu.Lock()
defer p.mu.Unlock()
var items []T
for _, item := range p.Array {
if fn(&item) {
items = append(items, item)
}
return true
})
return items
}
func (p *XMLProvider[T]) FindKey(fn func(string) bool) []*T {
var items []*T
p.Items.Range(func(key, value interface{}) bool {
if fn(key.(string)) {
items = append(items, value.(*T))
}
return true
})
}
return items
}

View File

@@ -4,6 +4,7 @@ import (
"html/template"
"io"
"io/fs"
"strings"
"sync"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/app"
@@ -38,16 +39,23 @@ func (e *Engine) Funcs(app *app.KGPZ) error {
e.FuncMap = make(map[string]interface{})
e.mu.Unlock()
// Dates
e.AddFunc("MonthName", functions.MonthName)
e.AddFunc("WeekdayName", functions.WeekdayName)
e.AddFunc("HRDateShort", functions.HRDateShort)
// Strings
e.AddFunc("FirstLetter", functions.FirstLetter)
e.AddFunc("Upper", strings.ToUpper)
e.AddFunc("Lower", strings.ToLower)
// App specific
e.AddFunc("GetAgent", app.Library.Agents.Item)
e.AddFunc("GetPlace", app.Library.Places.Item)
e.AddFunc("GetWork", app.Library.Works.Item)
e.AddFunc("GetCategory", app.Library.Categories.Item)
e.AddFunc("GetIssue", app.Library.Issues.Item)
e.AddFunc("GetPiece", app.Library.Pieces.Item)
e.AddFunc("GetGND", app.GND.Person)
return nil

View File

@@ -1,58 +1,85 @@
package viewmodels
import (
"maps"
"slices"
"strings"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/xmlprovider"
)
type AgentView struct {
Agents []xmlprovider.Agent
Works map[string][]xmlprovider.Work
Pieces map[string][]xmlprovider.Piece
type AgentsListView struct {
Search string
AvailableLetters []string
Agents map[string]AgentView
Sorted []string
}
func AgentsView(letterorid string, lib *xmlprovider.Library) *AgentView {
res := AgentView{}
lib.Agents.Items.Range(func(key, value interface{}) bool {
k := key.(string)
if strings.HasPrefix(k, letterorid) {
agent := value.(xmlprovider.Agent)
res.Agents = append(res.Agents, agent)
}
return true
})
type AgentView struct {
xmlprovider.Agent
Works []WorkByAgent
Pieces []PieceByAgent
}
res.Works = make(map[string][]xmlprovider.Work)
res.Pieces = make(map[string][]xmlprovider.Piece)
type WorkByAgent struct {
xmlprovider.Work
Reference xmlprovider.AgentRef
}
lib.Works.Items.Range(func(key, value interface{}) bool {
w := value.(xmlprovider.Work)
for _, a := range res.Agents {
type PieceByAgent struct {
xmlprovider.Piece
Reference xmlprovider.AgentRef
}
func AgentsView(letterorid string, lib *xmlprovider.Library) *AgentsListView {
res := AgentsListView{Search: letterorid, Agents: make(map[string]AgentView)}
av := make(map[string]bool)
if len(letterorid) == 1 {
// INFO: This is all persons beginning with a letter
for _, a := range lib.Agents.Array {
av[strings.ToUpper(a.ID[:1])] = true
if strings.HasPrefix(a.ID, letterorid) {
_, ok := res.Works[a.ID]
if !ok {
res.Works[a.ID] = []xmlprovider.Work{}
}
res.Works[a.ID] = append(res.Works[a.ID], w)
res.Sorted = append(res.Sorted, a.ID)
res.Agents[a.ID] = AgentView{Agent: a}
}
}
return true
})
lib.Pieces.Items.Range(func(key, value interface{}) bool {
p := value.(xmlprovider.Piece)
for _, a := range res.Agents {
if strings.HasPrefix(a.ID, letterorid) {
_, ok := res.Pieces[a.ID]
if !ok {
res.Pieces[a.ID] = []xmlprovider.Piece{}
}
res.Pieces[a.ID] = append(res.Pieces[a.ID], p)
} else {
// INFO: This is a specific person lookup by ID
for _, a := range lib.Agents.Array {
av[strings.ToUpper(a.ID[:1])] = true
if a.ID == letterorid {
res.Sorted = append(res.Sorted, a.ID)
res.Agents[a.ID] = AgentView{Agent: a}
break
}
}
return true
})
}
// TODO: We won't need to lock the library if we take down all routes during parsing
lib.Works.Lock()
for _, w := range lib.Works.Array {
if ref, ok := w.ReferencesAgent(letterorid); ok {
if entry, ok := res.Agents[ref.Ref]; ok {
entry.Works = append(entry.Works, WorkByAgent{Work: w, Reference: *ref})
}
}
}
lib.Works.Unlock()
lib.Pieces.Lock()
for _, p := range lib.Pieces.Array {
if ref, ok := p.ReferencesAgent(letterorid); ok {
if entry, ok := res.Agents[ref.Ref]; ok {
entry.Pieces = append(entry.Pieces, PieceByAgent{Piece: p, Reference: *ref})
}
}
}
lib.Pieces.Unlock()
res.AvailableLetters = slices.Collect(maps.Keys(av))
slices.Sort(res.AvailableLetters)
slices.Sort(res.Sorted)
return &res
}

View File

@@ -2,7 +2,6 @@ package viewmodels
import (
"fmt"
"log/slog"
"maps"
"slices"
@@ -10,20 +9,20 @@ import (
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/xmlprovider"
)
type PieceListitemVM struct {
type PieceByIssue struct {
xmlprovider.Piece
// TODO: this is a bit hacky, but it refences the page number of the piece in the issue
Reference xmlprovider.IssueRef
}
type PiecesByPage struct {
Items map[int][]PieceListitemVM
Items map[int][]PieceByIssue
Pages []int
}
// TODO: Next & Prev
type IssueVM struct {
IssueListitemVM
xmlprovider.Issue
Pieces PiecesByPage
AdditionalPieces PiecesByPage
}
@@ -34,35 +33,34 @@ func NewSingleIssueView(y string, no string, lib *xmlprovider.Library) (*IssueVM
return nil, fmt.Errorf("No issue found for %v-%v", y, no)
}
ivm, err := ListitemFromIssue(*issue)
sivm := IssueVM{Issue: *issue}
ppi, ppa, err := PiecesForIsssue(lib, *issue)
if err != nil {
return nil, err
}
sivm := IssueVM{IssueListitemVM: *ivm}
ppi, ppa, err := PiecesForIsssue(lib, *issue)
slices.Sort(ppi.Pages)
slices.Sort(ppa.Pages)
sivm.Pieces = *ppi
sivm.AdditionalPieces = *ppa
sivm.Pieces = ppi
sivm.AdditionalPieces = ppa
return &sivm, nil
}
func PiecesForIsssue(lib *xmlprovider.Library, issue xmlprovider.Issue) (*PiecesByPage, *PiecesByPage, error) {
func PiecesForIsssue(lib *xmlprovider.Library, issue xmlprovider.Issue) (PiecesByPage, PiecesByPage, error) {
year := issue.Datum.When.Year
ppi := PiecesByPage{Items: make(map[int][]PieceListitemVM)}
ppa := PiecesByPage{Items: make(map[int][]PieceListitemVM)}
ppi := PiecesByPage{Items: make(map[int][]PieceByIssue)}
ppa := PiecesByPage{Items: make(map[int][]PieceByIssue)}
// TODO: will we have to lock this, if we shutdown the server while loading the library?
lib.Pieces.Lock()
defer lib.Pieces.Unlock()
slog.Debug(fmt.Sprintf("Checking piece for year %v, number %v", year, issue.Number.No))
for _, piece := range lib.Pieces.Array {
if d, ok := piece.ReferencesIssue(year, issue.Number.No); ok {
slog.Debug(fmt.Sprintf("Found piece %v in issue %v-%v", piece, year, issue.Number.No))
p := PieceListitemVM{Piece: piece, Reference: *d}
p := PieceByIssue{Piece: piece, Reference: *d}
if d.Beilage > 0 {
functions.MapArrayInsert(ppa.Items, d.Von, p)
} else {
@@ -74,5 +72,5 @@ func PiecesForIsssue(lib *xmlprovider.Library, issue xmlprovider.Issue) (*Pieces
ppi.Pages = slices.Collect(maps.Keys(ppi.Items))
ppa.Pages = slices.Collect(maps.Keys(ppa.Items))
return &ppi, &ppa, nil
return ppi, ppa, nil
}

View File

@@ -1,25 +0,0 @@
package viewmodels
import (
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/xmlprovider"
)
const TLAYOUT = "2006-01-02"
type IssueListitemVM struct {
xmlprovider.Issue
No int
Day int
Month int
Year int
}
func ListitemFromIssue(i xmlprovider.Issue) (*IssueListitemVM, error) {
return &IssueListitemVM{
No: i.Number.No,
Issue: i,
Day: i.Datum.When.Day,
Month: i.Datum.When.Month,
Year: i.Datum.When.Year,
}, nil
}

View File

@@ -6,10 +6,11 @@ import (
"slices"
"sort"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/functions"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/xmlprovider"
)
type IssuesByMonth map[int][]IssueListitemVM
type IssuesByMonth map[int][]xmlprovider.Issue
func (ibm *IssuesByMonth) Sort() {
for _, issues := range *ibm {
@@ -34,9 +35,7 @@ func YearView(year int, lib *xmlprovider.Library) (*YearVM, error) {
y := issue.Datum.When.Year
years[y] = true
if y == year {
if issuevm, err := ListitemFromIssue(issue); err == nil {
issues[issuevm.Month] = append(issues[issuevm.Month], *issuevm)
}
functions.MapArrayInsert(issues, issue.Datum.When.Month, issue)
}
}
lib.Issues.Unlock()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 2.6 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 2.6 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,58 @@
{{ if ne (len .model.Search) 1 }}
{{ $agent := index $.model.Agents .model.Search }}
{{ if not $agent }}
<div>Agent nicht gefunden: {{ .model.Search }}</div>
{{ else }}
<div>
{{ $letter := Upper (FirstLetter $agent.ID) }}
<a href="/akteure/{{ $letter }}">
&larr; Personen &amp; Körperschaften &ndash; Buchstabe
{{ $letter }}
</a>
</div>
<div>{{ index $agent.Names 0 }}</div>
{{ end }}
{{ else }}
<div>
{{ range $_, $l := .model.AvailableLetters }}
<a href="/akteure/{{ $l }}">
{{ $l }}
</a>
{{ end }}
</div>
{{ range $_, $id := .model.Sorted }}
<div class="pb-4">
<a href="/akteure/{{ $id }}">
{{ $a := index $.model.Agents $id }}
{{ index $a.Names 0 }}
</a>
<div>
{{ $gnd := GetGND $a.GND }}
{{ if and (ne $gnd nil) (ne $gnd.DateOfBirth nil) }}
{{- if ne (len $gnd.DateOfBirth) 0 -}}
<i class="ri-asterisk text-xs relative bottom-0.5"></i>&nbsp;
{{- HRDateShort (index $gnd.DateOfBirth 0) -}}
{{- end -}}
{{- if ne (len $gnd.DateOfDeath) 0 }}
&emsp;<i class="ri-cross-fill text-xs relative bottom-0.5"></i
>&nbsp;{{ HRDateShort (index $gnd.DateOfDeath 0) }}
{{ end }}
{{- if ne (len $gnd.ProfessionOrOccupation) 0 -}}
<div>
{{- (index $gnd.ProfessionOrOccupation 0).Label -}}
{{- if gt (len $gnd.ProfessionOrOccupation) 1 -}}
,
{{ (index $gnd.ProfessionOrOccupation 1).Label -}}
{{ end -}}
{{- if gt (len $gnd.ProfessionOrOccupation) 2 -}}
,
{{ (index $gnd.ProfessionOrOccupation 2).Label -}}
{{ end -}}
</div>
{{ end }}
{{ end }}
</div>
</div>
{{ end }}
{{ end }}

View File

@@ -0,0 +1,9 @@
<title>
KGPZ &ndash;
{{ if ne (len .model.Search) 1 }}
{{ index (index .model.Agents .model.Search).Names 0 }}
{{ else }}
Personen &amp; Körperschaften:
{{ Upper .model.Search }}
{{ end }}
</title>

View File

@@ -10,7 +10,7 @@
<div>
<div class="py-3 text-xl">
<div>{{ $date.Year }}</div>
<div>Stück {{ $model.No }}</div>
<div>Stück {{ $model.Number.No }}</div>
<div>{{ WeekdayName $date.Weekday }}, {{ $date.Day }}. {{ MonthName $date.Month }}</div>
</div>
{{ template "_inhaltsverzeichnis" . }}

View File

@@ -8,7 +8,6 @@
<div>Seite {{ $page }}</div>
{{ range $piece := (index $model.Pieces.Items $page) }}
{{ template "_inhaltsverzeichnis_eintrag" $piece }}
@@ -21,7 +20,9 @@
<li>
<a
href="/{{- $issue.When -}}/{{- $issue.Nr -}}"
{{ if and (eq $issue.Nr $model.No) (eq $issue.When.Year $model.Datum.When.Year) }}
{{- if and (eq $issue.Nr $model.Number.No) (eq $issue.When.Year
$model.Datum.When.Year)
-}}
aria-current="page"
{{ end }}>
{{- $issue.When.Year }} Nr.

View File

@@ -1 +1 @@
<title>KGPZ &ndash; Ausgabe {{ .model.No }}&hairsp;/&hairsp;{{ .model.Year }}</title>
<title>KGPZ &ndash; Ausgabe {{ .model.Number.No }}&hairsp;/&hairsp;{{ .model.Year }}</title>