File: daemon.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 (292 lines) | stat: -rw-r--r-- 15,075 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
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
"""
Created on Aug 28, 2018

@author: mjasnik
"""

# imports
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)
from datetime import timedelta
import os
import dbus
from gi.repository import GLib

# timekpr imports
from timekpr.common.constants import constants as cons
from timekpr.common.log import log
from timekpr.common.utils import misc
from timekpr.common.utils.config import timekprClientConfig
from timekpr.client.interface.ui.appindicator import timekprIndicator as appind_timekprIndicator
from timekpr.client.interface.ui.statusicon import timekprIndicator as statico_timekprIndicator
from timekpr.common.constants import messages as msg


class timekprClient(object):
    """Main class for holding all client logic (including dbus)"""

    # --------------- initialization / control methods --------------- #

    def __init__(self):
        """Initialize client"""
        # set username , etc.
        self._userName, self._userNameFull = misc.getNormalizedUserNames(pUID=os.getuid())
        self._userNameDBUS = self._userName.replace(".", "").replace("-", "")

        # get our bus
        self._timekprBus = (dbus.SessionBus() if (cons.TK_DEV_ACTIVE and cons.TK_DEV_BUS == "ses") else dbus.SystemBus())

        # loop
        self._mainLoop = GLib.MainLoop()

        # init logging (load config which has all the necessarry bits)
        self._timekprClientConfig = timekprClientConfig()
        self._timekprClientConfig.loadClientConfiguration()

        # init logging
        log.setLogging(self._timekprClientConfig.getClientLogLevel(), cons.TK_LOG_TEMP_DIR, cons.TK_LOG_OWNER_CLIENT, self._userName)


    def startTimekprClient(self):
        """Start up timekpr (choose appropriate gui and start this up)"""
        log.log(cons.TK_LOG_LEVEL_INFO, "starting up timekpr client")

        # check if appind is supported
        self._timekprClientIndicator = appind_timekprIndicator(self._userName, self._userNameFull, self._timekprClientConfig)

        # if not supported fall back to statico
        if not self._timekprClientIndicator.isSupported():
            # check if appind is supported
            self._timekprClientIndicator = statico_timekprIndicator(self._userName, self._userNameFull, self._timekprClientConfig)

        # this will check whether we have an icon, if not, the rest goes through timekprClient anyway
        if self._timekprClientIndicator.isSupported():
            # init timekpr
            self._timekprClientIndicator.initTimekprIcon()
        else:
            # process time left notification (notifications should be available in any of the icons, even of not supported)
            self._timekprClientIndicator.notifyUser(cons.TK_MSG_CODE_ICON_INIT_ERROR, None, cons.TK_PRIO_CRITICAL, None, "cannot initialize the icon in any way")

        # connect to timekpr etc.
        self.connectTimekprSignalsDBUS()

        # init startup notification at default interval
        GLib.timeout_add_seconds(cons.TK_POLLTIME, self.requestInitialTimeValues)

        # periodic log flusher
        GLib.timeout_add_seconds(cons.TK_POLLTIME, self.autoFlushLogFile)

        # start main loop
        self._mainLoop.run()

    def autoFlushLogFile(self):
        """Periodically save file"""
        log.autoFlushLogFile()
        return True

    def finishTimekpr(self, signal=None, frame=None):
        """Exit timekpr gracefully"""
        log.log(cons.TK_LOG_LEVEL_INFO, "Finishing up")
        # exit main loop
        self._mainLoop.quit()
        log.log(cons.TK_LOG_LEVEL_INFO, "Finished")
        log.flushLogFile()

    def requestInitialTimeValues(self):
        """Request initial config from server"""
        # whether to process again
        result = False
        # check if connected
        if self._notificationFromDBUS is not None:
            # connect to DBUS for the rest of modules
            self._timekprClientIndicator.initClientConnections()
            # request values if connections are made successfully
            result = self._timekprClientIndicator.isTimekprConnected()
            # connected?
            if result:
                # get limits
                self._timekprClientIndicator.requestTimeLimits()
                # get left
                self._timekprClientIndicator.requestTimeLeft()

        # continue execution while not connected (this is called from glib exec)
        return not result

    # --------------- DBUS / communication methods --------------- #

    def connectTimekprSignalsDBUS(self):
        """Init connections to dbus provided by server"""
        log.log(cons.TK_LOG_LEVEL_DEBUG, "start connectTimekprSignalsDBUS")

        # trying to connect
        self._timekprClientIndicator.setStatus(msg.getTranslation("TK_MSG_STATUS_CONNECTING"))

        try:
            # dbus performance measurement
            misc.measureTimeElapsed(pStart=True)

            # get dbus object
            self._notificationFromDBUS = self._timekprBus.get_object(cons.TK_DBUS_BUS_NAME, cons.TK_DBUS_USER_NOTIF_PATH_PREFIX + self._userNameDBUS)

            # connect to signal
            self._sessionAttributeVerificationSignal = self._timekprBus.add_signal_receiver(
                path             = cons.TK_DBUS_USER_NOTIF_PATH_PREFIX + self._userNameDBUS,
                handler_function = self.receiveSessionAttributeVerificationRequest,
                dbus_interface   = cons.TK_DBUS_USER_SESSION_ATTRIBUTE_INTERFACE,
                signal_name      = "sessionAttributeVerification")

            # connect to signal
            self._timeLeftSignal = self._timekprBus.add_signal_receiver(
                path             = cons.TK_DBUS_USER_NOTIF_PATH_PREFIX + self._userNameDBUS,
                handler_function = self.receiveTimeLeft,
                dbus_interface   = cons.TK_DBUS_USER_LIMITS_INTERFACE,
                signal_name      = "timeLeft")

            # connect to signal
            self._timeLimitsSignal = self._timekprBus.add_signal_receiver(
                path             = cons.TK_DBUS_USER_NOTIF_PATH_PREFIX + self._userNameDBUS,
                handler_function = self.receiveTimeLimits,
                dbus_interface   = cons.TK_DBUS_USER_LIMITS_INTERFACE,
                signal_name      = "timeLimits")

            # connect to signal
            self._timeLeftNotificatonSignal = self._timekprBus.add_signal_receiver(
                path             = cons.TK_DBUS_USER_NOTIF_PATH_PREFIX + self._userNameDBUS,
                handler_function = self.receiveTimeLeftNotification,
                dbus_interface   = cons.TK_DBUS_USER_NOTIF_INTERFACE,
                signal_name      = "timeLeftNotification")

            # connect to signal
            self._timeCriticalNotificatonSignal = self._timekprBus.add_signal_receiver(
                path             = cons.TK_DBUS_USER_NOTIF_PATH_PREFIX + self._userNameDBUS,
                handler_function = self.receiveTimeCriticalNotification,
                dbus_interface   = cons.TK_DBUS_USER_NOTIF_INTERFACE,
                signal_name      = "timeCriticalNotification")

            # connect to signal
            self._timeNoLimitNotificationSignal = self._timekprBus.add_signal_receiver(
                path             = cons.TK_DBUS_USER_NOTIF_PATH_PREFIX + self._userNameDBUS,
                handler_function = self.receiveTimeNoLimitNotification,
                dbus_interface   = cons.TK_DBUS_USER_NOTIF_INTERFACE,
                signal_name      = "timeNoLimitNotification")

            # connect to signal
            self._timeLeftChangedNotificationSignal = self._timekprBus.add_signal_receiver(
                path             = cons.TK_DBUS_USER_NOTIF_PATH_PREFIX + self._userNameDBUS,
                handler_function = self.receiveTimeLeftChangedNotification,
                dbus_interface   = cons.TK_DBUS_USER_NOTIF_INTERFACE,
                signal_name      = "timeLeftChangedNotification")

            # connect to signal
            self._timeConfigurationChangedNotificationSignal = self._timekprBus.add_signal_receiver(
                path             = cons.TK_DBUS_USER_NOTIF_PATH_PREFIX + self._userNameDBUS,
                handler_function = self.receiveTimeConfigurationChangedNotification,
                dbus_interface   = cons.TK_DBUS_USER_NOTIF_INTERFACE,
                signal_name      = "timeConfigurationChangedNotification")

            # measurement logging
            log.log(cons.TK_LOG_LEVEL_INFO, "PERFORMANCE (DBUS) - connecting signals \"%s\" took too long (%is)" % (cons.TK_DBUS_BUS_NAME, misc.measureTimeElapsed(pResult=True))) if misc.measureTimeElapsed(pStop=True) >= cons.TK_DBUS_ANSWER_TIME else True

            # set status
            self._timekprClientIndicator.setStatus(msg.getTranslation("TK_MSG_STATUS_CONNECTED"))

            log.log(cons.TK_LOG_LEVEL_DEBUG, "main DBUS signals connected")

        except Exception as dbusEx:
            # logging
            log.log(cons.TK_LOG_LEVEL_INFO, "ERROR (DBUS): \"%s\" in \"%s.%s\"" % (str(dbusEx), __name__, self.connectTimekprSignalsDBUS.__name__))
            log.log(cons.TK_LOG_LEVEL_INFO, "ERROR: failed to connect to timekpr dbus, trying again...")

            # did not connect (set connection to None) and schedule for reconnect at default interval
            self._notificationFromDBUS = None

            # connect until successful
            GLib.timeout_add_seconds(cons.TK_POLLTIME, self.connectTimekprSignalsDBUS)

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

        # finish
        return False

    # --------------- admininstration / verification methods (from dbus) --------------- #

    def receiveSessionAttributeVerificationRequest(self, pWhat, pKey):
        """Receive the signal and process the data"""
        log.log(cons.TK_LOG_LEVEL_DEBUG, "receive verification request: %s, %s" % (pWhat, "key"))
        # resend stuff to server
        self._timekprClientIndicator.verifySessionAttributes(pWhat, pKey)

    def processShowClientIcon(self, pTimeInformation):
        """Check wheter to show or hide tray icon"""
        # do we have information about show or hide icon
        if cons.TK_CTRL_HIDEI in pTimeInformation:
            # enable?
            iconStatus = (not bool(pTimeInformation[cons.TK_CTRL_HIDEI]))
            # check if those differ
            if self._timekprClientIndicator.getTrayIconEnabled() != iconStatus:
                # set it
                self._timekprClientIndicator.setTrayIconEnabled(iconStatus)

    # --------------- worker methods (from dbus) --------------- #

    def receiveTimeLeft(self, pPriority, pTimeInformation):
        """Receive the signal and process the data to user"""
        # check which options are available
        timeLeft = (pTimeInformation[cons.TK_CTRL_LEFT] if cons.TK_CTRL_LEFT in pTimeInformation else 0)
        playTimeLeft = (pTimeInformation[cons.TK_CTRL_PTLPD] if cons.TK_CTRL_PTLSTC in pTimeInformation and cons.TK_CTRL_PTLPD in pTimeInformation and cons.TK_CTRL_PTTLO in pTimeInformation else None)
        isTimeNotLimited = (pTimeInformation[cons.TK_CTRL_TNL] if cons.TK_CTRL_TNL in pTimeInformation else 0)
        log.log(cons.TK_LOG_LEVEL_DEBUG, "receive timeleft, prio: %s, tl: %i, ptl: %s, nolim: %i" % (pPriority, timeLeft, str(playTimeLeft), isTimeNotLimited))
        # process show / hide icon
        self.processShowClientIcon(pTimeInformation)
        # process time left
        self._timekprClientIndicator.setTimeLeft(pPriority, cons.TK_DATETIME_START + timedelta(seconds=timeLeft), isTimeNotLimited, cons.TK_DATETIME_START + timedelta(seconds=playTimeLeft) if playTimeLeft is not None else playTimeLeft)
        # renew limits in GUI
        self._timekprClientIndicator.renewUserLimits(pTimeInformation)
        # process PlayTime notifications as well
        self._timekprClientIndicator.processPlayTimeNotifications(pTimeInformation)

    def receiveTimeLimits(self, pPriority, pTimeLimits):
        """Receive the signal and process the data to user"""
        log.log(cons.TK_LOG_LEVEL_DEBUG, "receive timelimits: %s" % (pPriority))
        # renew limits in GUI
        self._timekprClientIndicator.renewLimitConfiguration(pTimeLimits)

    # --------------- notification methods (from dbus) --------------- #

    def receiveTimeLeftNotification(self, pPriority, pTimeLeftTotal, pTimeLeftToday, pTimeLimitToday):
        """Receive time left and update GUI"""
        log.log(cons.TK_LOG_LEVEL_DEBUG, "receive tl notif: %s, %i" % (pPriority, pTimeLeftTotal))
        # if notifications are turned on
        if (self._timekprClientConfig.getClientShowAllNotifications() and self._timekprClientIndicator.getTrayIconEnabled()) or pPriority == cons.TK_PRIO_CRITICAL:
            # process time left notification
            self._timekprClientIndicator.notifyUser(cons.TK_MSG_CODE_TIMELEFT, None, pPriority, cons.TK_DATETIME_START + timedelta(seconds=pTimeLeftTotal))

    def receiveTimeCriticalNotification(self, pFinalNotificationType, pPriority, pSecondsLeft):
        """Receive critical time left and show that to user"""
        log.log(cons.TK_LOG_LEVEL_DEBUG, "receive crit notif: %s, %i" % (pFinalNotificationType, pSecondsLeft))
        # process time left (this shows in any case)
        self._timekprClientIndicator.notifyUser(cons.TK_MSG_CODE_TIMECRITICAL, pFinalNotificationType, pPriority, cons.TK_DATETIME_START + timedelta(seconds=pSecondsLeft))

    def receiveTimeNoLimitNotification(self, pPriority):
        """Receive no limit notificaton and show that to user"""
        log.log(cons.TK_LOG_LEVEL_DEBUG, "receive nl notif")
        # if notifications are turned on
        if self._timekprClientConfig.getClientShowAllNotifications() and self._timekprClientIndicator.getTrayIconEnabled():
            # process time left
            self._timekprClientIndicator.notifyUser(cons.TK_MSG_CODE_TIMEUNLIMITED, None, pPriority)

    def receiveTimeLeftChangedNotification(self, pPriority):
        """Receive time left notification and show it to user"""
        log.log(cons.TK_LOG_LEVEL_DEBUG, "receive time left changed notif")
        # if notifications are turned on
        if self._timekprClientConfig.getClientShowLimitNotifications() and self._timekprClientIndicator.getTrayIconEnabled():
            # limits have changed and applied
            self._timekprClientIndicator.notifyUser(cons.TK_MSG_CODE_TIMELEFTCHANGED, None, pPriority)

    def receiveTimeConfigurationChangedNotification(self, pPriority):
        """Receive notification about config change and show it to user"""
        log.log(cons.TK_LOG_LEVEL_DEBUG, "receive config changed notif")
        # if notifications are turned on
        if self._timekprClientConfig.getClientShowLimitNotifications() and self._timekprClientIndicator.getTrayIconEnabled():
            # configuration has changed, new limits may have been applied
            self._timekprClientIndicator.notifyUser(cons.TK_MSG_CODE_TIMECONFIGCHANGED, None, pPriority)