ui.py

changeset 45
aa2a95dc6093
parent 42
00accd11978b
child 46
ecb41072a1b7
equal deleted inserted replaced
44:ac449729a896 45:aa2a95dc6093
6 import time 6 import time
7 import datetime 7 import datetime
8 import logging 8 import logging
9 import borgend 9 import borgend
10 import utils 10 import utils
11 import backup
11 from threading import Lock, Timer 12 from threading import Lock, Timer
12 from config import settings 13 from config import settings
13 import objc 14 import objc
14 15
15 logger=borgend.logger.getChild(__name__) 16 logger=borgend.logger.getChild(__name__)
16 17
17 INACTIVE=0
18 SCHEDULED_OK=1
19 ACTIVE=2
20 OFFLINE=3
21 ERRORS=4
22
23 traynames={ 18 traynames={
24 INACTIVE: 'B.', 19 backup.INACTIVE: 'B.',
25 SCHEDULED_OK: 'B.', 20 backup.SCHEDULED: 'B.',
26 ACTIVE: 'B!', 21 backup.ACTIVE: 'B!',
27 OFFLINE: 'B⦙', 22 backup.BUSY: 'B⦙',
28 ERRORS: 'B?' 23 backup.OFFLINE: 'B⦙',
24 backup.ERRORS: 'B?'
25 }
26
27 statestring={
28 backup.INACTIVE: 'inactive',
29 backup.SCHEDULED: 'scheduled',
30 backup.ACTIVE: 'active',
31 backup.BUSY: 'busy',
32 backup.OFFLINE: 'offline',
33 backup.ERRORS: 'errors'
29 } 34 }
30 35
31 # Refresh the menu at most once a second to reduce flicker 36 # Refresh the menu at most once a second to reduce flicker
32 refresh_interval=1.0 37 refresh_interval=1.0
33
34 def combine_state(state1, state2):
35 return max(state1, state2)
36 38
37 # Workaround to rumps brokenness; 39 # Workaround to rumps brokenness;
38 # see https://github.com/jaredks/rumps/issues/59 40 # see https://github.com/jaredks/rumps/issues/59
39 def notification_workaround(title, subtitle, message): 41 def notification_workaround(title, subtitle, message):
40 try: 42 try:
66 return '{0:.2f}GB'.format(B/GB) 68 return '{0:.2f}GB'.format(B/GB)
67 elif TB <= B: 69 elif TB <= B:
68 return '{0:.2f}TB'.format(B/TB) 70 return '{0:.2f}TB'.format(B/TB)
69 71
70 def make_title(status): 72 def make_title(status):
71 state=INACTIVE 73 state=status['state']
72
73 if status['type']=='scheduled': 74 if status['type']=='scheduled':
74 # Operation scheduled 75 # Operation scheduled
75 when=status['when'] 76 when=status['when']
76 now=time.time() 77 now=time.time()
77 if when<now: 78 if when<now:
86 if twhen.tm_sec>30: 87 if twhen.tm_sec>30:
87 # Round up minute display to avoid user confusion 88 # Round up minute display to avoid user confusion
88 twhen=time.localtime(when+30) 89 twhen=time.localtime(when+30)
89 whenstr='at %02d:%02d' % (twhen.tm_hour, twhen.tm_min) 90 whenstr='at %02d:%02d' % (twhen.tm_hour, twhen.tm_min)
90 91
91 if status['detail']=='normal': 92 detail=''
92 state=SCHEDULED_OK 93 if state>=backup.BUSY and state in statestring:
93 detail='' 94 detail=statestring[state] + '; '
94 else: 95 if status['detail']!='normal':
95 if status['detail']=='retry': 96 detail=detail+status['detail']+' '
96 state=ERRORS
97 detail=status['detail']+' '
98 title="%s (%s%s %s)" % (status['name'], detail, status['operation'], whenstr) 97 title="%s (%s%s %s)" % (status['name'], detail, status['operation'], whenstr)
99 elif status['type']=='current': 98 elif status['type']=='current':
100 # Operation running 99 # Operation running
101 progress='' 100 progress=''
102 if 'progress_current' in status and 'progress_total' in status: 101 if 'progress_current' in status and 'progress_total' in status:
103 progress=' %d%%' % (status['progress_current']/status['progress_total']) 102 progress=' %d%%' % (status['progress_current']/status['progress_total'])
104 elif 'original_size' in status and 'deduplicated_size' in status: 103 elif 'original_size' in status and 'deduplicated_size' in status:
105 progress=' %s→%s' % (humanbytes(status['original_size']), 104 progress=' %s→%s' % (humanbytes(status['original_size']),
106 humanbytes(status['deduplicated_size'])) 105 humanbytes(status['deduplicated_size']))
107 title="%s (running: %s%s)" % (status['name'], status['operation'], progress) 106 title="%s (running: %s%s)" % (status['name'], status['operation'], progress)
108 state=ACTIVE
109 else: # status['type']=='nothing': 107 else: # status['type']=='nothing':
110 # Should be unscheduled, nothing running 108 # Should be unscheduled, nothing running
111 title=status['name'] 109 detail=''
112 if status['errors']: 110 if state>=backup.BUSY and state in statestring:
113 title=title + " (errors)" 111 detail=' (' + statestring[state] + ')'
114 112 title=status['name'] + detail
115 if status['errors']:
116 state=ERRORS
117 113
118 return title, state 114 return title, state
119 115
120 class BorgendTray(rumps.App): 116 class BorgendTray(rumps.App):
121 def __init__(self, backups): 117 def __init__(self, backups):
145 141
146 super().__init__(traynames[state], menu=menu, quit_button=None) 142 super().__init__(traynames[state], menu=menu, quit_button=None)
147 143
148 def __rebuild_menu(self): 144 def __rebuild_menu(self):
149 menu=[] 145 menu=[]
150 state=INACTIVE 146 state=backup.INACTIVE
151 for index in range(len(self.backups)): 147 for index in range(len(self.backups)):
152 b=self.backups[index] 148 b=self.backups[index]
153 title, this_state=make_title(self.statuses[index]) 149 title, this_state=make_title(self.statuses[index])
154 # Python closures suck dog's balls... 150 # Python closures suck dog's balls...
155 # first and the last program I write in Python until somebody 151 # first and the last program I write in Python until somebody
156 # fixes this brain damage 152 # fixes this brain damage
157 cbm=lambda sender, _b=b: self.__menu_select_backup(sender, _b) 153 cbm=lambda sender, _b=b: self.__menu_select_backup(sender, _b)
158 item=rumps.MenuItem(title, callback=cbm) 154 item=rumps.MenuItem(title, callback=cbm)
159 if this_state==SCHEDULED_OK: 155 if this_state==backup.SCHEDULED:
160 item.state=1 156 item.state=1
161 elif this_state>=OFFLINE: 157 elif this_state>=backup.BUSY:
162 item.state=-1 158 item.state=-1
163 menu.append(item) 159 menu.append(item)
164 state=combine_state(state, this_state) 160 state=backup.combine_state(state, this_state)
165 161
166 menu_log=rumps.MenuItem("Show log", callback=lambda _: showlog()) 162 menu_log=rumps.MenuItem("Show log", callback=lambda _: showlog())
167 menu.append(menu_log) 163 menu.append(menu_log)
168 164
169 if not settings['no_quit_menu_entry']: 165 if not settings['no_quit_menu_entry']:

mercurial