ledgerPanel.py (20239B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\ledger\ledgerPanel.py 2 import evetypes 3 import telemetry 4 import threadutils 5 import uthread 6 from caching.memoize import Memoize 7 from carbonui.button.const import HEIGHT_NORMAL 8 from carbonui.control.combo import Combo 9 from carbonui.control.dragResizeCont import DragResizeCont 10 from carbonui.primitives.container import Container 11 import carbonui.const as uiconst 12 from carbonui.primitives.containerAutoSize import ContainerAutoSize 13 from carbonui.primitives.fill import Fill 14 from carbonui.primitives.line import Line 15 from carbonui.util.sortUtil import SortListOfTuples 16 from carbonui.button.group import ButtonGroup 17 from eve.client.script.ui.control.dateSinglelineEdit import DateRangePicker 18 from eve.client.script.ui.control.eveLabel import EveLabelMedium, EveLabelLarge 19 from eve.client.script.ui.control.eveScroll import Scroll 20 import eve.client.script.ui.shared.ledger.ledgerUtil as ledgerUtil 21 from eve.client.script.ui.control.filter import OptionObject, FilterController, Filter 22 from eve.client.script.ui.control.infoIcon import MoreInfoIcon 23 from eve.client.script.ui.quickFilter import QuickFilterEdit 24 from eve.client.script.ui.shared.fitting.fittingUtil import EatSignalChangingErrors 25 from eve.client.script.ui.shared.ledger.ledgerEntry import GetLedgerEntriesPersonal, GetLedgerEntriesCorp 26 from eve.client.script.ui.shared.ledger.ledgerGraph import LedgerGraphCont 27 from eve.client.script.ui.shared.ledger.ledgerMinimap import LedgerMinimap, MiniMapSliderController 28 from eveservices.ledger import GetLedgerService 29 from localization import GetByLabel 30 from signals.signalUtil import ChangeSignalConnect 31 import blue 32 from utillib import KeyVal 33 OPTION_TYPEID = 1 34 OPTION_ITEMID = 2 35 OPTION_TYPE_GROUPINGID = 3 36 37 class LedgerPanelPersonal(Container): 38 __notifyevents__ = ['OnMiningLedgerGraphSettingChanged'] 39 default_align = uiconst.TOALL 40 41 def ApplyAttributes(self, attributes): 42 self.filterController = None 43 self.currentNormalOreGroupIDs = set() 44 self.currentMoonTypeIDsByGroupID = set() 45 self.currentPersonalDataVersion = -1 46 Container.ApplyAttributes(self, attributes) 47 addInfoIcon = attributes.addInfoIcon 48 if addInfoIcon: 49 self.AddMoreInfoIcon() 50 self.graphParent = DragResizeCont(name='scrollCont', parent=self, align=uiconst.TOTOP_PROP, minSize=200, maxSize=0.6, defaultSize=250, settingsID='ledgerPanel_left') 51 Line(parent=self.graphParent.dragArea, align=uiconst.TOBOTTOM, opacity=0.08) 52 Line(parent=self.graphParent.dragArea, align=uiconst.TOTOP, opacity=0.08) 53 self.bottomCont = Container(name='bottomCont', parent=self, align=uiconst.TOALL, clipChildren=True, padTop=16) 54 self.filterCont = ContainerAutoSize(name='filterCont', parent=self.bottomCont, align=uiconst.TOTOP) 55 self.ConstructFilterCont() 56 self.scrollCont = Container(name='scrollCont', parent=self.bottomCont, align=uiconst.TOALL) 57 EveLabelLarge(parent=self.scrollCont, text=GetByLabel('UI/Ledger/LogsHeader'), align=uiconst.TOTOP, padTop=8) 58 self.ChangeSignalConnection() 59 self.AddGraphs() 60 self.AddCopyBtns() 61 self.scroll = Scroll(parent=self.scrollCont, padding=(0, 4, 0, 0), id='ledgerPanelPersonal') 62 self.scroll.ApplyTabstopsToNode = self.ScrollApplyTabstopsToNode 63 uthread.new(self.LoadContent) 64 sm.RegisterNotify(self) 65 66 def OnTabSelect(self): 67 self.LoadContent() 68 69 def LoadContent(self): 70 self.LoadOreFilter() 71 self.ReloadContent() 72 73 def ScrollApplyTabstopsToNode(self, node, fromWhere = ''): 74 Scroll.ApplyTabstopsToNode(self.scroll, node, fromWhere) 75 if node.panel and node.tabs: 76 node.panel.UpdateLegendFillPos(node.tabs[0]) 77 78 def ConstructFilterCont(self): 79 EveLabelLarge(parent=self.filterCont, text=GetByLabel('UI/Ledger/FiltersHeader'), align=uiconst.TOTOP) 80 self.innerFilterCont = Container(name='innerFilterCont', parent=self.filterCont, align=uiconst.TOTOP, height=HEIGHT_NORMAL, padTop=4) 81 startTime, endTime = ledgerUtil.GetMiniMapRange() 82 self.sliderController = MiniMapSliderController(startTime, endTime) 83 fromTime, toTime = self.sliderController.GetVisibleRange() 84 labelPaths = ['UI/Ledger/DateRange', 'UI/Ledger/DateRangeTo'] 85 DateRangePicker(name='DateRangePicker', parent=self.innerFilterCont, pos=(150, 4, 50, 0), fromTime=fromTime, toTime=toTime, sliderController=self.sliderController, labelPaths=labelPaths) 86 self.filterController = FilterController('ledgerWnd_personal_oreType', [], doSortChildren=False) 87 Filter(name='oreFilter', parent=self.innerFilterCont, filterText=GetByLabel('UI/Ledger/OreTypeFilter'), filterController=self.filterController, columns=1, align=uiconst.CENTERLEFT, maxSize=300) 88 EveLabelMedium(parent=self.filterCont, align=uiconst.TOTOP, text=GetByLabel('UI/Ledger/DateSelectionText'), padTop=8) 89 minimapCont = Container(name='miniMapCont', parent=self.filterCont, align=uiconst.TOTOP, height=60, top=0, padTop=4) 90 minimapCont.isTabStop = True 91 ledgerMinimap = LedgerMinimap(parent=minimapCont, sliderController=self.sliderController) 92 ledgerMinimap.LoadMiniMap(ledgerUtil.GetDataForPersonaMinimap()) 93 Fill(bgParent=minimapCont, color=(1, 1, 1, 0.05)) 94 95 def LoadOreFilter(self): 96 data, self.currentPersonalDataVersion = GetLedgerService().GetPersonalData() 97 self._LoadOreFilterData(data) 98 99 def _LoadOreFilterData(self, data): 100 options = GetOreOptionsFromData(data) 101 self.filterController.SetOptions(options) 102 103 def LoadOreFilterIfNeeded(self, data): 104 normalOreGroupIDs, moonTypeIDsByGroupID = ledgerUtil.GetGroupingsForOreFromData(data) 105 if self.currentNormalOreGroupIDs == normalOreGroupIDs and self.currentMoonTypeIDsByGroupID == set(moonTypeIDsByGroupID.keys()): 106 return 107 self.currentNormalOreGroupIDs = normalOreGroupIDs 108 self.currentMoonTypeIDsByGroupID = set(moonTypeIDsByGroupID.keys()) 109 self._LoadOreFilterData(data) 110 111 def AddCopyBtns(self): 112 self.btnGroup = ButtonGroup(parent=self.scrollCont, idx=0) 113 text = GetByLabel('UI/Fitting/FittingWindow/FittingManagement/ExportToClipboard') 114 self.btnGroup.AddButton(text, self.CopyToClipboard, ()) 115 116 def OnFiltersChanged(self, controller): 117 self.ReloadContent() 118 119 def AddGraphs(self): 120 EveLabelLarge(parent=self.graphParent, text=GetByLabel('UI/Ledger/GraphsHeader'), align=uiconst.TOTOP) 121 self.graphCont = LedgerGraphCont(parent=self.graphParent, padBottom=8) 122 123 def OnMiningLedgerGraphSettingChanged(self): 124 self.ReloadContent() 125 126 @threadutils.throttled(0.5) 127 def ReloadContent(self): 128 visibleRange = self.sliderController.GetVisibleRange() 129 data, personalDataVersion = GetLedgerService().GetPersonalData() 130 if self.currentPersonalDataVersion != personalDataVersion: 131 self.LoadOreFilterIfNeeded(data) 132 self.currentPersonalDataVersion = personalDataVersion 133 data = self.FilterOutData(data) 134 self.LoadScroll(data, visibleRange) 135 self.graphCont.UpdateGraphFromRawData(data, *visibleRange) 136 137 def LoadScroll(self, data, visibleRange): 138 contentList = GetLedgerEntriesPersonal(data, *visibleRange) 139 self.scroll.Load(contentList=contentList, headers=ledgerUtil.SCROLL_HEADERS_PERSONAL, noContentHint=GetByLabel('UI/Ledger/NoLogsToShow')) 140 141 def OnOreFilterChanged(self, *args): 142 self.ReloadContent() 143 144 def FilterOutData(self, dataList): 145 newDataList = [] 146 for each in dataList: 147 if self.IsFilteredOut(each): 148 continue 149 newDataList.append(each) 150 151 return newDataList 152 153 def IsFilteredOut(self, info): 154 isFilterActive = self.filterController.IsActive() 155 if not isFilterActive: 156 return False 157 selectedOptions = self.filterController.GetSelectedOptions() 158 groupsSelected = {x[1] for x in selectedOptions if x[0] == OPTION_TYPE_GROUPINGID} 159 typesSelected = filter(None, {x[1] for x in selectedOptions if x[0] == OPTION_TYPEID}) 160 if evetypes.GetGroupID(info.typeID) in groupsSelected or ledgerUtil.GetBasicOreForTypeID(info.typeID) in typesSelected: 161 return False 162 return True 163 164 def ChangeSignalConnection(self, connect = True): 165 signalAndCallback = [(self.sliderController.on_change, self.ReloadContent), (self.filterController.on_filter_changed, self.OnFiltersChanged)] 166 ChangeSignalConnect(signalAndCallback, connect) 167 168 def CopyToClipboard(self, *args): 169 extraColumns = [GetByLabel('UI/Ledger/OreTypeID'), GetByLabel('UI/Ledger/SolarSystemID')] 170 allLines = ['\t'.join(ledgerUtil.SCROLL_HEADERS_PERSONAL + extraColumns)] 171 for eachData in self.scroll.GetNodes(): 172 text = '\t'.join([eachData.formattedText['date'], 173 eachData.formattedText['typeName'], 174 unicode(eachData.formattedText['qty']), 175 unicode(eachData.formattedText['qtyWasted']), 176 unicode(eachData.formattedText['volume']), 177 unicode(eachData.formattedText['volumeWasted']), 178 unicode(eachData.formattedText['estPrice']), 179 unicode(eachData.formattedText['estPriceWasted']), 180 unicode(eachData.formattedText['locationName']), 181 unicode(eachData.ledgerData.typeID), 182 unicode(eachData.ledgerData.solarsystemID)]) 183 allLines.append(text) 184 185 allText = '\r\n'.join(allLines) 186 if allText: 187 blue.pyos.SetClipboardData(allText) 188 189 def AddMoreInfoIcon(self): 190 moreinfoicon = MoreInfoIcon(left=2, top=-1, parent=self, idx=0, align=uiconst.TOPRIGHT) 191 moreinfoicon.hint = GetByLabel('UI/Ledger/PersonalLedgerCacheHint') 192 193 def Close(self): 194 with EatSignalChangingErrors(errorMsg='LedgerPanelPersonal'): 195 self.ChangeSignalConnection(connect=False) 196 Container.Close(self) 197 198 199 class LedgerPanelCorp(Container): 200 default_align = uiconst.TOALL 201 202 def ApplyAttributes(self, attributes): 203 Container.ApplyAttributes(self, attributes) 204 self.oreTypeFilterController = FilterController('ledgerWnd_corp_oreType', ()) 205 self.corpFilterController = FilterController('ledgerWnd_corp_corp', ()) 206 self.filterCont = Container(parent=self, align=uiconst.TOTOP, height=30, padLeft=4, padRight=4) 207 self.rangeCont = Container(parent=self, align=uiconst.TOTOP, height=70, padLeft=4, padRight=4) 208 self.AddComboFilters() 209 self.AddRangeSlider() 210 self.AddCopyBtns() 211 self.scroll = Scroll(parent=self, padding=4, id='ledgerPanelCorp') 212 self.scroll.LoadContent(noContentHint=GetByLabel('UI/ScienceAndIndustry/ScienceAndIndustryWindow/FetchingData')) 213 uthread.new(self.PopulateStructureCombo) 214 self.ChangeSignalConnection() 215 216 def AddComboFilters(self): 217 structureOptions = [] 218 self.structureCombo = Combo(parent=self.filterCont, name='ledger_corp_oreFilter', options=structureOptions, prefskey='ledger_corp_oreFilter', callback=self.OnStructureChanged, align=uiconst.CENTERLEFT, width=280) 219 left = self.structureCombo.left + self.structureCombo.width + 16 220 self.oreFilter = Filter(name='oreFilter', parent=self.filterCont, filterText=GetByLabel('UI/Ledger/OreTypeFilter'), filterController=self.oreTypeFilterController, columns=1, align=uiconst.CENTERLEFT, left=left, maxSize=300) 221 left = self.oreFilter.left + self.oreFilter.width + 16 222 self.corpFilter = Filter(name='corpFilter', parent=self.filterCont, filterText=GetByLabel('UI/Ledger/CorpFilter'), filterController=self.corpFilterController, columns=1, align=uiconst.CENTERLEFT, left=left, maxSize=300) 223 self.filterInput = QuickFilterEdit(name='filterInput', parent=self.filterCont, setvalue='', hintText=GetByLabel('UI/Inventory/Filter'), maxLength=64, pos=(6, 0, 100, 0), align=uiconst.CENTERRIGHT, OnClearFilter=self.LoadScroll, isCharacterField=True) 224 self.filterInput.OnReturn = self.LoadScroll 225 self.filterInput.ReloadFunction = self.LoadScroll 226 227 def AddRangeSlider(self): 228 startTime, endTime = ledgerUtil.GetMiniMapRange() 229 self.sliderController = MiniMapSliderController(startTime, endTime) 230 fromTime, toTime = self.sliderController.GetVisibleRange() 231 labelPaths = ['UI/Ledger/DateRange', 'UI/Ledger/DateRangeTo'] 232 DateRangePicker(name='DateRangePicker', parent=self.rangeCont, fromTime=fromTime, toTime=toTime, sliderController=self.sliderController, labelPaths=labelPaths) 233 minimapCont = Container(name='minimapCont', parent=self.rangeCont, align=uiconst.TOTOP, height=40, top=24) 234 minimapCont.isTabStop = True 235 self.ledgerMinimap = LedgerMinimap(parent=minimapCont, sliderController=self.sliderController) 236 Fill(bgParent=minimapCont, color=(1, 1, 1, 0.05)) 237 238 def PopulateStructureCombo(self): 239 select = settings.user.ui.Get(self.structureCombo.prefskey, None) 240 structureOptions = self.GetStructureComboOptions() 241 self.structureCombo.LoadOptions(structureOptions, select) 242 structureIdSelected = self.structureCombo.GetValue() 243 if structureIdSelected: 244 self.LoadFiltersAndScroll(structureIdSelected) 245 246 def LoadFiltersAndScroll(self, structureIdSelected): 247 data = self.GetCorpData(structureIdSelected) 248 self.LoadExtraFilters(data) 249 self.LoadScroll() 250 251 def AddCopyBtns(self): 252 self.btnGroup = ButtonGroup(parent=self, idx=0) 253 text = GetByLabel('UI/Fitting/FittingWindow/FittingManagement/ExportToClipboard') 254 self.btnGroup.AddButton(text, self.CopyToClipboard, ()) 255 256 def GetStructureComboOptions(self): 257 allStructuresByID, allSolarSystemIDs = GetLedgerService().GetStructuresForCorpData() 258 options = [] 259 for eachItemID, eachItemInfo in allStructuresByID.iteritems(): 260 name = eachItemInfo.itemName 261 options.append((name.lower(), (name, eachItemID))) 262 263 options = SortListOfTuples(options) 264 return options 265 266 def LoadExtraFilters(self, data): 267 allCorps, allCharIDs = ledgerUtil.GetGroupsForData(data) 268 toPrime = allCorps.union(allCharIDs.union(allCorps)) 269 cfg.eveowners.Prime(toPrime) 270 corpOptions = [ (cfg.eveowners.Get(x).name, x) for x in allCorps ] 271 corpOptions.sort(key=lambda x: x[0]) 272 oreFilterOptions = GetOreOptionsFromData(data) 273 self.oreTypeFilterController.SetOptions(oreFilterOptions) 274 corpFilterOptions = [ OptionObject(cfg.eveowners.Get(corpID).name, corpID, OPTION_ITEMID) for corpID in allCorps ] 275 self.corpFilterController.SetOptions(corpFilterOptions) 276 277 def GetCorpData(self, structureID): 278 data = GetLedgerService().GetCorpData(structureID) 279 return data 280 281 def LoadScroll(self): 282 structureID = self.structureCombo.GetValue() 283 self.ledgerMinimap.LoadMiniMap(ledgerUtil.GetDataForCorpMinimap(structureID)) 284 data = self.GetCorpData(structureID) 285 data = self.FilterOutData(data) 286 filterText = self.filterInput.GetValue() 287 contentList = GetLedgerEntriesCorp(data, filterText.lower()) 288 self.scroll.Load(contentList=contentList, headers=ledgerUtil.SCROLL_HEADERS_CORP, noContentHint=GetByLabel('UI/ScienceAndIndustry/ScienceAndIndustryWindow/FiltersReturnedNoResults')) 289 self.scroll.HideLoading() 290 291 def OnSliderChanged(self): 292 self.scroll.ShowLoading() 293 uthread.new(self.ReloadContent) 294 295 @threadutils.throttled(1.0) 296 def ReloadContent(self): 297 self.LoadScroll() 298 299 def OnStructureChanged(self, combo, key, structureID): 300 settings.user.ui.Set(combo.prefskey, structureID) 301 self.LoadFiltersAndScroll(structureID) 302 303 def OnMainFilterChanged(self, *args): 304 self.LoadScroll() 305 306 @telemetry.ZONE_METHOD 307 def FilterOutData(self, dataList): 308 fromTime, toTime = self.sliderController.GetVisibleRange() 309 selectedOreOptions = self.oreTypeFilterController.GetSelectedOptions() 310 oreTypeIDs = [ x[1] for x in selectedOreOptions if x[0] == OPTION_TYPEID ] 311 oreGroupIDs = {x[1] for x in selectedOreOptions if x[0] == OPTION_TYPE_GROUPINGID} 312 filterInfo = KeyVal(oreTypeIDs=oreTypeIDs, oreGroupIDs=oreGroupIDs, corpIDs=[ x[1] for x in self.corpFilterController.GetSelectedOptions() ], oreActive=self.oreTypeFilterController.IsActive(), corpActive=self.corpFilterController.IsActive(), fromTime=fromTime, toTime=toTime) 313 newDataList = [] 314 for each in dataList: 315 if self.IsFilteredOut(each, filterInfo): 316 continue 317 newDataList.append(each) 318 319 return newDataList 320 321 @telemetry.ZONE_METHOD 322 def IsFilteredOut(self, info, filterInfo): 323 if not ledgerUtil.IsInRange(info.eventDate, filterInfo.fromTime, filterInfo.toTime): 324 return True 325 if not filterInfo.oreActive and not filterInfo.corpActive: 326 return False 327 if filterInfo.corpActive and info.corporationID not in filterInfo.corpIDs: 328 return True 329 if filterInfo.oreActive and self._IsOreTypeFilteredOut(info.typeID, filterInfo): 330 return True 331 return False 332 333 def _IsOreTypeFilteredOut(self, typeID, filterInfo): 334 if typeID in filterInfo.oreTypeIDs: 335 return False 336 if _GetGroupID(typeID) in filterInfo.oreGroupIDs: 337 return False 338 if _GetBasicOreForTypeID(typeID) in filterInfo.oreTypeIDs: 339 return False 340 return True 341 342 def OnTabSelect(self): 343 self.PopulateStructureCombo() 344 self.LoadScroll() 345 346 def ChangeSignalConnection(self, connect = True): 347 signalAndCallback = [(self.oreTypeFilterController.on_filter_changed, self.OnMainFilterChanged), (self.corpFilterController.on_filter_changed, self.OnMainFilterChanged), (self.sliderController.on_change, self.OnSliderChanged)] 348 ChangeSignalConnect(signalAndCallback, connect) 349 350 def CopyToClipboard(self, *args): 351 extraColumns = [GetByLabel('UI/Ledger/OreTypeID'), GetByLabel('UI/Ledger/SolarSystemID')] 352 allLines = ['\t'.join(ledgerUtil.SCROLL_HEADERS_CORP + extraColumns)] 353 for eachData in self.scroll.GetNodes(): 354 text = '\t'.join([eachData.formattedText['date'], 355 eachData.formattedText['corpName'], 356 eachData.formattedText['charName'], 357 eachData.formattedText['typeName'], 358 unicode(eachData.formattedText['qty']), 359 unicode(eachData.formattedText['volume']), 360 unicode(eachData.formattedText['estPrice']), 361 unicode(eachData.ledgerData.typeID), 362 unicode(eachData.ledgerData.solarsystemID)]) 363 allLines.append(text) 364 365 allText = '\r\n'.join(allLines) 366 if allText: 367 blue.pyos.SetClipboardData(allText) 368 369 def Close(self): 370 with EatSignalChangingErrors(errorMsg='LedgerPanelCorp'): 371 self.ChangeSignalConnection(connect=False) 372 Container.Close(self) 373 374 375 def GetOreOptionsFromData(data): 376 normalOreGroupIDs, moonTypeIDsByGroupID = ledgerUtil.GetGroupingsForOreFromData(data) 377 moonOptions = [] 378 for moonGroupID, typeIDs in moonTypeIDsByGroupID.iteritems(): 379 basicTypeIDs = {ledgerUtil.GetBasicOreForTypeID(x) for x in typeIDs} 380 subOptions = [ OptionObject(evetypes.GetName(x), x, OPTION_TYPEID) for x in basicTypeIDs ] 381 moonOptions.append(OptionObject(evetypes.GetGroupNameByGroup(moonGroupID), moonGroupID, OPTION_TYPE_GROUPINGID, subOptions)) 382 383 moonOptions.sort(key=lambda x: x.value) 384 normalOptions = [ OptionObject(evetypes.GetGroupNameByGroup(x), x, OPTION_TYPE_GROUPINGID) for x in normalOreGroupIDs ] 385 normalOptions.sort(key=lambda x: x.name) 386 options = moonOptions + normalOptions 387 return options 388 389 390 @Memoize(1) 391 def _GetGroupID(typeID): 392 return evetypes.GetGroupID(typeID) 393 394 395 @Memoize(1) 396 def _GetBasicOreForTypeID(typeID): 397 return ledgerUtil.GetBasicOreForTypeID(typeID)