PyXR

c:\projects\bitpim\src \ call_history.py



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