Files
bamort/backend/bmrt/gsmaster/learning_costs_enhancement.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

351 lines
12 KiB
Go

package gsmaster
import (
"bamort/database"
"bamort/bmrt/models"
"fmt"
"log"
)
// EnhanceLearningDataWithExistingTables ergänzt die neuen Lernkosten-Tabellen mit Daten aus den bereits vorhandenen Tabellen
func EnhanceLearningDataWithExistingTables() error {
log.Println("Starting enhancement of learning data with existing tables...")
// 1. Fertigkeiten aus der bestehenden Skill-Tabelle in SkillCategoryDifficulty einbinden
if err := linkExistingSkillsToCategories(); err != nil {
return fmt.Errorf("failed to link existing skills to categories: %w", err)
}
// 2. Zauber aus der bestehenden Spell-Tabelle mit Zauberschulen verknüpfen
if err := linkExistingSpellsToSchools(); err != nil {
return fmt.Errorf("failed to link existing spells to schools: %w", err)
}
// 3. Charaktere aus der bestehenden Character-Tabelle mit Charakterklassen verknüpfen
if err := linkExistingCharactersToClasses(); err != nil {
return fmt.Errorf("failed to link existing characters to classes: %w", err)
}
// 4. Fehlende Fertigkeiten aus der bestehenden Tabelle zu den Kategorien hinzufügen
if err := addMissingSkillsToCategories(); err != nil {
return fmt.Errorf("failed to add missing skills to categories: %w", err)
}
// 5. Verknüpfe bestehende Fertigkeiten und Zauber mit Standardquellen
if err := linkExistingItemsToDefaultSources(); err != nil {
return fmt.Errorf("failed to link existing items to default sources: %w", err)
}
log.Println("Enhancement completed successfully!")
return nil
}
// linkExistingSkillsToCategories verknüpft vorhandene Fertigkeiten mit Kategorien und Schwierigkeiten
func linkExistingSkillsToCategories() error {
var skills []models.Skill
if err := database.DB.Find(&skills).Error; err != nil {
return fmt.Errorf("failed to fetch existing skills: %w", err)
}
for _, skill := range skills {
// Prüfe ob bereits Verknüpfungen für diese Fertigkeit existieren
var existingLinks []models.SkillCategoryDifficulty
if err := database.DB.Where("skill_id = ?", skill.ID).Find(&existingLinks).Error; err != nil {
return fmt.Errorf("failed to check existing links for skill %s: %w", skill.Name, err)
}
// Wenn bereits Verknüpfungen existieren, überspringe
if len(existingLinks) > 0 {
log.Printf("Skill %s already has category links, skipping", skill.Name)
continue
}
// Suche nach der Fertigkeit in den lerningCostsData
category, difficulty := findSkillInLearningData(skill.Name)
if category == "" {
log.Printf("Warning: Skill %s not found in learning data, using default category", skill.Name)
// Verwende eine Standard-Kategorie basierend auf der bestehenden Category
if skill.Category != "" {
category = skill.Category
difficulty = skill.Difficulty
if difficulty == "" {
difficulty = "normal"
}
} else {
category = "Unbekannt"
difficulty = "normal"
}
}
// Erstelle Verknüpfung
if err := createSkillCategoryLink(skill.ID, skill.Name, category, difficulty); err != nil {
log.Printf("Warning: Failed to create category link for skill %s: %v", skill.Name, err)
}
}
return nil
}
// linkExistingSpellsToSchools verknüpft vorhandene Zauber mit Zauberschulen
func linkExistingSpellsToSchools() error {
var spells []models.Spell
if err := database.DB.Find(&spells).Error; err != nil {
return fmt.Errorf("failed to fetch existing spells: %w", err)
}
for _, spell := range spells {
// Prüfe ob die Zauberschule existiert
if spell.Category == "" {
log.Printf("Warning: Spell %s has no category, skipping", spell.Name)
continue
}
var spellSchool models.SpellSchool
if err := spellSchool.FirstByName(spell.Category); err != nil {
// Zauberschule existiert nicht, erstelle sie
newSchool := models.SpellSchool{
Name: spell.Category,
GameSystemId: 1,
}
if err := newSchool.Create(); err != nil {
log.Printf("Warning: Failed to create spell school %s for spell %s: %v", spell.Category, spell.Name, err)
continue
}
log.Printf("Created new spell school: %s", spell.Category)
}
log.Printf("Spell %s linked to school %s", spell.Name, spell.Category)
}
return nil
}
// linkExistingCharactersToClasses verknüpft vorhandene Charaktere mit Charakterklassen
func linkExistingCharactersToClasses() error {
var characters []models.Char
if err := database.DB.Find(&characters).Error; err != nil {
return fmt.Errorf("failed to fetch existing characters: %w", err)
}
// Lade alle verfügbaren Charakterklassen aus der Datenbank
var characterClasses []models.CharacterClass
if err := database.DB.Find(&characterClasses).Error; err != nil {
return fmt.Errorf("failed to fetch character classes from database: %w", err)
}
// Erstelle Mapping-Maps für beide Richtungen (Code->Klasse und Name->Klasse)
codeToClass := make(map[string]models.CharacterClass)
nameToClass := make(map[string]models.CharacterClass)
for _, class := range characterClasses {
codeToClass[class.Code] = class
nameToClass[class.Name] = class
}
for _, character := range characters {
var characterClass models.CharacterClass
var found bool
// Versuche zuerst über Code zu finden (falls character.Typ bereits ein Code ist)
if class, exists := codeToClass[character.Typ]; exists {
characterClass = class
found = true
} else if class, exists := nameToClass[character.Typ]; exists {
// Wenn nicht über Code gefunden, versuche über vollständigen Namen
characterClass = class
found = true
}
if !found {
log.Printf("Warning: Character class '%s' not found in database for character %s", character.Typ, character.Name)
continue
}
log.Printf("Character %s linked to class %s (%s)", character.Name, characterClass.Code, characterClass.Name)
}
return nil
}
// addMissingSkillsToCategories fügt Fertigkeiten hinzu, die in der Datenbank existieren aber nicht in den Lernkosten-Daten
func addMissingSkillsToCategories() error {
var skills []models.Skill
if err := database.DB.Find(&skills).Error; err != nil {
return fmt.Errorf("failed to fetch skills: %w", err)
}
for _, skill := range skills {
// Prüfe ob bereits Kategorien-Verknüpfungen existieren
var count int64
if err := database.DB.Model(&models.SkillCategoryDifficulty{}).Where("skill_id = ?", skill.ID).Count(&count).Error; err != nil {
return fmt.Errorf("failed to count category links for skill %s: %w", skill.Name, err)
}
if count == 0 {
// Keine Kategorien-Verknüpfung gefunden, erstelle eine Standard-Verknüpfung
category := "Unbekannt"
difficulty := "normal"
// Verwende bestehende Kategorie falls vorhanden
if skill.Category != "" {
category = skill.Category
}
if skill.Difficulty != "" {
difficulty = skill.Difficulty
}
if err := createSkillCategoryLink(skill.ID, skill.Name, category, difficulty); err != nil {
log.Printf("Warning: Failed to create default category link for skill %s: %v", skill.Name, err)
} else {
log.Printf("Created default category link for skill %s: %s - %s", skill.Name, category, difficulty)
}
}
}
return nil
}
// Hilfsfunktionen
// findSkillInLearningData sucht eine Fertigkeit in den learningCostsData und gibt Kategorie und Schwierigkeit zurück
func findSkillInLearningData(skillName string) (string, string) {
for categoryName, difficulties := range learningCostsData.ImprovementCost {
for difficultyName, data := range difficulties {
for _, skill := range data.Skills {
if skill == skillName {
return categoryName, difficultyName
}
}
}
}
return "", ""
}
// createSkillCategoryLink erstellt eine Verknüpfung zwischen Fertigkeit, Kategorie und Schwierigkeit
func createSkillCategoryLink(skillID uint, skillName, categoryName, difficultyName string) error {
// Hole oder erstelle die Kategorie
var skillCategory models.SkillCategory
gs := GetGameSystem(0, "midgard")
if err := skillCategory.FirstByName(categoryName); err != nil {
// Kategorie existiert nicht, erstelle sie
skillCategory = models.SkillCategory{
Name: categoryName,
GameSystemId: gs.ID,
}
if err := skillCategory.Create(); err != nil {
return fmt.Errorf("failed to create skill category %s: %w", categoryName, err)
}
log.Printf("Created new skill category: %s", categoryName)
}
// Hole oder erstelle die Schwierigkeit
var skillDifficulty models.SkillDifficulty
if err := skillDifficulty.FirstByName(difficultyName); err != nil {
// Schwierigkeit existiert nicht, erstelle sie
skillDifficulty = models.SkillDifficulty{
Name: difficultyName,
GameSystemId: gs.ID,
}
if err := skillDifficulty.Create(); err != nil {
return fmt.Errorf("failed to create skill difficulty %s: %w", difficultyName, err)
}
log.Printf("Created new skill difficulty: %s", difficultyName)
}
// Bestimme LearnCost basierend auf den learningCostsData
learnCost := 2 // Standard-Wert
if categoryData, exists := learningCostsData.ImprovementCost[categoryName]; exists {
if difficultyData, exists := categoryData[difficultyName]; exists {
learnCost = difficultyData.LearnCost
}
}
// Erstelle die Verknüpfung
categoryDifficulty := models.SkillCategoryDifficulty{
SkillID: skillID,
SkillCategoryID: skillCategory.ID,
SkillDifficultyID: skillDifficulty.ID,
LearnCost: learnCost,
}
if err := categoryDifficulty.Create(); err != nil {
return fmt.Errorf("failed to create skill category difficulty link: %w", err)
}
log.Printf("Created skill category link: %s - %s - %s (Learn: %d LE)", skillName, categoryName, difficultyName, learnCost)
return nil
}
// CreateLearningCostsTables erstellt alle neuen Tabellen für das Lernkosten-System
func CreateLearningCostsTables() error {
log.Println("Creating learning costs tables...")
// Liste aller neuen Modelle
models := []interface{}{
&models.Source{},
&models.CharacterClass{},
&models.SkillCategory{},
&models.SkillDifficulty{},
&models.SpellSchool{},
&models.ClassCategoryEPCost{},
&models.ClassSpellSchoolEPCost{},
&models.SpellLevelLECost{},
&models.SkillCategoryDifficulty{},
&models.SkillImprovementCost{},
}
// Erstelle oder migriere alle Tabellen
for _, model := range models {
if err := database.DB.AutoMigrate(model); err != nil {
return fmt.Errorf("failed to migrate table for model %T: %w", model, err)
}
}
log.Println("Learning costs tables created successfully!")
return nil
}
// linkExistingItemsToDefaultSources verknüpft bestehende Fertigkeiten und Zauber mit Standardquellen
func linkExistingItemsToDefaultSources() error {
// Hole die Standardquellen
var kodSource models.Source
if err := kodSource.FirstByCode("KOD"); err != nil {
return fmt.Errorf("KOD source not found: %w", err)
}
var arkSource models.Source
if err := arkSource.FirstByCode("ARK"); err != nil {
return fmt.Errorf("ARK source not found: %w", err)
}
// Verknüpfe Fertigkeiten mit KOD (falls noch keine Quelle zugeordnet)
var skills []models.Skill
if err := database.DB.Where("source_id IS NULL OR source_id = 0").Find(&skills).Error; err != nil {
return fmt.Errorf("failed to fetch skills without source: %w", err)
}
for _, skill := range skills {
if err := database.DB.Model(&skill).Update("source_id", kodSource.ID).Error; err != nil {
log.Printf("Warning: Failed to update source for skill %s: %v", skill.Name, err)
} else {
log.Printf("Linked skill %s to source KOD", skill.Name)
}
}
// Verknüpfe Zauber mit ARK (falls noch keine Quelle zugeordnet)
var spells []models.Spell
if err := database.DB.Where("source_id IS NULL OR source_id = 0").Find(&spells).Error; err != nil {
return fmt.Errorf("failed to fetch spells without source: %w", err)
}
for _, spell := range spells {
if err := database.DB.Model(&spell).Update("source_id", arkSource.ID).Error; err != nil {
log.Printf("Warning: Failed to update source for spell %s: %v", spell.Name, err)
} else {
log.Printf("Linked spell %s to source ARK", spell.Name)
}
}
return nil
}