OK, hope this works
This commit is contained in:
@@ -0,0 +1,537 @@
|
||||
package importer
|
||||
|
||||
import (
|
||||
"bamort/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
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
package importer
|
||||
|
||||
import (
|
||||
"bamort/database"
|
||||
"bamort/models"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestExportChar2VTT(t *testing.T) {
|
||||
database.SetupTestDB()
|
||||
defer database.ResetTestDB()
|
||||
|
||||
// Import a test character first
|
||||
fileName := fmt.Sprintf("../testdata/%s", "VTT_Import1.json")
|
||||
char, err := ImportVTTJSON(fileName)
|
||||
assert.NoError(t, err, "Expected no error when importing char")
|
||||
|
||||
// Export the character back to VTT format
|
||||
exportedChar, err := ExportCharToVTT(char)
|
||||
assert.NoError(t, err, "Expected no error when exporting char")
|
||||
|
||||
// Basic validations
|
||||
assert.Equal(t, char.Name, exportedChar.Name)
|
||||
assert.Equal(t, char.Rasse, exportedChar.Rasse)
|
||||
assert.Equal(t, char.Typ, exportedChar.Typ)
|
||||
assert.Equal(t, char.Alter, exportedChar.Alter)
|
||||
assert.Equal(t, char.Grad, exportedChar.Grad)
|
||||
assert.Equal(t, char.Groesse, exportedChar.Groesse)
|
||||
assert.Equal(t, char.Gewicht, exportedChar.Gewicht)
|
||||
assert.Equal(t, char.Glaube, exportedChar.Glaube)
|
||||
assert.Equal(t, char.Hand, exportedChar.Hand)
|
||||
|
||||
// Check LP
|
||||
assert.Equal(t, char.Lp.Max, exportedChar.Lp.Max)
|
||||
assert.Equal(t, char.Lp.Value, exportedChar.Lp.Value)
|
||||
|
||||
// Check AP
|
||||
assert.Equal(t, char.Ap.Max, exportedChar.Ap.Max)
|
||||
assert.Equal(t, char.Ap.Value, exportedChar.Ap.Value)
|
||||
|
||||
// Check Eigenschaften
|
||||
eigenschaftenMap := getEigenschaftenMap(char)
|
||||
assert.Equal(t, eigenschaftenMap["Au"], exportedChar.Eigenschaften.Au)
|
||||
assert.Equal(t, eigenschaftenMap["Gs"], exportedChar.Eigenschaften.Gs)
|
||||
assert.Equal(t, eigenschaftenMap["Gw"], exportedChar.Eigenschaften.Gw)
|
||||
assert.Equal(t, eigenschaftenMap["In"], exportedChar.Eigenschaften.In)
|
||||
assert.Equal(t, eigenschaftenMap["Ko"], exportedChar.Eigenschaften.Ko)
|
||||
assert.Equal(t, eigenschaftenMap["PA"], exportedChar.Eigenschaften.Pa)
|
||||
assert.Equal(t, eigenschaftenMap["St"], exportedChar.Eigenschaften.St)
|
||||
assert.Equal(t, eigenschaftenMap["Wk"], exportedChar.Eigenschaften.Wk)
|
||||
assert.Equal(t, eigenschaftenMap["Zt"], exportedChar.Eigenschaften.Zt)
|
||||
|
||||
// Check Fertigkeiten exist
|
||||
assert.Greater(t, len(exportedChar.Fertigkeiten), 0, "Should have fertigkeiten")
|
||||
|
||||
// Check Waffenfertigkeiten exist
|
||||
assert.Greater(t, len(exportedChar.Waffenfertigkeiten), 0, "Should have waffenfertigkeiten")
|
||||
}
|
||||
|
||||
func TestExportChar2VTTRoundTrip(t *testing.T) {
|
||||
database.SetupTestDB()
|
||||
defer database.ResetTestDB()
|
||||
|
||||
// Import original
|
||||
fileName := fmt.Sprintf("../testdata/%s", "VTT_Import1.json")
|
||||
char1, err := ImportVTTJSON(fileName)
|
||||
assert.NoError(t, err, "Expected no error when importing char")
|
||||
|
||||
// Export to VTT
|
||||
exportedChar, err := ExportCharToVTT(char1)
|
||||
assert.NoError(t, err, "Expected no error when exporting char")
|
||||
|
||||
// Write to temp file
|
||||
tempFile, err := os.CreateTemp("", "vtt_export_*.json")
|
||||
assert.NoError(t, err, "Expected no error creating temp file")
|
||||
defer os.Remove(tempFile.Name())
|
||||
|
||||
encoder := json.NewEncoder(tempFile)
|
||||
encoder.SetIndent("", " ")
|
||||
err = encoder.Encode(exportedChar)
|
||||
assert.NoError(t, err, "Expected no error encoding JSON")
|
||||
tempFile.Close()
|
||||
|
||||
// Re-import the exported file
|
||||
char2, err := ImportVTTJSON(tempFile.Name())
|
||||
assert.NoError(t, err, "Expected no error when re-importing char")
|
||||
|
||||
// Compare key fields
|
||||
assert.Equal(t, char1.Name, char2.Name)
|
||||
assert.Equal(t, char1.Rasse, char2.Rasse)
|
||||
assert.Equal(t, char1.Typ, char2.Typ)
|
||||
assert.Equal(t, char1.Alter, char2.Alter)
|
||||
assert.Equal(t, char1.Grad, char2.Grad)
|
||||
assert.Equal(t, char1.Lp.Max, char2.Lp.Max)
|
||||
assert.Equal(t, char1.Ap.Max, char2.Ap.Max)
|
||||
}
|
||||
|
||||
// Helper function to convert char eigenschaften array to map
|
||||
func getEigenschaftenMap(char *models.Char) map[string]int {
|
||||
m := make(map[string]int)
|
||||
for _, e := range char.Eigenschaften {
|
||||
m[e.Name] = e.Value
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func TestExportSpellsToCSV(t *testing.T) {
|
||||
database.SetupTestDB()
|
||||
defer database.ResetTestDB()
|
||||
|
||||
// Get some spells from master data
|
||||
var spells []models.Spell
|
||||
database.DB.Limit(10).Find(&spells)
|
||||
|
||||
if len(spells) == 0 {
|
||||
t.Skip("No spells in test database")
|
||||
}
|
||||
|
||||
// Export to CSV
|
||||
tempFile, err := os.CreateTemp("", "spell_export_*.csv")
|
||||
assert.NoError(t, err, "Expected no error creating temp file")
|
||||
defer os.Remove(tempFile.Name())
|
||||
tempFile.Close()
|
||||
|
||||
err = ExportSpellsToCSV(spells, tempFile.Name())
|
||||
assert.NoError(t, err, "Expected no error exporting spells to CSV")
|
||||
|
||||
// Verify file exists and has content
|
||||
data, err := os.ReadFile(tempFile.Name())
|
||||
assert.NoError(t, err, "Expected no error reading CSV file")
|
||||
assert.Greater(t, len(data), 0, "CSV file should have content")
|
||||
|
||||
// Verify CSV has header
|
||||
content := string(data)
|
||||
assert.Contains(t, content, "game_system")
|
||||
assert.Contains(t, content, "name")
|
||||
assert.Contains(t, content, "Beschreibung")
|
||||
}
|
||||
|
||||
func TestExportCharToCSV(t *testing.T) {
|
||||
database.SetupTestDB()
|
||||
defer database.ResetTestDB()
|
||||
|
||||
// Import a test character first
|
||||
fileName := fmt.Sprintf("../testdata/%s", "VTT_Import1.json")
|
||||
char, err := ImportVTTJSON(fileName)
|
||||
assert.NoError(t, err, "Expected no error when importing char")
|
||||
|
||||
// Export to CSV
|
||||
tempFile, err := os.CreateTemp("", "char_export_*.csv")
|
||||
assert.NoError(t, err, "Expected no error creating temp file")
|
||||
defer os.Remove(tempFile.Name())
|
||||
tempFile.Close()
|
||||
|
||||
err = ExportCharToCSV(char, tempFile.Name())
|
||||
assert.NoError(t, err, "Expected no error exporting character to CSV")
|
||||
|
||||
// Verify file exists and has content
|
||||
data, err := os.ReadFile(tempFile.Name())
|
||||
assert.NoError(t, err, "Expected no error reading CSV file")
|
||||
assert.Greater(t, len(data), 0, "CSV file should have content")
|
||||
|
||||
// Verify CSV has expected sections
|
||||
content := string(data)
|
||||
assert.Contains(t, content, char.Name, "Should contain character name")
|
||||
assert.Contains(t, content, "Basiseigenschaften", "Should contain base attributes section")
|
||||
assert.Contains(t, content, "Fertigkeit", "Should contain skills section")
|
||||
assert.Contains(t, content, "Waffe", "Should contain weapons section")
|
||||
assert.Contains(t, content, "Erfahrung", "Should contain experience section")
|
||||
}
|
||||
|
||||
func TestExportImportWithoutMasterData(t *testing.T) {
|
||||
database.SetupTestDB()
|
||||
defer database.ResetTestDB()
|
||||
|
||||
// Import a test character first
|
||||
fileName := fmt.Sprintf("../testdata/%s", "VTT_Import1.json")
|
||||
char1, err := ImportVTTJSON(fileName)
|
||||
assert.NoError(t, err, "Expected no error when importing char")
|
||||
|
||||
// Export to VTT
|
||||
vttChar, err := ExportCharToVTT(char1)
|
||||
assert.NoError(t, err, "Expected no error when exporting char")
|
||||
|
||||
// Write to temp file
|
||||
tempFile, err := os.CreateTemp("", "vtt_export_*.json")
|
||||
assert.NoError(t, err, "Expected no error creating temp file")
|
||||
defer os.Remove(tempFile.Name())
|
||||
|
||||
encoder := json.NewEncoder(tempFile)
|
||||
encoder.SetIndent("", " ")
|
||||
err = encoder.Encode(vttChar)
|
||||
assert.NoError(t, err, "Expected no error encoding JSON")
|
||||
tempFile.Close()
|
||||
|
||||
// Clear all master data tables
|
||||
database.DB.Exec("DELETE FROM gsm_skills")
|
||||
database.DB.Exec("DELETE FROM gsm_weaponskills")
|
||||
database.DB.Exec("DELETE FROM gsm_spells")
|
||||
database.DB.Exec("DELETE FROM gsm_weapons")
|
||||
database.DB.Exec("DELETE FROM gsm_equipments")
|
||||
database.DB.Exec("DELETE FROM gsm_containers")
|
||||
database.DB.Exec("DELETE FROM gsm_transportations")
|
||||
database.DB.Exec("DELETE FROM gsm_believes")
|
||||
database.DB.Exec("DELETE FROM sqlite_sequence WHERE name LIKE 'gsm_%'")
|
||||
|
||||
// Re-import without master data
|
||||
char2, err := ImportVTTJSON(tempFile.Name())
|
||||
assert.NoError(t, err, "Expected no error when re-importing without master data")
|
||||
assert.NotNil(t, char2, "Character should be imported")
|
||||
|
||||
// Verify critical data was preserved
|
||||
assert.Equal(t, char1.Name, char2.Name, "Name should match")
|
||||
assert.Equal(t, char1.Rasse, char2.Rasse, "Race should match")
|
||||
assert.Equal(t, char1.Typ, char2.Typ, "Type should match")
|
||||
assert.Equal(t, char1.Grad, char2.Grad, "Grade should match")
|
||||
|
||||
// Verify LP/AP
|
||||
assert.Equal(t, char1.Lp.Max, char2.Lp.Max, "LP Max should match")
|
||||
assert.Equal(t, char1.Ap.Max, char2.Ap.Max, "AP Max should match")
|
||||
|
||||
// Verify skills were imported
|
||||
assert.Greater(t, len(char2.Fertigkeiten), 0, "Should have skills after reimport")
|
||||
assert.Greater(t, len(char2.Waffenfertigkeiten), 0, "Should have weapon skills after reimport")
|
||||
|
||||
// Verify weapons were imported
|
||||
assert.Greater(t, len(char2.Waffen), 0, "Should have weapons after reimport")
|
||||
|
||||
// Verify master data was created
|
||||
var skillCount, weaponSkillCount, weaponCount int64
|
||||
database.DB.Model(&models.Skill{}).Count(&skillCount)
|
||||
database.DB.Model(&models.WeaponSkill{}).Count(&weaponSkillCount)
|
||||
database.DB.Model(&models.Weapon{}).Count(&weaponCount)
|
||||
|
||||
assert.Greater(t, skillCount, int64(0), "Master data should be created for skills")
|
||||
assert.Greater(t, weaponSkillCount, int64(0), "Master data should be created for weapon skills")
|
||||
assert.Greater(t, weaponCount, int64(0), "Master data should be created for weapons")
|
||||
}
|
||||
|
||||
func TestExportImportPreservesCharacterData(t *testing.T) {
|
||||
database.SetupTestDB()
|
||||
defer database.ResetTestDB()
|
||||
|
||||
// Import a test character
|
||||
fileName := fmt.Sprintf("../testdata/%s", "VTT_Import1.json")
|
||||
char1, err := ImportVTTJSON(fileName)
|
||||
assert.NoError(t, err, "Expected no error when importing char")
|
||||
|
||||
// Store original counts and values
|
||||
originalSkillCount := len(char1.Fertigkeiten)
|
||||
originalWeaponSkillCount := len(char1.Waffenfertigkeiten)
|
||||
originalSpellCount := len(char1.Zauber)
|
||||
originalWeaponCount := len(char1.Waffen)
|
||||
originalEquipmentCount := len(char1.Ausruestung)
|
||||
originalContainerCount := len(char1.Behaeltnisse)
|
||||
originalTransportCount := len(char1.Transportmittel)
|
||||
|
||||
// Export to VTT
|
||||
vttChar, err := ExportCharToVTT(char1)
|
||||
assert.NoError(t, err, "Expected no error when exporting char")
|
||||
|
||||
// Verify export has all data
|
||||
assert.Equal(t, originalSkillCount, len(vttChar.Fertigkeiten), "All skills should be exported")
|
||||
assert.Equal(t, originalWeaponSkillCount, len(vttChar.Waffenfertigkeiten), "All weapon skills should be exported")
|
||||
assert.Equal(t, originalSpellCount, len(vttChar.Zauber), "All spells should be exported")
|
||||
assert.Equal(t, originalWeaponCount, len(vttChar.Waffen), "All weapons should be exported")
|
||||
assert.Equal(t, originalEquipmentCount, len(vttChar.Ausruestung), "All equipment should be exported")
|
||||
assert.Equal(t, originalContainerCount, len(vttChar.Behaeltnisse), "All containers should be exported")
|
||||
assert.Equal(t, originalTransportCount, len(vttChar.Transportmittel), "All transportation should be exported")
|
||||
|
||||
// Verify specific skill data is preserved
|
||||
if originalSkillCount > 0 {
|
||||
assert.NotEmpty(t, vttChar.Fertigkeiten[0].Name, "Skill name should be exported")
|
||||
assert.GreaterOrEqual(t, vttChar.Fertigkeiten[0].Fertigkeitswert, 0, "Skill value should be exported")
|
||||
}
|
||||
|
||||
// Verify weapon data is preserved
|
||||
if originalWeaponCount > 0 {
|
||||
assert.NotEmpty(t, vttChar.Waffen[0].Name, "Weapon name should be exported")
|
||||
assert.GreaterOrEqual(t, vttChar.Waffen[0].Gewicht, float64(0), "Weapon weight should be exported")
|
||||
}
|
||||
|
||||
// Write to temp file
|
||||
tempFile, err := os.CreateTemp("", "vtt_export_*.json")
|
||||
assert.NoError(t, err, "Expected no error creating temp file")
|
||||
defer os.Remove(tempFile.Name())
|
||||
|
||||
encoder := json.NewEncoder(tempFile)
|
||||
encoder.SetIndent("", " ")
|
||||
err = encoder.Encode(vttChar)
|
||||
assert.NoError(t, err, "Expected no error encoding JSON")
|
||||
tempFile.Close()
|
||||
|
||||
// Clear master data
|
||||
database.DB.Exec("DELETE FROM gsm_skills")
|
||||
database.DB.Exec("DELETE FROM gsm_weaponskills")
|
||||
database.DB.Exec("DELETE FROM gsm_spells")
|
||||
database.DB.Exec("DELETE FROM gsm_weapons")
|
||||
database.DB.Exec("DELETE FROM gsm_equipments")
|
||||
database.DB.Exec("DELETE FROM gsm_containers")
|
||||
database.DB.Exec("DELETE FROM gsm_transportations")
|
||||
|
||||
// Re-import
|
||||
char2, err := ImportVTTJSON(tempFile.Name())
|
||||
assert.NoError(t, err, "Expected no error when re-importing")
|
||||
|
||||
// Verify all data was preserved
|
||||
assert.Equal(t, originalSkillCount, len(char2.Fertigkeiten), "All skills should be reimported")
|
||||
assert.Equal(t, originalWeaponSkillCount, len(char2.Waffenfertigkeiten), "All weapon skills should be reimported")
|
||||
assert.Equal(t, originalSpellCount, len(char2.Zauber), "All spells should be reimported")
|
||||
assert.Equal(t, originalWeaponCount, len(char2.Waffen), "All weapons should be reimported")
|
||||
assert.Equal(t, originalEquipmentCount, len(char2.Ausruestung), "All equipment should be reimported")
|
||||
assert.Equal(t, originalContainerCount, len(char2.Behaeltnisse), "All containers should be reimported")
|
||||
assert.Equal(t, originalTransportCount, len(char2.Transportmittel), "All transportation should be reimported")
|
||||
|
||||
// Verify specific values match
|
||||
if originalSkillCount > 0 {
|
||||
skill1 := findSkillByName(char1.Fertigkeiten, char1.Fertigkeiten[0].Name)
|
||||
skill2 := findSkillByName(char2.Fertigkeiten, char1.Fertigkeiten[0].Name)
|
||||
assert.NotNil(t, skill1, "Original skill should exist")
|
||||
assert.NotNil(t, skill2, "Reimported skill should exist")
|
||||
assert.Equal(t, skill1.Fertigkeitswert, skill2.Fertigkeitswert, "Skill values should match")
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to find skill by name
|
||||
func findSkillByName(skills []models.SkFertigkeit, name string) *models.SkFertigkeit {
|
||||
for i := range skills {
|
||||
if skills[i].Name == name {
|
||||
return &skills[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -200,3 +200,213 @@ func ImportSpellCSVHandler(c *gin.Context) {
|
||||
"total_spells": spellCount,
|
||||
})
|
||||
}
|
||||
|
||||
// ExportCharacterVTTHandler exports a character to VTT JSON format
|
||||
// @Summary Export character to VTT JSON format
|
||||
// @Description Exports a character to VTT JSON format for use in other systems
|
||||
// @Tags importer
|
||||
// @Produce json
|
||||
// @Param id path int true "Character ID"
|
||||
// @Success 200 {object} CharacterImport "Export successful"
|
||||
// @Failure 400 {object} map[string]interface{} "Bad request - invalid character ID"
|
||||
// @Failure 404 {object} map[string]interface{} "Character not found"
|
||||
// @Failure 500 {object} map[string]interface{} "Internal server error - export failed"
|
||||
// @Router /api/importer/export/vtt/{id} [get]
|
||||
func ExportCharacterVTTHandler(c *gin.Context) {
|
||||
// Get character ID from URL parameter
|
||||
charID := c.Param("id")
|
||||
if charID == "" {
|
||||
respondWithError(c, http.StatusBadRequest, "Character ID is required")
|
||||
return
|
||||
}
|
||||
|
||||
// Load character from database
|
||||
var char models.Char
|
||||
err := database.DB.Preload("Eigenschaften").
|
||||
Preload("Fertigkeiten").
|
||||
Preload("Waffenfertigkeiten").
|
||||
Preload("Zauber").
|
||||
Preload("Waffen").
|
||||
Preload("Ausruestung").
|
||||
Preload("Behaeltnisse").
|
||||
Preload("Transportmittel").
|
||||
First(&char, charID).Error
|
||||
|
||||
if err != nil {
|
||||
respondWithError(c, http.StatusNotFound, "Character not found")
|
||||
return
|
||||
}
|
||||
|
||||
// Export to VTT format
|
||||
vttChar, err := ExportCharToVTT(&char)
|
||||
if err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, fmt.Sprintf("Failed to export character: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// Return as JSON
|
||||
c.JSON(http.StatusOK, vttChar)
|
||||
}
|
||||
|
||||
// ExportCharacterVTTFileHandler exports a character to VTT JSON file
|
||||
// @Summary Export character to VTT JSON file
|
||||
// @Description Exports a character to VTT JSON file and returns it as a download
|
||||
// @Tags importer
|
||||
// @Produce json
|
||||
// @Param id path int true "Character ID"
|
||||
// @Success 200 {file} file "VTT JSON file"
|
||||
// @Failure 400 {object} map[string]interface{} "Bad request - invalid character ID"
|
||||
// @Failure 404 {object} map[string]interface{} "Character not found"
|
||||
// @Failure 500 {object} map[string]interface{} "Internal server error - export failed"
|
||||
// @Router /api/importer/export/vtt/{id}/file [get]
|
||||
func ExportCharacterVTTFileHandler(c *gin.Context) {
|
||||
// Get character ID from URL parameter
|
||||
charID := c.Param("id")
|
||||
if charID == "" {
|
||||
respondWithError(c, http.StatusBadRequest, "Character ID is required")
|
||||
return
|
||||
}
|
||||
|
||||
// Load character from database
|
||||
var char models.Char
|
||||
err := database.DB.Preload("Eigenschaften").
|
||||
Preload("Fertigkeiten").
|
||||
Preload("Waffenfertigkeiten").
|
||||
Preload("Zauber").
|
||||
Preload("Waffen").
|
||||
Preload("Ausruestung").
|
||||
Preload("Behaeltnisse").
|
||||
Preload("Transportmittel").
|
||||
First(&char, charID).Error
|
||||
|
||||
if err != nil {
|
||||
respondWithError(c, http.StatusNotFound, "Character not found")
|
||||
return
|
||||
}
|
||||
|
||||
// Create temp file
|
||||
tempFile, err := os.CreateTemp("", fmt.Sprintf("vtt_export_%s_*.json", char.Name))
|
||||
if err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, "Failed to create temp file")
|
||||
return
|
||||
}
|
||||
defer os.Remove(tempFile.Name())
|
||||
tempFile.Close()
|
||||
|
||||
// Export to file
|
||||
err = ExportCharToVTTFile(&char, tempFile.Name())
|
||||
if err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, fmt.Sprintf("Failed to export character: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// Send file as download
|
||||
filename := fmt.Sprintf("%s_vtt_export.json", char.Name)
|
||||
c.FileAttachment(tempFile.Name(), filename)
|
||||
}
|
||||
|
||||
// ExportSpellsCSVHandler exports spell master data to CSV file
|
||||
// @Summary Export spells to CSV file
|
||||
// @Description Exports spell master data to CSV format
|
||||
// @Tags importer
|
||||
// @Produce text/csv
|
||||
// @Param game_system query string false "Game system filter (e.g., 'midgard')"
|
||||
// @Success 200 {file} file "CSV file"
|
||||
// @Failure 500 {object} map[string]interface{} "Internal server error - export failed"
|
||||
// @Router /api/importer/export/spells/csv [get]
|
||||
func ExportSpellsCSVHandler(c *gin.Context) {
|
||||
gameSystem := c.Query("game_system")
|
||||
|
||||
// Load spells from database
|
||||
var spells []models.Spell
|
||||
query := database.DB
|
||||
if gameSystem != "" {
|
||||
query = query.Where("game_system = ?", gameSystem)
|
||||
}
|
||||
err := query.Find(&spells).Error
|
||||
|
||||
if err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, "Failed to load spells")
|
||||
return
|
||||
}
|
||||
|
||||
// Create temp file
|
||||
tempFile, err := os.CreateTemp("", "spells_export_*.csv")
|
||||
if err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, "Failed to create temp file")
|
||||
return
|
||||
}
|
||||
defer os.Remove(tempFile.Name())
|
||||
tempFile.Close()
|
||||
|
||||
// Export to CSV
|
||||
err = ExportSpellsToCSV(spells, tempFile.Name())
|
||||
if err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, fmt.Sprintf("Failed to export spells: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// Send file as download
|
||||
filename := "spells_export.csv"
|
||||
if gameSystem != "" {
|
||||
filename = fmt.Sprintf("spells_%s_export.csv", gameSystem)
|
||||
}
|
||||
c.FileAttachment(tempFile.Name(), filename)
|
||||
}
|
||||
|
||||
// ExportCharacterCSVHandler exports a character to CSV file
|
||||
// @Summary Export character to CSV file
|
||||
// @Description Exports a character to CSV format (MOAM-compatible)
|
||||
// @Tags importer
|
||||
// @Produce text/csv
|
||||
// @Param id path int true "Character ID"
|
||||
// @Success 200 {file} file "CSV file"
|
||||
// @Failure 400 {object} map[string]interface{} "Bad request - invalid character ID"
|
||||
// @Failure 404 {object} map[string]interface{} "Character not found"
|
||||
// @Failure 500 {object} map[string]interface{} "Internal server error - export failed"
|
||||
// @Router /api/importer/export/csv/{id} [get]
|
||||
func ExportCharacterCSVHandler(c *gin.Context) {
|
||||
// Get character ID from URL parameter
|
||||
charID := c.Param("id")
|
||||
if charID == "" {
|
||||
respondWithError(c, http.StatusBadRequest, "Character ID is required")
|
||||
return
|
||||
}
|
||||
|
||||
// Load character from database
|
||||
var char models.Char
|
||||
err := database.DB.Preload("Eigenschaften").
|
||||
Preload("Fertigkeiten").
|
||||
Preload("Waffenfertigkeiten").
|
||||
Preload("Zauber").
|
||||
Preload("Waffen").
|
||||
Preload("Ausruestung").
|
||||
Preload("Behaeltnisse").
|
||||
Preload("Transportmittel").
|
||||
First(&char, charID).Error
|
||||
|
||||
if err != nil {
|
||||
respondWithError(c, http.StatusNotFound, "Character not found")
|
||||
return
|
||||
}
|
||||
|
||||
// Create temp file
|
||||
tempFile, err := os.CreateTemp("", fmt.Sprintf("csv_export_%s_*.csv", char.Name))
|
||||
if err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, "Failed to create temp file")
|
||||
return
|
||||
}
|
||||
defer os.Remove(tempFile.Name())
|
||||
tempFile.Close()
|
||||
|
||||
// Export to CSV
|
||||
err = ExportCharToCSV(&char, tempFile.Name())
|
||||
if err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, fmt.Sprintf("Failed to export character: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// Send file as download
|
||||
filename := fmt.Sprintf("%s_export.csv", char.Name)
|
||||
c.FileAttachment(tempFile.Name(), filename)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,15 @@ import (
|
||||
|
||||
func RegisterRoutes(r *gin.RouterGroup) {
|
||||
charGrp := r.Group("/importer")
|
||||
|
||||
// Import routes
|
||||
charGrp.POST("/upload", UploadFiles)
|
||||
charGrp.POST("/spells/csv", ImportSpellCSVHandler)
|
||||
|
||||
// Export routes
|
||||
exportGrp := charGrp.Group("/export")
|
||||
exportGrp.GET("/vtt/:id", ExportCharacterVTTHandler)
|
||||
exportGrp.GET("/vtt/:id/file", ExportCharacterVTTFileHandler)
|
||||
exportGrp.GET("/csv/:id", ExportCharacterCSVHandler)
|
||||
exportGrp.GET("/spells/csv", ExportSpellsCSVHandler)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user