Better structure of files; introduced XML models

This commit is contained in:
Simon Martens
2025-01-01 17:00:26 +01:00
parent e46d540c01
commit 7539a2dca7
25 changed files with 297 additions and 348 deletions

View File

@@ -9,17 +9,7 @@ import (
"github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging" "github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers" "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/gnd" "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/gnd"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/xmlprovider" "github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels"
)
const (
AGENTS_PATH = "XML/akteure.xml"
PLACES_PATH = "XML/orte.xml"
WORKS_PATH = "XML/werke.xml"
CATEGORIES_PATH = "XML/kategorien.xml"
ISSUES_DIR = "XML/stuecke/"
PIECES_DIR = "XML/beitraege/"
) )
type KGPZ struct { type KGPZ struct {
@@ -29,7 +19,7 @@ type KGPZ struct {
Config *providers.ConfigProvider Config *providers.ConfigProvider
Repo *providers.GitProvider Repo *providers.GitProvider
GND *gnd.GNDProvider GND *gnd.GNDProvider
Library *xmlprovider.Library Library *xmlmodels.Library
} }
func (k *KGPZ) Init() { func (k *KGPZ) Init() {
@@ -81,10 +71,8 @@ func (k *KGPZ) Enrich() error {
return nil return nil
} }
// TODO: Library locking is never needed, since the library items, once set, are never changed
// We only need to check if set
go func() { go func() {
data := gnd.ProviderIntoDataset(k.Library.Agents) data := xmlmodels.AgentsIntoDataset(k.Library.Agents)
k.GND.FetchPersons(data) k.GND.FetchPersons(data)
k.GND.WriteCache(k.Config.GNDPath) k.GND.WriteCache(k.Config.GNDPath)
}() }()
@@ -110,22 +98,9 @@ func (k *KGPZ) Serialize() {
helpers.Assert(err, "Error getting pieces") helpers.Assert(err, "Error getting pieces")
if k.Library == nil { if k.Library == nil {
k.Library = xmlprovider.NewLibrary( k.Library = xmlmodels.NewLibrary(k.Config.FolderPath)
[]string{filepath.Join(k.Config.FolderPath, AGENTS_PATH)},
[]string{filepath.Join(k.Config.FolderPath, PLACES_PATH)},
[]string{filepath.Join(k.Config.FolderPath, WORKS_PATH)},
[]string{filepath.Join(k.Config.FolderPath, CATEGORIES_PATH)},
*issues,
*pieces)
} else {
k.Library.SetPaths(
[]string{filepath.Join(k.Config.FolderPath, AGENTS_PATH)},
[]string{filepath.Join(k.Config.FolderPath, PLACES_PATH)},
[]string{filepath.Join(k.Config.FolderPath, WORKS_PATH)},
[]string{filepath.Join(k.Config.FolderPath, CATEGORIES_PATH)},
*issues,
*pieces)
} }
k.Library.Serialize(commit) k.Library.Serialize(commit)
} }
@@ -177,13 +152,3 @@ func (k *KGPZ) initRepo() {
func (k *KGPZ) Shutdown() { func (k *KGPZ) Shutdown() {
k.Repo.Wait() k.Repo.Wait()
} }
func getXMLFiles(path string) (*[]string, error) {
if _, err := os.Stat(path); os.IsNotExist(err) {
return nil, err
}
matches, err := filepath.Glob(filepath.Join(path, "*.xml"))
return &matches, err
}

View File

@@ -4,5 +4,5 @@
"webhook_endpoint": "/webhook", "webhook_endpoint": "/webhook",
"webhook_secret": "secret", "webhook_secret": "secret",
"debug": true, "debug": true,
"watch": true "watch": false
} }

View File

