0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2005 Joe Pham <djpham@bitpim.org> 0004 ### 0005 ### This program is free software; you can redistribute it and/or modify 0006 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0007 ### 0008 ### $Id: call_history.py 4601 2008-03-04 22:37:48Z djpham $ 0009 0010 """ 0011 Code to handle Call History data storage and display. 0012 0013 The format of the Call History is standardized. It is an object with the 0014 following attributes: 0015 0016 folder: string (where this item belongs) 0017 datetime: string 'YYYYMMDDThhmmss' or (y,m,d,h,m,s) 0018 number: string (the phone number of this call) 0019 name: string (optional name associated with this number) 0020 duration: int (optional duration of the call in minutes) 0021 0022 To implement Call History feature for a phone module: 0023 0024 Add an entry into Profile._supportedsyncs: 0025 ('call_history', 'read', None), 0026 0027 Implement the following method in your Phone class: 0028 def getcallhistory(self, result, merge): 0029 ... 0030 return result 0031 0032 The result dict key is 'call_history'. 0033 0034 """ 0035 0036 # standard modules 0037 from __future__ import with_statement 0038 import copy 0039 import sha 0040 import time 0041 0042 # wx modules 0043 import wx 0044 import wx.lib.scrolledpanel as scrolled 0045 0046 # BitPim modules 0047 import database 0048 import guiwidgets 0049 import guihelper 0050 import helpids 0051 import phonenumber 0052 import pubsub 0053 import today 0054 import widgets 0055 0056 #------------------------------------------------------------------------------- 0057 class CallHistoryDataobject(database.basedataobject): 0058 _knownproperties=['folder', 'datetime', 'number', 'name', 'duration' ] 0059 _knownlistproperties=database.basedataobject._knownlistproperties.copy() 0060 def __init__(self, data=None): 0061 if data is None or not isinstance(data, CallHistoryEntry): 0062 return; 0063 self.update(data.get_db_dict()) 0064 callhistoryobjectfactory=database.dataobjectfactory(CallHistoryDataobject) 0065 0066 #------------------------------------------------------------------------------- 0067 def GetDurationStr(duration): 0068 """convert duration int into an h:mm:ss formatted string""" 0069 if duration is None: 0070 return '' 0071 else: 0072 sec=duration%60 0073 min=duration/60 0074 hr=min/60 0075 min=min%60 0076 return "%d:%02d:%02d" % (hr, min, sec) 0077 0078 #------------------------------------------------------------------------------- 0079 class CallHistoryEntry(object): 0080 Folder_Incoming='Incoming' 0081 Folder_Outgoing='Outgoing' 0082 Folder_Missed='Missed' 0083 Folder_Data='Data' 0084 Valid_Folders=(Folder_Incoming, Folder_Outgoing, Folder_Missed, Folder_Data) 0085 _folder_key='folder' 0086 _datetime_key='datetime' 0087 _number_key='number' 0088 _name_key='name' 0089 _duration_key='duration' 0090 _unknown_datetime='YYYY-MM-DD hh:mm:ss' 0091 _id_index=0 0092 _max_id_index=999 0093 def __init__(self): 0094 self._data={ 'serials': [] } 0095 self._create_id() 0096 0097 def __eq__(self, rhs): 0098 return self.folder==rhs.folder and self.datetime==rhs.datetime and\ 0099 self.number==rhs.number 0100 def __ne__(self, rhs): 0101 return self.folder!=rhs.folder or self.datetime!=rhs.datetime or\ 0102 self.number!=rhs.number 0103 def get(self): 0104 return copy.deepcopy(self._data, {}) 0105 def set(self, d): 0106 self._data={} 0107 self._data.update(d) 0108 0109 def get_db_dict(self): 0110 return self.get() 0111 def set_db_dict(self, d): 0112 self.set(d) 0113 0114 def _create_id(self): 0115 "Create a BitPim serial for this entry" 0116 self._data.setdefault("serials", []).append(\ 0117 {"sourcetype": "bitpim", 0118 "id": '%.3f%03d'%(time.time(), CallHistoryEntry._id_index) }) 0119 if CallHistoryEntry._id_index<CallHistoryEntry._max_id_index: 0120 CallHistoryEntry._id_index+=1 0121 else: 0122 CallHistoryEntry._id_index=0 0123 def _get_id(self): 0124 s=self._data.get('serials', []) 0125 for n in s: 0126 if n.get('sourcetype', None)=='bitpim': 0127 return n.get('id', None) 0128 return None 0129 def _set_id(self, id): 0130 s=self._data.get('serials', []) 0131 for n in s: 0132 if n.get('sourcetype', None)=='bitpim': 0133 n['id']=id 0134 return 0135 self._data['serials'].append({'sourcetype': 'bitpim', 'id': id } ) 0136 id=property(fget=_get_id, fset=_set_id) 0137 0138 def _set_or_del(self, key, v, v_list=[]): 0139 if v is None or v in v_list: 0140 if self._data.has_key(key): 0141 del self._data[key] 0142 else: 0143 self._data[key]=v 0144 0145 def _get_folder(self): 0146 return self._data.get(self._folder_key, '') 0147 def _set_folder(self, v): 0148 if v is None: 0149 if self._data.has_key(self._folder_key): 0150 del self._data[self._folder_key] 0151 return 0152 if not isinstance(v, (str, unicode)): 0153 raise TypeError,'not a string or unicode type' 0154 if v not in self.Valid_Folders: 0155 raise ValueError,'not a valid folder' 0156 self._data[self._folder_key]=v 0157 folder=property(fget=_get_folder, fset=_set_folder) 0158 0159 def _get_number(self): 0160 return self._data.get(self._number_key, '') 0161 def _set_number(self, v): 0162 self._set_or_del(self._number_key, v, ['']) 0163 number=property(fget=_get_number, fset=_set_number) 0164 0165 def _get_name(self): 0166 return self._data.get(self._name_key, '') 0167 def _set_name(self, v): 0168 self._set_or_del(self._name_key, v, ('',)) 0169 name=property(fget=_get_name, fset=_set_name) 0170 0171 def _get_duration(self): 0172 return self._data.get(self._duration_key, None) 0173 def _set_duration(self, v): 0174 if v is not None and not isinstance(v, int): 0175 raise TypeError('duration property is an int arg') 0176 self._set_or_del(self._duration_key, v) 0177 def _get_durationstr(self): 0178 return GetDurationStr(self.duration) 0179 duration=property(fget=_get_duration, fset=_set_duration) 0180 durationstr=property(fget=_get_durationstr) 0181 0182 0183 def _get_datetime(self): 0184 return self._data.get(self._datetime_key, '') 0185 def _set_datetime(self, v): 0186 # this routine supports 2 formats: 0187 # (y,m,d,h,m,s) and 'YYYYMMDDThhmmss' 0188 # check for None and delete manually 0189 if v is None: 0190 if self._data.has_key(self._datetime_key): 0191 del self._data[self._datetime_key] 0192 return 0193 if isinstance(v, (tuple, list)): 0194 if len(v)!=6: 0195 raise ValueError,'(y, m, d, h, m, s)' 0196 s='%04d%02d%02dT%02d%02d%02d'%tuple(v) 0197 elif isinstance(v, (str, unicode)): 0198 # some primitive validation 0199 if len(v)!=15 or v[8]!='T': 0200 raise ValueError,'value must be in format YYYYMMDDThhmmss' 0201 s=v 0202 else: 0203 raise TypeError 0204 self._data[self._datetime_key]=s 0205 datetime=property(fget=_get_datetime, fset=_set_datetime) 0206 def get_date_time_str(self): 0207 # return a string representing this date/time in the format of 0208 # YYYY-MM-DD hh:mm:ss 0209 s=self.datetime 0210 if not len(s): 0211 s=self._unknown_datetime 0212 else: 0213 s=s[:4]+'-'+s[4:6]+'-'+s[6:8]+' '+s[9:11]+':'+s[11:13]+':'+s[13:] 0214 return s 0215 def summary(self, name=None): 0216 # return a short summary for this entry in the format of 0217 # MM/DD hh:mm <Number/Name> 0218 s=self.datetime 0219 if s: 0220 s=s[4:6]+'/'+s[6:8]+' '+s[9:11]+':'+s[11:13]+' ' 0221 else: 0222 s='**/** **:** ' 0223 if name: 0224 s+=name 0225 elif self.name: 0226 s+=self.name 0227 else: 0228 s+=phonenumber.format(self.number) 0229 return s 0230 0231 #------------------------------------------------------------------------------- 0232 class CallHistoryWidget(scrolled.ScrolledPanel, widgets.BitPimWidget): 0233 _data_key='call_history' 0234 stat_list=("Data", "Missed", "Incoming", "Outgoing", "All") 0235 def __init__(self, mainwindow, parent): 0236 super(CallHistoryWidget, self).__init__(parent, -1) 0237 self._main_window=mainwindow 0238 self.call_history_tree_nodes={} 0239 self._parent=parent 0240 self.read_only=False 0241 self.historical_date=None 0242 self._data={} 0243 self._name_map={} 0244 pubsub.subscribe(self._OnPBLookup, pubsub.RESPONSE_PB_LOOKUP) 0245 self.list_widget=CallHistoryList(self._main_window, self._parent, self) 0246 # main box sizer 0247 vbs=wx.BoxSizer(wx.VERTICAL) 0248 # data date adjuster 0249 hbs=wx.BoxSizer(wx.HORIZONTAL) 0250 static_bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, 0251 'Historical Data Status:'), 0252 wx.VERTICAL) 0253 self.historical_data_label=wx.StaticText(self, -1, 'Current Data') 0254 static_bs.Add(self.historical_data_label, 1, wx.EXPAND|wx.ALL, 5) 0255 hbs.Add(static_bs, 1, wx.EXPAND|wx.ALL, 5) 0256 vbs.Add(hbs, 0, wx.EXPAND|wx.ALL, 5) 0257 # main stats display 0258 self.total_calls=today.HyperLinkCtrl(self, -1, ' Total Calls: 0') 0259 today.EVT_HYPERLINK_LEFT(self, self.total_calls.GetId(), 0260 self.OnNodeSelection) 0261 self.total_in=today.HyperLinkCtrl(self, -1, ' Incoming Calls: 0') 0262 today.EVT_HYPERLINK_LEFT(self, self.total_in.GetId(), 0263 self.OnNodeSelection) 0264 self.total_out=today.HyperLinkCtrl(self, -1, ' Outgoing Calls: 0') 0265 today.EVT_HYPERLINK_LEFT(self, self.total_out.GetId(), 0266 self.OnNodeSelection) 0267 self.total_missed=today.HyperLinkCtrl(self, -1, ' Missed Calls: 0') 0268 today.EVT_HYPERLINK_LEFT(self, self.total_missed.GetId(), 0269 self.OnNodeSelection) 0270 self.total_data=today.HyperLinkCtrl(self, -1, ' Data Calls: 0') 0271 today.EVT_HYPERLINK_LEFT(self, self.total_data.GetId(), 0272 self.OnNodeSelection) 0273 self.duration_all=wx.StaticText(self, -1, ' Total Duration(h:m:s): 0') 0274 self.duration_in=wx.StaticText(self, -1, ' Incoming Duration(h:m:s): 0') 0275 self.duration_out=wx.StaticText(self, -1, ' Outgoing Duration(h:m:s): 0') 0276 self.duration_data=wx.StaticText(self, -1, ' Data Duration(h:m:s): 0') 0277 self._id_dict={ 0278 self.total_calls.GetId(): self.stat_list[4], 0279 self.total_in.GetId(): self.stat_list[2], 0280 self.total_out.GetId(): self.stat_list[3], 0281 self.total_missed.GetId(): self.stat_list[1], 0282 self.total_data.GetId(): self.stat_list[0], 0283 } 0284 vbs.Add(wx.StaticText(self, -1, ''), 0, wx.ALIGN_LEFT|wx.ALL, 2) 0285 vbs.Add(self.total_calls, 0, wx.ALIGN_LEFT|wx.ALL, 2) 0286 vbs.Add(self.total_in, 0, wx.ALIGN_LEFT|wx.ALL, 2) 0287 vbs.Add(self.total_out, 0, wx.ALIGN_LEFT|wx.ALL, 2) 0288 vbs.Add(self.total_missed, 0, wx.ALIGN_LEFT|wx.ALL, 2) 0289 vbs.Add(self.total_data, 0, wx.ALIGN_LEFT|wx.ALL, 2) 0290 vbs.Add(wx.StaticText(self, -1, ''), 0, wx.ALIGN_LEFT|wx.ALL, 2) 0291 vbs.Add(self.duration_all, 0, wx.ALIGN_LEFT|wx.ALL, 2) 0292 vbs.Add(self.duration_in, 0, wx.ALIGN_LEFT|wx.ALL, 2) 0293 vbs.Add(self.duration_out, 0, wx.ALIGN_LEFT|wx.ALL, 2) 0294 vbs.Add(self.duration_data, 0, wx.ALIGN_LEFT|wx.ALL, 2) 0295 # all done 0296 self.SetSizer(vbs) 0297 self.SetAutoLayout(True) 0298 vbs.Fit(self) 0299 self.SetupScrolling() 0300 self.SetBackgroundColour(wx.WHITE) 0301 # populate data 0302 self._populate() 0303 0304 def OnNodeSelection(self, evt): 0305 # Request to select a subnode 0306 _node=self._id_dict.get(evt.GetId(), None) 0307 if _node and self.call_history_tree_nodes.get(_node, None): 0308 self.ActivateSelf(self.call_history_tree_nodes[_node]) 0309 0310 def populate(self, dict, force=False): 0311 if self.read_only and not force: 0312 # historical data, bail 0313 return 0314 self._data=dict.get(self._data_key, {}) 0315 self._populate() 0316 0317 def OnInit(self): 0318 for stat in self.stat_list: 0319 self.call_history_tree_nodes[stat]=self.AddSubPage(self.list_widget, stat, self._tree.calls) 0320 0321 def GetRightClickMenuItems(self, node): 0322 result=[] 0323 result.append((widgets.BitPimWidget.MENU_NORMAL, guihelper.ID_EXPORT_CSV_CALL_HISTORY, "Export to CSV ...", "Export the call history to a csv file")) 0324 result.append((widgets.BitPimWidget.MENU_NORMAL, guihelper.ID_DATAHISTORICAL, "Historical Data ...", "Display Historical Data")) 0325 return result 0326 0327 def HasHistoricalData(self): 0328 return True 0329 0330 def OnHistoricalData(self): 0331 """Display current or historical data""" 0332 if self.read_only: 0333 current_choice=guiwidgets.HistoricalDataDialog.Historical_Data 0334 else: 0335 current_choice=guiwidgets.HistoricalDataDialog.Current_Data 0336 with guihelper.WXDialogWrapper(guiwidgets.HistoricalDataDialog(self, 0337 current_choice=current_choice, 0338 historical_date=self.historical_date, 0339 historical_events=\ 0340 self._main_window.database.getchangescount(self._data_key)), 0341 True) as (dlg, retcode): 0342 if retcode==wx.ID_OK: 0343 with guihelper.MWBusyWrapper(self._main_window): 0344 current_choice, self.historical_date=dlg.GetValue() 0345 r={} 0346 if current_choice==guiwidgets.HistoricalDataDialog.Current_Data: 0347 self.read_only=False 0348 msg_str='Current Data' 0349 self.getfromfs(r) 0350 else: 0351 self.read_only=True 0352 msg_str='Historical Data as of %s'%\ 0353 str(wx.DateTimeFromTimeT(self.historical_date)) 0354 self.getfromfs(r, self.historical_date) 0355 self.populate(r, True) 0356 self.historical_data_label.SetLabel(msg_str) 0357 self.list_widget.historical_data_label.SetLabel(msg_str) 0358 0359 def _publish_today_data(self): 0360 keys=[(x.datetime, k) for k,x in self._data.items()] 0361 keys.sort() 0362 keys.reverse() 0363 today_event_in=today.TodayIncomingCallsEvent() 0364 today_event_miss=today.TodayMissedCallsEvent() 0365 for _,k in keys: 0366 if self._data[k].folder==CallHistoryEntry.Folder_Incoming: 0367 today_event_in.append(self._data[k].summary(self._name_map.get(self._data[k].number, None)), {'id':k}) 0368 if self._data[k].folder==CallHistoryEntry.Folder_Missed: 0369 today_event_miss.append(self._data[k].summary(self._name_map.get(self._data[k].number, None)), {'id':k}) 0370 today_event_in.broadcast() 0371 today_event_miss.broadcast() 0372 0373 def _populate(self): 0374 # lookup phone book for names 0375 for k,e in self._data.items(): 0376 if e.name: 0377 if not self._name_map.has_key(e.number): 0378 self._name_map[e.number]=e.name 0379 else: 0380 if not self._name_map.has_key(e.number): 0381 pubsub.publish(pubsub.REQUEST_PB_LOOKUP, 0382 { 'item': e.number } ) 0383 self.list_widget.populate() 0384 #update stats 0385 self.CalculateStats() 0386 # tell today 'bout it 0387 self._publish_today_data() 0388 0389 def CalculateStats(self): 0390 total=inc=out=miss=data=0 0391 total_d=in_d=out_d=data_d=0 0392 for k, e in self._data.items(): 0393 total+=1 0394 if e.duration==None: 0395 dur=0 0396 else: 0397 dur=e.duration 0398 total_d+=dur 0399 if e.folder==CallHistoryEntry.Folder_Incoming: 0400 inc+=1 0401 in_d+=dur 0402 elif e.folder==CallHistoryEntry.Folder_Outgoing: 0403 out+=1 0404 out_d+=dur 0405 elif e.folder==CallHistoryEntry.Folder_Missed: 0406 miss+=1 0407 elif e.folder==CallHistoryEntry.Folder_Data: 0408 data+=1 0409 data_d+=dur 0410 self.total_calls.SetLabel(' Total Calls: '+`total`) 0411 self.total_in.SetLabel(' Incoming Calls: '+`inc`) 0412 self.total_out.SetLabel(' Outgoing Calls: '+`out`) 0413 self.total_missed.SetLabel(' Missed Calls: '+`miss`) 0414 self.total_data.SetLabel(' Data Calls: '+`data`) 0415 self.duration_all.SetLabel(' Total Duration(h:m:s): '+GetDurationStr(total_d)) 0416 self.duration_in.SetLabel(' Incoming Duration(h:m:s): '+GetDurationStr(in_d)) 0417 self.duration_out.SetLabel(' Outgoing Duration(h:m:s): '+GetDurationStr(out_d)) 0418 self.duration_data.SetLabel(' Data Duration(h:m:s): '+GetDurationStr(data_d)) 0419 0420 def _OnPBLookup(self, msg): 0421 d=msg.data 0422 k=d.get('item', None) 0423 name=d.get('name', None) 0424 if k is None: 0425 return 0426 self._name_map[k]=name 0427 0428 def getdata(self, dict, want=None): 0429 dict[self._data_key]=copy.deepcopy(self._data) 0430 0431 def _save_to_db(self, dict): 0432 if self.read_only: 0433 return 0434 db_rr={} 0435 for k,e in dict.items(): 0436 db_rr[k]=CallHistoryDataobject(e) 0437 database.ensurerecordtype(db_rr, callhistoryobjectfactory) 0438 self._main_window.database.savemajordict(self._data_key, db_rr) 0439 0440 def populatefs(self, dict): 0441 if self.read_only: 0442 wx.MessageBox('You are viewing historical data which cannot be changed or saved', 0443 'Cannot Save Call History Data', 0444 style=wx.OK|wx.ICON_ERROR) 0445 else: 0446 self._save_to_db(dict.get(self._data_key, {})) 0447 return dict 0448 0449 def getfromfs(self, result, timestamp=None): 0450 dict=self._main_window.database.\ 0451 getmajordictvalues(self._data_key, 0452 callhistoryobjectfactory, 0453 at_time=timestamp) 0454 r={} 0455 for k,e in dict.items(): 0456 ce=CallHistoryEntry() 0457 ce.set_db_dict(e) 0458 r[ce.id]=ce 0459 result.update({ self._data_key: r}) 0460 return result 0461 0462 def merge(self, dict): 0463 if self.read_only: 0464 wx.MessageBox('You are viewing historical data which cannot be changed or saved', 0465 'Cannot Save Call History Data', 0466 style=wx.OK|wx.ICON_ERROR) 0467 return 0468 d=dict.get(self._data_key, {}) 0469 l=[e for k,e in self._data.items()] 0470 for k,e in d.items(): 0471 if e not in l: 0472 self._data[e.id]=e 0473 self._save_to_db(self._data) 0474 self._populate() 0475 0476 def get_selected_data(self): 0477 # return a dict of selected items 0478 res={} 0479 for sel_idx in self.list_widget._item_list.GetSelections(): 0480 k=self.list_widget._item_list.GetItemData(sel_idx) 0481 if k: 0482 res[k]=self._data[k] 0483 return res 0484 0485 def get_data(self): 0486 return self._data 0487 0488 #------------------------------------------------------------------------------- 0489 class CallHistoryList(wx.Panel, widgets.BitPimWidget): 0490 _by_type=0 0491 _by_date=1 0492 _by_number=2 0493 def __init__(self, mainwindow, parent, stats): 0494 super(CallHistoryList, self).__init__(parent, -1) 0495 self._main_window=mainwindow 0496 self._stats=stats 0497 self.nodes={} 0498 self.nodes_keys={} 0499 self._display_filter="All" 0500 # main box sizer 0501 vbs=wx.BoxSizer(wx.VERTICAL) 0502 # data date adjuster 0503 hbs=wx.BoxSizer(wx.HORIZONTAL) 0504 static_bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, 0505 'Historical Data Status:'), 0506 wx.VERTICAL) 0507 self.historical_data_label=wx.StaticText(self, -1, 'Current Data') 0508 static_bs.Add(self.historical_data_label, 1, wx.EXPAND|wx.ALL, 5) 0509 hbs.Add(static_bs, 1, wx.EXPAND|wx.ALL, 5) 0510 vbs.Add(hbs, 0, wx.EXPAND|wx.ALL, 5) 0511 # main list 0512 column_info=[] 0513 column_info.append(("Call Type", 80, False)) 0514 column_info.append(("Date", 120, False)) 0515 column_info.append(("Number", 100, False)) 0516 column_info.append(("Duration", 80, False)) 0517 column_info.append(("Name", 130, False)) 0518 self._item_list=guiwidgets.BitPimListCtrl(self, column_info) 0519 self._item_list.ResetView(self.nodes, self.nodes_keys) 0520 vbs.Add(self._item_list, 1, wx.EXPAND|wx.ALL, 5) 0521 vbs.Add(wx.StaticText(self, -1, ' Note: Click column headings to sort data'), 0, wx.ALIGN_CENTRE|wx.BOTTOM, 10) 0522 # all done 0523 today.bind_notification_event(self.OnTodaySelectionIncoming, 0524 today.Today_Group_IncomingCalls) 0525 today.bind_notification_event(self.OnTodaySelectionMissed, 0526 today.Today_Group_MissedCalls) 0527 self.today_data=None 0528 self.SetSizer(vbs) 0529 self.SetAutoLayout(True) 0530 vbs.Fit(self) 0531 0532 def OnSelected(self, node): 0533 for stat in self._stats.stat_list: 0534 if self._stats.call_history_tree_nodes[stat]==node: 0535 if self._display_filter!=stat: 0536 self._display_filter=stat 0537 # for some reason GetTopItem return 0 (instead of -1) 0538 # when the list is empty 0539 if self._item_list.GetItemCount(): 0540 item=self._item_list.GetTopItem() 0541 # deselect all the items when changing view 0542 while item!=-1: 0543 self._item_list.Select(item, 0) 0544 item=self._item_list.GetNextItem(item) 0545 self.populate() 0546 self._on_today_selection() 0547 return 0548 0549 def OnTodaySelectionIncoming(self, evt): 0550 node=self._stats.call_history_tree_nodes["Incoming"] 0551 self.today_data=evt.data 0552 self.ActivateSelf(node) 0553 0554 def OnTodaySelectionMissed(self, evt): 0555 node=self._stats.call_history_tree_nodes["Missed"] 0556 self.today_data=evt.data 0557 self.ActivateSelf(node) 0558 0559 def _on_today_selection(self): 0560 if self.today_data and self._item_list.GetItemCount(): 0561 item=self._item_list.GetTopItem() 0562 while item!=-1: 0563 if self.today_data['id']==self._item_list.GetItemData(item): 0564 self._item_list.Select(item, 1) 0565 self._item_list.EnsureVisible(item) 0566 else: 0567 self._item_list.Select(item, 0) 0568 item=self._item_list.GetNextItem(item) 0569 self.today_data=None 0570 0571 def GetRightClickMenuItems(self, node): 0572 result=[] 0573 result.append((widgets.BitPimWidget.MENU_NORMAL, guihelper.ID_EDITSELECTALL, "Select All", "Select All Items")) 0574 result.append((widgets.BitPimWidget.MENU_NORMAL, guihelper.ID_EDITDELETEENTRY, "Delete Selected", "Delete Selected Items")) 0575 result.append((widgets.BitPimWidget.MENU_SPACER, 0, "", "")) 0576 result.append((widgets.BitPimWidget.MENU_NORMAL, guihelper.ID_EXPORT_CSV_CALL_HISTORY, "Export to CSV ...", "Export the call history to a csv file")) 0577 result.append((widgets.BitPimWidget.MENU_NORMAL, guihelper.ID_DATAHISTORICAL, "Historical Data ...", "Display Historical Data")) 0578 return result 0579 0580 def CanSelectAll(self): 0581 if self._item_list.GetItemCount(): 0582 return True 0583 return False 0584 0585 def OnSelectAll(self, _): 0586 item=self._item_list.GetTopItem() 0587 while item!=-1: 0588 self._item_list.Select(item) 0589 item=self._item_list.GetNextItem(item) 0590 0591 def HasHistoricalData(self): 0592 return self._stats.HasHistoricalData() 0593 0594 def OnHistoricalData(self): 0595 return self._stats.OnHistoricalData() 0596 0597 def populate(self): 0598 self.nodes={} 0599 self.nodes_keys={} 0600 index=0 0601 for k,e in self._stats._data.items(): 0602 if self._display_filter=="All" or e.folder==self._display_filter: 0603 name=e.name 0604 if name==None or name=="": 0605 temp=self._stats._name_map.get(e.number, None) 0606 if temp !=None: 0607 name=temp 0608 else: 0609 name="" 0610 self.nodes[index]=(e.folder, e.get_date_time_str(), 0611 phonenumber.format(e.number), 0612 e.durationstr, name) 0613 self.nodes_keys[index]=k 0614 index+=1 0615 self._item_list.ResetView(self.nodes, self.nodes_keys) 0616 0617 def CanDelete(self): 0618 if self._stats.read_only: 0619 return False 0620 sels_idx=self._item_list.GetFirstSelected() 0621 if sels_idx==-1: 0622 return False 0623 return True 0624 0625 def GetDeleteInfo(self): 0626 return wx.ART_DEL_BOOKMARK, "Delete Call Record" 0627 0628 def OnDelete(self, _): 0629 if self._stats.read_only: 0630 return 0631 sels_idx=self._item_list.GetSelections() 0632 if len(sels_idx): 0633 # delete them from the data list 0634 for i,item in sels_idx.items(): 0635 del self._stats._data[self._item_list.GetItemData(item)] 0636 self._item_list.Select(item, 0) 0637 self.populate() 0638 self._stats._save_to_db(self._stats._data) 0639 self._stats.CalculateStats() 0640 def GetHelpID(self): 0641 return helpids.ID_TAB_CALLHISTORY 0642
Generated by PyXR 0.9.4