Compare commits

...

1 Commits

Author SHA1 Message Date
e119eeb14a Fix the fucking diff algorithm 2026-02-09 18:09:21 +01:00

View File

@@ -20,15 +20,19 @@
# noinspection PyPackageRequirements # noinspection PyPackageRequirements
import wx import wx
from collections import Counter import re
from eos.const import FittingSlot
from service.fit import Fit as svcFit from service.fit import Fit as svcFit
from service.port.eft import exportEft, importEft, _importPrepare from service.port.eft import exportEft
from service.const import PortEftOptions from service.const import PortEftOptions
_t = wx.GetTranslation _t = wx.GetTranslation
# Regex for parsing items: itemName x? quantity?, ,? chargeName?
ITEM_REGEX = re.compile(
r"^(?P<itemName>[-\'\w\s]+?)x?\s*(?P<quantity>\d+)?\s*(?:,\s*(?P<chargeName>[-\'\w\s]+))?$"
)
class FitDiffFrame(wx.Frame): class FitDiffFrame(wx.Frame):
"""A frame to display differences between two fits.""" """A frame to display differences between two fits."""
@@ -169,158 +173,44 @@ class FitDiffFrame(wx.Frame):
self.diffText.SetValue('\n'.join(diffLines)) self.diffText.SetValue('\n'.join(diffLines))
def parsePastedFit(self, text): def parsePastedFit(self, text):
"""Parse pasted EFT text into a fit object.""" """Parse pasted EFT text into a map of item name to count."""
try: items = {}
lines = _importPrepare(text.splitlines()) for line in text.splitlines():
if not lines: line = line.strip()
return None if not line or line.startswith("["):
return importEft(lines) continue
except Exception: match = ITEM_REGEX.match(line)
return None if match:
item_name = match.group("itemName").strip()
quantity = match.group("quantity")
count = int(quantity) if quantity else 1
if item_name not in items:
items[item_name] = 0
items[item_name] += count
return items
def calculateDiff(self, fit1, fit2): def calculateDiff(self, fit1_items, fit2_items):
"""Calculate items needed to transform fit1 into fit2. """Calculate items needed to transform fit1 into fit2.
Returns a list of strings in the format: "<item> <quantity>" Returns a list of strings showing additions and extra items.
Only shows items that need to be added (no negative values).
""" """
diffLines = [] diffLines = []
# Get module counts by type for each fit (grouped by slot type) all_items = set(fit1_items.keys()) | set(fit2_items.keys())
fit1_modules = self.getModuleCounts(fit1) additions = []
fit2_modules = self.getModuleCounts(fit2) extras = []
# Slot order for item in sorted(all_items):
slotOrder = [ count1 = fit1_items.get(item, 0)
FittingSlot.HIGH, count2 = fit2_items.get(item, 0)
FittingSlot.MED,
FittingSlot.LOW,
FittingSlot.RIG,
FittingSlot.SUBSYSTEM,
FittingSlot.SERVICE,
]
# Diff modules by slot - only show items needed to add
for slot in slotOrder:
fit1_slot_modules = fit1_modules.get(slot, Counter())
fit2_slot_modules = fit2_modules.get(slot, Counter())
all_module_types = set(fit1_slot_modules.keys()) | set(fit2_slot_modules.keys())
slot_diff_lines = []
for module_type in sorted(all_module_types):
count1 = fit1_slot_modules.get(module_type, 0)
count2 = fit2_slot_modules.get(module_type, 0)
if count2 > count1: if count2 > count1:
slot_diff_lines.append(f"{module_type} x{count2 - count1}") additions.append(f"{item} x{count2 - count1}")
elif count1 > count2:
extras.append(f"{item} x-{count1 - count2}")
if slot_diff_lines: diffLines.extend(additions)
if diffLines: if additions and extras:
diffLines.append("") diffLines.extend(["", ""])
diffLines.extend(slot_diff_lines) diffLines.extend(extras)
# Get drone counts
fit1_drones = self.getDroneCounts(fit1)
fit2_drones = self.getDroneCounts(fit2)
all_drone_types = set(fit1_drones.keys()) | set(fit2_drones.keys())
for drone_type in sorted(all_drone_types):
count1 = fit1_drones.get(drone_type, 0)
count2 = fit2_drones.get(drone_type, 0)
if count2 > count1:
diffLines.append(f"{drone_type} x{count2 - count1}")
# Get fighter counts
fit1_fighters = self.getFighterCounts(fit1)
fit2_fighters = self.getFighterCounts(fit2)
all_fighter_types = set(fit1_fighters.keys()) | set(fit2_fighters.keys())
for fighter_type in sorted(all_fighter_types):
count1 = fit1_fighters.get(fighter_type, 0)
count2 = fit2_fighters.get(fighter_type, 0)
if count2 > count1:
diffLines.append(f"{fighter_type} x{count2 - count1}")
# Get cargo counts
fit1_cargo = self.getCargoCounts(fit1)
fit2_cargo = self.getCargoCounts(fit2)
all_cargo_types = set(fit1_cargo.keys()) | set(fit2_cargo.keys())
for cargo_type in sorted(all_cargo_types):
count1 = fit1_cargo.get(cargo_type, 0)
count2 = fit2_cargo.get(cargo_type, 0)
if count2 > count1:
diffLines.append(f"{cargo_type} x{count2 - count1}")
# Get implants
fit1_implants = self.getImplantNames(fit1)
fit2_implants = self.getImplantNames(fit2)
for implant in sorted(fit2_implants - fit1_implants):
diffLines.append(f"{implant} x1")
# Get boosters
fit1_boosters = self.getBoosterNames(fit1)
fit2_boosters = self.getBoosterNames(fit2)
for booster in sorted(fit2_boosters - fit1_boosters):
diffLines.append(f"{booster} x1")
return diffLines return diffLines
def getModuleCounts(self, fit):
"""Get a counter of module types for a fit, grouped by slot type.
Returns a dict mapping FittingSlot -> Counter of module names.
Position doesn't matter, just counts by module name.
"""
counts_by_slot = {}
for module in fit.modules:
if module.isEmpty:
continue
slot = module.slot
if slot not in counts_by_slot:
counts_by_slot[slot] = Counter()
# Use item type name for comparison
name = module.item.typeName if module.item else ""
counts_by_slot[slot][name] += 1
return counts_by_slot
def getDroneCounts(self, fit):
"""Get a counter of drone types for a fit."""
counts = Counter()
for drone in fit.drones:
if drone.item:
counts[drone.item.typeName] += drone.amount
return counts
def getFighterCounts(self, fit):
"""Get a counter of fighter types for a fit."""
counts = Counter()
for fighter in fit.fighters:
if fighter.item:
counts[fighter.item.typeName] += fighter.amount
return counts
def getCargoCounts(self, fit):
"""Get a counter of cargo items for a fit."""
counts = Counter()
for cargo in fit.cargo:
if cargo.item:
counts[cargo.item.typeName] += cargo.amount
return counts
def getImplantNames(self, fit):
"""Get a set of implant names for a fit."""
names = set()
for implant in fit.implants:
if implant.item:
names.add(implant.item.typeName)
return names
def getBoosterNames(self, fit):
"""Get a set of booster names for a fit."""
names = set()
for booster in fit.boosters:
if booster.item:
names.add(booster.item.typeName)
return names