Files
bamort/backend/gsmaster/export_import.go
T

2408 lines
79 KiB
Go
Raw Normal View History

package gsmaster
import (
"bamort/database"
"bamort/models"
"encoding/json"
"fmt"
"os"
"path/filepath"
"gorm.io/gorm"
)
// ExportableCategoryDifficulty represents a category/difficulty combination for a skill
type ExportableCategoryDifficulty struct {
Category string `json:"category"`
Difficulty string `json:"difficulty"`
LearnCost int `json:"learn_cost"`
}
// ExportableSkill represents a skill without database IDs for export
type ExportableSkill struct {
Name string `json:"name"`
GameSystem string `json:"game_system"`
GameSystemId uint `json:"game_system_id"`
Beschreibung string `json:"beschreibung"`
SourceCode string `json:"source_code"` // Instead of SourceID
PageNumber int `json:"page_number"`
Initialwert int `json:"initialwert"`
BasisWert int `json:"basiswert"`
Bonuseigenschaft string `json:"bonuseigenschaft"`
Improvable bool `json:"improvable"`
InnateSkill bool `json:"innate_skill"`
Category string `json:"category"` // Deprecated: use CategoriesDifficulties
Difficulty string `json:"difficulty"` // Deprecated: use CategoriesDifficulties
CategoriesDifficulties []ExportableCategoryDifficulty `json:"categories_difficulties"` // All category/difficulty combinations
}
// ExportableSource represents a source for export
type ExportableSource struct {
Code string `json:"code"`
Name string `json:"name"`
FullName string `json:"full_name"`
Edition string `json:"edition"`
Publisher string `json:"publisher"`
PublishYear int `json:"publish_year"`
Description string `json:"description"`
IsCore bool `json:"is_core"`
IsActive bool `json:"is_active"`
GameSystem string `json:"game_system"`
}
// ExportableSkillCategory represents a skill category for export
type ExportableSkillCategory struct {
Name string `json:"name"`
GameSystem string `json:"game_system"`
SourceCode string `json:"source_code"`
}
// ExportableSkillDifficulty represents a skill difficulty for export
type ExportableSkillDifficulty struct {
Name string `json:"name"`
GameSystem string `json:"game_system"`
}
// ExportableSkillCategoryDifficulty represents the relationship for export
type ExportableSkillCategoryDifficulty struct {
SkillName string `json:"skill_name"`
SkillSystem string `json:"skill_system"`
CategoryName string `json:"category_name"`
CategorySystem string `json:"category_system"`
DifficultyName string `json:"difficulty_name"`
DifficultySystem string `json:"difficulty_system"`
LearnCost int `json:"learn_cost"`
}
// ExportableWeaponSkillCategoryDifficulty represents the weapon skill relationship for export
type ExportableWeaponSkillCategoryDifficulty struct {
WeaponSkillName string `json:"weapon_skill_name"`
SkillSystem string `json:"skill_system"`
CategoryName string `json:"category_name"`
CategorySystem string `json:"category_system"`
DifficultyName string `json:"difficulty_name"`
DifficultySystem string `json:"difficulty_system"`
LearnCost int `json:"learn_cost"`
}
// ExportableSpell represents a spell for export
type ExportableSpell struct {
Name string `json:"name"`
GameSystem string `json:"game_system"`
2026-01-28 08:51:35 +01:00
GameSystemId uint `json:"game_system_id"`
Beschreibung string `json:"beschreibung"`
SourceCode string `json:"source_code"`
PageNumber int `json:"page_number"`
Bonus int `json:"bonus"`
Stufe int `json:"level"`
AP string `json:"ap"`
Art string `json:"art"`
Zauberdauer string `json:"zauberdauer"`
Reichweite string `json:"reichweite"`
Wirkungsziel string `json:"wirkungsziel"`
Wirkungsbereich string `json:"wirkungsbereich"`
Wirkungsdauer string `json:"wirkungsdauer"`
Ursprung string `json:"ursprung"`
Category string `json:"category"`
LearningCategory string `json:"learning_category"`
}
// ExportableCharacterClass represents a character class for export
type ExportableCharacterClass struct {
Code string `json:"code"`
Name string `json:"name"`
Description string `json:"description"`
SourceCode string `json:"source_code"`
GameSystem string `json:"game_system"`
}
// ExportableSpellSchool represents a spell school for export
type ExportableSpellSchool struct {
Name string `json:"name"`
Description string `json:"description"`
SourceCode string `json:"source_code"`
GameSystem string `json:"game_system"`
}
// ExportableClassCategoryEPCost represents class-category EP costs for export
type ExportableClassCategoryEPCost struct {
CharacterClassCode string `json:"character_class_code"`
SkillCategoryName string `json:"skill_category_name"`
EPPerTE int `json:"ep_per_te"`
}
// ExportableClassSpellSchoolEPCost represents class-spell school EP costs for export
type ExportableClassSpellSchoolEPCost struct {
CharacterClassCode string `json:"character_class_code"`
SpellSchoolName string `json:"spell_school_name"`
EPPerLE int `json:"ep_per_le"`
}
// ExportableSpellLevelLECost represents spell level LE costs for export
type ExportableSpellLevelLECost struct {
Level int `json:"level"`
LERequired int `json:"le_required"`
GameSystem string `json:"game_system"`
GameSystemId uint `json:"game_system_id"`
}
// ExportableSkillImprovementCost represents skill improvement costs for export
type ExportableSkillImprovementCost struct {
SkillName string `json:"skill_name"`
SkillSystem string `json:"skill_system"`
CategoryName string `json:"category_name"`
CategorySystem string `json:"category_system"`
DifficultyName string `json:"difficulty_name"`
DifficultySystem string `json:"difficulty_system"`
CurrentLevel int `json:"current_level"`
TERequired int `json:"te_required"`
}
// ExportableWeaponSkill represents a weapon skill for export
type ExportableWeaponSkill struct {
Name string `json:"name"`
GameSystem string `json:"game_system"`
GameSystemId uint `json:"game_system_id"`
Beschreibung string `json:"beschreibung"`
SourceCode string `json:"source_code"`
PageNumber int `json:"page_number"`
Initialwert int `json:"initialwert"`
BasisWert int `json:"basiswert"`
Bonuseigenschaft string `json:"bonuseigenschaft"`
Improvable bool `json:"improvable"`
InnateSkill bool `json:"innate_skill"`
Category string `json:"category"` // Deprecated: use CategoriesDifficulties
Difficulty string `json:"difficulty"` // Deprecated: use CategoriesDifficulties
CategoriesDifficulties []ExportableCategoryDifficulty `json:"categories_difficulties"` // All category/difficulty combinations
}
// ExportableEquipment represents equipment for export
type ExportableEquipment struct {
Name string `json:"name"`
GameSystem string `json:"game_system"`
Beschreibung string `json:"beschreibung"`
SourceCode string `json:"source_code"`
PageNumber int `json:"page_number"`
Gewicht float64 `json:"gewicht"`
Wert float64 `json:"wert"`
PersonalItem bool `json:"personal_item"`
}
// ExportableWeapon represents a weapon for export
type ExportableWeapon struct {
Name string `json:"name"`
GameSystem string `json:"game_system"`
Beschreibung string `json:"beschreibung"`
SourceCode string `json:"source_code"`
PageNumber int `json:"page_number"`
Gewicht float64 `json:"gewicht"`
Wert float64 `json:"wert"`
PersonalItem bool `json:"personal_item"`
SkillRequired string `json:"skill_required"`
Damage string `json:"damage"`
RangeNear int `json:"range_near"`
RangeMiddle int `json:"range_middle"`
RangeFar int `json:"range_far"`
}
// ExportableContainer represents a container for export
type ExportableContainer struct {
Name string `json:"name"`
GameSystem string `json:"game_system"`
Beschreibung string `json:"beschreibung"`
SourceCode string `json:"source_code"`
PageNumber int `json:"page_number"`
Gewicht float64 `json:"gewicht"`
Wert float64 `json:"wert"`
PersonalItem bool `json:"personal_item"`
Tragkraft float64 `json:"tragkraft"`
Volumen float64 `json:"volumen"`
}
// ExportableTransportation represents transportation for export
type ExportableTransportation struct {
Name string `json:"name"`
GameSystem string `json:"game_system"`
Beschreibung string `json:"beschreibung"`
SourceCode string `json:"source_code"`
PageNumber int `json:"page_number"`
Gewicht float64 `json:"gewicht"`
Wert float64 `json:"wert"`
PersonalItem bool `json:"personal_item"`
Tragkraft float64 `json:"tragkraft"`
Volumen float64 `json:"volumen"`
}
// ExportableBelieve represents a belief system for export
type ExportableBelieve struct {
Name string `json:"name"`
GameSystem string `json:"game_system"`
Beschreibung string `json:"beschreibung"`
SourceCode string `json:"source_code"`
PageNumber int `json:"page_number"`
}
// ExportableClassCategoryLearningPoints represents learning points allocation for export
type ExportableClassCategoryLearningPoints struct {
ClassName string `json:"class_name"`
ClassSystem string `json:"class_system"`
CategoryName string `json:"category_name"`
CategorySystem string `json:"category_system"`
Points int `json:"points"`
}
// ExportableClassSpellPoints represents spell points for export
type ExportableClassSpellPoints struct {
ClassName string `json:"class_name"`
ClassSystem string `json:"class_system"`
SpellPoints int `json:"spell_points"`
}
// ExportableClassTypicalSkill represents typical skills for export
type ExportableClassTypicalSkill struct {
ClassName string `json:"class_name"`
ClassSystem string `json:"class_system"`
SkillName string `json:"skill_name"`
SkillSystem string `json:"skill_system"`
Bonus int `json:"bonus"`
Attribute string `json:"attribute,omitempty"`
Notes string `json:"notes,omitempty"`
}
// ExportableClassTypicalSpell represents typical spells for export
type ExportableClassTypicalSpell struct {
ClassName string `json:"class_name"`
ClassSystem string `json:"class_system"`
SpellName string `json:"spell_name"`
SpellSystem string `json:"spell_system"`
Notes string `json:"notes,omitempty"`
}
2026-01-14 22:14:23 +01:00
// ExportableMiscLookup represents miscellaneous lookup values for export
type ExportableMiscLookup struct {
2026-01-28 07:44:17 +01:00
Key string `json:"key"`
Value string `json:"value"`
SourceCode string `json:"source_code"`
PageNumber int `json:"page_number"`
GameSystemId uint `json:"game_system_id"`
2026-01-14 22:14:23 +01:00
}
// ExportSkills exports all skills to a JSON file
func ExportSkills(outputDir string) error {
var skills []models.Skill
if err := database.DB.Find(&skills).Error; err != nil {
return fmt.Errorf("failed to fetch skills: %w", err)
}
2026-01-05 09:56:12 +01:00
sourceMap := buildSourceMap()
// Get all skill category difficulties
var scds []models.SkillCategoryDifficulty
database.DB.Preload("SkillCategory").Preload("SkillDifficulty").Find(&scds)
// Build map of skill_id -> []category/difficulty combinations
scdMap := make(map[uint][]ExportableCategoryDifficulty)
for _, scd := range scds {
scdMap[scd.SkillID] = append(scdMap[scd.SkillID], ExportableCategoryDifficulty{
Category: scd.SkillCategory.Name,
Difficulty: scd.SkillDifficulty.Name,
LearnCost: scd.LearnCost,
})
}
// Convert to exportable format
exportable := make([]ExportableSkill, len(skills))
for i, skill := range skills {
exportable[i] = ExportableSkill{
Name: skill.Name,
GameSystem: skill.GameSystem,
GameSystemId: skill.GameSystemId,
Beschreibung: skill.Beschreibung,
SourceCode: sourceMap[skill.SourceID],
PageNumber: skill.PageNumber,
Initialwert: skill.Initialwert,
BasisWert: skill.BasisWert,
Bonuseigenschaft: skill.Bonuseigenschaft,
Improvable: skill.Improvable,
InnateSkill: skill.InnateSkill,
Category: skill.Category,
Difficulty: skill.Difficulty,
CategoriesDifficulties: scdMap[skill.ID],
}
}
return writeJSON(filepath.Join(outputDir, "skills.json"), exportable)
}
// ImportSkills imports skills from a JSON file
func ImportSkills(inputDir string) error {
var exportable []ExportableSkill
if err := readJSON(filepath.Join(inputDir, "skills.json"), &exportable); err != nil {
return err
}
if err := database.DB.AutoMigrate(&models.Skill{}); err != nil {
return fmt.Errorf("failed to migrate skills table: %w", err)
}
2026-01-05 09:56:12 +01:00
sourceMap := buildSourceMapReverse()
categoryMap := buildCategoryMap()
difficultyMap := buildDifficultyMap()
for _, exp := range exportable {
var skill models.Skill
gs := models.GetGameSystem(exp.GameSystemId, exp.GameSystem)
effectiveName := exp.GameSystem
if effectiveName == "" {
effectiveName = gs.Name
}
effectiveID := gs.ID
if effectiveID == 0 {
effectiveID = models.GetGameSystem(0, "").ID
}
result := database.DB.Where("name = ? AND (game_system = ? OR game_system_id = ?)", exp.Name, effectiveName, effectiveID).First(&skill)
sourceID := sourceMap[exp.SourceCode]
if result.Error == gorm.ErrRecordNotFound {
// Create new skill
skill = models.Skill{
Name: exp.Name,
GameSystem: effectiveName,
GameSystemId: effectiveID,
Beschreibung: exp.Beschreibung,
SourceID: sourceID,
PageNumber: exp.PageNumber,
Initialwert: exp.Initialwert,
BasisWert: exp.BasisWert,
Bonuseigenschaft: exp.Bonuseigenschaft,
Improvable: exp.Improvable,
InnateSkill: exp.InnateSkill,
Category: exp.Category,
Difficulty: exp.Difficulty,
}
if err := database.DB.Create(&skill).Error; err != nil {
return fmt.Errorf("failed to create skill %s: %w", exp.Name, err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query skill %s: %w", exp.Name, result.Error)
} else {
// Update existing skill
skill.Beschreibung = exp.Beschreibung
skill.SourceID = sourceID
skill.PageNumber = exp.PageNumber
skill.Initialwert = exp.Initialwert
skill.BasisWert = exp.BasisWert
skill.Bonuseigenschaft = exp.Bonuseigenschaft
skill.Improvable = exp.Improvable
skill.InnateSkill = exp.InnateSkill
skill.Category = exp.Category
skill.Difficulty = exp.Difficulty
skill.GameSystemId = effectiveID
skill.GameSystem = effectiveName
if err := database.DB.Save(&skill).Error; err != nil {
return fmt.Errorf("failed to update skill %s: %w", exp.Name, err)
}
}
// Import category/difficulty combinations if present
if len(exp.CategoriesDifficulties) > 0 {
// Delete existing relationships for this skill
database.DB.Where("skill_id = ?", skill.ID).Delete(&models.SkillCategoryDifficulty{})
// Create new relationships
for _, cd := range exp.CategoriesDifficulties {
categoryID := categoryMap[effectiveName][cd.Category]
difficultyID := difficultyMap[effectiveName][cd.Difficulty]
if categoryID == 0 || difficultyID == 0 {
continue // Skip if category or difficulty not found
}
scd := models.SkillCategoryDifficulty{
SkillID: skill.ID,
SkillCategoryID: categoryID,
SkillDifficultyID: difficultyID,
LearnCost: cd.LearnCost,
SCategory: cd.Category,
SDifficulty: cd.Difficulty,
}
if err := database.DB.Create(&scd).Error; err != nil {
return fmt.Errorf("failed to create skill category difficulty for %s: %w", exp.Name, err)
}
}
}
}
return nil
}
// ExportSources exports all sources to a JSON file
func ExportSources(outputDir string) error {
var sources []models.Source
if err := database.DB.Find(&sources).Error; err != nil {
return fmt.Errorf("failed to fetch sources: %w", err)
}
exportable := make([]ExportableSource, len(sources))
for i, s := range sources {
exportable[i] = ExportableSource{
Code: s.Code,
Name: s.Name,
FullName: s.FullName,
Edition: s.Edition,
Publisher: s.Publisher,
PublishYear: s.PublishYear,
Description: s.Description,
IsCore: s.IsCore,
IsActive: s.IsActive,
GameSystem: s.GameSystem,
}
}
return writeJSON(filepath.Join(outputDir, "sources.json"), exportable)
}
// ImportSources imports sources from a JSON file
func ImportSources(inputDir string) error {
var exportable []ExportableSource
if err := readJSON(filepath.Join(inputDir, "sources.json"), &exportable); err != nil {
return err
}
for _, exp := range exportable {
2026-01-05 09:56:12 +01:00
source := models.Source{
Code: exp.Code,
Name: exp.Name,
FullName: exp.FullName,
Edition: exp.Edition,
Publisher: exp.Publisher,
PublishYear: exp.PublishYear,
Description: exp.Description,
IsCore: exp.IsCore,
IsActive: exp.IsActive,
GameSystem: exp.GameSystem,
}
2026-01-05 09:56:12 +01:00
if err := findOrCreateByCode(exp.Code, &source, "source"); err != nil {
return err
}
}
return nil
}
// ExportSkillCategoryDifficulties exports skill-category-difficulty relationships
func ExportSkillCategoryDifficulties(outputDir string) error {
var scds []models.SkillCategoryDifficulty
if err := database.DB.Find(&scds).Error; err != nil {
return fmt.Errorf("failed to fetch skill category difficulties: %w", err)
}
exportable := make([]ExportableSkillCategoryDifficulty, len(scds))
for i, scd := range scds {
var skill models.Skill
var category models.SkillCategory
var difficulty models.SkillDifficulty
database.DB.First(&skill, scd.SkillID)
database.DB.First(&category, scd.SkillCategoryID)
database.DB.First(&difficulty, scd.SkillDifficultyID)
exportable[i] = ExportableSkillCategoryDifficulty{
SkillName: skill.Name,
SkillSystem: skill.GameSystem,
CategoryName: category.Name,
CategorySystem: category.GameSystem,
DifficultyName: difficulty.Name,
DifficultySystem: difficulty.GameSystem,
LearnCost: scd.LearnCost,
}
}
return writeJSON(filepath.Join(outputDir, "skill_category_difficulties.json"), exportable)
}
// ImportSkillCategoryDifficulties imports skill-category-difficulty relationships
func ImportSkillCategoryDifficulties(inputDir string) error {
var exportable []ExportableSkillCategoryDifficulty
if err := readJSON(filepath.Join(inputDir, "skill_category_difficulties.json"), &exportable); err != nil {
return err
}
for _, exp := range exportable {
// Find the skill
var skill models.Skill
if err := database.DB.Where("name = ? AND game_system = ?", exp.SkillName, exp.SkillSystem).First(&skill).Error; err != nil {
return fmt.Errorf("skill not found: %s/%s", exp.SkillName, exp.SkillSystem)
}
// Find the category
var category models.SkillCategory
if err := database.DB.Where("name = ? AND game_system = ?", exp.CategoryName, exp.CategorySystem).First(&category).Error; err != nil {
return fmt.Errorf("category not found: %s/%s", exp.CategoryName, exp.CategorySystem)
}
// Find the difficulty
var difficulty models.SkillDifficulty
if err := database.DB.Where("name = ? AND game_system = ?", exp.DifficultyName, exp.DifficultySystem).First(&difficulty).Error; err != nil {
return fmt.Errorf("difficulty not found: %s/%s", exp.DifficultyName, exp.DifficultySystem)
}
// Check if relationship exists
var scd models.SkillCategoryDifficulty
result := database.DB.Where("skill_id = ? AND skill_category_id = ? AND skill_difficulty_id = ?",
skill.ID, category.ID, difficulty.ID).First(&scd)
if result.Error == gorm.ErrRecordNotFound {
// Create new relationship
scd = models.SkillCategoryDifficulty{
SkillID: skill.ID,
SkillCategoryID: category.ID,
SkillDifficultyID: difficulty.ID,
LearnCost: exp.LearnCost,
SCategory: category.Name,
SDifficulty: difficulty.Name,
}
if err := database.DB.Create(&scd).Error; err != nil {
return fmt.Errorf("failed to create relationship: %w", err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query relationship: %w", result.Error)
} else {
// Update existing relationship
scd.LearnCost = exp.LearnCost
scd.SCategory = category.Name
scd.SDifficulty = difficulty.Name
if err := database.DB.Save(&scd).Error; err != nil {
return fmt.Errorf("failed to update relationship: %w", err)
}
}
}
return nil
}
// ExportWeaponSkillCategoryDifficulties exports weapon skill-category-difficulty relationships
func ExportWeaponSkillCategoryDifficulties(outputDir string) error {
var wscds []models.WeaponSkillCategoryDifficulty
if err := database.DB.Find(&wscds).Error; err != nil {
return fmt.Errorf("failed to fetch weapon skill category difficulties: %w", err)
}
exportable := make([]ExportableWeaponSkillCategoryDifficulty, len(wscds))
for i, wscd := range wscds {
var weaponSkill models.WeaponSkill
var category models.SkillCategory
var difficulty models.SkillDifficulty
database.DB.First(&weaponSkill, wscd.WeaponSkillID)
database.DB.First(&category, wscd.SkillCategoryID)
database.DB.First(&difficulty, wscd.SkillDifficultyID)
exportable[i] = ExportableWeaponSkillCategoryDifficulty{
WeaponSkillName: weaponSkill.Name,
SkillSystem: weaponSkill.GameSystem,
CategoryName: category.Name,
CategorySystem: category.GameSystem,
DifficultyName: difficulty.Name,
DifficultySystem: difficulty.GameSystem,
LearnCost: wscd.LearnCost,
}
}
return writeJSON(filepath.Join(outputDir, "weaponskill_category_difficulties.json"), exportable)
}
// ImportWeaponSkillCategoryDifficulties imports weapon skill-category-difficulty relationships
func ImportWeaponSkillCategoryDifficulties(inputDir string) error {
var exportable []ExportableWeaponSkillCategoryDifficulty
if err := readJSON(filepath.Join(inputDir, "weaponskill_category_difficulties.json"), &exportable); err != nil {
return err
}
for _, exp := range exportable {
// Find the weapon skill
var weaponSkill models.WeaponSkill
if err := database.DB.Where("name = ? AND game_system = ?", exp.WeaponSkillName, exp.SkillSystem).First(&weaponSkill).Error; err != nil {
return fmt.Errorf("weapon skill not found: %s/%s", exp.WeaponSkillName, exp.SkillSystem)
}
// Find the category
var category models.SkillCategory
if err := database.DB.Where("name = ? AND game_system = ?", exp.CategoryName, exp.CategorySystem).First(&category).Error; err != nil {
return fmt.Errorf("category not found: %s/%s", exp.CategoryName, exp.CategorySystem)
}
// Find the difficulty
var difficulty models.SkillDifficulty
if err := database.DB.Where("name = ? AND game_system = ?", exp.DifficultyName, exp.DifficultySystem).First(&difficulty).Error; err != nil {
return fmt.Errorf("difficulty not found: %s/%s", exp.DifficultyName, exp.DifficultySystem)
}
// Check if relationship exists
var wscd models.WeaponSkillCategoryDifficulty
result := database.DB.Where("weapon_skill_id = ? AND skill_category_id = ? AND skill_difficulty_id = ?",
weaponSkill.ID, category.ID, difficulty.ID).First(&wscd)
if result.Error == gorm.ErrRecordNotFound {
// Create new relationship
wscd = models.WeaponSkillCategoryDifficulty{
WeaponSkillID: weaponSkill.ID,
SkillCategoryID: category.ID,
SkillDifficultyID: difficulty.ID,
LearnCost: exp.LearnCost,
SCategory: category.Name,
SDifficulty: difficulty.Name,
}
if err := database.DB.Create(&wscd).Error; err != nil {
return fmt.Errorf("failed to create relationship: %w", err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query relationship: %w", result.Error)
} else {
// Update existing relationship
wscd.LearnCost = exp.LearnCost
wscd.SCategory = category.Name
wscd.SDifficulty = difficulty.Name
if err := database.DB.Save(&wscd).Error; err != nil {
return fmt.Errorf("failed to update relationship: %w", err)
}
}
}
return nil
}
// Helper functions for JSON I/O
func writeJSON(filename string, data interface{}) error {
file, err := os.Create(filename)
if err != nil {
return fmt.Errorf("failed to create file %s: %w", filename, err)
}
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
if err := encoder.Encode(data); err != nil {
return fmt.Errorf("failed to encode JSON to %s: %w", filename, err)
}
return nil
}
func readJSON(filename string, data interface{}) error {
file, err := os.Open(filename)
if err != nil {
return fmt.Errorf("failed to open file %s: %w", filename, err)
}
defer file.Close()
decoder := json.NewDecoder(file)
if err := decoder.Decode(data); err != nil {
return fmt.Errorf("failed to decode JSON from %s: %w", filename, err)
}
return nil
}
// ExportSkillCategories exports all skill categories to a JSON file
func ExportSkillCategories(outputDir string) error {
var categories []models.SkillCategory
if err := database.DB.Find(&categories).Error; err != nil {
return fmt.Errorf("failed to fetch skill categories: %w", err)
}
2026-01-05 09:56:12 +01:00
sourceMap := buildSourceMap()
exportable := make([]ExportableSkillCategory, len(categories))
for i, cat := range categories {
exportable[i] = ExportableSkillCategory{
Name: cat.Name,
GameSystem: cat.GameSystem,
SourceCode: sourceMap[cat.SourceID],
}
}
return writeJSON(filepath.Join(outputDir, "skill_categories.json"), exportable)
}
// ImportSkillCategories imports skill categories from a JSON file
func ImportSkillCategories(inputDir string) error {
var exportable []ExportableSkillCategory
if err := readJSON(filepath.Join(inputDir, "skill_categories.json"), &exportable); err != nil {
return err
}
2026-01-05 09:56:12 +01:00
sourceMap := buildSourceMapReverse()
for _, exp := range exportable {
2026-01-05 09:56:12 +01:00
category := models.SkillCategory{
Name: exp.Name,
GameSystem: exp.GameSystem,
SourceID: sourceMap[exp.SourceCode],
}
2026-01-05 09:56:12 +01:00
if err := findOrCreateByNameAndSystem(exp.Name, exp.GameSystem, &category, "skill category"); err != nil {
return err
}
}
return nil
}
// ExportSkillDifficulties exports all skill difficulties to a JSON file
func ExportSkillDifficulties(outputDir string) error {
var difficulties []models.SkillDifficulty
if err := database.DB.Find(&difficulties).Error; err != nil {
return fmt.Errorf("failed to fetch skill difficulties: %w", err)
}
exportable := make([]ExportableSkillDifficulty, len(difficulties))
for i, diff := range difficulties {
exportable[i] = ExportableSkillDifficulty{
Name: diff.Name,
GameSystem: diff.GameSystem,
}
}
return writeJSON(filepath.Join(outputDir, "skill_difficulties.json"), exportable)
}
// ImportSkillDifficulties imports skill difficulties from a JSON file
func ImportSkillDifficulties(inputDir string) error {
var exportable []ExportableSkillDifficulty
if err := readJSON(filepath.Join(inputDir, "skill_difficulties.json"), &exportable); err != nil {
return err
}
for _, exp := range exportable {
2026-01-05 09:56:12 +01:00
difficulty := models.SkillDifficulty{
Name: exp.Name,
GameSystem: exp.GameSystem,
}
2026-01-05 09:56:12 +01:00
if err := findOrCreateByNameAndSystem(exp.Name, exp.GameSystem, &difficulty, "skill difficulty"); err != nil {
return err
}
}
return nil
}
// ExportSpells exports all spells to a JSON file
func ExportSpells(outputDir string) error {
var spells []models.Spell
if err := database.DB.Find(&spells).Error; err != nil {
return fmt.Errorf("failed to fetch spells: %w", err)
}
2026-01-05 09:56:12 +01:00
sourceMap := buildSourceMap()
exportable := make([]ExportableSpell, len(spells))
for i, spell := range spells {
exportable[i] = ExportableSpell{
Name: spell.Name,
GameSystem: spell.GameSystem,
2026-01-28 08:51:35 +01:00
GameSystemId: spell.GameSystemId,
Beschreibung: spell.Beschreibung,
SourceCode: sourceMap[spell.SourceID],
PageNumber: spell.PageNumber,
Bonus: spell.Bonus,
Stufe: spell.Stufe,
AP: spell.AP,
Art: spell.Art,
Zauberdauer: spell.Zauberdauer,
Reichweite: spell.Reichweite,
Wirkungsziel: spell.Wirkungsziel,
Wirkungsbereich: spell.Wirkungsbereich,
Wirkungsdauer: spell.Wirkungsdauer,
Ursprung: spell.Ursprung,
Category: spell.Category,
LearningCategory: spell.LearningCategory,
}
}
return writeJSON(filepath.Join(outputDir, "spells.json"), exportable)
}
// ImportSpells imports spells from a JSON file
func ImportSpells(inputDir string) error {
var exportable []ExportableSpell
if err := readJSON(filepath.Join(inputDir, "spells.json"), &exportable); err != nil {
return err
}
2026-01-05 09:56:12 +01:00
sourceMap := buildSourceMapReverse()
2026-01-28 08:51:35 +01:00
if err := database.DB.AutoMigrate(&models.Spell{}); err != nil {
return fmt.Errorf("failed to migrate spells table: %w", err)
}
gs := models.GetGameSystem(exportable[0].GameSystemId, exportable[0].GameSystem)
for _, exp := range exportable {
var spell models.Spell
2026-01-28 08:51:35 +01:00
result := database.DB.Where("name = ? AND (game_system = ? OR game_system_id = ?)", exp.Name, gs.Name, gs.ID).First(&spell)
sourceID := sourceMap[exp.SourceCode]
if result.Error == gorm.ErrRecordNotFound {
spell = models.Spell{
Name: exp.Name,
2026-01-28 08:51:35 +01:00
GameSystem: gs.Name,
GameSystemId: gs.ID,
Beschreibung: exp.Beschreibung,
SourceID: sourceID,
PageNumber: exp.PageNumber,
Bonus: exp.Bonus,
Stufe: exp.Stufe,
AP: exp.AP,
Art: exp.Art,
Zauberdauer: exp.Zauberdauer,
Reichweite: exp.Reichweite,
Wirkungsziel: exp.Wirkungsziel,
Wirkungsbereich: exp.Wirkungsbereich,
Wirkungsdauer: exp.Wirkungsdauer,
Ursprung: exp.Ursprung,
Category: exp.Category,
LearningCategory: exp.LearningCategory,
}
if err := database.DB.Create(&spell).Error; err != nil {
return fmt.Errorf("failed to create spell %s: %w", exp.Name, err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query spell %s: %w", exp.Name, result.Error)
} else {
// Update existing spell
spell.Beschreibung = exp.Beschreibung
spell.SourceID = sourceID
spell.PageNumber = exp.PageNumber
spell.Bonus = exp.Bonus
spell.Stufe = exp.Stufe
spell.AP = exp.AP
spell.Art = exp.Art
spell.Zauberdauer = exp.Zauberdauer
spell.Reichweite = exp.Reichweite
spell.Wirkungsziel = exp.Wirkungsziel
spell.Wirkungsbereich = exp.Wirkungsbereich
spell.Wirkungsdauer = exp.Wirkungsdauer
spell.Ursprung = exp.Ursprung
spell.Category = exp.Category
spell.LearningCategory = exp.LearningCategory
2026-01-28 08:51:35 +01:00
spell.GameSystemId = gs.ID
spell.GameSystem = gs.Name
if err := database.DB.Save(&spell).Error; err != nil {
return fmt.Errorf("failed to update spell %s: %w", exp.Name, err)
}
}
}
return nil
}
// ExportCharacterClasses exports all character classes to a JSON file
func ExportCharacterClasses(outputDir string) error {
var classes []models.CharacterClass
if err := database.DB.Find(&classes).Error; err != nil {
return fmt.Errorf("failed to fetch character classes: %w", err)
}
2026-01-05 09:56:12 +01:00
sourceMap := buildSourceMap()
exportable := make([]ExportableCharacterClass, len(classes))
for i, class := range classes {
exportable[i] = ExportableCharacterClass{
Code: class.Code,
Name: class.Name,
Description: class.Description,
SourceCode: sourceMap[class.SourceID],
GameSystem: class.GameSystem,
}
}
return writeJSON(filepath.Join(outputDir, "character_classes.json"), exportable)
}
// ImportCharacterClasses imports character classes from a JSON file
func ImportCharacterClasses(inputDir string) error {
var exportable []ExportableCharacterClass
if err := readJSON(filepath.Join(inputDir, "character_classes.json"), &exportable); err != nil {
return err
}
2026-01-05 09:56:12 +01:00
sourceMap := buildSourceMapReverse()
for _, exp := range exportable {
2026-01-05 09:56:12 +01:00
class := models.CharacterClass{
Code: exp.Code,
Name: exp.Name,
Description: exp.Description,
SourceID: sourceMap[exp.SourceCode],
GameSystem: exp.GameSystem,
}
2026-01-05 09:56:12 +01:00
if err := findOrCreateByCode(exp.Code, &class, "character class"); err != nil {
return err
}
}
return nil
}
// ExportSpellSchools exports all spell schools to a JSON file
func ExportSpellSchools(outputDir string) error {
var schools []models.SpellSchool
if err := database.DB.Find(&schools).Error; err != nil {
return fmt.Errorf("failed to fetch spell schools: %w", err)
}
2026-01-05 09:56:12 +01:00
sourceMap := buildSourceMap()
exportable := make([]ExportableSpellSchool, len(schools))
for i, school := range schools {
exportable[i] = ExportableSpellSchool{
Name: school.Name,
Description: school.Description,
SourceCode: sourceMap[school.SourceID],
GameSystem: school.GameSystem,
}
}
return writeJSON(filepath.Join(outputDir, "spell_schools.json"), exportable)
}
// ImportSpellSchools imports spell schools from a JSON file
func ImportSpellSchools(inputDir string) error {
var exportable []ExportableSpellSchool
if err := readJSON(filepath.Join(inputDir, "spell_schools.json"), &exportable); err != nil {
return err
}
// Get source map
var sources []models.Source
database.DB.Find(&sources)
sourceMap := make(map[string]uint)
for _, s := range sources {
sourceMap[s.Code] = s.ID
}
for _, exp := range exportable {
var school models.SpellSchool
result := database.DB.Where("name = ? AND game_system = ?", exp.Name, exp.GameSystem).First(&school)
sourceID := sourceMap[exp.SourceCode]
if result.Error == gorm.ErrRecordNotFound {
school = models.SpellSchool{
Name: exp.Name,
Description: exp.Description,
SourceID: sourceID,
GameSystem: exp.GameSystem,
}
if err := database.DB.Create(&school).Error; err != nil {
return fmt.Errorf("failed to create spell school %s: %w", exp.Name, err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query spell school %s: %w", exp.Name, result.Error)
} else {
school.Description = exp.Description
school.SourceID = sourceID
school.GameSystem = exp.GameSystem
if err := database.DB.Save(&school).Error; err != nil {
return fmt.Errorf("failed to update spell school %s: %w", exp.Name, err)
}
}
}
return nil
}
// Export/Import functions for ClassCategoryEPCost, ClassSpellSchoolEPCost, SpellLevelLECost
// SkillImprovementCost, WeaponSkill, Equipment, Weapon, Container, Transportation, Believe
// ExportClassCategoryEPCosts exports class-category EP costs
func ExportClassCategoryEPCosts(outputDir string) error {
var costs []models.ClassCategoryEPCost
if err := database.DB.Find(&costs).Error; err != nil {
return fmt.Errorf("failed to fetch class category EP costs: %w", err)
}
2026-01-05 09:56:12 +01:00
// Build reverse maps for export (ID -> Code/Name)
var classes []models.CharacterClass
database.DB.Find(&classes)
classMap := make(map[uint]string)
for _, c := range classes {
classMap[c.ID] = c.Code
}
var categories []models.SkillCategory
database.DB.Find(&categories)
categoryMap := make(map[uint]string)
for _, cat := range categories {
categoryMap[cat.ID] = cat.Name
}
exportable := make([]ExportableClassCategoryEPCost, len(costs))
for i, cost := range costs {
exportable[i] = ExportableClassCategoryEPCost{
CharacterClassCode: classMap[cost.CharacterClassID],
SkillCategoryName: categoryMap[cost.SkillCategoryID],
EPPerTE: cost.EPPerTE,
}
}
return writeJSON(filepath.Join(outputDir, "class_category_ep_costs.json"), exportable)
}
// ImportClassCategoryEPCosts imports class-category EP costs
func ImportClassCategoryEPCosts(inputDir string) error {
var exportable []ExportableClassCategoryEPCost
if err := readJSON(filepath.Join(inputDir, "class_category_ep_costs.json"), &exportable); err != nil {
return err
}
2026-01-05 09:56:12 +01:00
classMap := buildCharacterClassMap()
2026-01-05 09:56:12 +01:00
// Build category map (name -> id) - need to aggregate by name since we don't have game_system in the exportable
var categories []models.SkillCategory
database.DB.Find(&categories)
categoryMap := make(map[string]uint)
for _, cat := range categories {
categoryMap[cat.Name] = cat.ID
}
for _, exp := range exportable {
classID := classMap[exp.CharacterClassCode]
categoryID := categoryMap[exp.SkillCategoryName]
var cost models.ClassCategoryEPCost
result := database.DB.Where("character_class_id = ? AND skill_category_id = ?", classID, categoryID).First(&cost)
if result.Error == gorm.ErrRecordNotFound {
cost = models.ClassCategoryEPCost{
CharacterClassID: classID,
SkillCategoryID: categoryID,
EPPerTE: exp.EPPerTE,
CCLass: exp.CharacterClassCode,
SCategory: exp.SkillCategoryName,
}
if err := database.DB.Create(&cost).Error; err != nil {
return fmt.Errorf("failed to create class category EP cost: %w", err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query class category EP cost: %w", result.Error)
} else {
cost.EPPerTE = exp.EPPerTE
cost.CCLass = exp.CharacterClassCode
cost.SCategory = exp.SkillCategoryName
if err := database.DB.Save(&cost).Error; err != nil {
return fmt.Errorf("failed to update class category EP cost: %w", err)
}
}
}
return nil
}
// ExportClassSpellSchoolEPCosts exports class-spell school EP costs
func ExportClassSpellSchoolEPCosts(outputDir string) error {
var costs []models.ClassSpellSchoolEPCost
if err := database.DB.Find(&costs).Error; err != nil {
return fmt.Errorf("failed to fetch class spell school EP costs: %w", err)
}
2026-01-05 09:56:12 +01:00
// Build reverse maps for export
var classes []models.CharacterClass
database.DB.Find(&classes)
classMap := make(map[uint]string)
for _, c := range classes {
classMap[c.ID] = c.Code
}
var schools []models.SpellSchool
database.DB.Find(&schools)
schoolMap := make(map[uint]string)
for _, s := range schools {
schoolMap[s.ID] = s.Name
}
exportable := make([]ExportableClassSpellSchoolEPCost, len(costs))
for i, cost := range costs {
exportable[i] = ExportableClassSpellSchoolEPCost{
CharacterClassCode: classMap[cost.CharacterClassID],
SpellSchoolName: schoolMap[cost.SpellSchoolID],
EPPerLE: cost.EPPerLE,
}
}
return writeJSON(filepath.Join(outputDir, "class_spell_school_ep_costs.json"), exportable)
}
// ImportClassSpellSchoolEPCosts imports class-spell school EP costs
func ImportClassSpellSchoolEPCosts(inputDir string) error {
var exportable []ExportableClassSpellSchoolEPCost
if err := readJSON(filepath.Join(inputDir, "class_spell_school_ep_costs.json"), &exportable); err != nil {
return err
}
2026-01-05 09:56:12 +01:00
classMap := buildCharacterClassMap()
2026-01-05 09:56:12 +01:00
// Build spell school map (name -> id)
var schools []models.SpellSchool
database.DB.Find(&schools)
schoolMap := make(map[string]uint)
for _, s := range schools {
schoolMap[s.Name] = s.ID
}
for _, exp := range exportable {
classID := classMap[exp.CharacterClassCode]
schoolID := schoolMap[exp.SpellSchoolName]
var cost models.ClassSpellSchoolEPCost
result := database.DB.Where("character_class_id = ? AND spell_school_id = ?", classID, schoolID).First(&cost)
if result.Error == gorm.ErrRecordNotFound {
cost = models.ClassSpellSchoolEPCost{
CharacterClassID: classID,
SpellSchoolID: schoolID,
EPPerLE: exp.EPPerLE,
CCLass: exp.CharacterClassCode,
SCategory: exp.SpellSchoolName,
}
if err := database.DB.Create(&cost).Error; err != nil {
return fmt.Errorf("failed to create class spell school EP cost: %w", err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query class spell school EP cost: %w", result.Error)
} else {
cost.EPPerLE = exp.EPPerLE
cost.CCLass = exp.CharacterClassCode
cost.SCategory = exp.SpellSchoolName
if err := database.DB.Save(&cost).Error; err != nil {
return fmt.Errorf("failed to update class spell school EP cost: %w", err)
}
}
}
return nil
}
// ExportSpellLevelLECosts exports spell level LE costs
func ExportSpellLevelLECosts(outputDir string) error {
var costs []models.SpellLevelLECost
if err := database.DB.Find(&costs).Error; err != nil {
return fmt.Errorf("failed to fetch spell level LE costs: %w", err)
}
exportable := make([]ExportableSpellLevelLECost, len(costs))
for i, cost := range costs {
exportable[i] = ExportableSpellLevelLECost{
Level: cost.Level,
LERequired: cost.LERequired,
GameSystem: cost.GameSystem,
GameSystemId: cost.GameSystemId,
}
}
return writeJSON(filepath.Join(outputDir, "spell_level_le_costs.json"), exportable)
}
// ImportSpellLevelLECosts imports spell level LE costs
func ImportSpellLevelLECosts(inputDir string) error {
var exportable []ExportableSpellLevelLECost
if err := readJSON(filepath.Join(inputDir, "spell_level_le_costs.json"), &exportable); err != nil {
return err
}
for _, exp := range exportable {
gs := models.GetGameSystem(exp.GameSystemId, exp.GameSystem)
var cost models.SpellLevelLECost
result := database.DB.Where("level = ? AND (game_system = ? OR game_system_id = ?)", exp.Level, gs.Name, gs.ID).First(&cost)
if result.Error == gorm.ErrRecordNotFound {
cost = models.SpellLevelLECost{
Level: exp.Level,
LERequired: exp.LERequired,
GameSystem: gs.Name,
GameSystemId: gs.ID,
}
if err := cost.Create(); err != nil {
return fmt.Errorf("failed to create spell level LE cost: %w", err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query spell level LE cost: %w", result.Error)
} else {
cost.LERequired = exp.LERequired
cost.GameSystem = gs.Name
cost.GameSystemId = gs.ID
if err := cost.Save(); err != nil {
return fmt.Errorf("failed to update spell level LE cost: %w", err)
}
}
}
return nil
}
// ExportSkillImprovementCosts exports all skill improvement costs to a JSON file
func ExportSkillImprovementCosts(outputDir string) error {
var costs []models.SkillImprovementCost
if err := database.DB.Find(&costs).Error; err != nil {
return fmt.Errorf("failed to fetch skill improvement costs: %w", err)
}
exportable := make([]ExportableSkillImprovementCost, 0, len(costs))
for _, cost := range costs {
var lc models.SkillCategory
if err := database.DB.First(&lc, cost.CategoryID).Error; err != nil {
continue
}
var ld models.SkillDifficulty
if err := database.DB.First(&ld, cost.DifficultyID).Error; err != nil {
continue
}
var scd models.SkillCategoryDifficulty
if err := database.DB.Where("skill_category = ? AND skill_difficulty = ?", lc.Name, ld.Name).First(&scd).Error; err != nil {
continue
}
var skill models.Skill
if err := database.DB.First(&skill, scd.SkillID).Error; err != nil {
continue
}
exportable = append(exportable, ExportableSkillImprovementCost{
SkillName: skill.Name,
SkillSystem: skill.GameSystem,
CategoryName: lc.Name,
CategorySystem: lc.GameSystem,
DifficultyName: ld.Name,
DifficultySystem: ld.GameSystem,
CurrentLevel: cost.CurrentLevel,
TERequired: cost.TERequired,
})
}
return writeJSON(filepath.Join(outputDir, "skill_improvement_costs.json"), exportable)
}
// ImportSkillImprovementCosts imports skill improvement costs from a JSON file
func ImportSkillImprovementCosts(inputDir string) error {
var exportable []ExportableSkillImprovementCost
if err := readJSON(filepath.Join(inputDir, "skill_improvement_costs.json"), &exportable); err != nil {
return err
}
// Build lookup maps using helpers
skillMap := buildSkillMap()
categoryMap := buildCategoryMap()
difficultyMap := buildDifficultyMap()
for _, exp := range exportable {
// Find skill ID
skillID, ok := skillMap[exp.SkillSystem][exp.SkillName]
if !ok {
return fmt.Errorf("skill not found: %s (%s)", exp.SkillName, exp.SkillSystem)
}
// Find category ID
categoryID, ok := categoryMap[exp.CategorySystem][exp.CategoryName]
if !ok {
return fmt.Errorf("category not found: %s (%s)", exp.CategoryName, exp.CategorySystem)
}
// Find difficulty ID
difficultyID, ok := difficultyMap[exp.DifficultySystem][exp.DifficultyName]
if !ok {
return fmt.Errorf("difficulty not found: %s (%s)", exp.DifficultyName, exp.DifficultySystem)
}
// Find SkillCategoryDifficulty
var scd models.SkillCategoryDifficulty
if err := database.DB.Where("skill_id = ? AND skill_category_id = ? AND skill_difficulty_id = ?",
skillID, categoryID, difficultyID).First(&scd).Error; err != nil {
return fmt.Errorf("skill category difficulty not found for %s/%s/%s: %w",
exp.SkillName, exp.CategoryName, exp.DifficultyName, err)
}
// Find or create SkillImprovementCost using category/difficulty IDs
var cost models.SkillImprovementCost
result := database.DB.Where("skill_category_id = ? AND skill_difficulty_id = ? AND current_level = ?",
categoryID, difficultyID, exp.CurrentLevel).First(&cost)
if result.Error == gorm.ErrRecordNotFound {
cost = models.SkillImprovementCost{
CategoryID: categoryID,
DifficultyID: difficultyID,
CurrentLevel: exp.CurrentLevel,
TERequired: exp.TERequired,
}
if err := database.DB.Create(&cost).Error; err != nil {
return fmt.Errorf("failed to create skill improvement cost: %w", err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query skill improvement cost: %w", result.Error)
} else {
cost.TERequired = exp.TERequired
if err := database.DB.Save(&cost).Error; err != nil {
return fmt.Errorf("failed to update skill improvement cost: %w", err)
}
}
}
return nil
}
// ExportWeaponSkills exports all weapon skills to a JSON file
func ExportWeaponSkills(outputDir string) error {
var skills []models.WeaponSkill
if err := database.DB.Find(&skills).Error; err != nil {
return fmt.Errorf("failed to fetch weapon skills: %w", err)
}
sourceMap := buildSourceMap()
// Get all weapon skill category difficulties
var wscds []models.WeaponSkillCategoryDifficulty
database.DB.Preload("SkillCategory").Preload("SkillDifficulty").Find(&wscds)
// Build map of weapon_skill_id -> []category/difficulty combinations
wscdMap := make(map[uint][]ExportableCategoryDifficulty)
for _, wscd := range wscds {
wscdMap[wscd.WeaponSkillID] = append(wscdMap[wscd.WeaponSkillID], ExportableCategoryDifficulty{
Category: wscd.SkillCategory.Name,
Difficulty: wscd.SkillDifficulty.Name,
LearnCost: wscd.LearnCost,
})
}
exportable := make([]ExportableWeaponSkill, len(skills))
for i, skill := range skills {
exportable[i] = ExportableWeaponSkill{
Name: skill.Name,
GameSystem: skill.GameSystem,
GameSystemId: skill.GameSystemId,
Beschreibung: skill.Beschreibung,
SourceCode: sourceMap[skill.SourceID],
PageNumber: skill.PageNumber,
Initialwert: skill.Initialwert,
BasisWert: skill.BasisWert,
Bonuseigenschaft: skill.Bonuseigenschaft,
Improvable: skill.Improvable,
InnateSkill: skill.InnateSkill,
Category: skill.Category,
Difficulty: skill.Difficulty,
CategoriesDifficulties: wscdMap[skill.ID],
}
}
return writeJSON(filepath.Join(outputDir, "weapon_skills.json"), exportable)
}
// ImportWeaponSkills imports weapon skills from a JSON file
func ImportWeaponSkills(inputDir string) error {
var exportable []ExportableWeaponSkill
if err := readJSON(filepath.Join(inputDir, "weapon_skills.json"), &exportable); err != nil {
return err
}
if err := database.DB.AutoMigrate(&models.Skill{}, &models.WeaponSkill{}); err != nil {
return fmt.Errorf("failed to migrate weapon skills: %w", err)
}
sourceMap := buildSourceMapReverse()
for _, exp := range exportable {
var skill models.WeaponSkill
gs := models.GetGameSystem(exp.GameSystemId, exp.GameSystem)
effectiveName := exp.GameSystem
if effectiveName == "" {
effectiveName = gs.Name
}
effectiveID := gs.ID
if effectiveID == 0 {
effectiveID = models.GetGameSystem(0, "").ID
}
result := database.DB.Where("name = ? AND (game_system = ? OR game_system_id = ?)", exp.Name, effectiveName, effectiveID).First(&skill)
sourceID := sourceMap[exp.SourceCode]
if result.Error == gorm.ErrRecordNotFound {
skill = models.WeaponSkill{
Skill: models.Skill{
Name: exp.Name,
GameSystem: effectiveName,
GameSystemId: effectiveID,
Beschreibung: exp.Beschreibung,
SourceID: sourceID,
PageNumber: exp.PageNumber,
Initialwert: exp.Initialwert,
BasisWert: exp.BasisWert,
Bonuseigenschaft: exp.Bonuseigenschaft,
Improvable: exp.Improvable,
InnateSkill: exp.InnateSkill,
Category: exp.Category,
Difficulty: exp.Difficulty,
},
}
if err := database.DB.Create(&skill).Error; err != nil {
return fmt.Errorf("failed to create weapon skill %s: %w", exp.Name, err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query weapon skill %s: %w", exp.Name, result.Error)
} else {
skill.Beschreibung = exp.Beschreibung
skill.SourceID = sourceID
skill.PageNumber = exp.PageNumber
skill.Initialwert = exp.Initialwert
skill.BasisWert = exp.BasisWert
skill.Bonuseigenschaft = exp.Bonuseigenschaft
skill.Improvable = exp.Improvable
skill.InnateSkill = exp.InnateSkill
skill.Category = exp.Category
skill.Difficulty = exp.Difficulty
skill.GameSystemId = effectiveID
skill.GameSystem = effectiveName
if err := database.DB.Save(&skill).Error; err != nil {
return fmt.Errorf("failed to update weapon skill %s: %w", exp.Name, err)
}
}
// Handle category/difficulty relationships if present
if len(exp.CategoriesDifficulties) > 0 {
// Delete existing relationships
database.DB.Where("weapon_skill_id = ?", skill.ID).Delete(&models.WeaponSkillCategoryDifficulty{})
// Create new relationships
for _, cd := range exp.CategoriesDifficulties {
var category models.SkillCategory
var difficulty models.SkillDifficulty
if err := database.DB.Where("name = ?", cd.Category).First(&category).Error; err != nil {
continue // Skip if category not found
}
if err := database.DB.Where("name = ?", cd.Difficulty).First(&difficulty).Error; err != nil {
continue // Skip if difficulty not found
}
wscd := models.WeaponSkillCategoryDifficulty{
WeaponSkillID: skill.ID,
SkillCategoryID: category.ID,
SkillDifficultyID: difficulty.ID,
LearnCost: cd.LearnCost,
SCategory: category.Name,
SDifficulty: difficulty.Name,
}
if err := database.DB.Create(&wscd).Error; err != nil {
return fmt.Errorf("failed to create weapon skill category difficulty: %w", err)
}
}
}
}
return nil
}
// ExportEquipment exports all equipment to a JSON file
func ExportEquipment(outputDir string) error {
var equipment []models.Equipment
if err := database.DB.Find(&equipment).Error; err != nil {
return fmt.Errorf("failed to fetch equipment: %w", err)
}
sourceMap := buildSourceMap()
exportable := make([]ExportableEquipment, len(equipment))
for i, eq := range equipment {
exportable[i] = ExportableEquipment{
Name: eq.Name,
GameSystem: eq.GameSystem,
Beschreibung: eq.Beschreibung,
SourceCode: sourceMap[eq.SourceID],
PageNumber: eq.PageNumber,
Gewicht: eq.Gewicht,
Wert: eq.Wert,
PersonalItem: eq.PersonalItem,
}
}
return writeJSON(filepath.Join(outputDir, "equipment.json"), exportable)
}
// ImportEquipment imports equipment from a JSON file
func ImportEquipment(inputDir string) error {
var exportable []ExportableEquipment
if err := readJSON(filepath.Join(inputDir, "equipment.json"), &exportable); err != nil {
return err
}
sourceMap := buildSourceMapReverse()
for _, exp := range exportable {
var eq models.Equipment
result := database.DB.Where("name = ? AND game_system = ?", exp.Name, exp.GameSystem).First(&eq)
sourceID := sourceMap[exp.SourceCode]
if result.Error == gorm.ErrRecordNotFound {
eq = models.Equipment{
Name: exp.Name,
GameSystem: exp.GameSystem,
Beschreibung: exp.Beschreibung,
SourceID: sourceID,
PageNumber: exp.PageNumber,
Gewicht: exp.Gewicht,
Wert: exp.Wert,
PersonalItem: exp.PersonalItem,
}
if err := database.DB.Create(&eq).Error; err != nil {
return fmt.Errorf("failed to create equipment %s: %w", exp.Name, err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query equipment %s: %w", exp.Name, result.Error)
} else {
eq.Beschreibung = exp.Beschreibung
eq.SourceID = sourceID
eq.PageNumber = exp.PageNumber
eq.Gewicht = exp.Gewicht
eq.Wert = exp.Wert
eq.PersonalItem = exp.PersonalItem
if err := database.DB.Save(&eq).Error; err != nil {
return fmt.Errorf("failed to update equipment %s: %w", exp.Name, err)
}
}
}
return nil
}
// ExportWeapons exports all weapons to a JSON file
func ExportWeapons(outputDir string) error {
var weapons []models.Weapon
if err := database.DB.Find(&weapons).Error; err != nil {
return fmt.Errorf("failed to fetch weapons: %w", err)
}
sourceMap := buildSourceMap()
exportable := make([]ExportableWeapon, len(weapons))
for i, weapon := range weapons {
exportable[i] = ExportableWeapon{
Name: weapon.Name,
GameSystem: weapon.GameSystem,
Beschreibung: weapon.Beschreibung,
SourceCode: sourceMap[weapon.SourceID],
PageNumber: weapon.PageNumber,
Gewicht: weapon.Gewicht,
Wert: weapon.Wert,
PersonalItem: weapon.PersonalItem,
SkillRequired: weapon.SkillRequired,
Damage: weapon.Damage,
RangeNear: weapon.RangeNear,
RangeMiddle: weapon.RangeMiddle,
RangeFar: weapon.RangeFar,
}
}
return writeJSON(filepath.Join(outputDir, "weapons.json"), exportable)
}
// ImportWeapons imports weapons from a JSON file
func ImportWeapons(inputDir string) error {
var exportable []ExportableWeapon
if err := readJSON(filepath.Join(inputDir, "weapons.json"), &exportable); err != nil {
return err
}
sourceMap := buildSourceMapReverse()
for _, exp := range exportable {
var weapon models.Weapon
result := database.DB.Where("name = ? AND game_system = ?", exp.Name, exp.GameSystem).First(&weapon)
sourceID := sourceMap[exp.SourceCode]
if result.Error == gorm.ErrRecordNotFound {
weapon = models.Weapon{
Equipment: models.Equipment{
Name: exp.Name,
GameSystem: exp.GameSystem,
Beschreibung: exp.Beschreibung,
SourceID: sourceID,
PageNumber: exp.PageNumber,
Gewicht: exp.Gewicht,
Wert: exp.Wert,
PersonalItem: exp.PersonalItem,
},
SkillRequired: exp.SkillRequired,
Damage: exp.Damage,
RangeNear: exp.RangeNear,
RangeMiddle: exp.RangeMiddle,
RangeFar: exp.RangeFar,
}
if err := database.DB.Create(&weapon).Error; err != nil {
return fmt.Errorf("failed to create weapon %s: %w", exp.Name, err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query weapon %s: %w", exp.Name, result.Error)
} else {
weapon.Beschreibung = exp.Beschreibung
weapon.SourceID = sourceID
weapon.PageNumber = exp.PageNumber
weapon.Gewicht = exp.Gewicht
weapon.Wert = exp.Wert
weapon.PersonalItem = exp.PersonalItem
weapon.SkillRequired = exp.SkillRequired
weapon.Damage = exp.Damage
weapon.RangeNear = exp.RangeNear
weapon.RangeMiddle = exp.RangeMiddle
weapon.RangeFar = exp.RangeFar
if err := database.DB.Save(&weapon).Error; err != nil {
return fmt.Errorf("failed to update weapon %s: %w", exp.Name, err)
}
}
}
return nil
}
// ExportContainers exports all containers to a JSON file
func ExportContainers(outputDir string) error {
var containers []models.Container
if err := database.DB.Find(&containers).Error; err != nil {
return fmt.Errorf("failed to fetch containers: %w", err)
}
sourceMap := buildSourceMap()
exportable := make([]ExportableContainer, len(containers))
for i, container := range containers {
exportable[i] = ExportableContainer{
Name: container.Name,
GameSystem: container.GameSystem,
Beschreibung: container.Beschreibung,
SourceCode: sourceMap[container.SourceID],
PageNumber: container.PageNumber,
Gewicht: container.Gewicht,
Wert: container.Wert,
PersonalItem: container.PersonalItem,
Tragkraft: container.Tragkraft,
Volumen: container.Volumen,
}
}
return writeJSON(filepath.Join(outputDir, "containers.json"), exportable)
}
// ImportContainers imports containers from a JSON file
func ImportContainers(inputDir string) error {
var exportable []ExportableContainer
if err := readJSON(filepath.Join(inputDir, "containers.json"), &exportable); err != nil {
return err
}
sourceMap := buildSourceMapReverse()
for _, exp := range exportable {
var container models.Container
result := database.DB.Where("name = ? AND game_system = ?", exp.Name, exp.GameSystem).First(&container)
sourceID := sourceMap[exp.SourceCode]
if result.Error == gorm.ErrRecordNotFound {
container = models.Container{
Equipment: models.Equipment{
Name: exp.Name,
GameSystem: exp.GameSystem,
Beschreibung: exp.Beschreibung,
SourceID: sourceID,
PageNumber: exp.PageNumber,
Gewicht: exp.Gewicht,
Wert: exp.Wert,
PersonalItem: exp.PersonalItem,
},
Tragkraft: exp.Tragkraft,
Volumen: exp.Volumen,
}
if err := database.DB.Create(&container).Error; err != nil {
return fmt.Errorf("failed to create container %s: %w", exp.Name, err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query container %s: %w", exp.Name, result.Error)
} else {
container.Beschreibung = exp.Beschreibung
container.SourceID = sourceID
container.PageNumber = exp.PageNumber
container.Gewicht = exp.Gewicht
container.Wert = exp.Wert
container.PersonalItem = exp.PersonalItem
container.Tragkraft = exp.Tragkraft
container.Volumen = exp.Volumen
if err := database.DB.Save(&container).Error; err != nil {
return fmt.Errorf("failed to update container %s: %w", exp.Name, err)
}
}
}
return nil
}
// ExportTransportation exports all transportation to a JSON file
func ExportTransportation(outputDir string) error {
var transportation []models.Transportation
if err := database.DB.Find(&transportation).Error; err != nil {
return fmt.Errorf("failed to fetch transportation: %w", err)
}
sourceMap := buildSourceMap()
exportable := make([]ExportableTransportation, len(transportation))
for i, trans := range transportation {
exportable[i] = ExportableTransportation{
Name: trans.Name,
GameSystem: trans.GameSystem,
Beschreibung: trans.Beschreibung,
SourceCode: sourceMap[trans.SourceID],
PageNumber: trans.PageNumber,
Gewicht: trans.Gewicht,
Wert: trans.Wert,
PersonalItem: trans.PersonalItem,
Tragkraft: trans.Tragkraft,
Volumen: trans.Volumen,
}
}
return writeJSON(filepath.Join(outputDir, "transportation.json"), exportable)
}
// ImportTransportation imports transportation from a JSON file
func ImportTransportation(inputDir string) error {
var exportable []ExportableTransportation
if err := readJSON(filepath.Join(inputDir, "transportation.json"), &exportable); err != nil {
return err
}
sourceMap := buildSourceMapReverse()
for _, exp := range exportable {
var trans models.Transportation
result := database.DB.Where("name = ? AND game_system = ?", exp.Name, exp.GameSystem).First(&trans)
sourceID := sourceMap[exp.SourceCode]
if result.Error == gorm.ErrRecordNotFound {
trans = models.Transportation{
Container: models.Container{
Equipment: models.Equipment{
Name: exp.Name,
GameSystem: exp.GameSystem,
Beschreibung: exp.Beschreibung,
SourceID: sourceID,
PageNumber: exp.PageNumber,
Gewicht: exp.Gewicht,
Wert: exp.Wert,
PersonalItem: exp.PersonalItem,
},
Tragkraft: exp.Tragkraft,
Volumen: exp.Volumen,
},
}
if err := database.DB.Create(&trans).Error; err != nil {
return fmt.Errorf("failed to create transportation %s: %w", exp.Name, err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query transportation %s: %w", exp.Name, result.Error)
} else {
trans.Beschreibung = exp.Beschreibung
trans.SourceID = sourceID
trans.PageNumber = exp.PageNumber
trans.Gewicht = exp.Gewicht
trans.Wert = exp.Wert
trans.PersonalItem = exp.PersonalItem
trans.Tragkraft = exp.Tragkraft
trans.Volumen = exp.Volumen
if err := database.DB.Save(&trans).Error; err != nil {
return fmt.Errorf("failed to update transportation %s: %w", exp.Name, err)
}
}
}
return nil
}
// ExportBelieves exports all beliefs to a JSON file
func ExportBelieves(outputDir string) error {
var believes []models.Believe
if err := database.DB.Find(&believes).Error; err != nil {
return fmt.Errorf("failed to fetch believes: %w", err)
}
sourceMap := buildSourceMap()
exportable := make([]ExportableBelieve, len(believes))
for i, believe := range believes {
exportable[i] = ExportableBelieve{
Name: believe.Name,
GameSystem: believe.GameSystem,
Beschreibung: believe.Beschreibung,
SourceCode: sourceMap[believe.SourceID],
PageNumber: believe.PageNumber,
}
}
return writeJSON(filepath.Join(outputDir, "believes.json"), exportable)
}
// ImportBelieves imports believes from a JSON file
func ImportBelieves(inputDir string) error {
var exportable []ExportableBelieve
if err := readJSON(filepath.Join(inputDir, "believes.json"), &exportable); err != nil {
return err
}
sourceMap := buildSourceMapReverse()
for _, exp := range exportable {
var believe models.Believe
result := database.DB.Where("name = ? AND game_system = ?", exp.Name, exp.GameSystem).First(&believe)
sourceID := sourceMap[exp.SourceCode]
if result.Error == gorm.ErrRecordNotFound {
believe = models.Believe{
Name: exp.Name,
GameSystem: exp.GameSystem,
Beschreibung: exp.Beschreibung,
SourceID: sourceID,
PageNumber: exp.PageNumber,
}
if err := database.DB.Create(&believe).Error; err != nil {
return fmt.Errorf("failed to create believe %s: %w", exp.Name, err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query believe %s: %w", exp.Name, result.Error)
} else {
believe.Beschreibung = exp.Beschreibung
believe.SourceID = sourceID
believe.PageNumber = exp.PageNumber
if err := database.DB.Save(&believe).Error; err != nil {
return fmt.Errorf("failed to update believe %s: %w", exp.Name, err)
}
}
}
return nil
}
// ExportClassCategoryLearningPoints exports class category learning points to a JSON file
func ExportClassCategoryLearningPoints(outputDir string) error {
var records []models.ClassCategoryLearningPoints
if err := database.DB.Preload("CharacterClass").Preload("SkillCategory").Find(&records).Error; err != nil {
return fmt.Errorf("failed to fetch class category learning points: %w", err)
}
exportable := make([]ExportableClassCategoryLearningPoints, len(records))
for i, record := range records {
exportable[i] = ExportableClassCategoryLearningPoints{
ClassName: record.CharacterClass.Name,
ClassSystem: record.CharacterClass.GameSystem,
CategoryName: record.SkillCategory.Name,
CategorySystem: record.SkillCategory.GameSystem,
Points: record.Points,
}
}
return writeJSON(filepath.Join(outputDir, "class_category_learning_points.json"), exportable)
}
// ImportClassCategoryLearningPoints imports class category learning points from a JSON file
func ImportClassCategoryLearningPoints(inputDir string) error {
var exportable []ExportableClassCategoryLearningPoints
if err := readJSON(filepath.Join(inputDir, "class_category_learning_points.json"), &exportable); err != nil {
return err
}
for _, exp := range exportable {
// Find class
var class models.CharacterClass
if err := database.DB.Where("name = ? AND game_system = ?", exp.ClassName, exp.ClassSystem).First(&class).Error; err != nil {
return fmt.Errorf("failed to find class %s: %w", exp.ClassName, err)
}
// Find category
var category models.SkillCategory
if err := database.DB.Where("name = ? AND game_system = ?", exp.CategoryName, exp.CategorySystem).First(&category).Error; err != nil {
return fmt.Errorf("failed to find category %s: %w", exp.CategoryName, err)
}
// Check if record exists
var record models.ClassCategoryLearningPoints
result := database.DB.Where("character_class_id = ? AND skill_category_id = ?", class.ID, category.ID).First(&record)
if result.Error == gorm.ErrRecordNotFound {
record = models.ClassCategoryLearningPoints{
CharacterClassID: class.ID,
SkillCategoryID: category.ID,
Points: exp.Points,
}
if err := database.DB.Create(&record).Error; err != nil {
return fmt.Errorf("failed to create class category learning points: %w", err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query class category learning points: %w", result.Error)
} else {
record.Points = exp.Points
if err := database.DB.Save(&record).Error; err != nil {
return fmt.Errorf("failed to update class category learning points: %w", err)
}
}
}
return nil
}
// ExportClassSpellPoints exports class spell points to a JSON file
func ExportClassSpellPoints(outputDir string) error {
var records []models.ClassSpellPoints
if err := database.DB.Preload("CharacterClass").Find(&records).Error; err != nil {
return fmt.Errorf("failed to fetch class spell points: %w", err)
}
exportable := make([]ExportableClassSpellPoints, len(records))
for i, record := range records {
exportable[i] = ExportableClassSpellPoints{
ClassName: record.CharacterClass.Name,
ClassSystem: record.CharacterClass.GameSystem,
SpellPoints: record.SpellPoints,
}
}
return writeJSON(filepath.Join(outputDir, "class_spell_points.json"), exportable)
}
// ImportClassSpellPoints imports class spell points from a JSON file
func ImportClassSpellPoints(inputDir string) error {
var exportable []ExportableClassSpellPoints
if err := readJSON(filepath.Join(inputDir, "class_spell_points.json"), &exportable); err != nil {
return err
}
for _, exp := range exportable {
// Find class
var class models.CharacterClass
if err := database.DB.Where("name = ? AND game_system = ?", exp.ClassName, exp.ClassSystem).First(&class).Error; err != nil {
return fmt.Errorf("failed to find class %s: %w", exp.ClassName, err)
}
// Check if record exists
var record models.ClassSpellPoints
result := database.DB.Where("character_class_id = ?", class.ID).First(&record)
if result.Error == gorm.ErrRecordNotFound {
record = models.ClassSpellPoints{
CharacterClassID: class.ID,
SpellPoints: exp.SpellPoints,
}
if err := database.DB.Create(&record).Error; err != nil {
return fmt.Errorf("failed to create class spell points: %w", err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query class spell points: %w", result.Error)
} else {
record.SpellPoints = exp.SpellPoints
if err := database.DB.Save(&record).Error; err != nil {
return fmt.Errorf("failed to update class spell points: %w", err)
}
}
}
return nil
}
// ExportClassTypicalSkills exports class typical skills to a JSON file
func ExportClassTypicalSkills(outputDir string) error {
var records []models.ClassTypicalSkill
if err := database.DB.Preload("CharacterClass").Preload("Skill").Find(&records).Error; err != nil {
return fmt.Errorf("failed to fetch class typical skills: %w", err)
}
exportable := make([]ExportableClassTypicalSkill, len(records))
for i, record := range records {
exportable[i] = ExportableClassTypicalSkill{
ClassName: record.CharacterClass.Name,
ClassSystem: record.CharacterClass.GameSystem,
SkillName: record.Skill.Name,
SkillSystem: record.Skill.GameSystem,
Bonus: record.Bonus,
Attribute: record.Attribute,
Notes: record.Notes,
}
}
return writeJSON(filepath.Join(outputDir, "class_typical_skills.json"), exportable)
}
// ImportClassTypicalSkills imports class typical skills from a JSON file
func ImportClassTypicalSkills(inputDir string) error {
var exportable []ExportableClassTypicalSkill
if err := readJSON(filepath.Join(inputDir, "class_typical_skills.json"), &exportable); err != nil {
return err
}
for _, exp := range exportable {
// Find class
var class models.CharacterClass
if err := database.DB.Where("name = ? AND game_system = ?", exp.ClassName, exp.ClassSystem).First(&class).Error; err != nil {
return fmt.Errorf("failed to find class %s: %w", exp.ClassName, err)
}
// Find skill
var skill models.Skill
if err := database.DB.Where("name = ? AND game_system = ?", exp.SkillName, exp.SkillSystem).First(&skill).Error; err != nil {
return fmt.Errorf("failed to find skill %s: %w", exp.SkillName, err)
}
// Check if record exists
var record models.ClassTypicalSkill
result := database.DB.Where("character_class_id = ? AND skill_id = ?", class.ID, skill.ID).First(&record)
if result.Error == gorm.ErrRecordNotFound {
record = models.ClassTypicalSkill{
CharacterClassID: class.ID,
SkillID: skill.ID,
Bonus: exp.Bonus,
Attribute: exp.Attribute,
Notes: exp.Notes,
}
if err := database.DB.Create(&record).Error; err != nil {
return fmt.Errorf("failed to create class typical skill: %w", err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query class typical skill: %w", result.Error)
} else {
record.Bonus = exp.Bonus
record.Attribute = exp.Attribute
record.Notes = exp.Notes
if err := database.DB.Save(&record).Error; err != nil {
return fmt.Errorf("failed to update class typical skill: %w", err)
}
}
}
return nil
}
// ExportClassTypicalSpells exports class typical spells to a JSON file
func ExportClassTypicalSpells(outputDir string) error {
var records []models.ClassTypicalSpell
if err := database.DB.Preload("CharacterClass").Preload("Spell").Find(&records).Error; err != nil {
return fmt.Errorf("failed to fetch class typical spells: %w", err)
}
exportable := make([]ExportableClassTypicalSpell, len(records))
for i, record := range records {
exportable[i] = ExportableClassTypicalSpell{
ClassName: record.CharacterClass.Name,
ClassSystem: record.CharacterClass.GameSystem,
SpellName: record.Spell.Name,
SpellSystem: record.Spell.GameSystem,
Notes: record.Notes,
}
}
return writeJSON(filepath.Join(outputDir, "class_typical_spells.json"), exportable)
}
// ImportClassTypicalSpells imports class typical spells from a JSON file
func ImportClassTypicalSpells(inputDir string) error {
var exportable []ExportableClassTypicalSpell
if err := readJSON(filepath.Join(inputDir, "class_typical_spells.json"), &exportable); err != nil {
return err
}
for _, exp := range exportable {
// Find class
var class models.CharacterClass
if err := database.DB.Where("name = ? AND game_system = ?", exp.ClassName, exp.ClassSystem).First(&class).Error; err != nil {
return fmt.Errorf("failed to find class %s: %w", exp.ClassName, err)
}
// Find spell
var spell models.Spell
if err := database.DB.Where("name = ? AND game_system = ?", exp.SpellName, exp.SpellSystem).First(&spell).Error; err != nil {
return fmt.Errorf("failed to find spell %s: %w", exp.SpellName, err)
}
// Check if record exists
var record models.ClassTypicalSpell
result := database.DB.Where("character_class_id = ? AND spell_id = ?", class.ID, spell.ID).First(&record)
if result.Error == gorm.ErrRecordNotFound {
record = models.ClassTypicalSpell{
CharacterClassID: class.ID,
SpellID: spell.ID,
Notes: exp.Notes,
}
if err := database.DB.Create(&record).Error; err != nil {
return fmt.Errorf("failed to create class typical spell: %w", err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query class typical spell: %w", result.Error)
} else {
record.Notes = exp.Notes
if err := database.DB.Save(&record).Error; err != nil {
return fmt.Errorf("failed to update class typical spell: %w", err)
}
}
}
return nil
}
2026-01-14 22:14:23 +01:00
// ExportMiscLookups exports miscellaneous lookup values to a JSON file
func ExportMiscLookups(outputDir string) error {
var lookups []models.MiscLookup
if err := database.DB.Find(&lookups).Error; err != nil {
return fmt.Errorf("failed to fetch misc lookups: %w", err)
}
sourceMap := buildSourceMap()
exportable := make([]ExportableMiscLookup, len(lookups))
for i, lookup := range lookups {
exportable[i] = ExportableMiscLookup{
2026-01-28 07:44:17 +01:00
Key: lookup.Key,
Value: lookup.Value,
SourceCode: sourceMap[lookup.SourceID],
PageNumber: lookup.PageNumber,
GameSystemId: lookup.GameSystemId,
2026-01-14 22:14:23 +01:00
}
}
return writeJSON(filepath.Join(outputDir, "misc_lookups.json"), exportable)
}
// ImportMiscLookups imports miscellaneous lookup values from a JSON file
func ImportMiscLookups(inputDir string) error {
var exportable []ExportableMiscLookup
if err := readJSON(filepath.Join(inputDir, "misc_lookups.json"), &exportable); err != nil {
return err
}
sourceMap := buildSourceMapReverse()
2026-01-28 07:44:17 +01:00
// ensure new columns exist when importing older exports
if err := database.DB.AutoMigrate(&models.MiscLookup{}); err != nil {
return fmt.Errorf("failed to migrate misc lookup table: %w", err)
}
gs := models.GetGameSystem(exportable[0].GameSystemId, "")
2026-01-14 22:14:23 +01:00
for _, exp := range exportable {
var lookup models.MiscLookup
2026-01-28 07:44:17 +01:00
result := database.DB.Where("key = ? AND (game_system_id = ? OR game_system_id = 0)", exp.Key, gs.ID).First(&lookup)
2026-01-14 22:14:23 +01:00
sourceID := sourceMap[exp.SourceCode]
if result.Error == gorm.ErrRecordNotFound {
lookup = models.MiscLookup{
2026-01-28 07:44:17 +01:00
Key: exp.Key,
Value: exp.Value,
SourceID: sourceID,
PageNumber: exp.PageNumber,
GameSystemId: gs.ID,
2026-01-14 22:14:23 +01:00
}
if err := database.DB.Create(&lookup).Error; err != nil {
return fmt.Errorf("failed to create misc lookup %s: %w", exp.Key, err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query misc lookup %s: %w", exp.Key, result.Error)
} else {
lookup.Value = exp.Value
lookup.SourceID = sourceID
lookup.PageNumber = exp.PageNumber
2026-01-28 07:44:17 +01:00
lookup.GameSystemId = gs.ID
2026-01-14 22:14:23 +01:00
if err := database.DB.Save(&lookup).Error; err != nil {
return fmt.Errorf("failed to update misc lookup %s: %w", exp.Key, err)
}
}
}
return nil
}
// Note: SkillImprovementCost, WeaponSkill, Equipment, Weapon, Container, Transportation, Believe
// export/import functions follow similar patterns - implement as needed
// ExportAll exports all master data to the specified directory
func ExportAll(outputDir string) error {
// Export in dependency order
if err := ExportSources(outputDir); err != nil {
return err
}
if err := ExportCharacterClasses(outputDir); err != nil {
return err
}
if err := ExportSkillCategories(outputDir); err != nil {
return err
}
if err := ExportSkillDifficulties(outputDir); err != nil {
return err
}
if err := ExportSpellSchools(outputDir); err != nil {
return err
}
if err := ExportSkills(outputDir); err != nil {
return err
}
if err := ExportSkillCategoryDifficulties(outputDir); err != nil {
return err
}
if err := ExportSpells(outputDir); err != nil {
return err
}
if err := ExportClassCategoryEPCosts(outputDir); err != nil {
return err
}
if err := ExportClassSpellSchoolEPCosts(outputDir); err != nil {
return err
}
if err := ExportSpellLevelLECosts(outputDir); err != nil {
return err
}
if err := ExportSkillImprovementCosts(outputDir); err != nil {
return err
}
if err := ExportWeaponSkills(outputDir); err != nil {
return err
}
if err := ExportWeaponSkillCategoryDifficulties(outputDir); err != nil {
return err
}
if err := ExportEquipment(outputDir); err != nil {
return err
}
if err := ExportWeapons(outputDir); err != nil {
return err
}
if err := ExportContainers(outputDir); err != nil {
return err
}
if err := ExportTransportation(outputDir); err != nil {
return err
}
if err := ExportBelieves(outputDir); err != nil {
return err
}
if err := ExportClassCategoryLearningPoints(outputDir); err != nil {
return err
}
if err := ExportClassSpellPoints(outputDir); err != nil {
return err
}
if err := ExportClassTypicalSkills(outputDir); err != nil {
return err
}
if err := ExportClassTypicalSpells(outputDir); err != nil {
return err
}
2026-01-14 22:14:23 +01:00
if err := ExportMiscLookups(outputDir); err != nil {
return err
}
return nil
}
// ImportAll imports all master data from the specified directory
func ImportAll(inputDir string) error {
// Import in dependency order
if err := ImportSources(inputDir); err != nil {
return err
}
if err := ImportCharacterClasses(inputDir); err != nil {
return err
}
if err := ImportSkillCategories(inputDir); err != nil {
return err
}
if err := ImportSkillDifficulties(inputDir); err != nil {
return err
}
if err := ImportSpellSchools(inputDir); err != nil {
return err
}
if err := ImportSkills(inputDir); err != nil {
return err
}
if err := ImportSkillCategoryDifficulties(inputDir); err != nil {
return err
}
if err := ImportSpells(inputDir); err != nil {
return err
}
if err := ImportClassCategoryEPCosts(inputDir); err != nil {
return err
}
if err := ImportClassSpellSchoolEPCosts(inputDir); err != nil {
return err
}
if err := ImportSpellLevelLECosts(inputDir); err != nil {
return err
}
if err := ImportSkillImprovementCosts(inputDir); err != nil {
return err
}
if err := ImportWeaponSkills(inputDir); err != nil {
return err
}
if err := ImportWeaponSkillCategoryDifficulties(inputDir); err != nil {
return err
}
if err := ImportEquipment(inputDir); err != nil {
return err
}
if err := ImportWeapons(inputDir); err != nil {
return err
}
if err := ImportContainers(inputDir); err != nil {
return err
}
if err := ImportTransportation(inputDir); err != nil {
return err
}
if err := ImportBelieves(inputDir); err != nil {
return err
}
if err := ImportClassCategoryLearningPoints(inputDir); err != nil {
return err
}
if err := ImportClassSpellPoints(inputDir); err != nil {
return err
}
if err := ImportClassTypicalSkills(inputDir); err != nil {
return err
}
if err := ImportClassTypicalSpells(inputDir); err != nil {
return err
}
2026-01-14 22:14:23 +01:00
if err := ImportMiscLookups(inputDir); err != nil {
return err
}
return nil
}