NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

mixins.py (32472B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\industry\mixins.py
      2 import collections
      3 import copy
      4 import numbers
      5 import blue
      6 from caching.memoize import Memoize
      7 from carbon.common.script.util.format import DateToBlue, FmtDate
      8 from clonegrade.const import CLONE_STATE_ALPHA
      9 from eve.client.script.environment.invControllers import POSCorpHangar, StationContainer, StationCorpHangar, StationItems, StructureCorpHangar, StructureItemHangar
     10 from eve.common.lib import appConst as const
     11 from eve.client.script.ui import eveColor
     12 from eve.common.script.sys import idCheckers
     13 from eve.common.script.sys.idCheckers import IsStation, flagCorpSAGs, IsShip
     14 import evetypes
     15 from industry.const import Reference
     16 from inventorycommon.util import GetPackagedVolume
     17 from localization.formatters.timeIntervalFormatters import FormatTimeInterval
     18 import industry
     19 import structures
     20 import localization
     21 import telemetry
     22 import inventorycommon.typeHelpers
     23 from eve.client.script.ui.shared.inventory.invCommon import CONTAINERGROUPS
     24 from industry.const import REACTION_GROUPS
     25 from eve.client.script.ui.shared.industry import industryUIConst, GetGroupName, GetProductGroupAndCategory
     26 from eve.common.script.util import industryCommon
     27 from eve.common.script.util.slimItem import SlimItem
     28 from carbonui.util.color import Color
     29 from omega.client.clone_grade_svc import get_clone_grade_svc
     30 from utillib import KeyVal
     31 from eveprefs import boot
     32 INSTALLED_TEXT = localization.GetByLabel('UI/Industry/Installed')
     33 IMPOUNTED_TEXT = localization.GetByLabel('UI/Corporations/Assets/Impounded')
     34 NON_FACILITY_TEXT = localization.GetByLabel('UI/Industry/NonFacility')
     35 AVAILABLE_TEXT = localization.GetByLabel('UI/Industry/Available')
     36 
     37 @telemetry.ZONE_METHOD
     38 def GetFacilityName(facilityID, typeID = None, solarSystemID = None):
     39     return _GetFacilityName(facilityID, typeID, solarSystemID)
     40 
     41 
     42 @Memoize(0.5)
     43 def _GetFacilityName(facilityID, typeID = None, solarSystemID = None):
     44     try:
     45         name = cfg.evelocations.Get(facilityID).name
     46     except (KeyError, AttributeError):
     47         name = None
     48         if typeID:
     49             name = evetypes.GetName(typeID)
     50 
     51     if not idCheckers.IsStation(facilityID) and solarSystemID:
     52         if name:
     53             if not (typeID and evetypes.GetCategoryID(typeID) == const.categoryStructure):
     54                 name = localization.GetByLabel('UI/Industry/LocationAndFacility', locationName=cfg.evelocations.Get(solarSystemID).name, facilityName=name)
     55         else:
     56             name = cfg.evelocations.Get(solarSystemID).name
     57     return name
     58 
     59 
     60 GROUPORDERINDEXES_BY_NUMGROUPS = ((0,),
     61  (0, 1),
     62  (1, 0, 2),
     63  (0,
     64   3,
     65   2,
     66   1),
     67  (1,
     68   3,
     69   0,
     70   4,
     71   2),
     72  (4,
     73   2,
     74   0,
     75   1,
     76   3,
     77   5))
     78 
     79 def GetMaterialsByGroups(materialsData):
     80     groups = collections.defaultdict(list)
     81     for materialData in materialsData:
     82         if materialData.IsSelectable():
     83             invGroupID = industryUIConst.GROUP_SELECTABLEITEM
     84         elif materialData.IsOptional():
     85             invGroupID = industryUIConst.GROUP_OPTIONALITEM
     86         else:
     87             typeID = materialData.typeID
     88             groupID = evetypes.GetGroupID(typeID)
     89             categoryID = evetypes.GetCategoryID(typeID)
     90             invGroupID = industryUIConst.GetIndustryGroupID(typeID, groupID, categoryID)
     91         groups[invGroupID].append(materialData)
     92 
     93     if not groups:
     94         return []
     95     for groupID, materialDataList in groups.iteritems():
     96         materialDataList.sort(key=lambda x: (-IsShip(evetypes.GetCategoryID(x.typeID)) if x.typeID else 0, x.typeID))
     97 
     98     ret = groups.items()
     99     ret.sort(key=lambda groupID_materials: (len(groupID_materials[1]), groupID_materials[0]), reverse=True)
    100     numGroups = max(len(ret) - 1, 0)
    101     groupOrderIndexes = GROUPORDERINDEXES_BY_NUMGROUPS[numGroups]
    102     return [ ret[index] for index in groupOrderIndexes ]
    103 
    104 
    105 class BlueprintMixin(object):
    106 
    107     def GetItem(self):
    108         return SlimItem(itemID=self.blueprintID, typeID=self.blueprintTypeID, ownerID=self.ownerID)
    109 
    110     def GetName(self):
    111         return evetypes.GetName(self.blueprintTypeID)
    112 
    113     def GetDescription(self):
    114         return evetypes.GetDescription(self.blueprintTypeID)
    115 
    116     def GetEstimatedUnitPrice(self):
    117         try:
    118             return inventorycommon.typeHelpers.GetAveragePrice(self.blueprintTypeID) or 0.0
    119         except KeyError:
    120             return 0.0
    121 
    122     def GetLocationName(self):
    123         if self.facilityID is None:
    124             return self.location.GetLocationName()
    125         return self.location.GetName()
    126 
    127     def GetGroupName(self):
    128         typeID = self.GetProductOrBlueprintTypeID()
    129         return GetGroupName(typeID)
    130 
    131     def GetProductOrBlueprintTypeID(self):
    132         productTypeID = self.productTypeID
    133         if productTypeID:
    134             return productTypeID
    135         else:
    136             return self.blueprintTypeID
    137 
    138     @telemetry.ZONE_METHOD
    139     def GetProductGroupAndCategory(self):
    140         typeID = self.GetProductOrBlueprintTypeID()
    141         return GetProductGroupAndCategory(typeID)
    142 
    143     def GetDistance(self):
    144         if self.facility:
    145             return self.facility.distance
    146 
    147     def GetFacilityName(self):
    148         if self.facility:
    149             return self.facility.GetName()
    150 
    151     def GetFacilityType(self):
    152         if self.facility:
    153             return self.facility.typeID
    154 
    155     def GetRunsRemainingLabel(self):
    156         return str(self.runsRemaining)
    157 
    158     def GetLabel(self):
    159         if self.quantity > 1:
    160             return '%s x %s' % (self.quantity, self.GetName())
    161         else:
    162             return self.GetName()
    163 
    164     def GetAvailabilityText(self):
    165         if self.IsInstalled():
    166             return INSTALLED_TEXT
    167         if self.IsImpounded():
    168             return IMPOUNTED_TEXT
    169         if self.facilityID is None:
    170             return NON_FACILITY_TEXT
    171         return AVAILABLE_TEXT
    172 
    173     def IsInstalled(self):
    174         return self.jobID is not None
    175 
    176     def IsImpounded(self):
    177         return bool(self.isImpounded)
    178 
    179     def IsSameBlueprint(self, bpData):
    180         if bpData is None:
    181             return False
    182         if self.blueprintTypeID != bpData.blueprintTypeID:
    183             return False
    184         if self.blueprintID != bpData.blueprintID:
    185             return False
    186         if self.locationID != bpData.locationID:
    187             return False
    188         if self.flagID != bpData.flagID:
    189             return False
    190         if self.ownerID != bpData.ownerID:
    191             return False
    192         if self.original != bpData.original:
    193             return False
    194         return True
    195 
    196     def IsAncientRelic(self):
    197         return evetypes.GetCategoryID(self.blueprintTypeID) == const.categoryAncientRelic
    198 
    199     def IsReactionBlueprint(self):
    200         return evetypes.GetGroupID(self.blueprintTypeID) in REACTION_GROUPS
    201 
    202     def IsWithinRange(self):
    203         if IsStation(self.facilityID):
    204             return self.facilityID == session.stationid
    205         if session.structureid and self.facilityID == session.structureid:
    206             return True
    207         if session.solarsystemid:
    208             bp = sm.GetService('michelle').GetBallpark()
    209             if bp is None:
    210                 return False
    211             slimItem = bp.GetInvItem(self.facilityID)
    212             if slimItem and slimItem.categoryID != const.categoryStructure:
    213                 return True
    214         return False
    215 
    216     def GetCopy(self):
    217         bpData = sm.GetService('blueprintSvc').GetBlueprintType(self.blueprintTypeID).copy()
    218         bpData.original = self.original
    219         bpData.materialEfficiency = self.materialEfficiency
    220         bpData.timeEfficiency = self.timeEfficiency
    221         bpData.runsRemaining = self.runsRemaining
    222         return bpData
    223 
    224     def GetDragData(self):
    225         dragData = None
    226         if self.IsWithinRange():
    227             try:
    228                 invController = self.location.GetInvController()
    229             except TypeError:
    230                 pass
    231             else:
    232                 item = invController.GetItem(self.blueprintID)
    233                 if item:
    234                     return KeyVal(__guid__='xtriui.InvItem', name=self.GetLabel(), item=item, rec=item)
    235 
    236         return KeyVal(__guid__='uicls.GenericDraggableForTypeID', typeID=self.blueprintTypeID, itemID=self.blueprintID, label=self.GetLabel())
    237 
    238 
    239 class ActivityMixin(object):
    240 
    241     def GetName(self):
    242         return localization.GetByLabel(industryUIConst.ACTIVITY_NAMES.get(self.activityID))
    243 
    244     def GetHint(self):
    245         return self.GetName()
    246 
    247     def GetIcon(self):
    248         return industryUIConst.ACTIVITY_ICONS_SMALL[self.activityID]
    249 
    250     def GetTime(self, runs = 1):
    251         return FormatTimeInterval(self.time * runs * const.SEC)
    252 
    253     def GetMaterialsByGroups(self):
    254         return GetMaterialsByGroups(self.materials)
    255 
    256     def IsOmegaActivity(self):
    257         return self.IsClonestateRestricted(CLONE_STATE_ALPHA)
    258 
    259     def IsClonestateRestricted(self, clonestate = None):
    260         for skill in self.skills:
    261             maxLvl = get_clone_grade_svc().get_max_skill_level(skill.typeID, clonestate)
    262             if maxLvl < skill.level:
    263                 return True
    264 
    265         return False
    266 
    267 
    268 class FacilityMixin(object):
    269 
    270     def GetName(self):
    271         return GetFacilityName(self.facilityID, self.typeID, self.solarSystemID)
    272 
    273     def GetTypeName(self):
    274         categoryID = evetypes.GetCategoryID(self.typeID)
    275         if categoryID == const.categoryStation:
    276             return evetypes.GetCategoryNameByCategory(categoryID)
    277         elif categoryID == const.categoryStructure:
    278             nameFunc = evetypes.GetName if boot.region == 'optic' else evetypes.GetEnglishName
    279             return '%s - %s ' % (nameFunc(self.typeID), evetypes.GetGroupName(self.typeID))
    280         else:
    281             return evetypes.GetGroupName(self.typeID)
    282 
    283     def GetFacilityType(self):
    284         return self.typeID
    285 
    286     def GetOwnerName(self):
    287         try:
    288             return cfg.eveowners.Get(self.ownerID).name
    289         except KeyError:
    290             return ''
    291 
    292     def GetHint(self):
    293         return self.GetName()
    294 
    295     def HasFacilityModifiers(self, activityID):
    296         for modifier in self.modifiers:
    297             if modifier.reference in (Reference.FACILITY, Reference.HULL, Reference.RIG) and modifier.activity == activityID:
    298                 return True
    299 
    300         if self.rigModifiers:
    301             for modifierTypeID, activities in self.rigModifiers.iteritems():
    302                 if activityID in activities:
    303                     return True
    304 
    305         return False
    306 
    307     def GetFacilityModifiersByActivityID(self):
    308         ret = collections.defaultdict(list)
    309         for modifier in self.modifiers:
    310             if modifier.reference in (industry.Reference.FACILITY, industry.Reference.HULL, industry.Reference.RIG) and modifier.groupID is None and modifier.categoryID is None and modifier.invTypeID is None:
    311                 ret[modifier.activity].append(modifier)
    312 
    313         return ret
    314 
    315     def GetCostIndexByActivityID(self):
    316         ret = {}
    317         for modifier in self.modifiers:
    318             if modifier.reference == industry.Reference.SYSTEM:
    319                 ret[modifier.activity] = modifier.GetAsSystemCostIndex(modifier.activity)
    320 
    321         return ret
    322 
    323     def GetCostIndex(self, activityID):
    324         return self.GetCostIndexByActivityID().get(activityID, 0.0)
    325 
    326     def CanDoActivity(self, activityID, productTypeID):
    327         if activityID not in self.activities:
    328             return False
    329         if evetypes.GetCategoryID(self.GetFacilityType()) == const.categoryStructure:
    330             accessKey = structures.services.GetServiceID(activityID, productTypeID)
    331             return self.serviceAccess.get(accessKey, None) is not None
    332         return True
    333 
    334     def GetServiceTaxes(self):
    335         if hasattr(self, 'taxRates'):
    336             return self.taxRates.copy()
    337 
    338         def _GetGroupIDs():
    339             if activityID in (industry.MANUFACTURING, industry.REACTION):
    340                 for groupID in self.activities[activityID]['groups']:
    341                     yield groupID
    342 
    343                 for groupID in evetypes.GetGroupIDsByCategories(self.activities[activityID]['categories']):
    344                     yield groupID
    345 
    346             else:
    347                 yield
    348             raise StopIteration
    349 
    350         taxRates = collections.OrderedDict()
    351         for serviceID in sorted(structures.INDUSTRY_SERVICES):
    352             if serviceID in structures.META_SERVICES:
    353                 continue
    354             activityID = structures.GetActivityID(serviceID)
    355             if activityID not in self.activities:
    356                 continue
    357             for groupID in _GetGroupIDs():
    358                 if structures.GetServiceID(activityID, None, groupID) == serviceID:
    359                     if isinstance(self.tax, numbers.Number):
    360                         tax = self.tax * 100
    361                     else:
    362                         tax = self.serviceAccess.get(serviceID)
    363                         if tax is not None:
    364                             tax *= 100
    365                     taxRates[serviceID] = tax
    366                     break
    367 
    368         self.taxRates = taxRates.copy()
    369         return taxRates
    370 
    371 
    372 class SkillMixin(object):
    373 
    374     def GetName(self):
    375         return evetypes.GetName(self.typeID)
    376 
    377     def GetHint(self):
    378         return localization.GetByLabel('UI/InfoWindow/SkillAndLevel', skill=self.typeID, skillLevel=self.level)
    379 
    380 
    381 class MaterialMixin(object):
    382 
    383     def GetName(self):
    384         return evetypes.GetName(self.typeID)
    385 
    386     def GetDescription(self):
    387         return evetypes.GetDescription(self.typeID)
    388 
    389     def GetHint(self):
    390         return localization.GetByLabel('UI/Common/QuantityAndItem', item=self.typeID, quantity=self.quantity)
    391 
    392     def GetEstimatedUnitPrice(self):
    393         try:
    394             return inventorycommon.typeHelpers.GetAveragePrice(self.typeID) or 0.0
    395         except KeyError:
    396             return 0.0
    397 
    398     def GetPackagedVolume(self):
    399         try:
    400             return GetPackagedVolume(self.typeID) or 0.0
    401         except KeyError:
    402             return 0.0
    403 
    404     def IsOptional(self):
    405         return bool(self.options)
    406 
    407     def IsSelectable(self):
    408         if not self.IsOptional():
    409             return False
    410         typeIDs = [ material.typeID for material in self.options ]
    411         return None not in typeIDs
    412 
    413     def IsOptionSelected(self):
    414         return self.typeID is not None
    415 
    416 
    417 class JobMixin(object):
    418 
    419     def GetStartDateLabel(self):
    420         return FmtDate(DateToBlue(self.startDate), 'ls')
    421 
    422     def GetEndDateLabel(self):
    423         return FmtDate(DateToBlue(self.endDate), 'ls')
    424 
    425     def GetJobTimeLeftLabel(self):
    426         if self.status == industry.STATUS_UNSUBMITTED:
    427             time = self.time.total_seconds() * const.SEC
    428         elif self.status == industry.STATUS_INSTALLED:
    429             time = DateToBlue(self.endDate) - blue.os.GetWallclockTime()
    430         elif self.status == industry.STATUS_PAUSED:
    431             time = DateToBlue(self.endDate) - DateToBlue(self.pauseDate)
    432         elif self.status == industry.STATUS_READY:
    433             time = 0
    434         else:
    435             return '-'
    436         time = long(max(time, 0))
    437         return FormatTimeInterval(time)
    438 
    439     def GetJobStateLabel(self):
    440         if self.status <= industry.STATUS_READY:
    441             return self.GetJobTimeLeftLabel()
    442         elif self.status == industry.STATUS_DELIVERED:
    443             if self.activityID == industry.INVENTION:
    444                 if self.successfulRuns == 0:
    445                     return '<color=red>%s' % localization.GetByLabel('UI/Industry/JobFailed')
    446                 else:
    447                     return localization.GetByLabel('UI/Industry/PartiallySucceeded', numSucceeded=self.successfulRuns, numTotal=self.runs)
    448             return localization.GetByLabel('UI/Industry/Succeeded')
    449         elif self.status == industry.STATUS_CANCELLED:
    450             color = Color.RGBtoHex(*industryUIConst.COLOR_NOTREADY)
    451             return '<color=%s>%s' % (color, localization.GetByLabel('UI/Industry/Cancelled'))
    452         elif self.status == industry.STATUS_REVERTED:
    453             color = Color.RGBtoHex(*industryUIConst.COLOR_NOTREADY)
    454             return '<color=%s>%s' % (color, localization.GetByLabel('UI/Industry/Reverted'))
    455         else:
    456             return ''
    457 
    458     def GetJobProgressRatio(self):
    459         if self.status == industry.STATUS_INSTALLED:
    460             timeLeft = blue.os.GetWallclockTime() - DateToBlue(self.startDate)
    461             totalTime = long(self.time.total_seconds()) * const.SEC
    462             return float(timeLeft) / totalTime
    463         elif self.status == industry.STATUS_PAUSED:
    464             timeLeft = blue.os.GetWallclockTime() - DateToBlue(self.startDate)
    465             totalTime = long(self.time.total_seconds()) * const.SEC
    466             return float(timeLeft) / totalTime
    467         elif self.status > industry.STATUS_COMPLETED:
    468             return 1.0
    469         else:
    470             return 0.0
    471 
    472     def GetProductTypeID(self):
    473         if self.product:
    474             return self.product.typeID
    475         return self.productTypeID
    476 
    477     def GetProductLabel(self):
    478         if self.activityID == industry.MANUFACTURING:
    479             return self.product.GetName()
    480         if self.activityID in (industry.RESEARCH_MATERIAL, industry.RESEARCH_TIME):
    481             return localization.GetByLabel('UI/Industry/ResearchedBlueprint')
    482         if self.activityID == industry.COPYING:
    483             return localization.GetByLabel('UI/Industry/BlueprintCopy')
    484         if self.activityID == industry.INVENTION:
    485             if self.product:
    486                 return self.product.GetName()
    487             else:
    488                 return localization.GetByLabel('UI/Industry/NoOutcomeSelected')
    489 
    490     def GetProductAmountLabel(self):
    491         if self.activityID in (industry.MANUFACTURING, industry.INVENTION, industry.REACTION):
    492             return 'x %s' % self.product.quantity
    493         if self.activityID == industry.RESEARCH_MATERIAL:
    494             diff = self.product.materialEfficiency - self.blueprint.materialEfficiency
    495             return '+%s%%' % diff
    496         if self.activityID == industry.RESEARCH_TIME:
    497             diff = self.product.timeEfficiency - self.blueprint.timeEfficiency
    498             return '+%s%%' % diff
    499         if self.activityID == industry.COPYING:
    500             return 'x %s' % self.product.quantity
    501 
    502     def GetProductNewBlueprint(self):
    503         if isinstance(self.product, industry.Blueprint) and self.product.blueprintID is None:
    504             return self.product
    505 
    506     def GetRunsRemainingCaption(self):
    507         if self.activityID in (industry.MANUFACTURING, industry.INVENTION):
    508             if self.blueprint.original:
    509                 return localization.GetByLabel('UI/Industry/MaximumRuns')
    510             else:
    511                 return localization.GetByLabel('UI/Industry/RunsRemaining')
    512         else:
    513             if self.activityID in (industry.RESEARCH_MATERIAL, industry.RESEARCH_TIME):
    514                 return localization.GetByLabel('UI/Industry/LevelsRemaining')
    515             if self.activityID in (industry.COPYING, industry.REACTION):
    516                 return localization.GetByLabel('UI/Industry/MaximumRuns')
    517 
    518     def GetRunsRemainingLabel(self):
    519         remaining = industry.MAX_RUNS_HARD_CAP if self.blueprint.original else self.blueprint.runsRemaining
    520         minRemaining = min(remaining, self.maxRuns)
    521         if minRemaining is None:
    522             return industry.MAX_RUNS_HARD_CAP
    523         return minRemaining
    524 
    525     def GetStatusColor(self):
    526         if self.status == industry.STATUS_READY:
    527             return (1, 1, 1, 1)
    528         elif self.status == industry.STATUS_INSTALLED:
    529             return (1, 1, 1, 0.7)
    530         elif self.status == industry.STATUS_PAUSED:
    531             return (1, 0, 0, 0.7)
    532         else:
    533             return (1, 1, 1, 0.3)
    534 
    535     def GetInstallerName(self):
    536         return cfg.eveowners.Get(self.installerID).name
    537 
    538     @telemetry.ZONE_METHOD
    539     def GetLocationsInvControllersAndLocations(self):
    540         ret = []
    541         for location in self.locations:
    542             invController = location.GetInvController()
    543             ret.append((invController, location))
    544 
    545         ret = sorted(ret, key=self._GetSortKey)
    546         return ret
    547 
    548     @telemetry.ZONE_METHOD
    549     def _GetSortKey(self, data):
    550         invController, location = data
    551         isContainer = evetypes.GetGroupID(location.typeID) in CONTAINERGROUPS
    552         return (location.flagID, isContainer, invController.GetName())
    553 
    554     def IsInstalled(self):
    555         return self.status >= industry.STATUS_INSTALLED
    556 
    557     def HasMultipleProducts(self):
    558         return len(self.products) > 1
    559 
    560     def IsProductSelectable(self):
    561         return self.activityID == industry.INVENTION and self.HasMultipleProducts()
    562 
    563     def GetGaugeValue(self):
    564         maxRuns = self.maxRuns
    565         if not maxRuns or maxRuns == industry.MAX_RUNS_HARD_CAP:
    566             return 0.0
    567         else:
    568             return float(self.runs) / maxRuns
    569 
    570     def GetModifierCaption(self, modifierCls):
    571         if modifierCls == industry.TimeModifier:
    572             return localization.GetByLabel('UI/Industry/ModifierTimeCaption')
    573         if modifierCls == industry.CostModifier:
    574             return localization.GetByLabel('UI/Industry/ModifierCostCaption')
    575         if modifierCls == industry.MaterialModifier:
    576             return localization.GetByLabel('UI/Industry/ModifierMaterialCaption')
    577         if modifierCls == industry.ProbabilityModifier:
    578             return localization.GetByLabel('UI/Industry/ModifierProbabilityCaption')
    579 
    580     def GetModifiers(self, modifierCls):
    581         modifiers = {}
    582         for modifier in self.input_modifiers:
    583             if self._FilterModifiers(modifier, modifierCls):
    584                 if modifier.reference.value in modifiers:
    585                     modifiers[modifier.reference.value].amount *= modifier.amount
    586                 else:
    587                     modifiers[modifier.reference.value] = copy.copy(modifier)
    588 
    589         return [ modifier for ref, modifier in sorted(modifiers.items()) ]
    590 
    591     def _FilterModifiers(self, modifier, modifierCls):
    592         if isinstance(modifier, industry.CostModifier) and modifier.reference == industry.Reference.SYSTEM:
    593             return False
    594         if isinstance(modifier, industry.CostModifier) and modifier.reference == industry.Reference.CLONE_STATE:
    595             return False
    596         return isinstance(modifier, modifierCls)
    597 
    598     def GetTimeSkillTypes(self):
    599         skills = []
    600         if self.activityID == industry.MANUFACTURING:
    601             skills += [const.typeIndustry, const.typeAdvancedIndustry]
    602         elif self.activityID == industry.COPYING:
    603             skills += [const.typeScience, const.typeAdvancedIndustry]
    604         elif self.activityID == industry.RESEARCH_TIME:
    605             skills += [const.typeResearch, const.typeAdvancedIndustry]
    606         elif self.activityID == industry.RESEARCH_MATERIAL:
    607             skills += [const.typeMetallurgy, const.typeAdvancedIndustry]
    608         elif self.activityID == industry.INVENTION:
    609             skills += [const.typeAdvancedIndustry]
    610         godma = sm.GetService('godma')
    611         for modifier, attribute, activityID in industryCommon.REQUIRED_SKILL_MODIFIERS:
    612             if self.activityID == activityID and modifier == industry.TimeModifier:
    613                 for skill in self.required_skills:
    614                     if godma.GetTypeAttribute(skill.typeID, attribute):
    615                         skills.append(skill.typeID)
    616 
    617         return skills
    618 
    619     def GetMaterialsByGroups(self):
    620         if self.IsInstalled():
    621             return []
    622         return GetMaterialsByGroups(self.materials)
    623 
    624     def GetFacilityName(self):
    625         try:
    626             if self.facility:
    627                 return self.facility.GetName()
    628             primeFacility = self.status < industry.STATUS_COMPLETED
    629             return sm.GetService('facilitySvc').GetFacility(self.facilityID, prime=primeFacility).GetName()
    630         except KeyError:
    631             return GetFacilityName(self.stationID, None, self.solarSystemID)
    632 
    633     def GetFacilityType(self):
    634         try:
    635             if self.facility:
    636                 return self.facility.typeID
    637             primeFacility = self.status < industry.STATUS_COMPLETED
    638             return sm.GetService('facilitySvc').GetFacility(self.facilityID, prime=primeFacility).typeID
    639         except KeyError:
    640             pass
    641 
    642     def IsPreview(self):
    643         return self.blueprintID is None
    644 
    645     def HasError(self, errorID):
    646         for errorID2, args in self.errors:
    647             if errorID == errorID2:
    648                 return True
    649 
    650         return False
    651 
    652     def GetCurrentActivity(self):
    653         return self.blueprint.activities.get(self.activityID)
    654 
    655     def GetCostWithoutAlphaTax(self):
    656         modifiers = [ x for x in self.input_modifiers if x.reference != industry.Reference.CLONE_STATE ]
    657         return self._get_cost_for_modifiers(modifiers)
    658 
    659     def GetFacilityTax(self):
    660         tax = 0
    661         if self.facility and self.facility.tax is not None:
    662             tax = self.facility.tax
    663         elif self.activity and self.facility:
    664             tax = self.activity.activity_tax(self.facility)
    665         return int(round(self._get_base_cost() * tax))
    666 
    667     def GetOutputLocationName(self):
    668         try:
    669             if self.IsOutputLocationSameAsFacility():
    670                 return localization.GetByLabel('UI/Industry/Hangar')
    671             if self.outputLocationID:
    672                 outputName = ''
    673                 outputLocation = cfg.evelocations.GetIfExists(self.outputLocationID)
    674                 if outputLocation:
    675                     name = outputLocation.locationName
    676                     if name:
    677                         outputName = name
    678                     else:
    679                         outputName = localization.GetByLabel('UI/Inventory/UnnamedContainer')
    680                 if self.outputFlagID in flagCorpSAGs:
    681                     divisionName = sm.GetService('corp').GetHangarDivisionNameFromFlagID(self.outputFlagID)
    682                     if outputName:
    683                         outputName = '%s (%s)' % (outputName, divisionName)
    684                     else:
    685                         outputName = divisionName
    686                 return outputName
    687         except Exception as e:
    688             return ''
    689 
    690     def IsOutputLocationSameAsFacility(self):
    691         try:
    692             if self.facility.facilityID and self.facility.facilityID == self.outputLocationID:
    693                 return True
    694             return False
    695         except Exception:
    696             return False
    697 
    698 
    699 class LocationMixin(object):
    700 
    701     def GetName(self):
    702         try:
    703             return self.GetInvController().GetName()
    704         except TypeError:
    705             return self.GetLocationName()
    706 
    707     def GetIcon(self):
    708         try:
    709             return self.GetInvController().GetIconName()
    710         except TypeError:
    711             try:
    712                 return inventorycommon.typeHelpers.GetIcon(self.typeID).iconName
    713             except AttributeError:
    714                 return None
    715 
    716     def GetInvController(self):
    717         groupID = evetypes.GetGroupID(self.typeID)
    718         categoryID = evetypes.GetCategoryID(self.typeID)
    719         if groupID in CONTAINERGROUPS:
    720             return StationContainer(self.itemID, self.typeID)
    721         if self.typeID == const.typeOffice:
    722             return StationCorpHangar(self.itemID, const.corpDivisionsByFlag.get(self.flagID))
    723         if groupID in (const.groupAssemblyArray, const.groupMobileLaboratory):
    724             return POSCorpHangar(self.itemID, const.corpDivisionsByFlag.get(self.flagID))
    725         if categoryID == const.categoryStructure:
    726             if self.flagID == const.flagHangar:
    727                 return StructureItemHangar(self.itemID)
    728             if self.flagID in const.corpDivisionsByFlag:
    729                 return StructureCorpHangar(self.itemID, const.corpDivisionsByFlag.get(self.flagID))
    730         if categoryID == const.categoryStation:
    731             return StationItems(self.itemID)
    732         raise TypeError
    733 
    734     def GetLocationName(self):
    735         if self.solarSystemID == const.locationAssetSafety or self.typeID == const.typeAssetSafetyWrap:
    736             typeName = evetypes.GetName(const.typeAssetSafetyWrap)
    737             wraps = []
    738             if self.ownerID == session.charid:
    739                 wraps = sm.GetService('assetSafety').GetItemsInSafetyForCharacter()
    740             elif self.ownerID == session.corpid and self.canView:
    741                 wraps = sm.GetService('assetSafety').GetItemsInSafetyForCorp()
    742             for asw in wraps:
    743                 if asw['assetWrapID'] == self.itemID:
    744                     return '%s - %s' % (typeName, asw['wrapName'])
    745 
    746             return typeName
    747         if evetypes.GetCategoryID(self.typeID) in (const.categoryStructure, const.categoryStation):
    748             return cfg.evelocations.Get(self.itemID).locationName
    749         try:
    750             locationName = cfg.evelocations.Get(self.itemID).locationName
    751         except KeyError:
    752             locationName = evetypes.GetName(self.typeID)
    753 
    754         return '%s - %s' % (sm.GetService('map').GetItem(self.solarSystemID).itemName, locationName)
    755 
    756 
    757 class ModifierMixin(object):
    758 
    759     def GetName(self):
    760         if self.reference == industry.Reference.BLUEPRINT:
    761             if isinstance(self, industry.TimeModifier):
    762                 return localization.GetByLabel('UI/Industry/BlueprintTimeEfficiency')
    763             if isinstance(self, industry.MaterialModifier):
    764                 return localization.GetByLabel('UI/Industry/BlueprintMaterialEfficiency')
    765             if isinstance(self, industry.ProbabilityModifier):
    766                 return localization.GetByLabel('UI/Industry/BlueprintBaseProbability')
    767             if isinstance(self, industry.CostModifier):
    768                 return localization.GetByLabel('UI/Industry/BlueprintCostModifier')
    769         else:
    770             if self.reference == industry.Reference.DECRYPTOR:
    771                 return localization.GetByLabel('UI/Industry/OptionalDecryptor')
    772             if self.reference == industry.Reference.SYSTEM:
    773                 return localization.GetByLabel('UI/Industry/SystemCostIndex')
    774             if self.reference == industry.Reference.FACILITY:
    775                 return localization.GetByLabel('UI/Industry/Facility')
    776             if self.reference == industry.Reference.SKILLS:
    777                 return localization.GetByLabel('UI/Industry/SkillsAndImplants')
    778             if self.reference == industry.Reference.DISTRICT:
    779                 return localization.GetByLabel('UI/Industry/DistrictModifier')
    780             if self.reference == industry.Reference.FACTION:
    781                 return localization.GetByLabel('UI/Industry/FactionModifier')
    782             if self.reference == industry.Reference.CLONE_STATE:
    783                 return localization.GetByLabel('UI/Industry/ClonestateModifier')
    784             if self.reference == industry.Reference.RIG:
    785                 return localization.GetByLabel('UI/Industry/RigModifier')
    786             if self.reference == industry.Reference.HULL:
    787                 return localization.GetByLabel('UI/Industry/HullModifier')
    788 
    789     def GetAsPercentage(self):
    790         return self.GetAsBonus() * 100.0
    791 
    792     def GetAsBonus(self):
    793         return self.amount - 1.0
    794 
    795     def GetAsSystemCostIndex(self, activityID):
    796         return self.amount
    797 
    798     def GetPercentageLabel(self, decimals = 1):
    799         percent = self.GetAsPercentage()
    800         if isinstance(self, industry.ProbabilityModifier) and self.reference == Reference.BLUEPRINT:
    801             perentageText = self._GetPercentageText(100.0 + percent, decimals)
    802             return perentageText
    803         else:
    804             color = self.GetModifierColor()
    805             if percent > 0:
    806                 perentageText = self._GetPercentageText(percent, decimals)
    807                 return '<color=%s>+%s</color>' % (Color.RGBtoHex(*color), perentageText)
    808             perentageText = self._GetPercentageText(percent, decimals)
    809             return '<color=%s>%s</color>' % (Color.RGBtoHex(*color), perentageText)
    810 
    811     def _GetPercentageText(self, percent, decimals = 1):
    812         perentageText = '%.*f%%' % (decimals, percent)
    813         return perentageText
    814 
    815     def GetModifierColor(self):
    816         amount = self.GetAsBonus()
    817         if isinstance(self, industry.ProbabilityModifier):
    818             amount = -amount
    819         if amount < 0.0:
    820             return eveColor.SUCCESS_GREEN
    821         elif amount > 0.0:
    822             return eveColor.DANGER_RED
    823         else:
    824             return Color.GRAY
    825 
    826 
    827 industry.Blueprint.extend(BlueprintMixin)
    828 industry.Activity.extend(ActivityMixin)
    829 industry.Facility.extend(FacilityMixin)
    830 industry.Skill.extend(SkillMixin)
    831 industry.Material.extend(MaterialMixin)
    832 industry.Job.extend(JobMixin)
    833 industry.JobData.extend(JobMixin)
    834 industry.Location.extend(LocationMixin)
    835 industry.modifiers.Modifier.extend(ModifierMixin)