Merge branch 'master' into sso_v2

This commit is contained in:
Anton Vorobyov
2021-10-26 17:03:24 +03:00
committed by GitHub
227 changed files with 60733 additions and 18571 deletions

View File

@@ -0,0 +1,8 @@
"""
Conversion pack for October 2021 release
"""
CONVERSIONS = {
# Renamed items
"Quafe Zero": "Quafe Zero Classic",
}

View File

@@ -379,7 +379,7 @@ class Fit:
return fits
def changeMutatedValuePrelim(self, mutator, value):
pyfalog.debug("Changing mutated value for {} / {}: {} => {}".format(mutator.module, mutator.module.mutaplasmid, mutator.value, value))
pyfalog.debug("Changing mutated value for {} / {}: {} => {}".format(mutator.item, mutator.item.mutaplasmid, mutator.value, value))
if mutator.value != value:
mutator.value = value
eos.db.flush()

View File

@@ -49,7 +49,25 @@ OFFLINE_SUFFIX = '/OFFLINE'
NAME_CHARS = '[^,/\[\]]' # Characters which are allowed to be used in name
class MutationExportData:
def __init__(self):
self.reference = 1
self.mutants = {}
def formatMutants(self):
mutationLines = []
if self.mutants:
for mutantReference in sorted(self.mutants):
mutant = self.mutants[mutantReference]
mutationLines.append(renderMutant(mutant, firstPrefix='[{}] '.format(mutantReference), prefix=' '))
return '\n'.join(mutationLines)
def exportEft(fit, options, callback):
mutaData = MutationExportData()
# EFT formatted export is split in several sections, each section is
# separated from another using 2 blank lines. Sections might have several
# sub-sections, which are separated by 1 blank line
@@ -58,46 +76,15 @@ def exportEft(fit, options, callback):
header = '[{}, {}]'.format(fit.ship.item.typeName, fit.name)
# Section 1: modules, rigs, subsystems, services
modsBySlotType = {}
for module in fit.modules:
modsBySlotType.setdefault(module.slot, []).append(module)
modSection = []
mutants = {} # Format: {reference number: module}
mutantReference = 1
for slotType in SLOT_ORDER:
rackLines = []
modules = modsBySlotType.get(slotType, ())
for module in modules:
if module.item:
# if module was mutated, use base item name for export
if module.isMutated:
modName = module.baseItem.typeName
else:
modName = module.item.typeName
if module.isMutated and options[PortEftOptions.MUTATIONS]:
mutants[mutantReference] = module
mutationSuffix = ' [{}]'.format(mutantReference)
mutantReference += 1
else:
mutationSuffix = ''
modOfflineSuffix = ' {}'.format(OFFLINE_SUFFIX) if module.state == FittingModuleState.OFFLINE else ''
if module.charge and options[PortEftOptions.LOADED_CHARGES]:
rackLines.append('{}, {}{}{}'.format(
modName, module.charge.typeName, modOfflineSuffix, mutationSuffix))
else:
rackLines.append('{}{}{}'.format(modName, modOfflineSuffix, mutationSuffix))
else:
rackLines.append('[Empty {} slot]'.format(
FittingSlot(slotType).name.capitalize() if slotType is not None else ''))
if rackLines:
modSection.append('\n'.join(rackLines))
if modSection:
sections.append('\n\n'.join(modSection))
moduleExport = exportModules(fit.modules, options, mutaData=mutaData)
if moduleExport:
sections.append(moduleExport)
# Section 2: drones, fighters
minionSection = []
droneExport = exportDrones(fit.drones)
droneExport = exportDrones(
fit.drones, exportMutants=options[PortEftOptions.MUTATIONS],
mutaData=mutaData, standAlone=False)
if droneExport:
minionSection.append(droneExport)
fighterExport = exportFighters(fit.fighters)
@@ -125,14 +112,11 @@ def exportEft(fit, options, callback):
if cargoExport:
sections.append(cargoExport)
# Section 5: mutated modules' details
mutationLines = []
if mutants and options[PortEftOptions.MUTATIONS]:
for mutantReference in sorted(mutants):
mutant = mutants[mutantReference]
mutationLines.append(renderMutant(mutant, firstPrefix='[{}] '.format(mutantReference), prefix=' '))
if mutationLines:
sections.append('\n'.join(mutationLines))
# Section 5: mutated items' details
if options[PortEftOptions.MUTATIONS]:
mutationExport = mutaData.formatMutants()
if mutationExport:
sections.append(mutationExport)
text = '{}\n\n{}'.format(header, '\n\n\n'.join(sections))
@@ -142,11 +126,67 @@ def exportEft(fit, options, callback):
return text
def exportDrones(drones):
def exportModules(modules, options, mutaData=None):
if mutaData is None:
mutaData = MutationExportData()
modsBySlotType = {}
for module in modules:
modsBySlotType.setdefault(module.slot, []).append(module)
modSection = []
for slotType in SLOT_ORDER:
rackLines = []
rackModules = modsBySlotType.get(slotType, ())
for module in rackModules:
if module.item:
# if module was mutated, use base item name for export
if module.isMutated:
modName = module.baseItem.typeName
else:
modName = module.item.typeName
if module.isMutated and options[PortEftOptions.MUTATIONS]:
mutaData.mutants[mutaData.reference] = module
mutationSuffix = ' [{}]'.format(mutaData.reference)
mutaData.reference += 1
else:
mutationSuffix = ''
modOfflineSuffix = ' {}'.format(OFFLINE_SUFFIX) if module.state == FittingModuleState.OFFLINE else ''
if module.charge and options[PortEftOptions.LOADED_CHARGES]:
rackLines.append('{}, {}{}{}'.format(
modName, module.charge.typeName, modOfflineSuffix, mutationSuffix))
else:
rackLines.append('{}{}{}'.format(modName, modOfflineSuffix, mutationSuffix))
else:
rackLines.append('[Empty {} slot]'.format(
FittingSlot(slotType).name.capitalize() if slotType is not None else ''))
if rackLines:
modSection.append('\n'.join(rackLines))
return '\n\n'.join(modSection)
def exportDrones(drones, exportMutants=True, mutaData=None, standAlone=True):
def getDroneName(drone):
if drone.isMutated:
return drone.baseItem.typeName
return drone.item.typeName
if mutaData is None:
mutaData = MutationExportData()
sections = []
droneLines = []
for drone in sorted(drones, key=lambda d: d.item.typeName):
droneLines.append('{} x{}'.format(drone.item.typeName, drone.amount))
return '\n'.join(droneLines)
for drone in sorted(drones, key=getDroneName):
if drone.isMutated and exportMutants:
mutaData.mutants[mutaData.reference] = drone
mutationSuffix = ' [{}]'.format(mutaData.reference)
mutaData.reference += 1
else:
mutationSuffix = ''
droneLines.append('{} x{}{}'.format(getDroneName(drone), drone.amount, mutationSuffix))
if droneLines:
sections.append('\n'.join(droneLines))
if exportMutants and mutaData.mutants and standAlone:
sections.append(mutaData.formatMutants())
return '\n\n\n'.join(sections)
def exportFighters(fighters):
@@ -185,11 +225,11 @@ def importEft(lines):
return
aFit = AbstractFit()
aFit.mutations = _importGetMutationData(lines)
aFit.mutations = importGetMutationData(lines)
stubPattern = '^\[.+?\]$'
modulePattern = '^(?P<typeName>{0}+?)(,\s*(?P<chargeName>{0}+?))?(?P<offline>\s*{1})?(\s*\[(?P<mutation>\d+?)\])?$'.format(NAME_CHARS, OFFLINE_SUFFIX)
droneCargoPattern = '^(?P<typeName>{}+?) x(?P<amount>\d+?)$'.format(NAME_CHARS)
droneCargoPattern = '^(?P<typeName>{}+?) x(?P<amount>\d+?)(\s*\[(?P<mutation>\d+?)\])?$'.format(NAME_CHARS)
sections = []
for section in _importSectionIter(lines):
@@ -209,6 +249,8 @@ def importEft(lines):
else:
itemSpec.amount = int(m.group('amount'))
section.itemSpecs.append(itemSpec)
if m.group('mutation'):
itemSpec.mutationIdx = int(m.group('mutation'))
continue
# All other items
m = re.match(modulePattern, line)
@@ -297,7 +339,7 @@ def importEft(lines):
fit.implants.append(implant)
for booster in aFit.boosters:
fit.boosters.append(booster)
for drone in aFit.drones.values():
for drone in aFit.drones:
fit.drones.append(drone)
for fighter in aFit.fighters:
fit.fighters.append(fighter)
@@ -530,8 +572,7 @@ def _importPrepare(lines):
mutantHeaderPattern = re.compile('^\[(?P<ref>\d+)\](?P<tail>.*)')
def _importGetMutationData(lines):
data = {}
def importGetMutationData(lines):
# Format: {ref: [lines]}
mutaLinesMap = {}
currentMutaRef = None
@@ -725,6 +766,7 @@ class MultiItemSpec(BaseItemSpec):
def __init__(self, typeName):
super().__init__(typeName)
self.amount = 0
self.mutationIdx = None
@property
def isDrone(self):
@@ -752,7 +794,7 @@ class AbstractFit:
# Non-modules
self.implants = []
self.boosters = []
self.drones = {} # Format: {item: Drone}
self.drones = []
self.fighters = []
self.cargo = {} # Format: {item: Cargo}
# Other stuff
@@ -856,9 +898,34 @@ class AbstractFit:
def addDrone(self, itemSpec):
if itemSpec is None:
return
if itemSpec.item not in self.drones:
self.drones[itemSpec.item] = Drone(itemSpec.item)
self.drones[itemSpec.item].amount += itemSpec.amount
drone = None
if itemSpec.mutationIdx in self.mutations:
mutaItem, mutaAttrs = self.mutations[itemSpec.mutationIdx]
mutaplasmid = getDynamicItem(mutaItem.ID)
if mutaplasmid:
try:
drone = Drone(mutaplasmid.resultingItem, itemSpec.item, mutaplasmid)
except ValueError:
pass
else:
for attrID, mutator in drone.mutators.items():
if attrID in mutaAttrs:
mutator.value = mutaAttrs[attrID]
if drone is None:
try:
drone = Drone(itemSpec.item)
except ValueError:
return
drone.amount = itemSpec.amount
if drone.isMutated:
self.drones.append(drone)
else:
for fitDrone in self.drones:
if fitDrone.item.ID == itemSpec.item.ID:
fitDrone.amount += drone.amount
break
else:
self.drones.append(drone)
def addFighter(self, itemSpec):
if itemSpec is None:
@@ -875,7 +942,7 @@ class AbstractFit:
self.cargo[itemSpec.item].amount += itemSpec.amount
def _lineIter(text):
def lineIter(text):
"""Iterate over non-blank lines."""
for line in text.splitlines():
line = line.strip()
@@ -883,11 +950,11 @@ def _lineIter(text):
yield line
def parseAdditions(text):
def parseAdditions(text, mutaData=None):
items = []
sMkt = Market.getInstance()
pattern = '^(?P<typeName>{}+?)( x(?P<amount>\d+?))?$'.format(NAME_CHARS)
for line in _lineIter(text):
pattern = '^(?P<typeName>{}+?)( x(?P<amount>\d+?))?(\s*\[(?P<mutaref>\d+?)\])?$'.format(NAME_CHARS)
for line in lineIter(text):
m = re.match(pattern, line)
if not m:
continue
@@ -896,19 +963,27 @@ def parseAdditions(text):
continue
amount = m.group('amount')
amount = 1 if amount is None else int(amount)
items.append((item, amount))
mutaRef = int(m.group('mutaref')) if m.group('mutaref') else None
if mutaRef and mutaData and mutaRef in mutaData:
mutation = mutaData[mutaRef]
else:
mutation = None
items.append((item, amount, mutation))
return items
def isValidDroneImport(text):
pattern = 'x\d+$'
for line in _lineIter(text):
lines = list(lineIter(text))
mutaData = importGetMutationData(lines)
text = '\n'.join(lines)
pattern = 'x\d+(\s*\[\d+\])?$'
for line in lineIter(text):
if not re.search(pattern, line):
return False, ()
itemData = parseAdditions(text)
itemData = parseAdditions(text, mutaData=mutaData)
if not itemData:
return False, ()
for item, amount in itemData:
for item, amount, mutation in itemData:
if not item.isDrone:
return False, ()
return True, itemData
@@ -916,13 +991,13 @@ def isValidDroneImport(text):
def isValidFighterImport(text):
pattern = 'x\d+$'
for line in _lineIter(text):
for line in lineIter(text):
if not re.search(pattern, line):
return False, ()
itemData = parseAdditions(text)
if not itemData:
return False, ()
for item, amount in itemData:
for item, amount, mutation in itemData:
if not item.isFighter:
return False, ()
return True, itemData
@@ -930,13 +1005,13 @@ def isValidFighterImport(text):
def isValidCargoImport(text):
pattern = 'x\d+$'
for line in _lineIter(text):
for line in lineIter(text):
if not re.search(pattern, line):
return False, ()
itemData = parseAdditions(text)
if not itemData:
return False, ()
for item, amount in itemData:
for item, amount, mutation in itemData:
if item.isAbyssal:
return False, ()
return True, itemData
@@ -944,13 +1019,13 @@ def isValidCargoImport(text):
def isValidImplantImport(text):
pattern = 'x\d+$'
for line in _lineIter(text):
for line in lineIter(text):
if re.search(pattern, line):
return False, ()
itemData = parseAdditions(text)
if not itemData:
return False, ()
for item, amount in itemData:
for item, amount, mutation in itemData:
if not item.isImplant:
return False, ()
return True, itemData
@@ -958,13 +1033,13 @@ def isValidImplantImport(text):
def isValidBoosterImport(text):
pattern = 'x\d+$'
for line in _lineIter(text):
for line in lineIter(text):
if re.search(pattern, line):
return False, ()
itemData = parseAdditions(text)
if not itemData:
return False, ()
for item, amount in itemData:
for item, amount, mutation in itemData:
if not item.isBooster:
return False, ()
return True, itemData

View File

@@ -232,14 +232,14 @@ class Port:
# If we've got source file name which is used to describe ship name
# and first line contains something like [setup name], detect as eft config file
if re.match("\[.*\]", firstLine) and path is not None:
if re.match("^\s*\[.*\]", firstLine) and path is not None:
filename = os.path.split(path)[1]
shipName = filename.rsplit('.')[0]
return "EFT Config", True, cls.importEftCfg(shipName, lines, iportuser)
# If no file is specified and there's comma between brackets,
# consider that we have [ship, setup name] and detect like eft export format
if re.match("\[.*,.*\]", firstLine):
if re.match("^\s*\[.*,.*\]", firstLine):
return "EFT", True, (cls.importEft(lines),)
# Check if string is in DNA format