mirror of
https://github.com/Theodor-Springmann-Stiftung/kgpz_web.git
synced 2025-10-28 08:35:30 +00:00
Better structure of files; introduced XML models
This commit is contained in:
45
app/kgpz.go
45
app/kgpz.go
@@ -9,17 +9,7 @@ import (
|
||||
"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/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/"
|
||||
"github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels"
|
||||
)
|
||||
|
||||
type KGPZ struct {
|
||||
@@ -29,7 +19,7 @@ type KGPZ struct {
|
||||
Config *providers.ConfigProvider
|
||||
Repo *providers.GitProvider
|
||||
GND *gnd.GNDProvider
|
||||
Library *xmlprovider.Library
|
||||
Library *xmlmodels.Library
|
||||
}
|
||||
|
||||
func (k *KGPZ) Init() {
|
||||
@@ -81,10 +71,8 @@ func (k *KGPZ) Enrich() error {
|
||||
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() {
|
||||
data := gnd.ProviderIntoDataset(k.Library.Agents)
|
||||
data := xmlmodels.AgentsIntoDataset(k.Library.Agents)
|
||||
k.GND.FetchPersons(data)
|
||||
k.GND.WriteCache(k.Config.GNDPath)
|
||||
}()
|
||||
@@ -110,22 +98,9 @@ func (k *KGPZ) Serialize() {
|
||||
helpers.Assert(err, "Error getting pieces")
|
||||
|
||||
if k.Library == nil {
|
||||
k.Library = xmlprovider.NewLibrary(
|
||||
[]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 = xmlmodels.NewLibrary(k.Config.FolderPath)
|
||||
}
|
||||
|
||||
k.Library.Serialize(commit)
|
||||
}
|
||||
|
||||
@@ -177,13 +152,3 @@ func (k *KGPZ) initRepo() {
|
||||
func (k *KGPZ) Shutdown() {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
"webhook_endpoint": "/webhook",
|
||||
"webhook_secret": "secret",
|
||||
"debug": true,
|
||||
"watch": true
|
||||
"watch": false
|
||||
}
|
||||
|
||||
@@ -1,17 +1,5 @@
|
||||
package gnd
|
||||
|
||||
import "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/xmlprovider"
|
||||
|
||||
type GNDData struct {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging"
|
||||
)
|
||||
@@ -30,3 +31,13 @@ func UnmarshalFile[T any](filename string, data T) error {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ type ItemInfo struct {
|
||||
Parse *ParseMeta
|
||||
}
|
||||
|
||||
type KeyedItem struct {
|
||||
keys []string
|
||||
// INFO: These are just root elements that hold the data of the XML files.
|
||||
// They get discarded after a parse.
|
||||
type XMLRootElement[T any] interface {
|
||||
Children() []T
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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.
|
||||
type XMLProvider[T XMLItem] struct {
|
||||
Paths []string
|
||||
// INFO: map is type map[string]*T
|
||||
Items sync.Map
|
||||
// INFO: map is type [string]ItemInfo
|
||||
@@ -60,15 +59,17 @@ func (p *XMLProvider[T]) Serialize(dataholder XMLRootElement[T], path string) er
|
||||
}
|
||||
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
if len(p.parses) == 0 {
|
||||
logging.Error(fmt.Errorf("No commit set"), "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.
|
||||
for _, id := range item.Keys() {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -13,13 +13,14 @@ import (
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"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/recover"
|
||||
"github.com/gofiber/storage/memory/v2"
|
||||
)
|
||||
|
||||
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
|
||||
SERVER_TIMEOUT = 16 * time.Second
|
||||
|
||||
@@ -91,6 +92,7 @@ func (s *Server) Engine(e *templating.Engine) {
|
||||
s.Start()
|
||||
}
|
||||
|
||||
// TODO: There is no error handler
|
||||
func (s *Server) Start() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
@@ -133,6 +135,7 @@ func (s *Server) Start() {
|
||||
|
||||
srv.Use(recover.New())
|
||||
|
||||
srv.Use(ASSETS_URL_PREFIX, etag.New())
|
||||
srv.Use(ASSETS_URL_PREFIX, static(&views.StaticFS))
|
||||
|
||||
// TODO: Dont cache static assets, bc storage gets huge
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/xmlprovider"
|
||||
"github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels"
|
||||
)
|
||||
|
||||
type AgentsListView struct {
|
||||
@@ -16,22 +16,28 @@ type AgentsListView struct {
|
||||
}
|
||||
|
||||
type AgentView struct {
|
||||
xmlprovider.Agent
|
||||
xmlmodels.Agent
|
||||
Works []WorkByAgent
|
||||
Pieces []PieceByAgent
|
||||
}
|
||||
|
||||
type WorkByAgent struct {
|
||||
xmlprovider.Work
|
||||
Reference xmlprovider.AgentRef
|
||||
xmlmodels.Work
|
||||
Pieces []PieceByWork
|
||||
Reference xmlmodels.AgentRef
|
||||
}
|
||||
|
||||
type PieceByAgent struct {
|
||||
xmlprovider.Piece
|
||||
Reference xmlprovider.AgentRef
|
||||
xmlmodels.Piece
|
||||
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)}
|
||||
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()
|
||||
for _, w := range lib.Works.Array {
|
||||
if ref, ok := w.ReferencesAgent(letterorid); ok {
|
||||
@@ -76,6 +82,15 @@ func AgentsView(letterorid string, lib *xmlprovider.Library) *AgentsListView {
|
||||
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()
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@ import (
|
||||
"slices"
|
||||
|
||||
"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 {
|
||||
xmlprovider.Piece
|
||||
xmlmodels.Piece
|
||||
// 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 {
|
||||
@@ -22,12 +22,12 @@ type PiecesByPage struct {
|
||||
|
||||
// TODO: Next & Prev
|
||||
type IssueVM struct {
|
||||
xmlprovider.Issue
|
||||
xmlmodels.Issue
|
||||
Pieces 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)
|
||||
if issue == nil {
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
ppi := PiecesByPage{Items: make(map[int][]PieceByIssue)}
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
"sort"
|
||||
|
||||
"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() {
|
||||
for _, issues := range *ibm {
|
||||
@@ -26,7 +26,7 @@ type YearVM struct {
|
||||
Issues IssuesByMonth
|
||||
}
|
||||
|
||||
func YearView(year int, lib *xmlprovider.Library) (*YearVM, error) {
|
||||
func YearView(year int, lib *xmlmodels.Library) (*YearVM, error) {
|
||||
issues := make(IssuesByMonth, 12)
|
||||
years := make(map[int]bool)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
{{ $letter }}
|
||||
</a>
|
||||
</div>
|
||||
<div>{{ template "_agent" $agent }}</div>
|
||||
<div>{{ template "_akteur" $agent }}</div>
|
||||
{{ end }}
|
||||
|
||||
{{ else }}
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
{{ range $_, $id := .model.Sorted }}
|
||||
{{ $a := index $.model.Agents $id }}
|
||||
{{ template "_agent" $a }}
|
||||
{{ template "_akteur" $a }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package xmlprovider
|
||||
package xmlmodels
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
@@ -1,4 +1,4 @@
|
||||
package xmlprovider
|
||||
package xmlmodels
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
@@ -1,4 +1,4 @@
|
||||
package xmlprovider
|
||||
package xmlmodels
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
@@ -50,8 +50,8 @@ type Note struct {
|
||||
}
|
||||
|
||||
type Identifier struct {
|
||||
ID string `xml:"id,attr"`
|
||||
KeyedItem
|
||||
ID string `xml:"id,attr"`
|
||||
keys []string
|
||||
}
|
||||
|
||||
func (i Identifier) Keys() []string {
|
||||
@@ -62,13 +62,6 @@ func (i Identifier) Keys() []string {
|
||||
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 {
|
||||
Chardata string `xml:",chardata"`
|
||||
}
|
||||
26
xmlmodels/helpers.go
Normal file
26
xmlmodels/helpers.go
Normal 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
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package xmlprovider
|
||||
package xmlmodels
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
135
xmlmodels/library.go
Normal file
135
xmlmodels/library.go
Normal 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()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package xmlprovider
|
||||
package xmlmodels
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
@@ -70,6 +70,15 @@ func (p Piece) ReferencesAgent(a string) (*AgentRef, bool) {
|
||||
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
|
||||
// but we'll have to define rules for every single category (~35 of them)
|
||||
func (p Piece) IsCat(k string) bool {
|
||||
@@ -1,4 +1,4 @@
|
||||
package xmlprovider
|
||||
package xmlmodels
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
47
xmlmodels/references.go
Normal file
47
xmlmodels/references.go
Normal 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
|
||||
}
|
||||
@@ -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.
|
||||
type XMLRootElement[T any] interface {
|
||||
Children() []T
|
||||
}
|
||||
|
||||
type AgentRoot struct {
|
||||
XMLName xml.Name `xml:"akteure"`
|
||||
Agents []Agent `xml:"akteur"`
|
||||
}
|
||||
|
||||
func NewAgentRoot() *AgentRoot {
|
||||
return &AgentRoot{}
|
||||
}
|
||||
|
||||
func (a AgentRoot) New() *AgentRoot {
|
||||
return NewAgentRoot()
|
||||
}
|
||||
|
||||
func (a AgentRoot) Children() []Agent {
|
||||
return a.Agents
|
||||
}
|
||||
@@ -30,14 +20,6 @@ type PlaceRoot struct {
|
||||
Place []Place `xml:"ort"`
|
||||
}
|
||||
|
||||
func NewPlaceRoot() *PlaceRoot {
|
||||
return &PlaceRoot{}
|
||||
}
|
||||
|
||||
func (p PlaceRoot) New() *PlaceRoot {
|
||||
return NewPlaceRoot()
|
||||
}
|
||||
|
||||
func (p PlaceRoot) Children() []Place {
|
||||
return p.Place
|
||||
}
|
||||
@@ -47,14 +29,6 @@ type CategoryRoot struct {
|
||||
Category []Category `xml:"kategorie"`
|
||||
}
|
||||
|
||||
func NewCategoryRoot() *CategoryRoot {
|
||||
return &CategoryRoot{}
|
||||
}
|
||||
|
||||
func (c CategoryRoot) New() XMLRootElement[Category] {
|
||||
return NewCategoryRoot()
|
||||
}
|
||||
|
||||
func (c CategoryRoot) Children() []Category {
|
||||
return c.Category
|
||||
}
|
||||
@@ -64,14 +38,6 @@ type PieceRoot struct {
|
||||
Piece []Piece `xml:"beitrag"`
|
||||
}
|
||||
|
||||
func NewPieceRoot() *PieceRoot {
|
||||
return &PieceRoot{}
|
||||
}
|
||||
|
||||
func (p PieceRoot) New() XMLRootElement[Piece] {
|
||||
return NewPieceRoot()
|
||||
}
|
||||
|
||||
func (p PieceRoot) Children() []Piece {
|
||||
return p.Piece
|
||||
}
|
||||
@@ -81,14 +47,6 @@ type IssueRoot struct {
|
||||
Issues []Issue `xml:"stueck"`
|
||||
}
|
||||
|
||||
func NewIssueRoot() *IssueRoot {
|
||||
return &IssueRoot{}
|
||||
}
|
||||
|
||||
func (i IssueRoot) New() XMLRootElement[Issue] {
|
||||
return NewIssueRoot()
|
||||
}
|
||||
|
||||
func (i IssueRoot) Children() []Issue {
|
||||
return i.Issues
|
||||
}
|
||||
@@ -98,14 +56,6 @@ type WorkRoot struct {
|
||||
Work []Work `xml:"werk"`
|
||||
}
|
||||
|
||||
func NewWorkRoot() *WorkRoot {
|
||||
return &WorkRoot{}
|
||||
}
|
||||
|
||||
func (w WorkRoot) New() XMLRootElement[Work] {
|
||||
return NewWorkRoot()
|
||||
}
|
||||
|
||||
func (w WorkRoot) Children() []Work {
|
||||
return w.Work
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package xmlprovider
|
||||
package xmlmodels
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
Reference in New Issue
Block a user