fix(api): handle null/None values in API parsing

Added safe_decimal() helper to handle None/empty values from API
that were causing decimal.ConversionSyntax errors.

Fixed parsers:
- NexusHealingTool: Uses safe_decimal for decay/heal amounts
- NexusRing: Uses safe_decimal for max_tt/min_tt
- NexusClothing: Safely handles None buff values

Errors were occurring when API returned null for numeric fields
instead of 0.
This commit is contained in:
LemonNexus 2026-02-09 14:35:12 +00:00
parent ad3d8e535a
commit 9decd0ba5d
1 changed files with 27 additions and 11 deletions

View File

@ -3,7 +3,7 @@ Complete Entropia Nexus API Integration for Lemontropia Suite
Fetches all gear types: weapons, armors, plates, attachments, enhancers, healing, rings, clothes, pets Fetches all gear types: weapons, armors, plates, attachments, enhancers, healing, rings, clothes, pets
""" """
from decimal import Decimal from decimal import Decimal, InvalidOperation
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional, List, Dict, Any from typing import Optional, List, Dict, Any
import requests import requests
@ -191,6 +191,16 @@ class NexusEnhancer(NexusItem):
) )
def safe_decimal(value: Any, default: str = "0") -> Decimal:
"""Safely convert a value to Decimal, handling None and invalid values."""
if value is None or value == "":
return Decimal(default)
try:
return Decimal(str(value))
except (InvalidOperation, ValueError, TypeError):
return Decimal(default)
@dataclass @dataclass
class NexusHealingTool(NexusItem): class NexusHealingTool(NexusItem):
"""Healing tool from Entropia Nexus API.""" """Healing tool from Entropia Nexus API."""
@ -208,8 +218,8 @@ class NexusHealingTool(NexusItem):
economy = props.get('Economy', {}) economy = props.get('Economy', {})
heal = props.get('Heal', {}) heal = props.get('Heal', {})
decay = Decimal(str(economy.get('Decay', 0))) decay = safe_decimal(economy.get('Decay'))
heal_amount = Decimal(str(heal.get('Amount', 0))) heal_amount = safe_decimal(heal.get('Amount'))
heal_per_pec = heal_amount / decay if decay > 0 else Decimal('0') heal_per_pec = heal_amount / decay if decay > 0 else Decimal('0')
return cls( return cls(
@ -221,7 +231,7 @@ class NexusHealingTool(NexusItem):
decay=decay, decay=decay,
heal_per_pec=heal_per_pec, heal_per_pec=heal_per_pec,
type=props.get('Type', 'fap'), type=props.get('Type', 'fap'),
profession_level=int(props.get('RequiredLevel', 0)), profession_level=int(props.get('RequiredLevel', 0) or 0),
is_limited='(L)' in data.get('Name', ''), is_limited='(L)' in data.get('Name', ''),
) )
@ -242,14 +252,14 @@ class NexusRing(NexusItem):
props = data.get('Properties', {}) props = data.get('Properties', {})
economy = props.get('Economy', {}) economy = props.get('Economy', {})
effects_data = props.get('Effects', {}) effects_data = props.get('Effects', {})
# Parse effects (usually under "Effects on Equip") # Parse effects (usually under "Effects on Equip")
effects = {} effects = {}
if 'Effects on Equip' in effects_data: if 'Effects on Equip' in effects_data:
effects = effects_data['Effects on Equip'] effects = effects_data['Effects on Equip']
elif isinstance(effects_data, dict): elif isinstance(effects_data, dict):
effects = effects_data effects = effects_data
return cls( return cls(
id=data.get('Id', 0), id=data.get('Id', 0),
name=data.get('Name', 'Unknown'), name=data.get('Name', 'Unknown'),
@ -258,8 +268,8 @@ class NexusRing(NexusItem):
slot=props.get('Slot', 'Left Finger'), slot=props.get('Slot', 'Left Finger'),
gender=props.get('Gender', 'Both'), gender=props.get('Gender', 'Both'),
effects=effects, effects=effects,
max_tt=Decimal(str(economy.get('MaxTT', 0))), max_tt=safe_decimal(economy.get('MaxTT')),
min_tt=Decimal(str(economy.get('MinTT', 0))), min_tt=safe_decimal(economy.get('MinTT')),
is_limited='(L)' in data.get('Name', ''), is_limited='(L)' in data.get('Name', ''),
) )
@ -270,13 +280,19 @@ class NexusClothing(NexusItem):
slot: str # 'face', 'body', 'legs', 'feet' slot: str # 'face', 'body', 'legs', 'feet'
buffs: Dict[str, Decimal] buffs: Dict[str, Decimal]
is_cosmetic: bool is_cosmetic: bool
@classmethod @classmethod
def from_api(cls, data: Dict[str, Any]) -> "NexusClothing": def from_api(cls, data: Dict[str, Any]) -> "NexusClothing":
"""Create from API response.""" """Create from API response."""
props = data.get('Properties', {}) props = data.get('Properties', {})
buffs = {k: Decimal(str(v)) for k, v in props.get('Buffs', {}).items()} buffs_raw = props.get('Buffs', {}) or {}
buffs = {}
for k, v in buffs_raw.items():
try:
buffs[k] = safe_decimal(v)
except:
buffs[k] = Decimal('0')
return cls( return cls(
id=data.get('Id', 0), id=data.get('Id', 0),
name=data.get('Name', 'Unknown'), name=data.get('Name', 'Unknown'),