Files
bamort/backend/bmrt/gsmaster/export_import.go
T
Bardioc26 042a1d4773 Learncost frontend (#42)
* introduced central package  registry by package init function
* dynamic registration of routes, model, migrations and initializers.
* setting a docker compose project name to prevent shutdown of other containers with the same (composer)name
* ai documentation
* app template
* Create tests for ALL API entpoints in ALL packages Based on current data. Ensure that all API endpoints used in frontend are tested. These tests are crucial for the next refactoring tasks.
* adopting agent instructions for a more consistent coding style
* added desired module layout and debugging information
* Fix All Failing tests All failing tests are fixed now that makes the refactoring more easy since all tests must pass
* restored routes for maintenance
* added common translations
* added new tests for API Endpoint
* Merge branch 'separate_business_logic'
* added lern and skill improvement cost editing
* Set Docker image tag when building to prevent rebuild when nothing has changed
* add and remove PP for Weaponskill fixed
* add and remove PP for same named skills fixed
* add new task
2026-05-01 18:15:31 +02:00

2408 lines
79 KiB
Go

package gsmaster
import (
"bamort/database"
"bamort/bmrt/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"`
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"`
}
// ExportableMiscLookup represents miscellaneous lookup values for export
type ExportableMiscLookup struct {
Key string `json:"key"`
Value string `json:"value"`
SourceCode string `json:"source_code"`
PageNumber int `json:"page_number"`
GameSystemId uint `json:"game_system_id"`
}
// 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)
}
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)
}
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 {
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,
}
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)
}
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
}
sourceMap := buildSourceMapReverse()
for _, exp := range exportable {
category := models.SkillCategory{
Name: exp.Name,
GameSystem: exp.GameSystem,
SourceID: sourceMap[exp.SourceCode],
}
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 {
difficulty := models.SkillDifficulty{
Name: exp.Name,
GameSystem: exp.GameSystem,
}
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)
}
sourceMap := buildSourceMap()
exportable := make([]ExportableSpell, len(spells))
for i, spell := range spells {
exportable[i] = ExportableSpell{
Name: spell.Name,
GameSystem: spell.GameSystem,
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
}
sourceMap := buildSourceMapReverse()
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
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,
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
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)
}
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
}
sourceMap := buildSourceMapReverse()
for _, exp := range exportable {
class := models.CharacterClass{
Code: exp.Code,
Name: exp.Name,
Description: exp.Description,
SourceID: sourceMap[exp.SourceCode],
GameSystem: exp.GameSystem,
}
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)
}
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)
}
// 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
}
classMap := buildCharacterClassMap()
// 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)
}
// 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
}
classMap := buildCharacterClassMap()
// 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
}
// 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{
Key: lookup.Key,
Value: lookup.Value,
SourceCode: sourceMap[lookup.SourceID],
PageNumber: lookup.PageNumber,
GameSystemId: lookup.GameSystemId,
}
}
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()
// 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, "")
for _, exp := range exportable {
var lookup models.MiscLookup
result := database.DB.Where("key = ? AND (game_system_id = ? OR game_system_id = 0)", exp.Key, gs.ID).First(&lookup)
sourceID := sourceMap[exp.SourceCode]
if result.Error == gorm.ErrRecordNotFound {
lookup = models.MiscLookup{
Key: exp.Key,
Value: exp.Value,
SourceID: sourceID,
PageNumber: exp.PageNumber,
GameSystemId: gs.ID,
}
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
lookup.GameSystemId = gs.ID
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
}
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
}
if err := ImportMiscLookups(inputDir); err != nil {
return err
}
return nil
}