This commit is contained in:
Simon Martens
2025-09-20 22:01:41 +02:00
parent edc86df0b5
commit 0f6ffbf63f
6 changed files with 442 additions and 77 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -5,6 +5,7 @@ import (
"html/template"
"io"
"io/fs"
"reflect"
"strings"
"sync"
@@ -90,6 +91,99 @@ func IssueContext(issueRef interface{}) string {
}
}
// shouldSwap determines if two pieces should be swapped for chronological ordering
func shouldSwap(item1, item2 interface{}) bool {
// Use reflection to access IssueRefs field
v1 := reflect.ValueOf(item1)
v2 := reflect.ValueOf(item2)
if v1.Kind() == reflect.Ptr {
v1 = v1.Elem()
}
if v2.Kind() == reflect.Ptr {
v2 = v2.Elem()
}
if v1.Kind() != reflect.Struct || v2.Kind() != reflect.Struct {
return false
}
refs1 := v1.FieldByName("IssueRefs")
refs2 := v2.FieldByName("IssueRefs")
if !refs1.IsValid() || !refs2.IsValid() || refs1.Len() == 0 || refs2.Len() == 0 {
return false
}
// Get first IssueRef for each piece
ref1 := refs1.Index(0)
ref2 := refs2.Index(0)
// Get year
when1 := ref1.FieldByName("When")
when2 := ref2.FieldByName("When")
if !when1.IsValid() || !when2.IsValid() {
return false
}
year1 := when1.FieldByName("Year")
year2 := when2.FieldByName("Year")
if !year1.IsValid() || !year2.IsValid() {
return false
}
y1 := int(year1.Int())
y2 := int(year2.Int())
if y1 != y2 {
return y1 > y2 // Sort by year ascending
}
// If same year, sort by issue number
nr1 := ref1.FieldByName("Nr")
nr2 := ref2.FieldByName("Nr")
if !nr1.IsValid() || !nr2.IsValid() {
return false
}
n1 := int(nr1.Int())
n2 := int(nr2.Int())
if n1 != n2 {
return n1 > n2 // Sort by issue number ascending
}
// If same issue, sort by order
order1 := ref1.FieldByName("Order")
order2 := ref2.FieldByName("Order")
if !order1.IsValid() || !order2.IsValid() {
return false
}
o1 := int(order1.Int())
o2 := int(order2.Int())
return o1 > o2 // Sort by order ascending
}
// extractItem extracts the Item field from a Resolved struct
func extractItem(piece interface{}) interface{} {
v := reflect.ValueOf(piece)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
return nil
}
itemField := v.FieldByName("Item")
if !itemField.IsValid() {
return nil
}
return itemField.Interface()
}
func (e *Engine) funcs() error {
e.mu.Lock()
e.mu.Unlock()
@@ -137,6 +231,50 @@ func (e *Engine) funcs() error {
e.AddFunc("Lower", strings.ToLower)
e.AddFunc("Safe", functions.Safe)
// Sorting
e.AddFunc("SortPiecesByDate", func(pieces interface{}) interface{} {
// Use reflection to handle any slice type
v := reflect.ValueOf(pieces)
if v.Kind() != reflect.Slice {
return pieces
}
length := v.Len()
if length == 0 {
return pieces
}
// Create indices for sorting
indices := make([]int, length)
for i := range indices {
indices[i] = i
}
// Sort indices based on piece comparison
for i := 0; i < len(indices)-1; i++ {
for j := i + 1; j < len(indices); j++ {
piece1 := v.Index(indices[i]).Interface()
piece2 := v.Index(indices[j]).Interface()
// Extract the Item field from each resolved piece
item1 := extractItem(piece1)
item2 := extractItem(piece2)
if item1 != nil && item2 != nil && shouldSwap(item1, item2) {
indices[i], indices[j] = indices[j], indices[i]
}
}
}
// Create sorted slice with same type as input
sortedSlice := reflect.MakeSlice(v.Type(), length, length)
for i, idx := range indices {
sortedSlice.Index(i).Set(v.Index(idx))
}
return sortedSlice.Interface()
})
// Embedding of file contents
embedder := functions.NewEmbedder(views.StaticFS)
e.AddFunc("EmbedSafe", embedder.EmbedSafe())

File diff suppressed because one or more lines are too long

View File

@@ -20,8 +20,8 @@
<script src="/assets/js/htmx-response-targets.js" defer></script>
<script src="/assets/js/client-side-templates.js" defer></script>
<link rel="stylesheet" type="text/css" href="/assets/css/fonts.css" />
<link rel="stylesheet" type="text/css" href="/assets/style.css" />
<link rel="stylesheet" type="text/css" href="/assets/css/fonts.css?v=1.0" />
<link rel="stylesheet" type="text/css" href="/assets/style.css?v=1.0" />
<script type="module">
import { setup } from "/assets/scripts.js";

View File

@@ -5,92 +5,137 @@
{{ $pieces := LookupPieces $a }}
<div>
<!-- Dictionary-style entry with hanging indent -->
<div class="pl-4" style="text-indent: -1rem;">
<!-- Name (bold), dates, links -->
<strong>
<a href="/akteure/{{ $a.ID }}" class="hover:text-blue-600 transition-colors">
{{ index $a.Names 0 }}
</a>
</strong>
{{ if ne $gnd nil }}
{{- if ne (len $gnd.DateOfBirth) 0 -}}
, {{ HRDateShort (index $gnd.DateOfBirth 0) }}
{{- end -}}
{{- if ne (len $gnd.DateOfDeath) 0 -}}
{{ HRDateShort (index $gnd.DateOfDeath 0) }}
{{- end -}}
{{ end }}
{{- if ne $gnd nil -}}
, <a href="{{ $a.GND }}" target="_blank" class="text-blue-600 hover:text-blue-800" title="Gemeinsame Normdatei">GND <i class="ri-external-link-line text-xs"></i></a>
{{- end -}}
</div>
<!-- Professions on second line -->
{{ if ne $gnd nil }}
{{- if ne (len $gnd.ProfessionOrOccupation) 0 -}}
<div class="text-gray-600">
{{ range $i, $prof := $gnd.ProfessionOrOccupation }}
{{ if lt $i 3 }}
{{ if gt $i 0 }}, {{ end }}{{ $prof.Label }}
{{ end }}
{{ end }}
<!-- Name and external links -->
<div class="flex items-start justify-between gap-4">
<div class="flex-1">
<!-- Large serif name - bold -->
<div class="text-xl font-serif font-bold text-gray-900 leading-tight">
<a href="/akteure/{{ $a.ID }}" class="hover:text-blue-600 transition-colors">
{{ index $a.Names 0 }}
</a>
</div>
{{- end -}}
{{ end }}
{{- if ne (len $works) 0 -}}
<div class="mt-2">
<div>
<strong><i class="ri-book-line mr-1"></i>Werke</strong>
</div>
{{ range $_, $w := $works }}
<div>
{{- if ne (len $w.Item.Citation.InnerXML ) 0 -}}
<div class="italic">
<script type="application/xml" xslt-template="transform-citation" xslt-onload>
<xml>
{{- Safe $w.Item.Citation.InnerXML -}}
</xml>
</script>
</div>
{{- end -}}
{{ range $_, $url := $w.Item.URLs }}
<div>
<a href="{{ $url.Address }}" target="_blank" class="text-blue-600 hover:text-blue-800">
{{ $url.Chardata }} <i class="ri-external-link-line text-xs"></i>
</a>
</div>
{{ end }}
</div>
{{ $workPieces := LookupPieces $w.Item }}
{{ if len $workPieces }}
<div class="flex flex-wrap gap-1 mt-1">
{{ range $_, $p := $workPieces }}
{{ range $_, $issue := $p.Item.IssueRefs }}
<span class="inline-block bg-green-50 hover:bg-green-100 text-green-700 hover:text-green-800 px-2 py-1 rounded text-sm transition-colors">
{{ template "_citation" $issue }}
</span>
<!-- Years only below name -->
{{ if ne $gnd nil }}
{{- if or (ne (len $gnd.DateOfBirth) 0) (ne (len $gnd.DateOfDeath) 0) -}}
<div class="text-gray-600 text-sm mt-1">
{{- if ne (len $gnd.DateOfBirth) 0 -}}
{{ HRDateYear (index $gnd.DateOfBirth 0) }}
{{- end -}}
{{- if ne (len $gnd.DateOfDeath) 0 -}}
{{ HRDateYear (index $gnd.DateOfDeath 0) }}
{{- end -}}
</div>
{{- end -}}
{{ end }}
<!-- First three professions -->
{{ if ne $gnd nil }}
{{- if ne (len $gnd.ProfessionOrOccupation) 0 -}}
<div class="text-gray-600 text-sm mt-1">
{{ range $i, $prof := $gnd.ProfessionOrOccupation }}
{{ if lt $i 3 }}
{{ if gt $i 0 }} · {{ end }}{{ $prof.Label }}
{{ end }}
{{ end }}
</div>
{{ end }}
{{- end -}}
{{ end }}
</div>
<!-- External link symbols on the right -->
<div class="flex gap-2 flex-shrink-0">
{{- if ne $gnd nil -}}
{{- /* Wikipedia link if available */ -}}
{{- if ne (len $gnd.Wikipedia) 0 -}}
<a href="{{ (index $gnd.Wikipedia 0).ID }}" target="_blank" class="text-gray-500 hover:text-blue-600 transition-colors text-lg" title="Wikipedia">
<i class="ri-wikipedia-line"></i>
</a>
{{- end -}}
{{- /* GND link if available */ -}}
{{- if ne $a.GND "" -}}
<a href="{{ $a.GND }}" target="_blank" class="text-gray-500 hover:text-blue-600 transition-colors text-lg" title="Gemeinsame Normdatei">
<i class="ri-links-line"></i>
</a>
{{- else -}}
{{- /* VIAF link if no GND available */ -}}
{{- if ne (len $gnd.SameAs) 0 -}}
{{ range $_, $ref := $gnd.SameAs }}
{{- if ne $ref.ID "" -}}
<a href="{{ $ref.ID }}" target="_blank" class="text-gray-500 hover:text-blue-600 transition-colors text-lg" title="External Link">
<i class="ri-global-line"></i>
</a>
{{- end -}}
{{ end }}
{{- end -}}
{{- end -}}
{{- end -}}
</div>
</div>
{{- if ne (len $works) 0 -}}
<div class="mt-3">
<div class="text-sm font-medium text-gray-700 mb-2">
<i class="ri-book-line mr-1"></i>Werke
</div>
{{ range $_, $w := $works }}
<div class="mb-2">
{{- if ne (len $w.Item.Citation.InnerXML ) 0 -}}
<div class="text-sm">
<span class="italic">
<script type="application/xml" xslt-template="transform-citation" xslt-onload>
<xml>
{{- Safe $w.Item.Citation.InnerXML -}}
</xml>
</script>
</span>
{{- range $_, $url := $w.Item.URLs -}}
<span class="ml-1">
<a href="{{ $url.Address }}" target="_blank" class="text-blue-600 hover:text-blue-800 text-sm">
{{ $url.Chardata }} <i class="ri-external-link-line text-xs"></i>
</a>
</span>
{{- end -}}
</div>
{{- end -}}
{{ $workPieces := LookupPieces $w.Item }}
{{ if len $workPieces }}
<div class="flex flex-wrap gap-1 mt-1">
{{ range $_, $p := $workPieces }}
{{ range $_, $issue := $p.Item.IssueRefs }}
<span class="inline-block bg-green-50 hover:bg-green-100 text-green-700 hover:text-green-800 px-2 py-1 rounded text-xs transition-colors">
{{ template "_citation" $issue }}
</span>
{{ end }}
{{ end }}
</div>
{{ end }}
</div>
{{ end }}
</div>
{{ end }}
{{ $pieces := LookupPieces $a }}
{{ if ne (len $pieces) 0 }}
<div class="mt-2">
<div>
<strong><i class="ri-newspaper-line mr-1"></i>Beiträge</strong>
<div class="mt-3">
<div class="text-sm font-medium text-gray-700 mb-2">
<i class="ri-newspaper-line mr-1"></i>Beiträge
</div>
<div class="flex flex-wrap gap-1 mt-1">
{{ range $_, $p := $pieces }}
{{ range $_, $issue := $p.Item.IssueRefs }}
<span class="inline-block bg-green-50 hover:bg-green-100 text-green-700 hover:text-green-800 px-2 py-1 rounded text-sm transition-colors">
{{ template "_citation" $issue }}
</span>
{{ end }}
<div class="space-y-2">
{{ range $_, $p := SortPiecesByDate $pieces }}
<div class="border-l-2 border-gray-200 pl-3">
<div class="text-sm">
{{ template "_piece_summary" $p.Item }}
</div>
<div class="mt-1 flex flex-wrap gap-1">
{{ range $_, $issue := $p.Item.IssueRefs }}
<span class="inline-block bg-green-50 hover:bg-green-100 text-green-700 hover:text-green-800 px-2 py-1 rounded text-xs transition-colors">
{{ template "_citation" $issue }}
</span>
{{ end }}
</div>
</div>
{{ end }}
</div>
</div>

View File

@@ -0,0 +1,182 @@
{{- $piece := . -}}
<div class="leading-snug">
{{- $categoryFlags := GetCategoryFlags $piece -}}
{{- $place := "" -}}
{{- if $piece.PlaceRefs -}}
{{- $placeObj := GetPlace (index $piece.PlaceRefs 0).Ref -}}
{{- if gt (len $placeObj.Names) 0 -}}
{{- $place = index $placeObj.Names 0 -}}
{{- end -}}
{{- end -}}
{{- $title := "" -}}
{{- if $piece.Title -}}
{{- $title = index $piece.Title 0 -}}
{{- else if $piece.Incipit -}}
{{- $title = index $piece.Incipit 0 -}}
{{- end -}}
{{- $workTitle := "" -}}
{{- $workAuthorName := "" -}}
{{- $workAuthorID := "" -}}
{{- if $piece.WorkRefs -}}
{{- $work := GetWork (index $piece.WorkRefs 0).Ref -}}
{{- if $work.PreferredTitle -}}
{{- $workTitle = $work.PreferredTitle -}}
{{- else if $work.Citation.Title -}}
{{- $workTitle = $work.Citation.Title -}}
{{- end -}}
{{- if $work.AgentRefs -}}
{{- range $workAgentRef := $work.AgentRefs -}}
{{- if (or (eq $workAgentRef.Category "") (eq $workAgentRef.Category "autor")) -}}
{{- $workAgent := GetAgent $workAgentRef.Ref -}}
{{- if and $workAgent (gt (len $workAgent.Names) 0) -}}
{{- $workAuthorName = index $workAgent.Names 0 -}}
{{- $workAuthorID = $workAgentRef.Ref -}}
{{- break -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- /* Generate piece descriptions */ -}}
{{- if $categoryFlags.Rezension -}}
{{- $authorFound := false -}}
{{- range $agentref := $piece.AgentRefs -}}
{{- if (or (eq $agentref.Category "") (eq $agentref.Category "autor")) -}}
{{- $agent := GetAgent $agentref.Ref -}}
{{- if and $agent (gt (len $agent.Names) 0) -}}
<a href="/akteure/{{ $agentref.Ref }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ index $agent.Names 0 }}</a>, Rezension von:
{{ if $workAuthorName }}
<a href="/akteure/{{ $workAuthorID }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ $workAuthorName }}</a>,
{{ end }}
{{ if $workTitle }}
<em>{{ $workTitle }}</em>
{{ else if $title }}
<em>{{ $title }}</em>
{{ else }}
[Werk unbekannt]
{{ end }}
{{- $authorFound = true -}}
{{- break -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if not $authorFound -}}
Rezension von:
{{ if $workAuthorName }}
<a href="/akteure/{{ $workAuthorID }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ $workAuthorName }}</a>,
{{ end }}
{{ if $workTitle }}
<em>{{ $workTitle }}</em>
{{ else if $title }}
<em>{{ $title }}</em>
{{ else }}
[Werk unbekannt]
{{ end }}
{{- end -}}
{{- else if $categoryFlags.Gedicht -}}
{{- $authorFound := false -}}
{{- range $agentref := $piece.AgentRefs -}}
{{- if (or (eq $agentref.Category "") (eq $agentref.Category "autor")) -}}
{{- $agent := GetAgent $agentref.Ref -}}
{{- if and $agent (gt (len $agent.Names) 0) -}}
<a href="/akteure/{{ $agentref.Ref }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ index $agent.Names 0 }}</a>, Gedicht{{ if $title }}: <em>„{{ $title }}"</em>{{ end }}
{{- $authorFound = true -}}
{{- break -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if not $authorFound -}}
Gedicht{{ if $title }}: <em>„{{ $title }}"</em>{{ end }}
{{- end -}}
{{- else if $categoryFlags.Aufsatz -}}
{{- $authorFound := false -}}
{{- range $agentref := $piece.AgentRefs -}}
{{- if (or (eq $agentref.Category "") (eq $agentref.Category "autor")) -}}
{{- $agent := GetAgent $agentref.Ref -}}
{{- if and $agent (gt (len $agent.Names) 0) -}}
<a href="/akteure/{{ $agentref.Ref }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ index $agent.Names 0 }}</a>, Aufsatz{{ if $title }}: <em>„{{ $title }}"</em>{{ end }}
{{- $authorFound = true -}}
{{- break -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if not $authorFound -}}
Aufsatz{{ if $title }}: <em>„{{ $title }}"</em>{{ end }}
{{- end -}}
{{- else if $categoryFlags.Theaterkritik -}}
{{- $authorFound := false -}}
{{- range $agentref := $piece.AgentRefs -}}
{{- if (or (eq $agentref.Category "") (eq $agentref.Category "autor")) -}}
{{- $agent := GetAgent $agentref.Ref -}}
{{- if and $agent (gt (len $agent.Names) 0) -}}
<a href="/akteure/{{ $agentref.Ref }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ index $agent.Names 0 }}</a>, Theaterkritik{{ if $workTitle }} zu <em>{{ $workTitle }}</em>{{ else if $title }} zu <em>{{ $title }}</em>{{ end }}
{{- $authorFound = true -}}
{{- break -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if not $authorFound -}}
Theaterkritik{{ if $workTitle }} zu <em>{{ $workTitle }}</em>{{ else if $title }} zu <em>{{ $title }}</em>{{ end }}
{{- end -}}
{{- else if $categoryFlags.Brief -}}
{{ if $categoryFlags.Nachruf }}Kondolenzbrief{{ else }}Leserbrief{{ end }}
{{- range $agentref := $piece.AgentRefs -}}
{{- if (or (eq $agentref.Category "") (eq $agentref.Category "autor")) -}}
{{- $agent := GetAgent $agentref.Ref -}}
{{- if and $agent (gt (len $agent.Names) 0) -}}
von <a href="/akteure/{{ $agentref.Ref }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ index $agent.Names 0 }}</a>
{{- break -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- else if $categoryFlags.Erzaehlung -}}
{{- $authorFound := false -}}
{{- range $agentref := $piece.AgentRefs -}}
{{- if (or (eq $agentref.Category "") (eq $agentref.Category "autor")) -}}
{{- $agent := GetAgent $agentref.Ref -}}
{{- if and $agent (gt (len $agent.Names) 0) -}}
<a href="/akteure/{{ $agentref.Ref }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ index $agent.Names 0 }}</a>, Erzählung{{ if $title }}: <em>„{{ $title }}"</em>{{ end }}
{{- $authorFound = true -}}
{{- break -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if not $authorFound -}}
Erzählung{{ if $title }}: <em>„{{ $title }}"</em>{{ end }}
{{- end -}}
{{- else if $categoryFlags.Lokalnachrichten -}}
{{ if $categoryFlags.Lotterie }}Lotterienachrichten{{ else if $categoryFlags.Nachruf }}Nachrufe{{ else if $categoryFlags.Theaterkritik }}Theaternachrichten{{ else }}Lokalnachrichten{{ end }}
{{- else if $categoryFlags.Weltnachrichten -}}
Politische Nachrichten aus aller Welt
{{- else -}}
{{- $authorFound := false -}}
{{- range $agentref := $piece.AgentRefs -}}
{{- if (or (eq $agentref.Category "") (eq $agentref.Category "autor")) -}}
{{- $agent := GetAgent $agentref.Ref -}}
{{- if and $agent (gt (len $agent.Names) 0) -}}
<a href="/akteure/{{ $agentref.Ref }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ index $agent.Names 0 }}</a>{{ if $title }}: <em>{{ $title }}</em>{{ end }}
{{- $authorFound = true -}}
{{- break -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if not $authorFound -}}
{{ if $title }}<em>{{ $title }}</em>{{ else }}Beitrag ohne Titel{{ end }}
{{- end -}}
{{- end -}}
{{- if $place }} <span class="inline-block bg-slate-200 text-slate-700 text-xs px-2 py-0.5 rounded-md ml-1 whitespace-nowrap">{{ $place }}</span>{{ end }}
</div>