@@ -1,17 +1,5 @@
package gnd package gnd
import "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/xmlprovider"
type GNDData struct { type GNDData struct {
ID, GND string ID, GND string
} }
func ProviderIntoDataset(provider *xmlprovider.XMLProvider[xmlprovider.Agent]) []GNDData {
provider.Lock()
defer provider.Unlock()
var data []GNDData
for _, agent := range provider.Array {
data = append(data, GNDData{ID: agent.ID, GND: agent.GND})
}
return data
}

View File

@@ -4,6 +4,7 @@ import (
"encoding/xml" "encoding/xml"
"io" "io"
"os" "os"
"path/filepath"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging" "github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging"
) )
@@ -30,3 +31,13 @@ func UnmarshalFile[T any](filename string, data T) error {
} }
return nil return nil
} }
func XMLFilesForPath(path string) ([]string, error) {
if _, err := os.Stat(path); os.IsNotExist(err) {
return nil, err
}
matches, err := filepath.Glob(filepath.Join(path, "*.xml"))
return matches, err
}

View File

@@ -5,6 +5,8 @@ type ItemInfo struct {
Parse *ParseMeta Parse *ParseMeta
} }
type KeyedItem struct { // INFO: These are just root elements that hold the data of the XML files.
keys []string // They get discarded after a parse.
type XMLRootElement[T any] interface {
Children() []T
} }

View File

@@ -1,147 +0,0 @@
package xmlprovider
import (
"fmt"
"sync"
)
type Library struct {
amu sync.Mutex
Agents *XMLProvider[Agent]
Places *XMLProvider[Place]
Works *XMLProvider[Work]
Categories *XMLProvider[Category]
Issues *XMLProvider[Issue]
Pieces *XMLProvider[Piece]
}
func (l *Library) String() string {
return fmt.Sprintf("Agents: %s\nPlaces: %s\nWorks: %s\nCategories: %s\nIssues: %s\nPieces: %s\n",
l.Agents.String(), l.Places.String(), l.Works.String(), l.Categories.String(), l.Issues.String(), l.Pieces.String())
}
// INFO: this is the only place where the providers are created. There is no need for locking on access.
func NewLibrary(agentpaths, placepaths, workpaths, categorypaths, issuepaths, piecepaths []string) *Library {
return &Library{
Agents: &XMLProvider[Agent]{Paths: agentpaths},
Places: &XMLProvider[Place]{Paths: placepaths},
Works: &XMLProvider[Work]{Paths: workpaths},
Categories: &XMLProvider[Category]{Paths: categorypaths},
Issues: &XMLProvider[Issue]{Paths: issuepaths},
Pieces: &XMLProvider[Piece]{Paths: piecepaths},
}
}
func (l *Library) SetPaths(agentpaths, placepaths, workpaths, categorypaths, issuepaths, piecepaths []string) {
l.amu.Lock()
defer l.amu.Unlock()
l.Agents.Paths = agentpaths
l.Places.Paths = placepaths
l.Works.Paths = workpaths
l.Categories.Paths = categorypaths
l.Issues.Paths = issuepaths
l.Pieces.Paths = piecepaths
}
func (l *Library) Serialize(commit string) {
wg := sync.WaitGroup{}
l.Prepare(commit)
for _, path := range l.Places.Paths {
wg.Add(1)
go func() {
l.Places.Serialize(NewPlaceRoot(), path)
wg.Done()
}()
}
for _, path := range l.Agents.Paths {
wg.Add(1)
go func() {
l.Agents.Serialize(NewAgentRoot(), path)
wg.Done()
}()
}
for _, path := range l.Categories.Paths {
wg.Add(1)
go func() {
l.Categories.Serialize(NewCategoryRoot(), path)
wg.Done()
}()
}
for _, path := range l.Works.Paths {
wg.Add(1)
go func() {
l.Works.Serialize(NewWorkRoot(), path)
wg.Done()
}()
}
for _, path := range l.Issues.Paths {
wg.Add(1)
go func() {
l.Issues.Serialize(NewIssueRoot(), path)
wg.Done()
}()
}
for _, path := range l.Pieces.Paths {
wg.Add(1)
go func() {
l.Pieces.Serialize(NewPieceRoot(), path)
wg.Done()
}()
}
wg.Wait()
l.Cleanup()
}
func (l *Library) Prepare(commit string) {
l.Agents.Prepare(commit)
l.Places.Prepare(commit)
l.Works.Prepare(commit)
l.Categories.Prepare(commit)
l.Issues.Prepare(commit)
l.Pieces.Prepare(commit)
}
func (l *Library) Cleanup() {
wg := sync.WaitGroup{}
wg.Add(6)
go func() {
l.Agents.Cleanup()
wg.Done()
}()
go func() {
l.Places.Cleanup()
wg.Done()
}()
go func() {
l.Works.Cleanup()
wg.Done()
}()
go func() {
l.Categories.Cleanup()
wg.Done()
}()
go func() {
l.Issues.Cleanup()
wg.Done()
}()
go func() {
l.Pieces.Cleanup()
wg.Done()
}()
wg.Wait()
}

