File: notificationarea.py

package info (click to toggle)
timekpr-next 0.5.4-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,440 kB
  • sloc: python: 7,938; sh: 98; xml: 39; makefile: 6
file content (247 lines) | stat: -rw-r--r-- 12,436 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
"""
Created on Aug 28, 2018

@author: mjasnik
"""

# import
from datetime import timedelta
import os

# timekpr imports
from timekpr.common.constants import constants as cons
from timekpr.common.log import log
from timekpr.client.interface.dbus.notifications import timekprNotifications
from timekpr.client.gui.clientgui import timekprGUI


class timekprNotificationArea(object):
    """Support appindicator or other means of showing icon on the screen (this class is a parent for classes like indicator or staticon)"""

    def __init__(self, pUserName, pUserNameFull, pTimekprClientConfig):
        """Init all required stuff for indicator"""

        log.log(cons.TK_LOG_LEVEL_INFO, "start init timekpr indicator")

        # configuration
        self._timekprClientConfig = pTimekprClientConfig

        # set version
        self._timekprVersion = "-.-.-"
        # set username
        self._userName = pUserName
        # initialize priority
        self._lastUsedPriority = self._lastUsedServerPriority = ""
        # priority level
        self._lastUsedPriorityLvl = -99
        # PlayTime priority level
        self._lastUsedPTPriorityLvl = -99
        # initialize time left
        self._timeLeftTotal = None
        # initialize PlayTime left
        self._playTimeLeftTotal = None
        # initialize time limit
        self._timeNotLimited = 0

        # init notificaction stuff
        self._timekprNotifications = timekprNotifications(self._userName, self._timekprClientConfig)

        # dbus
        self._timekprBus = None
        self._notifyObject = None
        self._notifyInterface = None

        # gui forms
        self._timekprGUI = timekprGUI(cons.TK_VERSION, self._timekprClientConfig, self._userName, pUserNameFull)

        log.log(cons.TK_LOG_LEVEL_INFO, "finish init timekpr indicator")

    def initClientConnections(self):
        """Proxy method for initialization"""
        # initalize DBUS connections to every additional module
        self._timekprNotifications.initClientConnections()

    def isTimekprConnected(self):
        """Proxy method for initialization status"""
        # check if main connection to timekpr is up
        return self._timekprNotifications.isTimekprConnected()

    def verifySessionAttributes(self, pWhat, pKey):
        """Proxy method for receive the signal and process the data"""
        self._timekprNotifications.verifySessionAttributes(pWhat, pKey)

    def requestTimeLimits(self):
        """Proxy method for request time limits from server"""
        self._timekprNotifications.requestTimeLimits()

    def requestTimeLeft(self):
        """Proxy method for request time left from server"""
        self._timekprNotifications.requestTimeLeft()

    def _determinePriority(self, pType, pPriority, pTimeLeft):
        """Determine priority based on client config"""
        # def
        finalPrio = pPriority
        finalLimitSecs = -1
        # keep in mind that this applies to timeLeft only and critical notifications can STILL be pushed from server
        if pTimeLeft is not None:
            # calculate
            for rPrio in self._timekprClientConfig.getClientNotificationLevels() if pType == "Time" else self._timekprClientConfig.getClientPlayTimeNotificationLevels():
                # determine which is the earliest priority level we need to use
                # it is determined as time left is less then this interval
                if rPrio[0] >= pTimeLeft and (finalLimitSecs > rPrio[0] or finalLimitSecs < 0):
                    # determine if this is the gratest level that is lower than limit
                    finalLimitSecs = rPrio[0]
                    finalPrio = cons.TK_PRIO_LVL_MAP[rPrio[1]]
        # final priority
        return finalPrio, finalLimitSecs

    def formatTimeLeft(self, pPriority, pTimeLeft, pTimeNotLimited, pPlayTimeLeft=None):
        """Set time left in the indicator"""
        log.log(cons.TK_LOG_LEVEL_DEBUG, "start formatTimeLeft")

        # prio
        prio = pPriority
        timekprIcon = None
        timeLeftStr = None
        isTimeChanged = self._timeLeftTotal != pTimeLeft
        isPlayTimeChanged = self._playTimeLeftTotal != pPlayTimeLeft

        # determine hours and minutes for PlayTime (if there is such time)
        if (isTimeChanged or isPlayTimeChanged) and pPlayTimeLeft is not None and pTimeLeft is not None:
            # get the smallest one
            timeLeftPT = min(pPlayTimeLeft, pTimeLeft)
            # determine hours and minutes
            timeLeftStrPT = str((timeLeftPT - cons.TK_DATETIME_START).days * 24 + timeLeftPT.hour).rjust(2, "0")
            timeLeftStrPT += ":" + str(timeLeftPT.minute).rjust(2, "0")
            timeLeftStrPT += ((":" + str(timeLeftPT.second).rjust(2, "0")) if self._timekprClientConfig.getClientShowSeconds() else "")

        # execute time and icon changes + notifications only when there are changes
        if isTimeChanged or isPlayTimeChanged or pTimeLeft is None or self._lastUsedServerPriority != pPriority:
            # if there is no time left set yet, show --
            if pTimeLeft is None:
                # determine hours and minutes
                timeLeftStr = "--:--" + (":--" if self._timekprClientConfig.getClientShowSeconds() else "")
            else:
                # update time
                self._timeLeftTotal = pTimeLeft
                self._playTimeLeftTotal = pPlayTimeLeft
                self._timeNotLimited = pTimeNotLimited

                # unlimited has special icon and text (if it's not anymore, these will change)
                if self._timeNotLimited > 0:
                    # unlimited!
                    timeLeftStr = "∞"
                    prio = "unlimited"
                else:
                    # determine hours and minutes
                    timeLeftStr = str((self._timeLeftTotal - cons.TK_DATETIME_START).days * 24 + self._timeLeftTotal.hour).rjust(2, "0")
                    timeLeftStr += ":" + str(self._timeLeftTotal.minute).rjust(2, "0")
                    timeLeftStr += ((":" + str(self._timeLeftTotal.second).rjust(2, "0")) if self._timekprClientConfig.getClientShowSeconds() else "")

                    # notifications and icons only when time has changed
                    if isTimeChanged:
                        # get user configured level and priority
                        prio, finLvl = (pPriority, -1) if pPriority == cons.TK_PRIO_UACC else self._determinePriority("Time", pPriority, (pTimeLeft - cons.TK_DATETIME_START).total_seconds())

                        # if level actually changed
                        if self._lastUsedPriorityLvl != finLvl:
                            # do not notify if this is the first invocation, because initial limits are already asked from server
                            # do not notify user in case icon is hidden and no notifications should be shown
                            if self._lastUsedPriorityLvl > 0 and self.getTrayIconEnabled():
                                # emit notification
                                self.notifyUser(cons.TK_MSG_CODE_TIMELEFT, None, prio, pTimeLeft, None)
                            # level this up
                            self._lastUsedPriorityLvl = finLvl

                # determine hours and minutes for PlayTime (if there is such time)
                if pPlayTimeLeft is not None:
                    # format final time string
                    timeLeftStr = "%s / %s" % (timeLeftStr, timeLeftStrPT)

                # now, if priority changes, set up icon as well
                if isTimeChanged and self._lastUsedPriority != prio:
                    # log
                    log.log(cons.TK_LOG_LEVEL_DEBUG, "changing icon for level, old: %s, new: %s" % (self._lastUsedPriority, prio))
                    # set up last used prio
                    self._lastUsedPriority = prio
                    # get status icon
                    timekprIcon = os.path.join(self._timekprClientConfig.getTimekprSharedDir(), "icons", cons.TK_PRIO_CONF[cons.getNotificationPrioriy(self._lastUsedPriority)][cons.TK_ICON_STAT])

            # adjust server priority: server sends all time left messages with low priority, except when there is no time left, then priority is critical
            self._lastUsedServerPriority = pPriority

        log.log(cons.TK_LOG_LEVEL_DEBUG, "finish formatTimeLeft")

        # return time left and icon (if changed), so implementations can use it
        return timeLeftStr, timekprIcon

    def processPlayTimeNotifications(self, pTimeLimits):
        """Process PlayTime notifications (if there is PT info in limits)"""
        isPTInfoEnabled = self._timekprGUI.isPlayTimeAccountingInfoEnabled()
        # determine whether we actually need to process PlayTime
        if cons.TK_CTRL_PTLSTC in pTimeLimits and cons.TK_CTRL_PTLPD in pTimeLimits and cons.TK_CTRL_PTTLO in pTimeLimits:
            # only of not enabled
            if not isPTInfoEnabled:
                self._timekprGUI.setPlayTimeAccountingInfoEnabled(True)
            # get user configured level and priority
            prio, finLvl = self._determinePriority("PlayTime", cons.TK_PRIO_LOW, pTimeLimits[cons.TK_CTRL_PTLPD])
            # log
            log.log(cons.TK_LOG_LEVEL_DEBUG, "process PT notif, prio: %s, prevLVL: %i, lvl: %i, icoena: %s" % (prio, self._lastUsedPTPriorityLvl, finLvl, self.getTrayIconEnabled()))
            # if any priority is effective, determine whether we need to inform user
            if (finLvl > 0 or self._lastUsedPTPriorityLvl < -1) and self._lastUsedPTPriorityLvl != finLvl and self.isTimekprConnected():
                # adjust level too
                self._lastUsedPTPriorityLvl = finLvl
                # if icon is hidden, do not show any notifications
                if self.getTrayIconEnabled():
                    # notify user
                    self._timekprNotifications.notifyUser(cons.TK_MSG_CODE_TIMELEFT, "PlayTime", prio, cons.TK_DATETIME_START + timedelta(seconds=min(pTimeLimits[cons.TK_CTRL_PTLPD], pTimeLimits[cons.TK_CTRL_LEFTD])), None)
        elif isPTInfoEnabled:
            # disable info (if it was enabled)
            log.log(cons.TK_LOG_LEVEL_DEBUG, "disable PT info tab")
            self._timekprGUI.setPlayTimeAccountingInfoEnabled(False)

    def notifyUser(self, pMsgCode, pMsgType, pPriority, pTimeLeft=None, pAdditionalMessage=None):
        """Notify user (a wrapper call)"""
        # prio
        prio = pPriority
        timeLeft = cons.TK_DATETIME_START if pTimeLeft is None else pTimeLeft
        # for time left, we need to determine final priority accoriding to user defined priority (if not defined, that will come from server)
        if pMsgCode == cons.TK_MSG_CODE_TIMELEFT:
            # get user configured level and priority
            prio, finLvl = self._determinePriority("Time", pPriority, (timeLeft - cons.TK_DATETIME_START).total_seconds())
        #  notify user
        self._timekprNotifications.notifyUser(pMsgCode, pMsgType, prio, timeLeft, pAdditionalMessage)

    def setStatus(self, pStatus):
        """Change status of timekpr"""
        return self._timekprGUI.setStatus(pStatus)

    # --------------- user clicked methods --------------- #

    def invokeTimekprTimeLeft(self, pEvent):
        """Inform user about (almost) exact time left"""
        # inform user about precise time
        self.notifyUser((cons.TK_MSG_CODE_TIMEUNLIMITED if self._timeNotLimited > 0 else cons.TK_MSG_CODE_TIMELEFT), None, self._lastUsedPriority, self._timeLeftTotal)

    def invokeTimekprUserProperties(self, pEvent):
        """Bring up a window for property editing"""
        # show limits and config
        self._timekprGUI.initConfigForm()

    def invokeTimekprAbout(self, pEvent):
        """Bring up a window for timekpr configration (this needs elevated privileges to do anything)"""
        # show about
        self._timekprGUI.initAboutForm()

    # --------------- configuration update methods --------------- #

    def renewUserLimits(self, pTimeInformation):
        """Call an update to renew time left"""
        # pass this to actual gui storage
        self._timekprGUI.renewLimits(pTimeInformation)

    def renewLimitConfiguration(self, pLimits):
        """Call an update on actual limits"""
        # pass this to actual gui storage
        self._timekprGUI.renewLimitConfiguration(pLimits)