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

notificationUtil.py (86814B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\common\script\util\notificationUtil.py
      2 from __future__ import absolute_import
      3 import sys
      4 import types
      5 import yaml
      6 import evetypes
      7 import log
      8 import utillib
      9 import operator
     10 import eve.common.script.mgt.entityConst as entities
     11 import eve.common.script.util.eveFormat as evefmt
     12 import carbon.common.script.util.format as fmtutil
     13 import eve.common.script.sys.idCheckers as idCheckers
     14 import localization
     15 import eve.common.script.mgt.appLogConst as logConst
     16 import eve.common.lib.appConst as const
     17 from billtypes.data import get_bill_type_name_id
     18 from carbon.common.script.util.linkUtil import GetShowInfoLink
     19 from carbon.common.script.util.mathCommon import FloatCloseEnough
     20 from eveprefs import boot
     21 from .notificationconst import *
     22 from corporation import voting
     23 from corporation.common.goals.link import get_goal_link
     24 from freelanceprojects.common.link import get_project_link
     25 from npcs.npccorporations import get_corporation_faction_id
     26 import logging
     27 securityLevelDescriptions = {-10: 'Notifications/SecurityStatus/SecurityDescription_-10',
     28  -9: 'Notifications/SecurityStatus/SecurityDescription_-9',
     29  -8: 'Notifications/SecurityStatus/SecurityDescription_-8',
     30  -7: 'Notifications/SecurityStatus/SecurityDescription_-7',
     31  -6: 'Notifications/SecurityStatus/SecurityDescription_-6',
     32  -5: 'Notifications/SecurityStatus/SecurityDescription_-5',
     33  -4: 'Notifications/SecurityStatus/SecurityDescription_-4',
     34  -3: 'Notifications/SecurityStatus/SecurityDescription_-3',
     35  -2: 'Notifications/SecurityStatus/SecurityDescription_-2',
     36  -1: 'Notifications/SecurityStatus/SecurityDescription_-1',
     37  0: 'Notifications/SecurityStatus/SecurityDescription_0',
     38  1: 'Notifications/SecurityStatus/SecurityDescription_1',
     39  2: 'Notifications/SecurityStatus/SecurityDescription_2',
     40  3: 'Notifications/SecurityStatus/SecurityDescription_3',
     41  4: 'Notifications/SecurityStatus/SecurityDescription_4',
     42  5: 'Notifications/SecurityStatus/SecurityDescription_5',
     43  6: 'Notifications/SecurityStatus/SecurityDescription_6',
     44  7: 'Notifications/SecurityStatus/SecurityDescription_7',
     45  8: 'Notifications/SecurityStatus/SecurityDescription_8',
     46  9: 'Notifications/SecurityStatus/SecurityDescription_9',
     47  10: 'Notifications/SecurityStatus/SecurityDescription_10'}
     48 rankLost = {const.factionCaldariState: 'UI/FactionWarfare/Ranks/RankLostCaldari',
     49  const.factionMinmatarRepublic: 'UI/FactionWarfare/Ranks/RankLostMinmatar',
     50  const.factionAmarrEmpire: 'UI/FactionWarfare/Ranks/RankLostAmarr',
     51  const.factionGallenteFederation: 'UI/FactionWarfare/Ranks/RankLostGallente',
     52  const.factionAngelCartel: 'UI/FactionWarfare/Ranks/RankLostAngel',
     53  const.factionGuristasPirates: 'UI/FactionWarfare/Ranks/RankLostGuristas'}
     54 rankGain = {const.factionCaldariState: 'UI/FactionWarfare/Ranks/RankGainCaldari',
     55  const.factionMinmatarRepublic: 'UI/FactionWarfare/Ranks/RankGainMinmatar',
     56  const.factionAmarrEmpire: 'UI/FactionWarfare/Ranks/RankGainAmarr',
     57  const.factionGallenteFederation: 'UI/FactionWarfare/Ranks/RankGainGallente',
     58  const.factionAngelCartel: 'UI/FactionWarfare/Ranks/RankGainAngel',
     59  const.factionGuristasPirates: 'UI/FactionWarfare/Ranks/RankGainGuristas'}
     60 logger = logging.getLogger('freelance_projects')
     61 
     62 def CreateItemInfoLink(itemID):
     63     item = cfg.eveowners.Get(itemID)
     64     return GetShowInfoLink(item.typeID, item.name, itemID)
     65 
     66 
     67 def CreateLocationInfoLink(locationID, locationTypeID = None):
     68     locationName = cfg.evelocations.Get(locationID).name
     69     if locationTypeID is None:
     70         if idCheckers.IsRegion(locationID):
     71             locationTypeID = const.typeRegion
     72         elif idCheckers.IsConstellation(locationID):
     73             locationTypeID = const.typeConstellation
     74         elif idCheckers.IsSolarSystem(locationID):
     75             locationTypeID = const.typeSolarSystem
     76         elif idCheckers.IsStation(locationID):
     77             if boot.role == 'client':
     78                 stationinfo = sm.RemoteSvc('stationSvc').GetStation(locationID)
     79             else:
     80                 stationinfo = sm.GetService('stationSvc').GetStation(locationID)
     81             locationTypeID = stationinfo.stationTypeID
     82     if locationTypeID is None:
     83         return locationName
     84     return localization.GetByLabel('UI/Contracts/ContractsWindow/ShowInfoLink', showInfoName=locationName, info=('showinfo', locationTypeID, locationID))
     85 
     86 
     87 def CreateTypeInfoLink(typeID):
     88     return localization.GetByLabel('UI/Contracts/ContractsWindow/ShowInfoLink', showInfoName=evetypes.GetName(typeID), info=('showinfo', typeID))
     89 
     90 
     91 def GetAgent(agentID):
     92     if boot.role == 'client':
     93         return sm.GetService('agents').GetAgentByID(agentID)
     94     else:
     95         agent = utillib.KeyVal(sm.GetService('agentMgr').GetAgentStaticInfo(agentID))
     96         if agent is None:
     97             return
     98         uiSvc = sm.GetService('ui')
     99         stationInfo = uiSvc.GetStationStaticInfo(agent.stationID)
    100         if agent.corporationID is None:
    101             agent.corporationID = uiSvc.GetStationOwner(agent.stationID)
    102         agent.factionID = get_corporation_faction_id(agent.corporationID)
    103         agent.solarsystemID = stationInfo.solarSystemID
    104         return agent
    105 
    106 
    107 def GetAgentArgs(agentID):
    108     agentInfo = GetAgent(agentID)
    109     if not agentInfo:
    110         return {}
    111     agentArgs = {'agentID': agentInfo.agentID,
    112      'agentCorpID': agentInfo.corporationID,
    113      'agentFactionID': agentInfo.factionID,
    114      'agentSolarSystemID': agentInfo.solarsystemID,
    115      'agentLocation': agentInfo.solarsystemID}
    116     if getattr(agentInfo, 'stationID', None):
    117         agentArgs['agentStationID'] = agentInfo.stationID
    118         agentArgs['agentLocation'] = agentInfo.stationID
    119     if boot.role == 'client':
    120         mapSvc = sm.GetService('map')
    121         agentArgs['agentConstellationID'] = mapSvc.GetConstellationForSolarSystem(agentInfo.solarsystemID)
    122         agentArgs['agentRegionID'] = mapSvc.GetRegionForSolarSystem(agentInfo.solarsystemID)
    123     else:
    124         ss = sm.GetService('stationSvc').GetSolarSystem(agentInfo.solarsystemID)
    125         agentArgs['agentConstellationID'] = ss.constellationID
    126         agentArgs['agentRegionID'] = ss.regionID
    127     return agentArgs
    128 
    129 
    130 def ParamCharacterTerminationNotification(notification):
    131     security = int(round(notification.data['security']))
    132     roleNameIDs = notification.data.get('roleNameIDs', None)
    133     if roleNameIDs is None or len(roleNameIDs) == 0:
    134         roleName = notification.data.get('roleName', localization.GetByLabel('UI/Generic/Person'))
    135     else:
    136         roleNames = [ localization.GetByMessageID(roleNameID) for roleNameID in roleNameIDs ]
    137         roleName = localization.formatters.FormatGenericList(roleNames, useConjunction=True)
    138     return {'securityDescription': localization.GetByLabel(securityLevelDescriptions[security], **notification.data),
    139      'corpName': CreateItemInfoLink(notification.data['corpID']),
    140      'roleName': roleName}
    141 
    142 
    143 def ParamCharacterMedalNotification(notification):
    144     if boot.role == 'client':
    145         medalDetails = sm.GetService('medals').GetMedalDetails(notification.data['medalID'])
    146     else:
    147         medalDetails = sm.GetService('corporationSvc').GetMedalDetails(notification.data['medalID'])
    148     return {'issuerCorp': CreateItemInfoLink(notification.data['corpID']),
    149      'description': medalDetails.info[0].description,
    150      'title': medalDetails.info[0].title}
    151 
    152 
    153 def ParamWarOwners(notification):
    154     return {'againstName': CreateItemInfoLink(notification.data['againstID']),
    155      'declaredByName': CreateItemInfoLink(notification.data['declaredByID'])}
    156 
    157 
    158 def ParamWarOwnersWithCost(notification):
    159     return {'againstName': CreateItemInfoLink(notification.data['againstID']),
    160      'declaredByName': CreateItemInfoLink(notification.data['declaredByID']),
    161      'costText': localization.GetByLabel('Notifications/bodyWar', **notification.data) if notification.data['cost'] else ''}
    162 
    163 
    164 def ParamSovFmtCorpID(notification):
    165     return {'corporation': CreateItemInfoLink(notification.data['corpID'])}
    166 
    167 
    168 def ParamFmtFactionWarfareCorps(notification):
    169     return {'corporationName': CreateItemInfoLink(notification.data['corpID']),
    170      'factionName': CreateItemInfoLink(notification.data['factionID'])}
    171 
    172 
    173 def ParamFmtFactionWarfareAlliances(notification):
    174     return {'allianceName': CreateItemInfoLink(notification.data['allianceID']),
    175      'factionName': CreateItemInfoLink(notification.data['factionID'])}
    176 
    177 
    178 def ParamFmtSovDamagedNotification(notification):
    179     res = {'shieldValue': notification.data['shieldValue'] * 100.0,
    180      'armorValue': notification.data['armorValue'] * 100.0,
    181      'hullValue': notification.data['hullValue'] * 100.0}
    182     aggressorID = notification.data.get('aggressorID', None)
    183     if aggressorID is not None:
    184         res['aggressor'] = CreateItemInfoLink(aggressorID)
    185     else:
    186         res['aggressor'] = localization.GetByLabel('UI/Common/Unknown')
    187     aggressorCorpID = notification.data.get('aggressorCorpID', None)
    188     if aggressorCorpID is not None:
    189         res['aggressorCorp'] = CreateItemInfoLink(aggressorCorpID)
    190     else:
    191         res['aggressorCorp'] = localization.GetByLabel('UI/Common/Unknown')
    192     aggressorAllID = notification.data.get('aggressorAllianceID', None)
    193     if aggressorAllID is not None:
    194         res['aggressorAlliance'] = CreateItemInfoLink(aggressorAllID)
    195     else:
    196         res['aggressorAlliance'] = localization.GetByLabel('UI/Common/NoAlliance')
    197     return res
    198 
    199 
    200 def ParamFmtCorpDividendNotification(notification):
    201     msgID = 'Notifications/bodyCorpPayoutDividendsNonMember'
    202     if 'isMembers' in notification.data and notification.data['isMembers']:
    203         msgID = 'Notifications/bodyCorpPayoutDividendsMember'
    204     corpName = CreateItemInfoLink(notification.data['corpID'])
    205     notification.data.update({'corporationName': corpName})
    206     return {'corporationName': corpName,
    207      'body': localization.GetByLabel(msgID, **notification.data)}
    208 
    209 
    210 def ParamFmtInsurancePayout(notification):
    211     msg = ''
    212     if notification.data['payout']:
    213         msg = localization.GetByLabel('Notifications/bodyInsurancePayoutDefault')
    214     return {'defaultPayoutText': msg}
    215 
    216 
    217 def ParamCorpOfficeExpiration(notification):
    218     if notification.data['typeID'] == const.typeOfficeRental:
    219         whereText = localization.GetByLabel('Notifications/bodyCorpOfficeExpiresOffice', **notification.data)
    220     else:
    221         whereText = localization.GetByLabel('Notifications/bodyCorpOfficeExpiresItems', **notification.data)
    222     reasonText = ''
    223     if notification.data['errorText']:
    224         reasonText = localization.GetByLabel('Notifications/bodyCorpOfficeExpiresReason', **notification.data)
    225     return {'reasonText': reasonText,
    226      'whereText': whereText}
    227 
    228 
    229 def ParamCloneActivationNotification(notification):
    230     res = {'cloningServiceText': '',
    231      'lastCloneText': '',
    232      'cloneBoughtText': '',
    233      'spLostText': ''}
    234     if notification.data['cloneStationID'] != notification.data['corpStationID']:
    235         res['cloningServiceText'] = localization.GetByLabel('Notifications/bodyCloneActivatedStationChange', **notification.data)
    236     skillPointsLost = notification.data.get('skillPointsLost', None)
    237     if skillPointsLost is not None:
    238         res['spLostText'] = localization.GetByLabel('Notifications/bodyCloneActivatedSkillPointsLost', **notification.data)
    239         lastCloned = notification.data.get('lastCloned', None)
    240         if lastCloned is not None:
    241             res['lastCloneText'] = '<br><br>' + localization.GetByLabel('Notifications/bodyCloneActivatedLastCloned', **notification.data)
    242         cloneBought = notification.data.get('cloneBought', None)
    243         if cloneBought is not None:
    244             res['cloneBoughtText'] = '<br>' + localization.GetByLabel('Notifications/bodyCloneActivatedLastPurchased', **notification.data)
    245     return res
    246 
    247 
    248 def ParamCloneActivation2Notification(notification):
    249     res = {'cloningServiceText': '',
    250      'lastCloneText': ''}
    251     if notification.data['cloneStationID'] != notification.data['corpStationID']:
    252         res['cloningServiceText'] = localization.GetByLabel('Notifications/bodyCloneActivatedStationChange2', **notification.data)
    253     lastCloned = notification.data.get('lastCloned', None)
    254     if lastCloned is not None:
    255         res['lastCloneText'] = '<br><br>' + localization.GetByLabel('Notifications/bodyCloneActivatedLastCloned2', **notification.data)
    256     return res
    257 
    258 
    259 def ParamContainerPasswordNotification(notification):
    260     res = {}
    261     stationID = notification.data.get('stationID', None)
    262     if stationID is not None:
    263         res['locationName'] = localization.GetByLabel('Notifications/subjContainerLocation', **notification.data)
    264     else:
    265         res['locationName'] = CreateLocationInfoLink(notification.data['solarSystemID'])
    266     password = notification.data.get('password', None)
    267     if password is None:
    268         res['password'] = localization.GetByLabel('Notifications/bodyContainerPasswordChangedToBlank')
    269     if notification.data['passwordType'] == 'general':
    270         res['passwordType'] = localization.GetByLabel('UI/SystemMenu/GeneralSettings/General/Header')
    271     else:
    272         res['passwordType'] = localization.GetByLabel('UI/SystemMenu/AudioAndChat/GenericConfiguration/Header')
    273     return res
    274 
    275 
    276 def ParamCustomsNotification(notification):
    277     standingPenaltySum = 0
    278     fineSum = 0
    279     res = {'factionName': CreateItemInfoLink(notification.data['factionID'])}
    280     msg = ''
    281     for lost in notification.data['lostList']:
    282         standingPenaltySum += lost['penalty']
    283         fineSum += lost['fine']
    284         responseText = ''
    285         if notification.data['shouldAttack']:
    286             responseText = localization.GetByLabel('Notifications/bodyContrabandConfiscationResponseDeadly')
    287         elif notification.data['shouldConfiscate']:
    288             responseText = localization.GetByLabel('Notifications/bodyContrabandConfiscationResponseConfiscation')
    289         msg += localization.GetByLabel('Notifications/bodyContrabandConfiscationLostItems', responseText=responseText, **lost)
    290 
    291     res['confiscatedItems'] = msg
    292     res['summaryResponseText'] = ''
    293     if notification.data['shouldAttack']:
    294         res['summaryResponseText'] = localization.GetByLabel('Notifications/bodyContrabandConfiscationSummaryResponseDeadly')
    295     elif notification.data['shouldConfiscate']:
    296         res['summaryResponseText'] = localization.GetByLabel('Notifications/bodyContrabandConfiscationSummaryResponseConfiscation')
    297     res['ideal'] = standingPenaltySum
    298     res['actual'] = standingPenaltySum / notification.data['standingDivision']
    299     res['total'] = fineSum
    300     return res
    301 
    302 
    303 def ParamInsuranceFirstShipNotification(notification):
    304     res = {}
    305     if notification.data.get('isHouseWarmingGift', 0):
    306         res['gift'] = CreateTypeInfoLink(const.deftypeHouseWarmingGift)
    307     else:
    308         res['gift'] = localization.GetByLabel('Notifications/bodyNoobShipNoGift')
    309     return res
    310 
    311 
    312 def ParamInsuranceInvalidatedNotification(notification):
    313     res = {}
    314     reason = notification.data['reason']
    315     if reason == 1:
    316         res['reason'] = localization.GetByLabel('Notifications/bodyInsuranceInvalidNotOwnedByYou', **notification.data)
    317     elif reason == 2:
    318         res['reason'] = localization.GetByLabel('Notifications/bodyInsuranceInvalidHasExpired', **notification.data)
    319     elif reason == 3:
    320         res['reason'] = localization.GetByLabel('Notifications/bodyInsuranceInvalidNoValue', **notification.data)
    321     return res
    322 
    323 
    324 def ParamSovAllClaimFailNotification(notification):
    325     reason = notification.data['reason']
    326     res = {'solarSystemID': notification.data['solarSystemID'],
    327      'corporation': CreateItemInfoLink(notification.data['corpID']),
    328      'alliance': CreateItemInfoLink(notification.data['allianceID']),
    329      'body': ''}
    330     if reason == 1:
    331         res['body'] = localization.GetByLabel('Notifications/bodySovClaimFailedByOther', **res)
    332     elif reason == 2:
    333         res['body'] = localization.GetByLabel('Notifications/bodySovClaimFailedNoAlliance', **res)
    334     elif reason == 3:
    335         res['body'] = localization.GetByLabel('Notifications/bodySovClaimFailedBillNotPaid', **res)
    336     return res
    337 
    338 
    339 def ParamAllAnchoringNotification(notification):
    340     res = {'corpName': CreateItemInfoLink(notification.data['corpID']),
    341      'allianceText': '',
    342      'otherTowersText': localization.GetByLabel('Notifications/bodyPOSAnchoredNoTowers')}
    343     allianceID = notification.data.get('allianceID', None)
    344     if allianceID is not None:
    345         allianceName = CreateItemInfoLink(notification.data['allianceID'])
    346         res['allianceText'] = localization.GetByLabel('Notifications/bodyPOSAnchoredAlliance', allianceName=allianceName)
    347     corpsPresent = notification.data.get('corpsPresent', None)
    348     if corpsPresent is not None and len(notification.data['corpsPresent']):
    349         otherTowers = localization.GetByLabel('Notifications/bodyPOSAnchoredOtherTowers')
    350         for corp in notification.data['corpsPresent']:
    351             if len(corp['towers']) > 0:
    352                 allianceText = ''
    353                 if corp['allianceID'] is not None:
    354                     allianceName = CreateItemInfoLink(corp['allianceID'])
    355                     allianceText = localization.GetByLabel('Notifications/bodyPOSAnchoredOthersTowerAlliance', allianceName=allianceName)
    356                 otherTowers += localization.GetByLabel('Notifications/bodyPOSAnchoredTowersByCorp', towerCorp=CreateItemInfoLink(corp['corpID']), allianceText=allianceText)
    357                 for tower in corp['towers']:
    358                     otherTowers += localization.GetByLabel('Notifications/bodyPOSAnchoredTower', **tower)
    359 
    360                 otherTowers += '<br>'
    361 
    362         res['otherTowersText'] = otherTowers
    363     return res
    364 
    365 
    366 def ParamJumpCloneDeleted1Notification(notification):
    367     res = {'destroyerID': notification.data['locationOwnerID']}
    368     implantListText = ''
    369     if len(notification.data['typeIDs']):
    370         implantListText += localization.GetByLabel('Notifications/bodyCloneJumpImplantDestructionHeader')
    371         for implantTypeID in notification.data['typeIDs']:
    372             msg = 'Notifications/bodyCloneJumpImplantDestructionImplantType'
    373             implantListText += localization.GetByLabel(msg, implantTypeID=int(implantTypeID))
    374 
    375     else:
    376         implantListText += localization.GetByLabel('Notifications/bodyCloneJumpImplantDestructionNone')
    377     res['implantListText'] = implantListText
    378     return res
    379 
    380 
    381 def ParamJumpCloneDeleted2Notification(notification):
    382     res = ParamJumpCloneDeleted1Notification(notification)
    383     res['destroyerID'] = notification.data['destroyerID']
    384     return res
    385 
    386 
    387 def ParamStoryLineMissionAvailableNotification(notification):
    388     agent = GetAgent(notification.senderID)
    389     if agent is None:
    390         res = notification.data
    391         res['body'] = localization.GetByLabel('Notifications/bodyAgentRetired')
    392         return res
    393     res = {'agent_corporationName': CreateItemInfoLink(agent.corporationID),
    394      'agent_agentID': agent.agentID,
    395      'agent_stationID': agent.stationID,
    396      'agent_solarsystemID': agent.solarsystemID}
    397     notification.data.update(res)
    398     if agent.stationID:
    399         res['body'] = localization.GetByLabel('Notifications/bodyStoryLineMissionAvilableStation', **notification.data)
    400     else:
    401         res['body'] = localization.GetByLabel('Notifications/bodyStoryLineMissionAvilableSpace', **notification.data)
    402     return res
    403 
    404 
    405 def ParamStationAggression1Notification(notification):
    406     aggressorID = notification.data.get('aggressorID', None)
    407     res = {'shieldDamage': int(notification.data['shieldValue'] * 100),
    408      'agressorText': ''}
    409     if aggressorID is not None:
    410         ownerOb = cfg.eveowners.Get(aggressorID)
    411         if ownerOb.IsCharacter():
    412             notification.data['aggressorCorpName'] = CreateItemInfoLink(notification.data['aggressorCorpID'])
    413             res['agressorText'] = localization.GetByLabel('Notifications/bodyOutpostAgressionAgressorCharacter', **notification.data)
    414         else:
    415             res['agressorText'] = localization.GetByLabel('Notifications/bodyOutpostAgressionAgressorOwner', owner=ownerOb.name)
    416     return res
    417 
    418 
    419 def ParamStationConquerNotification(notification):
    420     res = {'oldCorpName': CreateItemInfoLink(notification.data['oldOwnerID']),
    421      'newCorpName': CreateItemInfoLink(notification.data['newOwnerID']),
    422      'characterText': ''}
    423     charID = notification.data.get('charID')
    424     if charID is not None:
    425         res['characterText'] = localization.GetByLabel('Notifications/bodyOutpostConqueredCharacter', **notification.data)
    426     return res
    427 
    428 
    429 def ParamStationStateChangeNotification(notification):
    430     state = notification.data['state']
    431     if state == entities.STATE_IDLE:
    432         return {'subject': localization.GetByLabel('Notifications/subjOutpostServiceReenabled', **notification.data)}
    433     if state == entities.STATE_INCAPACITATED:
    434         return {'subject': localization.GetByLabel('Notifications/subjOutpostServiceDisabled', **notification.data)}
    435     return {'subject': ''}
    436 
    437 
    438 def ParamStationAggression2Notification(notification):
    439     uknMsg = localization.GetByLabel('UI/Common/Unknown')
    440     res = {'shieldDamage': notification.data.get('shieldValue', 0.0) * 100.0,
    441      'armorDamage': notification.data.get('armorValue', 0.0) * 100.0,
    442      'hullDamage': notification.data.get('hullValue', 0.0) * 100.0,
    443      'aggressor': uknMsg,
    444      'aggressorCorp': uknMsg,
    445      'aggressorAlliance': uknMsg}
    446     aggressorID = notification.data.get('aggressorID', None)
    447     if aggressorID is not None:
    448         res['aggressor'] = CreateItemInfoLink(notification.data['aggressorID'])
    449     aggressorCorpID = notification.data.get('aggressorCorpID', None)
    450     if aggressorCorpID is not None:
    451         res['aggressorCorp'] = CreateItemInfoLink(notification.data['aggressorCorpID'])
    452     aggressorAllianceID = notification.data.get('aggressorAllianceID', None)
    453     if aggressorAllianceID is None:
    454         res['aggressorAlliance'] = localization.GetByLabel('UI/Common/CorporationNotInAlliance')
    455     elif aggressorAllianceID != 0:
    456         res['aggressorAlliance'] = CreateItemInfoLink(aggressorAllianceID)
    457     return res
    458 
    459 
    460 def ParamIncursionCompletedNotification(notification):
    461     topTen = notification.data['topTen']
    462     charIDs, discarded = zip(*topTen)
    463     cfg.eveowners.Prime(charIDs)
    464     res = {'topTenString': ''}
    465     if boot.role == 'client':
    466         res['constellationID'] = sm.GetService('map').GetParent(notification.data['solarSystemID'])
    467     else:
    468         res['constellationID'] = cfg.mapSystemCache[notification.data['solarSystemID']].constellationID
    469     for index, (topTenCharacterID, topTenRewardAmount) in enumerate(topTen):
    470         topTenArgs = {'number': index + 1,
    471          'topTenCharacterID': topTenCharacterID,
    472          'LPAmount': topTenRewardAmount}
    473         if hasattr(notification.data, 'emailMessageId'):
    474             res['topTenString'] += localization.GetByMessageID(notification.data['emailMessageId'], **topTenArgs)
    475         else:
    476             res['topTenString'] += localization.GetByLabel('Notifications/bodyIncursionCompleteTopTenEntry', **topTenArgs)
    477 
    478     res['journalLink'] = '<b>' + localization.GetByLabel('UI/Neocom/JournalBtn') + '</b>'
    479     return res
    480 
    481 
    482 def ParamInvasionCompletedNotification(notification):
    483     topTen = notification.data['topTen']
    484     charIDs, discarded = zip(*topTen)
    485     cfg.eveowners.Prime(charIDs)
    486     topTenEntries = [ localization.GetByLabel('Notifications/bodyIncursionCompleteTopTenEntry', number=index + 1, topTenCharacterID=topTenCharacterID, LPAmount=topTenRewardAmount) for index, (topTenCharacterID, topTenRewardAmount) in enumerate(topTen) ]
    487     res = {'topTenString': ''.join(topTenEntries),
    488      'solarsystemID': notification.data['solarSystemID']}
    489     return res
    490 
    491 
    492 def FormatFWCharRankLossNotification(notification):
    493     factionID = notification.data['factionID']
    494     newRank = notification.data['newRank']
    495     rankLabel, rankDescription = sm.GetService('facwar').GetRankLabel(factionID, newRank)
    496     message = localization.GetByLabel(rankLost[factionID], rank=rankLabel)
    497     return (localization.GetByLabel('UI/Generic/FormatStandingTransactions/RankDemotion'), message)
    498 
    499 
    500 def FormatFWCharRankGainNotification(notification):
    501     factionID = notification.data['factionID']
    502     newRank = notification.data['newRank']
    503     rankLabel, rankDescription = sm.GetService('facwar').GetRankLabel(factionID, newRank)
    504     message = localization.GetByLabel(rankGain[factionID], rank=rankLabel)
    505     return (localization.GetByLabel('UI/Generic/FormatStandingTransactions/subjectFacwarPromotion'), message)
    506 
    507 
    508 def FormatShipReimbursementMessage(notification):
    509     if 'body' in notification.data:
    510         return (localization.GetByLabel('Notifications/subjLegacy', subject=notification.data['subject']), localization.GetByLabel('Notifications/bodyLegacy', body=notification.data['body']))
    511     subject = localization.GetByLabel('Notifications/subjShipReimbursement')
    512     bodyPars = [localization.GetByLabel('Notifications/bodyShipReimbursement', **notification.data)]
    513     bodyPars.append(localization.GetByLabel('Notifications/bodyConcordSignOff'))
    514     return (subject, '\n\n'.join(bodyPars))
    515 
    516 
    517 def FormatWarDeclared(notification):
    518     notification.data.update(ParamWarOwnersWithCost(notification))
    519     try:
    520         hqInfo = notification.data['warHQ_IdType']
    521         hqTypeID = hqInfo[1]
    522         hqItemID = hqInfo[0]
    523         hqItemName = notification.data['warHQ']
    524         notification.data['warHQ'] = GetShowInfoLink(hqTypeID, hqItemName, hqItemID)
    525     except KeyError:
    526         pass
    527 
    528     messageVersion = 1
    529     if 'warHQ' in notification.data:
    530         messageVersion = 2
    531         if 'delayHours' in notification.data:
    532             messageVersion = 3
    533     heading = localization.GetByLabel('Notifications/subjWarDeclare', **notification.data)
    534     if notification.data.get('hostileState'):
    535         label = 'Notifications/bodyWarLegal' if messageVersion < 2 else 'Notifications/bodyWarLegal2'
    536         message = localization.GetByLabel(label, **notification.data)
    537     else:
    538         label = 'Notifications/bodyWarDelayed' if messageVersion < 2 else 'Notifications/bodyWarDelayed%d' % messageVersion
    539         message = localization.GetByLabel(label, **notification.data)
    540     return (heading, message)
    541 
    542 
    543 def FormatLocateCharNotification(notification):
    544     characterID = notification.data['characterID']
    545     messageIndex = notification.data['messageIndex']
    546     agentLocation = notification.data['agentLocation']
    547     agentSystem = agentLocation.get(const.groupSolarSystem, None)
    548     agentStation = agentLocation.get(const.groupStation, None)
    549     targetLocation = notification.data['targetLocation']
    550     targetRegion = targetLocation.get(const.groupRegion, None)
    551     targetConstellation = targetLocation.get(const.groupConstellation, None)
    552     targetSystem = targetLocation.get(const.groupSolarSystem, None)
    553     targetStation = targetLocation.get(const.groupStation, None)
    554     if isinstance(targetRegion, (int, long)):
    555         targetRegion = cfg.evelocations.Get(targetRegion).name
    556     if isinstance(targetConstellation, (int, long)):
    557         targetConstellation = cfg.evelocations.Get(targetConstellation).name
    558     if isinstance(targetSystem, (int, long)):
    559         targetSystem = cfg.evelocations.Get(targetSystem).name
    560     if isinstance(targetStation, (int, long)):
    561         targetStation = cfg.evelocations.Get(targetStation).name
    562     locationText = ''
    563     if agentStation == targetStation:
    564         locationText = localization.GetByLabel('UI/Agents/Locator/InYourStation', charID=characterID)
    565     elif targetStation is not None and agentSystem == targetSystem and agentStation != targetStation:
    566         locationText = localization.GetByLabel('UI/Agents/Locator/InYourSystemInStation', charID=characterID, stationName=targetStation)
    567     elif agentSystem == targetSystem:
    568         locationText = localization.GetByLabel('UI/Agents/Locator/InYourSystem', charID=characterID)
    569     elif targetStation is None:
    570         if targetRegion is not None:
    571             locationText = localization.GetByLabel('UI/Agents/Locator/InOtherRegion', charID=characterID, systemName=targetSystem, constellationName=targetConstellation, regionName=targetRegion)
    572         elif targetConstellation is not None:
    573             locationText = localization.GetByLabel('UI/Agents/Locator/InOtherConstellation', charID=characterID, systemName=targetSystem, constellationName=targetConstellation)
    574         elif targetSystem is not None:
    575             locationText = localization.GetByLabel('UI/Agents/Locator/InOtherSystem', charID=characterID, systemName=targetSystem)
    576     elif targetRegion is not None:
    577         locationText = localization.GetByLabel('UI/Agents/Locator/InOtherRegionInStation', charID=characterID, stationName=targetStation, systemName=targetSystem, constellationName=targetConstellation, regionName=targetRegion)
    578     elif targetConstellation is not None:
    579         locationText = localization.GetByLabel('UI/Agents/Locator/InOtherConstellationInStation', charID=characterID, stationName=targetStation, systemName=targetSystem, constellationName=targetConstellation)
    580     elif targetSystem is not None:
    581         locationText = localization.GetByLabel('UI/Agents/Locator/InOtherSystemInStation', charID=characterID, stationName=targetStation, systemName=targetSystem)
    582     title = localization.GetByLabel('UI/Agents/Locator/LocatedEmailHeader', charID=characterID, linkdata=['showinfo', cfg.eveowners.Get(characterID).typeID, characterID])
    583     introLabel = ['UI/Agents/Locator/LocatedEmailIntro1', 'UI/Agents/Locator/LocatedEmailIntro2'][messageIndex]
    584     message = localization.GetByLabel(introLabel, charID=characterID, agentID=notification.senderID)
    585     message += '<br><br>'
    586     message += locationText
    587     message += '<br><br>'
    588     message += localization.GetByLabel('UI/Agents/Locator/LocatedEmailGoodbyeText', agentID=notification.senderID, linkdata=['showinfo', cfg.eveowners.Get(notification.senderID).typeID, notification.senderID])
    589     return (title, message)
    590 
    591 
    592 def FormatMissionOfferExpiredNotification(notification):
    593     if 'missionKeywords' in notification.data:
    594         messageKeywords = notification.data['missionKeywords']
    595         messageKeywords.update(GetAgentArgs(notification.senderID))
    596         messageKeywords.update({'player': notification.receiverID})
    597     else:
    598         messageKeywords = {}
    599     retVal = {}
    600     for x in ['header', 'body']:
    601         msg = notification.data[x]
    602         if isinstance(msg, (list, tuple)):
    603             msgLbl, msgArgs = msg
    604             msgArgs.update(messageKeywords)
    605             retVal[x] = localization.GetByLabel(msgLbl, **msgArgs)
    606         elif isinstance(msg, (int, long)):
    607             retVal[x] = localization.GetByMessageID(msg, **messageKeywords)
    608         else:
    609             retVal[x] = msg
    610 
    611     return (retVal['header'], retVal['body'])
    612 
    613 
    614 def FormatBillNotification(notification):
    615     billTypeID = notification.data['billTypeID']
    616     if 'currentDate' not in notification.data:
    617         notification.data['currentDate'] = notification.created
    618     notification.data['creditorsName'] = CreateItemInfoLink(notification.data['creditorID'])
    619     notification.data['debtorsName'] = CreateItemInfoLink(notification.data['debtorID'])
    620     if billTypeID == const.billTypeMarketFine:
    621         messagePath = 'Notifications/bodyBillMarketFine'
    622     elif billTypeID == const.billTypeRentalBill:
    623         messagePath = 'Notifications/bodyBillRental'
    624     elif billTypeID == const.billTypeBrokerBill:
    625         messagePath = 'Notifications/bodyBillBroker'
    626     elif billTypeID == const.billTypeWarBill:
    627         notification.data['against'] = CreateItemInfoLink(notification.data['externalID'])
    628         messagePath = 'Notifications/bodyBillWar'
    629     elif billTypeID == const.billTypeAllianceMaintainanceBill:
    630         notification.data['allianceName'] = CreateItemInfoLink(notification.data['externalID'])
    631         messagePath = 'Notifications/bodyBillAllianceMaintenance'
    632     elif billTypeID == const.billTypeSovereignityMarker:
    633         messagePath = 'Notifications/bodyBillSovereignty'
    634     elif billTypeID == const.billTypeInfrastructureHub:
    635         messagePath = 'Notifications/bodyBillInfrastructureHub'
    636     else:
    637         raise RuntimeError('Notification for a bill of unknown type %s' % billTypeID)
    638     message = localization.GetByLabel(messagePath, **notification.data)
    639     subject = localization.GetByLabel('Notifications/subjBill', **notification.data)
    640     return (subject, message)
    641 
    642 
    643 def FormatCorpNewsNotification(notification):
    644     notification.data['corpName'] = CreateItemInfoLink(notification.data['corpID'])
    645     title = ''
    646     voteType = notification.data['voteType']
    647     if voteType == voting.voteWar:
    648         notification.data['parameterCorpName'] = CreateItemInfoLink(notification.data['parameter'])
    649         if notification.data['inEffect']:
    650             title = localization.GetByLabel('Notifications/subjCorpNewsAtWar', **notification.data)
    651         else:
    652             title = localization.GetByLabel('Notifications/subjCorpNewsAtPeace', **notification.data)
    653     elif voteType == voting.voteShares:
    654         title = localization.GetByLabel('Notifications/subjCorpNewsCreatedShares', **notification.data)
    655     elif voteType == voting.voteKickMember:
    656         title = localization.GetByLabel('Notifications/subjCorpNewsExpelsMemeber', **notification.data)
    657     message = notification.data['body']
    658     if message == '':
    659         message = title
    660     return (title, message)
    661 
    662 
    663 def FormatTowerAlertNotification(notification):
    664     moonID = notification.data.get('moonID', None)
    665     if moonID is not None:
    666         notification.data['moonName'] = CreateLocationInfoLink(moonID)
    667     else:
    668         notification.data['moonName'] = localization.GetByLabel('Notifications/UnknownSystem')
    669     message = localization.GetByLabel('Notifications/bodyStarbaseDamageLocation', **notification.data)
    670     shieldValue = notification.data['shieldValue']
    671     armorValue = notification.data['armorValue']
    672     hullValue = notification.data['hullValue']
    673     if shieldValue is not None and armorValue is not None and hullValue is not None:
    674         notification.data['shieldDamage'] = int(shieldValue * 100)
    675         notification.data['armorDamage'] = int(armorValue * 100)
    676         notification.data['hullDamage'] = int(hullValue * 100)
    677         message += localization.GetByLabel('Notifications/bodyStarbaseDamageValues', **notification.data)
    678     else:
    679         message += localization.GetByLabel('Notifications/bodyStarbaseDamageMissing')
    680     if notification.data['aggressorID'] is not None:
    681         ownerOb = cfg.eveowners.Get(notification.data['aggressorID'])
    682         if ownerOb.IsCharacter():
    683             notification.data['agressorCorp'] = CreateItemInfoLink(notification.data['aggressorCorpID'])
    684             notification.data['agressorAlliance'] = '-'
    685             if notification.data['aggressorAllianceID'] is not None:
    686                 notification.data['agressorAlliance'] = CreateItemInfoLink(notification.data['aggressorAllianceID'])
    687             message += localization.GetByLabel('Notifications/bodyStarbaseDamageAttacker', **notification.data)
    688         else:
    689             aggressorName = CreateItemInfoLink(notification.data['aggressorID'])
    690             message += localization.GetByLabel('Notifications/bodyStarbaseDamageAgressorNotCharacter', aggressorName=aggressorName)
    691     return (localization.GetByLabel('Notifications/subjStarbaseDamage', **notification.data), message)
    692 
    693 
    694 def FormatTutorialNotification(notification):
    695     title = localization.GetByLabel('Notifications/subjTutorial')
    696     agent = GetAgent(notification.senderID)
    697     if agent is None:
    698         message = localization.GetByLabel('Notifications/bodyAgentRetired')
    699     else:
    700         notification.data['charID'] = notification.receiverID
    701         notification.data['corpName'] = CreateItemInfoLink(agent.corporationID)
    702         notification.data['stationID'] = agent.stationID
    703         notification.data['agentID'] = agent.agentID
    704         message = localization.GetByLabel('Notifications/bodyTutorial', **notification.data)
    705     return (title, message)
    706 
    707 
    708 def FormatTowerResourceAlertNotification(notification):
    709     if notification.data['corpID']:
    710         corpName = CreateItemInfoLink(notification.data['corpID'])
    711         msg = localization.GetByLabel('Notifications/bodyStarbaseLowResourcesCorp', corpName=corpName)
    712         allianceID = notification.data.get('allianceID', None)
    713         if allianceID is not None:
    714             allianceName = CreateItemInfoLink(notification.data['allianceID'])
    715             msg += localization.GetByLabel('Notifications/bodyStarbaseLowResourcesAlliance', allianceName=allianceName)
    716         notification.data['corpAllianceText'] = msg
    717     else:
    718         notification.data['corpAllianceText'] = ''
    719     message = localization.GetByLabel('Notifications/bodyStarbaseLowResources', **notification.data)
    720     for want in notification.data['wants']:
    721         message += localization.GetByLabel('Notifications/bodyStarbaseLowResourcesWants', **want)
    722 
    723     return (localization.GetByLabel('Notifications/subjStarbaseLowResources', **notification.data), message)
    724 
    725 
    726 def FormatOrbitalAttackedNotification(notification):
    727     uknMsg = localization.GetByLabel('UI/Common/Unknown')
    728     res = {'shieldValue': notification.data.get('shieldLevel', 0.0) * 100.0,
    729      'planetID': notification.data['planetID'],
    730      'planetLinkArgs': ['showinfo', notification.data['planetTypeID'], notification.data['planetID']],
    731      'typeID': notification.data['typeID'],
    732      'solarSystemID': notification.data['solarSystemID'],
    733      'aggressor': notification.data['aggressorID'],
    734      'aggressorCorp': uknMsg,
    735      'aggressorAlliance': uknMsg}
    736     if notification.data['aggressorCorpID'] is not None:
    737         res['aggressorCorp'] = CreateItemInfoLink(notification.data['aggressorCorpID'])
    738     aggressorAllianceID = notification.data.get('aggressorAllianceID', None)
    739     if aggressorAllianceID is None:
    740         res['aggressorAlliance'] = localization.GetByLabel('UI/Common/CorporationNotInAlliance')
    741     elif aggressorAllianceID != 0:
    742         res['aggressorAlliance'] = CreateItemInfoLink(aggressorAllianceID)
    743     return res
    744 
    745 
    746 def FormatOrbitalReinforcedNotification(notification):
    747     uknMsg = localization.GetByLabel('UI/Common/Unknown')
    748     res = {'reinforceExitTime': notification.data['reinforceExitTime'],
    749      'planetID': notification.data['planetID'],
    750      'planetLinkArgs': ['showinfo', notification.data['planetTypeID'], notification.data['planetID']],
    751      'typeID': notification.data['typeID'],
    752      'solarSystemID': notification.data['solarSystemID'],
    753      'aggressor': notification.data['aggressorID'],
    754      'aggressorCorp': uknMsg,
    755      'aggressorAlliance': uknMsg}
    756     aggressorCorpID = notification.data.get('aggressorCorpID', None)
    757     if aggressorCorpID is not None:
    758         res['aggressorCorp'] = CreateItemInfoLink(notification.data['aggressorCorpID'])
    759     aggressorAllianceID = notification.data.get('aggressorAllianceID', None)
    760     if aggressorAllianceID is None:
    761         res['aggressorAlliance'] = localization.GetByLabel('UI/Common/CorporationNotInAlliance')
    762     elif aggressorAllianceID != 0:
    763         res['aggressorAlliance'] = CreateItemInfoLink(aggressorAllianceID)
    764     return res
    765 
    766 
    767 def FormatFacWarDirectEnlistmentRevoked(notification):
    768     factionName = CreateItemInfoLink(notification.data['factionID'])
    769     corporationID = notification.data.get('corporationID', None)
    770     if corporationID is not None:
    771         corporationName = CreateItemInfoLink(corporationID)
    772         reason = localization.GetByLabel('Notifications/FacWar/DirectEnlistmentRevokedReasonCorpBlocked', corporation=corporationName, faction=factionName)
    773     else:
    774         standing = notification.data['rank'] * 10
    775         requiredRank = notification.data['requiredRank']
    776         if FloatCloseEnough(requiredRank, 0, 0.00011):
    777             requiredRank = abs(requiredRank)
    778         required = requiredRank * 10
    779         reason = localization.GetByLabel('Notifications/FacWar/DirectEnlistmentRevokedStandingsTooLow', faction=factionName, standing=standing, required=required)
    780     title = localization.GetByLabel('Notifications/FacWar/subjFacWarDirectEnlistmentRevoked')
    781     body = localization.GetByLabel('Notifications/FacWar/bodyFacWarDirectEnlistmentRevoked', faction=factionName, reason=reason)
    782     return (title, body)
    783 
    784 
    785 def FormatFacWarLPPayout(notification):
    786     location = CreateLocationInfoLink(notification.data['locationID'], const.typeSolarSystem)
    787     corporation = CreateItemInfoLink(notification.data['corpID'])
    788     loyaltyPoints = notification.data['amount']
    789     eventType = notification.data['event']
    790     if loyaltyPoints > 0:
    791         title = localization.GetByLabel('Notifications/FacWar/subjLPPayout')
    792     else:
    793         title = localization.GetByLabel('Notifications/FacWar/subjNoLPPayout')
    794     if eventType == logConst.eventFacWarLPCapturePayout:
    795         bodyLabel = 'Notifications/FacWar/bodyLPCapturePayout'
    796     elif eventType == logConst.eventFacWarLPMissionPayout:
    797         bodyLabel = 'Notifications/FacWar/bodyLPMissionPayout'
    798     elif eventType == logConst.eventFacWarLPDungeonPayout:
    799         if loyaltyPoints > 0:
    800             bodyLabel = 'Notifications/FacWar/bodyLPDungeonPayout'
    801         else:
    802             bodyLabel = 'Notifications/FacWar/bodyNoLPDungeonPayout'
    803     elif eventType == logConst.eventFacWarLPDungeonDefensivePayout:
    804         if loyaltyPoints > 0:
    805             bodyLabel = 'Notifications/FacWar/bodyLPDungeonDefensivePayout'
    806         else:
    807             bodyLabel = 'Notifications/FacWar/bodyNoLPDungeonDefensivePayout'
    808     else:
    809         bodyLabel = 'Notifications/FacWar/bodyLPGenericPayout'
    810     body = localization.GetByLabel(bodyLabel, location=location, corporation=corporation, itemRefID=notification.data['itemRefID'], amount=loyaltyPoints)
    811     return (title, body)
    812 
    813 
    814 def FormatFacWarLPDisqualified(notification):
    815     location = CreateLocationInfoLink(notification.data['locationID'], const.typeSolarSystem)
    816     corporation = CreateItemInfoLink(notification.data['corpID'])
    817     title = localization.GetByLabel('Notifications/FacWar/subjLPDisqualified')
    818     eventType = notification.data['event']
    819     if eventType == logConst.eventFacWarLPCapturePayout:
    820         bodyLabel = 'Notifications/FacWar/bodyLPCaptureDisqualified'
    821     elif eventType == logConst.eventFacWarLPDungeonPayout:
    822         bodyLabel = 'Notifications/FacWar/bodyLPDungeonDisqualified'
    823     elif eventType == logConst.eventFacWarLPDungeonDefensivePayout:
    824         bodyLabel = 'Notifications/FacWar/bodyLPDefensiveDungeonDisqualified'
    825     else:
    826         bodyLabel = 'Notifications/FacWar/bodyGenericLPDisqualified'
    827     disqType = notification.data['disqualificationType']
    828     if disqType == const.rewardIneligibleReasonInvalidGroup:
    829         disqLabel = 'Notifications/FacWar/DisqualifiedReasonInvalidGroup'
    830     elif disqType == const.rewardIneligibleReasonShipCloaked:
    831         disqLabel = 'Notifications/FacWar/DisqualifiedReasonShipCloaked'
    832     elif disqType == const.rewardIneligibleReasonNotInRange:
    833         disqLabel = 'Notifications/FacWar/DisqualifiedReasonNotInRange'
    834     elif disqType == const.rewardIneligibleReasonNoISKLost:
    835         disqLabel = 'Notifications/FacWar/DisqualifiedReasonNoISKLost'
    836     elif disqType == const.rewardIneligibleReasonLowContribution:
    837         disqLabel = 'Notifications/FacWar/DisqualifiedReasonLowContribution'
    838     else:
    839         disqLabel = 'UI/Generic/Unknown'
    840     reason = localization.GetByLabel(disqLabel)
    841     body = localization.GetByLabel(bodyLabel, location=location, corporation=corporation, amount=notification.data['amount'], reason=reason)
    842     return (title, body)
    843 
    844 
    845 def FormatGoalClosed(notification):
    846     title = localization.GetByLabel('Notifications/Corporations/CorporationGoalClosedTitle')
    847     data = notification.data
    848     goal_name_link = get_goal_link(data['goal_id'], data['goal_name'])
    849     if 'closer_id' in data:
    850         closer_label = 'Notifications/Corporations/CorporationGoalClosedWithCloserBody'
    851         closer_link = CreateItemInfoLink(data['closer_id'])
    852         body = localization.GetByLabel(closer_label, goal_name=goal_name_link, goal_closer=closer_link)
    853         return (title, body)
    854     closer_label = 'Notifications/Corporations/CorporationGoalClosedBody'
    855     body = localization.GetByLabel(closer_label, goal_name=goal_name_link)
    856     return (title, body)
    857 
    858 
    859 def FormatSkinSequencingCompleted(notification):
    860     try:
    861         data = notification.data
    862         job_id = data['job_id']
    863         title = localization.GetByLabel(labelNameAndPath='Notifications/Skins/SkinSequencingCompletedTitle')
    864         body = localization.GetByLabel(labelNameAndPath='Notifications/Skins/SkinSequencingCompletedBody')
    865         log.LogInfo('Formatted EVE Mail notification for SKIN Sequencing Completed, job ID: %s' % job_id)
    866     except Exception as exc:
    867         log.LogException('Failed to format EVE Mail notification for SKIN Sequencing Completed', exc=exc)
    868         title = localization.GetByLabel('UI/Common/Unknown')
    869         body = localization.GetByLabel('UI/Common/Unknown')
    870 
    871     return (title, body)
    872 
    873 
    874 def FormatCorpLeft(notification):
    875     corp_info_link = CreateItemInfoLink(notification.data['corpID'])
    876     title = localization.GetByLabel('Notifications/Corporations/LeftTitle', corp_name=corp_info_link)
    877     body = localization.GetByLabel('Notifications/Corporations/LeftBody', corp_name=corp_info_link)
    878     return (title, body)
    879 
    880 
    881 def FormatLPAutoRedeemed(notification):
    882     issuer_id = notification.data['issuer_id']
    883     amount = fmtutil.FmtAmt(notification.data['amount'])
    884     if issuer_id == const.corpHeraldry:
    885         title = localization.GetByLabel('Notifications/DailyGoals/AutomaticEverMarksRedemptionTitle')
    886         body = localization.GetByLabel('Notifications/DailyGoals/AutomaticEverMarksRedemptionBody', amount=amount)
    887         return (title, body)
    888     corporation_name = cfg.eveowners.Get(issuer_id).name
    889     title = localization.GetByLabel('Notifications/DailyGoals/AutomaticLPRedemptionTitle', lp_corp_name=corporation_name)
    890     body = localization.GetByLabel('Notifications/DailyGoals/AutomaticLPRedemptionBody', lp_corp_name=corporation_name, amount=amount)
    891     return (title, body)
    892 
    893 
    894 def FormatSPAutoRedeemed(notification):
    895     amount = fmtutil.FmtAmt(notification.data['amount'])
    896     title = localization.GetByLabel('Notifications/DailyGoals/AutomaticSPRedemptionTitle')
    897     body = localization.GetByLabel('Notifications/DailyGoals/AutomaticSPRedemptionBody', amount=amount)
    898     return (title, body)
    899 
    900 
    901 def FormatDailyGoalItemAutoRedeemed(notification):
    902     station_id = notification.data['station_id']
    903     station_link = CreateLocationInfoLink(station_id)
    904     type_id = notification.data['type_id']
    905     item_link = CreateTypeInfoLink(type_id)
    906     title = localization.GetByLabel('Notifications/DailyGoals/DailyGoalAutoRedeemedItemTitle')
    907     body = localization.GetByLabel('Notifications/DailyGoals/DailyGoalAutoRedeemedItemDescription', item_type_id=item_link, station_id=station_link)
    908     return (title, body)
    909 
    910 
    911 def _ConstructTeamName(corpID, typeIdentifier, uniqueIdentifier):
    912     return u'{corpName}<br>Team {typeIdentifier}{uniqueIdentifier}'.format(corpName=cfg.eveowners.Get(corpID).name, typeIdentifier=typeIdentifier, uniqueIdentifier=str(uniqueIdentifier).zfill(2))
    913 
    914 
    915 def _TeamBidsForLocalization(bids, labelGenerator, numBids):
    916     return '\n'.join((labelGenerator(charID, amount) for charID, amount in sorted(bids.iteritems(), key=operator.itemgetter(1), reverse=True)[:numBids]))
    917 
    918 
    919 def _GetIndustryTeamDict(d):
    920     numBids = 5
    921     return {'location': CreateLocationInfoLink(d.data['solarSystemID'], const.typeSolarSystem),
    922      'amount': evefmt.FmtISK(d.data['yourAmount'], 0),
    923      'totalAmount': evefmt.FmtISK(d.data['totalIsk'], 0),
    924      'teamName': _ConstructTeamName(*d.data['teamNameInfo']),
    925      'numBids': min(numBids, len(d.data['systemBids'])),
    926      'bids': _TeamBidsForLocalization(d.data['systemBids'], lambda charID, iskAmount: localization.GetByLabel('Notifications/IndustryTeamContributorItem', charID=charID, iskAmount=evefmt.FmtISK(iskAmount)), numBids)}
    927 
    928 
    929 def _FormatFriendlyFireChangeStarted(d):
    930     return {'characterName': CreateItemInfoLink(d.data['charID']),
    931      'corporationName': CreateItemInfoLink(d.data['corpID']),
    932      'dueDate': d.data['timeFinished']}
    933 
    934 
    935 def _FormatFriendlyFireCompleted(d):
    936     return {'corporationName': CreateItemInfoLink(d.data['corpID'])}
    937 
    938 
    939 def _FormatSovCaptureNotification(d):
    940     return {'structureTypeID': d.data['structureTypeID'],
    941      'solarSystemID': d.data['solarSystemID']}
    942 
    943 
    944 def _FormatGameTimeReceived(d):
    945     return {'charactername': CreateItemInfoLink(d.data['senderCharID'])}
    946 
    947 
    948 def _FormatGameTimeSent(d):
    949     return {'charactername': CreateItemInfoLink(d.data['receiverCharID'])}
    950 
    951 
    952 def _FormatGiftReceived(d):
    953     return {'charactername': CreateItemInfoLink(d.data['senderCharID'])}
    954 
    955 
    956 def _FormatRaffle(d):
    957     return {'type_link': CreateTypeInfoLink(d.data['type_id']),
    958      'node_count': d.data['ticket_count'],
    959      'node_price': evefmt.FmtISK(d.data['ticket_price'])}
    960 
    961 
    962 def _FormatAgentMissionCanceledTriglavian(notification):
    963     header = localization.GetByLabel('UI/Agents/Triglavian/MissionCanceledHeader')
    964     body = localization.GetByLabel('UI/Agents/Triglavian/MissionCanceledBody')
    965     return (header, body)
    966 
    967 
    968 def _FormatAgentRetiredTriglavian(notification):
    969     header = localization.GetByLabel('UI/Agents/Triglavian/AgentRetiredHeader')
    970     body = localization.GetByLabel('UI/Agents/Triglavian/AgentRetiredBody')
    971     return (header, body)
    972 
    973 
    974 def _FormatGoalDataWithCreator(d):
    975     goal_id = d.data['goal_id']
    976     goal_name = d.data['goal_name']
    977     creator_id = d.data['creator_id']
    978     return {'goal_name': get_goal_link(goal_id, goal_name),
    979      'creator': CreateItemInfoLink(creator_id)}
    980 
    981 
    982 def _FormatGoalNameChanged(notification):
    983     goal_id = notification.data['goal_id']
    984     goal_name = notification.data['goal_name']
    985     return {'goal_name': get_goal_link(goal_id, goal_name)}
    986 
    987 
    988 def _FormatStandardProjectEveMail(notification):
    989     return {'project_name': get_project_link(notification.data['project_id'], notification.data['project_name']),
    990      'job_creator_name': CreateItemInfoLink(notification.data['creator_id']),
    991      'issuing_corporation_name': CreateItemInfoLink(notification.data['corporation_id'])}
    992 
    993 
    994 def FormatClosedFreelanceProjectEveMail(notification):
    995     d = notification.data
    996     header = localization.GetByLabel('UI/Corporations/FreelanceProjects/Notifications/FreelanceJobCancelledTitle')
    997     project_link = get_project_link(d['project_id'], d['project_name'])
    998     creator_link = CreateItemInfoLink(d['creator_id'])
    999     if d.get('admin_closer'):
   1000         closer_text = localization.GetByLabel('UI/FreelanceProjects/AdminCloser')
   1001     elif d.get('player_closer'):
   1002         closer_text = CreateItemInfoLink(d['player_closer'])
   1003     else:
   1004         closer_text = localization.GetByLabel('UI/Common/Unknown')
   1005     body = localization.GetByLabel('UI/Corporations/FreelanceProjects/Notifications/FreelanceJobCancelledBody', project_name=project_link, job_creator_name=creator_link, closing_character_name=closer_text)
   1006     return (header, body)
   1007 
   1008 
   1009 def FormatKickedFreelanceProjectEveMail(notification):
   1010     header = localization.GetByLabel('UI/Corporations/FreelanceProjects/Notifications/FreelanceJobKickedTitle')
   1011     project_id = notification.data['project_id']
   1012     project_name = notification.data['project_name']
   1013     project_link = get_project_link(project_id, project_name)
   1014     kicker = notification.data['kicker']
   1015     corporation_id = notification.data['corporation_id']
   1016     if idCheckers.IsCharacter(kicker):
   1017         body = localization.GetByLabel('UI/Corporations/FreelanceProjects/Notifications/FreelanceJobKickedBody', project_name=project_link, kicking_character_name=CreateItemInfoLink(kicker), issuing_corporation_name=CreateItemInfoLink(corporation_id))
   1018     else:
   1019         body = localization.GetByLabel('UI/Corporations/FreelanceProjects/Notifications/FreelanceJobKickedBodyNoKicker', project_name=project_link, issuing_corporation_name=CreateItemInfoLink(corporation_id))
   1020     return (header, body)
   1021 
   1022 
   1023 formatters = {notificationTypeOldLscMessages: ('Notifications/subjLegacy', 'Notifications/bodyLegacy'),
   1024  notificationTypeCharTerminationMsg: ('Notifications/subjCharacterTermination', 'Notifications/bodyCharacterTermination', ParamCharacterTerminationNotification),
   1025  notificationTypeCharMedalMsg: ('Notifications/subjCharacterMedal', 'Notifications/bodyCharacterMedal', ParamCharacterMedalNotification),
   1026  notificationTypeAllMaintenanceBillMsg: ('Notifications/subjMaintenanceBill', 'Notifications/bodyMaintenanceBill', lambda d: {'allianceName': CreateItemInfoLink(d.data['allianceID'])}),
   1027  notificationTypeAllWarDeclaredMsg: FormatWarDeclared,
   1028  notificationTypeAllWarDeclaredMsg2: FormatWarDeclared,
   1029  notificationTypeAllWarCorpJoinedAllianceMsg: ('Notifications/subjWarCorpJoinAlliance', 'Notifications/bodyWarCorpJoinAlliance', lambda d: {'alliance': CreateItemInfoLink(d.data['allianceID']),
   1030                                                 'corporation': CreateItemInfoLink(d.data['corpID'])}),
   1031  notificationTypeAllyJoinedWarDefenderMsg: ('Notifications/subjWarAllyJoinedWar', 'Notifications/bodyWarAllyJoinedWarDefender', lambda d: {'ally': CreateItemInfoLink(d.data['allyID']),
   1032                                              'aggressor': CreateItemInfoLink(d.data['aggressorID']),
   1033                                              'time': d.data['startTime']}),
   1034  notificationTypeAllyJoinedWarAggressorMsg: ('Notifications/subjWarAllyJoinedWar', 'Notifications/bodyWarAllyJoinedWarAggressor', lambda d: {'ally': CreateItemInfoLink(d.data['allyID']),
   1035                                               'defender': CreateItemInfoLink(d.data['defenderID']),
   1036                                               'time': d.data['startTime']}),
   1037  notificationTypeAllyJoinedWarAllyMsg: ('Notifications/subjWarAllyJoinedWar', 'Notifications/bodyWarAllyJoinedWarAlly', lambda d: {'ally': CreateItemInfoLink(d.data['allyID']),
   1038                                          'defender': CreateItemInfoLink(d.data['defenderID']),
   1039                                          'aggressor': CreateItemInfoLink(d.data['aggressorID']),
   1040                                          'time': d.data['startTime']}),
   1041  notificationTypeMercOfferedNegotiationMsg: ('Notifications/subjMercOfferedContract', 'Notifications/bodyMercOfferedContract', lambda d: {'merc': CreateItemInfoLink(d.data['mercID']),
   1042                                               'defender': CreateItemInfoLink(d.data['defenderID']),
   1043                                               'aggressor': CreateItemInfoLink(d.data['aggressorID']),
   1044                                               'iskOffered': evefmt.FmtISK(d.data['iskValue'])}),
   1045  notificationTypeMercOfferRetractedMsg: ('Notifications/subjMercRetractedContract', 'Notifications/bodyMercRetractedContract', lambda d: {'merc': CreateItemInfoLink(d.data['mercID']),
   1046                                           'defender': CreateItemInfoLink(d.data['defenderID']),
   1047                                           'aggressor': CreateItemInfoLink(d.data['aggressorID'])}),
   1048  notificationTypeWarSurrenderOfferMsg: ('Notifications/subjWarSurrenderOffer', 'Notifications/bodyWarSurrenderOffer', lambda d: {'owner1': CreateItemInfoLink(d.data['ownerID1']),
   1049                                          'owner2': CreateItemInfoLink(d.data['ownerID1']),
   1050                                          'iskOffered': evefmt.FmtISK(d.data['iskValue'])}),
   1051  notificationTypeWarSurrenderDeclinedMsg: ('Notifications/subjWarSurrenderDeclined', 'Notifications/bodyWarSurrenderDeclined', lambda d: {'owner': CreateItemInfoLink(d.data['ownerID']),
   1052                                             'iskOffered': evefmt.FmtISK(d.data['iskValue'])}),
   1053  notificationTypeAllyContractCancelled: ('Notifications/subjWarAllyContractCancelled', 'Notifications/bodyWarAllyContractCancelled', lambda d: {'defender': CreateItemInfoLink(d.data['defenderID']),
   1054                                           'aggressor': CreateItemInfoLink(d.data['aggressorID']),
   1055                                           'time': fmtutil.FmtDate(d.data['timeFinished'])}),
   1056  notificationTypeWarAllyOfferDeclinedMsg: ('Notifications/subjWarAllyOfferDeclined', 'Notifications/bodyWarAllyOfferDeclined', lambda d: {'defender': CreateItemInfoLink(d.data['defenderID']),
   1057                                             'aggressor': CreateItemInfoLink(d.data['aggressorID']),
   1058                                             'ally': CreateItemInfoLink(d.data['allyID']),
   1059                                             'char': CreateItemInfoLink(d.data['charID'])}),
   1060  notificationTypeAllWarSurrenderMsg: ('Notifications/subjWarSurender', 'Notifications/bodyWarSunrender', ParamWarOwners),
   1061  notificationTypeAllWarRetractedMsg: ('Notifications/subjWarRetracts', 'Notifications/bodyWarRetract', ParamWarOwners),
   1062  notificationTypeAllWarInvalidatedMsg: ('Notifications/subjWarConcordInvalidates', 'Notifications/bodyWarConcordInvalidates', ParamWarOwners),
   1063  notificationTypeCharBillMsg: FormatBillNotification,
   1064  notificationTypeCorpAllBillMsg: FormatBillNotification,
   1065  notificationTypeBillOutOfMoneyMsg: ('Notifications/subjBillOutOfMoney', 'Notifications/bodyBillOutOfMoney', lambda d: {'billType': localization.GetByMessageID(get_bill_type_name_id(d.data['billTypeID']))}),
   1066  notificationTypeBillPaidCharMsg: ('Notifications/subjBillPaid', 'Notifications/bodyBillPaid'),
   1067  notificationTypeBillPaidCorpAllMsg: ('Notifications/subjBillPaid', 'Notifications/bodyBillPaid'),
   1068  notificationTypeBountyClaimMsg: ('Notifications/subjBountyPayment', 'Notifications/bodyBountyPayment'),
   1069  notificationTypeCloneActivationMsg: ('Notifications/subjCloneActivated', 'Notifications/bodyCloneActivated', ParamCloneActivationNotification),
   1070  notificationTypeCloneActivationMsg2: ('Notifications/subjCloneActivated2', 'Notifications/bodyCloneActivated2', ParamCloneActivation2Notification),
   1071  notificationTypeCorpAppNewMsg: ('Notifications/subjCorpApplicationNew', 'Notifications/bodyApplicationNew'),
   1072  notificationTypeCorpAppRejectMsg: ('Notifications/subjCorpAppRejected', 'Notifications/bodyCorpAppRejected', lambda d: {'corporationName': CreateItemInfoLink(d.data['corpID'])}),
   1073  notificationTypeCorpAppRejectCustomMsg: ('Notifications/subjCorpAppRejected', 'Notifications/bodyCorpAppCustomRejected', lambda d: {'corporationName': CreateItemInfoLink(d.data['corpID']),
   1074                                            'customMessage': d.data['customMessage']}),
   1075  notificationTypeCorpAppAcceptMsg: ('Notifications/subjCorpAppAccepted', 'Notifications/bodyCorpAppAccepted', lambda d: {'corporationName': CreateItemInfoLink(d.data['corpID'])}),
   1076  notificationTypeCharAppAcceptMsg: ('Notifications/subjCharAppAccepted', 'Notifications/bodyCharAppAccepted', lambda d: {'corporationName': CreateItemInfoLink(d.data['corpID'])}),
   1077  notificationTypeCharAppRejectMsg: ('Notifications/subjCharAppRejected', 'Notifications/bodyCharAppRejected', lambda d: {'corporationName': CreateItemInfoLink(d.data['corpID'])}),
   1078  notificationTypeCharAppWithdrawMsg: ('Notifications/subjCharAppWithdrawn', 'Notifications/bodyCharAppWithdrawn', lambda d: {'corporationName': CreateItemInfoLink(d.data['corpID'])}),
   1079  notificationTypeCorpAppInvitedMsg: ('Notifications/subjCorpAppInvited', 'Notifications/bodyCorpAppInvited', lambda d: {'corporationName': CreateItemInfoLink(d.data['corpID'])}),
   1080  notificationTypeDustAppAcceptedMsg: ('Notifications/subjDustAppAccepted', 'Notifications/bodyDustAppAccepted', lambda d: {'corporationName': CreateItemInfoLink(d.data['corpID'])}),
   1081  notificationTypeCorpKicked: ('Notifications/Corporations/KickedTitle', 'Notifications/Corporations/KickedBody', lambda d: {'corporationName': CreateItemInfoLink(d.data['corpID'])}),
   1082  notificationTypeCorpTaxChangeMsg: ('Notifications/subjCorpTaxRateChange', 'Notifications/bodyCorpTaxRateChange', lambda d: {'corporationName': CreateItemInfoLink(d.data['corpID']),
   1083                                      'currencyName': localization.GetByLabel(d.data.get('currencyNameLabel', 'UI/Common/ISK'))}),
   1084  notificationTypeCorpNewsMsg: FormatCorpNewsNotification,
   1085  notificationTypeCharLeftCorpMsg: ('Notifications/subjCharLeftCorp', 'Notifications/bodyCharLeftCorp', lambda d: {'corporationName': CreateItemInfoLink(d.data['corpID'])}),
   1086  notificationTypeCorpNewCEOMsg: ('Notifications/subjCEOQuit', 'Notifications/bodyCEOQuit', lambda d: {'corporationName': CreateItemInfoLink(d.data['corpID'])}),
   1087  notificationTypeCorpLiquidationMsg: ('Notifications/subjCorpLiquidation', 'Notifications/bodyCorpLiquidation', lambda d: {'corporationName': CreateItemInfoLink(d.data['corpID'])}),
   1088  notificationTypeCorpDividendMsg: ('Notifications/subjCorpPayoutDividends', 'Notifications/bodyLegacy', ParamFmtCorpDividendNotification),
   1089  notificationTypeCorpVoteMsg: ('Notifications/subjCorpVote', 'Notifications/bodyLegacy'),
   1090  notificationTypeCorpVoteCEORevokedMsg: ('Notifications/subjCEORollRevoked', 'Notifications/bodyCEORollRevoked', lambda d: {'corporationName': CreateItemInfoLink(d.data['corpID'])}),
   1091  notificationTypeCorpWarDeclaredMsg: ('Notifications/subjWarDeclare', 'Notifications/bodyWarDeclare', ParamWarOwnersWithCost),
   1092  notificationTypeCorpWarDeclaredMsg2: FormatWarDeclared,
   1093  notificationTypeCorpWarFightingLegalMsg: ('Notifications/subjWarLegal', 'Notifications/bodyWarLegal', ParamWarOwnersWithCost),
   1094  notificationTypeCorpWarSurrenderMsg: ('Notifications/subjWarSurender', 'Notifications/bodyWarSunrender', ParamWarOwners),
   1095  notificationTypeCorpWarRetractedMsg: ('Notifications/subjWarRetracts', 'Notifications/bodyWarRetract', ParamWarOwners),
   1096  notificationTypeContainerPasswordMsg: ('Notifications/subjContainerPasswordChanged', 'Notifications/bodyContainerPasswordChanged', ParamContainerPasswordNotification),
   1097  notificationTypeCustomsMsg: ('Notifications/subjContrabandConfiscation', 'Notifications/bodyContrabandConfiscation', ParamCustomsNotification),
   1098  notificationTypeInsuranceFirstShipMsg: ('Notifications/subjNoobShip', 'Notifications/bodyNoobShip', ParamInsuranceFirstShipNotification),
   1099  notificationTypeInsurancePayoutMsg: ('Notifications/subjInsurancePayout', 'Notifications/bodyInsurancePayout', ParamFmtInsurancePayout),
   1100  notificationTypeInsuranceInvalidatedMsg: ('Notifications/subjInsuranceInvalid', 'Notifications/bodyInsuranceInvalid', ParamInsuranceInvalidatedNotification),
   1101  notificationTypeSovAllClaimFailMsg: ('Notifications/subjSovClaimFailed', 'Notifications/bodyLegacy', ParamSovAllClaimFailNotification),
   1102  notificationTypeSovCorpClaimFailMsg: ('Notifications/subjSovClaimFailed', 'Notifications/bodyLegacy', ParamSovAllClaimFailNotification),
   1103  notificationTypeSovAllBillLateMsg: ('Notifications/subjSovBillLate', 'Notifications/bodySovBillLate', lambda d: {'corporation': CreateItemInfoLink(d.data['corpID'])}),
   1104  notificationTypeSovCorpBillLateMsg: ('Notifications/subjSovBillLate', 'Notifications/bodySovBillLate', lambda d: {'corporation': CreateItemInfoLink(d.data['corpID'])}),
   1105  notificationTypeInfrastructureHubBillAboutToExpire: ('Notifications/subjIHubBillLate', 'Notifications/bodyIHubBillLate', lambda d: {'corporation': CreateItemInfoLink(d.data['corpID'])}),
   1106  notificationTypeSovAllClaimLostMsg: ('Notifications/subjSovAllianceClaimLost', 'Notifications/bodySovAllianceClaimLost'),
   1107  notificationTypeSovCorpClaimLostMsg: ('Notifications/subjSovCorporationClaimLost', 'Notifications/bodySovCorporationClaimLost'),
   1108  notificationTypeSovAllClaimAquiredMsg: ('Notifications/subjSovClaimAquiredAlliance', 'Notifications/bodySovClaimAquiredAlliance', lambda d: {'corporation': CreateItemInfoLink(d.data['corpID']),
   1109                                           'alliance': CreateItemInfoLink(d.data['allianceID'])}),
   1110  notificationTypeSovCorpClaimAquiredMsg: ('Notifications/subjSovClaimAquiredCorporation', 'Notifications/bodySovClaimAquiredCorporation', lambda d: {'corporation': CreateItemInfoLink(d.data['corpID']),
   1111                                            'alliance': CreateItemInfoLink(d.data['allianceID'])}),
   1112  notificationTypeAllAnchoringMsg: ('Notifications/subjPOSAnchored', 'Notifications/bodyPOSAnchored', ParamAllAnchoringNotification),
   1113  notificationTypeAllStructVulnerableMsg: ('Notifications/subjSovVulnerable', 'Notifications/bodySovVulnerable'),
   1114  notificationTypeAllStrucInvulnerableMsg: ('Notifications/subjSovNotVulnerable', 'Notifications/bodySovNotVulnerable'),
   1115  notificationTypeSovDisruptorMsg: ('Notifications/subjSovDisruptionDetected', 'Notifications/bodySovDisruptionDetected'),
   1116  notificationTypeCorpStructLostMsg: ('Notifications/subjInfraStructureLost', 'Notifications/bodyInfraStructureLost'),
   1117  notificationTypeCorpOfficeExpirationMsg: ('Notifications/SubjCorpOfficeExpires', 'Notifications/bodyCorpOfficeExpires', ParamCorpOfficeExpiration),
   1118  notificationTypeCloneRevokedMsg1: ('Notifications/subjClone', 'Notifications/bodyCloneRevoked1', lambda d: {'managerStation': CreateItemInfoLink(d.data['corpID'])}),
   1119  notificationTypeCloneMovedMsg: ('Notifications/subjClone', 'Notifications/bodyCloneMoved', lambda d: {'corporation': CreateItemInfoLink(d.data['charsInCorpID']),
   1120                                   'managerStation': CreateItemInfoLink(d.data['corpID'])}),
   1121  notificationTypeCloneRevokedMsg2: ('Notifications/subjClone', 'Notifications/bodyCloneRevoke2', lambda d: {'managerStation': CreateItemInfoLink(d.data['corpID'])}),
   1122  notificationTypeInsuranceExpirationMsg: ('Notifications/subjInsuranceExpired', 'Notifications/bodyInsuranceExpired'),
   1123  notificationTypeInsuranceIssuedMsg: ('Notifications/subjInsuranceIssued', 'Notifications/bodyInsuranceIssued', lambda d: {'typeID2': d.data['typeID']}),
   1124  notificationTypeJumpCloneDeletedMsg1: ('Notifications/subjCloneJumpImplantDestruction', 'Notifications/bodyCloneJumpImplantDestruction', ParamJumpCloneDeleted1Notification),
   1125  notificationTypeJumpCloneDeletedMsg2: ('Notifications/subjCloneJumpImplantDestruction', 'Notifications/bodyCloneJumpImplantDestruction', ParamJumpCloneDeleted2Notification),
   1126  notificationTypeFWCorpJoinMsg: ('Notifications/subjFacWarCorpJoin', 'Notifications/bodyFacWarCorpJoin', ParamFmtFactionWarfareCorps),
   1127  notificationTypeFWCorpLeaveMsg: ('Notifications/subjFacWarCorpLeave', 'Notifications/bodyFacWarCorpLeave', ParamFmtFactionWarfareCorps),
   1128  notificationTypeFWCorpKickMsg: ('Notifications/subjFacWarCorpKicked', 'Notifications/bodyFacWarCorpKicked', ParamFmtFactionWarfareCorps),
   1129  notificationTypeFWCharKickMsg: ('Notifications/subjFacWarCharKicked', 'Notifications/bodyFacWarCharKicked', ParamFmtFactionWarfareCorps),
   1130  notificationTypeFWAllianceKickMsg: ('Notifications/subjFacWarAllianceKicked', 'Notifications/bodyFacWarAllianceKicked', ParamFmtFactionWarfareAlliances),
   1131  notificationTypeFWCorpWarningMsg: ('Notifications/subjFacWarCorpWarrning', 'Notifications/bodyFacWarCorpWarrning', ParamFmtFactionWarfareCorps),
   1132  notificationTypeFWCharWarningMsg: ('Notifications/subjFacWarCharWarrning', 'Notifications/bodyFacWarCharWarrning', ParamFmtFactionWarfareCorps),
   1133  notificationTypeFWAllianceWarningMsg: ('Notifications/subjFacWarCorpWarrning', 'Notifications/bodyFacWarAllianceWarrning', ParamFmtFactionWarfareAlliances),
   1134  notificationTypeFWCharRankLossMsg: FormatFWCharRankLossNotification,
   1135  notificationTypeFWCharRankGainMsg: FormatFWCharRankGainNotification,
   1136  notificationTypeAgentMoveMsg: ('Notifications/subjLegacy', 'Notifications/bodyLegacy'),
   1137  notificationTypeTransactionReversalMsg: ('Notifications/subjMassReversal', 'Notifications/bodyLegacy'),
   1138  notificationTypeReimbursementMsg: FormatShipReimbursementMessage,
   1139  notificationTypeLocateCharMsg: FormatLocateCharNotification,
   1140  notificationTypeResearchMissionAvailableMsg: ('Notifications/subjResearchMissionAvailable', 'Notifications/bodyResearchMissionAvailable'),
   1141  notificationTypeMissionOfferExpirationMsg: FormatMissionOfferExpiredNotification,
   1142  notificationTypeMissionCanceledTriglavian: _FormatAgentMissionCanceledTriglavian,
   1143  notificationTypeAgentRetiredTriglavian: _FormatAgentRetiredTriglavian,
   1144  notificationTypeMissionTimeoutMsg: ('Notifications/subjMissionTimeout', 'Notifications/bodyMissionTimeout'),
   1145  notificationTypeStoryLineMissionAvailableMsg: ('Notifications/subjStoryLineMissionAvilable', 'Notifications/bodyLegacy', ParamStoryLineMissionAvailableNotification),
   1146  notificationTypeTowerAlertMsg: FormatTowerAlertNotification,
   1147  notificationTypeTowerResourceAlertMsg: FormatTowerResourceAlertNotification,
   1148  notificationTypeStationAggressionMsg1: ('Notifications/subjOutpostAgression', 'Notifications/bodyOutpostAgression', ParamStationAggression1Notification),
   1149  notificationTypeStationStateChangeMsg: ('Notifications/subjLegacy', 'Notifications/bodyOutpostService', ParamStationStateChangeNotification),
   1150  notificationTypeStationConquerMsg: ('Notifications/subjOutpostConquered', 'Notifications/bodyOutpostConquered', ParamStationConquerNotification),
   1151  notificationTypeStationAggressionMsg2: ('Notifications/subjOutpostAgressed', 'Notifications/bodyOutpostAgressed', ParamStationAggression2Notification),
   1152  notificationTypeFacWarCorpJoinRequestMsg: ('Notifications/subjFacWarCorpJoinRequest', 'Notifications/bodyFacWarCorpJoinRequest', ParamFmtFactionWarfareCorps),
   1153  notificationTypeFacWarCorpLeaveRequestMsg: ('Notifications/subjFacWarCorpLeaveRequest', 'Notifications/bodyFacWarCorpLeaveRequest', ParamFmtFactionWarfareCorps),
   1154  notificationTypeFacWarCorpJoinWithdrawMsg: ('Notifications/subjFacWarCorpJoinWithdraw', 'Notifications/bodyFacWarCorpJoinWithdraw', ParamFmtFactionWarfareCorps),
   1155  notificationTypeFacWarCorpLeaveWithdrawMsg: ('Notifications/subjFacWarCorpLeaveWithdraw', 'Notifications/bodyjFacWarCorpLeaveWithdraw', ParamFmtFactionWarfareCorps),
   1156  notificationTypeSovereigntyTCUDamageMsg: ('Notifications/subjSovTCUDamaged', 'Notifications/bodySovTCUDamaged', ParamFmtSovDamagedNotification),
   1157  notificationTypeSovereigntySBUDamageMsg: ('Notifications/subjSovSBUDamaged', 'Notifications/bodySovSBUDamaged', ParamFmtSovDamagedNotification),
   1158  notificationTypeSovereigntyIHDamageMsg: ('Notifications/subjSovIHDamaged', 'Notifications/bodySovIHDamaged', ParamFmtSovDamagedNotification),
   1159  notificationTypeIncursionCompletedMsg: ('Notifications/subjIncursionComplete', 'Notifications/bodyIncursionComplete', ParamIncursionCompletedNotification),
   1160  notificationTypeInvasionCompletedMsg: ('Notifications/subjInvasionComplete', 'Notifications/bodyInvasionComplete', ParamInvasionCompletedNotification),
   1161  notificationTypeTutorialMsg: FormatTutorialNotification,
   1162  notificationTypeOrbitalAttacked: ('Notifications/subjOrbitalAttacked', 'Notifications/bodyOrbitalAttacked', FormatOrbitalAttackedNotification),
   1163  notificationTypeOrbitalReinforced: ('Notifications/subjOrbitalReinforced', 'Notifications/bodyOrbitalReinforced', FormatOrbitalReinforcedNotification),
   1164  notificationTypeOwnershipTransferred: ('Notifications/subjOwnershipTransferred', 'Notifications/bodyOwnershipTransferred'),
   1165  notificationTypeFacWarLPPayoutKill: ('Notifications/FacWar/subjLPPayout', 'Notifications/FacWar/bodyLPPayoutKill', lambda d: {'location': CreateLocationInfoLink(d.data['locationID'], const.typeSolarSystem),
   1166                                        'victim': CreateItemInfoLink(d.data['charRefID']),
   1167                                        'corporation': CreateItemInfoLink(d.data['corpID'])}),
   1168  notificationTypeFacWarLPDisqualifiedKill: ('Notifications/FacWar/subjLPDisqualified', 'Notifications/FacWar/bodyLPDisqualifiedKill', lambda d: {'location': CreateLocationInfoLink(d.data['locationID'], const.typeSolarSystem),
   1169                                              'victim': CreateItemInfoLink(d.data['charRefID']),
   1170                                              'corporation': CreateItemInfoLink(d.data['corpID'])}),
   1171  notificationTypeFacWarLPPayoutEvent: FormatFacWarLPPayout,
   1172  notificationTypeFacWarLPDisqualifiedEvent: FormatFacWarLPDisqualified,
   1173  notificationTypeBountyYourBountyClaimed: ('Notifications/subjBountyYourBountyClaimed', 'Notifications/bodyBountyYourBountyClaimed', lambda d: {'victim': CreateItemInfoLink(d.data['victimID']),
   1174                                             'bountyPaid': evefmt.FmtISK(d.data['bounty'], 0)}),
   1175  notificationTypeBountyPlacedChar: ('Notifications/subjBountyPlacedChar', 'Notifications/bodyBountyPlacedChar', lambda d: {'bountyPlacer': CreateItemInfoLink(d.data['bountyPlacerID']),
   1176                                      'amount': evefmt.FmtISK(d.data['bounty'], 0)}),
   1177  notificationTypeBountyPlacedCorp: ('Notifications/subjBountyPlacedCorp', 'Notifications/bodyBountyPlacedCorp', lambda d: {'bountyPlacer': CreateItemInfoLink(d.data['bountyPlacerID']),
   1178                                      'amount': evefmt.FmtISK(d.data['bounty'], 0)}),
   1179  notificationTypeBountyPlacedAlliance: ('Notifications/subjBountyPlacedAlliance', 'Notifications/bodyBountyPlacedAlliance', lambda d: {'bountyPlacer': CreateItemInfoLink(d.data['bountyPlacerID']),
   1180                                          'amount': evefmt.FmtISK(d.data['bounty'], 0)}),
   1181  notificationTypeKillRightAvailable: ('Notifications/subjKillRightSale', 'Notifications/bodyKillRightSale', lambda d: {'charName': CreateItemInfoLink(d.data['charID']),
   1182                                        'amount': evefmt.FmtISK(d.data['price'], 0),
   1183                                        'availableToName': CreateItemInfoLink(d.data['toEntityID'])}),
   1184  notificationTypeKillRightAvailableOpen: ('Notifications/subjKillRightSaleOpen', 'Notifications/bodyKillRightSaleOpen', lambda d: {'charName': CreateItemInfoLink(d.data['charID']),
   1185                                            'amount': evefmt.FmtISK(d.data['price'], 0)}),
   1186  notificationTypeKillRightEarned: ('Notifications/subjKillRightEarned', 'Notifications/bodyKillRightEarned', lambda d: {'charName': CreateItemInfoLink(d.data['charID'])}),
   1187  notificationTypeKillRightUsed: ('Notifications/subjKillRightUsed', 'Notifications/bodyKillRightUsed', lambda d: {'charName': CreateItemInfoLink(d.data['charID'])}),
   1188  notificationTypeKillRightUnavailable: ('Notifications/subjKillRightUnavailable', 'Notifications/bodyKillRightUnavailable', lambda d: {'charName': CreateItemInfoLink(d.data['charID']),
   1189                                          'availableToName': CreateItemInfoLink(d.data['toEntityID'])}),
   1190  notificationTypeKillRightUnavailableOpen: ('Notifications/subjKillRightUnavailableAll', 'Notifications/bodyKillRightUnavailableAll', lambda d: {'charName': CreateItemInfoLink(d.data['charID'])}),
   1191  notificationTypeDeclareWar: ('Notifications/subjDeclareWar', 'Notifications/bodyDeclareWar', lambda d: {'defenderName': CreateItemInfoLink(d.data['defenderID']),
   1192                                'entityName': CreateItemInfoLink(d.data['entityID']),
   1193                                'charName': CreateItemInfoLink(d.data['charID'])}),
   1194  notificationTypeOfferedSurrender: ('Notifications/subjOfferedSurrender', 'Notifications/bodyOfferedSurrender', lambda d: {'entityName': CreateItemInfoLink(d.data['entityID']),
   1195                                      'offeredName': CreateItemInfoLink(d.data['offeredID']),
   1196                                      'charName': CreateItemInfoLink(d.data['charID']),
   1197                                      'iskOffer': evefmt.FmtISK(d.data['iskValue'], 0)}),
   1198  notificationTypeAcceptedSurrender: ('Notifications/subjAcceptedSurrender', 'Notifications/bodyAcceptedSurrender', lambda d: {'entityName': CreateItemInfoLink(d.data['entityID']),
   1199                                       'offeringName': CreateItemInfoLink(d.data['offeringID']),
   1200                                       'charName': CreateItemInfoLink(d.data['charID']),
   1201                                       'iskOffer': evefmt.FmtISK(d.data['iskValue'], 0)}),
   1202  notificationTypeMadeWarMutual: ('Notifications/subjMadeWarMutual', 'Notifications/bodyMadeWarMutual', lambda d: {'enemyName': CreateItemInfoLink(d.data['enemyID']),
   1203                                   'charName': CreateItemInfoLink(d.data['charID'])}),
   1204  notificationTypeRetractsWar: ('Notifications/subjRetractsWar', 'Notifications/bodyRetractsWar', lambda d: {'enemyName': CreateItemInfoLink(d.data['enemyID']),
   1205                                 'charName': CreateItemInfoLink(d.data['charID'])}),
   1206  notificationTypeOfferedToAlly: ('Notifications/subjOfferedToAlly', 'Notifications/bodyOfferedToAlly', lambda d: {'defenderName': CreateItemInfoLink(d.data['defenderID']),
   1207                                   'enemyName': CreateItemInfoLink(d.data['enemyID']),
   1208                                   'charName': CreateItemInfoLink(d.data['charID']),
   1209                                   'iskOffer': evefmt.FmtISK(d.data['iskValue'], 0)}),
   1210  notificationTypeOfferToAllyRetracted: ('Notifications/subjRetractedAllyOffer', 'Notifications/bodyRetractedAllyOffer', lambda d: {'allyName': CreateItemInfoLink(d.data['allyID']),
   1211                                          'defenderName': CreateItemInfoLink(d.data['defenderID']),
   1212                                          'enemyName': CreateItemInfoLink(d.data['enemyID']),
   1213                                          'charName': CreateItemInfoLink(d.data['charID'])}),
   1214  notificationTypeAcceptedAlly: ('Notifications/subjAcceptedAlly', 'Notifications/bodyAcceptedAlly', lambda d: {'allyName': CreateItemInfoLink(d.data['allyID']),
   1215                                  'enemyName': CreateItemInfoLink(d.data['enemyID']),
   1216                                  'charName': CreateItemInfoLink(d.data['charID']),
   1217                                  'iskOffer': evefmt.FmtISK(d.data['iskValue'], 0),
   1218                                  'joinTime': fmtutil.FmtDate(d.data['time'])}),
   1219  notificationTypeDistrictAttacked: ('Notifications/subjDistrictAttacked', 'Notifications/bodyDistrictAttacked', lambda d: {'DistrictName': localization.GetImportantByLabel('UI/Locations/LocationDistrictFormatter', solarSystemID=d.data['solarSystemID'], romanCelestialIndex=fmtutil.IntToRoman(d.data['celestialIndex']), districtIndex=d.data['districtIndex']),
   1220                                      'BattleTime': fmtutil.FmtDate(d.data['startDate']),
   1221                                      'AttackingCorporation': CreateItemInfoLink(d.data['attackerID'])}),
   1222  notificationTypeBattlePunishFriendlyFire: ('Notifications/subjBattlePunishFriendlyFire', 'Notifications/bodyBattlePunishFriendlyFire', lambda d: {'corporationName': CreateItemInfoLink(d.data['corporationID']),
   1223                                              'standingsChange': d.data['standingsChange'],
   1224                                              'hours': d.data['hours']}),
   1225  notificationTypeIndustryTeamAuctionWon: ('Notifications/subjIndustryTeamAuctionWon', 'Notifications/bodyIndustryTeamAuctionWon', lambda d: _GetIndustryTeamDict(d)),
   1226  notificationTypeIndustryTeamAuctionLost: ('Notifications/subjIndustryTeamAuctionLost', 'Notifications/bodyIndustryTeamAuctionLost', lambda d: _GetIndustryTeamDict(d)),
   1227  notificationTypeCorpFriendlyFireEnableTimerStarted: ('Notifications/subjCorpFriendlyFireEnableTimerStarted', 'Notifications/bodyCorpFriendlyFireEnableTimerStarted', _FormatFriendlyFireChangeStarted),
   1228  notificationTypeCorpFriendlyFireDisableTimerStarted: ('Notifications/subjCorpFriendlyFireDisableTimerStarted', 'Notifications/bodyCorpFriendlyFireDisableTimerStarted', _FormatFriendlyFireChangeStarted),
   1229  notificationTypeCorpFriendlyFireEnableTimerCompleted: ('Notifications/subjCorpFriendlyFireEnableTimerCompleted', 'Notifications/bodyCorpFriendlyFireEnableTimerCompleted', _FormatFriendlyFireCompleted),
   1230  notificationTypeCorpFriendlyFireDisableTimerCompleted: ('Notifications/subjCorpFriendlyFireDisableTimerCompleted', 'Notifications/bodyCorpFriendlyFireDisableTimerCompleted', _FormatFriendlyFireCompleted),
   1231  notificationTypeStationServiceEnabled: ('Notifications/subjSovereigntyServiceEnabled', 'Notifications/bodySovereigntyServiceEnabled', _FormatSovCaptureNotification),
   1232  notificationTypeStationServiceDisabled: ('Notifications/subjSovereigntyServiceDisabled', 'Notifications/bodySovereigntyServiceDisabled', _FormatSovCaptureNotification),
   1233  notificationTypeStationServiceHalfCaptured: ('Notifications/subjSovereigntyServiceHalfCapture', 'Notifications/bodySovereigntyServiceHalfCapture', _FormatSovCaptureNotification),
   1234  notificationTypeGameTimeReceived: ('Notifications/NotificationNames/PlexDonation', 'Notifications/Plex/PlexReceived', _FormatGameTimeReceived),
   1235  notificationTypeGameTimeSent: ('Notifications/NotificationNames/PlexDonation', 'Notifications/Plex/PlexDonated', _FormatGameTimeSent),
   1236  notificationTypeGiftReceived: ('Notifications/NotificationNames/GiftReceived', 'Notifications/GiftingSubject', _FormatGiftReceived),
   1237  notificationTypeWarDeclared: FormatWarDeclared,
   1238  notificationTypeRaffleCreated: ('Notifications/NotificationNames/HyperNetOfferCreated', 'Notifications/bodyHyperNetOfferCreated', _FormatRaffle),
   1239  notificationTypeRaffleFinished: ('Notifications/NotificationNames/HyperNetOfferFinished', 'Notifications/HyperNetOfferFinished', _FormatRaffle),
   1240  notificationTypeRaffleExpired: ('Notifications/NotificationNames/HyperNetOfferExpired', 'Notifications/HyperNetOfferExpired', _FormatRaffle),
   1241  notificationTypeExpertSystemExpired: ('Notifications/NotificationNames/ExpertSystemExpired', 'Notifications/ExpertSystemExpired', lambda data: data.data),
   1242  notificationTypeCorpGoalCreated: ('Notifications/Corporations/CorporationGoalCreatedTitle', 'Notifications/Corporations/CorporationGoalCreatedBody', _FormatGoalDataWithCreator),
   1243  notificationTypeCorpGoalCompleted: ('Notifications/Corporations/CorporationGoalCompletedTitle', 'Notifications/Corporations/CorporationGoalCompletedBody', _FormatGoalDataWithCreator),
   1244  notificationTypeCorpGoalClosed: FormatGoalClosed,
   1245  notificationTypeCorpGoalNameChange: ('Notifications/Corporations/CorporationGoalNameChangeTitle', 'Notifications/Corporations/CorporationGoalNameChangeBody', _FormatGoalNameChanged),
   1246  notificationTypeCorpGoalExpired: ('Notifications/Corporations/GoalExpiredTitle', 'Notifications/Corporations/GoalExpiredBody', _FormatGoalDataWithCreator),
   1247  notificationTypeCorpGoalLimitReached: ('Notifications/Corporations/GoalLimitReachedTitle', 'Notifications/Corporations/GoalLimitReachedBody', _FormatGoalDataWithCreator),
   1248  notificationTypeCorpLeft: FormatCorpLeft,
   1249  notificationTypeLPAutoRedeemed: FormatLPAutoRedeemed,
   1250  notificationTypeSPAutoRedeemed: FormatSPAutoRedeemed,
   1251  notificationTypeSkinSequencingCompleted: FormatSkinSequencingCompleted,
   1252  notificationTypeDailyItemRewardAutoClaimed: FormatDailyGoalItemAutoRedeemed,
   1253  notificationTypeFreelanceProjectClosed: FormatClosedFreelanceProjectEveMail,
   1254  notificationTypeFreelanceProjectCompleted: ('UI/Corporations/FreelanceProjects/Notifications/FreelanceJobCompletedTitle', 'UI/Corporations/FreelanceProjects/Notifications/FreelanceJobCompletedBody', _FormatStandardProjectEveMail),
   1255  notificationTypeFreelanceProjectCreated: ('UI/Corporations/FreelanceProjects/Notifications/FreelanceJobCreatedTitle', 'UI/Corporations/FreelanceProjects/Notifications/FreelanceJobCreatedBody', _FormatStandardProjectEveMail),
   1256  notificationTypeFreelanceProjectExpired: ('UI/Corporations/FreelanceProjects/Notifications/FreelanceJobExpiredTitle', 'UI/Corporations/FreelanceProjects/Notifications/FreelanceJobExpiredBody', _FormatStandardProjectEveMail),
   1257  notificationTypeFreelanceProjectLimitReached: ('UI/Corporations/FreelanceProjects/Notifications/FreelanceJobContributionLimitReachedTitle', 'UI/Corporations/FreelanceProjects/Notifications/FreelanceJobContributionLimitReachedBody', _FormatStandardProjectEveMail),
   1258  notificationTypeFreelanceProjectACLDeleted: ('UI/Corporations/FreelanceProjects/Notifications/FreelanceJobACLDeletedTitle', 'UI/Corporations/FreelanceProjects/Notifications/FreelanceJobACLDeletedBody', _FormatStandardProjectEveMail),
   1259  notificationTypeFreelanceProjectParticipantKicked: FormatKickedFreelanceProjectEveMail,
   1260  notificationTypeFacWarDirectEnlistmentRevoked: FormatFacWarDirectEnlistmentRevoked}
   1261 
   1262 def FormatESP(notificationDBRow):
   1263     if notificationDBRow.typeID in formatters:
   1264         return Format(notificationDBRow)
   1265     from notifications.common.formatting.notificationFormatMapping import GetFormatterForType
   1266     finstance = GetFormatterForType(notificationDBRow.typeID)
   1267     if finstance:
   1268         from notifications.common.notification import Notification
   1269         notification = Notification.FromDTO(notificationDBRow)
   1270         if type(notification.data) in types.StringTypes:
   1271             notification.data = yaml.load(notification.data, Loader=yaml.CSafeLoader)
   1272         finstance.Format(notification)
   1273         return (notification.subject, notification.body)
   1274     else:
   1275         return GetBadFormatSubjectAndBody(notificationDBRow)
   1276 
   1277 
   1278 def GetBadFormatSubjectAndBody(notification):
   1279     subject = localization.GetByLabel('Notifications/subjBadNotificationMessage')
   1280     body = localization.GetByLabel('Notifications/bodyBadNotificationMessage', id=notification.notificationID)
   1281     return (body, subject)
   1282 
   1283 
   1284 def Format(notification):
   1285     if notification.typeID in formatters:
   1286         try:
   1287             if type(notification.data) in types.StringTypes:
   1288                 notification.data = yaml.load(notification.data, Loader=yaml.CSafeLoader)
   1289             if type(formatters[notification.typeID]) is types.TupleType:
   1290                 if type(notification.data) is types.DictType:
   1291                     notification.data['notification_senderID'] = notification.senderID
   1292                     notification.data['notification_receiverID'] = notification.receiverID
   1293                     notification.data['notification_created'] = notification.created
   1294                     if len(formatters[notification.typeID]) > 2:
   1295                         funcDict = formatters[notification.typeID][2](notification)
   1296                         if type(funcDict) is types.DictType:
   1297                             notification.data.update(funcDict)
   1298                         else:
   1299                             log.LogException('Parameter Processor failed to return a Dic of data for formatter typeID=%s' % notification.typeID)
   1300                     subject = localization.GetByLabel(formatters[notification.typeID][0], **notification.data)
   1301                     body = localization.GetByLabel(formatters[notification.typeID][1], **notification.data)
   1302                 else:
   1303                     log.LogException('No formatter found for typeID=%s %s' % (notification.typeID, type(notification.data)))
   1304                     body, subject = GetBadFormatSubjectAndBody(notification)
   1305             else:
   1306                 subject, body = formatters[notification.typeID](notification)
   1307         except Exception as e:
   1308             log.LogException('Error processing notification=%s, error=%s' % (str(notification), e))
   1309             sys.exc_clear()
   1310             body, subject = GetBadFormatSubjectAndBody(notification)
   1311 
   1312         return (subject, body)
   1313     else:
   1314         log.LogException('No formatter found for typeID=%s' % notification.typeID)
   1315         body, subject = GetBadFormatSubjectAndBody(notification)
   1316         return (subject, body)