View File

@@ -21,7 +21,6 @@ type XMLItem interface {
// An XMLProvider is a struct that holds holds serialized XML data of a specific type. It combines multiple parses IF a succeeded parse can not serialize the data from a path. // An XMLProvider is a struct that holds holds serialized XML data of a specific type. It combines multiple parses IF a succeeded parse can not serialize the data from a path.
type XMLProvider[T XMLItem] struct { type XMLProvider[T XMLItem] struct {
Paths []string
// INFO: map is type map[string]*T // INFO: map is type map[string]*T
Items sync.Map Items sync.Map
// INFO: map is type [string]ItemInfo // INFO: map is type [string]ItemInfo
@@ -60,15 +59,17 @@ func (p *XMLProvider[T]) Serialize(dataholder XMLRootElement[T], path string) er
} }
p.mu.Lock() p.mu.Lock()
defer p.mu.Unlock()
if len(p.parses) == 0 { if len(p.parses) == 0 {
logging.Error(fmt.Errorf("No commit set"), "No commit set") logging.Error(fmt.Errorf("No commit set"), "No commit set")
return fmt.Errorf("No commit set") return fmt.Errorf("No commit set")
} }
commit := &p.parses[len(p.parses)-1]
p.Array = append(p.Array, dataholder.Children()...)
p.mu.Unlock()
for _, item := range dataholder.Children() { commit := &p.parses[len(p.parses)-1]
newItems := dataholder.Children()
for _, item := range newItems {
// INFO: Mostly it's just one ID, so the double loop is not that bad. // INFO: Mostly it's just one ID, so the double loop is not that bad.
for _, id := range item.Keys() { for _, id := range item.Keys() {
p.Infos.Store(id, ItemInfo{Source: path, Parse: commit}) p.Infos.Store(id, ItemInfo{Source: path, Parse: commit})
@@ -76,6 +77,7 @@ func (p *XMLProvider[T]) Serialize(dataholder XMLRootElement[T], path string) er
} }
} }
p.Array = append(p.Array, newItems...)
return nil return nil
} }

View File

@@ -1,50 +0,0 @@
package xmlprovider
import "encoding/xml"
type AgentRef struct {
XMLName xml.Name `xml:"akteur"`
Reference
}
type AdditionalRef struct {
XMLName xml.Name `xml:"beilage"`
Reference // Ist nicht im Schema
Datum string `xml:"datum,attr"`
Nr int `xml:"nr,attr"`
AdditionalNo int `xml:"beilage,attr"`
Von int `xml:"von,attr"`
Bis int `xml:"bis,attr"`
}
type IssueRef struct {
XMLName xml.Name `xml:"stueck"`
Reference // Ist nicht im Schema
DateAttributes
Nr int `xml:"nr,attr"`
Von int `xml:"von,attr"`
Bis int `xml:"bis,attr"`
Beilage int `xml:"beilage,attr"`
}
type PlaceRef struct {
XMLName xml.Name `xml:"ort"`
Reference
}
type CategoryRef struct {
XMLName xml.Name `xml:"kategorie"`
Reference
}
type WorkRef struct {
XMLName xml.Name `xml:"werk"`
Reference
Page string `xml:"s,attr"`
}
type PieceRef struct {
XMLName xml.Name `xml:"beitrag"`
Page string `xml:"s,attr"`
Reference
}

