Learncost frontend (#42)

* introduced central package  registry by package init function
* dynamic registration of routes, model, migrations and initializers.
* setting a docker compose project name to prevent shutdown of other containers with the same (composer)name
* ai documentation
* app template
* Create tests for ALL API entpoints in ALL packages Based on current data. Ensure that all API endpoints used in frontend are tested. These tests are crucial for the next refactoring tasks.
* adopting agent instructions for a more consistent coding style
* added desired module layout and debugging information
* Fix All Failing tests All failing tests are fixed now that makes the refactoring more easy since all tests must pass
* restored routes for maintenance
* added common translations
* added new tests for API Endpoint
* Merge branch 'separate_business_logic'
* added lern and skill improvement cost editing
* Set Docker image tag when building to prevent rebuild when nothing has changed
* add and remove PP for Weaponskill fixed
* add and remove PP for same named skills fixed
* add new task
This commit is contained in:
Bardioc26
2026-05-01 18:15:31 +02:00
committed by GitHub
parent 261a6294cb
commit 042a1d4773
293 changed files with 17411 additions and 1540 deletions
+537
View File
@@ -0,0 +1,537 @@
package importer
import (
"bamort/bmrt/models"
"encoding/csv"
"encoding/json"
"fmt"
"os"
"strconv"
"strings"
)
// ExportCharToVTT converts a BaMoRT character to VTT JSON format
func ExportCharToVTT(char *models.Char) (*CharacterImport, error) {
vtt := &CharacterImport{}
// Basic info
vtt.ID = fmt.Sprintf("bamort-character-%d", char.ID)
vtt.Name = char.Name
vtt.Rasse = char.Rasse
vtt.Typ = char.Typ
vtt.Alter = char.Alter
vtt.Anrede = char.Anrede
vtt.Grad = char.Grad
vtt.Groesse = char.Groesse
vtt.Gewicht = char.Gewicht
vtt.Glaube = char.Glaube
vtt.Hand = char.Hand
vtt.Image = char.Image
// LP
vtt.Lp.Max = char.Lp.Max
vtt.Lp.Value = char.Lp.Value
// AP
vtt.Ap.Max = char.Ap.Max
vtt.Ap.Value = char.Ap.Value
// B
vtt.B.Max = char.B.Max
vtt.B.Value = char.B.Value
// Eigenschaften - convert from array to struct
for _, e := range char.Eigenschaften {
switch e.Name {
case "Au":
vtt.Eigenschaften.Au = e.Value
case "Gs":
vtt.Eigenschaften.Gs = e.Value
case "Gw":
vtt.Eigenschaften.Gw = e.Value
case "In":
vtt.Eigenschaften.In = e.Value
case "Ko":
vtt.Eigenschaften.Ko = e.Value
case "PA":
vtt.Eigenschaften.Pa = e.Value
case "St":
vtt.Eigenschaften.St = e.Value
case "Wk":
vtt.Eigenschaften.Wk = e.Value
case "Zt":
vtt.Eigenschaften.Zt = e.Value
}
}
// Merkmale
vtt.Merkmale.Augenfarbe = char.Merkmale.Augenfarbe
vtt.Merkmale.Haarfarbe = char.Merkmale.Haarfarbe
vtt.Merkmale.Sonstige = char.Merkmale.Sonstige
// Gestalt
vtt.Gestalt.Breite = char.Merkmale.Breite
vtt.Gestalt.Groesse = char.Merkmale.Groesse
// Bennies
vtt.Bennies.Gg = char.Bennies.Gg
vtt.Bennies.Sg = char.Bennies.Sg
vtt.Bennies.Gp = char.Bennies.Gp
// Erfahrungsschatz
vtt.Erfahrungsschatz.Value = char.Erfahrungsschatz.ES
// Spezialisierung
vtt.Spezialisierung = char.Spezialisierung
// Fertigkeiten
for i, f := range char.Fertigkeiten {
vtt.Fertigkeiten = append(vtt.Fertigkeiten, Fertigkeit{
ImportBase: ImportBase{
ID: fmt.Sprintf("bamort-skill-%d-%d", char.ID, i),
Name: f.Name,
},
Beschreibung: f.Beschreibung,
Fertigkeitswert: f.Fertigkeitswert,
Bonus: f.Bonus,
Pp: f.Pp,
Quelle: "", // Not stored in character skill
})
}
// Waffenfertigkeiten
for i, w := range char.Waffenfertigkeiten {
vtt.Waffenfertigkeiten = append(vtt.Waffenfertigkeiten, Waffenfertigkeit{
ImportBase: ImportBase{
ID: fmt.Sprintf("bamort-weaponskill-%d-%d", char.ID, i),
Name: w.Name,
},
Beschreibung: w.Beschreibung,
Fertigkeitswert: w.Fertigkeitswert,
Bonus: w.Bonus,
Pp: w.Pp,
Quelle: "", // Not stored in character skill
})
}
// Zauber
for i, z := range char.Zauber {
vtt.Zauber = append(vtt.Zauber, Zauber{
ImportBase: ImportBase{
ID: fmt.Sprintf("bamort-spell-%d-%d", char.ID, i),
Name: z.Name,
},
Beschreibung: z.Beschreibung,
Bonus: z.Bonus,
Quelle: z.Quelle,
})
}
// Waffen
for i, w := range char.Waffen {
vtt.Waffen = append(vtt.Waffen, Waffe{
ImportBase: ImportBase{
ID: fmt.Sprintf("bamort-weapon-%d-%d", char.ID, i),
Name: w.Name,
},
Beschreibung: w.Beschreibung,
Gewicht: w.Gewicht,
Wert: w.Wert,
Anzahl: w.Anzahl,
Anb: w.Anb,
Schb: w.Schb,
Abwb: w.Abwb,
NameFuerSpezialisierung: w.NameFuerSpezialisierung,
BeinhaltetIn: fmt.Sprintf("bamort-container-%d", w.ContainedIn),
ContainedIn: w.ContainedIn,
Magisch: Magisch{
IstMagisch: w.IstMagisch,
Abw: w.Abw,
Ausgebrannt: w.Ausgebrannt,
},
})
}
// Ausrüstung
for i, a := range char.Ausruestung {
vtt.Ausruestung = append(vtt.Ausruestung, Ausruestung{
ImportBase: ImportBase{
ID: fmt.Sprintf("bamort-equipment-%d-%d", char.ID, i),
Name: a.Name,
},
Beschreibung: a.Beschreibung,
Gewicht: a.Gewicht,
Wert: a.Wert,
Anzahl: a.Anzahl,
Bonus: a.Bonus,
BeinhaltetIn: fmt.Sprintf("bamort-container-%d", a.ContainedIn),
ContainedIn: a.ContainedIn,
Magisch: Magisch{
IstMagisch: a.IstMagisch,
Abw: a.Abw,
Ausgebrannt: a.Ausgebrannt,
},
})
}
// Behältnisse
for i, b := range char.Behaeltnisse {
vtt.Behaeltnisse = append(vtt.Behaeltnisse, Behaeltniss{
ImportBase: ImportBase{
ID: fmt.Sprintf("bamort-container-%d-%d", char.ID, i),
Name: b.Name,
},
Beschreibung: b.Beschreibung,
Gewicht: b.Gewicht,
Wert: b.Wert,
Tragkraft: b.Tragkraft,
Volumen: b.Volumen,
BeinhaltetIn: fmt.Sprintf("bamort-container-%d", b.ContainedIn),
ContainedIn: b.ContainedIn,
Magisch: Magisch{
IstMagisch: b.IstMagisch,
Abw: b.Abw,
Ausgebrannt: b.Ausgebrannt,
},
})
}
// Transportmittel
for i, tm := range char.Transportmittel {
vtt.Transportmittel = append(vtt.Transportmittel, Transportation{
ImportBase: ImportBase{
ID: fmt.Sprintf("bamort-transport-%d-%d", char.ID, i),
Name: tm.Name,
},
Beschreibung: tm.Beschreibung,
Gewicht: int(tm.Gewicht),
Wert: tm.Wert,
Tragkraft: tm.Tragkraft,
BeinhaltetIn: fmt.Sprintf("bamort-container-%d", tm.ContainedIn),
ContainedIn: tm.ContainedIn,
Magisch: Magisch{
IstMagisch: tm.IstMagisch,
Abw: tm.Abw,
Ausgebrannt: tm.Ausgebrannt,
},
})
}
return vtt, nil
}
// ExportCharToVTTFile exports a character to VTT JSON file
func ExportCharToVTTFile(char *models.Char, filename string) error {
vtt, err := ExportCharToVTT(char)
if err != nil {
return err
}
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
return encoder.Encode(vtt)
}
// ExportSpellsToCSV exports spell master data to CSV format
func ExportSpellsToCSV(spells []models.Spell, filename string) error {
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
writer := csv.NewWriter(file)
defer writer.Flush()
// Header
header := []string{
"Nr", "game_system", "name", "Beschreibung", "Quelle", "source_id",
"page_number", "bonus", "stufe", "ap", "Art", "Zauberdauer",
"Reichweite", "Wirkungsziel", "Wirkungsbereich", "Wirkungsdauer",
"Ursprung", "Category", "learning_category", "Agens", "Reagens",
"Material (Kosten)",
}
if err := writer.Write(header); err != nil {
return err
}
// Data rows
for i, spell := range spells {
row := []string{
strconv.Itoa(i + 1),
spell.GameSystem,
spell.Name,
spell.Beschreibung,
spell.Quelle,
strconv.Itoa(int(spell.SourceID)),
strconv.Itoa(spell.PageNumber),
strconv.Itoa(spell.Bonus),
strconv.Itoa(spell.Stufe),
spell.AP,
spell.Art,
spell.Zauberdauer,
spell.Reichweite,
spell.Wirkungsziel,
spell.Wirkungsbereich,
spell.Wirkungsdauer,
spell.Ursprung,
spell.Category,
spell.LearningCategory,
"", // Agens - not in current model
"", // Reagens - not in current model
"", // Material - not in current model
}
if err := writer.Write(row); err != nil {
return err
}
}
return nil
}
// ExportCharToCSV exports a character to CSV format (MOAM-compatible)
func ExportCharToCSV(char *models.Char, filename string) error {
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
writer := csv.NewWriter(file)
writer.Comma = ';'
defer writer.Flush()
// Helper to write a row
writeRow := func(fields ...string) error {
return writer.Write(fields)
}
// Header: Name, Typ, Grad, Stand, Glaube, Herkunft
if err := writeRow("Name", "Typ", "Grad", "Stand", "Glaube", "Herkunft"); err != nil {
return err
}
stand := ""
herkunft := ""
if err := writeRow(char.Name, char.Typ, strconv.Itoa(char.Grad), stand, char.Glaube, herkunft); err != nil {
return err
}
// Spezialisierung
if err := writeRow("Spezialisierung", strings.Join(char.Spezialisierung, ";")); err != nil {
return err
}
if err := writeRow(""); err != nil { // Empty line
return err
}
// Basiseigenschaften
if err := writeRow("Basiseigenschaften"); err != nil {
return err
}
eigenschaftenMap := make(map[string]int)
for _, e := range char.Eigenschaften {
eigenschaftenMap[e.Name] = e.Value
}
if err := writeRow("St", strconv.Itoa(eigenschaftenMap["St"])); err != nil {
return err
}
if err := writeRow("Gs", strconv.Itoa(eigenschaftenMap["Gs"])); err != nil {
return err
}
if err := writeRow("Gw", strconv.Itoa(eigenschaftenMap["Gw"])); err != nil {
return err
}
if err := writeRow("Ko", strconv.Itoa(eigenschaftenMap["Ko"])); err != nil {
return err
}
if err := writeRow("In", strconv.Itoa(eigenschaftenMap["In"])); err != nil {
return err
}
if err := writeRow("Zt", strconv.Itoa(eigenschaftenMap["Zt"])); err != nil {
return err
}
if err := writeRow("Au", strconv.Itoa(eigenschaftenMap["Au"])); err != nil {
return err
}
if err := writeRow("pA", strconv.Itoa(eigenschaftenMap["PA"])); err != nil {
return err
}
if err := writeRow("Wk", strconv.Itoa(eigenschaftenMap["Wk"])); err != nil {
return err
}
// LP, AP, B, SchB, AbB, AnB
if err := writeRow("LP", "AP", "B", "SchB", "AbB", "AnB"); err != nil {
return err
}
if err := writeRow(
strconv.Itoa(char.Lp.Max),
strconv.Itoa(char.Ap.Max),
strconv.Itoa(char.B.Max),
"0", // SchB - not in model
"0", // AbB - not in model
"0", // AnB - not in model
); err != nil {
return err
}
if err := writeRow(""); err != nil {
return err
}
// Raufen, Abwehr (not in current model, use defaults)
if err := writeRow("Raufen", "Abwehr"); err != nil {
return err
}
if err := writeRow("7", "14"); err != nil {
return err
}
// Resistenz
if err := writeRow("Resistenz Geist", "Resistenz Körper"); err != nil {
return err
}
if err := writeRow("0", "0"); err != nil {
return err
}
if err := writeRow("Bonus Resistenz Geist", "Bonus Resistenz Körper"); err != nil {
return err
}
if err := writeRow("0", "0"); err != nil {
return err
}
if err := writeRow(""); err != nil {
return err
}
// Waffen
if err := writeRow("Waffe", "Erfolgswert", "Angriffsbonus", "Schadensbonus", "Abwehrbonus", "Praxispunkte"); err != nil {
return err
}
for _, w := range char.Waffen {
// Find corresponding weapon skill
erfolgswert := 0
for _, ws := range char.Waffenfertigkeiten {
if strings.Contains(w.Name, ws.Name) || strings.Contains(ws.Name, w.Name) {
erfolgswert = ws.Fertigkeitswert
break
}
}
if err := writeRow(
w.Name,
strconv.Itoa(erfolgswert),
strconv.Itoa(w.Anb),
strconv.Itoa(w.Schb),
strconv.Itoa(w.Abwb),
"0",
); err != nil {
return err
}
}
if err := writeRow(""); err != nil {
return err
}
// Rüstung (armor from equipment)
if err := writeRow("Rüstung", "RK", "Rüstungsbonus"); err != nil {
return err
}
for _, a := range char.Ausruestung {
if strings.Contains(strings.ToLower(a.Name), "rüstung") || strings.Contains(strings.ToLower(a.Name), "armor") {
if err := writeRow(a.Name, strconv.Itoa(a.Bonus), "0"); err != nil {
return err
}
}
}
if err := writeRow(""); err != nil {
return err
}
// Fertigkeiten
if err := writeRow("Fertigkeit", "Erfolgswert", "Bonus", "Praxispunkte"); err != nil {
return err
}
for _, f := range char.Fertigkeiten {
desc := f.Beschreibung
if desc != "" {
desc = " (" + desc + ")"
}
if err := writeRow(
f.Name+desc,
strconv.Itoa(f.Fertigkeitswert),
strconv.Itoa(f.Bonus),
strconv.Itoa(f.Pp),
); err != nil {
return err
}
}
if err := writeRow(""); err != nil {
return err
}
// Zauber section (if character has spells)
if len(char.Zauber) > 0 {
if err := writeRow("Zaubern", "ZauB"); err != nil {
return err
}
if err := writeRow("0", "0"); err != nil { // Placeholder values
return err
}
if err := writeRow("Zauber", "Bonus", "Praxispunkte"); err != nil {
return err
}
for _, z := range char.Zauber {
if err := writeRow(z.Name, strconv.Itoa(z.Bonus), "0"); err != nil {
return err
}
}
if err := writeRow(""); err != nil {
return err
}
}
// Ausrüstung (equipment list)
if err := writeRow("Ausrüstung"); err != nil {
return err
}
var equipmentNames []string
for _, a := range char.Ausruestung {
equipmentNames = append(equipmentNames, a.Name)
}
for _, b := range char.Behaeltnisse {
equipmentNames = append(equipmentNames, b.Name)
}
for _, t := range char.Transportmittel {
equipmentNames = append(equipmentNames, t.Name)
}
if len(equipmentNames) > 0 {
if err := writeRow(strings.Join(equipmentNames, ";")); err != nil {
return err
}
}
if err := writeRow(""); err != nil {
return err
}
// Erfahrung
if err := writeRow("Erfahrung"); err != nil {
return err
}
if err := writeRow("Erfahrungsschatz", "EP", "Gold"); err != nil {
return err
}
if err := writeRow(
strconv.Itoa(char.Erfahrungsschatz.ES),
"0", // EP not in model
"0", // Gold not in model
); err != nil {
return err
}
return nil
}