View File

@@ -13,13 +13,14 @@ import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cache" "github.com/gofiber/fiber/v2/middleware/cache"
"github.com/gofiber/fiber/v2/middleware/etag"
"github.com/gofiber/fiber/v2/middleware/logger" "github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/recover" "github.com/gofiber/fiber/v2/middleware/recover"
"github.com/gofiber/storage/memory/v2" "github.com/gofiber/storage/memory/v2"
) )
const ( const (
// INFO: This timeout is stupid. Uploads can take a long time, others might not. It's messy. // INFO: This timeout is stupid. Uploads can take a long time, other routes might not. It's messy.
REQUEST_TIMEOUT = 16 * time.Second REQUEST_TIMEOUT = 16 * time.Second
SERVER_TIMEOUT = 16 * time.Second SERVER_TIMEOUT = 16 * time.Second
@@ -91,6 +92,7 @@ func (s *Server) Engine(e *templating.Engine) {
s.Start() s.Start()
} }
// TODO: There is no error handler
func (s *Server) Start() { func (s *Server) Start() {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
@@ -133,6 +135,7 @@ func (s *Server) Start() {
srv.Use(recover.New()) srv.Use(recover.New())
srv.Use(ASSETS_URL_PREFIX, etag.New())
srv.Use(ASSETS_URL_PREFIX, static(&views.StaticFS)) srv.Use(ASSETS_URL_PREFIX, static(&views.StaticFS))
// TODO: Dont cache static assets, bc storage gets huge // TODO: Dont cache static assets, bc storage gets huge

View File

@@ -5,7 +5,7 @@ import (
"slices" "slices"
"strings" "strings"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/xmlprovider" "github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels"
) )
type AgentsListView struct { type AgentsListView struct {
@@ -16,22 +16,28 @@ type AgentsListView struct {
} }
type AgentView struct { type AgentView struct {
xmlprovider.Agent xmlmodels.Agent
Works []WorkByAgent Works []WorkByAgent
Pieces []PieceByAgent Pieces []PieceByAgent
} }
type WorkByAgent struct { type WorkByAgent struct {
xmlprovider.Work xmlmodels.Work
Reference xmlprovider.AgentRef Pieces []PieceByWork
Reference xmlmodels.AgentRef
} }
type PieceByAgent struct { type PieceByAgent struct {
xmlprovider.Piece xmlmodels.Piece
Reference xmlprovider.AgentRef Reference xmlmodels.AgentRef
} }
func AgentsView(letterorid string, lib *xmlprovider.Library) *AgentsListView { type PieceByWork struct {
xmlmodels.Piece
Reference xmlmodels.WorkRef
}
func AgentsView(letterorid string, lib *xmlmodels.Library) *AgentsListView {
res := AgentsListView{Search: letterorid, Agents: make(map[string]AgentView)} res := AgentsListView{Search: letterorid, Agents: make(map[string]AgentView)}
av := make(map[string]bool) av := make(map[string]bool)
@@ -56,7 +62,7 @@ func AgentsView(letterorid string, lib *xmlprovider.Library) *AgentsListView {
} }
} }
// TODO: We won't need to lock the library if we take down all routes during parsing // TODO: We won't need to lock the library if we take down the server during parsing
lib.Works.Lock() lib.Works.Lock()
for _, w := range lib.Works.Array { for _, w := range lib.Works.Array {
if ref, ok := w.ReferencesAgent(letterorid); ok { if ref, ok := w.ReferencesAgent(letterorid); ok {
@@ -76,6 +82,15 @@ func AgentsView(letterorid string, lib *xmlprovider.Library) *AgentsListView {
res.Agents[ref.Ref] = entry res.Agents[ref.Ref] = entry
} }
} }
// PERF: This is really slow: resolve all backlinks after parse?
for _, a := range res.Agents {
for _, w := range a.Works {
if ref, ok := p.ReferencesWork(w.ID); ok {
w.Pieces = append(w.Pieces, PieceByWork{Piece: p, Reference: *ref})
}
}
}
} }
lib.Pieces.Unlock() lib.Pieces.Unlock()

View File

@@ -6,13 +6,13 @@ import (
"slices" "slices"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/functions" "github.com/Theodor-Springmann-Stiftung/kgpz_web/functions"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/xmlprovider" "github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels"
) )
type PieceByIssue struct { type PieceByIssue struct {
xmlprovider.Piece xmlmodels.Piece
// TODO: this is a bit hacky, but it refences the page number of the piece in the issue // TODO: this is a bit hacky, but it refences the page number of the piece in the issue
Reference xmlprovider.IssueRef Reference xmlmodels.IssueRef
} }
type PiecesByPage struct { type PiecesByPage struct {
@@ -22,12 +22,12 @@ type PiecesByPage struct {
// TODO: Next & Prev // TODO: Next & Prev
type IssueVM struct { type IssueVM struct {
xmlprovider.Issue xmlmodels.Issue
Pieces PiecesByPage Pieces PiecesByPage
AdditionalPieces PiecesByPage AdditionalPieces PiecesByPage
} }
func NewSingleIssueView(y string, no string, lib *xmlprovider.Library) (*IssueVM, error) { func NewSingleIssueView(y string, no string, lib *xmlmodels.Library) (*IssueVM, error) {
issue := lib.Issues.Item(no + "-" + y) issue := lib.Issues.Item(no + "-" + y)
if issue == nil { if issue == nil {
return nil, fmt.Errorf("No issue found for %v-%v", y, no) return nil, fmt.Errorf("No issue found for %v-%v", y, no)
@@ -48,7 +48,7 @@ func NewSingleIssueView(y string, no string, lib *xmlprovider.Library) (*IssueVM
return &sivm, nil return &sivm, nil
} }
func PiecesForIsssue(lib *xmlprovider.Library, issue xmlprovider.Issue) (PiecesByPage, PiecesByPage, error) { func PiecesForIsssue(lib *xmlmodels.Library, issue xmlmodels.Issue) (PiecesByPage, PiecesByPage, error) {
year := issue.Datum.When.Year year := issue.Datum.When.Year
ppi := PiecesByPage{Items: make(map[int][]PieceByIssue)} ppi := PiecesByPage{Items: make(map[int][]PieceByIssue)}

View File

@@ -7,10 +7,10 @@ import (
"sort" "sort"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/functions" "github.com/Theodor-Springmann-Stiftung/kgpz_web/functions"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/xmlprovider" "github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels"
) )
type IssuesByMonth map[int][]xmlprovider.Issue type IssuesByMonth map[int][]xmlmodels.Issue
func (ibm *IssuesByMonth) Sort() { func (ibm *IssuesByMonth) Sort() {
for _, issues := range *ibm { for _, issues := range *ibm {
@@ -26,7 +26,7 @@ type YearVM struct {
Issues IssuesByMonth Issues IssuesByMonth
} }
func YearView(year int, lib *xmlprovider.Library) (*YearVM, error) { func YearView(year int, lib *xmlmodels.Library) (*YearVM, error) {
issues := make(IssuesByMonth, 12) issues := make(IssuesByMonth, 12)
years := make(map[int]bool) years := make(map[int]bool)

View File

@@ -11,7 +11,7 @@
{{ $letter }} {{ $letter }}
</a> </a>
</div> </div>
<div>{{ template "_agent" $agent }}</div> <div>{{ template "_akteur" $agent }}</div>
{{ end }} {{ end }}
{{ else }} {{ else }}
@@ -25,7 +25,7 @@
{{ range $_, $id := .model.Sorted }} {{ range $_, $id := .model.Sorted }}
{{ $a := index $.model.Agents $id }} {{ $a := index $.model.Agents $id }}
{{ template "_agent" $a }} {{ template "_akteur" $a }}
{{ end }} {{ end }}
{{ end }} {{ end }}

View File

@@ -1,4 +1,4 @@
package xmlprovider package xmlmodels
import ( import (
"encoding/xml" "encoding/xml"

View File

@@ -1,4 +1,4 @@
package xmlprovider package xmlmodels
import ( import (
"encoding/xml" "encoding/xml"

View File

@@ -1,4 +1,4 @@
package xmlprovider package xmlmodels
import ( import (
"encoding/xml" "encoding/xml"
@@ -50,8 +50,8 @@ type Note struct {
} }
type Identifier struct { type Identifier struct {
ID string `xml:"id,attr"` ID string `xml:"id,attr"`
KeyedItem keys []string
} }
func (i Identifier) Keys() []string { func (i Identifier) Keys() []string {
@@ -62,13 +62,6 @@ func (i Identifier) Keys() []string {
return i.keys return i.keys
} }
type Reference struct {
Ref string `xml:"ref,attr"`
Category string `xml:"kat,attr"`
Unsicher bool `xml:"unsicher,attr"`
Value
}
type Value struct { type Value struct {
Chardata string `xml:",chardata"` Chardata string `xml:",chardata"`
} }

26
xmlmodels/helpers.go Normal file
View File

@@ -0,0 +1,26 @@
package xmlmodels
import (
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/gnd"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/xmlprovider"
)
const (
AGENTS_PATH = "XML/akteure.xml"
PLACES_PATH = "XML/orte.xml"
WORKS_PATH = "XML/werke.xml"
CATEGORIES_PATH = "XML/kategorien.xml"
ISSUES_DIR = "XML/stuecke/"
PIECES_DIR = "XML/beitraege/"
)
func AgentsIntoDataset(provider *xmlprovider.XMLProvider[Agent]) []gnd.GNDData {
provider.Lock()
defer provider.Unlock()
var data []gnd.GNDData
for _, agent := range provider.Array {
data = append(data, gnd.GNDData{ID: agent.ID, GND: agent.GND})
}
return data
}

View File

@@ -1,4 +1,4 @@
package xmlprovider package xmlmodels
import ( import (
"encoding/xml" "encoding/xml"

135
xmlmodels/library.go Normal file
View File

@@ -0,0 +1,135 @@
package xmlmodels
import (
"fmt"
"path/filepath"
"sync"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/xmlprovider"
)
type Library struct {
baseDir string
Agents *xmlprovider.XMLProvider[Agent]
Places *xmlprovider.XMLProvider[Place]
Works *xmlprovider.XMLProvider[Work]
Categories *xmlprovider.XMLProvider[Category]
Issues *xmlprovider.XMLProvider[Issue]
Pieces *xmlprovider.XMLProvider[Piece]
}
func (l *Library) String() string {
return fmt.Sprintf("Agents: %s\nPlaces: %s\nWorks: %s\nCategories: %s\nIssues: %s\nPieces: %s\n",
l.Agents.String(), l.Places.String(), l.Works.String(), l.Categories.String(), l.Issues.String(), l.Pieces.String())
}
// INFO: this is the only place where the providers are created. There is no need for locking on access.
func NewLibrary(basedir string) *Library {
return &Library{
baseDir: basedir,
Agents: &xmlprovider.XMLProvider[Agent]{},
Places: &xmlprovider.XMLProvider[Place]{},
Works: &xmlprovider.XMLProvider[Work]{},
Categories: &xmlprovider.XMLProvider[Category]{},
Issues: &xmlprovider.XMLProvider[Issue]{},
Pieces: &xmlprovider.XMLProvider[Piece]{},
}
}
func (l *Library) Serialize(commit string) {
wg := sync.WaitGroup{}
l.Prepare(commit)
wg.Add(1)
go func() {
l.Places.Serialize(&PlaceRoot{}, filepath.Join(l.baseDir, PLACES_PATH))
wg.Done()
}()
wg.Add(1)
go func() {
l.Agents.Serialize(&AgentRoot{}, filepath.Join(l.baseDir, AGENTS_PATH))
wg.Done()
}()
wg.Add(1)
go func() {
l.Categories.Serialize(&CategoryRoot{}, filepath.Join(l.baseDir, CATEGORIES_PATH))
wg.Done()
}()
wg.Add(1)
go func() {
l.Works.Serialize(&WorkRoot{}, filepath.Join(l.baseDir, WORKS_PATH))
wg.Done()
}()
issuepaths, _ := xmlprovider.XMLFilesForPath(filepath.Join(l.baseDir, ISSUES_DIR))
for _, path := range issuepaths {
wg.Add(1)
go func() {
l.Issues.Serialize(&IssueRoot{}, path)
wg.Done()
}()
}
piecepaths, _ := xmlprovider.XMLFilesForPath(filepath.Join(l.baseDir, PIECES_DIR))
for _, path := range piecepaths {
wg.Add(1)
go func() {
l.Pieces.Serialize(&PieceRoot{}, path)
wg.Done()
}()
}
wg.Wait()
l.Cleanup()
}
func (l *Library) Prepare(commit string) {
l.Agents.Prepare(commit)
l.Places.Prepare(commit)
l.Works.Prepare(commit)
l.Categories.Prepare(commit)
l.Issues.Prepare(commit)
l.Pieces.Prepare(commit)
}
func (l *Library) Cleanup() {
wg := sync.WaitGroup{}
wg.Add(6)
go func() {
l.Agents.Cleanup()
wg.Done()
}()
go func() {
l.Places.Cleanup()
wg.Done()
}()
go func() {
l.Works.Cleanup()
wg.Done()
}()
go func() {
l.Categories.Cleanup()
wg.Done()
}()
go func() {
l.Issues.Cleanup()
wg.Done()
}()
go func() {
l.Pieces.Cleanup()
wg.Done()
}()
wg.Wait()
}

View File

@@ -1,4 +1,4 @@
package xmlprovider package xmlmodels
import ( import (
"encoding/xml" "encoding/xml"
@@ -70,6 +70,15 @@ func (p Piece) ReferencesAgent(a string) (*AgentRef, bool) {
return nil, false return nil, false
} }
func (p Piece) ReferencesWork(id string) (*WorkRef, bool) {
for _, w := range p.WorkRefs {
if w.Ref == id {
return &w, true
}
}
return nil, false
}
// TODO: We can make this fast depending on which category to look for // 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) // but we'll have to define rules for every single category (~35 of them)
func (p Piece) IsCat(k string) bool { func (p Piece) IsCat(k string) bool {

View File

@@ -1,4 +1,4 @@
package xmlprovider package xmlmodels
import ( import (
"encoding/xml" "encoding/xml"

47
xmlmodels/references.go Normal file
View File

@@ -0,0 +1,47 @@
package xmlmodels
import "encoding/xml"
type Reference struct {
Ref string `xml:"ref,attr"`
Category string `xml:"kat,attr"`
Unsicher bool `xml:"unsicher,attr"`
Value
}
type AgentRef struct {
XMLName xml.Name `xml:"akteur"`
Reference
}
type IssueRef struct {
XMLName xml.Name `xml:"stueck"`
Nr int `xml:"nr,attr"`
Von int `xml:"von,attr"`
Bis int `xml:"bis,attr"`
Beilage int `xml:"beilage,attr"`
DateAttributes
Reference // Nicht im Schema
}
type PlaceRef struct {
XMLName xml.Name `xml:"ort"`
Reference
}
type CategoryRef struct {
XMLName xml.Name `xml:"kategorie"`
Reference
}
type WorkRef struct {
XMLName xml.Name `xml:"werk"`
Page string `xml:"s,attr"`
Reference
}
type PieceRef struct {
XMLName xml.Name `xml:"beitrag"`
Page string `xml:"s,attr"`
Reference
}

View File

@@ -1,26 +1,16 @@
package xmlprovider package xmlmodels
import "encoding/xml" import (
"encoding/xml"
)
// INFO: These are just root elements that hold the data of the XML files. // INFO: These are just root elements to hold the data of a file
// They get discarded after a parse. // They get discarded after a parse.
type XMLRootElement[T any] interface {
Children() []T
}
type AgentRoot struct { type AgentRoot struct {
XMLName xml.Name `xml:"akteure"` XMLName xml.Name `xml:"akteure"`
Agents []Agent `xml:"akteur"` Agents []Agent `xml:"akteur"`
} }
func NewAgentRoot() *AgentRoot {
return &AgentRoot{}
}
func (a AgentRoot) New() *AgentRoot {
return NewAgentRoot()
}
func (a AgentRoot) Children() []Agent { func (a AgentRoot) Children() []Agent {
return a.Agents return a.Agents
} }
@@ -30,14 +20,6 @@ type PlaceRoot struct {
Place []Place `xml:"ort"` Place []Place `xml:"ort"`
} }
func NewPlaceRoot() *PlaceRoot {
return &PlaceRoot{}
}
func (p PlaceRoot) New() *PlaceRoot {
return NewPlaceRoot()
}
func (p PlaceRoot) Children() []Place { func (p PlaceRoot) Children() []Place {
return p.Place return p.Place
} }
@@ -47,14 +29,6 @@ type CategoryRoot struct {
Category []Category `xml:"kategorie"` Category []Category `xml:"kategorie"`
} }
func NewCategoryRoot() *CategoryRoot {
return &CategoryRoot{}
}
func (c CategoryRoot) New() XMLRootElement[Category] {
return NewCategoryRoot()
}
func (c CategoryRoot) Children() []Category { func (c CategoryRoot) Children() []Category {
return c.Category return c.Category
} }
@@ -64,14 +38,6 @@ type PieceRoot struct {
Piece []Piece `xml:"beitrag"` Piece []Piece `xml:"beitrag"`
} }
func NewPieceRoot() *PieceRoot {
return &PieceRoot{}
}
func (p PieceRoot) New() XMLRootElement[Piece] {
return NewPieceRoot()
}
func (p PieceRoot) Children() []Piece { func (p PieceRoot) Children() []Piece {
return p.Piece return p.Piece
} }
@@ -81,14 +47,6 @@ type IssueRoot struct {
Issues []Issue `xml:"stueck"` Issues []Issue `xml:"stueck"`
} }
func NewIssueRoot() *IssueRoot {
return &IssueRoot{}
}
func (i IssueRoot) New() XMLRootElement[Issue] {
return NewIssueRoot()
}
func (i IssueRoot) Children() []Issue { func (i IssueRoot) Children() []Issue {
return i.Issues return i.Issues
} }
@@ -98,14 +56,6 @@ type WorkRoot struct {
Work []Work `xml:"werk"` Work []Work `xml:"werk"`
} }
func NewWorkRoot() *WorkRoot {
return &WorkRoot{}
}
func (w WorkRoot) New() XMLRootElement[Work] {
return NewWorkRoot()
}
func (w WorkRoot) Children() []Work { func (w WorkRoot) Children() []Work {
return w.Work return w.Work
} }

View File

@@ -1,4 +1,4 @@
package xmlprovider package xmlmodels
import ( import (
"encoding/xml" "encoding/xml"