timekpr-next/ 000775 001750 001750 00000000000 15122253261 015174 5 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/client/ 000775 001750 001750 00000000000 14575617135 016471 5 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/client/gui/ 000775 001750 001750 00000000000 15122253261 017236 5 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/client/gui/admingui.py 000644 001750 001750 00000472120 15122253261 021411 0 ustar 00bezvfedu bezvfedu 000000 000000 """
Created on Aug 28, 2018
@author: mjasnik
"""
import gi
import os
import webbrowser
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GLib
from datetime import timedelta, datetime
import re
# timekpr imports
from timekpr.common.constants import constants as cons
from timekpr.common.log import log
from timekpr.client.interface.dbus.administration import timekprAdminConnector
from timekpr.common.constants import messages as msg
# constant
_NO_TIME_LABEL_SHORT = "--:--"
_NO_TIME_LABEL = "--:--:--"
_NO_TIME_LIMIT_LABEL = "--:--:--:--"
_HOUR_REGEXP = re.compile(r"^([0-9]{1,2}).*$")
_HOUR_MIN_REGEXP = re.compile(r"^([0-9]{1,2}):([0-9]{1,2}).*$")
_DAY_HOUR_MIN_REGEXP = re.compile(r"^([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}).*$")
_DAY_HOUR_MIN_SEC_REGEXP = re.compile(r"^([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}).*$")
class timekprAdminGUI(object):
"""Main class for supporting timekpr forms"""
def __init__(self, pTimekprVersion, pResourcePath, pUsername):
"""Initialize gui"""
# set up base variables
self._userName = pUsername
self._timekprVersion = pTimekprVersion
self._resourcePath = pResourcePath
self._timekprAdminConnector = None
self._isConnected = False
self._ROWCOL_OK = "#FFFFFF"
self._ROWSTYLE_OK = False
self._ROWCOL_NOK = "Yellow"
self._ROWSTYLE_NOK = True
# ## forms builders ##
# init config builder
self._timekprAdminFormBuilder = Gtk.Builder()
# get our dialog
self._timekprAdminFormBuilder.add_from_file(os.path.join(self._resourcePath, "admin.glade"))
# connect signals, so they get executed
self._timekprAdminFormBuilder.connect_signals(self)
# get window
self._timekprAdminForm = self._timekprAdminFormBuilder.get_object("TimekprApplicationWindow")
# set up GUI elements
self.initGUIElements()
# initialize internal stuff
self.initInternalConfiguration()
# disable all user config buttons firstly
self.toggleUserConfigControls(False)
# disable all timekpr config buttons firstly
self.toggleTimekprConfigControls(False)
# status
self.setTimekprStatus(True, msg.getTranslation("TK_MSG_STATUS_STARTED"))
# initialize internal stuff
GLib.timeout_add_seconds(0.1, self.initTimekprAdmin)
# periodic log flusher
GLib.timeout_add_seconds(cons.TK_POLLTIME, self.autoFlushLogFile)
# loop
self._mainLoop = GLib.MainLoop()
# --------------- initialization / helper methods --------------- #
def startAdminGUI(self):
"""Start up main loop"""
# show up all
self._timekprAdminForm.show()
# this seems to be needed
self.dummyPageChanger()
# start main loop
self._mainLoop.run()
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.autoFlushLogFile(True)
def autoFlushLogFile(self):
"""Periodically save file"""
log.autoFlushLogFile()
return True
def dummyPageChanger(self):
"""Switch tabs back and forth"""
# change pages (so objects get initialized, w/o this, spin butons don't get values when set :O)
for rIdx in (1, 0):
self._timekprAdminFormBuilder.get_object("TimekprMainTabBar").set_current_page(rIdx)
for rIdx in (1, 2, 0):
self._timekprAdminFormBuilder.get_object("TimekprConfigurationTabBar").set_current_page(rIdx)
# init timekpr admin client
def initTimekprAdmin(self):
"""Initialize admin client"""
# get our connector
self._timekprAdminConnector = timekprAdminConnector()
# connect
GLib.timeout_add_seconds(0, self._timekprAdminConnector.initTimekprConnection, False)
# check connection
GLib.timeout_add_seconds(0.1, self.checkConnection)
# user calculated info retriever
GLib.timeout_add_seconds(cons.TK_SAVE_INTERVAL, self.retrieveUserInfoAndConfig, None, cons.TK_CL_INF_SAVED)
# user "realtime" info retriever
GLib.timeout_add_seconds(cons.TK_POLLTIME, self.retrieveUserInfoAndConfig, None, cons.TK_CL_INF_RT)
def checkConnection(self):
"""Check connection on the fly"""
# connection statuses
interfacesOk, connecting = self._timekprAdminConnector.isConnected()
# if not connected, give up and get out
if interfacesOk and connecting:
# status
self.setTimekprStatus(True, msg.getTranslation("TK_MSG_STATUS_CONNECTED"))
# in case we are connected, do not retrieve config again
if not self._isConnected:
# connected
self._isConnected = True
# get users
GLib.timeout_add_seconds(0, self.getAdminUserList)
GLib.timeout_add_seconds(0.1, self.retrieveTimekprConfig)
elif not interfacesOk and connecting:
# status
self.setTimekprStatus(True, msg.getTranslation("TK_MSG_STATUS_CONNECTING"))
# invoke again
GLib.timeout_add_seconds(1, self.checkConnection)
# not connected
self._isConnected = False
else:
# status
self.setTimekprStatus(True, msg.getTranslation("TK_MSG_STATUS_CONNECTION_FAILED"))
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_CONNECTION_ACCESS_DENIED"))
# not connected
self._isConnected = False
def initGUIElements(self):
"""Initialize all GUI elements for stores"""
# ## tracked session types ##
rend = Gtk.CellRendererText()
rend.set_property("editable", True)
rend.set_property("placeholder-text", msg.getTranslation("TK_MSG_TRACKED_SESSIONS_PHLD_LABEL"))
rend.connect("edited", self.timekprTrackedSessionsEdited)
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_TRACKED_SESSIONS_LABEL"), rend, text=0)
col.set_min_width(125)
self._timekprAdminFormBuilder.get_object("TimekprTrackingSessionsTreeView").append_column(col)
# clear
self._timekprAdminFormBuilder.get_object("TimekprTrackingSessionsLS").clear()
# ## excluded session types ##
rend = Gtk.CellRendererText()
rend.set_property("editable", True)
rend.set_property("placeholder-text", msg.getTranslation("TK_MSG_UNTRACKED_SESSIONS_PHLD_LABEL"))
rend.connect("edited", self.timekprExcludedSessionsEdited)
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_UNTRACKED_SESSIONS_LABEL"), rend, text=0)
col.set_min_width(125)
self._timekprAdminFormBuilder.get_object("TimekprExcludedSessionsTreeView").append_column(col)
# clear
self._timekprAdminFormBuilder.get_object("TimekprExcludedSessionsLS").clear()
# ## excluded users ##
rend = Gtk.CellRendererText()
rend.set_property("editable", True)
rend.set_property("placeholder-text", msg.getTranslation("TK_MSG_EXCLUDED_USERS_PHLD_LABEL"))
rend.connect("edited", self.timekprExcludedUsersEdited)
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_EXCLUDED_USERS_LABEL"), rend, text=0)
col.set_min_width(125)
self._timekprAdminFormBuilder.get_object("TimekprExcludedUsersTreeView").append_column(col)
# clear
self._timekprAdminFormBuilder.get_object("TimekprExcludedUsersLS").clear()
# ## days ##
# day name
rend = Gtk.CellRendererText()
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_DAY_LIST_DAY_LABEL"), rend, text=1)
col.set_min_width(115)
self._timekprAdminFormBuilder.get_object("TimekprWeekDaysTreeView").append_column(col)
# limit
rend = Gtk.CellRendererText()
rend.set_property("editable", True)
rend.connect("edited", self.userLimitsDailyLimitsEdited)
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_DAY_LIST_LIMIT_LABEL"), rend, text=4)
col.set_min_width(60)
self._timekprAdminFormBuilder.get_object("TimekprWeekDaysTreeView").append_column(col)
# day enabled
rend = Gtk.CellRendererToggle()
rend.connect("toggled", self.dayAvailabilityChanged)
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_DAY_LIST_ENABLED_LABEL"), rend, active=2)
rend.set_property("activatable", True)
col.set_min_width(35)
self._timekprAdminFormBuilder.get_object("TimekprWeekDaysTreeView").append_column(col)
# final col
col = Gtk.TreeViewColumn("", Gtk.CellRendererText())
col.set_min_width(20)
self._timekprAdminFormBuilder.get_object("TimekprWeekDaysTreeView").append_column(col)
# ## intervals ##
# from hour
rend = Gtk.CellRendererText()
rend.set_property("editable", True)
rend.set_property("placeholder-text", msg.getTranslation("TK_MSG_DAY_INTERVALS_FROM_PHLD_LABEL"))
rend.connect("edited", self.userLimitsHourFromEdited)
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_DAY_INTERVALS_FROM_LABEL"), rend, text=1, background=6, underline=7)
col.set_min_width(62)
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsTreeView").append_column(col)
# to hour
rend = Gtk.CellRendererText()
rend.set_property("editable", True)
rend.set_property("placeholder-text", msg.getTranslation("TK_MSG_DAY_INTERVALS_TO_PHLD_LABEL"))
rend.connect("edited", self.userLimitsHourToEdited)
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_DAY_INTERVALS_TO_LABEL"), rend, text=2, background=6, underline=7)
col.set_min_width(62)
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsTreeView").append_column(col)
# unaccountable interval column
rend = Gtk.CellRendererToggle()
rend.connect("toggled", self.userLimitsHourUnaccountableToggled)
col = Gtk.TreeViewColumn("∞", rend, active=8)
col.set_property("alignment", 0.5)
col.set_min_width(20)
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsTreeView").append_column(col)
# final col
col = Gtk.TreeViewColumn("", Gtk.CellRendererText())
col.set_min_width(20)
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsTreeView").append_column(col)
# clear out existing intervals
self._timekprAdminFormBuilder.get_object("TimekprWeekDaysLS").clear()
# lets prepare week days
for rDay in range(1, 7+1):
# fill in the intervals
self._timekprAdminFormBuilder.get_object("TimekprWeekDaysLS").append([str(rDay), (cons.TK_DATETIME_START + timedelta(days=rDay-1)).strftime("%A"), False, 0, _NO_TIME_LABEL])
# ## weekly / monthly limits ##
# type
rend = Gtk.CellRendererText()
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_WK_MON_LABEL"), rend, text=1)
col.set_min_width(90)
self._timekprAdminFormBuilder.get_object("TimekprUserConfWkMonLimitsTreeView").append_column(col)
# weekly/monthly limit
rend = Gtk.CellRendererText()
rend.set_property("editable", True)
rend.connect("edited", self.userLimitsWeeklyLimitsEdited)
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_WK_MON_LIMIT_LABEL"), rend, text=3)
col.set_min_width(95)
self._timekprAdminFormBuilder.get_object("TimekprUserConfWkMonLimitsTreeView").append_column(col)
# final col
col = Gtk.TreeViewColumn("", Gtk.CellRendererText())
col.set_min_width(20)
self._timekprAdminFormBuilder.get_object("TimekprUserConfWkMonLimitsTreeView").append_column(col)
# clear out existing intervals
self._timekprAdminFormBuilder.get_object("TimekprUserConfWkMonLimitsLS").clear()
# lets prepare week days
for rType in (("WK", msg.getTranslation("TK_MSG_WEEKLY_LABEL")), ("MON", msg.getTranslation("TK_MSG_MONTHLY_LABEL"))):
# fill in the intervals
self._timekprAdminFormBuilder.get_object("TimekprUserConfWkMonLimitsLS").append([rType[0], rType[1], 0, _NO_TIME_LIMIT_LABEL])
# ## PlayTime elements ##
# day name
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_DAY_LIST_DAY_LABEL"), Gtk.CellRendererText(), text=1)
col.set_min_width(115)
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLimitsTreeView").append_column(col)
# day enabled
rend = Gtk.CellRendererToggle()
rend.connect("toggled", self.dayPlayTimeAvailabilityChanged)
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_DAY_LIST_ENABLED_LABEL"), rend, active=2)
col.set_min_width(35)
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLimitsTreeView").append_column(col)
# limit
rend = Gtk.CellRendererText()
rend.set_property("editable", True)
rend.connect("edited", self.userLimitsDailyPlayTimeLimitsEdited)
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_DAY_LIST_LIMIT_LABEL"), rend, text=4)
col.set_min_width(60)
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLimitsTreeView").append_column(col)
# final col
col = Gtk.TreeViewColumn("", Gtk.CellRendererText())
col.set_min_width(20)
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLimitsTreeView").append_column(col)
# PT activity mask
rend = Gtk.CellRendererText()
rend.set_property("editable", True)
rend.set_property("placeholder-text", msg.getTranslation("TK_MSG_PLAYTIME_ACTIVITY_MASK_PHLD_LABEL"))
rend.connect("edited", self.playTimeActivityMaskEntryEdited)
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_PLAYTIME_ACTIVITY_MASK_LABEL"), rend, text=1)
col.set_min_width(90)
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesTreeView").append_column(col)
# PT activity name
rend = Gtk.CellRendererText()
rend.set_property("editable", True)
rend.set_property("placeholder-text", msg.getTranslation("TK_MSG_PLAYTIME_ACTIVITY_DESCRIPTION_PHLD_LABEL"))
rend.connect("edited", self.playTimeActivityDescriptionEntryEdited)
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_PLAYTIME_ACTIVITY_DESCRIPTION_LABEL"), rend, text=2)
col.set_min_width(120)
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesTreeView").append_column(col)
# lets prepare week days for PlayTime
for rDay in range(1, 7+1):
# fill in the intervals
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLimitsLS").append([str(rDay), (cons.TK_DATETIME_START + timedelta(days=rDay-1)).strftime("%A"), False, 0, _NO_TIME_LABEL])
# --------------- GUI control methods --------------- #
def initInternalConfiguration(self):
"""Initialize the internal configuration for admin form"""
self._timekprUserConfigControlElements = [
# combo
"TimekprUserSelectionCB",
# combom refresh
"TimekprUserSelectionRefreshBT",
# control buttons
"TimekprUserConfDaySettingsApplyBT",
"TimekprUserConfTodaySettingsSetAddBT",
"TimekprUserConfTodaySettingsSetSubractBT",
"TimekprUserConfTodaySettingsSetSetBT",
"TimekprUserPlayTimeProcessesAdjustmentAddBT",
"TimekprUserPlayTimeProcessesAdjustmentRemoveBT",
"TimekprUserPlayTimeProcessesApplyBT",
"TimekprUserConfDaySettingsSetDaysIntervalsVerifyBT",
"TimekprUserConfAddOptsApplyBT",
# check box
"TimekprUserConfTodaySettingsTrackInactiveCB",
"TimekprUserConfTodaySettingsHideTrayIconCB",
"TimekprUserPlayTimeEnableCB",
"TimekprUserPlayTimeOverrideEnableCB",
"TimekprUserPlayTimeUnaccountedIntervalsEnabledCB",
# spin buttons for adjustments
"TimekprUserConfTodaySettingsSetMinSB",
"TimekprUserConfTodaySettingsSetHrSB",
"TimekprUserConfAddOptsLockoutTypeSuspendWakeFromSB",
"TimekprUserConfAddOptsLockoutTypeSuspendWakeToSB",
# lists
"TimekprWeekDaysTreeView",
"TimekprHourIntervalsTreeView",
"TimekprUserConfWkMonLimitsTreeView",
"TimekprUserPlayTimeLimitsTreeView",
"TimekprUserPlayTimeProcessesTreeView",
# radio / control groups
"TimekprUserConfDaySettingsSetDaysHeaderControlBX",
"TimekprUserConfDaySettingsSetDaysIntervalsControlBX",
"TimekprUserConfWkMonLimitsAdjustmentsBX",
"TimekprUserConfWkMonLimitsAdjustmentControlButtonsBX",
"TimekprUserPlayTimeLimitsHeaderControlBX",
"TimekprUserConfAddOptsLockoutTypeChoiceBoxBX",
"TimekprUserConfTodaySettingsChoiceBX"
]
self._timekprConfigControlElements = [
# control buttons
"TimekprConfigurationApplyBT",
# check boxes
"TimekprPlayTimeEnableGlobalCB",
"TimekprPlayTimeEnhancedActivityMonitorCB",
# spin buttons for adjustments
"TimekprConfigurationLoglevelSB",
"TimekprConfigurationWarningTimeSB",
"TimekprConfigurationPollIntervalSB",
"TimekprConfigurationSaveTimeSB",
"TimekprConfigurationTerminationTimeSB",
"TimekprConfigurationFinalNotificationSB",
# lists
"TimekprTrackingSessionsTreeView",
"TimekprExcludedSessionsTreeView",
"TimekprExcludedUsersTreeView",
# radio / control groups
"TimekprTrackingSessionsButtonControlBX",
"TimekprExcludedSessionsButtonControlBX",
"TimekprExcludedUsersButtonControlBX"
]
# sets up limit variables for user configuration (internal config to compare to)
self._tkSavedCfg = {}
self._tkSavedCfg["timeTrackInactive"] = False
self._tkSavedCfg["timeHideTrayIcon"] = False
self._tkSavedCfg["timeLockoutType"] = cons.TK_CTRL_RES_T
self._tkSavedCfg["timeWakeInterval"] = "0;23"
self._tkSavedCfg["timeLimitWeek"] = 0
self._tkSavedCfg["timeLimitMonth"] = 0
self._tkSavedCfg["timeLimitDays"] = []
self._tkSavedCfg["timeLimitDaysLimits"] = []
self._tkSavedCfg["timeLimitDaysHoursActual"] = {}
self._tkSavedCfg["timeLimitDaysHoursSaved"] = {}
# initial config
for rDay in range(1, 7+1):
self._tkSavedCfg["timeLimitDaysHoursActual"][str(rDay)] = {}
for rHour in range(0, 23+1):
self._tkSavedCfg["timeLimitDaysHoursActual"][str(rDay)][str(rHour)] = {cons.TK_CTRL_SMIN: 0, cons.TK_CTRL_EMIN: cons.TK_LIMIT_PER_MINUTE, cons.TK_CTRL_UACC: True}
# saved means from server, actual means modified in form
self._tkSavedCfg["timeLimitDaysHoursSaved"] = self._tkSavedCfg["timeLimitDaysHoursActual"].copy()
# ## set up PlayTime variables ##
self._tkSavedCfg["playTimeEnabled"] = False
self._tkSavedCfg["playTimeOverrideEnabled"] = False
self._tkSavedCfg["playTimeUnaccountedIntervalsEnabled"] = False
self._tkSavedCfg["playTimeLimitDays"] = []
self._tkSavedCfg["playTimeLimitDaysLimits"] = []
self._tkSavedCfg["playTimeActivities"] = []
# sets up limit variables for timekpr configuration
self._tkSavedCfg["timekprWarningTime"] = 0
self._tkSavedCfg["timekprNotificationTime"] = 0
self._tkSavedCfg["timekprPollingInterval"] = 0
self._tkSavedCfg["timekprSaveTime"] = 0
self._tkSavedCfg["timekprTerminationTime"] = 0
self._tkSavedCfg["timekprLogLevel"] = 0
self._tkSavedCfg["timekprTrackingSessions"] = []
self._tkSavedCfg["timekprExcludedSessions"] = []
self._tkSavedCfg["timekprExcludedUsers"] = []
self._tkSavedCfg["timekprPlayTimeEnabled"] = False
self._tkSavedCfg["timekprPlayTimeEnhancedActivityMonitorEnabled"] = False
def clearAdminForm(self):
"""Clear and default everything to default values"""
# ## clear form ##
# time limits
for rCtrl in (
"TimekprUserConfTodayInfoSpentTodayLB",
"TimekprUserConfTodayInfoSpentWeekLB",
"TimekprUserConfTodayInfoSpentMonthLB",
"TimekprUserConfTodayInfoLeftTodayLB",
"TimekprUserConfTodayInfoLeftContLB",
"TimekprUserConfTodayInfoInactiveLB"
):
self._timekprAdminFormBuilder.get_object(rCtrl).set_text(_NO_TIME_LIMIT_LABEL)
# check / radio boxes
for rCtrl in (
"TimekprUserConfTodaySettingsTrackInactiveCB",
"TimekprUserConfTodaySettingsHideTrayIconCB"
):
self._timekprAdminFormBuilder.get_object(rCtrl).set_active(False)
# days / labels
for rDay in self._timekprAdminFormBuilder.get_object("TimekprWeekDaysLS"):
# clear list store
rDay[2] = False
rDay[3] = 0
rDay[4] = _NO_TIME_LABEL
# clear day config
for rHour in range(0, 23+1):
self._tkSavedCfg["timeLimitDaysHoursActual"][rDay[0]][str(rHour)] = {cons.TK_CTRL_SMIN: 0, cons.TK_CTRL_EMIN: cons.TK_LIMIT_PER_MINUTE, cons.TK_CTRL_UACC: False}
# clear up the intervals
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsLS").clear()
# week / month limits
for rWkMon in self._timekprAdminFormBuilder.get_object("TimekprUserConfWkMonLimitsLS"):
# clear list store
rWkMon[2] = 0
rWkMon[3] = _NO_TIME_LIMIT_LABEL
# clear day config
self._tkSavedCfg["timeLimitWeek"] = 0
self._tkSavedCfg["timeLimitMonth"] = 0
# hide lockout intervals
self.controlSelectedLockoutTypeHourIntervals(None)
# reset lockout too
self._tkSavedCfg["timeLockoutType"] = cons.TK_CTRL_RES_T
self._tkSavedCfg["timeWakeInterval"] = "0;23"
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeSuspendWakeFromSB").set_value(0)
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeSuspendWakeToSB").set_value(23)
# set default lockout type
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeTerminate").set_active(True)
# ## PlayTIme reset ##
# reset times left
for rCtrl in (
"TimekprUserPlayTimeLeftActualLB",
"TimekprUserPlayTimeLeftSavedLB",
"TimekprUserPlayTimeSpentLB"
):
self._timekprAdminFormBuilder.get_object(rCtrl).set_text(_NO_TIME_LABEL)
# reset activity count
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeTodaySettingsActivityCntLB").set_text("---")
# reset day limits
for rDay in range(1, 7+1):
# clear list store
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLimitsLS")[rDay-1][2] = False
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLimitsLS")[rDay-1][3] = 0
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLimitsLS")[rDay-1][4] = _NO_TIME_LABEL_SHORT
# clear activities and add one placeholder
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesLS").clear()
# CB not checked
for rCtrl in (
"TimekprUserPlayTimeEnableCB",
"TimekprUserPlayTimeOverrideEnableCB",
"TimekprUserPlayTimeUnaccountedIntervalsEnabledCB"
):
self._timekprAdminFormBuilder.get_object(rCtrl).set_active(False)
# color
for rCtrl in (
"TimekprUserConfTodaySettingsSetAddBT",
"TimekprUserConfTodaySettingsSetSubractBT",
"TimekprUserConfTodaySettingsSetSetBT",
"TimekprUserConfTodayLabel",
"TimekprUserConfDaySettingsApplyBT",
"TimekprUserConfDaySettingsSetDaysIntervalsVerifyBT",
"TimekprUserConfDailyLabel",
"TimekprUserPlayTimeProcessesApplyBT",
"TimekprUserPlayTimeLabel",
"TimekprUserConfAddOptsApplyBT",
"TimekprUserConfAddOptsLabel"
):
# reset
self._timekprAdminFormBuilder.get_object(rCtrl).modify_fg(Gtk.StateFlags.NORMAL, None)
# failure
if self._timekprAdminConnector is not None:
# disable all
if not self._timekprAdminConnector.isConnected()[0]:
# reset
for rCtrl in (
"TimekprTrackingSessionsTreeView",
"TimekprTrackingSessionsButtonControlBX",
"TimekprExcludedSessionsTreeView",
"TimekprExcludedSessionsButtonControlBX",
"TimekprExcludedUsersTreeView",
"TimekprExcludedUsersButtonControlBX",
"TimekprPlayTimeEnableGlobalCB",
"TimekprPlayTimeEnhancedActivityMonitorCB"
):
# reset
self._timekprAdminFormBuilder.get_object(rCtrl).set_sensitive(False)
# reset
for rCtrl in (
"TimekprPlayTimeEnableGlobalCB",
"TimekprPlayTimeEnhancedActivityMonitorCB"
):
# reset
self._timekprAdminFormBuilder.get_object(rCtrl).set_active(False)
# reset
for rCtrl in (
"TimekprConfigurationLoglevelSB",
"TimekprConfigurationPollIntervalSB",
"TimekprConfigurationSaveTimeSB",
"TimekprConfigurationTerminationTimeSB",
"TimekprConfigurationWarningTimeSB",
"TimekprConfigurationFinalNotificationSB"
):
# reset
self._timekprAdminFormBuilder.get_object(rCtrl).set_sensitive(False)
self._timekprAdminFormBuilder.get_object(rCtrl).set_value(0)
# reset
for rCtrl in (
"TimekprTrackingSessionsLS",
"TimekprExcludedSessionsLS",
"TimekprExcludedUsersLS"
):
# reset
self._timekprAdminFormBuilder.get_object(rCtrl).clear()
# --------------- DEV test methods --------------- #
def initDEVDefaultConfig(self):
"""Initialize GUI elements for DEV mode"""
# DEV
if cons.TK_DEV_ACTIVE and 1 == 2:
# if there is date, no need to add one
if len(self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsLS")) == 0:
# standard time intervals
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsLS").append([0, "08:00", "13:00", "1", 0, 0, self._ROWCOL_NOK, self._ROWSTYLE_NOK])
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsLS").append([0, "15:00", "18:00", "1", 0, 0, self._ROWCOL_OK, self._ROWSTYLE_OK])
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsLS").append([0, "18:30", "22:00", "1", 0, 0, self._ROWCOL_OK, self._ROWSTYLE_OK])
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsLS").append([0, "22:30", "23:00", "1", 0, 0, self._ROWCOL_NOK, self._ROWSTYLE_NOK])
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsTreeView").set_sensitive(True)
# if there is date, no need to add one
if len(self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesLS")) == 0:
# PlayTime activities
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesLS").append(["1", "mask", "Doom Eternal"])
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesLS").append(["2", "mask.*", "The Talos Principle"])
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesLS").append(["3", "mask.*", "Mafia remastered"])
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesLS").append(["4", "csgo_linux", "CS: GO"])
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesLS").append(["5", "kca.*c", "Stupid calculator"])
# enable certain functionality
if 1 == 1:
# enable certain objects (fot testing)
for rO in (
"TimekprUserPlayTimeProcessesAdjustmentAddBT",
"TimekprUserPlayTimeProcessesAdjustmentRemoveBT",
"TimekprUserPlayTimeLimitsTreeView",
"TimekprUserPlayTimeProcessesTreeView"
):
self._timekprAdminFormBuilder.get_object(rO).set_sensitive(True)
# false
return False
# --------------- control / helper methods --------------- #
def getSelectedUserName(self):
"""Get selected username"""
# result
userName = None
# is admin app connected to server
if self._isConnected:
# get object
userCombobox = self._timekprAdminFormBuilder.get_object("TimekprUserSelectionCB")
# get chosen index, model and actual id of the item
userIdx = userCombobox.get_active()
userModel = userCombobox.get_model()
# only if we have selection
if userIdx is not None and userModel is not None:
# only if selected
if userIdx >= 0:
# get username
userName = userModel[userIdx][0]
# result
return userName
def toggleUserConfigControls(self, pEnable=True, pLeaveUserList=False):
"""Enable or disable all user controls for the form"""
# if disable
if not pEnable:
self.clearAdminForm()
# apply settings to all buttons in user configuration
for rObj in self._timekprUserConfigControlElements:
# if we need to leave user selection intact
if not (pLeaveUserList and rObj == "TimekprUserSelectionCB"):
# get the button and set availability
self._timekprAdminFormBuilder.get_object(rObj).set_sensitive(pEnable)
# DEV / samples too
self.initDEVDefaultConfig()
def toggleTimekprConfigControls(self, pEnable=True):
"""Enable or disable all timekpr controls for the form"""
# enable for timekpr main config can be done only in admin mode
enable = pEnable and (os.geteuid() == 0 or cons.TK_DEV_ACTIVE)
# apply settings to all buttons`in user configuration
for rButton in self._timekprConfigControlElements:
# get the button and set availability
self._timekprAdminFormBuilder.get_object(rButton).set_sensitive(enable)
def setTimekprStatus(self, pConnectionStatus, pStatus):
"""Change status of timekpr admin client"""
if pStatus is not None:
# connection
if pConnectionStatus:
# get main status
statusBar = self._timekprAdminFormBuilder.get_object("TimekprConnectionStatusbar")
else:
# get message status
statusBar = self._timekprAdminFormBuilder.get_object("TimekprMessagesStatusbar")
# get context
contextId = statusBar.get_context_id("status")
# pop existing message and add new one
statusBar.remove_all(contextId)
statusBar.push(contextId, pStatus[:80])
def normalizeAllowedDaysAndLimits(self):
"""Method will normalize allowed days and limits, in case user sets them differently"""
# get the least of size
limitLen = min(len(self._tkSavedCfg["timeLimitDays"]), len(self._tkSavedCfg["timeLimitDaysLimits"]))
# remove excess elements
for rElem in (
"timeLimitDays",
"timeLimitDaysLimits"
):
for i in range(limitLen, len(self._tkSavedCfg[rElem])):
self._tkSavedCfg[rElem].pop()
# get the least of size
limitLen = min(len(self._tkSavedCfg["playTimeLimitDays"]), len(self._tkSavedCfg["playTimeLimitDaysLimits"]))
# remove excess elements
for rElem in (
"playTimeLimitDays",
"playTimeLimitDaysLimits"
):
for i in range(limitLen, len(self._tkSavedCfg[rElem])):
self._tkSavedCfg[rElem].pop()
# --------------- format helper methods --------------- #
def formatTimeStr(self, pTotalSeconds, pFormatSecs=False, pFormatDays=False):
"""Format the time intervals as string label"""
# get time out of seconds
time = cons.TK_DATETIME_START + timedelta(seconds=pTotalSeconds)
# day format
isDayFmt = (pTotalSeconds >= cons.TK_LIMIT_PER_DAY and not pFormatDays)
# limit
limitDay = "%s:" % (str((time - cons.TK_DATETIME_START).days).rjust(2, "0")) if pFormatDays else ""
limitHr = "%s" % (str(24 if isDayFmt else time.hour).rjust(2, "0"))
limitMin = ":%s" % (str(0 if isDayFmt else time.minute).rjust(2, "0"))
limitSec = ":%s" % (str(0 if isDayFmt else time.second).rjust(2, "0")) if pFormatSecs else ""
limit = "%s%s%s%s" % (limitDay, limitHr, limitMin, limitSec)
# value
return limit
def getIntervalList(self, pDay):
"""Get intervals for use in GUI"""
# init hours for intervals
timeLimits = []
startTimeStr = None
endTimeStr = None
startSeconds = None
endSeconds = None
uaccValue = None
uaccChanged = False
# loop through all days
for rHour in range(0, 23+1):
# hour in str
hourStr = str(rHour)
# we process only hours that are available
if hourStr in self._tkSavedCfg["timeLimitDaysHoursActual"][pDay]:
# no value (interval was changed)
uaccValue = self._tkSavedCfg["timeLimitDaysHoursActual"][pDay][hourStr][cons.TK_CTRL_UACC] if uaccValue is None else uaccValue
# calc uacc changes
uaccChanged = self._tkSavedCfg["timeLimitDaysHoursActual"][pDay][hourStr][cons.TK_CTRL_UACC] != uaccValue
# if interval is complete and next hour is not available or there is not a continous interval (start != 0 or unaccounted changed, so it's different)
if startTimeStr is not None and endTimeStr is not None:
if (hourStr not in self._tkSavedCfg["timeLimitDaysHoursActual"][pDay]
or (hourStr in self._tkSavedCfg["timeLimitDaysHoursActual"][pDay]
and (self._tkSavedCfg["timeLimitDaysHoursActual"][pDay][hourStr][cons.TK_CTRL_SMIN] != 0
or uaccChanged is True))
):
# add new limit interval
timeLimits.append([startTimeStr, endTimeStr, startSeconds, endSeconds, uaccValue])
# erase values
startTimeStr = None
endTimeStr = None
uaccValue = None
uaccChanged = False
# we process only hours that are available
if hourStr in self._tkSavedCfg["timeLimitDaysHoursActual"][pDay]:
# uacc value
uaccValue = self._tkSavedCfg["timeLimitDaysHoursActual"][pDay][hourStr][cons.TK_CTRL_UACC]
# if start hour is not yet defined
if startTimeStr is None:
# first avaiable hour
startSeconds = rHour * cons.TK_LIMIT_PER_HOUR + self._tkSavedCfg["timeLimitDaysHoursActual"][pDay][hourStr][cons.TK_CTRL_SMIN] * cons.TK_LIMIT_PER_MINUTE
startTimeStr = self.formatTimeStr(startSeconds)
# define end hour
endDate = cons.TK_DATETIME_START + timedelta(hours=rHour, minutes=self._tkSavedCfg["timeLimitDaysHoursActual"][str(pDay)][hourStr][cons.TK_CTRL_EMIN])
endSeconds = int((endDate - cons.TK_DATETIME_START).total_seconds())
endTimeStr = self.formatTimeStr(endSeconds)
# if current interval changes (process end of interval) or this is it, no more hours
if self._tkSavedCfg["timeLimitDaysHoursActual"][pDay][hourStr][cons.TK_CTRL_EMIN] != cons.TK_LIMIT_PER_MINUTE or rHour == 23:
# add new limit interval
timeLimits.append([startTimeStr, endTimeStr, startSeconds, endSeconds, uaccValue])
# erase values
startTimeStr = None
endTimeStr = None
uaccValue = None
uaccChanged = False
# return intervals
return timeLimits
# --------------- field value helper methods --------------- #
def getSelectedDays(self):
"""Get selected day from day list"""
# get selected rows
for i in range(0, 2):
# get selected rows
(tm, paths) = self._timekprAdminFormBuilder.get_object("TimekprWeekDaysTreeView").get_selection().get_selected_rows()
# if nothing is selected, set first selected row (if there is nothing, no row is active)
sel = paths is not None
sel = len(paths) > 0 if sel else sel
# nothing selected
if not sel:
# set
self._timekprAdminFormBuilder.get_object("TimekprWeekDaysTreeView").set_cursor(0)
else:
break
# dict of id and nr of day
days = []
# only if there is smth selected
if paths is not None:
# idx
for path in paths:
# get iter and values
ti = tm.get_iter(path)
days.append({"idx": tm.get_path(ti)[0], "nr": str(tm.get_value(ti, 0))})
# return
return days
def getSelectedHourInterval(self):
"""Get selected hour interval from hour interval list"""
# refresh the child
for i in range(0, 2):
# get selection
(tm, ti) = self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsTreeView").get_selection().get_selected()
# if nothing is selected, get first selected row (if there is nothing, no row is active)
if ti is None:
# set
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsTreeView").set_cursor(0)
else:
break
# only if there is smth selected
if ti is not None:
# idx
intervalIdx = tm.get_path(ti)[0]
intervalDayNr = str(tm.get_value(ti, 3))
else:
# nothing
intervalIdx = None
intervalDayNr = None
# return
return intervalIdx, intervalDayNr
def getSelectedConfigElement(self, pElementName):
"""Get selected config element"""
# refresh the child
(tm, ti) = self._timekprAdminFormBuilder.get_object(pElementName).get_selection().get_selected()
# return
return tm.get_path(ti)[0] if ti is not None else None
def sortHourIntervals(self):
"""Sort hour intervals for ease of use"""
# sort vairables
hours = {}
rIdx = 0
# prepare sort
for rIt in self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsLS"):
hours[rIt[4]] = rIdx
# count further
rIdx += 1
# set sort order
sortedHours = []
# set up proper order
for rKey in sorted(hours):
# append to order
sortedHours.append(hours[rKey])
# reorder rows in liststore
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsLS").reorder(sortedHours)
# --------------- additional configuration methods --------------- #
def getSelectedLockoutType(self):
"""Get selected restriction / lockout type"""
# get lockout type
lockoutType = None
lockoutType = cons.TK_CTRL_RES_T if lockoutType is None and self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeTerminate").get_active() else lockoutType
lockoutType = cons.TK_CTRL_RES_K if lockoutType is None and self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeKill").get_active() else lockoutType
lockoutType = cons.TK_CTRL_RES_D if lockoutType is None and self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeShutdown").get_active() else lockoutType
lockoutType = cons.TK_CTRL_RES_S if lockoutType is None and self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeSuspend").get_active() else lockoutType
lockoutType = cons.TK_CTRL_RES_W if lockoutType is None and self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeSuspendWake").get_active() else lockoutType
lockoutType = cons.TK_CTRL_RES_L if lockoutType is None and self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeLock").get_active() else lockoutType
# result
return lockoutType
def setSelectedLockoutType(self, pLockoutType):
"""Get selected restriction / lockout type"""
# set lockout type
if pLockoutType == cons.TK_CTRL_RES_T:
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeTerminate").set_active(True)
elif pLockoutType == cons.TK_CTRL_RES_K:
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeKill").set_active(True)
elif pLockoutType == cons.TK_CTRL_RES_D:
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeShutdown").set_active(True)
elif pLockoutType == cons.TK_CTRL_RES_S:
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeSuspend").set_active(True)
elif pLockoutType == cons.TK_CTRL_RES_W:
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeSuspendWake").set_active(True)
elif pLockoutType == cons.TK_CTRL_RES_L:
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeLock").set_active(True)
def controlSelectedLockoutTypeHourIntervals(self, pInterval):
"""Set selected hour intervals"""
# if no interval, just hide them
if pInterval is not None:
# get split interval
hrInterval = pInterval.split(";")
# set values
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeSuspendWakeFromSB").set_value(int(hrInterval[0]))
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeSuspendWakeToSB").set_value(int(hrInterval[1]))
# set hours visible only when suspendwake
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeWakeupIntervalsLabel").set_visible(pInterval is not None)
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeSuspendWakeFromSB").set_visible(pInterval is not None)
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeSuspendWakeToSB").set_visible(pInterval is not None)
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeSuspendWakeFromSB").set_sensitive(pInterval is not None)
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeSuspendWakeToSB").set_sensitive(pInterval is not None)
def enableTimeControlToday(self, pEnable=True):
"""Enable buttons to add time and PlayTime today"""
for rCtrl in (
"TimekprUserConfTodaySettingsSetHrSB",
"TimekprUserConfTodaySettingsSetMinSB",
"TimekprUserConfTodaySettingsChoiceBX"
):
self._timekprAdminFormBuilder.get_object(rCtrl).set_sensitive(pEnable)
# --------------- info retrieval methods --------------- #
def getAdminUserList(self):
"""Get user list via dbus"""
# store
userStore = self._timekprAdminFormBuilder.get_object("TimekprUserSelectionLS")
# clear up
userStore.clear()
userStore.append(["", ""])
# def len
widthInChars = 15
# get list
result, message, userList = self._timekprAdminConnector.getUserList()
# all ok
if result == 0:
# loop and print
for rUser in userList:
# name
userName = "%s (%s)" % (rUser[0], rUser[1]) if (rUser[1] is not None and rUser[1] != "") else rUser[0]
# determine maxlen
widthInChars = max(widthInChars, len(userName) - 3)
# add user
userStore.append([rUser[0], userName])
# status
self.setTimekprStatus(False, "User list retrieved")
# enable
self._timekprAdminFormBuilder.get_object("TimekprUserSelectionCB").set_sensitive(True)
self._timekprAdminFormBuilder.get_object("TimekprUserSelectionRefreshBT").set_sensitive(self._timekprAdminFormBuilder.get_object("TimekprUserSelectionCB").get_sensitive())
# adjust widht
self._timekprAdminFormBuilder.get_object("TimekprUserSelectionCBEntry").set_width_chars(widthInChars)
# init first selection
self._timekprAdminFormBuilder.get_object("TimekprUserSelectionCB").set_active(0)
else:
# status
self.setTimekprStatus(False, message)
# check the connection
self.checkConnection()
def retrieveTimekprConfig(self):
"""Retrieve timekpr configuration"""
# init
timekprConfig = {}
result = 0
message = ""
# get list
result, message, timekprConfig = self._timekprAdminConnector.getTimekprConfiguration()
# all ok
if result == 0:
# loop and print
for rKey, rValue in timekprConfig.items():
# check all by keys
if rKey == "TIMEKPR_LOGLEVEL":
# log level
self._tkSavedCfg["timekprLogLevel"] = int(rValue)
elif rKey == "TIMEKPR_POLLTIME":
# poll time
self._tkSavedCfg["timekprPollingInterval"] = int(rValue)
elif rKey == "TIMEKPR_SAVE_TIME":
# save time
self._tkSavedCfg["timekprSaveTime"] = int(rValue)
elif rKey == "TIMEKPR_TERMINATION_TIME":
# termination time
self._tkSavedCfg["timekprTerminationTime"] = int(rValue)
elif rKey == "TIMEKPR_FINAL_WARNING_TIME":
# final warning time
self._tkSavedCfg["timekprWarningTime"] = int(rValue)
elif rKey == "TIMEKPR_FINAL_NOTIFICATION_TIME":
# final notification time
self._tkSavedCfg["timekprNotificationTime"] = int(rValue)
elif rKey == "TIMEKPR_SESSION_TYPES_CTRL":
# init
self._tkSavedCfg["timekprTrackingSessions"] = []
# loop through available session types
for rSessionType in rValue:
# add config
self._tkSavedCfg["timekprTrackingSessions"].append(str(rSessionType))
elif rKey == "TIMEKPR_SESSION_TYPES_EXCL":
# init
self._tkSavedCfg["timekprExcludedSessions"] = []
# loop through available session types
for rSessionType in rValue:
# add config
self._tkSavedCfg["timekprExcludedSessions"].append(str(rSessionType))
elif rKey == "TIMEKPR_USERS_EXCL":
# init
self._tkSavedCfg["timekprExcludedUsers"] = []
# loop through available users
for rUser in rValue:
# add config
self._tkSavedCfg["timekprExcludedUsers"].append(str(rUser))
elif rKey == "TIMEKPR_PLAYTIME_ENABLED":
# PlayTime enabled
self._tkSavedCfg["timekprPlayTimeEnabled"] = bool(rValue)
elif rKey == "TIMEKPR_PLAYTIME_ENHANCED_ACTIVITY_MONITOR_ENABLED":
# PlayTime enhanced activity monitor enabled
self._tkSavedCfg["timekprPlayTimeEnhancedActivityMonitorEnabled"] = bool(rValue)
# apply config
self.applyTimekprConfig()
# determine control state
self.calculateTimekprConfigControlAvailability()
# status
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_CONFIG_RETRIEVED"))
else:
# disable all but choser
self.toggleUserConfigControls(False, True)
# status
self.setTimekprStatus(False, message)
# check the connection
self.checkConnection()
def retrieveUserInfoAndConfig(self, pUserName, pInfoLvl):
"""Retrieve user configuration"""
# clear before user
if pInfoLvl == cons.TK_CL_INF_FULL:
# reset form
self.clearAdminForm()
# no username passed, we try to find one
userName = self.getSelectedUserName() if pUserName is None else pUserName
# if nothing is passed, nothing is done
if userName is not None and userName != "":
# init
userConfig = {}
# get list
result, message, userConfig = self._timekprAdminConnector.getUserConfigurationAndInformation(userName, pInfoLvl)
# all ok
if result == 0:
# reset if full info or realtime requested
if pInfoLvl in (cons.TK_CL_INF_FULL, cons.TK_CL_INF_RT):
# reset optional information labels
for rCtrl in (
"TimekprUserConfTodayInfoLeftContLB",
"TimekprUserConfTodayInfoInactiveLB"
):
self._timekprAdminFormBuilder.get_object(rCtrl).set_text(_NO_TIME_LIMIT_LABEL)
# reset optional information labels for PlayTime
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLeftActualLB").set_text(_NO_TIME_LABEL)
# reset activity count
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeTodaySettingsActivityCntLB").set_text("---")
# loop and print
for rKey, rValue in userConfig.items():
# these ar saved values, refresh of saved or full is asked
if pInfoLvl in (cons.TK_CL_INF_FULL, cons.TK_CL_INF_SAVED):
# this info is refreshed regularly (based on config keys)
if rKey == "TIME_SPENT_DAY":
# spent
timeSpent = cons.TK_DATETIME_START + timedelta(seconds=abs(rValue))
timeSpentStr = "%s:%s:%s:%s" % (str((timeSpent - cons.TK_DATETIME_START).days).rjust(2, "0"), str(timeSpent.hour).rjust(2, "0") , str(timeSpent.minute).rjust(2, "0"), str(timeSpent.second).rjust(2, "0"))
self._timekprAdminFormBuilder.get_object("TimekprUserConfTodayInfoSpentTodayLB").set_text(timeSpentStr)
elif rKey == "TIME_SPENT_WEEK":
# spent week
timeSpentWeek = cons.TK_DATETIME_START + timedelta(seconds=rValue)
timeSpentWeekStr = "%s:%s:%s:%s" % (str((timeSpentWeek - cons.TK_DATETIME_START).days).rjust(2, "0"), str(timeSpentWeek.hour).rjust(2, "0"), str(timeSpentWeek.minute).rjust(2, "0"), str(timeSpentWeek.second).rjust(2, "0"))
self._timekprAdminFormBuilder.get_object("TimekprUserConfTodayInfoSpentWeekLB").set_text(timeSpentWeekStr)
elif rKey == "TIME_SPENT_MONTH":
# spent month
timeSpentMonth = cons.TK_DATETIME_START + timedelta(seconds=rValue)
timeSpentMonthStr = "%s:%s:%s:%s" % (str((timeSpentMonth - cons.TK_DATETIME_START).days).rjust(2, "0"), str(timeSpentMonth.hour).rjust(2, "0"), str(timeSpentMonth.minute).rjust(2, "0"), str(timeSpentMonth.second).rjust(2, "0"))
self._timekprAdminFormBuilder.get_object("TimekprUserConfTodayInfoSpentMonthLB").set_text(timeSpentMonthStr)
# show balance
elif rKey == "TIME_LEFT_DAY":
# balance
timeLeft = cons.TK_DATETIME_START + timedelta(seconds=rValue)
timeLeftStr = "%s:%s:%s:%s" % (str((timeLeft - cons.TK_DATETIME_START).days).rjust(2, "0"), str(timeLeft.hour).rjust(2, "0"), str(timeLeft.minute).rjust(2, "0"), str(timeLeft.second).rjust(2, "0"))
self._timekprAdminFormBuilder.get_object("TimekprUserConfTodayInfoLeftTodayLB").set_text(timeLeftStr)
# show saved PlayTime left
elif rKey == "PLAYTIME_LEFT_DAY":
# PlayTime left
timeLeft = cons.TK_DATETIME_START + timedelta(seconds=rValue)
timeLeftStr = "%s:%s:%s" % (str(timeLeft.hour).rjust(2, "0") , str(timeLeft.minute).rjust(2, "0"), str(timeLeft.second).rjust(2, "0"))
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLeftSavedLB").set_text(timeLeftStr)
# show actual PlayTime left
elif rKey == "PLAYTIME_SPENT_DAY":
# PlayTime left
timeLeft = cons.TK_DATETIME_START + timedelta(seconds=rValue)
timeLeftStr = "%s:%s:%s" % (str(timeLeft.hour).rjust(2, "0") , str(timeLeft.minute).rjust(2, "0"), str(timeLeft.second).rjust(2, "0"))
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeSpentLB").set_text(timeLeftStr)
# refresh only if full or realtime asked
if pInfoLvl in (cons.TK_CL_INF_FULL, cons.TK_CL_INF_RT):
# show actual time left for continous use
if rKey == "ACTUAL_TIME_LEFT_CONTINUOUS":
# total left
timeLeft = cons.TK_DATETIME_START + timedelta(seconds=rValue)
timeLeftStr = "%s:%s:%s:%s" % (str((timeLeft - cons.TK_DATETIME_START).days).rjust(2, "0"), str(timeLeft.hour).rjust(2, "0"), str(timeLeft.minute).rjust(2, "0"), str(timeLeft.second).rjust(2, "0"))
self._timekprAdminFormBuilder.get_object("TimekprUserConfTodayInfoLeftContLB").set_text(timeLeftStr)
# show actual time inactive
elif rKey == "ACTUAL_TIME_INACTIVE_SESSION":
# total left
timeLeft = cons.TK_DATETIME_START + timedelta(seconds=rValue)
timeLeftStr = "%s:%s:%s:%s" % (str((timeLeft - cons.TK_DATETIME_START).days).rjust(2, "0"), str(timeLeft.hour).rjust(2, "0"), str(timeLeft.minute).rjust(2, "0"), str(timeLeft.second).rjust(2, "0"))
self._timekprAdminFormBuilder.get_object("TimekprUserConfTodayInfoInactiveLB").set_text(timeLeftStr)
# show actual PlayTime left
elif rKey == "ACTUAL_PLAYTIME_LEFT_DAY":
# PlayTime left
timeLeft = cons.TK_DATETIME_START + timedelta(seconds=rValue)
timeLeftStr = "%s:%s:%s" % (str(timeLeft.hour).rjust(2, "0") , str(timeLeft.minute).rjust(2, "0"), str(timeLeft.second).rjust(2, "0"))
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLeftActualLB").set_text(timeLeftStr)
# show actual PlayTime count
elif rKey == "ACTUAL_ACTIVE_PLAYTIME_ACTIVITY_COUNT":
# PlayTime count
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeTodaySettingsActivityCntLB").set_text(str(rValue))
# info is needed when full refresh requested
if pInfoLvl == cons.TK_CL_INF_FULL:
if rKey == "TRACK_INACTIVE":
# track inactive
self._tkSavedCfg["timeTrackInactive"] = bool(rValue)
elif rKey == "HIDE_TRAY_ICON":
# hide icon and notif
self._tkSavedCfg["timeHideTrayIcon"] = bool(rValue)
elif rKey == "LOCKOUT_TYPE":
# set lockout type
self._tkSavedCfg["timeLockoutType"] = rValue
elif rKey == "WAKEUP_HOUR_INTERVAL":
# set interval values
self._tkSavedCfg["timeWakeInterval"] = rValue
elif rKey == "ALLOWED_WEEKDAYS":
# empty the values
self._tkSavedCfg["timeLimitDays"] = []
# allowed weekdays
for rDay in rValue:
# set values
self._tkSavedCfg["timeLimitDays"].append(str(rDay))
elif rKey == "LIMITS_PER_WEEKDAYS":
# limits per allowed weekdays
self._tkSavedCfg["timeLimitDaysLimits"] = []
# allowed weekdays
for rDay in range(0, len(rValue)):
# add the value
self._tkSavedCfg["timeLimitDaysLimits"].append(int(rValue[rDay]))
elif rKey == "LIMIT_PER_WEEK":
# value
self._tkSavedCfg["timeLimitWeek"] = int(rValue)
elif rKey == "LIMIT_PER_MONTH":
# value
self._tkSavedCfg["timeLimitMonth"] = int(rValue)
elif "ALLOWED_HOURS_" in rKey:
# determine the day
day = rKey[-1:]
self._tkSavedCfg["timeLimitDaysHoursActual"][day] = {}
# loop through available hours
for rHour, rHourMinutes in rValue.items():
# add config
self._tkSavedCfg["timeLimitDaysHoursActual"][day][str(rHour)] = {cons.TK_CTRL_SMIN: int(rHourMinutes[cons.TK_CTRL_SMIN]), cons.TK_CTRL_EMIN: int(rHourMinutes[cons.TK_CTRL_EMIN]), cons.TK_CTRL_UACC: bool(rHourMinutes[cons.TK_CTRL_UACC])}
# set up saved config as well
self._tkSavedCfg["timeLimitDaysHoursSaved"][day] = self._tkSavedCfg["timeLimitDaysHoursActual"][day].copy()
# ## PlayTime config ##
elif rKey == "PLAYTIME_ENABLED":
# PlayTime enabled
self._tkSavedCfg["playTimeEnabled"] = bool(rValue)
elif rKey == "PLAYTIME_LIMIT_OVERRIDE_ENABLED":
# PlayTime override enabled
self._tkSavedCfg["playTimeOverrideEnabled"] = bool(rValue)
elif rKey == "PLAYTIME_UNACCOUNTED_INTERVALS_ENABLED":
# PlayTime allowed during unaccounted intervals
self._tkSavedCfg["playTimeUnaccountedIntervalsEnabled"] = bool(rValue)
elif rKey == "PLAYTIME_ALLOWED_WEEKDAYS":
# empty the values
self._tkSavedCfg["playTimeLimitDays"] = []
# allowed weekdays
for rDay in rValue:
# set values
self._tkSavedCfg["playTimeLimitDays"].append(str(rDay))
elif rKey == "PLAYTIME_LIMITS_PER_WEEKDAYS":
# limits per allowed weekdays
self._tkSavedCfg["playTimeLimitDaysLimits"] = []
# allowed weekdays
for rDay in range(0, len(rValue)):
# add the value
self._tkSavedCfg["playTimeLimitDaysLimits"].append(int(rValue[rDay]))
elif rKey == "PLAYTIME_ACTIVITIES":
# PlayTime activity list
self._tkSavedCfg["playTimeActivities"] = []
# allowed weekdays
for rDay in range(0, len(rValue)):
# add the value
self._tkSavedCfg["playTimeActivities"].append([rValue[rDay][0], rValue[rDay][1]])
# clean up limits if full refresh requested
if pInfoLvl == cons.TK_CL_INF_FULL:
self.normalizeAllowedDaysAndLimits()
# if PT override is enabled, we do not show time information for PT
if self._tkSavedCfg["playTimeOverrideEnabled"]:
# disable time show
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLeftSavedLB").set_text(_NO_TIME_LABEL)
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLeftActualLB").set_text(_NO_TIME_LABEL)
# config was updated only when full
if pInfoLvl == cons.TK_CL_INF_FULL:
# status
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_USER_CONFIG_RETRIEVED"))
# apply config
self.applyUserConfig()
# determine control state
self.calculateUserConfigControlAvailability()
self.calculateUserPlayTimeConfigControlAvailability()
self.calculateUserAdditionalConfigControlAvailability()
# enable adding hours as well
self.enableTimeControlToday()
else:
# disable all but choser
self.toggleUserConfigControls(False, True)
# status
self.setTimekprStatus(False, message)
# check the connection
self.checkConnection()
# return
return True
# --------------- retrieved configuration apply methods --------------- #
def applyTimekprConfig(self):
"""Apply user configuration after getting it from server"""
# ## log level ##
self._timekprAdminFormBuilder.get_object("TimekprConfigurationLoglevelSB").set_value(self._tkSavedCfg["timekprLogLevel"])
self._timekprAdminFormBuilder.get_object("TimekprConfigurationLoglevelSB").set_sensitive(True)
# ## poll time ##
self._timekprAdminFormBuilder.get_object("TimekprConfigurationPollIntervalSB").set_value(self._tkSavedCfg["timekprPollingInterval"])
self._timekprAdminFormBuilder.get_object("TimekprConfigurationPollIntervalSB").set_sensitive(True)
# ## save time ##
self._timekprAdminFormBuilder.get_object("TimekprConfigurationSaveTimeSB").set_value(self._tkSavedCfg["timekprSaveTime"])
self._timekprAdminFormBuilder.get_object("TimekprConfigurationSaveTimeSB").set_sensitive(True)
# ## termination time ##
self._timekprAdminFormBuilder.get_object("TimekprConfigurationTerminationTimeSB").set_value(self._tkSavedCfg["timekprTerminationTime"])
self._timekprAdminFormBuilder.get_object("TimekprConfigurationTerminationTimeSB").set_sensitive(True)
# ## final warning time ##
self._timekprAdminFormBuilder.get_object("TimekprConfigurationWarningTimeSB").set_value(self._tkSavedCfg["timekprWarningTime"])
self._timekprAdminFormBuilder.get_object("TimekprConfigurationWarningTimeSB").set_sensitive(True)
# ## final notification time ##
self._timekprAdminFormBuilder.get_object("TimekprConfigurationFinalNotificationSB").set_value(self._tkSavedCfg["timekprNotificationTime"])
self._timekprAdminFormBuilder.get_object("TimekprConfigurationFinalNotificationSB").set_sensitive(True)
# ## tracking session types ###
self._timekprAdminFormBuilder.get_object("TimekprTrackingSessionsLS").clear()
for rSessionType in self._tkSavedCfg["timekprTrackingSessions"]:
# add config
self._timekprAdminFormBuilder.get_object("TimekprTrackingSessionsLS").append([str(rSessionType)])
# enable editing
for rObj in (
"TimekprTrackingSessionsTreeView",
"TimekprTrackingSessionsButtonControlBX"
):
self._timekprAdminFormBuilder.get_object(rObj).set_sensitive(True)
# select first row
if len(self._tkSavedCfg["timekprTrackingSessions"]) > 0:
self._timekprAdminFormBuilder.get_object("TimekprTrackingSessionsTreeView").set_cursor(0)
self._timekprAdminFormBuilder.get_object("TimekprTrackingSessionsTreeView").scroll_to_cell(0)
# ## exclusion session types ##
self._timekprAdminFormBuilder.get_object("TimekprExcludedSessionsLS").clear()
for rSessionType in self._tkSavedCfg["timekprExcludedSessions"]:
# add config
self._timekprAdminFormBuilder.get_object("TimekprExcludedSessionsLS").append([str(rSessionType)])
# enable editing
for rObj in (
"TimekprExcludedSessionsTreeView",
"TimekprExcludedSessionsButtonControlBX"
):
self._timekprAdminFormBuilder.get_object(rObj).set_sensitive(True)
# select first row
if len(self._tkSavedCfg["timekprExcludedSessions"]) > 0:
self._timekprAdminFormBuilder.get_object("TimekprExcludedSessionsTreeView").set_cursor(0)
self._timekprAdminFormBuilder.get_object("TimekprExcludedSessionsTreeView").scroll_to_cell(0)
# ## excluded users ##
self._timekprAdminFormBuilder.get_object("TimekprExcludedUsersLS").clear()
for rUser in self._tkSavedCfg["timekprExcludedUsers"]:
# add config
self._timekprAdminFormBuilder.get_object("TimekprExcludedUsersLS").append([str(rUser)])
# enable editing
for rObj in (
"TimekprExcludedUsersTreeView",
"TimekprExcludedUsersButtonControlBX"
):
self._timekprAdminFormBuilder.get_object(rObj).set_sensitive(True)
# select first row
if len(self._tkSavedCfg["timekprExcludedUsers"]) > 0:
self._timekprAdminFormBuilder.get_object("TimekprExcludedUsersTreeView").set_cursor(0)
self._timekprAdminFormBuilder.get_object("TimekprExcludedUsersTreeView").scroll_to_cell(0)
# ## PlayTime ##
# global enabled switch
self._timekprAdminFormBuilder.get_object("TimekprPlayTimeEnableGlobalCB").set_active(self._tkSavedCfg["timekprPlayTimeEnabled"])
self._timekprAdminFormBuilder.get_object("TimekprPlayTimeEnableGlobalCB").set_sensitive(True)
# global enhanced activity monitor
self._timekprAdminFormBuilder.get_object("TimekprPlayTimeEnhancedActivityMonitorCB").set_active(self._tkSavedCfg["timekprPlayTimeEnhancedActivityMonitorEnabled"])
self._timekprAdminFormBuilder.get_object("TimekprPlayTimeEnhancedActivityMonitorCB").set_sensitive(True)
# enable / disable controls
self.toggleTimekprConfigControls(True)
def applyUserConfig(self):
"""Apply user configuration after getting it from server"""
# enable refresh
self._timekprAdminFormBuilder.get_object("TimekprUserSelectionRefreshBT").set_sensitive(True)
# ## allowed days ###
for rDay in range(1, 7+1):
# enable certain days
self._timekprAdminFormBuilder.get_object("TimekprWeekDaysLS")[rDay-1][2] = (str(rDay) in self._tkSavedCfg["timeLimitDays"])
# enable editing
for rCtrl in (
"TimekprWeekDaysTreeView",
"TimekprHourIntervalsTreeView",
"TimekprUserConfDaySettingsSetDaysHeaderControlBX"
):
self._timekprAdminFormBuilder.get_object(rCtrl).set_sensitive(True)
# ## limits per allowed days ###
dayLimitIdx = -1
# loop through all days
for rDay in cons.TK_ALLOWED_WEEKDAYS.split(";"):
# day index
dayIdx = int(rDay) - 1
# check whether this day is enabled
if rDay in self._tkSavedCfg["timeLimitDays"]:
# advance index
dayLimitIdx += 1
else:
continue
# calculate time
limit = self.formatTimeStr(self._tkSavedCfg["timeLimitDaysLimits"][dayLimitIdx], True)
# enable certain days
self._timekprAdminFormBuilder.get_object("TimekprWeekDaysLS")[dayIdx][3] = self._tkSavedCfg["timeLimitDaysLimits"][dayLimitIdx]
# set appropriate label as well
self._timekprAdminFormBuilder.get_object("TimekprWeekDaysLS")[dayIdx][4] = limit if rDay in self._tkSavedCfg["timeLimitDays"] else _NO_TIME_LABEL
# ## hour intervals ##
# intervals themselves will be adjusted depending on selected day, we just enable them here
for rCtrl in (
"TimekprHourIntervalsTreeView",
"TimekprUserConfDaySettingsSetDaysIntervalsControlBX"
):
self._timekprAdminFormBuilder.get_object(rCtrl).set_sensitive(True)
# select first row (if there are intervals)
if dayLimitIdx > -1:
self._timekprAdminFormBuilder.get_object("TimekprUserConfWkMonLimitsTreeView").set_cursor(0)
self._timekprAdminFormBuilder.get_object("TimekprUserConfWkMonLimitsTreeView").scroll_to_cell(0)
# ## limit per week / month ##
for rWkDay in self._timekprAdminFormBuilder.get_object("TimekprUserConfWkMonLimitsLS"):
# week
if rWkDay[0] == "WK":
limit = self._tkSavedCfg["timeLimitWeek"]
elif rWkDay[0] == "MON":
limit = self._tkSavedCfg["timeLimitMonth"]
rWkDay[2] = limit
rWkDay[3] = self.formatTimeStr(limit, True, True)
# enable editing
for rCtrl in (
"TimekprUserConfWkMonLimitsTreeView",
"TimekprUserConfWkMonLimitsAdjustmentsBX",
"TimekprUserConfWkMonLimitsAdjustmentControlButtonsBX"
):
self._timekprAdminFormBuilder.get_object(rCtrl).set_sensitive(True)
# current day
currDay = datetime.now().isoweekday()-1
# determine curent day and point to it
self._timekprAdminFormBuilder.get_object("TimekprWeekDaysTreeView").set_cursor(currDay)
self._timekprAdminFormBuilder.get_object("TimekprWeekDaysTreeView").scroll_to_cell(currDay)
self._timekprAdminFormBuilder.get_object("TimekprWeekDaysTreeView").get_selection().emit("changed")
# ## PlayTime config ##
# PlayTime and PlayTime options enablement
for rCtrl in (
("TimekprUserPlayTimeEnableCB", "playTimeEnabled"),
("TimekprUserPlayTimeOverrideEnableCB", "playTimeOverrideEnabled"),
("TimekprUserPlayTimeUnaccountedIntervalsEnabledCB", "playTimeUnaccountedIntervalsEnabled")
):
# set value
self._timekprAdminFormBuilder.get_object(rCtrl[0]).set_active(self._tkSavedCfg[rCtrl[1]])
# enable field & set button
self._timekprAdminFormBuilder.get_object(rCtrl[0]).set_sensitive(True)
# ## PlayTime limits per allowed days ###
# loop through all days
for rDay in cons.TK_ALLOWED_WEEKDAYS.split(";"):
# day index
dayIdx = int(rDay) - 1
# check whether this day is enabled
if rDay in self._tkSavedCfg["playTimeLimitDays"]:
# advance index
dayLimitIdx = self._tkSavedCfg["playTimeLimitDays"].index(rDay)
else:
# day not enabled
dayLimitIdx = None
# calculate time
limit = self.formatTimeStr(self._tkSavedCfg["playTimeLimitDaysLimits"][dayLimitIdx], True) if dayLimitIdx is not None else _NO_TIME_LABEL
# enable certain days
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLimitsLS")[dayIdx][2] = dayLimitIdx is not None
# enable time limit
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLimitsLS")[dayIdx][3] = self._tkSavedCfg["playTimeLimitDaysLimits"][dayLimitIdx] if dayLimitIdx is not None else 0
# set appropriate label as well
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLimitsLS")[dayIdx][4] = limit
# determine curent day and point to it
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLimitsTreeView").set_cursor(currDay)
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLimitsTreeView").scroll_to_cell(currDay)
# enable PlayTime editing
for rCtrl in (
"TimekprUserPlayTimeLimitsTreeView",
"TimekprUserPlayTimeLimitsHeaderControlBX"
):
self._timekprAdminFormBuilder.get_object(rCtrl).set_sensitive(True)
# ## PlayTime activities ###
activityIdx = -1
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesLS").clear()
# check whether this day is enabled
for rAct in self._tkSavedCfg["playTimeActivities"]:
# advance index
activityIdx += 1
# enable certain days
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesLS").append([str(activityIdx), rAct[0], rAct[1]])
# enable PlayTime editing
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesTreeView").set_sensitive(True)
# if there are activities
if activityIdx > -1:
# select first row
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesTreeView").set_cursor(0)
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesTreeView").scroll_to_cell(0)
# set enablement for PlayTime controls
for rCtrl in (
"TimekprUserPlayTimeProcessesAdjustmentAddBT",
"TimekprUserPlayTimeProcessesAdjustmentRemoveBT",
"TimekprUserPlayTimeLimitsHeaderControlBX"
):
# enable field & set button
self._timekprAdminFormBuilder.get_object(rCtrl).set_sensitive(True)
# ## additional config ##
# set values for track inactive and disable notifications
for rCtrl in (
"TimekprUserConfTodaySettingsTrackInactiveCB",
"TimekprUserConfTodaySettingsHideTrayIconCB"
):
# set value
self._timekprAdminFormBuilder.get_object(rCtrl).set_active(self._tkSavedCfg["timeTrackInactive"] if rCtrl == "TimekprUserConfTodaySettingsTrackInactiveCB" else self._tkSavedCfg["timeHideTrayIcon"])
# enable field & set button
self._timekprAdminFormBuilder.get_object(rCtrl).set_sensitive(True)
# lockout type and intervals
# set option
self.setSelectedLockoutType(self._tkSavedCfg["timeLockoutType"])
# set option
self.controlSelectedLockoutTypeHourIntervals(self._tkSavedCfg["timeWakeInterval"])
# enable editing
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeChoiceBoxBX").set_sensitive(True)
# --------------- change detection and GUI action control methods --------------- #
def calculateTimekprConfigControlAvailability(self, pApplyControls=True):
"""Calculate main control availability"""
# this duplicates diff control as well
changeControl = {}
# perform?
if not self._timekprAdminFormBuilder.get_object("TimekprTrackingSessionsTreeView").get_sensitive():
return changeControl
# ## log level ##
control = "TimekprConfigurationLoglevelSB"
value = self._timekprAdminFormBuilder.get_object(control).get_value_as_int()
changeControl[control] = {"st": value != self._tkSavedCfg["timekprLogLevel"], "val": value}
# ## poll time ##
control = "TimekprConfigurationPollIntervalSB"
value = self._timekprAdminFormBuilder.get_object(control).get_value_as_int()
changeControl[control] = {"st": value != self._tkSavedCfg["timekprPollingInterval"], "val": value}
# ## save time ##
control = "TimekprConfigurationSaveTimeSB"
value = self._timekprAdminFormBuilder.get_object(control).get_value_as_int()
changeControl[control] = {"st": value != self._tkSavedCfg["timekprSaveTime"], "val": value}
# ## termination time ##
control = "TimekprConfigurationTerminationTimeSB"
value = self._timekprAdminFormBuilder.get_object(control).get_value_as_int()
changeControl[control] = {"st": value != self._tkSavedCfg["timekprTerminationTime"], "val": value}
# ## final warning time ##
control = "TimekprConfigurationWarningTimeSB"
value = self._timekprAdminFormBuilder.get_object(control).get_value_as_int()
changeControl[control] = {"st": value != self._tkSavedCfg["timekprWarningTime"], "val": value}
# ## final notification ##
control = "TimekprConfigurationFinalNotificationSB"
value = self._timekprAdminFormBuilder.get_object(control).get_value_as_int()
changeControl[control] = {"st": value != self._tkSavedCfg["timekprNotificationTime"], "val": value}
# ## tracking session types ###
tmpArray = [str(rIt[0]) for rIt in self._timekprAdminFormBuilder.get_object("TimekprTrackingSessionsLS") if rIt[0] != ""]
control = "TimekprTrackingSessionsLS"
changeControl[control] = {"st": tmpArray != self._tkSavedCfg["timekprTrackingSessions"], "val": tmpArray.copy()}
# ## exclusion session types ##
tmpArray = [str(rIt[0]) for rIt in self._timekprAdminFormBuilder.get_object("TimekprExcludedSessionsLS") if rIt[0] != ""]
control = "TimekprExcludedSessionsLS"
changeControl[control] = {"st": tmpArray != self._tkSavedCfg["timekprExcludedSessions"], "val": tmpArray.copy()}
# ## excluded users ##
tmpArray = [str(rIt[0]) for rIt in self._timekprAdminFormBuilder.get_object("TimekprExcludedUsersLS") if rIt[0] != ""]
control = "TimekprExcludedUsersLS"
changeControl[control] = {"st": tmpArray != self._tkSavedCfg["timekprExcludedUsers"], "val": tmpArray.copy()}
# ## global PlayTime switch ##
control = "TimekprPlayTimeEnableGlobalCB"
value = self._timekprAdminFormBuilder.get_object(control).get_active()
changeControl[control] = {"st": value != self._tkSavedCfg["timekprPlayTimeEnabled"], "val": value}
# ## global PlayTime switch ##
control = "TimekprPlayTimeEnhancedActivityMonitorCB"
value = self._timekprAdminFormBuilder.get_object(control).get_active()
changeControl[control] = {"st": value != self._tkSavedCfg["timekprPlayTimeEnhancedActivityMonitorEnabled"], "val": value}
# if at least one is changed
enable = False
if pApplyControls:
for rKey, rVal in changeControl.items():
# one thing changed
if rVal["st"]:
# enable
enable = rVal["st"]
# no need to search further
break
# enabled or not
self._timekprAdminFormBuilder.get_object("TimekprConfigurationApplyBT").set_sensitive(enable)
# color the buttons for ppl to see them better
self._timekprAdminFormBuilder.get_object("TimekprConfigurationApplyBT").modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("red") if enable else None)
# tab color
self._timekprAdminFormBuilder.get_object("TimekprConfigurationTabLabel").modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("red") if enable else None)
# return
return changeControl
def calculateUserTodayControlAvailability(self):
"""Calculate user today config control availability"""
# ## add time today ##
enabled = (self._timekprAdminFormBuilder.get_object("TimekprUserConfTodaySettingsSetHrSB").get_value_as_int() != 0 or self._timekprAdminFormBuilder.get_object("TimekprUserConfTodaySettingsSetMinSB").get_value_as_int() != 0)
for rCtrl in (
"TimekprUserConfTodaySettingsSetAddBT",
"TimekprUserConfTodaySettingsSetSubractBT",
"TimekprUserConfTodaySettingsSetSetBT"
):
self._timekprAdminFormBuilder.get_object(rCtrl).set_sensitive(enabled)
# color the buttons for ppl to see them better
self._timekprAdminFormBuilder.get_object("TimekprUserConfTodaySettingsSetAddBT").modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("red") if enabled else None)
self._timekprAdminFormBuilder.get_object("TimekprUserConfTodaySettingsSetSubractBT").modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("red") if enabled else None)
self._timekprAdminFormBuilder.get_object("TimekprUserConfTodaySettingsSetSetBT").modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("red") if enabled else None)
# tab color
self._timekprAdminFormBuilder.get_object("TimekprUserConfTodayLabel").modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("red") if enabled else None)
def calculateUserConfigControlAvailability(self, pApplyControls=True):
"""Calculate user config control availability"""
# this duplicates diff control as well
changeControl = {}
# perform?
if not self._timekprAdminFormBuilder.get_object("TimekprWeekDaysTreeView").get_sensitive():
return changeControl
# get stores (for use later)
limitSt = self._timekprAdminFormBuilder.get_object("TimekprWeekDaysLS")
wkMonSt = self._timekprAdminFormBuilder.get_object("TimekprUserConfWkMonLimitsLS")
# ## time day config ##
tmpArray = [str(rIt[0]) for rIt in limitSt if rIt[2]]
control = "TimekprUserWeekDaysLSD"
changeControl[control] = {"st": tmpArray != self._tkSavedCfg["timeLimitDays"], "val": tmpArray.copy()}
# ## time limits per allowed days ###
tmpArray = [rIt[3] for rIt in limitSt if rIt[2]]
control = "TimekprUserWeekDaysLSL"
changeControl[control] = {"st": tmpArray != self._tkSavedCfg["timeLimitDaysLimits"], "val": tmpArray.copy()}
# ## intervals ###
areIntervalsVerified = self.areHoursVerified()
control = "TimekprHourIntervalsLS"
changeControl[control] = {"st": self._tkSavedCfg["timeLimitDaysHoursActual"] != self._tkSavedCfg["timeLimitDaysHoursSaved"], "val": self._tkSavedCfg["timeLimitDaysHoursActual"]}
# ## week / month limits ###
for rIt in wkMonSt:
# week or month?
if rIt[0] == "WK":
control = "TimekprUserConfWkMonLimitsLSWK"
changeControl[control] = {"st": rIt[2] != self._tkSavedCfg["timeLimitWeek"], "val": rIt[2]}
elif rIt[0] == "MON":
control = "TimekprUserConfWkMonLimitsLSMON"
changeControl[control] = {"st": rIt[2] != self._tkSavedCfg["timeLimitMonth"], "val": rIt[2]}
# if at least one is changed
configChanged = False
if pApplyControls:
for rKey, rVal in changeControl.items():
# one thing changed
if rVal["st"]:
# enable
configChanged = rVal["st"]
# no need to search further
break
# enabled or not
self._timekprAdminFormBuilder.get_object("TimekprUserConfDaySettingsApplyBT").set_sensitive(configChanged and areIntervalsVerified)
# color the buttons for ppl to see them better
self._timekprAdminFormBuilder.get_object("TimekprUserConfDaySettingsApplyBT").modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("red") if configChanged else None)
# enable / disable verify
self._timekprAdminFormBuilder.get_object("TimekprUserConfDaySettingsSetDaysIntervalsVerifyBT").set_sensitive(not areIntervalsVerified)
# color the buttons for ppl to see them better
self._timekprAdminFormBuilder.get_object("TimekprUserConfDaySettingsSetDaysIntervalsVerifyBT").modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("red") if not areIntervalsVerified else None)
# tab color
self._timekprAdminFormBuilder.get_object("TimekprUserConfDailyLabel").modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("red") if (configChanged or not areIntervalsVerified) else None)
# return
return changeControl
def calculateUserPlayTimeConfigControlAvailability(self, pApplyControls=True):
"""Calculate user PlayTime config control availability"""
# this duplicates diff control as well
changeControl = {}
# perform?
if not self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLimitsTreeView").get_sensitive():
return changeControl
# ## PlayTime enabled ##
control = "TimekprUserPlayTimeEnableCB"
value = self._timekprAdminFormBuilder.get_object(control).get_active()
changeControl[control] = {"st": value != self._tkSavedCfg["playTimeEnabled"], "val": value}
# ## PlayTime override enabled ##
control = "TimekprUserPlayTimeOverrideEnableCB"
value = self._timekprAdminFormBuilder.get_object(control).get_active()
changeControl[control] = {"st": value != self._tkSavedCfg["playTimeOverrideEnabled"], "val": value}
# ## PlayTime allowed during unaccounted intervals ##
control = "TimekprUserPlayTimeUnaccountedIntervalsEnabledCB"
value = self._timekprAdminFormBuilder.get_object(control).get_active()
changeControl[control] = {"st": value != self._tkSavedCfg["playTimeUnaccountedIntervalsEnabled"], "val": value}
# get stores (for use later)
limitSt = self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLimitsLS")
actSt = self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesLS")
actStLen = len(actSt)
# ## PlayTime day config ##
tmpArray = [str(rIt[0]) for rIt in limitSt if rIt[2]]
control = "TimekprUserPlayTimeLimitsLSD"
changeControl[control] = {"st": tmpArray != self._tkSavedCfg["playTimeLimitDays"], "val": tmpArray.copy()}
# ## PlayTime limits per allowed days ###
tmpArray = [rIt[3] for rIt in limitSt if rIt[2]]
control = "TimekprUserPlayTimeLimitsLSL"
changeControl[control] = {"st": tmpArray != self._tkSavedCfg["playTimeLimitDaysLimits"], "val": tmpArray.copy()}
# ## PlayTime activities ###
tmpArray = []
idx = 0
for rIt in actSt:
# increase idx
idx += 1
# do not add, if last line is not filed in properly
if not (idx == actStLen and rIt[1] == ""):
# add mask and description
tmpArray.append([rIt[1], rIt[2]])
control = "TimekprUserPlayTimeProcessesLS"
changeControl[control] = {"st": tmpArray != self._tkSavedCfg["playTimeActivities"], "val": tmpArray.copy()}
# if at least one is changed
enable = False
if pApplyControls:
for rKey, rVal in changeControl.items():
# one thing changed
if rVal["st"]:
# enable
enable = rVal["st"]
# no need to search further
break
# enabled or not
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesApplyBT").set_sensitive(enable)
# color the buttons for ppl to see them better
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesApplyBT").modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("red") if enable else None)
# tab color
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLabel").modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("red") if enable else None)
# return
return changeControl
def calculateUserAdditionalConfigControlAvailability(self, pApplyControls=True):
"""Calculate user config control availability"""
# this duplicates diff control as well
changeControl = {}
# ## Track inactive enabled ##
control = "TimekprUserConfTodaySettingsTrackInactiveCB"
value = self._timekprAdminFormBuilder.get_object(control).get_active()
changeControl[control] = {"st": value != self._tkSavedCfg["timeTrackInactive"], "val": value}
# ## Hide try icon enabled ##
control = "TimekprUserConfTodaySettingsHideTrayIconCB"
value = self._timekprAdminFormBuilder.get_object(control).get_active()
changeControl[control] = {"st": value != self._tkSavedCfg["timeHideTrayIcon"], "val": value}
# ## Lockout type / interval ##
control = "TimekprUserConfAddOptsLockoutType"
# get lockout type
lockoutType = self.getSelectedLockoutType()
# intervals
hrFrom = str(self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeSuspendWakeFromSB").get_value_as_int()) if lockoutType == cons.TK_CTRL_RES_W else "0"
hrTo = str(self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLockoutTypeSuspendWakeToSB").get_value_as_int()) if lockoutType == cons.TK_CTRL_RES_W else "23"
interval = "%s;%s" % (hrFrom, hrTo)
value = (lockoutType, hrFrom, hrTo)
changeControl[control] = {"st": lockoutType != self._tkSavedCfg["timeLockoutType"] or interval != self._tkSavedCfg["timeWakeInterval"], "val": value}
# interval control
self.controlSelectedLockoutTypeHourIntervals(interval if self.getSelectedLockoutType() == cons.TK_CTRL_RES_W else None)
# if at least one is changed
enable = False
if pApplyControls:
for rKey, rVal in changeControl.items():
# one thing changed
if rVal["st"]:
# enable
enable = rVal["st"]
# no need to search further
break
# enabled or not
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsApplyBT").set_sensitive(enable)
# color the buttons for ppl to see them better
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsApplyBT").modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("red") if enable else None)
# tab color
self._timekprAdminFormBuilder.get_object("TimekprUserConfAddOptsLabel").modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("red") if enable else None)
# return
return changeControl
# --------------- changed information publish methods --------------- #
def applyTimekprConfigurationChanges(self):
"""Apply configuration changes to server"""
# get what's changed
changeControl = self.calculateTimekprConfigControlAvailability(False)
# initial values
result = 0
message = ""
# loop through all changes
for rKey, rVal in changeControl.items():
# changed
if rVal["st"]:
# check what element we have, depending on that call different interface
# ## poll time ##
if rKey == "TimekprConfigurationLoglevelSB":
# call server
result, message = self._timekprAdminConnector.setTimekprLogLevel(rVal["val"])
# successful call
if result == 0:
# set internal state
self._tkSavedCfg["timekprLogLevel"] = rVal["val"]
# ## poll time ##
elif rKey == "TimekprConfigurationPollIntervalSB":
# call server
result, message = self._timekprAdminConnector.setTimekprPollTime(rVal["val"])
# successful call
if result == 0:
# set internal state
self._tkSavedCfg["timekprPollingInterval"] = rVal["val"]
# ## save time ##
elif rKey == "TimekprConfigurationSaveTimeSB":
# call server
result, message = self._timekprAdminConnector.setTimekprSaveTime(rVal["val"])
# successful call
if result == 0:
# set internal state
self._tkSavedCfg["timekprSaveTime"] = rVal["val"]
# ## termination time ##
elif rKey == "TimekprConfigurationTerminationTimeSB":
# call server
result, message = self._timekprAdminConnector.setTimekprTerminationTime(rVal["val"])
# successful call
if result == 0:
# set internal state
self._tkSavedCfg["timekprTerminationTime"] = rVal["val"]
# ## final warning time ##
elif rKey == "TimekprConfigurationWarningTimeSB":
# call server
result, message = self._timekprAdminConnector.setTimekprFinalWarningTime(rVal["val"])
# successful call
if result == 0:
# set internal state
self._tkSavedCfg["timekprWarningTime"] = rVal["val"]
# ## final notification ##
elif rKey == "TimekprConfigurationFinalNotificationSB":
# call server
result, message = self._timekprAdminConnector.setTimekprFinalNotificationTime(rVal["val"])
# successful call
if result == 0:
# set internal state
self._tkSavedCfg["timekprNotificationTime"] = rVal["val"]
# ## tracking session types ###
elif rKey == "TimekprTrackingSessionsLS":
# call server
result, message = self._timekprAdminConnector.setTimekprSessionsCtrl(rVal["val"])
# successful call
if result == 0:
# set internal state
self._tkSavedCfg["timekprTrackingSessions"] = rVal["val"].copy()
# ## exclusion session types ##
elif rKey == "TimekprExcludedSessionsLS":
# call server
result, message = self._timekprAdminConnector.setTimekprSessionsExcl(rVal["val"])
# successful call
if result == 0:
# set internal state
self._tkSavedCfg["timekprExcludedSessions"] = rVal["val"].copy()
# ## excluded users ##
elif rKey == "TimekprExcludedUsersLS":
# call server
result, message = self._timekprAdminConnector.setTimekprUsersExcl(rVal["val"])
# successful call
if result == 0:
# set internal state
self._tkSavedCfg["timekprExcludedUsers"] = rVal["val"].copy()
# ## PlayTime enabled ##
elif rKey == "TimekprPlayTimeEnableGlobalCB":
# call server
result, message = self._timekprAdminConnector.setTimekprPlayTimeEnabled(rVal["val"])
# successful call
if result == 0:
# set internal state
self._tkSavedCfg["timekprPlayTimeEnabled"] = rVal["val"]
# ## PlayTime enhanced activity monitor ##
elif rKey == "TimekprPlayTimeEnhancedActivityMonitorCB":
# call server
result, message = self._timekprAdminConnector.setTimekprPlayTimeEnhancedActivityMonitorEnabled(rVal["val"])
# successful call
if result == 0:
# set internal state
self._tkSavedCfg["timekprPlayTimeEnhancedActivityMonitorEnabled"] = rVal["val"]
# if all ok
if result != 0:
# status
self.setTimekprStatus(False, message)
# that's it
break
# fine
if result != 0:
# check the connection
self.checkConnection()
else:
# status
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_CONFIGURATION_SAVED"))
# recalc the control state
self.calculateTimekprConfigControlAvailability()
def applyUserTodayConfigurationChanges(self, pType, pOperation):
"""Process actual call to set time for user"""
# get username
userName = self.getSelectedUserName()
# if we have username
if userName is not None:
# initial values
result = 0
message = ""
# regular time or PlayTime
hrSb = "TimekprUserConfTodaySettingsSetHrSB"
minSb = "TimekprUserConfTodaySettingsSetMinSB"
# get time to add
timeToAdjust = self._timekprAdminFormBuilder.get_object(hrSb).get_value_as_int() * cons.TK_LIMIT_PER_HOUR
timeToAdjust += self._timekprAdminFormBuilder.get_object(minSb).get_value_as_int() * cons.TK_LIMIT_PER_MINUTE
if pType == "Time":
# call server
result, message = self._timekprAdminConnector.setTimeLeft(userName, pOperation, timeToAdjust)
elif pType == "PlayTime":
# call server
result, message = self._timekprAdminConnector.setPlayTimeLeft(userName, pOperation, timeToAdjust)
# successful call
if result == 0:
if pType == "Time":
# status
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_ADJUSTTIME_PROCESSED"))
elif pType == "PlayTime":
# status
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_PT_ADJUSTTIME_PROCESSED"))
# reset values to form
for rCtrl in (hrSb, minSb):
# no value to add
self._timekprAdminFormBuilder.get_object(rCtrl).set_value(0)
else:
# status
self.setTimekprStatus(False, message)
# check the connection
self.checkConnection()
# recalc the control state
self.calculateUserTodayControlAvailability()
def applyUserLimitConfigurationChanges(self):
"""Apply configuration changes to server"""
# get what's changed
changeControl = self.calculateUserConfigControlAvailability(False)
# get username
userName = self.getSelectedUserName()
# initial values
result = 0
message = ""
changeCnt = 0
# loop through all changes
for rKey, rVal in changeControl.items():
# changed
if rVal["st"]:
# check what element we have, depending on that call different interface
# ## week limit ##
if rKey == "TimekprUserConfWkMonLimitsLSWK":
# call server
result, message = self._timekprAdminConnector.setTimeLimitForWeek(userName, rVal["val"])
# successful call
if result == 0:
# cnt
changeCnt += 1
# set internal state
self._tkSavedCfg["timeLimitWeek"] = rVal["val"]
# print success message
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_WKMONADJUSTTIME_PROCESSED"))
# ## month limit ##
elif rKey == "TimekprUserConfWkMonLimitsLSMON":
# call server
result, message = self._timekprAdminConnector.setTimeLimitForMonth(userName, rVal["val"])
# successful call
if result == 0:
# cnt
changeCnt += 1
# set internal state
self._tkSavedCfg["timeLimitMonth"] = rVal["val"]
# print success message
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_WKMONADJUSTTIME_PROCESSED"))
# ## day config ##
elif rKey == "TimekprUserWeekDaysLSD":
# call server
result, message = self._timekprAdminConnector.setAllowedDays(userName, rVal["val"])
# successful call
if result == 0:
# cnt
changeCnt += 1
# set internal state
self._tkSavedCfg["timeLimitDays"] = rVal["val"]
# print success message
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_ALLOWEDDAYS_PROCESSED"))
# ## limits per allowed days ###
elif rKey == "TimekprUserWeekDaysLSL":
# call server
result, message = self._timekprAdminConnector.setTimeLimitForDays(userName, rVal["val"])
# successful call
if result == 0:
# cnt
changeCnt += 1
# set internal state
self._tkSavedCfg["timeLimitDaysLimits"] = rVal["val"]
# print success message
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_TIMELIMITS_PROCESSED"))
# ## hour allowance activities ###
elif rKey == "TimekprHourIntervalsLS":
# loop through changed day hours
for rDay in rVal["val"]:
# day changed
hrs = None if self._tkSavedCfg["timeLimitDaysHoursActual"][rDay] == self._tkSavedCfg["timeLimitDaysHoursSaved"][rDay] else self._tkSavedCfg["timeLimitDaysHoursActual"][rDay]
# hours changed
if hrs is not None:
# call server
result, message = self._timekprAdminConnector.setAllowedHours(userName, rDay, hrs)
# successful call
if result == 0:
# cnt
changeCnt += 1
# set internal state
self._tkSavedCfg["timeLimitDaysHoursSaved"][rDay] = self._tkSavedCfg["timeLimitDaysHoursActual"][rDay].copy()
# status
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_ALLOWEDHOURS_PROCESSED"))
else:
# finish
break
# if all ok
if result != 0:
# status
self.setTimekprStatus(False, message)
# that's it
break
# fine
if result != 0:
# check the connection
self.checkConnection()
# override messages in case more then one option was processed
elif changeCnt > 1:
# status
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_USER_LIMIT_CONFIGURATION_SAVED"))
# recalc the control state
self.calculateUserConfigControlAvailability()
def applyUserPlayTimeConfigurationChanges(self):
"""Apply configuration changes to server"""
# get what's changed
changeControl = self.calculateUserPlayTimeConfigControlAvailability(False)
# get username
userName = self.getSelectedUserName()
# initial values
result = 0
message = ""
changeCnt = 0
# loop through all changes
for rKey, rVal in changeControl.items():
# changed
if rVal["st"]:
# check what element we have, depending on that call different interface
# ## PlayTime enabled ##
if rKey == "TimekprUserPlayTimeEnableCB":
# call server
result, message = self._timekprAdminConnector.setPlayTimeEnabled(userName, rVal["val"])
# successful call
if result == 0:
# cnt
changeCnt += 1
# set internal state
self._tkSavedCfg["playTimeEnabled"] = rVal["val"]
# print success message
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_PT_ENABLEMENT_PROCESSED"))
# ## PlayTime override enabled ##
elif rKey == "TimekprUserPlayTimeOverrideEnableCB":
# call server
result, message = self._timekprAdminConnector.setPlayTimeLimitOverride(userName, rVal["val"])
# successful call
if result == 0:
# cnt
changeCnt += 1
# set internal state
self._tkSavedCfg["playTimeOverrideEnabled"] = rVal["val"]
# print success message
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_PT_OVERRIDE_PROCESSED"))
# ## PlayTime allowed during unaccounted intervals ##
elif rKey == "TimekprUserPlayTimeUnaccountedIntervalsEnabledCB":
# call server
result, message = self._timekprAdminConnector.setPlayTimeUnaccountedIntervalsEnabled(userName, rVal["val"])
# successful call
if result == 0:
# cnt
changeCnt += 1
# set internal state
self._tkSavedCfg["playTimeUnaccountedIntervalsEnabled"] = rVal["val"]
# print success message
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_PT_ALLOWED_UNLIMITED_INTERVALS_PROCESSED"))
# ## PlayTime day config ##
elif rKey == "TimekprUserPlayTimeLimitsLSD":
# call server
result, message = self._timekprAdminConnector.setPlayTimeAllowedDays(userName, rVal["val"])
# successful call
if result == 0:
# cnt
changeCnt += 1
# set internal state
self._tkSavedCfg["playTimeLimitDays"] = rVal["val"]
# print success message
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_PT_ALLOWEDDAYS_PROCESSED"))
# ## PlayTime limits per allowed days ###
elif rKey == "TimekprUserPlayTimeLimitsLSL":
# call server
result, message = self._timekprAdminConnector.setPlayTimeLimitsForDays(userName, rVal["val"])
# successful call
if result == 0:
# cnt
changeCnt += 1
# set internal state
self._tkSavedCfg["playTimeLimitDaysLimits"] = rVal["val"]
# print success message
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_PT_TIMELIMITS_PROCESSED"))
# ## PlayTime activities ###
elif rKey == "TimekprUserPlayTimeProcessesLS":
# call server
result, message = self._timekprAdminConnector.setPlayTimeActivities(userName, rVal["val"])
# successful call
if result == 0:
# cnt
changeCnt += 1
# set internal state
self._tkSavedCfg["playTimeActivities"] = rVal["val"]
# print success message
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_PT_ACTIVITIES_PROCESSED"))
# if all ok
if result != 0:
# status
self.setTimekprStatus(False, message)
# that's it
break
# fine
if result != 0:
# check the connection
self.checkConnection()
# override messages in case more then one option was processed
elif changeCnt > 1:
# status
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_USER_PT_LIMIT_CONFIGURATION_SAVED"))
# recalc the control state
self.calculateUserPlayTimeConfigControlAvailability()
def applyUserAdditionalConfigurationChanges(self):
"""Apply configuration changes to server"""
# get what's changed
changeControl = self.calculateUserAdditionalConfigControlAvailability(False)
# get username
userName = self.getSelectedUserName()
# initial values
result = 0
message = ""
changeCnt = 0
# loop through all changes
for rKey, rVal in changeControl.items():
# changed
if rVal["st"]:
# check what element we have, depending on that call different interface
# ## Track inactive enabled ##
if rKey == "TimekprUserConfTodaySettingsTrackInactiveCB":
# call server
result, message = self._timekprAdminConnector.setTrackInactive(userName, rVal["val"])
# successful call
if result == 0:
# cnt
changeCnt += 1
# set internal state
self._tkSavedCfg["timeTrackInactive"] = rVal["val"]
# print success message
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_TRACKINACTIVE_PROCESSED"))
# ## Hide try icon enabled ##
elif rKey == "TimekprUserConfTodaySettingsHideTrayIconCB":
# call server
result, message = self._timekprAdminConnector.setHideTrayIcon(userName, rVal["val"])
# successful call
if result == 0:
# cnt
changeCnt += 1
# set internal state
self._tkSavedCfg["timeHideTrayIcon"] = rVal["val"]
# print success message
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_HIDETRAYICON_PROCESSED"))
# ## Lockout type / interval ##
elif rKey == "TimekprUserConfAddOptsLockoutType":
# call server
result, message = self._timekprAdminConnector.setLockoutType(userName, rVal["val"][0], rVal["val"][1], rVal["val"][2])
# successful call
if result == 0:
# cnt
changeCnt += 1
# set internal state
self._tkSavedCfg["timeLockoutType"] = rVal["val"][0]
self._tkSavedCfg["timeWakeInterval"] = "%s;%s" % ( rVal["val"][1], rVal["val"][2])
# print success message
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_LOCKOUTTYPE_PROCESSED"))
# if all ok
if result != 0:
# status
self.setTimekprStatus(False, message)
# that's it
break
# fine
if result != 0:
# check the connection
self.checkConnection()
# override messages in case more then one option was processed
elif changeCnt > 1:
# status
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_USER_ADDOPTS_CONFIGURATION_SAVED"))
# recalc the control state
self.calculateUserAdditionalConfigControlAvailability()
# --------------- GTK signal methods --------------- #
# --------------- timekpr configuration GTK signal helper methods --------------- #
def addElementToList(self, pName):
"""Add tracked session"""
lstSt = self._timekprAdminFormBuilder.get_object("%sLS" % (pName))
lstTw = self._timekprAdminFormBuilder.get_object("%sTreeView" % (pName))
lstLen = len(lstSt)
# check if the last one is not empty (no need to add more empty rows)
if lstLen > 0 and lstSt[lstLen-1][0] != "":
# add empty item
lstSt.append([""])
# scroll to end
lstTw.set_cursor(lstLen)
lstTw.scroll_to_cell(lstLen)
# verify control availability
self.calculateTimekprConfigControlAvailability()
def removeElementFromList(self, pName):
"""Remove tracked session"""
# defaults
elemIdx = self.getSelectedConfigElement("%sTreeView" % (pName))
rIdx = 0
# remove selected item
for rIt in self._timekprAdminFormBuilder.get_object("%sLS" % (pName)):
# check what to remove
if elemIdx == rIdx:
# remove
self._timekprAdminFormBuilder.get_object("%sLS" % (pName)).remove(rIt.iter)
# this is it
break
# count further
rIdx += 1
# verify control availability
self.calculateTimekprConfigControlAvailability()
def setTimekprExcludedTrackedValue(self, pPath, pText, pWhat):
"""Set internal representation of in-place edited value"""
# def
lStN = None
if pWhat == "TrackedSessions":
# store
lStN = "TimekprTrackingSessionsLS"
elif pWhat == "ExcludedSessions":
# store
lStN = "TimekprExcludedSessionsLS"
elif pWhat == "ExcludedUsers":
# store
lStN = "TimekprExcludedUsersLS"
# handled type
if lStN is not None:
# get store object
self._timekprAdminFormBuilder.get_object(lStN)[pPath][0] = pText
# calculate control availability
self.calculateTimekprConfigControlAvailability()
# --------------- timekpr configuration GTK signal methods --------------- #
def configControlSwitchesChanged(self, evt):
"""Change any control item (warning, poll, etc.)"""
# verify control availability
self.calculateTimekprConfigControlAvailability()
def trackedSessionsAddClicked(self, evt):
"""Add tracked session"""
self.addElementToList("TimekprTrackingSessions")
def trackedSessionsRemoveClicked(self, evt):
"""Remove tracked session"""
self.removeElementFromList("TimekprTrackingSessions")
def excludedSessionsAddClicked(self, evt):
"""Add excluded session"""
self.addElementToList("TimekprExcludedSessions")
def excludedSessionsRemoveClicked(self, evt):
"""Remove excluded session"""
self.removeElementFromList("TimekprExcludedSessions")
def excludedUsersAddClicked(self, evt):
"""Add excluded session"""
self.addElementToList("TimekprExcludedUsers")
def excludedUsersRemoveClicked(self, evt):
"""Remove excluded user"""
self.removeElementFromList("TimekprExcludedUsers")
def timekprTrackedSessionsEdited(self, widget, path, text):
"""Tracked session values edited"""
self.setTimekprExcludedTrackedValue(path, text, "TrackedSessions")
def timekprExcludedSessionsEdited(self, widget, path, text):
"""Excluded session values edited"""
self.setTimekprExcludedTrackedValue(path, text, "ExcludedSessions")
def timekprExcludedUsersEdited(self, widget, path, text):
"""Excluded user values edited"""
self.setTimekprExcludedTrackedValue(path, text, "ExcludedUsers")
def applyTimekprConfigurationChangesClicked(self, evt):
"""Apply configuration changes"""
# disable button so it cannot be triggered again
self._timekprAdminFormBuilder.get_object("TimekprConfigurationApplyBT").set_sensitive(False)
# color the buttons for ppl to see them better
self._timekprAdminFormBuilder.get_object("TimekprConfigurationApplyBT").modify_fg(Gtk.StateFlags.NORMAL, None)
# tab color
self._timekprAdminFormBuilder.get_object("TimekprConfigurationTabLabel").modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("red") if self._timekprAdminFormBuilder.get_object("TimekprConfigurationApplyBT").get_sensitive() else None)
# process setting
self.applyTimekprConfigurationChanges()
# --------------- user selection GTK signal methods --------------- #
def userSelectionChanged(self, evt, pInfoLvl=None):
"""User selected"""
# get username
userName = self.getSelectedUserName()
# only if connected
if userName is not None and userName != "":
# get user config
self.retrieveUserInfoAndConfig(userName, cons.TK_CL_INF_FULL if pInfoLvl is None else pInfoLvl)
else:
# disable all
self.toggleUserConfigControls(False, True)
def userConfigurationRefreshClicked(self, evt):
"""User requested config restore from server"""
self._timekprAdminFormBuilder.get_object("TimekprUserSelectionCB").emit("changed")
# --------------- today page GTK signal methods --------------- #
def todayAddTimeChanged(self, evt):
"""Call control calculations when time has been added"""
# recalc control availability
self.calculateUserTodayControlAvailability()
def todayAddPlayTimeChanged(self, evt):
"""Call control calculations when time has been added"""
# recalc control availability
self.calculateUserTodayControlAvailability()
def todayAddTimeClicked(self, evt):
"""Add time to user"""
# disable button so it cannot be triggered again
self._timekprAdminFormBuilder.get_object("TimekprUserConfTodaySettingsSetAddBT").set_sensitive(False)
# get choice
type = "Time" if self._timekprAdminFormBuilder.get_object("TimekprUserConfTodaySettingsChoiceTimeRB").get_active() else "PlayTime"
# process setting
self.applyUserTodayConfigurationChanges(type, "+")
def todaySubtractTimeClicked(self, evt):
"""Subtract time from user"""
# disable button so it cannot be triggered again
self._timekprAdminFormBuilder.get_object("TimekprUserConfTodaySettingsSetSubractBT").set_sensitive(False)
# get choice
type = "Time" if self._timekprAdminFormBuilder.get_object("TimekprUserConfTodaySettingsChoiceTimeRB").get_active() else "PlayTime"
# process setting
self.applyUserTodayConfigurationChanges(type, "-")
def todaySetTimeClicked(self, evt):
"""Set exact time for user"""
# disable button so it cannot be triggered again
self._timekprAdminFormBuilder.get_object("TimekprUserConfTodaySettingsSetSetBT").set_sensitive(False)
# get choice
type = "Time" if self._timekprAdminFormBuilder.get_object("TimekprUserConfTodaySettingsChoiceTimeRB").get_active() else "PlayTime"
# process setting
self.applyUserTodayConfigurationChanges(type, "=")
# --------------- limit configuration GTK signal helper methods --------------- #
def verifyAndSetHourInterval(self, path, text, pIsFrom):
"""Verify and set hour values"""
# store
intervalSt = self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsLS")
# value before
secsBefore = intervalSt[path][4 if pIsFrom else 5]
# def
secs = self.verifyAndCalcLimit(text, "h")
# if we could calculate seconds (i.e. entered text is correct)
if secs is not None:
# if values before and after does not change (or initial ""), we do nothing
if secsBefore != secs or (intervalSt[path][0] == -1 and intervalSt[path][1 if pIsFrom else 2] == ""):
# format secs
text = self.formatTimeStr(secs)
# set values
intervalSt[path][1 if pIsFrom else 2] = text
intervalSt[path][4 if pIsFrom else 5] = secs
# reset id
intervalSt[path][0] = -1
# calculate control availability
self.calculateUserConfigControlAvailability()
def verifyAndSetWeeklyLimits(self, path, text):
"""Verify and set weekly values"""
# store
limitsSt = self._timekprAdminFormBuilder.get_object("TimekprUserConfWkMonLimitsLS")
# value before
secsBefore = limitsSt[path][2]
# def
secs = self.verifyAndCalcLimit(text, "w" if limitsSt[path][0] == "WK" else "m")
# if we could calculate seconds (i.e. entered text is correct)
if secs is not None:
# if values before and after does not change, we do nothing
if secsBefore != secs:
# format secs
text = self.formatTimeStr(secs, pFormatSecs=True, pFormatDays=True)
# set values
limitsSt[path][3] = text
limitsSt[path][2] = secs
# calculate control availability
self.calculateUserConfigControlAvailability()
def verifyAndSetDayLimits(self, path, text, pIsPlayTime=False):
"""Verify and set daily values"""
# store
limitsSt = self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLimitsLS" if pIsPlayTime else "TimekprWeekDaysLS")
controlFnc = self.calculateUserPlayTimeConfigControlAvailability if pIsPlayTime else self.calculateUserConfigControlAvailability
# value before
secsBefore = limitsSt[path][3]
# def
secs = self.verifyAndCalcLimit(text, "d")
# if we could calculate seconds (i.e. entered text is correct)
if secs is not None:
# if values before and after does not change, we do nothing
if secsBefore != secs:
# format secs
text = self.formatTimeStr(secs, pFormatSecs=True)
# set values
limitsSt[path][4] = text
limitsSt[path][3] = secs
# calculate control availability
controlFnc()
def areHoursVerified(self):
"""Return whether all hours have been verified"""
# def
result = True
# store
intervalSt = self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsLS")
# loop through all
for rInt in intervalSt:
# check whether it has been verified (id = -1 means not verified)
if rInt[0] < 0 and not (rInt[4] == 0 and rInt[4] == rInt[5]):
# not verified
result = False
break
# result
return result
def verifyAndCalcLimit(self, pLimitStr, pLimitType):
"""Parse user entered limit"""
# add limits
def _addLimit(pSecs, pAddSecs):
# add limits
return pAddSecs if pSecs is None else pSecs + pAddSecs
# def
secs = None
# determine interval type and calculate seconds according to it
try:
# days to weeks/month
if pLimitType in ("w", "m") and _DAY_HOUR_MIN_SEC_REGEXP.match(pLimitStr):
# calculate seconds
secs = min(_addLimit(secs, int(_DAY_HOUR_MIN_SEC_REGEXP.sub(r"\4", pLimitStr))), cons.TK_LIMIT_PER_MINUTE)
# days to weeks/month
if pLimitType in ("w", "m", "d") and _DAY_HOUR_MIN_REGEXP.match(pLimitStr):
# calculate minutes
secs = min(_addLimit(secs, int(_DAY_HOUR_MIN_REGEXP.sub(r"\3", pLimitStr)) * (cons.TK_LIMIT_PER_MINUTE if pLimitType != "d" else 1)), cons.TK_LIMIT_PER_HOUR if pLimitType != "d" else cons.TK_LIMIT_PER_MINUTE)
# hours/minutes or days/hours
if _HOUR_MIN_REGEXP.match(pLimitStr):
# calculate seconds
secs = min(_addLimit(secs, int(_HOUR_MIN_REGEXP.sub(r"\2", pLimitStr)) * (cons.TK_LIMIT_PER_MINUTE if pLimitType in ("h", "d") else cons.TK_LIMIT_PER_HOUR)), cons.TK_LIMIT_PER_HOUR if pLimitType in ("h", "d") else cons.TK_LIMIT_PER_DAY)
# hours / days
if _HOUR_REGEXP.match(pLimitStr):
# calculate seconds
secs = min(_addLimit(secs, int(_HOUR_REGEXP.sub(r"\1", pLimitStr)) * (cons.TK_LIMIT_PER_HOUR if pLimitType in ("h", "d") else cons.TK_LIMIT_PER_DAY)), cons.TK_LIMIT_PER_DAY if pLimitType in ("h", "d") else cons.TK_LIMIT_PER_MONTH)
# no error
if secs is not None:
# normalize total seconds
secs = min(secs, cons.TK_LIMIT_PER_MONTH if pLimitType == "m" else cons.TK_LIMIT_PER_WEEK if pLimitType == "w" else cons.TK_LIMIT_PER_DAY)
except:
# we do not care about any errors
secs = None
# return seconds
return secs
# --------------- limit configuration GTK signal methods --------------- #
def dayLimitsIncreaseClicked(self, evt):
"""Increase time limits"""
self.adjustTimeLimits(pType="DailyLimits", pAdd=True)
def dayLimitsDecreaseClicked(self, evt):
"""Decrease time limits"""
self.adjustTimeLimits(pType="DailyLimits", pAdd=False)
def dailyLimitsDaySelectionChanged(self, evt):
"""Set up intervals on GUI day change"""
# refresh the child
days = self.getSelectedDays()
# get current seconds
dt = datetime.now().replace(microsecond=0)
dtd = str(datetime.date(dt).isoweekday())
dts = int((dt - datetime.now().replace(microsecond=0, second=0, minute=0, hour=0)).total_seconds())
selIdx = 0
# only if there is smth selected
if len(days) > 0:
# go to last day (this cannot and should not be calculated for everything)
dayIdx = days[len(days)-1]["idx"]
dayNum = days[len(days)-1]["nr"]
# whether day is enabled
enabled = self._timekprAdminFormBuilder.get_object("TimekprWeekDaysLS")[dayIdx][2]
limit = self._timekprAdminFormBuilder.get_object("TimekprWeekDaysLS")[dayIdx][3]
# clear out existing intervals
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsLS").clear()
# fill intervals only if that day exists
if dayNum in self._tkSavedCfg["timeLimitDaysHoursActual"] and enabled and limit > 0:
# idx
idx = 0
found = False
# fill the intervals
for rInterval in self.getIntervalList(dayNum):
# exists
found = True
# determine which is the current hour
selIdx = idx if rInterval[2] <= dts <= rInterval[3] and dtd == dayNum else selIdx
# fill in the intervals
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsLS").append([idx, rInterval[0], rInterval[1], dayNum, rInterval[2], rInterval[3], self._ROWCOL_OK, self._ROWSTYLE_OK, rInterval[4]])
idx += 1
# found
if found:
# set selection to found row
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsTreeView").set_cursor(selIdx)
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsTreeView").scroll_to_cell(selIdx)
def dayAvailabilityChanged(self, widget, path):
"""Change minutes depending on day availability"""
# get list store
limitSt = self._timekprAdminFormBuilder.get_object("TimekprWeekDaysLS")
# flip the checkbox
limitSt[path][2] = not limitSt[path][2]
# if we have a day, restore limits
if limitSt[path][2]:
# if we have limits set in background store, restore them
if limitSt[path][0] in self._tkSavedCfg["timeLimitDays"]:
limitSt[path][3] = self._tkSavedCfg["timeLimitDaysLimits"][self._tkSavedCfg["timeLimitDays"].index(limitSt[path][0])]
else:
limitSt[path][3] = 0
# format string too
limitSt[path][4] = self.formatTimeStr(limitSt[path][3], True)
# restore intervals from saved state
self._tkSavedCfg["timeLimitDaysHoursActual"][limitSt[path][0]] = self._tkSavedCfg["timeLimitDaysHoursSaved"][limitSt[path][0]].copy()
# enable interval refresh
self._timekprAdminFormBuilder.get_object("TimekprWeekDaysTreeView").get_selection().emit("changed")
else:
# reset hours & minutes
limitSt[path][3] = 0
limitSt[path][4] = _NO_TIME_LABEL
# intervals store
intervalsSt = self._timekprAdminFormBuilder.get_object("TimekprWeekDaysLS")
# change interval selection as well
for rHour in range(0, 23+1):
self._tkSavedCfg["timeLimitDaysHoursActual"][intervalsSt[path][0]][str(rHour)] = {cons.TK_CTRL_SMIN: 0, cons.TK_CTRL_EMIN: cons.TK_LIMIT_PER_MINUTE, cons.TK_CTRL_UACC: False}
# clear stuff and disable intervals
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsLS").clear()
# recalc control availability
self.calculateUserConfigControlAvailability()
def intervalsIncreaseClicked(self, evt):
"""Increase time limits"""
self.adjustTimeLimits(pType="Intervals", pAdd=True)
def intervalsDecreaseClicked(self, evt):
"""Decrease time limits"""
self.adjustTimeLimits(pType="Intervals", pAdd=False)
def userLimitsHourFromEdited(self, widget, path, text):
"""Set internal representation of in-place edited value"""
self.verifyAndSetHourInterval(path, text, pIsFrom=True)
def userLimitsHourToEdited(self, widget, path, text):
"""Set internal representation of in-place edited value"""
self.verifyAndSetHourInterval(path, text, pIsFrom=False)
def userLimitsWeeklyLimitsEdited(self, widget, path, text):
"""Set internal representation of in-place edited value"""
self.verifyAndSetWeeklyLimits(path, text)
def userLimitsDailyLimitsEdited(self, widget, path, text):
"""Set internal representation of in-place edited value"""
self.verifyAndSetDayLimits(path, text)
def userLimitsDailyPlayTimeLimitsEdited(self, widget, path, text):
"""Set internal representation of in-place edited value"""
self.verifyAndSetDayLimits(path, text, pIsPlayTime=True)
def userLimitsHourUnaccountableToggled(self, widget, path):
"""Set internal representation of in-place edited value"""
# store
intSt = self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsLS")
# flip the checkbox
intSt[path][8] = not intSt[path][8]
# we need to rebuild hours
self.rebuildHoursFromIntervals()
# calculate control availability
self.calculateUserConfigControlAvailability()
def addHourIntervalClicked(self, evt):
"""Add PlayTime activity placeholder to the list"""
limitsSt = self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsLS")
limitsTw = self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsTreeView")
limitsLen = len(limitsSt)
# add
addRow = True
# check if the last one is not empty (no need to add more empty rows)
if (limitsLen > 0 and limitsSt[limitsLen-1][4] == 0 and limitsSt[limitsLen-1][5] == 0):
addRow = False
# we can add the row
if addRow:
# get day to which add the interval
days = self.getSelectedDays()
# if it's not selected
if len(days) < 1:
# status
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_NODAY_SELECTED"))
else:
# normalize day
calcDay = days[len(days)-1]["nr"]
# add
limitsSt.append([-1, "", "", calcDay, 0, 0, self._ROWCOL_OK, self._ROWSTYLE_OK, False])
# scroll to end
limitsTw.set_cursor(limitsLen)
limitsTw.scroll_to_cell(limitsLen)
# verify control availability
self.calculateUserConfigControlAvailability()
def removeHourIntervalClicked(self, evt):
"""Remove hour interval"""
# defaults
limitsSt = self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsLS")
elemIdx = self.getSelectedConfigElement("TimekprHourIntervalsTreeView")
rIdx = 0
# only if something is selected
if elemIdx is not None:
# remove selected item
for rIt in limitsSt:
if elemIdx == rIdx:
# remove
limitsSt.remove(rIt.iter)
break
# count further
rIdx += 1
# verify hours
self.verifyHourIntervals(None)
# verify control availability
self.calculateUserConfigControlAvailability()
# status change
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_INTERVAL_REMOVED"))
def wkMonLimitsIncreaseClicked(self, evt):
"""Increase week / month time limits"""
self.adjustTimeLimits(pType="WeekMonthLimits", pAdd=True)
def wkMonLimitsDecreaseClicked(self, evt):
"""Decrease week / month time limits"""
self.adjustTimeLimits(pType="WeekMonthLimits", pAdd=False)
def applyUserLimitConfigurationChangesClicked(self, evt):
"""Call set methods for changes"""
# disable button so it cannot be triggered again
self._timekprAdminFormBuilder.get_object("TimekprUserConfDaySettingsApplyBT").set_sensitive(False)
# process setting
self.applyUserLimitConfigurationChanges()
# --------------- PlayTime limit configuration GTK signal methods --------------- #
def userPlayTimeEnabledChanged(self, evt):
"""PlayTime enablement changed"""
self.calculateUserPlayTimeConfigControlAvailability()
def userPlayTimeOverrideEnabledChanged(self, evt):
"""PlayTime override enablement changed"""
self.calculateUserPlayTimeConfigControlAvailability()
def userPlayTimeUnaccountedIntervalsEnabledChanged(self, evt):
"""PlayTime allowed during unaccounted intervals enablement changed"""
self.calculateUserPlayTimeConfigControlAvailability()
def playTimeLimitsIncreaseClicked(self, evt):
"""Increase PlayTime limits"""
self.adjustTimeLimits(pType="PlayTimeLimits", pAdd=True)
def playTimeLimitsDecreaseClicked(self, evt):
"""Decrease PlayTime limits"""
self.adjustTimeLimits(pType="PlayTimeLimits", pAdd=False)
def dayPlayTimeAvailabilityChanged(self, widget, path):
"""Change PlayTime minutes depending on day availability"""
# get list store
limitSt = self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLimitsLS")
# flip the checkbox
limitSt[path][2] = not limitSt[path][2]
# if we have a day, restore limits
if limitSt[path][2]:
# if we have limits set in background store, restore them
if limitSt[path][0] in self._tkSavedCfg["playTimeLimitDays"]:
limitSt[path][3] = self._tkSavedCfg["playTimeLimitDaysLimits"][self._tkSavedCfg["playTimeLimitDays"].index(limitSt[path][0])]
else:
limitSt[path][3] = 0
# format string too
limitSt[path][4] = self.formatTimeStr(limitSt[path][3], True)
else:
# reset hours & minutes
limitSt[path][3] = 0
limitSt[path][4] = _NO_TIME_LABEL
# recalc control availability
self.calculateUserPlayTimeConfigControlAvailability()
def addPlayTimeActivityClicked(self, evt):
"""Add PlayTime activity placeholder to the list"""
limitsSt = self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesLS")
limitsTw = self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesTreeView")
PTActivityLen = len(limitsSt)
# add
addRow = True
# check if the last one is not empty (no need to add more empty rows)
if (PTActivityLen > 0 and limitsSt[PTActivityLen-1][1] == ""):
addRow = False
# we can add the row
if addRow:
# get last index
PTActivityIdx = str(int(limitsSt[PTActivityLen-1][0]) + 1 if PTActivityLen > 0 else 1)
# add
limitsSt.append([PTActivityIdx, "", ""])
# scroll to end
limitsTw.set_cursor(PTActivityLen)
limitsTw.scroll_to_cell(PTActivityLen)
limitsTw.get_selection().emit("changed")
def removePlayTimeActivityClicked(self, evt):
"""Remove excluded user"""
# defaults
limitsSt = self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesLS")
elemIdx = self.getSelectedConfigElement("TimekprUserPlayTimeProcessesTreeView")
rIdx = 0
# only if something is selected
if elemIdx is not None:
# remove selected item
for rIt in limitsSt:
if elemIdx == rIdx:
# remove
limitsSt.remove(rIt.iter)
elif elemIdx < rIdx:
# adjust next element index
limitsSt[rIdx-1][0] = str(rIdx)
# count further
rIdx += 1
# verify control availability
self.calculateUserPlayTimeConfigControlAvailability()
def playTimeActivityMaskEntryEdited(self, widget, path, text):
"""Set internal representation of in-place edited value"""
# store value
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesLS")[path][1] = text
# recalc control availability
self.calculateUserPlayTimeConfigControlAvailability()
def playTimeActivityDescriptionEntryEdited(self, widget, path, text):
"""Set internal representation of in-place edited value"""
# store value
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesLS")[path][2] = text
# recalc control availability
self.calculateUserPlayTimeConfigControlAvailability()
def applyUserPlayTimeConfigurationChangesClicked(self, evt):
"""Apply PlayTime configuration changes"""
# disable button so it cannot be triggered again
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesApplyBT").set_sensitive(False)
# color the buttons for ppl to see them better
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesApplyBT").modify_fg(Gtk.StateFlags.NORMAL, None)
# tab color
self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeLabel").modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("red") if self._timekprAdminFormBuilder.get_object("TimekprUserPlayTimeProcessesApplyBT").get_sensitive() else None)
# process setting
self.applyUserPlayTimeConfigurationChanges()
# --------------- additional page configuration GTK signal methods --------------- #
def trackInactiveChanged(self, evt):
"""Call control calculations when inactive flag has been changed"""
# recalc control availability
self.calculateUserAdditionalConfigControlAvailability()
def hideTrayIconChanged(self, evt):
"""Call control calculations when hide icon has been changed"""
# recalc control availability
self.calculateUserAdditionalConfigControlAvailability()
def lockoutTypeGroupChanged(self, evt):
"""Call control calculations when restriction / lockout type has been changed"""
# recalc control availability
self.calculateUserAdditionalConfigControlAvailability()
def wakeUpIntervalChanged(self, evt):
"""Call control calculations when restriction / lockout wake up hours have been changed"""
# recalc control availability
self.calculateUserAdditionalConfigControlAvailability()
def applyUserAdditionalConfigurationChangesClicked(self, evt):
"""Apply additional configuration changes"""
# process setting
self.applyUserAdditionalConfigurationChanges()
def navigateTreeView(self, treeview, event):
# get key name
keyname = Gdk.keyval_name(event.keyval)
path, col = treeview.get_cursor()
# if there are no cols
if path is None:
return
# only visible columns
columns = [c for c in treeview.get_columns() if c.get_visible()]
colnum = columns.index(col)
next_column = None
# check key
if keyname == "Tab" or keyname == "Esc":
# wrap
if colnum + 1 < len(columns):
# choose next col
next_column = columns[colnum + 1]
else:
# get model
tmodel = treeview.get_model()
# model exists
if tmodel is not None:
titer = tmodel.iter_next(tmodel.get_iter(path))
# there are cols
if titer is None:
titer = tmodel.get_iter_first()
# next
path = tmodel.get_path(titer)
next_column = columns[0]
# key handling
if next_column is not None:
if keyname == 'Tab':
GLib.timeout_add(1, treeview.set_cursor, path, next_column, True)
elif keyname == 'Escape':
pass
# --------------- helper methods for signal methods --------------- #
def adjustTimeLimits(self, pType, pAdd):
"""Recalc total seconds"""
# get objects depending on type
# rb format:
# array of: checkbutton, seconds to add, check limit, seconds in liststore, string secs in liststore, control to execute, format seconds, format days
if pType == "PlayTimeLimits":
tw = "TimekprUserPlayTimeLimitsTreeView"
ls = "TimekprUserPlayTimeLimitsLS"
rb = [["TimekprUserPlayTimeLimitsHrRB", cons.TK_LIMIT_PER_HOUR, cons.TK_LIMIT_PER_DAY, 3, 4, self.calculateUserPlayTimeConfigControlAvailability, True, False],
["TimekprUserPlayTimeLimitsMinRB", cons.TK_LIMIT_PER_MINUTE, cons.TK_LIMIT_PER_DAY, 3, 4, self.calculateUserPlayTimeConfigControlAvailability, True, False]]
elif pType == "DailyLimits":
tw = "TimekprWeekDaysTreeView"
ls = "TimekprWeekDaysLS"
rb = [["TimekprUserTimeLimitsHrRB", cons.TK_LIMIT_PER_HOUR, cons.TK_LIMIT_PER_DAY, 3, 4, self.calculateUserConfigControlAvailability, True, False],
["TimekprUserTimeLimitsMinRB", cons.TK_LIMIT_PER_MINUTE, cons.TK_LIMIT_PER_DAY, 3, 4, self.calculateUserConfigControlAvailability, True, False]]
elif pType == "WeekMonthLimits":
tw = "TimekprUserConfWkMonLimitsTreeView"
ls = "TimekprUserConfWkMonLimitsLS"
rb = [["TimekprUserConfWkMonLimitsAdjustmentDayRB", cons.TK_LIMIT_PER_DAY, None, 2, 3, self.calculateUserConfigControlAvailability, True, True],
["TimekprUserConfWkMonLimitsAdjustmentHrRB", cons.TK_LIMIT_PER_HOUR, None, 2, 3, self.calculateUserConfigControlAvailability, True, True],
["TimekprUserConfWkMonLimitsAdjustmentMinRB", cons.TK_LIMIT_PER_MINUTE, None, 2, 3, self.calculateUserConfigControlAvailability, True, True]]
elif pType == "Intervals":
tw = "TimekprHourIntervalsTreeView"
ls = "TimekprHourIntervalsLS"
rb = [["TimekprUserConfDaySettingsSetDaysIntervalsHrRB", cons.TK_LIMIT_PER_HOUR, cons.TK_LIMIT_PER_DAY, None, None, self.calculateUserConfigControlAvailability, False, False],
["TimekprUserConfDaySettingsSetDaysIntervalsMinRB", cons.TK_LIMIT_PER_MINUTE, cons.TK_LIMIT_PER_DAY, None, None, self.calculateUserConfigControlAvailability, False, False]]
# depending on selected items
isFrom = self._timekprAdminFormBuilder.get_object("TimekprUserConfDaySettingsSetDaysIntervalsFromRB").get_active()
# set seconds and display idx
for rIdx in range(0, len(rb)):
# depending whether start or end is selected we change that
rb[rIdx][3] = 4 if isFrom else 5 # seconds
rb[rIdx][4] = 1 if isFrom else 2 # time (seconds) to display
# opposite indexes (special case)
rb[rIdx].append(4 if not isFrom else 5)
rb[rIdx].append(1 if not isFrom else 2)
# get selected rows
(tm, paths) = self._timekprAdminFormBuilder.get_object(tw).get_selection().get_selected_rows()
# if rows were selected
if paths is not None:
# def
adj = None
# determine adjustment amount
for rRb in (rRb for rRb in rb if self._timekprAdminFormBuilder.get_object(rRb[0]).get_active()):
# if checked
adj = rRb
# check type found
if adj is not None:
# limits store
limitsSt = self._timekprAdminFormBuilder.get_object(ls)
# idx
for path in paths:
# get idx
idx = tm.get_path(tm.get_iter(path))[0]
# for DailyLimits and PlayTimeLimits we do not need to adjust inactive rows
if pType in ("DailyLimits", "PlayTimeLimits"):
# check if day is active
if not limitsSt[idx][2]:
# we do not process disabled days
continue
# for week / month a row that is selected makes limits different (two rows in LS)
elif pType == "WeekMonthLimits":
if limitsSt[idx][0] == "WK":
adj[2] = cons.TK_LIMIT_PER_WEEK
elif limitsSt[idx][0] == "MON":
adj[2] = cons.TK_LIMIT_PER_MONTH
# adjust value
secs = int(limitsSt[idx][adj[3]]) + adj[1] * (1 if pAdd else -1)
secs = min(adj[2], max(0, secs))
# set up new value
limitsSt[idx][adj[3]] = secs
limitsSt[idx][adj[4]] = self.formatTimeStr(secs, adj[6], adj[7])
# in case of intervals, we need to manage the end / start of it too
if pType == "Intervals":
# now check the other end whether start is later than end (from both sides)
if (isFrom and limitsSt[idx][adj[3]] > limitsSt[idx][adj[8]]) or (not isFrom and limitsSt[idx][adj[3]] < limitsSt[idx][adj[8]]):
# equalize
limitsSt[idx][adj[8]] = limitsSt[idx][adj[3]]
# format string
limitsSt[idx][adj[9]] = self.formatTimeStr(limitsSt[idx][adj[8]], adj[6], adj[7])
# verify control availability by calling configured method (check setup)
adj[5]()
else:
# status
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_NODAY_SELECTED"))
def verifyHourIntervals(self, evt):
"""Verify hour intervals"""
# limits store
intervalsSt = self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsLS")
intervalIdx = -1
result = False
# loop through all entered intervals
for rInt in intervalsSt:
# get interval seconds
intervalIdx += 1
secondsFrom = intervalsSt[intervalIdx][4]
secondsTo = intervalsSt[intervalIdx][5]
# do not check empty intervals
if secondsFrom == 0 == secondsTo and intervalsSt[intervalIdx][0] < 0:
continue
# len
intervalsLen = len(intervalsSt)
# whether interval is valid
intervalOverlaps = False
intervalHourConflictStart = False
intervalHourConflictEnd = False
intervalDuplicate = False
intervalSameStartEnd = False
intervalStartEndMismatch = False
# check intervals
for rIdx in range(0, intervalsLen):
# interval boundaries
fromSecs = intervalsSt[rIdx][4]
toSecs = intervalsSt[rIdx][5]
result = False
errIdx = None
# do not check empty intervals
if fromSecs == 0 == toSecs and intervalsSt[rIdx][0] < 0:
continue
# start is the same as end
if secondsFrom == secondsTo:
# this is it
intervalSameStartEnd = True
elif secondsFrom > secondsTo:
# this is it
intervalStartEndMismatch = True
# these are for all hours
if intervalIdx != rIdx and not (intervalSameStartEnd or intervalStartEndMismatch):
# check whether user tries to insert duplicate iterval
if fromSecs == secondsFrom or toSecs == secondsTo:
# this is it
intervalDuplicate = True
# check whether start is betwen existing interval
elif secondsFrom < fromSecs < secondsTo or secondsFrom < toSecs < secondsTo:
# this is it
intervalOverlaps = True
# check whether start is betwen existing interval
elif fromSecs < secondsFrom < toSecs or fromSecs < secondsTo < toSecs:
# this is it
intervalOverlaps = True
# check whether user tries to insert iterval that doesn'y overlaps with start / end hours from other intervals, but are on the same day
elif int(fromSecs/cons.TK_LIMIT_PER_HOUR) <= int(secondsFrom/cons.TK_LIMIT_PER_HOUR) <= int(toSecs/cons.TK_LIMIT_PER_HOUR) and int(secondsFrom/cons.TK_LIMIT_PER_HOUR) * cons.TK_LIMIT_PER_HOUR not in (secondsTo, toSecs):
# this is it
intervalHourConflictStart = True
# check whether user tries to insert iterval that doesn'y overlaps with start / end hours from other intervals, but are on the same day
elif int(fromSecs/cons.TK_LIMIT_PER_HOUR) <= int(secondsTo/cons.TK_LIMIT_PER_HOUR) <= int(toSecs/cons.TK_LIMIT_PER_HOUR) and int(secondsTo/cons.TK_LIMIT_PER_HOUR) * cons.TK_LIMIT_PER_HOUR not in (secondsTo, toSecs):
# this is it
intervalHourConflictEnd = True
# get final result whether intervals are ok
result = (intervalOverlaps or intervalHourConflictStart or intervalHourConflictEnd or intervalDuplicate or intervalSameStartEnd or intervalStartEndMismatch)
# if we have errors
if intervalsSt[rIdx][7] != result or result:
# mark offending row
intervalsSt[rIdx][6] = self._ROWCOL_NOK if result else self._ROWCOL_OK
intervalsSt[rIdx][7] = self._ROWSTYLE_NOK if result else self._ROWSTYLE_OK
# mark checked row too
intervalsSt[intervalIdx][6] = self._ROWCOL_NOK if result else self._ROWCOL_OK
intervalsSt[intervalIdx][7] = self._ROWSTYLE_NOK if result else self._ROWSTYLE_OK
# we have a problem
if result:
# set ofending row and finish up
errIdx = rIdx
# interval is not ok, remove id
intervalsSt[rIdx][0] = -1
# exit on first error
break
else:
# assingn an id
intervalsSt[intervalIdx][0] = intervalIdx
# scroll to first error
if result:
# set status message if fail
if intervalOverlaps:
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_INTERVAL_OVERLAP_DETECTED"))
elif intervalHourConflictStart:
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_INTERVALSTART_CONFLICT_DETECTED"))
elif intervalHourConflictEnd:
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_INTERVALEND_CONFLICT_DETECTED"))
elif intervalDuplicate:
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_INTERVAL_DUPLICATE_DETECTED"))
elif intervalSameStartEnd:
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_INTERVAL_STARTENDEQUAL_DETECTED"))
elif intervalStartEndMismatch:
self.setTimekprStatus(False, msg.getTranslation("TK_MSG_STATUS_INTERVAL_ENDLESSTHANSTART_DETECTED"))
else:
# sort
self.sortHourIntervals()
self.setTimekprStatus(False, "")
# scroll
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsTreeView").set_cursor(errIdx)
self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsTreeView").scroll_to_cell(errIdx)
# fin
break
if not result:
# sort intervals
self.sortHourIntervals()
# rebuild hour intervals
self.rebuildHoursFromIntervals()
# calculate control availability
self.calculateUserConfigControlAvailability()
def rebuildHoursFromIntervals(self):
"""Rebuild hours from intervals in GUI, representation to user is different than actual config"""
# get days
days = self.getSelectedDays()
# day is here
if len(days) > 0:
# first day
day = days[0]["nr"]
# clear internal hour representation
self._tkSavedCfg["timeLimitDaysHoursActual"][day] = {}
# remove selected item
for rIt in self._timekprAdminFormBuilder.get_object("TimekprHourIntervalsLS"):
# start time
calcTime = cons.TK_DATETIME_START + timedelta(seconds=rIt[4])
# total seconds
totalSeconds = rIt[5] - rIt[4]
# failover
maxIt = 30
# now loop through time in interval
while totalSeconds > 0 and maxIt > 0:
# failover
maxIt -= 1
# hour
calcHour = str(calcTime.hour)
# build up hour
self._tkSavedCfg["timeLimitDaysHoursActual"][day][calcHour] = {cons.TK_CTRL_SMIN: calcTime.minute, cons.TK_CTRL_EMIN: None, cons.TK_CTRL_UACC: rIt[8]}
# calc end of the hour
timeToSubtract = min(cons.TK_LIMIT_PER_HOUR - calcTime.minute * cons.TK_LIMIT_PER_MINUTE, totalSeconds)
# adjust time
calcTime += timedelta(seconds=timeToSubtract)
# subtract hour
totalSeconds -= timeToSubtract
# add end hour
self._tkSavedCfg["timeLimitDaysHoursActual"][day][calcHour][cons.TK_CTRL_EMIN] = cons.TK_LIMIT_PER_MINUTE if calcTime.minute == 0 else calcTime.minute
# set this up to all selected rows
for rDay in days:
# set to all days (except the one we modified)
if rDay["nr"] != day:
# copy config
self._tkSavedCfg["timeLimitDaysHoursActual"][rDay["nr"]] = self._tkSavedCfg["timeLimitDaysHoursActual"][day].copy()
# --------------- additional and misc methods --------------- #
def timekprLogoClicked(self, evt, smth):
"""Open link to development support page, disabled, and maybe never will be enabled :)"""
if 1 == 1:
pass
elif os.geteuid() == 0:
# copy to clipboard and show message
Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD).set_text(cons.TK_DEV_SUPPORT_PAGE, -1)
tkrMsg = Gtk.MessageDialog(parent=self._timekprAdminForm, flags=Gtk.DialogFlags.MODAL, type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK, message_format="\nDonations link copied to clipbard!\nPlease paste the address in internet browser.\nThanks for your support!")
tkrMsg.run()
tkrMsg.destroy()
else:
# open link
webbrowser.open(cons.TK_DEV_SUPPORT_PAGE, new=2, autoraise=True)
def closePropertiesSignal(self, evt, smth):
"""Close the config form"""
# close
self._mainLoop.quit()
# flush log
log.autoFlushLogFile(True)
timekpr-next/client/gui/__init__.py 000664 001750 001750 00000000000 13476006650 021345 0 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/client/gui/clientgui.py 000644 001750 001750 00000113476 15122253261 021605 0 ustar 00bezvfedu bezvfedu 000000 000000 """
Created on Aug 28, 2018
@author: mjasnik
"""
import gi
import os
import re
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from datetime import datetime, timedelta
# timekpr imports
from timekpr.common.constants import constants as cons
from timekpr.common.log import log
from timekpr.common.constants import messages as msg
# constant
_NO_TIME_LABEL = "--:--:--"
_NO_TIME_LABEL_SHORT = "--:--"
_NO_TIME_LIMIT_LABEL = "--:--:--:--"
_HOUR_REGEXP = re.compile("^([0-9]{1,2})$")
_HOUR_MIN_REGEXP = re.compile("^([0-9]{1,2}):([0-9]{1,2})$")
class timekprGUI(object):
"""Main class for supporting timekpr forms"""
def __init__(self, pTimekprVersion, pTimekprClientConfig, pUsername, pUserNameFull):
"""Initialize gui"""
# set up base variables
self._userName = pUsername
self._timekprVersion = pTimekprVersion
self._timekprClientConfig = pTimekprClientConfig
self._timekprPTPageNr = 2
# sets up limit variables
self._timeSpent = None
self._timeSpentWeek = None
self._timeSpentMonth = None
self._timeInactive = None
self._timeLeftToday = None
self._timeLeftContinous = None
self._timeTrackInactive = True
self._timeTimeLimitOverridePT = False
self._timeUnaccountedIntervalsFlagPT = False
self._timeSpentPT = None
self._timeLeftPT = None
self._timePTActivityCntStr = "0"
self._limitConfig = {}
# change tracking
self._configChanged = False
# ## forms builders ##
# init about builder
self._timekprAboutDialogBuilder = Gtk.Builder()
# get our dialog
self._timekprAboutDialogBuilder.add_from_file(os.path.join(self._timekprClientConfig.getTimekprSharedDir(), "client/forms", "about.glade"))
# get main form (to set various runtime things)
self._timekprAboutDialog = self._timekprAboutDialogBuilder.get_object("timekprAboutDialog")
# init config builder
self._timekprConfigDialogBuilder = Gtk.Builder()
# get our dialog
self._timekprConfigDialogBuilder.add_from_file(os.path.join(self._timekprClientConfig.getTimekprSharedDir(), "client/forms", "client.glade"))
# get main form (to set various runtime things)
self._timekprConfigDialog = self._timekprConfigDialogBuilder.get_object("timekprConfigDialog")
self._timekprAboutDialogBuilder.connect_signals(self)
self._timekprConfigDialogBuilder.connect_signals(self)
# set up username (this does not change)
self._timekprConfigDialogBuilder.get_object("timekprUsernameLB").set_text("%s (%s)" % (self._userName, pUserNameFull) if pUserNameFull != "" else self._userName)
# this sets up columns for days config
col = Gtk.TreeViewColumn("Day", Gtk.CellRendererText(), text=1)
col.set_min_width(100)
self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysDaysTreeview").append_column(col)
col = Gtk.TreeViewColumn("Limit", Gtk.CellRendererText(), text=2)
col.set_min_width(60)
self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysDaysTreeview").append_column(col)
# this sets up columns for interval list
# interval string
col = Gtk.TreeViewColumn("Interval", Gtk.CellRendererText(), text=0)
self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysIntervalsTreeview").append_column(col)
# unaccountable interval column
col = Gtk.TreeViewColumn("Unaccounted", Gtk.CellRendererText(), text=1)
col.set_min_width(10)
self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysIntervalsTreeview").append_column(col)
# PlayTime
# this sets up columns for limits list
col = Gtk.TreeViewColumn("Day", Gtk.CellRendererText(), text=1)
col.set_min_width(100)
self._timekprConfigDialogBuilder.get_object("timekprPTAllowedDaysLimitsDaysTreeview").append_column(col)
col = Gtk.TreeViewColumn("Limit", Gtk.CellRendererText(), text=2)
col.set_min_width(60)
self._timekprConfigDialogBuilder.get_object("timekprPTAllowedDaysLimitsDaysTreeview").append_column(col)
# this sets up columns for process list
col = Gtk.TreeViewColumn("Day", Gtk.CellRendererText(), text=0)
col.set_min_width(140)
self._timekprConfigDialogBuilder.get_object("timekprPTAllowedDaysLimitsApplsTreeview").append_column(col)
# hide PT page by default
self._timekprConfigDialogBuilder.get_object("timekprConfigNotebook").get_nth_page(self._timekprPTPageNr).set_visible(False)
# hide PT config as well
self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigPlayTimeGrid").set_visible(False)
# initial config (everything is to the max)
for i in range(0, 7):
# set up default limits
self._limitConfig[str(i+1)] = {cons.TK_CTRL_LIMITD: None, cons.TK_CTRL_INT: [[None, None, False]]}
# initialize week and month limits
self._limitConfig[cons.TK_CTRL_LIMITW] = {cons.TK_CTRL_LIMITW: None}
self._limitConfig[cons.TK_CTRL_LIMITM] = {cons.TK_CTRL_LIMITM: None}
# ## notification configuration ##
# Less than
rend = Gtk.CellRendererText()
rend.set_property("editable", True)
rend.set_property("placeholder-text", msg.getTranslation("TK_MSG_NOTIF_CONFIG_TIME_PHLD_LABEL"))
rend.connect("edited", self.userTimeEdited)
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_NOTIF_CONFIG_TIME_LABEL"), rend, text=1)
col.set_min_width(90)
self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigTreeView").append_column(col)
# importance
rend = Gtk.CellRendererCombo()
rend.set_property("editable", True)
rend.set_property("placeholder-text", msg.getTranslation("TK_MSG_NOTIF_CONFIG_IMPORTANCE_PHLD_LABEL"))
rend.set_property("model", self._timekprConfigDialogBuilder.get_object("TimekprNotificationPrioritiesLS"))
rend.set_property("text-column", 1)
rend.set_property("has-entry", False)
rend.connect("edited", self.userPriorityEdited)
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_NOTIF_CONFIG_IMPORTANCE_LABEL"), rend, text=3)
col.set_min_width(120)
self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigTreeView").append_column(col)
# clear
self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigLS").clear()
# ## PlayTime notification configuration ##
# Less than
rend = Gtk.CellRendererText()
rend.set_property("editable", True)
rend.set_property("placeholder-text", msg.getTranslation("TK_MSG_NOTIF_CONFIG_TIME_PHLD_LABEL"))
rend.connect("edited", self.userPlayTimeEdited)
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_NOTIF_CONFIG_TIME_LABEL"), rend, text=1)
col.set_min_width(90)
self._timekprConfigDialogBuilder.get_object("TimekprUserPlayTimeNotificationConfigTreeView").append_column(col)
# importance
rend = Gtk.CellRendererCombo()
rend.set_property("editable", True)
rend.set_property("placeholder-text", msg.getTranslation("TK_MSG_NOTIF_CONFIG_IMPORTANCE_PHLD_LABEL"))
rend.set_property("model", self._timekprConfigDialogBuilder.get_object("TimekprNotificationPrioritiesLS"))
rend.set_property("text-column", 1)
rend.set_property("has-entry", False)
rend.connect("edited", self.userPlayTimePriorityEdited)
col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_NOTIF_CONFIG_IMPORTANCE_LABEL"), rend, text=3)
col.set_min_width(120)
self._timekprConfigDialogBuilder.get_object("TimekprUserPlayTimeNotificationConfigTreeView").append_column(col)
# clear
self._timekprConfigDialogBuilder.get_object("TimekprUserPlayTimeNotificationConfigLS").clear()
# status
self.setStatus(msg.getTranslation("TK_MSG_STATUS_STARTED"))
# --------------- TMP (move to proper places later) --------------- #
def userTimeEdited(self, widget, path, text):
"""Set internal representation of in-place edited value"""
self.setTimeValue(path, text, pConfType="Time")
def userPlayTimeEdited(self, widget, path, text):
"""Set internal representation of in-place edited value"""
self.setTimeValue(path, text, pConfType="PlayTime")
def userPriorityEdited(self, widget, path, text):
"""Set internal representation of in-place edited value"""
self.setPriorityValue(path, text, "Time")
def userPlayTimePriorityEdited(self, widget, path, text):
"""Set internal representation of in-place edited value"""
self.setPriorityValue(path, text, "PlayTime")
def setTimeValue(self, path, text, pConfType):
"""Verify and set time string values"""
# element
prioLs = "TimekprUserNotificationConfigLS" if pConfType == "Time" else "TimekprUserPlayTimeNotificationConfigLS"
# store
timelSt = self._timekprConfigDialogBuilder.get_object(prioLs)
# value before
secsBefore = timelSt[path][0]
secs = None
# verify values
if _HOUR_REGEXP.match(text):
# calculate seconds
secs = min(int(_HOUR_REGEXP.sub(r"\1", text)) * cons.TK_LIMIT_PER_HOUR, cons.TK_LIMIT_PER_DAY)
elif _HOUR_MIN_REGEXP.match(text):
# calculate seconds
secs = min(int(_HOUR_MIN_REGEXP.sub(r"\1", text)) * cons.TK_LIMIT_PER_HOUR + int(_HOUR_MIN_REGEXP.sub(r"\2", text)) * cons.TK_LIMIT_PER_MINUTE, cons.TK_LIMIT_PER_DAY)
# if we could calculate seconds (i.e. entered text is correct)
if secs is not None:
# only if changed
if secsBefore != secs:
# check if we have this interval already
dupl = [rPrio for rPrio in timelSt if rPrio[0] == secs]
# we can not allow duplicates
if not len(dupl) > 0:
# format secs
textStr = self.formatTimeStr(cons.TK_DATETIME_START + timedelta(seconds=secs), "s")
# set values
timelSt[path][0] = secs
timelSt[path][1] = textStr
# sort
self.sortNotificationConfig(pConfType)
# verify controls too
self.processConfigChanged()
def setPriorityValue(self, path, text, pConfType):
"""Verify and set time string values"""
# element
prioLs = "TimekprUserNotificationConfigLS" if pConfType == "Time" else "TimekprUserPlayTimeNotificationConfigLS"
# store
priolSt = self._timekprConfigDialogBuilder.get_object(prioLs)
# value before
prioBefore = priolSt[path][3]
# only if priority actuall changed
if prioBefore != text:
# find selected value
val = [(rVal[0], rVal[1]) for rVal in self._timekprConfigDialogBuilder.get_object("TimekprNotificationPrioritiesLS") if rVal[1] == text]
# set values
priolSt[path][2] = val[0][0]
priolSt[path][3] = val[0][1]
# verify controls too
self.processConfigChanged()
def addNotificationConfigClicked(self, evt):
"""Add notification interval to the list"""
self.addNotificationConf("Time")
def addPlayTimeNotificationConfigClicked(self, evt):
"""Add notification interval to the list"""
self.addNotificationConf("PlayTime")
def removeNotificationConfigClicked(self, evt):
"""Remove notification interval"""
self.removeNotificationConf("Time")
def removePlayTimeNotificationConfigClicked(self, evt):
"""Remove notification interval"""
self.removeNotificationConf("PlayTime")
def addNotificationConf(self, pConfType):
"""Add notification interval to the list"""
prioSt = self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigLS" if pConfType == "Time" else "TimekprUserPlayTimeNotificationConfigLS")
prioTw = self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigTreeView" if pConfType == "Time" else "TimekprUserPlayTimeNotificationConfigTreeView")
prioLen = len(prioSt)
# add
addRow = True
# check if the last one is not empty (no need to add more empty rows)
if (prioLen > 0 and prioSt[prioLen-1][0] < 0):
addRow = False
# we can add the row
if addRow:
# add
prioSt.append([-1, "", "", ""])
# scroll to end
prioTw.set_cursor(prioLen)
prioTw.scroll_to_cell(prioLen)
def removeNotificationConf(self, pConfType):
"""Remove notification interval"""
# defaults
prioSt = self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigLS" if pConfType == "Time" else "TimekprUserPlayTimeNotificationConfigLS")
# refresh the child
(tm, ti) = self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigTreeView" if pConfType == "Time" else "TimekprUserPlayTimeNotificationConfigTreeView").get_selection().get_selected()
# only if there is smth selected
elemIdx = tm.get_path(ti)[0] if ti is not None else None
# only if something is selected
if elemIdx is not None:
rIdx = 0
# remove selected item
for rIt in prioSt:
if elemIdx == rIdx:
# remove
prioSt.remove(rIt.iter)
# count further
rIdx += 1
# verify controls too
self.processConfigChanged()
def sortNotificationConfig(self, pConfType):
"""Sort notification config for ease of use"""
# element
prioSt = self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigLS" if pConfType == "Time" else "TimekprUserPlayTimeNotificationConfigLS")
# sort vairables
prio = {}
rIdx = 0
# prepare sort
for rIt in prioSt:
prio[rIt[0]] = rIdx
# count further
rIdx += 1
# set sort order
sortedPrio = []
# set up proper order
for rKey in sorted(prio, reverse=True):
# append to order
sortedPrio.append(prio[rKey])
# reorder rows in liststore
prioSt.reorder(sortedPrio)
# --------------- helper methods --------------- #
def formatTimeStr(self, pTime, pFormatType="f"):
"""Format time for output on form"""
# f - full, s - short, t - time
# final result
timeStr = None
if pTime is None:
timeStr = _NO_TIME_LABEL_SHORT if pFormatType == "s" else _NO_TIME_LABEL if pFormatType == "t" else _NO_TIME_LIMIT_LABEL
else:
# calculate days
days = (pTime - cons.TK_DATETIME_START).days
# calculate hours and mins
hrMin = "%s:%s" % (("24" if pFormatType != "f" and days >= 1 else str(pTime.hour)).rjust(2, "0"), str(pTime.minute).rjust(2, "0"))
# calculate secs
secs = str(pTime.second).rjust(2, "0")
# final composition
# for limit time (h:m:s)
if pFormatType == "t":
timeStr = "%s:%s" % (hrMin, secs)
# for full time (d:h:m:s)
else:
timeStr = "%s:%s:%s" % (str(days).rjust(2, "0"), hrMin, secs) if pFormatType != "s" else hrMin
# return
return timeStr
def renewUserConfiguration(self):
"""Update configuration options"""
# if speech is not supported, we disable and uncheck the box
self._timekprConfigDialogBuilder.get_object("timekprUseSpeechNotifCB").set_sensitive(self._timekprClientConfig.getIsNotificationSpeechSupported())
# if sound is not supported by libnotify implementation, we disable and uncheck the box
self._timekprConfigDialogBuilder.get_object("timekprUseNotificationSoundCB").set_sensitive(self._timekprClientConfig.getIsNotificationSoundSupported())
# user config
self._timekprConfigDialogBuilder.get_object("timekprLimitChangeNotifCB").set_active(self._timekprClientConfig.getClientShowLimitNotifications())
self._timekprConfigDialogBuilder.get_object("timekprShowAllNotifCB").set_active(self._timekprClientConfig.getClientShowAllNotifications())
self._timekprConfigDialogBuilder.get_object("timekprUseSpeechNotifCB").set_active(self._timekprClientConfig.getClientUseSpeechNotifications())
self._timekprConfigDialogBuilder.get_object("timekprShowSecondsCB").set_active(self._timekprClientConfig.getClientShowSeconds())
self._timekprConfigDialogBuilder.get_object("timekprUseNotificationSoundCB").set_active(self._timekprClientConfig.getClientUseNotificationSound())
self._timekprConfigDialogBuilder.get_object("timekprNotificationTimeoutSB").set_value(self._timekprClientConfig.getClientNotificationTimeout())
self._timekprConfigDialogBuilder.get_object("timekprNotificationTimeoutCriticalSB").set_value(self._timekprClientConfig.getClientNotificationTimeoutCritical())
self._timekprConfigDialogBuilder.get_object("timekprLogLevelSB").set_value(self._timekprClientConfig.getClientLogLevel())
# priority config
prioConfSt = self._timekprConfigDialogBuilder.get_object("TimekprNotificationPrioritiesLS")
# load notification priorities
prioSt = self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigLS")
prioSt.clear()
for rPrio in self._timekprClientConfig.getClientNotificationLevels():
# append intervals
val = [(rVal[0], rVal[1]) for rVal in prioConfSt if rVal[0] == cons.TK_PRIO_LVL_MAP[rPrio[1]]]
prioSt.append([rPrio[0], self.formatTimeStr(cons.TK_DATETIME_START + timedelta(seconds=rPrio[0]), "s"), val[0][0], val[0][1]])
# sort configd
self.sortNotificationConfig("Time")
# load PlayTime notification priorities
prioSt = self._timekprConfigDialogBuilder.get_object("TimekprUserPlayTimeNotificationConfigLS")
prioSt.clear()
for rPrio in self._timekprClientConfig.getClientPlayTimeNotificationLevels():
# append intervals
val = [(rVal[0], rVal[1]) for rVal in prioConfSt if rVal[0] == cons.TK_PRIO_LVL_MAP[rPrio[1]]]
prioSt.append([rPrio[0], self.formatTimeStr(cons.TK_DATETIME_START + timedelta(seconds=rPrio[0]), "s"), val[0][0], val[0][1]])
# sort config
self.sortNotificationConfig("PlayTime")
# verify controls too
self.processConfigChanged()
def renewLimits(self, pTimeInformation=None):
"""Renew information to be show for user in GUI"""
# sets time left
if pTimeInformation is not None:
# limits
self._timeSpent = cons.TK_DATETIME_START + timedelta(seconds=pTimeInformation[cons.TK_CTRL_SPENT])
self._timeSpentWeek = cons.TK_DATETIME_START + timedelta(seconds=pTimeInformation[cons.TK_CTRL_SPENTW])
self._timeSpentMonth = cons.TK_DATETIME_START + timedelta(seconds=pTimeInformation[cons.TK_CTRL_SPENTM])
self._timeInactive = cons.TK_DATETIME_START + timedelta(seconds=pTimeInformation[cons.TK_CTRL_SLEEP])
self._timeLeftToday = cons.TK_DATETIME_START + timedelta(seconds=pTimeInformation[cons.TK_CTRL_LEFTD])
self._timeLeftContinous = cons.TK_DATETIME_START + timedelta(seconds=pTimeInformation[cons.TK_CTRL_LEFT])
self._timeTrackInactive = True if pTimeInformation[cons.TK_CTRL_TRACK] else False
self._timeTimeLimitOverridePT = bool(pTimeInformation[cons.TK_CTRL_PTTLO]) if cons.TK_CTRL_PTTLO in pTimeInformation else False
self._timeUnaccountedIntervalsFlagPT = bool(pTimeInformation[cons.TK_CTRL_PTAUH]) if cons.TK_CTRL_PTAUH in pTimeInformation else False
self._timeSpentPT = cons.TK_DATETIME_START + timedelta(seconds=pTimeInformation[cons.TK_CTRL_PTSPD]) if cons.TK_CTRL_PTSPD in pTimeInformation else None
self._timeLeftPT = cons.TK_DATETIME_START + timedelta(seconds=pTimeInformation[cons.TK_CTRL_PTLPD]) if cons.TK_CTRL_PTLPD in pTimeInformation else None
self._timePTActivityCntStr = str(pTimeInformation[cons.TK_CTRL_PTLSTC] if cons.TK_CTRL_PTLSTC in pTimeInformation else 0)
# calculate strings to show (and show only those, which have data)
timeSpentStr = self.formatTimeStr(self._timeSpent)
timeSpentWeekStr = self.formatTimeStr(self._timeSpentWeek)
timeSpentMonthStr = self.formatTimeStr(self._timeSpentMonth)
timeSleepStr = self.formatTimeStr(self._timeInactive)
timeLeftTodayStr = self.formatTimeStr(self._timeLeftToday)
timeLeftTotalStr = self.formatTimeStr(self._timeLeftContinous)
timeSpentPTStr = self.formatTimeStr(self._timeSpentPT)
timeLeftPTStr = self.formatTimeStr(self._timeLeftPT if self._timeLeftPT is None else min(self._timeLeftPT, self._timeLeftToday)) if not self._timeTimeLimitOverridePT else _NO_TIME_LABEL
# sets up stuff
self._timekprConfigDialogBuilder.get_object("timekprLimitInfoTimeSpeneLB").set_text(timeSpentStr)
self._timekprConfigDialogBuilder.get_object("timekprLimitInfoTimeSpentWeeeLB").set_text(timeSpentWeekStr)
self._timekprConfigDialogBuilder.get_object("timekprLimitInfoTimeSpentMonteLB").set_text(timeSpentMonthStr)
self._timekprConfigDialogBuilder.get_object("timekprLimitInfoTimeInactiveLB").set_text(timeSleepStr)
self._timekprConfigDialogBuilder.get_object("timekprLimitInfoTimeLeftTodaeLB").set_text(timeLeftTodayStr)
self._timekprConfigDialogBuilder.get_object("timekprLimitInfoContTimeLefeLB").set_text(timeLeftTotalStr)
self._timekprConfigDialogBuilder.get_object("timekprLimitInfoTrackInactiveCB").set_active(self._timeTrackInactive)
self._timekprConfigDialogBuilder.get_object("timekprPTLimitInfoTimeLimitOverrideLB").set_active(self._timeTimeLimitOverridePT)
self._timekprConfigDialogBuilder.get_object("timekprPTLimitInfoUnaccountedIntervalsFlagLB").set_active(self._timeUnaccountedIntervalsFlagPT)
self._timekprConfigDialogBuilder.get_object("timekprPTLimitInfoTimeSpentTodayLB").set_text(timeSpentPTStr)
self._timekprConfigDialogBuilder.get_object("timekprPTLimitInfoTimeLeftTodayLB").set_text(timeLeftPTStr)
self._timekprConfigDialogBuilder.get_object("timekprPTLimitInfoActivityCountLB").set_text(self._timePTActivityCntStr)
def setStatus(self, pStatus):
"""Change status of timekpr"""
if pStatus is not None:
# get main status
statusBar = self._timekprConfigDialogBuilder.get_object("timekprStatusBar")
contextId = statusBar.get_context_id("status")
# pop existing message and add new one
statusBar.remove_all(contextId)
statusBar.push(contextId, pStatus)
def renewLimitConfiguration(self, pLimits=None):
"""Renew information to be show for user"""
# if there is smth
if pLimits is not None:
# new limits appeared
self._limitConfig = pLimits
# hide PT page by default
enablePT = False
# clear out days / limits / processes
self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysDaysLS").clear()
self._timekprConfigDialogBuilder.get_object("timekprPTAllowedDaysLimitsDaysLS").clear()
self._timekprConfigDialogBuilder.get_object("timekprPTAllowedDaysLimitsActsLS").clear()
# go in sorted order
for rKey in sorted(self._limitConfig):
# some of configuration needs different approach
if rKey in (cons.TK_CTRL_LIMITW, cons.TK_CTRL_LIMITM):
# set locally
if self._limitConfig[rKey][rKey] is not None:
# limit
timeLimitWKMON = cons.TK_DATETIME_START + timedelta(seconds=self._limitConfig[rKey][rKey])
# limit
timeLimitWKMONStr = self.formatTimeStr(timeLimitWKMON)
else:
timeLimitWKMONStr = _NO_TIME_LIMIT_LABEL
# set week limit
if rKey == cons.TK_CTRL_LIMITW:
# set up limits
self._timekprConfigDialogBuilder.get_object("timekprLimitForWeeeLB").set_text(timeLimitWKMONStr)
elif rKey == cons.TK_CTRL_LIMITM:
# set up limits
self._timekprConfigDialogBuilder.get_object("timekprLimitForMonteLB").set_text(timeLimitWKMONStr)
# check for override
elif rKey == cons.TK_CTRL_PTTLO:
# if enabled
self._timeTimeLimitOverridePT = True if bool(self._limitConfig[rKey][rKey]) else False
# check for allowed during unaccounted intervals
elif rKey == cons.TK_CTRL_PTAUH:
# if enabled
self._timeUnaccountedIntervalsFlagPT = True if bool(self._limitConfig[rKey][rKey]) else False
# for the days limits
elif rKey in ("1", "2", "3", "4", "5", "6", "7"):
# get time limit string
timeLimitStr = self.formatTimeStr(cons.TK_DATETIME_START + timedelta(seconds=self._limitConfig[rKey][cons.TK_CTRL_LIMITD]) if self._limitConfig[rKey][cons.TK_CTRL_LIMITD] is not None else None, "t")
# add limit to the list
self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysDaysLS").append([rKey, (cons.TK_DATETIME_START + timedelta(days=int(rKey)-1)).strftime("%A"), "%s" % (timeLimitStr)])
# current day
currDay = datetime.now().isoweekday()-1
# calculate day index for scrolling
dayIdx = 0
finalDayIdx = None
# PT limits are processed separately due to override detection
if cons.TK_CTRL_PTLMT in self._limitConfig and cons.TK_CTRL_PTLST in self._limitConfig and cons.TK_CTRL_PTTLE in self._limitConfig:
# PlayTime
for rKey in (cons.TK_CTRL_PTLMT, cons.TK_CTRL_PTLST, cons.TK_CTRL_PTTLE):
# PT enable
if rKey == cons.TK_CTRL_PTTLE:
# enable PT
enablePT = bool(self._limitConfig[rKey][cons.TK_CTRL_PTTLE])
# PT limits
elif rKey == cons.TK_CTRL_PTLMT:
# for all days
for rDay in self._limitConfig[rKey][cons.TK_CTRL_PTLMT]:
# count
dayIdx += 1
# if override enabled, we do not show limits because that's not meaningful
timeLimitStr = self.formatTimeStr(cons.TK_DATETIME_START + timedelta(seconds=int(rDay[1])) if not self._timeTimeLimitOverridePT else None, "t")
# add to the list
self._timekprConfigDialogBuilder.get_object("timekprPTAllowedDaysLimitsDaysLS").append([str(rDay[0]), (cons.TK_DATETIME_START + timedelta(days=int(rDay[0])-1)).strftime("%A"), "%s" % (timeLimitStr)])
# if alllowed list has current day
if currDay == int(rDay[0]) + 1:
# index
finalDayIdx = dayIdx
# PT process list
elif rKey == cons.TK_CTRL_PTLST:
# all activities (source array format: 0 - friendly name, 1 - process name)
for rAppl in self._limitConfig[rKey][cons.TK_CTRL_PTLST]:
# add process to the list
self._timekprConfigDialogBuilder.get_object("timekprPTAllowedDaysLimitsActsLS").append(["%s" % (rAppl[1] if rAppl[1] != "" else rAppl[0])])
# determine curent day and point to it
self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysDaysTreeview").set_cursor(currDay)
self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysDaysTreeview").scroll_to_cell(currDay)
# do the same for PT
if finalDayIdx is not None:
# scroll to current day
self._timekprConfigDialogBuilder.get_object("timekprPTAllowedDaysLimitsDaysTreeview").set_cursor(currDay)
self._timekprConfigDialogBuilder.get_object("timekprPTAllowedDaysLimitsDaysTreeview").scroll_to_cell(currDay)
def processConfigChanged(self):
"""Determine whether config has been changed and enable / disable apply"""
# initial
configChanged = False
# determine what's changed
configChanged = configChanged or self._timekprConfigDialogBuilder.get_object("timekprLimitChangeNotifCB").get_active() != self._timekprClientConfig.getClientShowLimitNotifications()
configChanged = configChanged or self._timekprConfigDialogBuilder.get_object("timekprShowAllNotifCB").get_active() != self._timekprClientConfig.getClientShowAllNotifications()
configChanged = configChanged or self._timekprConfigDialogBuilder.get_object("timekprUseSpeechNotifCB").get_active() != self._timekprClientConfig.getClientUseSpeechNotifications()
configChanged = configChanged or self._timekprConfigDialogBuilder.get_object("timekprShowSecondsCB").get_active() != self._timekprClientConfig.getClientShowSeconds()
configChanged = configChanged or self._timekprConfigDialogBuilder.get_object("timekprUseNotificationSoundCB").get_active() != self._timekprClientConfig.getClientUseNotificationSound()
configChanged = configChanged or self._timekprConfigDialogBuilder.get_object("timekprNotificationTimeoutSB").get_value_as_int() != self._timekprClientConfig.getClientNotificationTimeout()
configChanged = configChanged or self._timekprConfigDialogBuilder.get_object("timekprNotificationTimeoutCriticalSB").get_value_as_int() != self._timekprClientConfig.getClientNotificationTimeoutCritical()
configChanged = configChanged or self._timekprConfigDialogBuilder.get_object("timekprLogLevelSB").get_value_as_int() != self._timekprClientConfig.getClientLogLevel()
# interval changes
tmpVal = [[rVal[0], cons.TK_PRIO_LVL_MAP[rVal[2]]] for rVal in self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigLS") if rVal[2] in cons.TK_PRIO_LVL_MAP and rVal[0] > 0]
configChanged = configChanged or self._timekprClientConfig.getClientNotificationLevels() != tmpVal
# interval changes
tmpVal = [[rVal[0], cons.TK_PRIO_LVL_MAP[rVal[2]]] for rVal in self._timekprConfigDialogBuilder.get_object("TimekprUserPlayTimeNotificationConfigLS") if rVal[2] in cons.TK_PRIO_LVL_MAP and rVal[0] > 0]
configChanged = configChanged or self._timekprClientConfig.getClientPlayTimeNotificationLevels() != tmpVal
# this is it
self._timekprConfigDialogBuilder.get_object("timekprSaveBT").set_sensitive(configChanged)
# --------------- init methods --------------- #
def initAboutForm(self):
"""Initialize about form"""
# version
self._timekprAboutDialog.set_version(self._timekprVersion)
# comment
self._timekprAboutDialog.set_comments(msg.getTranslation("TK_MSG_LOGO_LABEL"))
# show up all
self._timekprAboutDialog.show()
self._timekprAboutDialog.run()
# hide for later use
self._timekprAboutDialog.hide()
def initConfigForm(self):
"""Initialize config form"""
# refresh info
self.renewUserConfiguration()
self.renewLimits()
self.renewLimitConfiguration()
self.configPageSwitchSignal()
# show up all
self._timekprConfigDialog.show()
self._timekprConfigDialog.run()
# hide for later use
self._timekprConfigDialog.hide()
# --------------- user clicked methods --------------- #
def clientConfigChangedSignal(self, evt):
"""Process config changed signal"""
self.processConfigChanged()
def daysChangedSignal(self, evt):
"""Refresh intervals when days change"""
# refresh the child
(tm, ti) = self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysDaysTreeview").get_selection().get_selected()
# only if there is smth selected
if ti is not None:
# get current seconds
dt = datetime.now().replace(microsecond=0)
dtd = str(datetime.date(dt).isoweekday())
dts = int((dt - datetime.now().replace(microsecond=0, second=0, minute=0, hour=0)).total_seconds())
idx = 0
selIdx = 0
# clear out existing intervals
self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysIntervalsLS").clear()
# fill intervals only if that day exists
if tm.get_value(ti, 0) in self._limitConfig:
# if no intervals
if not self._limitConfig[tm.get_value(ti, 0)][cons.TK_CTRL_INT]:
# fill in the intervals with empty values
self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysIntervalsLS").append([("%s - %s") % (_NO_TIME_LABEL_SHORT, _NO_TIME_LABEL_SHORT), ""])
else:
# fill the intervals
for r in self._limitConfig[tm.get_value(ti, 0)][cons.TK_CTRL_INT]:
# determine which is the current hour
selIdx = idx if r[0] is not None and r[0] <= dts <= r[1] and dtd == tm.get_value(ti, 0) else selIdx
# if we have no data, we fill this up with nothing
if r[0] is None or (r[0] == 0 and r[1] == 0):
# fill in the intervals with empty values
self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysIntervalsLS").append([("%s - %s") % (_NO_TIME_LABEL_SHORT, _NO_TIME_LABEL_SHORT), ""])
else:
start = (cons.TK_DATETIME_START + timedelta(seconds=r[0]))
end = (cons.TK_DATETIME_START + timedelta(seconds=r[1]))
uacc = "∞" if r[2] else ""
# fill in the intervals
self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysIntervalsLS").append([("%s:%s - %s:%s") % (str(start.hour).rjust(2, "0"), str(start.minute).rjust(2, "0"), str(end.hour).rjust(2, "0") if r[1] < cons.TK_LIMIT_PER_DAY else "24", str(end.minute).rjust(2, "0")), uacc])
# count
idx += 1
# set selection to found row
self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysIntervalsTreeview").set_cursor(selIdx)
self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysIntervalsTreeview").scroll_to_cell(selIdx)
def configPageSwitchSignal(self, nb=None, pg=None, pgn=None):
"""Enable or disable apply on page change"""
# nothing here
pass
def saveUserConfigSignal(self, evt):
"""Save the configuration using config file manager"""
# get config, set config to manager and save it
self._timekprClientConfig.setClientShowLimitNotifications(self._timekprConfigDialogBuilder.get_object("timekprLimitChangeNotifCB").get_active())
self._timekprClientConfig.setClientShowAllNotifications(self._timekprConfigDialogBuilder.get_object("timekprShowAllNotifCB").get_active())
self._timekprClientConfig.setClientUseSpeechNotifications(self._timekprConfigDialogBuilder.get_object("timekprUseSpeechNotifCB").get_active())
self._timekprClientConfig.setClientShowSeconds(self._timekprConfigDialogBuilder.get_object("timekprShowSecondsCB").get_active())
self._timekprClientConfig.setClientUseNotificationSound(self._timekprConfigDialogBuilder.get_object("timekprUseNotificationSoundCB").get_active())
self._timekprClientConfig.setClientNotificationTimeout(self._timekprConfigDialogBuilder.get_object("timekprNotificationTimeoutSB").get_value_as_int())
self._timekprClientConfig.setClientNotificationTimeoutCritical(self._timekprConfigDialogBuilder.get_object("timekprNotificationTimeoutCriticalSB").get_value_as_int())
self._timekprClientConfig.setClientLogLevel(self._timekprConfigDialogBuilder.get_object("timekprLogLevelSB").get_value_as_int())
# save notification priorities
tmpVal = [[rVal[0], cons.TK_PRIO_LVL_MAP[rVal[2]]] for rVal in self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigLS") if rVal[2] in cons.TK_PRIO_LVL_MAP and rVal[0] > 0]
self._timekprClientConfig.setClientNotificationLevels(tmpVal)
# save PlayTime notification priorities
tmpVal = [[rVal[0], cons.TK_PRIO_LVL_MAP[rVal[2]]] for rVal in self._timekprConfigDialogBuilder.get_object("TimekprUserPlayTimeNotificationConfigLS") if rVal[2] in cons.TK_PRIO_LVL_MAP and rVal[0] > 0]
self._timekprClientConfig.setClientPlayTimeNotificationLevels(tmpVal)
# save config
self._timekprClientConfig.saveClientConfig()
# disable apply for now
self._timekprConfigDialogBuilder.get_object("timekprSaveBT").set_sensitive(False)
# enable as well
log.setLogLevel(self._timekprClientConfig.getClientLogLevel())
def closePropertiesSignal(self, evt):
"""Close the config form"""
# close
self._timekprConfigDialog.hide()
def preventDestroyingDialogSignal(self, evt, bs):
"""Prevent destroying the dialog"""
return False
# --------------- helper methods --------------- #
def isPlayTimeAccountingInfoEnabled(self):
"""Whether PlayTime controls are enabled"""
return self._timekprConfigDialogBuilder.get_object("timekprConfigNotebook").get_nth_page(self._timekprPTPageNr).get_visible()
def setPlayTimeAccountingInfoEnabled(self, pState):
"""Whether PlayTime controls are enabled"""
# enable page
self._timekprConfigDialogBuilder.get_object("timekprConfigNotebook").get_nth_page(self._timekprPTPageNr).set_visible(pState)
# enable config
self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigPlayTimeGrid").set_visible(pState)
timekpr-next/client/timekprc.py 000600 001750 001750 00000001570 14575617135 020652 0 ustar 00bezvfedu bezvfedu 000000 000000 """
Created on Aug 28, 2018
@author: mjasnik
"""
# imports
import os
import getpass
import sys
import signal
# set up our python path
if "/usr/lib/python3/dist-packages" not in sys.path:
sys.path.append("/usr/lib/python3/dist-packages")
# timekpr imports
from timekpr.client.interface.dbus.daemon import timekprClient
from timekpr.common.utils import misc
# main start
if __name__ == "__main__":
# simple self-running check
if misc.checkAndSetRunning(os.path.splitext(os.path.basename(__file__))[0], getpass.getuser()):
# get out
sys.exit(0)
# get our client
_timekprClient = timekprClient()
# this is needed for appindicator to react to ctrl+c
signal.signal(signal.SIGINT, _timekprClient.finishTimekpr)
signal.signal(signal.SIGTERM, _timekprClient.finishTimekpr)
# start up timekpr client
_timekprClient.startTimekprClient()
timekpr-next/client/__init__.py 000664 001750 001750 00000000000 13476006650 020561 0 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/client/timekpra.py 000600 001750 001750 00000001676 14575617135 020657 0 ustar 00bezvfedu bezvfedu 000000 000000 """
Created on Jan 4, 2019
@author: mjasnik
"""
# imports
import os
import getpass
import sys
import signal
# set up our python path
if "/usr/lib/python3/dist-packages" not in sys.path:
sys.path.append("/usr/lib/python3/dist-packages")
# timekpr imports
from timekpr.client.admin.adminprocessor import timekprAdminClient
from timekpr.common.utils import misc
# main start
if __name__ == "__main__":
# simple self-running check
if misc.checkAndSetRunning(os.path.splitext(os.path.basename(__file__))[0], getpass.getuser()):
# get out
sys.exit(0)
# get our admin client
_timekprAdminClient = timekprAdminClient()
# this is needed for admin application to react to ctrl+c gracefully
signal.signal(signal.SIGINT, _timekprAdminClient.finishTimekpr)
signal.signal(signal.SIGTERM, _timekprAdminClient.finishTimekpr)
# start up timekpr admin client
_timekprAdminClient.startTimekprAdminClient(*sys.argv)
timekpr-next/client/admin/ 000775 001750 001750 00000000000 15122253261 017542 5 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/client/admin/adminprocessor.py 000644 001750 001750 00000077230 15122253261 023153 0 ustar 00bezvfedu bezvfedu 000000 000000 """
Created on Aug 28, 2018
@author: mjasnik
"""
# imports
import os
import getpass
from os import geteuid
# timekpr imports
from timekpr.common.constants import constants as cons
from timekpr.common.log import log
from timekpr.client.interface.dbus.administration import timekprAdminConnector
from timekpr.common.utils.config import timekprConfig
from timekpr.common.constants import messages as msg
from timekpr.common.utils.misc import findHourStartEndMinutes as findHourStartEndMinutes
from timekpr.common.utils.misc import splitConfigValueNameParam as splitConfigValueNameParam
from timekpr.common.utils.misc import getSecondsFromTimeStr
from timekpr.common.utils.misc import getTimeStrFromSeconds
class timekprAdminClient(object):
"""Main class for holding all client logic (including dbus)"""
# --------------- initialization / control methods --------------- #
def __init__(self):
"""Initialize admin client"""
# get our connector
self._timekprAdminConnector = timekprAdminConnector()
# main object for GUI
self._adminGUI = None
def startTimekprAdminClient(self, *args):
"""Start up timekpr admin (choose gui or cli and start this up)"""
# check whether we need CLI or GUI
lastParam = args[len(args)-1]
timekprForceCLI = False
# configuration init
_timekprConfig = timekprConfig()
# load config
_timekprConfig.loadMainConfiguration()
# init logging
log.setLogging(_timekprConfig.getTimekprLogLevel(), cons.TK_LOG_TEMP_DIR, (cons.TK_LOG_OWNER_ADMIN_SU if geteuid() == 0 else cons.TK_LOG_OWNER_ADMIN), getpass.getuser())
# check for script
if ("/timekpra" in lastParam or "timekpra.py" in lastParam):
# whether we have X running or wayland?
timekprX11Available = os.getenv("DISPLAY") is not None
timekprWaylandAvailable = os.getenv("WAYLAND_DISPLAY") is not None
timekprMirAvailable = os.getenv("MIR_SOCKET") is not None
# if we are required to run graphical thing
if (timekprX11Available or timekprWaylandAvailable or timekprMirAvailable):
# resource dir
_resourcePathGUI = os.path.join(_timekprConfig.getTimekprSharedDir(), "client/forms")
# use GUI
from timekpr.client.gui.admingui import timekprAdminGUI
# load GUI and process from there
self._adminGUI = timekprAdminGUI(cons.TK_VERSION, _resourcePathGUI, getpass.getuser())
# start GUI
self._adminGUI.startAdminGUI()
# nor X nor wayland are available
else:
# print to console
log.consoleOut("%s\n" % (msg.getTranslation("TK_MSG_CONSOLE_GUI_NOT_AVAILABLE")))
# forced CLI"
timekprForceCLI = True
else:
# CLI
timekprForceCLI = True
# for CLI connections
if timekprForceCLI:
# connect
self._timekprAdminConnector.initTimekprConnection(True, False, timekprForceCLI)
# connected?
if self._timekprAdminConnector.isConnected()[1]:
# use CLI
# validate possible parameters and their values, when fine - execute them as well
self.checkAndExecuteAdminCommands(*args)
log.autoFlushLogFile(True)
# --------------- initialization / helper methods --------------- #
def finishTimekpr(self, signal=None, frame=None):
"""Exit timekpr admin GUI gracefully"""
if self._adminGUI is not None:
# finish main thread on GUI`
self._adminGUI.finishTimekpr(signal, frame)
# --------------- parameter validation methods --------------- #
def checkAndExecuteAdminCommands(self, *args):
"""Init connection to timekpr dbus server"""
# initial param len
paramIdx = 0
paramLen = len(args)
adminCmdIncorrect = False
tmpIdx = 0
# determine parameter offset
for rArg in args:
# count offset
tmpIdx += 1
# check for script
if "/timekpra" in rArg or "timekpra.py" in rArg:
paramIdx = tmpIdx
# this gets the command itself (args[0] is the script name)
adminCmd = args[paramIdx] if paramLen > paramIdx else "timekpra"
# now based on params check them out
# this gets saved user list from the server
if adminCmd == "--help":
# fine
pass
# this gets saved user list from the server
elif adminCmd in ("--getuserlist", "--userlist"):
# check param len
if paramLen != paramIdx + 1:
# fail
adminCmdIncorrect = True
else:
# get list
result, message, userList = self._timekprAdminConnector.getUserList()
# process
if result == 0:
# process
self.printUserList(userList)
else:
# log error
log.consoleOut(message)
# this gets user configuration from the server
elif adminCmd in ("--getuserinfo", "--userinfo"):
# check param len
if not (paramLen >= paramIdx + 2 and paramLen <= paramIdx + 3):
# fail
adminCmdIncorrect = True
else:
# get user config
result, message, userConfig = self._timekprAdminConnector.getUserConfigurationAndInformation(args[paramIdx + 1], cons.TK_CL_INF_FULL)
# process
if result == 0:
# process
self.printUserConfig(args[paramIdx + 1], userConfig, True if paramLen > paramIdx + 2 and args[paramIdx + 2] == "-h" else False)
else:
# log error
log.consoleOut(message)
# this gets user configuration from the server
elif adminCmd in ("--getuserinfort", "--userinfort"):
# check param len
if not (paramLen >= paramIdx + 2 and paramLen <= paramIdx + 3):
# fail
adminCmdIncorrect = True
else:
# get user config
result, message, userConfig = self._timekprAdminConnector.getUserConfigurationAndInformation(args[paramIdx + 1], cons.TK_CL_INF_RT)
# process
if result == 0:
# process
self.printUserConfig(args[paramIdx + 1], userConfig, True if paramLen > paramIdx + 2 and args[paramIdx + 2] == "-h" else False)
else:
# log error
log.consoleOut(message)
# this sets allowed days for the user
elif adminCmd == "--setalloweddays":
# check param len
if paramLen != paramIdx + 3:
# fail
adminCmdIncorrect = True
else:
# set days
self.processSetAllowedDays(args[paramIdx + 1], args[paramIdx + 2])
# this sets allowed hours per specified day or ALL for every day
elif adminCmd == "--setallowedhours":
# check param len
if paramLen != paramIdx + 4:
# fail
adminCmdIncorrect = True
else:
# set days
self.processSetAllowedHours(args[paramIdx + 1], args[paramIdx + 2], args[paramIdx + 3])
# this sets time limits per allowed days
elif adminCmd == "--settimelimits":
# check param len
if paramLen != paramIdx + 3:
# fail
adminCmdIncorrect = True
else:
# set days
self.processSetTimeLimits(args[paramIdx + 1], args[paramIdx + 2])
# this sets time limits per week
elif adminCmd == "--settimelimitweek":
# check param len
if paramLen != paramIdx + 3:
# fail
adminCmdIncorrect = True
else:
# set days
self.processSetTimeLimitWeek(args[paramIdx + 1], args[paramIdx + 2])
# this sets time limits per month
elif adminCmd == "--settimelimitmonth":
# check param len
if paramLen != paramIdx + 3:
# fail
adminCmdIncorrect = True
else:
# set days
self.processSetTimeLimitMonth(args[paramIdx + 1], args[paramIdx + 2])
# this sets whether to track inactive user sessions
elif adminCmd == "--settrackinactive":
# check param len
if paramLen != paramIdx + 3:
# fail
adminCmdIncorrect = True
else:
# set days
self.processSetTrackInactive(args[paramIdx + 1], args[paramIdx + 2])
# this sets whether to show tray icon
elif adminCmd == "--sethidetrayicon":
# check param len
if paramLen != paramIdx + 3:
# fail
adminCmdIncorrect = True
else:
# set days
self.processSetHideTrayIcon(args[paramIdx + 1], args[paramIdx + 2])
# this sets lockout type for the user
elif adminCmd == "--setlockouttype":
# check param len
if paramLen != paramIdx + 3:
# fail
adminCmdIncorrect = True
else:
# set days
self.processSetLockoutType(args[paramIdx + 1], args[paramIdx + 2])
# this sets whether PlayTime is enabled for user
elif adminCmd == "--setplaytimeenabled":
# check param len
if paramLen != paramIdx + 3:
# fail
adminCmdIncorrect = True
else:
# set days
self.processSetPlayTimeEnabled(args[paramIdx + 1], args[paramIdx + 2])
# this sets playtime override for user
elif adminCmd == "--setplaytimelimitoverride":
# check param len
if paramLen != paramIdx + 3:
# fail
adminCmdIncorrect = True
else:
# set days
self.processSetPlayTimeLimitOverride(args[paramIdx + 1], args[paramIdx + 2])
# this sets playtime allowed during unaccounted intervals for user
elif adminCmd == "--setplaytimeunaccountedintervalsflag":
# check param len
if paramLen != paramIdx + 3:
# fail
adminCmdIncorrect = True
else:
# set days
self.processSetPlayTimeUnaccountedIntervalsEnabled(args[paramIdx + 1], args[paramIdx + 2])
# this sets allowed days for PlayTime for the user
elif adminCmd == "--setplaytimealloweddays":
# check param len
if paramLen != paramIdx + 3:
# fail
adminCmdIncorrect = True
else:
# set days
self.processSetPlayTimeAllowedDays(args[paramIdx + 1], args[paramIdx + 2])
# this sets PlayTime limits for allowed days for the user
elif adminCmd == "--setplaytimelimits":
# check param len
if paramLen != paramIdx + 3:
# fail
adminCmdIncorrect = True
else:
# set days
self.processSetPlayTimeLimits(args[paramIdx + 1], args[paramIdx + 2])
# this sets PlayTime activities for the user
elif adminCmd == "--setplaytimeactivities":
# check param len
if paramLen != paramIdx + 3:
# fail
adminCmdIncorrect = True
else:
# set days
self.processSetPlayTimeActivities(args[paramIdx + 1], args[paramIdx + 2])
# this sets time left for the user at current moment
elif adminCmd == "--settimeleft":
# check param len
if paramLen != paramIdx + 4:
# fail
adminCmdIncorrect = True
else:
# set days
self.processSetTimeLeft(args[paramIdx + 1], args[paramIdx + 2], args[paramIdx + 3])
# this sets time left for the user at current moment
elif adminCmd == "--setplaytimeleft":
# check param len
if paramLen != paramIdx + 4:
# fail
adminCmdIncorrect = True
else:
# set days
self.processSetPlayTimeLeft(args[paramIdx + 1], args[paramIdx + 2], args[paramIdx + 3])
else:
# out
adminCmdIncorrect = True
# initial order as well as sibling cmds
cmds = ["--help", "--getuserlist", "--userlist", "--getuserinfo", "--userinfo", "--getuserinfort", "--userinfort"]
# check whether command is supported
if ((adminCmd not in cons.TK_USER_ADMIN_COMMANDS and adminCmd not in cons.TK_ADMIN_COMMANDS and adminCmd not in cmds)
or adminCmd == "--help"
or adminCmdIncorrect
):
# fail
if adminCmdIncorrect:
# fail msg
log.consoleOut(msg.getTranslation("TK_MSG_CONSOLE_COMMAND_INCORRECT"), *args, "\n")
# log notice
log.consoleOut("%s\n*) %s\n*) %s\n*) %s\n" % (
msg.getTranslation("TK_MSG_CONSOLE_USAGE_NOTICE_HEAD"),
msg.getTranslation("TK_MSG_CONSOLE_USAGE_NOTICE_TIME"),
msg.getTranslation("TK_MSG_CONSOLE_USAGE_NOTICE_HOURS"),
msg.getTranslation("TK_MSG_CONSOLE_USAGE_NOTICE_DAYS"))
)
# log usage notes text
log.consoleOut("%s\n" % (msg.getTranslation("TK_MSG_CONSOLE_USAGE_NOTES")))
# print initial commands as first
for rCmd in cmds:
# if exists (this needs to be enabled if there are multiple cmds to do the same thing)
if rCmd in cons.TK_USER_ADMIN_COMMANDS:
# log on screen
log.consoleOut(" ", rCmd, cons.TK_USER_ADMIN_COMMANDS[rCmd], "\n")
# print help
for rCmd, rCmdDesc in cons.TK_USER_ADMIN_COMMANDS.items():
# do not print already known commands
if rCmd not in cmds:
# log on screen
log.consoleOut(" ", rCmd, rCmdDesc, "\n")
# --------------- parameter execution methods --------------- #
def printUserList(self, pUserList):
"""Format and print userlist"""
# print to console
log.consoleOut(msg.getTranslation("TK_MSG_CONSOLE_USERS_TOTAL", len(pUserList)))
# loop and print
for rUser in pUserList:
log.consoleOut(rUser[0])
def printUserConfig(self, pUserName, pPrintUserConfig, pEasyTime=False):
"""Format and print user config"""
# print to console
log.consoleOut("# %s" % (msg.getTranslation("TK_MSG_CONSOLE_CONFIG_FOR") % (pUserName)))
# loop and print the same format as ppl will use to set that
for rUserKey, rUserConfig in pPrintUserConfig.items():
# final value
result = ""
# join the lists for days
if rUserKey in ("ALLOWED_WEEKDAYS", "PLAYTIME_ALLOWED_WEEKDAYS"):
# get final value
result = ";".join(str(rVal) for rVal in rUserConfig)
# join the lists for limits
elif rUserKey in ("LIMITS_PER_WEEKDAYS", "PLAYTIME_LIMITS_PER_WEEKDAYS"):
# get final value
result = ";".join(getTimeStrFromSeconds(int(rVal)) if pEasyTime else str(rVal) for rVal in rUserConfig)
# join the lists for days / hours
elif "ALLOWED_HOURS_" in rUserKey:
# print join
if len(rUserConfig) > 0:
# process hours
for rUserHour in sorted(list(map(int, rUserConfig))):
# unaccounted hour
uacc = "!" if rUserConfig[str(rUserHour)][cons.TK_CTRL_UACC] else ""
# get config per hr
hr = "%s" % (rUserHour) if rUserConfig[str(rUserHour)][cons.TK_CTRL_SMIN] <= 0 and rUserConfig[str(rUserHour)][cons.TK_CTRL_EMIN] >= 60 else "%s[%s-%s]" % (rUserHour, rUserConfig[str(rUserHour)][cons.TK_CTRL_SMIN], rUserConfig[str(rUserHour)][cons.TK_CTRL_EMIN])
# empty
result = "%s%s" % (uacc, hr) if result == "" else "%s;%s%s" % (result, uacc, hr)
# bools
elif rUserKey in ("TRACK_INACTIVE", "HIDE_TRAY_ICON", "PLAYTIME_ENABLED", "PLAYTIME_LIMIT_OVERRIDE_ENABLED", "PLAYTIME_UNACCOUNTED_INTERVALS_ENABLED"):
# bools
result = True if str(rUserConfig).lower() == "true" else False
# strings
elif rUserKey in ("PLAYTIME_ACTIVITIES"):
# loop thorhough activities
for rActArr in rUserConfig:
# activity
act = "%s[%s]" % (rActArr[0], rActArr[1]) if rActArr[1] != "" else "%s" % (rActArr[0])
# gather activities
result = "%s" % (act) if result == "" else "%s;%s" % (result, act)
# limits
elif rUserKey in ("LIMIT_PER_WEEK" ,"LIMIT_PER_MONTH" ,"TIME_SPENT_BALANCE" ,"TIME_SPENT_DAY" ,"TIME_SPENT_WEEK" ,"TIME_SPENT_MONTH" ,"TIME_LEFT_DAY" ,"PLAYTIME_LEFT_DAY" ,"PLAYTIME_SPENT_DAY" ,"ACTUAL_TIME_SPENT_SESSION" ,"ACTUAL_TIME_INACTIVE_SESSION" ,"ACTUAL_TIME_SPENT_BALANCE " ,"ACTUAL_TIME_SPENT_DAY" ,"ACTUAL_TIME_LEFT_DAY" ,"ACTUAL_TIME_LEFT_CONTINUOUS" ,"ACTUAL_PLAYTIME_LEFT_DAY"):
# final
result = getTimeStrFromSeconds(int(rUserConfig)) if pEasyTime else str(rUserConfig)
# the rest
else:
# final val
result = str(rUserConfig)
# print final
log.consoleOut("%s: %s" % (rUserKey, result))
def processSetAllowedDays(self, pUserName, pDayList):
"""Process allowed days"""
# defaults
dayMap = []
result = 0
# day map
try:
# try to parse parameters
dayMap = str(pDayList).split(";")
except Exception as ex:
# fail
result = -1
message = msg.getTranslation("TK_MSG_PARSE_ERROR") % (str(ex))
# preprocess successful
if result == 0:
# invoke
result, message = self._timekprAdminConnector.setAllowedDays(pUserName, dayMap)
# process
if result != 0:
# log error
log.consoleOut(message)
def processSetAllowedHours(self, pUserName, pDayNumber, pHourList):
"""Process allowed hours"""
# this is the dict for hour config
allowedHours = {}
result = 0
# allowed hours
try:
# check hours
for rHour in str(pHourList).split(";"):
# get hours and minutes
hour, sMin, eMin, uacc = findHourStartEndMinutes(rHour)
# raise any error in case we can not get parsing right
if hour is None:
# raise
raise ValueError("this does not compute")
# set hours
allowedHours[str(hour)] = {cons.TK_CTRL_SMIN: sMin, cons.TK_CTRL_EMIN: eMin, cons.TK_CTRL_UACC: uacc}
except Exception as ex:
# fail
result = -1
message = msg.getTranslation("TK_MSG_PARSE_ERROR") % (str(ex))
# preprocess successful
if result == 0:
# invoke
result, message = self._timekprAdminConnector.setAllowedHours(pUserName, pDayNumber, allowedHours)
# process
if result != 0:
# log error
log.consoleOut(message)
def processSetTimeLimits(self, pUserName, pDayLimits):
"""Process time limits for days"""
# defaults
dayLimits = []
result = 0
# day limits
try:
# allow empty limits too
if str(pDayLimits) != "":
# convert limits
for rDl in pDayLimits.split(";"):
# add limit
dayLimits += [getSecondsFromTimeStr(rDl)]
except Exception as ex:
# fail
result = -1
message = msg.getTranslation("TK_MSG_PARSE_ERROR") % (str(ex))
# preprocess successful
if result == 0:
# invoke
result, message = self._timekprAdminConnector.setTimeLimitForDays(pUserName, dayLimits)
# process
if result != 0:
# log error
log.consoleOut(message)
def processSetTimeLimitWeek(self, pUserName, pTimeLimitWeek):
"""Process time limits for week"""
# defaults
weekLimit = 0
result = 0
# week limit
try:
# try to parse parameters
weekLimit = int(getSecondsFromTimeStr(pTimeLimitWeek))
except Exception as ex:
# fail
result = -1
message = msg.getTranslation("TK_MSG_PARSE_ERROR") % (str(ex))
# preprocess successful
if result == 0:
# invoke
result, message = self._timekprAdminConnector.setTimeLimitForWeek(pUserName, weekLimit)
# process
if result != 0:
# log error
log.consoleOut(message)
def processSetTimeLimitMonth(self, pUserName, pTimeLimitMonth):
"""Process time limits for month"""
# defaults
monthLimit = 0
result = 0
# week limit
try:
# try to parse parameters
monthLimit = int(getSecondsFromTimeStr(pTimeLimitMonth))
except Exception as ex:
# fail
result = -1
message = msg.getTranslation("TK_MSG_PARSE_ERROR") % (str(ex))
# preprocess successful
if result == 0:
# invoke
result, message = self._timekprAdminConnector.setTimeLimitForMonth(pUserName, monthLimit)
# process
if result != 0:
# log error
log.consoleOut(message)
def processSetTrackInactive(self, pUserName, pTrackInactive):
"""Process track inactive"""
# defaults
trackInactive = None
result = 0
# check
if str(pTrackInactive).lower() not in ("true", "false"):
# fail
result = -1
message = msg.getTranslation("TK_MSG_PARSE_ERROR") % ("please specify true or false")
else:
trackInactive = True if str(pTrackInactive).lower() == "true" else False
# preprocess successful
if result == 0:
# invoke
result, message = self._timekprAdminConnector.setTrackInactive(pUserName, trackInactive)
# process
if result != 0:
# log error
log.consoleOut(message)
def processSetHideTrayIcon(self, pUserName, pHideTrayIcon):
"""Process hide tray icon"""
# defaults
hideTrayIcon = None
result = 0
# check
if str(pHideTrayIcon).lower() not in ("true", "false"):
# fail
result = -1
message = msg.getTranslation("TK_MSG_PARSE_ERROR") % ("please specify true or false")
else:
hideTrayIcon = True if str(pHideTrayIcon).lower() == "true" else False
# preprocess successful
if result == 0:
# invoke
result, message = self._timekprAdminConnector.setHideTrayIcon(pUserName, hideTrayIcon)
# process
if result != 0:
# log error
log.consoleOut(message)
def processSetLockoutType(self, pUserName, pLockoutType):
"""Process lockout type"""
# defaults
result = 0
# parse lockout
lockout = str(pLockoutType).split(";")
lockoutType = lockout[0]
lockoutWakeFrom = lockout[1] if len(lockout) == 3 else '0'
lockoutWakeTo = lockout[2] if len(lockout) == 3 else '23'
# check
if lockoutType not in (cons.TK_CTRL_RES_L, cons.TK_CTRL_RES_S, cons.TK_CTRL_RES_W, cons.TK_CTRL_RES_T, cons.TK_CTRL_RES_K, cons.TK_CTRL_RES_D):
# fail
result = -1
message = msg.getTranslation("TK_MSG_PARSE_ERROR") % ("please specify one of these: %s, %s, %s, %s, %s, %s" % (cons.TK_CTRL_RES_L, cons.TK_CTRL_RES_S, cons.TK_CTRL_RES_W, cons.TK_CTRL_RES_T, cons.TK_CTRL_RES_K, cons.TK_CTRL_RES_D))
# preprocess successful
if result == 0:
# invoke
result, message = self._timekprAdminConnector.setLockoutType(pUserName, lockoutType, lockoutWakeFrom, lockoutWakeTo)
# process
if result != 0:
# log error
log.consoleOut(message)
def processSetTimeLeft(self, pUserName, pOperation, pLimit):
"""Process time left"""
# defaults
limit = 0
result = 0
# limit
try:
# try to parse parameters
limit = int(getSecondsFromTimeStr(pLimit))
except Exception as ex:
# fail
result = -1
message = msg.getTranslation("TK_MSG_PARSE_ERROR") % (str(ex))
# preprocess successful
if result == 0:
# invoke
result, message = self._timekprAdminConnector.setTimeLeft(pUserName, pOperation, limit)
# process
if result != 0:
# log error
log.consoleOut(message)
# --------------- parameter execution methods for PlayTime --------------- #
def processSetPlayTimeEnabled(self, pUserName, pPlayTimeEnabled):
"""Process PlayTime enabled flag"""
# defaults
isPlayTimeEnabled = None
result = 0
# check
if str(pPlayTimeEnabled).lower() not in ("true", "false"):
# fail
result = -1
message = msg.getTranslation("TK_MSG_PARSE_ERROR") % ("please specify true or false")
else:
isPlayTimeEnabled = True if str(pPlayTimeEnabled).lower() == "true" else False
# preprocess successful
if result == 0:
# invoke
result, message = self._timekprAdminConnector.setPlayTimeEnabled(pUserName, isPlayTimeEnabled)
# process
if result != 0:
# log error
log.consoleOut(message)
def processSetPlayTimeLimitOverride(self, pUserName, pPlayTimeLimitOverride):
"""Process PlayTime override flag"""
# defaults
isPlayTimeLimitOverride = None
result = 0
# check
if str(pPlayTimeLimitOverride).lower() not in ("true", "false"):
# fail
result = -1
message = msg.getTranslation("TK_MSG_PARSE_ERROR") % ("please specify true or false")
else:
isPlayTimeLimitOverride = True if str(pPlayTimeLimitOverride).lower() == "true" else False
# preprocess successful
if result == 0:
# invoke
result, message = self._timekprAdminConnector.setPlayTimeLimitOverride(pUserName, isPlayTimeLimitOverride)
# process
if result != 0:
# log error
log.consoleOut(message)
def processSetPlayTimeUnaccountedIntervalsEnabled(self, pUserName, pPlayTimeUnaccountedIntervalsEnabled):
"""Process PlayTime allowed during unaccounted intervals flag"""
# defaults
isPlayTimeUnaccountedIntervalsEnabled = None
result = 0
# check
if str(pPlayTimeUnaccountedIntervalsEnabled).lower() not in ("true", "false"):
# fail
result = -1
message = msg.getTranslation("TK_MSG_PARSE_ERROR") % ("please specify true or false")
else:
isPlayTimeUnaccountedIntervalsEnabled = True if str(pPlayTimeUnaccountedIntervalsEnabled).lower() == "true" else False
# preprocess successful
if result == 0:
# invoke
result, message = self._timekprAdminConnector.setPlayTimeUnaccountedIntervalsEnabled(pUserName, isPlayTimeUnaccountedIntervalsEnabled)
# process
if result != 0:
# log error
log.consoleOut(message)
def processSetPlayTimeAllowedDays(self, pUserName, pPlayTimeDayList):
"""Process allowed days for PlayTime"""
# defaults
dayMap = []
result = 0
# day map
try:
# try to parse parameters
dayMap = str(pPlayTimeDayList).split(";")
except Exception as ex:
# fail
result = -1
message = msg.getTranslation("TK_MSG_PARSE_ERROR") % (str(ex))
# preprocess successful
if result == 0:
# invoke
result, message = self._timekprAdminConnector.setPlayTimeAllowedDays(pUserName, dayMap)
# process
if result != 0:
# log error
log.consoleOut(message)
def processSetPlayTimeLimits(self, pUserName, pPlayTimeDayLimits):
"""Process time limits for allowed days for PlayTime"""
# defaults
dayLimits = []
result = 0
# day limists
try:
# allow empty limits too
if str(pPlayTimeDayLimits) != "":
# convert limits
for rDl in str(pPlayTimeDayLimits).split(";"):
# add limit
dayLimits += [getSecondsFromTimeStr(rDl)]
except Exception as ex:
# fail
result = -1
message = msg.getTranslation("TK_MSG_PARSE_ERROR") % (str(ex))
# preprocess successful
if result == 0:
# invoke
result, message = self._timekprAdminConnector.setPlayTimeLimitsForDays(pUserName, dayLimits)
# process
if result != 0:
# log error
log.consoleOut(message)
def processSetPlayTimeActivities(self, pUserName, pPlayTimeActivities):
"""Process PlayTime activities"""
# defaults
playTimeActivities = []
result = 0
# day limists
try:
# ## try to parse parameters ##
if str(pPlayTimeActivities) != "":
# split activities
for rAct in str(pPlayTimeActivities).split(";"):
# try parsing the names
mask, description = splitConfigValueNameParam(rAct)
# raise any error in case we can not get parsing right
if mask is None:
# raise
raise ValueError("this does not compute")
# set up activity list
playTimeActivities.append([mask, description])
except Exception as ex:
# fail
result = -1
message = msg.getTranslation("TK_MSG_PARSE_ERROR") % (str(ex))
# preprocess successful
if result == 0:
# invoke
result, message = self._timekprAdminConnector.setPlayTimeActivities(pUserName, playTimeActivities)
# process
if result != 0:
# log error
log.consoleOut(message)
def processSetPlayTimeLeft(self, pUserName, pOperation, pLimit):
"""Process time left"""
# defaults
limit = 0
result = 0
# limit
try:
# try to parse parameters
limit = int(getSecondsFromTimeStr(pLimit))
except Exception as ex:
# fail
result = -1
message = msg.getTranslation("TK_MSG_PARSE_ERROR") % (str(ex))
# preprocess successful
if result == 0:
# invoke
result, message = self._timekprAdminConnector.setPlayTimeLeft(pUserName, pOperation, limit)
# process
if result != 0:
# log error
log.consoleOut(message)
timekpr-next/client/interface/ 000775 001750 001750 00000000000 13476006650 020422 5 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/client/interface/speech/ 000775 001750 001750 00000000000 15122253204 021656 5 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/client/interface/speech/espeak.py 000644 001750 001750 00000004235 15122253204 023502 0 ustar 00bezvfedu bezvfedu 000000 000000 """
Created on Aug 28, 2018
@author: mjasnik
"""
import locale
import time
# init speech
try:
from espeak import espeak as espeak
_USE_SPEECH = True
except (ImportError, ValueError):
_USE_SPEECH = False
# init speech-ng
if not _USE_SPEECH:
try:
from espeakng import ESpeakNG as espeakng
_USE_SPEECH_NG = True
except (ImportError, ValueError):
_USE_SPEECH_NG = False
pass
pass
def isSupported():
"""Return whether speech can be used"""
# result
return (_USE_SPEECH or _USE_SPEECH_NG)
class timekprSpeech(object):
"""Class will provide speech synth functionality"""
def __init__(self):
"""Initialize config"""
def initSpeech(self):
"""Initialize speech"""
# set up speech synth
if _USE_SPEECH:
# espeak
espeak.set_voice(self.getDefaultSpeechLanguage())
espeak.set_parameter(espeak.Parameter.Pitch, 1)
espeak.set_parameter(espeak.Parameter.Rate, 135)
espeak.set_parameter(espeak.Parameter.Range, 600)
elif _USE_SPEECH_NG:
# ng espeak
self.espeak = espeakng()
self.espeak.voice = self.getDefaultSpeechLanguage();
self.espeak.pitch = 1
self.espeak.speed = 135
self.espeak.range = 600
def getDefaultSpeechLanguage(self):
"""Get default language"""
# no lang
lang = "en"
try:
if _USE_SPEECH:
lang = locale.getlocale()[0].split("_")[0]
elif _USE_SPEECH_NG:
lang = locale.getlocale()[0].replace("_", "-").lower()
except Exception:
pass
# result
return lang
def saySmth(self, pMsg):
"""Say something"""
# if supported
if self.isSpeechSupported():
if _USE_SPEECH:
# synth the speech
espeak.synth(pMsg)
elif _USE_SPEECH_NG:
# say
self.espeak.say(pMsg)
def isSpeechSupported(self):
"""Return whether speech can be used"""
# result
return isSupported()
timekpr-next/client/interface/speech/__init__.py 000664 001750 001750 00000000000 13476006650 023770 0 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/client/interface/__init__.py 000664 001750 001750 00000000000 13476006650 022521 0 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/client/interface/ui/ 000775 001750 001750 00000000000 14575617250 021044 5 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/client/interface/ui/appindicator.py 000600 001750 001750 00000011221 14575617134 024057 0 ustar 00bezvfedu bezvfedu 000000 000000 """
Created on Aug 28, 2018
@author: mjasnik
"""
# import
import gi
import os
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
# timekpr imports
from timekpr.common.constants import constants as cons
from timekpr.common.log import log
from timekpr.client.interface.ui.notificationarea import timekprNotificationArea
from timekpr.common.constants import messages as msg
# indicator stuff
try:
# try to load ayatanaappindicator (fully compatible with appindicator, I hope it stays that way)
gi.require_version("AyatanaAppIndicator3", "0.1")
from gi.repository import AyatanaAppIndicator3 as AppIndicator
# if successful, mark it so
_USE_INDICATOR = True
except (ImportError, ValueError):
try:
# try to load appindicator
gi.require_version("AppIndicator3", "0.1")
from gi.repository import AppIndicator3 as AppIndicator
# if successful, mark it so
_USE_INDICATOR = True
except (ImportError, ValueError):
# no indictor
_USE_INDICATOR = False
pass
class timekprIndicator(timekprNotificationArea):
"""Support appindicator"""
def __init__(self, pUserName, pUserNameFull, pTimekprClientConfig):
"""Init all required stuff for indicator"""
log.log(cons.TK_LOG_LEVEL_INFO, "start initTimekprIndicator")
# only if this is supported
if self.isSupported():
# init parent as well
super().__init__(pUserName, pUserNameFull, pTimekprClientConfig)
# this is our icon
self._indicator = None
log.log(cons.TK_LOG_LEVEL_INFO, "finish initTimekprIndicator")
def isSupported(self):
"""Get whether appindicator is supported"""
global _USE_INDICATOR
# returns whether we can use appindicator
return _USE_INDICATOR
def initTimekprIcon(self):
"""Initialize timekpr indicator"""
log.log(cons.TK_LOG_LEVEL_INFO, "start initTimekprIndicatorIcon")
# init indicator itself (icon will be set later)
self._indicator = AppIndicator.Indicator.new("indicator-timekpr", os.path.join(self._timekprClientConfig.getTimekprSharedDir(), "icons", cons.TK_PRIO_CONF["client-logo"][cons.TK_ICON_STAT]), AppIndicator.IndicatorCategory.APPLICATION_STATUS)
self._indicator.set_status(AppIndicator.IndicatorStatus.ACTIVE)
# define empty menu
self._timekprMenu = Gtk.Menu()
# add menu items
self._timekprMenuItemTimeLeft = Gtk.MenuItem(msg.getTranslation("TK_MSG_MENU_TIME_LEFT"))
self._timekprMenu.append(self._timekprMenuItemTimeLeft)
self._timekprMenu.append(Gtk.SeparatorMenuItem())
self._timekprMenuItemProperties = Gtk.MenuItem(msg.getTranslation("TK_MSG_MENU_CONFIGURATION"))
self._timekprMenu.append(self._timekprMenuItemProperties)
self._timekprMenu.append(Gtk.SeparatorMenuItem())
self._timekprMenuItemAbout = Gtk.MenuItem(msg.getTranslation("TK_MSG_MENU_ABOUT"))
self._timekprMenu.append(self._timekprMenuItemAbout)
# enable all
self._timekprMenu.show_all()
# connect signal to code
self._timekprMenuItemTimeLeft.connect("activate", super().invokeTimekprTimeLeft)
self._timekprMenuItemProperties.connect("activate", super().invokeTimekprUserProperties)
self._timekprMenuItemAbout.connect("activate", super().invokeTimekprAbout)
# set menu to indicator
self._indicator.set_menu(self._timekprMenu)
# initial config
self.setTimeLeft("", None, 0)
log.log(cons.TK_LOG_LEVEL_INFO, "finish initTimekprIndicatorIcon")
def setTimeLeft(self, pPriority, pTimeLeft, pTimeNotLimited, pPlayTimeLeft=None):
"""Set time left in the indicator"""
# make strings to set
timeLeftStr, icon = super().formatTimeLeft(pPriority, pTimeLeft, pTimeNotLimited, pPlayTimeLeft)
# if we have smth to set
if timeLeftStr is not None:
# set time left (this works with indicator in unity and gnome)
self._indicator.set_label(timeLeftStr, "")
# set time left (this works with indicator in kde5)
self._indicator.set_title(timeLeftStr)
# if we have smth to set
if icon is not None:
# set up the icon
self._indicator.set_icon(icon)
def getTrayIconEnabled(self):
"""Get whether tray icon is enabled"""
return self._indicator.get_status() == AppIndicator.IndicatorStatus.ACTIVE
def setTrayIconEnabled(self, pEnabled):
"""Set whether tray icon is enabled"""
self._indicator.set_status(AppIndicator.IndicatorStatus.ACTIVE if pEnabled else AppIndicator.IndicatorStatus.PASSIVE)
timekpr-next/client/interface/ui/__init__.py 000664 001750 001750 00000000000 13476006650 023136 0 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/client/interface/ui/statusicon.py 000600 001750 001750 00000010133 14575617135 023600 0 ustar 00bezvfedu bezvfedu 000000 000000 """
Created on Aug 28, 2018
@author: mjasnik
"""
# import
import os
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
# timekpr imports
from timekpr.common.constants import constants as cons
from timekpr.common.log import log
from timekpr.client.interface.ui.notificationarea import timekprNotificationArea
from timekpr.common.constants import messages as msg
# status icon stuff
_USE_STATUSICON = True
class timekprIndicator(timekprNotificationArea):
"""Support appindicator"""
def __init__(self, pUserName, pUserNameFull, pTimekprClientConfig):
"""Init all required stuff for indicator"""
log.log(cons.TK_LOG_LEVEL_INFO, "start initTimekprSystrayIcon")
# only if this is supported
if self.isSupported():
# init parent as well
super().__init__(pUserName, pUserNameFull, pTimekprClientConfig)
# this is our icon
self._tray = None
log.log(cons.TK_LOG_LEVEL_INFO, "finish initTimekprSystrayIcon")
def isSupported(self):
"""Get whether appindicator is supported"""
global _USE_STATUSICON
# returns whether we can use appindicator
return _USE_STATUSICON
def initTimekprIcon(self):
"""Initialize timekpr indicator"""
log.log(cons.TK_LOG_LEVEL_DEBUG, "start initTimekprStatusIcon")
# define our popupmenu
timekprMenu = """
"""
#
#
# set up tray
self._tray = Gtk.StatusIcon()
self._tray.set_visible(True)
# connect to methods
self._tray.connect("activate", super().invokeTimekprTimeLeft)
self._tray.connect("popup-menu", self.onTimekprMenu)
# build up menu actiongroups
timekprActionGroup = Gtk.ActionGroup("timekprActions")
timekprActionGroup.add_actions([
("TimeLeft", Gtk.STOCK_INFO, msg.getTranslation("TK_MSG_MENU_TIME_LEFT"), None, None, super().invokeTimekprTimeLeft),
("Limits & configuration", Gtk.STOCK_PROPERTIES, msg.getTranslation("TK_MSG_MENU_CONFIGURATION"), None, None, super().invokeTimekprUserProperties),
("About", Gtk.STOCK_ABOUT, msg.getTranslation("TK_MSG_MENU_ABOUT"), None, None, super().invokeTimekprAbout)
])
# build up menu
timekprUIManager = Gtk.UIManager()
timekprUIManager.add_ui_from_string(timekprMenu)
timekprUIManager.insert_action_group(timekprActionGroup)
self._popup = timekprUIManager.get_widget("/timekprPopupMenu")
# initial config
self._tray.set_from_file(os.path.join(self._timekprClientConfig.getTimekprSharedDir(), "icons", cons.TK_PRIO_CONF["client-logo"][cons.TK_ICON_STAT]))
self.setTimeLeft("", None, 0)
log.log(cons.TK_LOG_LEVEL_DEBUG, "finish initTimekprStatusIcon")
def setTimeLeft(self, pPriority, pTimeLeft, pTimeNotLimited, pPlayTimeLeft=None):
"""Set time left in the indicator"""
# make strings to set
timeLeftStr, icon = super().formatTimeLeft(pPriority, pTimeLeft, pTimeNotLimited, pPlayTimeLeft)
# if we have smth to set
if timeLeftStr is not None:
# set time left
self._tray.set_tooltip_text(timeLeftStr)
self._tray.set_title(timeLeftStr)
# if we have smth to set
if icon is not None:
# set up the icon
self._tray.set_from_file(icon)
def onTimekprMenu(self, status, button, time):
"""Show popup menu for tray"""
self._popup.popup(None, None, None, None, 0, time)
def getTrayIconEnabled(self):
"""Get whether tray icon is enabled"""
return self._tray.get_visible()
def setTrayIconEnabled(self, pEnabled):
"""Set whether tray icon is enabled"""
self._tray.set_visible(pEnabled)
timekpr-next/client/interface/ui/notificationarea.py 000600 001750 001750 00000030224 14575617250 024724 0 ustar 00bezvfedu bezvfedu 000000 000000 """
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)
timekpr-next/client/interface/dbus/ 000775 001750 001750 00000000000 15122253261 021347 5 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/client/interface/dbus/daemon.py 000644 001750 001750 00000035104 15122253261 023165 0 ustar 00bezvfedu bezvfedu 000000 000000 """
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.autoFlushLogFile(True)
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.measureDBUSTimeElapsed(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
misc.measureDBUSTimeElapsed(pStop=True, pDbusIFName=cons.TK_DBUS_BUS_NAME)
# 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_ERR, "ERROR (DBUS): \"%s\" in \"%s.%s\"" % (str(dbusEx), __name__, self.connectTimekprSignalsDBUS.__name__))
log.log(cons.TK_LOG_LEVEL_ERR, "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)
timekpr-next/client/interface/dbus/notifications.py 000644 001750 001750 00000076200 15122253261 024575 0 ustar 00bezvfedu bezvfedu 000000 000000 """
Created on Aug 28, 2018
@author: mjasnik
"""
# import
import dbus
import os
from gi.repository import GLib
from dbus.mainloop.glib import DBusGMainLoop
from datetime import datetime
# timekpr imports
from timekpr.common.constants import constants as cons
from timekpr.common.log import log
from timekpr.common.utils import misc
from timekpr.client.interface.speech.espeak import timekprSpeech
from timekpr.common.constants import messages as msg
# default loop
DBusGMainLoop(set_as_default=True)
class timekprNotifications(object):
"""Main class for supporting indicator notifications, connect to request methods for timekpr and connections to other DBUS modules"""
def __init__(self, pUserName, pTimekprClientConfig):
"""Initialize notifications"""
log.log(cons.TK_LOG_LEVEL_INFO, "start init timekpr notifications")
# uname
self._userName = pUserName
self._timekprClientConfig = pTimekprClientConfig
# notification (to replace itself in case they are incoming fast)
self._lastNotifId = 0
self._lastNotifDT = datetime.now()
self._lastPTNotifId = 0
self._lastPTNotifDT = datetime.now()
# session bus
self._userSessionBus = dbus.SessionBus()
# timekpr bus
self._timekprBus = (dbus.SessionBus() if (cons.TK_DEV_ACTIVE and cons.TK_DEV_BUS == "ses") else dbus.SystemBus())
# DBUS client connections
# connection types
self.CL_CONN_TK = "timekpr"
self.CL_CONN_NOTIF = "notifications"
self.CL_CONN_SCR = "screensaver"
# constants
self.CL_IF = "primary interface"
self.CL_IFA = "attributes interface"
self.CL_SI = "signal"
self.CL_CNT = "retry_count"
self.CL_DEL = "delay_times"
# object collection for DBUS connections
self._dbusConnections = {
self.CL_CONN_TK: {self.CL_IF: None, self.CL_IFA: None, self.CL_SI: None, self.CL_CNT: 999, self.CL_DEL: 0},
self.CL_CONN_NOTIF: {self.CL_IF: None, self.CL_IFA: None, self.CL_SI: None, self.CL_CNT: 99, self.CL_DEL: 0},
self.CL_CONN_SCR: {self.CL_IF: None, self.CL_IFA: None, self.CL_SI: None, self.CL_CNT: 5, self.CL_DEL: 2}
}
# WORKAROUNDS section start
# adjust even bigger delay with unity + 18.04 + HDD
if "UNITY" in os.getenv("XDG_CURRENT_DESKTOP", "N/A").upper():
# more delay, race condition with screensaver?
self._dbusConnections[self.CL_CONN_SCR][self.CL_DEL] += cons.TK_POLLTIME - 1
# WORKAROUNDS section end
# speech init
self._timekprSpeechManager = None
log.log(cons.TK_LOG_LEVEL_INFO, "finish init timekpr notifications")
def initClientConnections(self):
"""Init dbus (connect to session bus for notification)"""
log.log(cons.TK_LOG_LEVEL_DEBUG, "start initClientConnections")
# speech
if self._timekprSpeechManager is None:
# initialize
self._timekprSpeechManager = timekprSpeech()
# check if supported, if it is, initialize
if self._timekprSpeechManager.isSpeechSupported():
# initialize if supported
self._timekprSpeechManager.initSpeech()
# only if notifications are not ok
if self._dbusConnections[self.CL_CONN_NOTIF][self.CL_IF] is None and self._dbusConnections[self.CL_CONN_NOTIF][self.CL_CNT] > 0 and not self._dbusConnections[self.CL_CONN_NOTIF][self.CL_DEL] > 0:
# define inames (I hope "revolutionary company" won't sue me for using i in front of variable names)
iNames = ["org.freedesktop.Notifications"]
iPaths = ["/org/freedesktop/Notifications"]
# go through inames
for idx in range(0, len(iNames)):
# go through all possible interfaces
try:
# dbus performance measurement
misc.measureDBUSTimeElapsed(pStart=True)
# getting interface
self._dbusConnections[self.CL_CONN_NOTIF][self.CL_IF] = dbus.Interface(self._userSessionBus.get_object(iNames[idx], iPaths[idx]), iNames[idx])
# measurement logging
misc.measureDBUSTimeElapsed(pStop=True, pDbusIFName=iNames[idx])
# first sucess is enough
log.log(cons.TK_LOG_LEVEL_DEBUG, "CONNECTED to DBUS %s interface" % (self.CL_CONN_NOTIF))
# log
log.log(cons.TK_LOG_LEVEL_INFO, "INFO: connected to notification service through \"%s\"" % (iNames[idx]))
# check capabilities
if "sound" not in self._dbusConnections[self.CL_CONN_NOTIF][self.CL_IF].GetCapabilities():
# notifications w/ sound are not available
self._timekprClientConfig.setIsNotificationSoundSupported(False)
# log
log.log(cons.TK_LOG_LEVEL_INFO, "INFO: notification sound is not supported")
# add a connection to signal
self._dbusConnections[self.CL_CONN_NOTIF][self.CL_SI] = self._userSessionBus.add_signal_receiver(
path = iPaths[idx],
handler_function = self.receiveNotificationClosed,
dbus_interface = iNames[idx],
signal_name = "NotificationClosed")
# log
log.log(cons.TK_LOG_LEVEL_INFO, "INFO: connected to notification closing callback service through \"%s\"" % (iNames[idx]))
# finish
break
except Exception as dbusEx:
self._dbusConnections[self.CL_CONN_NOTIF][self.CL_IF] = None
# logging
log.log(cons.TK_LOG_LEVEL_WARN, "WARNING: initiating dbus connection (\"%s.%s\", %s, %s), error: %s" % (__name__, self.initClientConnections.__name__, self.CL_CONN_NOTIF, iNames[idx], str(dbusEx)))
# only if screensaver is not ok
if self._dbusConnections[self.CL_CONN_SCR][self.CL_IF] is None and self._dbusConnections[self.CL_CONN_SCR][self.CL_CNT] > 0 and not self._dbusConnections[self.CL_CONN_SCR][self.CL_DEL] > 0:
# define inames (I hope "revolutionary company" won't sue me for using i in front of variable names :) )
iNames = []
iPaths = []
chosenIdx = None
currentDE = None
isGnomeScrUsed = False
isFDScrUsed = False
# THIS WHOLE SECTION IS WORKAROUNDS FOR MULTIPLE VARIETIES OF SCREENSAVER IMPLEMENTATIONS - START
# they must be compatible to freedekstop standard, e.g. have corect naming and at least GetActive method as well as ActiveChanged signal
# get current DE
currentDE = os.getenv("XDG_CURRENT_DESKTOP", "N/A")
# log
log.log(cons.TK_LOG_LEVEL_INFO, "INFO: current desktop environment \"%s\"" % (currentDE))
# transform
currentDE = currentDE.lower().replace("x-", "")
# workarounds per desktop
for rIdx in range(0, len(cons.TK_SCR_XDGCD_OVERRIDE)):
# check desktops
if cons.TK_SCR_XDGCD_OVERRIDE[rIdx][0] in currentDE:
log.log(cons.TK_LOG_LEVEL_INFO, "INFO: using \"%s\" screensaver dbus interface as a workaround" % (cons.TK_SCR_XDGCD_OVERRIDE[rIdx][1]))
# use gnome stuff
iNames.extend(["org.%s.ScreenSaver" % (cons.TK_SCR_XDGCD_OVERRIDE[rIdx][1])])
iPaths.extend(["/org/%s/ScreenSaver" % (cons.TK_SCR_XDGCD_OVERRIDE[rIdx][1])])
# check if gnome screensaver is used (can it be used as failover?)
isGnomeScrUsed = ((cons.TK_SCR_XDGCD_OVERRIDE[rIdx][1] == "gnome") if not isGnomeScrUsed else isGnomeScrUsed)
# check if freedesktop screensaver is used (can it be used as failover?)
isFDScrUsed = ((cons.TK_SCR_XDGCD_OVERRIDE[rIdx][1] == "freedesktop") if not isFDScrUsed else isFDScrUsed)
# first match is enough
break
# in case overrides were found, do not try anything else
if len(iNames) < 1:
# try parsing DE to get the most accurate DE name (there are desktop names, like, KDE, X-Cinnamon, XFCE and ubuntu:GNOME, ...)
if ":" in currentDE:
# get desktop so we check for second option after ":"
currentDE = currentDE.split(":")[1]
# add to the list
if currentDE is not None and currentDE != "":
log.log(cons.TK_LOG_LEVEL_INFO, "INFO: trying to use \"%s\" as screensaver dbus object" % (currentDE))
# add
iNames.extend(["org.%s.ScreenSaver" % (currentDE)])
iPaths.extend(["/org/%s/ScreenSaver" % (currentDE)])
# check if gnome screensaver is used (can it be used as failover?)
isGnomeScrUsed = ((currentDE == "gnome") if not isGnomeScrUsed else isGnomeScrUsed)
# add gnome (most popular)
if not isGnomeScrUsed:
# add default section
iNames.extend(["org.gnome.ScreenSaver"])
iPaths.extend(["/org/gnome/ScreenSaver"])
# add freedesktop (almost no one uses this, except KDE and maybe some other, the rest return "method not implemented" error)
if not isFDScrUsed:
# add default section with the actual standard
iNames.extend(["org.freedesktop.ScreenSaver"])
iPaths.extend(["/org/freedesktop/ScreenSaver"])
# THIS WHOLE SECTION IS WORKAROUNDS FOR MULTIPLE VARIETIES OF SCREENSAVER IMPLEMENTATIONS - END
# go through inames
for idx in range(0, len(iNames)):
# go through all possible interfaces
try:
# dbus performance measurement
misc.measureDBUSTimeElapsed(pStart=True)
# getting interface
self._dbusConnections[self.CL_CONN_SCR][self.CL_IF] = dbus.Interface(self._userSessionBus.get_object(iNames[idx], iPaths[idx]), iNames[idx])
# log
log.log(cons.TK_LOG_LEVEL_INFO, "INFO: connected to screensaver service through \"%s\"" % (iNames[idx]))
# verification (Gnome has not implemented freedesktop methods, we need to verify this actually works)
self._dbusConnections[self.CL_CONN_SCR][self.CL_IF].GetActive()
# measurement logging
misc.measureDBUSTimeElapsed(pStop=True, pDbusIFName=iNames[idx])
# first sucess is enough
chosenIdx = idx
# finish
break
except Exception as dbusEx:
del self._dbusConnections[self.CL_CONN_SCR][self.CL_IF]
self._dbusConnections[self.CL_CONN_SCR][self.CL_IF] = None
# logging
log.log(cons.TK_LOG_LEVEL_WARN, "WARNING: initiating dbus connection (\"%s.%s\", %s, %s), error: %s" % (__name__, self.initClientConnections.__name__, self.CL_CONN_SCR, iNames[idx], str(dbusEx)))
# connection successful
if self._dbusConnections[self.CL_CONN_SCR][self.CL_IF] is not None:
# log
log.log(cons.TK_LOG_LEVEL_DEBUG, "CONNECTED to DBUS %s (%s) interface" % (self.CL_CONN_SCR, iNames[chosenIdx]))
# add a connection to signal
self._dbusConnections[self.CL_CONN_SCR][self.CL_SI] = self._userSessionBus.add_signal_receiver(
path = iPaths[chosenIdx],
handler_function = self.receiveScreenSaverActivityChange,
dbus_interface = iNames[chosenIdx],
signal_name = "ActiveChanged")
# log
log.log(cons.TK_LOG_LEVEL_INFO, "INFO: connected to screensaver active callback signal through \"%s\"" % (iNames[chosenIdx]))
# only if screensaver is not ok
if self._dbusConnections[self.CL_CONN_TK][self.CL_IF] is None and self._dbusConnections[self.CL_CONN_TK][self.CL_CNT] > 0 and not self._dbusConnections[self.CL_CONN_TK][self.CL_DEL] > 0:
try:
# dbus performance measurement
misc.measureDBUSTimeElapsed(pStart=True)
# getting interface
self._dbusConnections[self.CL_CONN_TK][self.CL_IF] = dbus.Interface(self._timekprBus.get_object(cons.TK_DBUS_BUS_NAME, cons.TK_DBUS_SERVER_PATH), cons.TK_DBUS_USER_LIMITS_INTERFACE)
# log
log.log(cons.TK_LOG_LEVEL_DEBUG, "CONNECTED to %s DBUS %s interface" % (self.CL_CONN_TK, self.CL_IF))
# log
log.log(cons.TK_LOG_LEVEL_INFO, "INFO: connected to timekpr limits service through \"%s\"" % (cons.TK_DBUS_USER_LIMITS_INTERFACE))
# getting interface
self._dbusConnections[self.CL_CONN_TK][self.CL_IFA] = dbus.Interface(self._timekprBus.get_object(cons.TK_DBUS_BUS_NAME, cons.TK_DBUS_SERVER_PATH), cons.TK_DBUS_USER_SESSION_ATTRIBUTE_INTERFACE)
# log
log.log(cons.TK_LOG_LEVEL_DEBUG, "CONNECTED to %s DBUS %s interface" % (self.CL_CONN_TK, self.CL_IFA))
# log
log.log(cons.TK_LOG_LEVEL_INFO, "INFO: connected to timekpr session attributes service through \"%s\"" % (cons.TK_DBUS_USER_SESSION_ATTRIBUTE_INTERFACE))
# measurement logging
misc.measureDBUSTimeElapsed(pStop=True, pDbusIFName=cons.TK_DBUS_USER_LIMITS_INTERFACE)
except Exception as dbusEx:
# reset
self._dbusConnections[self.CL_CONN_TK][self.CL_IF] = None
self._dbusConnections[self.CL_CONN_TK][self.CL_IFA] = None
# logging
log.log(cons.TK_LOG_LEVEL_INFO, "WARNING: initiating dbus connection (\"%s.%s\", %s, %s), error: %s" % (__name__, self.initClientConnections.__name__, self.CL_CONN_TK, cons.TK_DBUS_BUS_NAME, str(dbusEx)))
# retry?
doRetry = False
# all variants
for rConn in (self.CL_CONN_TK, self.CL_CONN_NOTIF, self.CL_CONN_SCR):
# if either of this fails, we keep trying to connect
if self._dbusConnections[rConn][self.CL_IF] is None:
# max retries
if self._dbusConnections[rConn][self.CL_CNT] > 0:
# only if delay is ended
if not self._dbusConnections[rConn][self.CL_DEL] > 0:
# decrease retries
self._dbusConnections[rConn][self.CL_CNT] -= 1
# continue if more retries available
if self._dbusConnections[rConn][self.CL_CNT] > 0:
# retry
doRetry = True
# do not take into account delay
if self._dbusConnections[rConn][self.CL_DEL] > 0:
# connection delayed
log.log(cons.TK_LOG_LEVEL_INFO, "INFO: dbus connection to %s delayed for %d more times" % (rConn, self._dbusConnections[rConn][self.CL_DEL]))
# decrease delay
self._dbusConnections[rConn][self.CL_DEL] -= 1
else:
# logging
log.log(cons.TK_LOG_LEVEL_ERR, "ERROR: failed to connect to %s dbus, trying again..." % (rConn))
else:
# connection aborted
log.log(cons.TK_LOG_LEVEL_WARN, "WARNING: dbus connection to %s failed, some functionality will not be available" % (rConn))
# retry
if doRetry:
# if either of this fails, we keep trying to connect
GLib.timeout_add_seconds(cons.TK_POLLTIME, self.initClientConnections)
# prepare notifications in case smth is not ok
else:
# let's inform user in case screensaver is not connected
if self._dbusConnections[self.CL_CONN_SCR][self.CL_IF] is None and self._timekprClientConfig.getClientShowAllNotifications():
# prepare notification
self.notifyUser(cons.TK_MSG_CODE_FEATURE_SCR_NOT_AVAILABLE_ERROR, None, cons.TK_PRIO_WARNING, pAdditionalMessage=self.CL_CONN_SCR)
log.log(cons.TK_LOG_LEVEL_DEBUG, "finish initClientConnections")
# finish
return False
def isTimekprConnected(self):
"""Return status of timekpr connection (nothing else, just timekpr itself)"""
return self._dbusConnections[self.CL_CONN_TK][self.CL_IF] is not None
def _prepareNotification(self, pMsgCode, pMsgType, pPriority, pTimeLeft=None, pAdditionalMessage=None):
"""Prepare the message to be sent to dbus notifications"""
log.log(cons.TK_LOG_LEVEL_DEBUG, "start prepareNotification")
# determine icon to use
timekprIcon = cons.TK_PRIO_CONF[cons.getNotificationPrioriy(pPriority)][cons.TK_ICON_NOTIF]
timekprPrio = cons.TK_PRIO_CONF[cons.getNotificationPrioriy(pPriority)][cons.TK_DBUS_PRIO]
# calculate hours in advance
if pTimeLeft is not None:
timeLeftHours = (pTimeLeft - cons.TK_DATETIME_START).days * 24 + pTimeLeft.hour
# determine the message to pass
if pMsgCode == cons.TK_MSG_CODE_TIMEUNLIMITED:
# no limit
msgStr = msg.getTranslation("TK_MSG_NOTIFICATION_NOT_LIMITED")
elif pMsgCode == cons.TK_MSG_CODE_TIMELEFT:
# msg
msgStr = " ".join((msg.getTranslation("TK_MSG_NOTIFICATION_TIME_LEFT_1", timeLeftHours), msg.getTranslation("TK_MSG_NOTIFICATION_TIME_LEFT_2", pTimeLeft.minute), msg.getTranslation("TK_MSG_NOTIFICATION_PLAYTIME_LEFT_3" if pMsgType == "PlayTime" else "TK_MSG_NOTIFICATION_TIME_LEFT_3", pTimeLeft.second)))
elif pMsgCode == cons.TK_MSG_CODE_TIMECRITICAL:
# depending on type
if pMsgType == cons.TK_CTRL_RES_L:
msgCode = "TK_MSG_NOTIFICATION_TIME_IS_UP_1L"
elif pMsgType in (cons.TK_CTRL_RES_S, cons.TK_CTRL_RES_W):
msgCode = "TK_MSG_NOTIFICATION_TIME_IS_UP_1S"
elif pMsgType == cons.TK_CTRL_RES_D:
msgCode = "TK_MSG_NOTIFICATION_TIME_IS_UP_1D"
else:
msgCode = "TK_MSG_NOTIFICATION_TIME_IS_UP_1T"
# msg
msgStr = " ".join((msg.getTranslation(msgCode), msg.getTranslation("TK_MSG_NOTIFICATION_TIME_IS_UP_2", pTimeLeft.second)))
elif pMsgCode == cons.TK_MSG_CODE_TIMELEFTCHANGED:
# msg
msgStr = msg.getTranslation("TK_MSG_NOTIFICATION_ALLOWANCE_CHANGED")
elif pMsgCode == cons.TK_MSG_CODE_TIMECONFIGCHANGED:
# msg
msgStr = msg.getTranslation("TK_MSG_NOTIFICATION_CONFIGURATION_CHANGED")
elif pMsgCode == cons.TK_MSG_CODE_REMOTE_COMMUNICATION_ERROR:
# msg
msgStr = msg.getTranslation("TK_MSG_NOTIFICATION_CANNOT_CONNECT") % (pAdditionalMessage)
elif pMsgCode == cons.TK_MSG_CODE_REMOTE_INVOCATION_ERROR:
# msg
msgStr = msg.getTranslation("TK_MSG_NOTIFICATION_CANNOT_COMMUNICATE") % (pAdditionalMessage)
elif pMsgCode == cons.TK_MSG_CODE_ICON_INIT_ERROR:
# msg
msgStr = msg.getTranslation("TK_MSG_NOTIFICATION_CANNOT_INIT_ICON") % (pAdditionalMessage)
elif pMsgCode == cons.TK_MSG_CODE_FEATURE_SCR_NOT_AVAILABLE_ERROR:
# msg
msgStr = msg.getTranslation("TK_MSG_NOTIFICATION_SCR_FEATURE_NOT_AVAILABLE") % (pAdditionalMessage)
log.log(cons.TK_LOG_LEVEL_DEBUG, "finish prepareNotification")
# pass this back
return timekprIcon, msgStr, timekprPrio
def notifyUser(self, pMsgCode, pMsgType, pPriority, pTimeLeft=None, pAdditionalMessage=None):
"""Notify the user."""
# if we have dbus connection, let"s do so
if self._dbusConnections[self.CL_CONN_NOTIF][self.CL_IF] is None:
# init
self.initClientConnections()
# if not then return
if not self.isTimekprConnected():
return
# can we notify user
if self._dbusConnections[self.CL_CONN_NOTIF][self.CL_IF] is not None:
# prepare notification
timekprIcon, msgStr, timekprPrio = self._prepareNotification(pMsgCode, pMsgType, pPriority, pTimeLeft, pAdditionalMessage)
# defaults
hints = {"urgency": timekprPrio}
# notification params based on criticality
if pPriority in (cons.TK_PRIO_CRITICAL, cons.TK_PRIO_IMPORTANT):
# timeout
notificationTimeout = self._timekprClientConfig.getClientNotificationTimeoutCritical()
# sound
if self._timekprClientConfig.getIsNotificationSoundSupported() and self._timekprClientConfig.getClientUseNotificationSound():
# add sound hint
if cons.TK_CL_NOTIF_SND_TYPE == "sound-name":
hints["sound-name"] = cons.TK_CL_NOTIF_SND_NAME_IMPORTANT
else:
hints["sound-file"] = cons.TK_CL_NOTIF_SND_FILE_CRITICAL
else:
# timeout
notificationTimeout = self._timekprClientConfig.getClientNotificationTimeout()
# sound
if self._timekprClientConfig.getIsNotificationSoundSupported() and self._timekprClientConfig.getClientUseNotificationSound():
# add sound hint
if cons.TK_CL_NOTIF_SND_TYPE == "sound-name":
hints["sound-name"] = cons.TK_CL_NOTIF_SND_NAME_WARNING
else:
hints["sound-file"] = cons.TK_CL_NOTIF_SND_FILE_WARN
# calculate last time notification is shown (if this is too recent - replace, otherwise add new notification)
if pMsgType == "PlayTime" and self._lastPTNotifId != 0 and abs((datetime.now() - self._lastPTNotifDT).total_seconds()) >= (notificationTimeout if notificationTimeout > 0 else abs((datetime.now() - self._lastPTNotifDT).total_seconds()) + 1):
self._lastPTNotifId = 0
elif pMsgType != "PlayTime" and self._lastNotifId != 0 and abs((datetime.now() - self._lastNotifDT).total_seconds()) >= (notificationTimeout if notificationTimeout > 0 else abs((datetime.now() - self._lastNotifDT).total_seconds()) + 1):
self._lastNotifId = 0
# calculate notification values
notificationTimeout = min(cons.TK_CL_NOTIF_MAX, max(0, notificationTimeout)) * 1000
# notification value of 0 means "forever"
actions = ["OK", "OK"] if notificationTimeout == 0 else []
log.log(cons.TK_LOG_LEVEL_DEBUG, "preshow: %s, %s, %i" % (msg.getTranslation("TK_MSG_NOTIFICATION_PLAYTIME_TITLE" if pMsgType == "PlayTime" else "TK_MSG_NOTIFICATION_TITLE"), msgStr, notificationTimeout))
# notify through dbus
try:
# before
notifId = 0
# call dbus method
notifId = self._dbusConnections[self.CL_CONN_NOTIF][self.CL_IF].Notify(
"Timekpr"
,self._lastPTNotifId if pMsgType == "PlayTime" else self._lastNotifId
,timekprIcon
,msg.getTranslation("TK_MSG_NOTIFICATION_PLAYTIME_TITLE" if pMsgType == "PlayTime" else "TK_MSG_NOTIFICATION_TITLE")
,msgStr
,actions
,hints
,notificationTimeout
)
except Exception as dbusEx:
# do not need to reconnect when too many notifs
if "ExcessNotificationGeneration" not in str(dbusEx):
# we cannot send notif through dbus
self._dbusConnections[self.CL_CONN_NOTIF][self.CL_IF] = None
# logging
log.log(cons.TK_LOG_LEVEL_ERR, "ERROR (DBUS): \"%s\" in \"%s.%s\"" % (str(dbusEx), __name__, self.notifyUser.__name__))
else:
# logging
log.log(cons.TK_LOG_LEVEL_WARN, "WARNING (DBUS): \"%s\" in \"%s.%s\"" % (str(dbusEx), __name__, self.notifyUser.__name__))
# save notification ID (only if message is not about PlayTime, otherwise it may dismiss standard time or vice versa)
if pMsgType == "PlayTime":
self._lastPTNotifId = notifId
self._lastPTNotifDT = datetime.now()
else:
self._lastNotifId = notifId
self._lastNotifDT = datetime.now()
# user wants to hear things
if self._timekprClientConfig.getIsNotificationSpeechSupported() and self._timekprClientConfig.getClientUseSpeechNotifications():
# say that out loud
self._timekprSpeechManager.saySmth(msgStr)
# --------------- admininstration / verification methods --------------- #
def verifySessionAttributes(self, pWhat, pKey):
"""Receive the signal and process the data"""
log.log(cons.TK_LOG_LEVEL_DEBUG, "prepare verification of attributes for server: %s, %s" % (pWhat, "key"))
# def
value = None
# for screensaver status
if pWhat == cons.TK_CTRL_SCR_N:
# value
value = str(bool(self._dbusConnections[self.CL_CONN_SCR][self.CL_IF].GetActive()))
# resend stuff to server
self.processUserSessionAttributes(pWhat, pKey, value)
# --------------- admininstration / verification signals --------------- #
def receiveScreenSaverActivityChange(self, pIsActive):
"""Receive the signal and process the data"""
log.log(cons.TK_LOG_LEVEL_DEBUG, "receive screensaver activity changes: %s" % (str(bool(pIsActive))))
# request to server for verification
self.processUserSessionAttributes(cons.TK_CTRL_SCR_N)
def receiveNotificationClosed(self, pNotifId, pReason):
"""Receive the signal and process the data"""
log.log(cons.TK_LOG_LEVEL_DEBUG, "receive notification closed: %i, %i" % (pNotifId, pReason))
# check and reset which notification has changed
if self._lastNotifId == pNotifId:
self._lastNotifId = 0
elif self._lastPTNotifId == pNotifId:
self._lastPTNotifId = 0
# --------------- request methods to timekpr --------------- #
def requestTimeLeft(self):
"""Request time left from server"""
# if we have dbus connection, let"s do so
if self._dbusConnections[self.CL_CONN_TK][self.CL_IF] is None:
# init
self.initClientConnections()
# if we have end-point
if self._dbusConnections[self.CL_CONN_TK][self.CL_IF] is not None:
log.log(cons.TK_LOG_LEVEL_INFO, "requesting timeleft")
# notify through dbus
try:
# call dbus method
result, message = self._dbusConnections[self.CL_CONN_TK][self.CL_IF].requestTimeLeft(self._userName)
# check call result
if result != 0:
# show message to user as well
self.notifyUser(cons.TK_MSG_CODE_REMOTE_INVOCATION_ERROR, None, cons.TK_PRIO_CRITICAL, pAdditionalMessage=message)
except Exception as dbusEx:
# we cannot send notif through dbus
self._dbusConnections[self.CL_CONN_TK][self.CL_IF] = None
# logging
log.log(cons.TK_LOG_LEVEL_ERR, "ERROR (DBUS): \"%s\" in \"%s.%s\"" % (str(dbusEx), __name__, self.requestTimeLeft.__name__))
# show message to user as well
self.notifyUser(cons.TK_MSG_CODE_REMOTE_COMMUNICATION_ERROR, None, cons.TK_PRIO_CRITICAL, pAdditionalMessage=msg.getTranslation("TK_MSG_NOTIFICATION_CONNECTION_ERROR"))
def requestTimeLimits(self):
"""Request time limits from server"""
# if we have dbus connection, let"s do so
if self._dbusConnections[self.CL_CONN_TK][self.CL_IF] is None:
# init
self.initClientConnections()
# if we have end-point
if self._dbusConnections[self.CL_CONN_TK][self.CL_IF] is not None:
log.log(cons.TK_LOG_LEVEL_INFO, "requesting timelimits")
# notify through dbus
try:
# call dbus method
result, message = self._dbusConnections[self.CL_CONN_TK][self.CL_IF].requestTimeLimits(self._userName)
# check call result
if result != 0:
# show message to user as well
self.notifyUser(cons.TK_MSG_CODE_REMOTE_INVOCATION_ERROR, None, cons.TK_PRIO_CRITICAL, pAdditionalMessage=message)
except Exception as dbusEx:
# we cannot send notif through dbus
self._dbusConnections[self.CL_CONN_TK][self.CL_IF] = None
# logging
log.log(cons.TK_LOG_LEVEL_ERR, "ERROR (DBUS): \"%s\" in \"%s.%s\"" % (str(dbusEx), __name__, self.requestTimeLimits.__name__))
# show message to user as well
self.notifyUser(cons.TK_MSG_CODE_REMOTE_COMMUNICATION_ERROR, None, cons.TK_PRIO_CRITICAL, pAdditionalMessage=msg.getTranslation("TK_MSG_NOTIFICATION_CONNECTION_ERROR"))
def processUserSessionAttributes(self, pWhat, pKey=None, pValue=None):
"""Process user session attributes from server"""
# if we have dbus connection, let"s do so
if self._dbusConnections[self.CL_CONN_TK][self.CL_IFA] is None:
# init
self.initClientConnections()
# if we have end-point
if self._dbusConnections[self.CL_CONN_TK][self.CL_IFA] is not None:
log.log(cons.TK_LOG_LEVEL_INFO, "%s session attributes" % ("requesting" if pKey is None else "verifying"))
# notify through dbus
try:
# call dbus method
result, message = self._dbusConnections[self.CL_CONN_TK][self.CL_IFA].processUserSessionAttributes(
self._userName,
dbus.String(pWhat if pWhat is not None else ""),
dbus.String(pKey if pKey is not None else ""),
dbus.String(pValue if pValue is not None else ""))
# check call result
if result != 0:
# show message to user as well
self.notifyUser(cons.TK_MSG_CODE_REMOTE_INVOCATION_ERROR, None, cons.TK_PRIO_CRITICAL, None, pAdditionalMessage=message)
except Exception as dbusEx:
# we cannot send notif through dbus
self._dbusConnections[self.CL_CONN_TK][self.CL_IF] = None
self._dbusConnections[self.CL_CONN_TK][self.CL_IFA] = None
# logging
log.log(cons.TK_LOG_LEVEL_INFO, "ERROR (DBUS): \"%s\" in \"%s.%s\"" % (str(dbusEx), __name__, self.processUserSessionAttributes.__name__))
# show message to user as well
self.notifyUser(cons.TK_MSG_CODE_REMOTE_COMMUNICATION_ERROR, None, cons.TK_PRIO_CRITICAL, pAdditionalMessage=msg.getTranslation("TK_MSG_NOTIFICATION_CONNECTION_ERROR"))
timekpr-next/client/interface/dbus/__init__.py 000664 001750 001750 00000000000 13476006650 023456 0 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/client/interface/dbus/administration.py 000644 001750 001750 00000110362 15122253261 024747 0 ustar 00bezvfedu bezvfedu 000000 000000 """
Created on Aug 28, 2018
@author: mjasnik
"""
# import
import dbus
from gi.repository import GLib
from dbus.mainloop.glib import DBusGMainLoop
# 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.constants import messages as msg
# default loop
DBusGMainLoop(set_as_default=True)
class timekprAdminConnector(object):
"""Main class for supporting indicator notifications"""
def __init__(self):
"""Initialize stuff for connecting to timekpr server"""
# times
self._retryCountLeft = 10
self._initFailed = False
self._isCLI = False
# dbus (timekpr)
self._timekprBus = (dbus.SessionBus() if (cons.TK_DEV_ACTIVE and cons.TK_DEV_BUS == "ses") else dbus.SystemBus())
self._timekprObject = None
self._timekprUserAdminDbusInterface = None
self._timekprAdminDbusInterface = None
def initTimekprConnection(self, pTryOnce, pRescheduleConnection=False, pCLI=None):
"""Init dbus (connect to timekpr for info)"""
# def
if pCLI is not None:
self._isCLI = pCLI
# reschedule
if pRescheduleConnection:
# rescheduling means dropping existing state and try again
self._timekprObject = None
self._timekprUserAdminDbusInterface = None
self._timekprAdminDbusInterface = None
self._retryCountLeft = 10
self._initFailed = False
# only if notifications are ok
if self._timekprObject is None:
try:
# dbus performance measurement
misc.measureDBUSTimeElapsed(pStart=True)
# timekpr connection stuff
self._timekprObject = self._timekprBus.get_object(cons.TK_DBUS_BUS_NAME, cons.TK_DBUS_SERVER_PATH)
# measurement logging
misc.measureDBUSTimeElapsed(pStop=True, pPrintToConsole=True, pDbusIFName=cons.TK_DBUS_BUS_NAME)
except Exception:
self._timekprObject = None
# logging
lMsg = "FAILED to obtain connection to timekpr. Please check that timekpr daemon is working and you have sufficient permissions to access it (either superuser or timekpr group)"
log.log(cons.TK_LOG_LEVEL_ERR, lMsg)
# console too, if CLI
if self._isCLI:
log.consoleOut(lMsg)
# only if notifications are ok
if self._timekprObject is not None and self._timekprUserAdminDbusInterface is None:
try:
# dbus performance measurement
misc.measureDBUSTimeElapsed(pStart=True)
# getting interface
self._timekprUserAdminDbusInterface = dbus.Interface(self._timekprObject, cons.TK_DBUS_USER_ADMIN_INTERFACE)
# measurement logging
misc.measureDBUSTimeElapsed(pStop=True, pPrintToConsole=True, pDbusIFName=cons.TK_DBUS_USER_ADMIN_INTERFACE)
except Exception:
self._timekprUserAdminDbusInterface = None
# logging
lMsg = "FAILED to connect to timekpr user admin interface. Please check that timekpr daemon is working and you have sufficient permissions to access it (either superuser or timekpr group)"
log.log(cons.TK_LOG_LEVEL_ERR, lMsg)
# console too, if CLI
if self._isCLI:
log.consoleOut(lMsg)
# only if notifications are ok
if self._timekprObject is not None and self._timekprAdminDbusInterface is None:
try:
# dbus performance measurement
misc.measureDBUSTimeElapsed(pStart=True)
# getting interface
self._timekprAdminDbusInterface = dbus.Interface(self._timekprObject, cons.TK_DBUS_ADMIN_INTERFACE)
# measurement logging
misc.measureDBUSTimeElapsed(pStop=True, pPrintToConsole=True, pDbusIFName=cons.TK_DBUS_ADMIN_INTERFACE)
except Exception:
self._timekprAdminDbusInterface = None
# logging
lMsg = "FAILED to connect to timekpr user admin interface. Please check that timekpr daemon is working and you have sufficient permissions to access it (either superuser or timekpr group)"
log.log(cons.TK_LOG_LEVEL_ERR, lMsg)
# console too, if CLI
if self._isCLI:
log.consoleOut(lMsg)
# if either of this fails, we keep trying to connect
if self._timekprUserAdminDbusInterface is None or self._timekprAdminDbusInterface is None:
if self._retryCountLeft > 0 and not pTryOnce:
self._retryCountLeft -= 1
lMsg = "connection failed, %i attempts left, will retry in %i seconds" % (self._retryCountLeft, cons.TK_POLLTIME)
log.log(cons.TK_LOG_LEVEL_ERR, lMsg)
# console too, if CLI
if self._isCLI:
log.consoleOut(lMsg)
# if either of this fails, we keep trying to connect
GLib.timeout_add_seconds(cons.TK_POLLTIME, self.initTimekprConnection, pTryOnce)
else:
# failed
self._initFailed = True
# --------------- helper methods --------------- #
def isConnected(self):
"""Return status of connection to DBUS"""
# if either of this fails, we keep trying to connect
return not (self._timekprUserAdminDbusInterface is None or self._timekprAdminDbusInterface is None), not self._initFailed
def formatException(self, pExceptionStr, pFPath, pFName):
"""Format exception and pass it back"""
# check for permission error
if "org.freedesktop.DBus.Error.AccessDenied" in pExceptionStr:
result = -1
message = msg.getTranslation("TK_MSG_DBUS_COMMUNICATION_COMMAND_FAILED")
else:
result = -1
message = msg.getTranslation("TK_MSG_UNEXPECTED_ERROR") % (("\"%s\" in \"%s.%s\"") % (pExceptionStr, pFPath, pFName))
# log error
log.log(cons.TK_LOG_LEVEL_ERR, "ERROR: \"%s\" in \"%s.%s\"" % (pExceptionStr, pFPath, pFName))
# result
return result, message
def initReturnCodes(self, pInit, pCall):
"""Initialize the return codes for calls"""
return -2 if pInit else -1 if pCall else 0, msg.getTranslation("TK_MSG_STATUS_INTERFACE_NOTREADY") if pInit else msg.getTranslation("TK_MSG_DBUS_COMMUNICATION_COMMAND_NOT_ACCEPTED") if pCall else ""
# --------------- user configuration info population methods --------------- #
def getUserList(self):
"""Get user list from server"""
# defaults
result, message = self.initReturnCodes(pInit=True, pCall=False)
userList = []
# if we have end-point
if self._timekprUserAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message, userList = self._timekprUserAdminDbusInterface.getUserList()
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.getUserList.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message, userList
def getUserConfigurationAndInformation(self, pUserName, pInfoLvl):
"""Get user configuration from server"""
# defaults
result, message = self.initReturnCodes(pInit=True, pCall=False)
userConfig = {}
# if we have end-point
if self._timekprUserAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message, userConfig = self._timekprUserAdminDbusInterface.getUserInformation(pUserName, pInfoLvl)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.getUserConfigurationAndInformation.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message, userConfig
# --------------- user configuration set methods --------------- #
def setAllowedDays(self, pUserName, pDayList):
"""Set user allowed days"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprUserAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprUserAdminDbusInterface.setAllowedDays(pUserName, pDayList)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setAllowedDays.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setAllowedHours(self, pUserName, pDayNumber, pHourList):
"""Set user allowed days"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprUserAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprUserAdminDbusInterface.setAllowedHours(pUserName, pDayNumber, pHourList)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setAllowedHours.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setTimeLimitForDays(self, pUserName, pDayLimits):
"""Set user allowed limit for days"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprUserAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprUserAdminDbusInterface.setTimeLimitForDays(pUserName, pDayLimits)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setTimeLimitForDays.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setTimeLimitForWeek(self, pUserName, pTimeLimitWeek):
"""Set user allowed limit for week"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprUserAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprUserAdminDbusInterface.setTimeLimitForWeek(pUserName, pTimeLimitWeek)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setTimeLimitForWeek.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setTimeLimitForMonth(self, pUserName, pTimeLimitMonth):
"""Set user allowed limit for month"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprUserAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprUserAdminDbusInterface.setTimeLimitForMonth(pUserName, pTimeLimitMonth)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setTimeLimitForMonth.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setTrackInactive(self, pUserName, pTrackInactive):
"""Set user allowed days"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprUserAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprUserAdminDbusInterface.setTrackInactive(pUserName, pTrackInactive)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.getUserList.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setHideTrayIcon(self, pUserName, pHideTrayIcon):
"""Set user allowed days"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprUserAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprUserAdminDbusInterface.setHideTrayIcon(pUserName, pHideTrayIcon)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setHideTrayIcon.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setLockoutType(self, pUserName, pLockoutType, pWakeFrom, pWakeTo):
"""Set user restriction / lockout type"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprUserAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprUserAdminDbusInterface.setLockoutType(pUserName, pLockoutType, pWakeFrom, pWakeTo)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setLockoutType.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setTimeLeft(self, pUserName, pOperation, pTimeLeft):
"""Set user time left"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprUserAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprUserAdminDbusInterface.setTimeLeft(pUserName, pOperation, pTimeLeft)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setTimeLeft.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
# --------------- PlayTime user configuration info set methods --------------- #
def setPlayTimeEnabled(self, pUserName, pPlayTimeEnabled):
"""Set PlayTime enabled flag for user"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprUserAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprUserAdminDbusInterface.setPlayTimeEnabled(pUserName, pPlayTimeEnabled)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setPlayTimeEnabled.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setPlayTimeLimitOverride(self, pUserName, pPlayTimeLimitOverride):
"""Set PlayTime override flag for user"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprUserAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprUserAdminDbusInterface.setPlayTimeLimitOverride(pUserName, pPlayTimeLimitOverride)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setPlayTimeLimitOverride.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setPlayTimeUnaccountedIntervalsEnabled(self, pUserName, pPlayTimeUnaccountedIntervalsEnabled):
"""Set PlayTime allowed during unaccounted intervals flag for user"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprUserAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprUserAdminDbusInterface.setPlayTimeUnaccountedIntervalsEnabled(pUserName, pPlayTimeUnaccountedIntervalsEnabled)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setPlayTimeUnaccountedIntervalsEnabled.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setPlayTimeAllowedDays(self, pUserName, pPlayTimeAllowedDays):
"""Set allowed days for PlayTime for user"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprUserAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprUserAdminDbusInterface.setPlayTimeAllowedDays(pUserName, pPlayTimeAllowedDays)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setPlayTimeAllowedDays.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setPlayTimeLimitsForDays(self, pUserName, pPlayTimeLimits):
"""Set PlayTime limits for the allowed days for the user"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprUserAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprUserAdminDbusInterface.setPlayTimeLimitsForDays(pUserName, pPlayTimeLimits)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setPlayTimeLimitsForDays.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setPlayTimeActivities(self, pUserName, pPlayTimeActivities):
"""Set PlayTime limits for the allowed days for the user"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprUserAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprUserAdminDbusInterface.setPlayTimeActivities(pUserName, pPlayTimeActivities)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setPlayTimeActivities.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setPlayTimeLeft(self, pUserName, pOperation, pTimeLeft):
"""Set user time left"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprUserAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprUserAdminDbusInterface.setPlayTimeLeft(pUserName, pOperation, pTimeLeft)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setPlayTimeLeft.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
# --------------- timekpr configuration info population / set methods --------------- #
def getTimekprConfiguration(self):
"""Get configuration from server"""
# defaults
result, message = self.initReturnCodes(pInit=True, pCall=False)
timekprConfig = {}
# if we have end-point
if self._timekprAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message, timekprConfig = self._timekprAdminDbusInterface.getTimekprConfiguration()
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.getTimekprConfiguration.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message, timekprConfig
def setTimekprLogLevel(self, pLogLevel):
"""Set the logging level for server"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprAdminDbusInterface.setTimekprLogLevel(pLogLevel)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setTimekprLogLevel.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setTimekprPollTime(self, pPollTimeSecs):
"""Set polltime for timekpr"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprAdminDbusInterface.setTimekprPollTime(pPollTimeSecs)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setTimekprPollTime.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setTimekprSaveTime(self, pSaveTimeSecs):
"""Set save time for timekpr"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprAdminDbusInterface.setTimekprSaveTime(pSaveTimeSecs)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setTimekprSaveTime.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setTimekprTrackInactive(self, pTrackInactive):
"""Set default value for tracking inactive sessions"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprAdminDbusInterface.setTimekprTrackInactive(pTrackInactive)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setTimekprTrackInactive.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setTimekprTerminationTime(self, pTerminationTimeSecs):
"""Set up user termination time"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprAdminDbusInterface.setTimekprTerminationTime(pTerminationTimeSecs)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setTimekprTerminationTime.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setTimekprFinalWarningTime(self, pFinalWarningTimeSecs):
"""Set up final warning time for users"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprAdminDbusInterface.setTimekprFinalWarningTime(pFinalWarningTimeSecs)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setTimekprFinalWarningTime.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setTimekprFinalNotificationTime(self, pFinalNotificationTimeSecs):
"""Set up final notification time for users"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprAdminDbusInterface.setTimekprFinalNotificationTime(pFinalNotificationTimeSecs)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setTimekprFinalNotificationTime.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setTimekprSessionsCtrl(self, pSessionsCtrl):
"""Set accountable session types for users"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprAdminDbusInterface.setTimekprSessionsCtrl(pSessionsCtrl)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setTimekprSessionsCtrl.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setTimekprSessionsExcl(self, pSessionsExcl):
"""Set NON-accountable session types for users"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprAdminDbusInterface.setTimekprSessionsExcl(pSessionsExcl)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setTimekprSessionsExcl.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setTimekprUsersExcl(self, pUsersExcl):
"""Set excluded usernames for timekpr"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprAdminDbusInterface.setTimekprUsersExcl(pUsersExcl)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setTimekprUsersExcl.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setTimekprPlayTimeEnabled(self, pPlayTimeEnabled):
"""Set up global PlayTime enable switch"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprAdminDbusInterface.setTimekprPlayTimeEnabled(pPlayTimeEnabled)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setTimekprPlayTimeEnabled.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
def setTimekprPlayTimeEnhancedActivityMonitorEnabled(self, pPlayTimeEnabled):
"""Set up global PlayTime enhanced activity monitor enable switch"""
# initial values
result, message = self.initReturnCodes(pInit=True, pCall=False)
# if we have end-point
if self._timekprAdminDbusInterface is not None:
# defaults
result, message = self.initReturnCodes(pInit=False, pCall=True)
# notify through dbus
try:
# call dbus method
result, message = self._timekprAdminDbusInterface.setTimekprPlayTimeEnhancedActivityMonitorEnabled(pPlayTimeEnabled)
except Exception as ex:
# exception
result, message = self.formatException(str(ex), __name__, self.setTimekprPlayTimeEnhancedActivityMonitorEnabled.__name__)
# we cannot send notif through dbus, we need to reschedule connecton
self.initTimekprConnection(False, True)
# result
return result, message
timekpr-next/debian/ 000775 001750 001750 00000000000 15122253261 016416 5 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/debian/timekpr-next.lintian-overrides 000600 001750 001750 00000000155 14575617250 024431 0 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next binary: desktop-command-not-in-package pkexec [usr/share/applications/timekpr-admin-su.desktop]
timekpr-next/debian/postinst 000700 001750 001750 00000004657 14575617135 020246 0 ustar 00bezvfedu bezvfedu 000000 000000 #!/bin/sh
# timekpr directories and units
TK_DIR="/usr/lib/python3/dist-packages/timekpr"
# unit name
TK_UNIT_NAME="timekpr.service"
# unit directory (starting with debian)
TK_UNIT_DIR="/lib/systemd/system"
# prefixes for other distros then debian based
TK_UNIT_DIR_PREFIXES="/usr"
# we need to take into account the various directory configs
for R_PREF in ${TK_UNIT_DIR_PREFIXES};
do
# alternatives exist
if [ -d "${R_PREF}${TK_UNIT_DIR}" ];
then
# assign additional prefix
TK_UNIT_DIR="${R_PREF}${TK_UNIT_DIR}"
# this is it
break
fi
done
# final unit
TK_UNIT="${TK_UNIT_DIR}/${TK_UNIT_NAME}"
# add timekpr group (for accessing administration functionality without sudo)
echo "Checking and/or adding timekpr group to access functionality without superuser privileges"
groupadd -g 2000 timekpr 2>/dev/null || true
# remove compiled python units (may interfere with python versions)
for R_DIR in ${TK_DIR};
do
# if compiled units exist, remove them
if [ -d "${R_DIR}" ];
then
# remove compiled
echo "Cleaning up Timekpr-nExT compiled units"
find "${R_DIR}" -type d -name '__pycache__' -exec rm -rf {} \; -prune
find "${R_DIR}" -type f -iname '*.pyc' -exec rm {} \;
fi
done
# previously we installed unit into /etc, we need to take care of it
TK_OBSOLETE_UNIT="/etc/systemd/system/${TK_UNIT_NAME}"
if [ -f "${TK_OBSOLETE_UNIT}" ];
then
echo "Removing obsolete Timekpr-nExT systemd unit"
rm "${TK_OBSOLETE_UNIT}"
fi
# enable service by default and start it
echo "Enabling Timekpr-nExT systemd unit"
systemctl enable "${TK_UNIT}"
# reload d-bus for access controls and systemd for service removals
echo "Reloading dbus daemon to refresh access policies for Timekpr-nExT daemon"
systemctl reload dbus
echo "Reloading systemd to refresh unit for Timekpr-nExT"
systemctl daemon-reload
# restart or start (if that was not started before)
echo "Starting Timekpr-nExT"
systemctl restart timekpr
# update gtk icon cache (if utility exists)
which gtk-update-icon-cache > /dev/null 2>&1
# utility exists, update cache
if [ $? -eq 0 ];
then
# determine whether cache exists and choose particular args for that
if [ -f /usr/share/icons/hicolor/icon-theme.cache ];
then
ICOARGS="-fv"
else
ICOARGS="-f"
fi
echo "Update icon cache"
touch --no-create /usr/share/icons/hicolor && gtk-update-icon-cache ${ICOARGS} /usr/share/icons/hicolor
fi
timekpr-next/debian/source/ 000775 001750 001750 00000000000 13476006650 017726 5 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/debian/source/format 000664 001750 001750 00000000016 13476006650 021136 0 ustar 00bezvfedu bezvfedu 000000 000000 3.0 (native)
timekpr-next/debian/compat 000664 001750 001750 00000000002 13476006650 017624 0 ustar 00bezvfedu bezvfedu 000000 000000 9
timekpr-next/debian/copyright 000644 001750 001750 00000000574 15122253204 020352 0 ustar 00bezvfedu bezvfedu 000000 000000 This package was debianized by Eduards Bezverhijs (mjasnik)
on Sun, 23 Sep 2018 01:10:17 +0200.
Upstream Authors:
Eduards Bezverhijs
Copyright:
2018-2024 Eduards Bezverhijs
All code is released under the GPLv3, see
/usr/share/common-licenses/GPL-3
With the exception of the artwork SVG source files,
which are released to the Public Domain.
timekpr-next/debian/changelog 000644 001750 001750 00000105722 15122254113 020272 0 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next (0.5.9-1ubuntu1~ppa1) unstable; urgency=medium
* fixed a bug in notifications when time is not limited was shown even the time was limited
this bug manifested itself only when there were only weekly or monthly time limits enforced on users
* fixed a bug with dbus notification signals for unknown python version (MR !492908 from alexandre-nunes / Houston4444)
* fixed a bug with config parser (#2130832)
in python 3.14 it became totally not fit for my purpose, rewrote config handling completely
* added more verbose error logging
* added synonyms for information retrieval options in CLI:
--getuserlist is the same as --userlist
--getuserinfo is the same as --userinfo
--getuserinfort is the same as --userinfort
* added possibility for CLI version to specify time as time string for those who cannot or don't want to convert time to seconds:
time can be specified as 1h10m10s as opposed to 4210 seconds previously
this affects the following options:
--settimelimits
--settimelimitweek
--settimelimitmonth
--settimeleft
--setplaytimelimits
--setplaytimeleft
time can be returned either in seconds (as currently) and in time string if "-h" is passed to information retrieval options:
--getuserinfo
--getuserinfort
* fixed a hint / description for "Hide icon and notifications" checkbox
* added fr_CA and en_GB translations from LP
* fix the shebangs, feng-shui style
* added support for dynamic users which are not specified in passwd (for example, gdm-greeter)
* added support for journald logging
timekpr logs are pushed to systemd logging facilities, if available
fallback to standard logging
* lock sessions are now performed from login1 manager interface rather than from user session one
-- Eduards Bezverhijs Mon, 22 Dec 2025 16:39:21 +0200
timekpr-next (0.5.8-1ubuntu1~ppa1) unstable; urgency=medium
* added translations for Russian, Dutch, Portugese languages
* improved sound notificatons:
added support for espeak-ng
system "bell" notifications are no longer relient on freedesktop sound files
* packaging warning fixes (#2089143)
* enable standard window close button (workaround for #2091011)
* fixes for .rpm packaging from Alexey (#2091080)
* improve pidfile handling (#2097253)
* added an option to kill sessions instead of terminate
at least Kubuntu 24.04 will not terminate sessions with TerminateSession, it works fine with KillSession + SIGTERM
* implement a workaround for weird CMOS date reset bug which results time being reported 55 years in the future
which results in a lot of free time for the subordinate (this is exceptionally rare condition)
-- Eduards Bezverhijs Sat, 10 May 2025 17:29:42 +0300
timekpr-next (0.5.7-1ubuntu1~ppa1) UNRELEASED; urgency=medium
* improve username and user shell filter (fixes #2061003)
usernames which do not conform to standard username patterns are filtered out
users with no login shell are filtered out
-- Eduards Bezverhijs Thu, 30 May 2024 21:09:45 +0300
timekpr-next (0.5.6-1ubuntu1~ppa1) UNRELEASED; urgency=medium
* remove UUID upper boundary check (fixes #2046222)
any user whose ID is greater than UUID_MIN is considered controllable by timekpr
this helps with systemd-homed, remote LDAP logins etc, where UID is arbitrary high
* fix a long standding bug when weekly limits are reset together with monthly limits
this occurs only when new month starts in the middle of the week
* improve session time accounting for display / reporting purposes
actually account and display all active and sleep time in case user is logged in for more than 2 days or even more than a week
* color "apply" buttons and tab pages in red
to warn users that there are pending changes to apply
* added keyboard navigation focus order to list type of elements (where there are ediable columns)
swapped day limit and enabled columns due to weird GTK behaviour when toggle can not be activated using keyboard as first editable item in list
* disable search boxes on list elements as they pop up using keyboard navigation
they are not needed and just confuse users
* fix "verify" button enablement
it was enabled even when user clicks in and out of "from" field and it contained 0 hour
* made lists a little wider for adwaita theme not to show horizontal scrollbar
* fix row positioning "Gtk-CRITICAL" when there is no rows in the list
* minor dbus performance measurement optimization
* prevent saving / processing of duplicate timekpr configuration which can happen very rarely
* when user sets / saves configuration or timekpr loads it from file, log it to the log file
this way the log file will contain configuration too and that will be enough to ask for debugging if smth goes south
* added new "realtime" information get method to CLI + translations
* trying to cover as much DE as we can for idle time accounting (still have to be compatible with freedesktop / gnome standard)
add more delay to connect to screensaver (hopefully that helps computers with slower DE start)
rewrote screensaver dbus name detection based on DE name - this should help with some DEs which are
less known but still have a compatible screensaver interface
fall back to gnome and then to freedesktop interfaces (gnome is more popular, freedesktop - very few actually implement it)
if nothing else is detected and works
-- Eduards Bezverhijs Sun, 17 Mar 2024 18:02:25 +0200
timekpr-next (0.5.5-1ubuntu1~ppa1) UNRELEASED; urgency=medium
* add initial support for systemd-homed users and systemd-homed dynamic users
* Spanish translations, thanks to Ivan L.
* add couple of Debian specific packaging fixes
* check for login.defs in multiple possible directories (for example, openSUSE moved the file to /usr)
-- Eduards Bezverhijs Fri, 1 Sep 2023 13:22:13 +0300
timekpr-next (0.5.4-1ubuntu1~ppa1) UNRELEASED; urgency=medium
* translations
Belorussian translations, thanks to Aliaxandr
enabled German translations
* misc
logging changes (more refined and enough verbose)
regular and PT notifications now are separate and follow the same logic (+logging)
added logging callback to know when the notification is closed
-- Eduards Bezverhijs Fri, 7 Oct 2022 07:39:11 +0300
timekpr-next (0.5.3-1ubuntu1~ppa1) UNRELEASED; urgency=medium
* bug fixes
fix DBUS time notification signal and intermittent user session restriction enforcement issues due to this error:
"TypeError: 'float' object cannot be interpreted as an integer"
this seems to happen on very recent python versions
* translations
German translations, thanks to Robert E.
* misc
add logging for linux distribution identification and core components (python version, dbus-python version)
-- Eduards Bezverhijs Mon, 21 Feb 2022 22:50:53 +0200
timekpr-next (0.5.2-1ubuntu1~ppa1) UNRELEASED; urgency=medium
* features
PlayTime left is now shown besides the icon / on label
* bug fixes
fix #1927771 (the issue was improper configuration when limit count was less then allowed day count)
additionally allow setting empty day / limit lists for regular time and PlayTime
fix error handling with empty lists
properly check allowed day numbers according to allowed values (1-7)
a small fix for time leak (couple of seconds) on day change
* technical improvements
address the situation when session type can change during the session in logind (https://github.com/systemd/systemd/pull/14925)
improved session accounting for situations when session type is in tracked list at the same time as in excluded, exclusion takes precedence
improved session logging to determine why the time for session is or is not accounted
improved session remaining process killer to include processes that use pseudo-terminals as well as recording a summary of killing results
prevent an installation error when blindly recreating GTK icon cache even if it is not present, in these cases just create it
properly check whether an instance of timekpr applications is actually running
on day change, reset not only this day spent and sleep times, but for next day too
tiny improvement on sleep time precision
make admin application to exit gracefully when it was opened from terminal and ctrl+c was pressed (SIGINT)
less logging in standard debug mode / remove duplicate logging for PT
-- Eduards Bezverhijs Wed, 23 Jun 2021 12:34:38 +0300
timekpr-next (0.5.1-2ubuntu1~ppa1) UNRELEASED; urgency=medium
* quick fix for PlayTime notifications
revert the cap in daemon for sending PlayTime left to clients
admin app will always show actual PlayTime left
client app will receive actual PlayTime left, but it will show
minimum of PlayTime or standard time left for that particular day to user (child, subordinate)
* possibly fix a bug #1917916 when process changed the owner from regular user to system user but was still left in user process list
-- Eduards Bezverhijs Fri, 5 Mar 2021 11:48:25 +0200
timekpr-next (0.5.1-1ubuntu1~ppa1) UNRELEASED; urgency=medium
* adjust README to fix MD parsing nuances on gitlab
* add gnome-shell-extension-appindicator to suggestions list
* fix project description to match the README
* technical improvements
added ":" as allowed separator for minute intervals (undocumented feature)
improve a workaround in config loader routines for cases when user makes a mistake while manually editing the config files
added explanatory comment for values in control files
print out uname when timekpr starts to better understand the environment the timekpr is running in
log more information about timekpr performance and computer load average
log information about day / week / month change
added elapsed time information output on lowest log level
rewritten logging routines and implemented buffered log save
time spent is accounted and saved even when file has been modified externally (this should help with proper time accounting
(2x+) if user is logged in from multiple machines when commonly shared directory is used)
* registered bug fixes
adjust Debian installation suggestions in README (fixes #1910457)
start using ayatana appindicators because regular ones seem to be deprecated (fixes #1910461)
adjust for openSUSE too (Fedora does not seem to be using ayatana appindicators yet)
* unregistered bug fixes
fixes to client application when is pressed instead of close window which leads to empty timekpr windows
fixes to PlayTime daily limit reset when the day has changed
fix for PlayTime disabled -> enabled did not work right away when user is already logged in
fix for user time accounting which added couple of seconds to time spent when using unaccountable intervals
fix time accounting for PlayTime in override mode and unaccounted time intervals
fix process handling when process does not have a normal user id that process monitor is interested in and it changed executable
or process changed user id from one we were interested in to one we are not
fix timekpr behaviour when user was able to use computer prior the starting minutes of unaccounted hour interval
now user will be dealt according to defined rules instead of using the computer when he is not allowed to
lift the terminate / shutdown restriction when termination sequence has been initialized, but admin gives more time prior to
terminate or shutdown
* new features
introduce an option to prevent PlayTime activities to be started during unaccountable ("∞") intervals
introduce an option to monitor PlayTime activities (processes) using first 512 characters of command line
by default only process name is used for activity detection
logging level changes are applied immediately
gray icon for unaccounted hours, which help to distinguish the interval, especially when there is no time left (was: red icon)
add possibility to edit single daily, daily PlayTime and weekly/monthly limits by editing value directly
-- Eduards Bezverhijs Mon, 1 Mar 2021 21:49:33 +0200
timekpr-next (0.5.0-1ubuntu1~ppa1) UNRELEASED; urgency=medium
* fix a bug when administration application tries to get user information
from server even when no user has been selected
* added clarifications to the template on some keywords not to be translated
fixed LV and IT translations with keywords
* enable Italian translations
* added Italian translations
* added clarifications on some keywords not to be translated
* translation template update, spelling fixes and Latvian translations
* replaced re.escape with simple replace due to it's different behavior in different versions
this fixes user list retrieval in Ubuntu 18.04 (python 3.6.9)
* fixed a bug when user names will not show up in administration application if username contains a . (dot)
* introduced PlayTime and PlayTime override modes
this basically allows a supervisor to account and control specific processes in the system for their subordinates
supervisor has to configure which processes are accounted by entering a process executable names / masks
* introducing "freeride" time periods
* some GUI improvements in client app
* total rewrite of administration app to improve usability
* introducing user configurable notifications
-- Eduards Bezverhijs Tue, 29 Dec 2020 17:51:19 +0200
timekpr-next (0.4.4-1ubuntu1~ppa1) UNRELEASED; urgency=medium
* adjust previously built workaround for switching to login manager screen
-- Eduards Bezverhijs Sun, 25 Oct 2020 19:25:59 +0300
timekpr-next (0.4.3-2ubuntu1~ppa1) UNRELEASED; urgency=medium
* added lock, suspend and shutdown options as a restriction / lockout types
* added option for computer to be woken up automatically if it was put to sleep by timekpr
additionally it's possible to specify hour interval when it is allowed to be woken up
* changed termination sequence a little
if user has no time and locked the screen, user is removed from termination list, which effectively means that
user sessions won't be terminated unless user unlocks the screen or other user logs in via user switching
(we should not leave sessions without time left floating around forever)
* proper english label / message corrections from Phil Hudson (thanks!)
-- Eduards Bezverhijs Sat, 10 Oct 2020 19:59:35 +0300
timekpr-next (0.4.2-1ubuntu1~ppa1) UNRELEASED; urgency=medium
* added verbose output to timekpr installation
* fix license names in AppStream file
* better explain usage notes for CLI usage
* improvements
added average performance metrics for timekpr execution
introduce server ability to survive login manager DBUS interface crash and restore functional timekpr state after recovery
* refactoring
improve various functionality in timekpr codebase
tiny optimizations for variables
improvements for config reader / writer
improvements for in-memory config creation
* fixed #1891921, critical notifications did not show up until final countdown (if show all notifications was checked)
now critical notification will be shown approx one minute before time will end
* improved notification frequency and urgency (now only critical notifications are actually critical)
new frequency values:
if more than 2 hours are left, notifications are shown every 2 hours
if more than 1 hour is left, notifications are shown every 1 hour, i.e. one notification
if more than 30 minutes are left, notifications are shown every 30 minutes, i.e. one notification
if more than 5 minutes are left, notifications are shown every 10 minutes as warnings
if more than a minute is left, notifications are shown every 2 minutes as important warnings
if about a minute is left, notifications are shown every 1 minute (i.e. one notification) as CRITICAL notification
* improved handling of icons
use non GTK icons that should work in every DE
* improved allowed days and respective limits processing in case these are not set up properly
mismatch possible only by manually and incorrectly editing the config file
* fix continous time calculation on day change
this issue did not affect functionality
-- Eduards Bezverhijs Fir, 4 Sep 2020 11:45:09 +0300
timekpr-next (0.4.1-2ubuntu1~ppa1) UNRELEASED; urgency=medium
* fixed a packaging bug with systemd units
-- Eduards Bezverhijs Thu, 30 Jul 2020 20:13:39 +0300
timekpr-next (0.4.1-1ubuntu1~ppa1) UNRELEASED; urgency=medium
* added quality of life configuration options for client application (as suggested by one of Timekpr-nExT users)
** added configuration option to set how long regular notifications should be visible
** added configuration option to set how long Critical notifications should be visible
** if time in seconds is set to 0 for either of options, notification will stay on until dismissed
** added option to play a short (standard) notification bell sound when showing notifications (if supported by current DE)
** added username verification for configuration files
** translations for new options in client application
** added full name display for username
* added quality of life improvements to administration app
** added refresh to actual ("realtime") values for users that are logged in
** a little optimization for time refresh in administration application
** added full name display for usernames (if username is the same as full name, show only username)
* fixed and changed warnings / suggestions from user foka based on his observations when importing package into Debian:
** adjust code install paths (with support for non-Debian distros as well)
** create metainfo file
** adjust service / desktop / control / install / changelog files
** fixed some lintian warnings (will not be fixing all)
** project README file
* changed version numbering scheme
* bug fixes in administration application
** time spent / left was shown from previous time (day / week / month) user was logged in
-- Eduards Bezverhijs Mon, 27 Jul 2020 13:40:01 +0300
timekpr-next (0.4.0~ppa1~ubuntu2) UNRELEASED; urgency=medium
* minor change for debian/install
* added Fedora/openSUSE build instructions
* improved user detection
-- Eduards Bezverhijs Sun, 12 Jul 2020 13:48:36 +0300
timekpr-next (0.4.0~ppa1~ubuntu1) UNRELEASED; urgency=medium
* Registered bug fixes:
** killing user sessions that do not have "seats" will not switch to login manager TTY (#1861479)
** properly show time spent for the day in administration app (#1882909)
** properly set any number values (spinbuttons) in admin app (#1826672)
** restore configuration from saved state in admin app when case uncheck / check is pressed (#1826673)
** it's now possible to set daily limits as well as intervals for multiple days at once (#1826671)
* Discovered bug fixes:
** fix config / control file recreation in case it's missing a configuration option entirely
** fix icon and time representation in system notification area when user has unlimited time left
* Changes / improvements:
** control files (.time) will now contain balance as well as time spent for day (existing TIME_SPENT will be removed, TIME_SPENT_BALANCE and TIME_SPENT_DAY are added)
** console application (timekpra) changes
*** rename TIME_SPENT (previously used as saved time spent value) to TIME_SPENT_BALANCE
*** added an option which represents hide tray icon option (HIDE_TRAY_ICON)
*** rename option "userconfig" to "userinfo" which better represents returned values
*** add method to hide tray icon for user (--sethidetrayicon)
*** added time left for today (TIME_LEFT_DAY) which is calculated from saved state
** console application (timekpra) improvements - added "live" (i.e. calculated every 3 secs) values for options below
*** ACTUAL_TIME_SPENT_SESSION - time spent in this session (since timekpr daemon startup)
*** ACTUAL_TIME_INACTIVE_SESSION - time user was inactive in this session (since timekpr daemon startup)
*** ACTUAL_TIME_SPENT_BALANCE - time spent balance (including added/removed bonus time)
*** ACTUAL_TIME_SPENT_DAY - time user spent this day (actual spent time, example: user has 1 hour, if there was a bonus 1 hr, this will show 2 hrs)
*** ACTUAL_TIME_LEFT_DAY - how much time is left today
*** ACTUAL_TIME_LEFT_CONTINUOUS - how much time is left continuously (but no more than this and next day)
** timekpr will not switch to login manager TTY after killing a user / session that is not active (i.e. in foreground)
** timekpr will reload user list when opening admin application (newly added users will be available in timekpr right away)
** add time inactive (actual) and time left (actual), i.e. when user is logged in, for admin app
** add translations to new features
** minor GUI changes
** message which informs user that timekpr can not connect to screensaver is now shown only when all notifications are enabled
** client application icon will not show if hide icon is checked in admin app
-- Eduards Bezverhijs Wed, 17 Jun 2020 00:33:21 +0300
timekpr-next (0.3.7~ppa1~ubuntu1) UNRELEASED; urgency=medium
* Added Czech language (thanks for translations to NOVAK Ludek)
* Fixed Hungarian language inclusion (thanks for spotting the typo to NOVAK Ludek)
-- Eduards Bezverhijs Tue, 21 Apr 2020 23:19:42 +0300
timekpr-next (0.3.6~ppa1~ubuntu1) UNRELEASED; urgency=medium
* Added support for LockedHint to determine whether session is locked
** requires systemd >= 230, e.g. Ubuntu 18.04 at least
** this is in addition to client screensaver detection, but less used (e.g. KDE does not use this)
* Exclude closing and lingering sessions from being considered as active when tracking inactive sessions is enabled
-- Eduards Bezverhijs Tue, 21 Apr 2020 23:19:42 +0300
timekpr-next (0.3.5~ppa1~ubuntu1) UNRELEASED; urgency=medium
* Improved login manager detection for non-standard names
* Added a previously forgotten Hungarian language to installation
-- Eduards Bezverhijs Sun, 17 Apr 2020 09:56:07 +0300
timekpr-next (0.3.4~ppa1~ubuntu1) UNRELEASED; urgency=medium
* Added more screensaver workarounds for implementations for major DEs
-- Eduards Bezverhijs Sun, 14 Apr 2020 10:31:32 +0300
timekpr-next (0.3.3~ppa1~ubuntu1) UNRELEASED; urgency=medium
* Warn user if screensaver feature is not available for screen idle time detection
** adjust translations as well
-- Eduards Bezverhijs Sun, 14 Apr 2020 11:02:31 +0300
timekpr-next (0.3.2~ppa1~ubuntu1) UNRELEASED; urgency=medium
* Introduced functionality to limit retries to screensaver for DEs that are not compatible with freedesktop or gnome screensaver interface
-- Eduards Bezverhijs Sun, 14 Apr 2020 16:04:02 +0300
timekpr-next (0.3.1~ppa1~ubuntu1) UNRELEASED; urgency=medium
* Introduced workarounds section for screensaver dbus interface to use gnome version instead of freedesktop for Unity and Gnome itself
-- Eduards Bezverhijs Sun, 13 Apr 2020 16:46:35 +0300
timekpr-next (0.3.0~ppa1~ubuntu1) UNRELEASED; urgency=medium
* Added possibility to determine screen lock status reliably for every DE that exposes screensaver status to DBUS (see below)
** It's implemented in client <-> server fashion using org.freedesktop.ScreenSaver
** but as Gnome3 is "special", I had to implement org.gnome.ScreenSaver as well
-- Eduards Bezverhijs Sun, 12 Apr 2020 17:10:06 +0300
timekpr-next (0.2.13~ppa1~ubuntu1) UNRELEASED; urgency=medium
* Fix time reporting
-- Eduards Bezverhijs Mon, 24 Feb 2020 17:39:44 +0300
timekpr-next (0.2.12~ppa1~ubuntu1) UNRELEASED; urgency=medium
* Fix time accounting when check interval is exactly at 0 secs in new hour (fixes a bug #1863482)
-- Eduards Bezverhijs Mon, 17 Feb 2020 00:08:29 +0300
timekpr-next (0.2.11~ppa1~ubuntu1) UNRELEASED; urgency=medium
* Rework time accouting routines (fixes a bug #1861758)
* Fix for week and month accounting when showing continous time (#1856744)
* Rework session caching routines (for preparation to locking detection)
* Fixed dependencies for package
* Improvements for old StatusIcon (at start there will be a default icon and labels as with indicator)
* Tiny python import cleanup
-- Eduards Bezverhijs Fri, 7 Feb 2020 11:29:49 +0300
timekpr-next (0.2.10~ppa1~ubuntu1) UNRELEASED; urgency=medium
* Fixed bug in configuration load when only couple of days are allowed per week
* Added hungarian translation (thanks)
-- Eduards Bezverhijs Sun, 12 Jan 2020 15:46:08 +0300
timekpr-next (0.2.9~ppa1~ubuntu1) UNRELEASED; urgency=medium
* Improve user detection when username is the same as one of the login managers
* Small improvements with login manager detection
* Fix user time accounting due to issues with Python 3.8 and DBUS typecasts
-- Eduards Bezverhijs Mon, 25 Nov 2019 19:50:05 +0300
timekpr-next (0.2.8~ppa1~ubuntu1) UNRELEASED; urgency=medium
* Fixed an issue for huuuuuuuge icons in in Budgie DE (however other DEs did not show such issue)
* A little logging improvement in VTNr search for login managers
* User list is now sorted
* Fixed an issue with object paths (timekpr uses usernames as part of pathnames) on DBUS which contain dots (.) and/or hyphens (-)
-- Eduards Bezverhijs Tue, 24 Sep 2019 01:28:12 +0300
timekpr-next (0.2.7~ppa1~ubuntu1) UNRELEASED; urgency=medium
* Fixed an issue when Timekpr-nExT confiuration may get corrupted or empty after sudden power outage
** all config files as well as time accounting files have previous backups (.prev files)
** invalid config / time files will have .invalid files to inspect later
** improved configuration handling by retrieving as much as possible from non-qualified config file (if user messes up config manually)
* Improved CLI tools (error checking and help)
* Fixed icon handling in race conditions between client and server as well improved icon handling during limit changes
-- Eduards Bezverhijs Thu, 22 Aug 2019 19:23:42 +0300
timekpr-next (0.2.6~ppa1~ubuntu1) UNRELEASED; urgency=medium
* Fixed an issue with Timekpr-nExT client not starting in non-major DE's (for example Deepin)
-- Eduards Bezverhijs Wed, 5 Jun 2019 20:12:12 +0300
timekpr-next (0.2.5~ppa1~ubuntu1) UNRELEASED; urgency=medium
* first official release
-- Eduards Bezverhijs We, 5 Jun 2019 20:12:12 +0300
timekpr-next (0.2.5~ppa1~ubuntu1) UNRELEASED; urgency=medium
* fixed application launcher names for unfortunate Gnome3 users
-- Eduards Bezverhijs Wed, 5 Jun 2019 20:12:12 +0300
timekpr-next (0.2.4~ppa1~ubuntu2) UNRELEASED; urgency=medium
* localization updates (fixes to translations all over the place, thanks to JP Lord)
** updated all other languages with capitalizations
* added French translations
-- Eduards Bezverhijs Mon, 29 Apr 2019 12:47:46 +0300
timekpr-next (0.2.4~ppa1~ubuntu1) UNRELEASED; urgency=medium
* implemented TTY switching functionality for those cases, when login manager sits on different TTY than session itself (this should fix #1823717)
** the implementation is as follows: Timekpr-nExT tries to determine on which TTY login manager sits and saves it's TTY number, login managers have their
** own users in standard installations, therefore we detect them using specific usernames (maybe not the best solution, but it's a solution)
* fixed control file initialization when timekpr starts (fixes #1802583)
* fixed icon in notification area when there is no time limit per day, but time intervals are limited
* improved termination list handling
-- Eduards Bezverhijs Sat, 27 Apr 2019 23:26:05 +0300
timekpr-next (0.2.3~ppa1~ubuntu3) UNRELEASED; urgency=medium
* fixed a bug in logging when multiple users use machine at the same time (permission problem)
-- Eduards Bezverhijs Sat, 27 Apr 2019 17:50:24 +0300
timekpr-next (0.2.3~ppa1~ubuntu2) UNRELEASED; urgency=medium
* kill leftover processes for the user which do not originate from session processes
-- Eduards Bezverhijs Sun, 14 Apr 2019 11:51:41 +0300
timekpr-next (0.2.3~ppa1~ubuntu1) UNRELEASED; urgency=medium
* kill inactive sessions as well (up until this point inactive sessions were saved from killing)
-- Eduards Bezverhijs Sun, 14 Apr 2019 11:51:41 +0300
timekpr-next (0.2.2~ppa1~ubuntu1) UNRELEASED; urgency=medium
* logging improvements (all client side logs are written to /tmp instead of HOME)
* tweak to translation credits (if none are supplied)
-- Eduards Bezverhijs Fri, 12 Apr 2019 15:58:56 +0200
timekpr-next (0.2.1~ppa1~ubuntu2) UNRELEASED; urgency=medium
* packaging fix - we do not need to reference login managers any more
-- Eduards Bezverhijs Mon, 08 Apr 2019 18:34:31 +0300
timekpr-next (0.2.1~ppa1~ubuntu1) UNRELEASED; urgency=medium
* added Italian and German translations
-- Eduards Bezverhijs Mon, 8 Apr 2019 8:25:17 +0300
timekpr-next (0.2.0~ppa1~ubuntu1) UNRELEASED; urgency=medium
* first beta release
-- Eduards Bezverhijs Fri, 29 Mar 2019 8:22:17 +0300
timekpr-next (0.2.0~ppa1~ubuntu1) UNRELEASED; urgency=medium
* implemented localization
* added Latvian translation
-- Eduards Bezverhijs Fri, 29 Mar 2019 8:22:17 +0300
timekpr-next (0.1.15~ppa1~ubuntu1) UNRELEASED; urgency=medium
* implemented localization (translations pending)
-- Eduards Bezverhijs Thu, 28 Mar 2019 18:27:42 +0300
timekpr-next (0.1.14~ppa1~ubuntu1) UNRELEASED; urgency=medium
* timekpr client code optimization and small bug fixes
* added speech output
TODO: localization
-- Eduards Bezverhijs Thu, 21 Mar 2019 21:06:43 +0300
timekpr-next (0.1.13~ppa1~ubuntu1) UNRELEASED; urgency=medium
* finalized admin GUI
* bug fixes here and there
TODO: localization and speech
-- Eduards Bezverhijs Mon, 18 Mar 2019 10:33:59 +0300
timekpr-next (0.1.12~ppa1~ubuntu1) UNRELEASED; urgency=medium
* finalized user admin GUI
* bug fixes here and there
TODO: timekpr administration via GUI
TODO: localization and speech
-- Eduards Bezverhijs Fri, 15 Mar 2019 07:11:23 +0300
timekpr-next (0.1.11~ppa1~ubuntu1) UNRELEASED; urgency=medium
* implemented user admin GUI
TODO: timekpr administration via GUI
TODO: localization and speech
-- Eduards Bezverhijs Sun, 03 Mar 2019 00:42:11 +0300
timekpr-next (0.1.10~ppa1~ubuntu1) UNRELEASED; urgency=medium
* fixed a bug for time accounting when there is a limit set up, but hours are not limited at all
* changed CLI option style from "-" to "--"
TODO: timekpr administration via GUI
TODO: user administration via GUI
TODO: localization and speech
-- Eduards Bezverhijs Tue, 26 Feb 2019 23:08:17 +0300
timekpr-next (0.1.9~ppa1~ubuntu1) UNRELEASED; urgency=medium
* added weekly and monthly quota administration and processing
* minor time accounting fixes
TODO: timekpr administration via GUI
TODO: user administration via GUI
TODO: week and month quotas reporting to user
TODO: localization and speech
-- Eduards Bezverhijs Fri, 22 Feb 2019 18:11:35 +0300
timekpr-next (0.1.8~ppa1~ubuntu1) UNRELEASED; urgency=medium
* fixes for user administration via DBUS and CLI
* major time accounting fixes
* clean up for user data processor
TODO: timekpr administration via GUI
TODO: user administration via GUI
TODO: week and month quotas
TODO: localization and speech
-- Eduards Bezverhijs Fri, 15 Feb 2019 6:53:03 +0300
timekpr-next (0.1.7~ppa1~ubuntu1) UNRELEASED; urgency=medium
* implemented user administration via DBUS
* fixed some bugs with client configuration
* fixed logrotate configuration
TODO: administration via DBUS and CLI/GUI around it
TODO: localization and speech
-- Eduards Bezverhijs Thu, 17 Jan 2019 07:35:19 +0300
timekpr-next (0.1.6~ppa1~ubuntu1) UNRELEASED; urgency=medium
* fixed issue with time being improperly calculated when user is set from "unlimited" mode to limited
* fixed issue about time initialization when day changes and previously user was in "unlimited" mode (mode was changed to limited + he never logged off)
TODO: administration via DBUS and CLI/GUI around it
TODO: localization and speech
-- Eduards Bezverhijs Sun, 18 Dec 2018 20:03:22 +0300
timekpr-next (0.1.5~ppa1~ubuntu1) UNRELEASED; urgency=medium
* fixed lazy dbus connection initialization on client side
* fixes for client side GUI
* minor fixes for time accounting and logging
TODO: administration via DBUS and CLI/GUI around it
TODO: localization and speech
-- Eduards Bezverhijs Sun, 11 Nov 2018 07:55:46 +0300
timekpr-next (0.1.4~ppa1~ubuntu1) UNRELEASED; urgency=medium
* first alpha version of timekpr (a lot has changed since old timekpr 0.3x version)
** new impementation of timekpr-revived (there is almost nothing left of timekpr-revived)
** released to very limited testers
** python3 and gtk3 are the must
** all based on DBUS (server and client)
** session management relies on systemd's login1
** notifications through DBUS (exclusively)
** server is managing everything, except notifications which is the responsibility of client
** major features include the following
*** support DE's which implement appindicaor3 (Unity/Gnome3¹/KDE5/...) and legacy statusIcon
*** support Ubuntu 12.04+ (on launchpad timekpr can support the versions which are on supported distro list)
*** it's possible to configure which session types are accountable
*** inactive session time is NOT accounted (if configured so)
*** time is configurable by days, hours and minutes (yes, limits within hour are possible)
*** there is no "hard" account lock, user will be logged out in 15 seconds if time is over
*** synchronous operation between server and client
*** improved notifications about system behaviour
**** notification about actual limit change
**** notification about general configuration change
**** notification logic improved
**** client acts merely as notificator
**** client can view limits applied by admin
¹ one has to install extension to show timekpr icon, otherwise icon is not shown (gnome architects do not want status icons in their design anymore, timekpr can not do anything about it)
TODO: administration via DBUS and CLI/GUI around it
-- Eduards Bezverhijs Mon, 17 Sep 2018 08:02:17 +0300
timekpr-next/debian/postrm 000775 001750 001750 00000002461 13745331025 017677 0 ustar 00bezvfedu bezvfedu 000000 000000 #!/bin/sh
# timekpr directories and units
TK_DIR="/usr/lib/python3/dist-packages/timekpr"
# unit name
TK_UNIT_NAME="timekpr.service"
# unit directory (starting with debian)
TK_UNIT_DIR="/lib/systemd/system"
# prefixes for other distros then debian based
TK_UNIT_DIR_PREFIXES="/usr"
# we need to take into account the various directory configs
for R_PREF in ${TK_UNIT_DIR_PREFIXES};
do
# alternatives exist
if [ -d "${R_PREF}${TK_UNIT_DIR}" ];
then
# assign additional prefix
TK_UNIT_DIR="${R_PREF}${TK_UNIT_DIR}"
# this is it
break
fi
done
# if there are units, let's handle them
if [ -f "${TK_UNIT_DIR}/${TK_UNIT_NAME}" ];
then
# systemctl
echo "Stopping and disabling Timekpr-nExT units"
systemctl stop timekpr
systemctl disable timekpr
fi
# remove compiled python units (may interfere with python versions)
if [ -d "${TK_DIR}" ];
then
# remove compiled
echo "Cleaning up Timekpr-nExT compiled units"
find "${TK_DIR}" -type d -name '__pycache__' -exec rm -rf {} \; -prune
find "${TK_DIR}" -type f -iname '*.pyc' -exec rm {} \;
fi
# reload d-bus for access controls and systemd for service removals
echo "Reloading dbus daemon to refresh access policies"
systemctl reload dbus
echo "Reloading systemd to refresh units"
systemctl daemon-reload
timekpr-next/debian/conffiles 000600 001750 001750 00000000032 14575617135 020311 0 ustar 00bezvfedu bezvfedu 000000 000000 /etc/timekpr/timekpr.conf
timekpr-next/debian/rules 000775 001750 001750 00000000235 13476006650 017506 0 ustar 00bezvfedu bezvfedu 000000 000000 #!/usr/bin/make -f
clean:
find . -name "*.pyc" -delete
find . -name "__pycache__" -delete
#doc/update.sh
dh $@ --with python3
%:
dh $@ --with python3
timekpr-next/debian/install 000644 001750 001750 00000015364 15122253261 020016 0 ustar 00bezvfedu bezvfedu 000000 000000 # binaries
bin/timekpra usr/bin/
bin/timekprc usr/bin/
bin/timekprd usr/bin/
# config files
resource/server/dbus/timekpr.conf etc/dbus-1/system.d/
resource/server/logrotate.d/timekpr etc/logrotate.d/
resource/server/polkit/com.ubuntu.timekpr.pkexec.policy usr/share/polkit-1/actions/
resource/server/systemd/timekpr.service lib/systemd/system/
resource/server/timekpr.conf etc/timekpr/
# sample runtime files
resource/server/timekpr.USER.conf var/lib/timekpr/config/
resource/server/USER.time var/lib/timekpr/work/
# icons
resource/icons/timekpr-128.png usr/share/icons/hicolor/128x128/apps/
resource/icons/timekpr-48.png usr/share/icons/hicolor/48x48/apps/
resource/icons/timekpr-64.png usr/share/icons/hicolor/64x64/apps/
resource/icons/timekpr-client-128.png usr/share/icons/hicolor/128x128/apps/
resource/icons/timekpr-client-48.png usr/share/icons/hicolor/48x48/apps/
resource/icons/timekpr-client-64.png usr/share/icons/hicolor/64x64/apps/
resource/icons/timekpr-client-logo.svg usr/share/timekpr/icons/
resource/icons/timekpr-client.svg usr/share/icons/hicolor/scalable/apps/
resource/icons/timekpr-logo.svg usr/share/timekpr/icons/
resource/icons/timekpr-padlock-limited-green.svg usr/share/timekpr/icons/
resource/icons/timekpr-padlock-limited-red.svg usr/share/timekpr/icons/
resource/icons/timekpr-padlock-limited-yellow.svg usr/share/timekpr/icons/
resource/icons/timekpr-padlock-limited-uacc.svg usr/share/timekpr/icons/
resource/icons/timekpr-padlock-unlimited-green.svg usr/share/timekpr/icons/
resource/icons/timekpr.svg usr/share/icons/hicolor/scalable/apps/
# launchers
resource/launchers/timekpr-admin.desktop usr/share/applications/
resource/launchers/timekpr-admin-su.desktop usr/share/applications/
resource/launchers/timekpr-client.desktop etc/xdg/autostart/
# metainfo
resource/appstream/org.timekpr.timekpr-next.metainfo.xml usr/share/metainfo/
# forms
resource/client/forms/about.glade usr/share/timekpr/client/forms/
resource/client/forms/admin.glade usr/share/timekpr/client/forms/
resource/client/forms/client.glade usr/share/timekpr/client/forms/
# python common
common/constants/constants.py usr/lib/python3/dist-packages/timekpr/common/constants/
common/constants/__init__.py usr/lib/python3/dist-packages/timekpr/common/constants/
common/constants/messages.py usr/lib/python3/dist-packages/timekpr/common/constants/
common/log/__init__.py usr/lib/python3/dist-packages/timekpr/common/log/
common/log/log.py usr/lib/python3/dist-packages/timekpr/common/log/
common/utils/config.py usr/lib/python3/dist-packages/timekpr/common/utils/
common/utils/__init__.py usr/lib/python3/dist-packages/timekpr/common/utils/
common/utils/misc.py usr/lib/python3/dist-packages/timekpr/common/utils/
common/utils/notifications.py usr/lib/python3/dist-packages/timekpr/common/utils/
# python client
client/admin/adminprocessor.py usr/lib/python3/dist-packages/timekpr/client/admin/
client/gui/admingui.py usr/lib/python3/dist-packages/timekpr/client/gui/
client/gui/clientgui.py usr/lib/python3/dist-packages/timekpr/client/gui/
client/gui/__init__.py usr/lib/python3/dist-packages/timekpr/client/gui/
client/__init__.py usr/lib/python3/dist-packages/timekpr/client/
client/interface/dbus/administration.py usr/lib/python3/dist-packages/timekpr/client/interface/dbus/
client/interface/dbus/daemon.py usr/lib/python3/dist-packages/timekpr/client/interface/dbus/
client/interface/dbus/__init__.py usr/lib/python3/dist-packages/timekpr/client/interface/dbus/
client/interface/dbus/notifications.py usr/lib/python3/dist-packages/timekpr/client/interface/dbus/
client/interface/__init__.py usr/lib/python3/dist-packages/timekpr/client/interface/
client/interface/speech/espeak.py usr/lib/python3/dist-packages/timekpr/client/interface/speech/
client/interface/speech/__init__.py usr/lib/python3/dist-packages/timekpr/client/interface/speech/
client/interface/ui/appindicator.py usr/lib/python3/dist-packages/timekpr/client/interface/ui/
client/interface/ui/__init__.py usr/lib/python3/dist-packages/timekpr/client/interface/ui/
client/interface/ui/notificationarea.py usr/lib/python3/dist-packages/timekpr/client/interface/ui/
client/interface/ui/statusicon.py usr/lib/python3/dist-packages/timekpr/client/interface/ui/
client/timekpra.py usr/lib/python3/dist-packages/timekpr/client/
client/timekprc.py usr/lib/python3/dist-packages/timekpr/client/
# python server
server/config/configprocessor.py usr/lib/python3/dist-packages/timekpr/server/config/
server/config/userhelper.py usr/lib/python3/dist-packages/timekpr/server/config/
server/__init__.py usr/lib/python3/dist-packages/timekpr/server/
server/interface/dbus/consolekit/__init__.py usr/lib/python3/dist-packages/timekpr/server/interface/dbus/consolekit/
server/interface/dbus/consolekit/manager.py usr/lib/python3/dist-packages/timekpr/server/interface/dbus/consolekit/
server/interface/dbus/consolekit/user.py usr/lib/python3/dist-packages/timekpr/server/interface/dbus/consolekit/
server/interface/dbus/daemon.py usr/lib/python3/dist-packages/timekpr/server/interface/dbus/
server/interface/dbus/__init__.py usr/lib/python3/dist-packages/timekpr/server/interface/dbus/
server/interface/dbus/logind/__init__.py usr/lib/python3/dist-packages/timekpr/server/interface/dbus/logind/
server/interface/dbus/logind/manager.py usr/lib/python3/dist-packages/timekpr/server/interface/dbus/logind/
server/interface/dbus/logind/user.py usr/lib/python3/dist-packages/timekpr/server/interface/dbus/logind/
server/interface/__init__.py usr/lib/python3/dist-packages/timekpr/server/interface/
server/timekprd.py usr/lib/python3/dist-packages/timekpr/server/
server/user/playtime.py usr/lib/python3/dist-packages/timekpr/server/user/
server/user/__init__.py usr/lib/python3/dist-packages/timekpr/server/user/
server/user/userdata.py usr/lib/python3/dist-packages/timekpr/server/user/
# translations (only the ones that are ready will be included)
resource/locale/be/LC_MESSAGES/timekpr.mo usr/share/locale/be/LC_MESSAGES/
#resource/locale/cs/LC_MESSAGES/timekpr.mo usr/share/locale/cs/LC_MESSAGES/
resource/locale/de/LC_MESSAGES/timekpr.mo usr/share/locale/de/LC_MESSAGES/
resource/locale/en_GB/LC_MESSAGES/timekpr.mo usr/share/locale/en_GB/LC_MESSAGES/
resource/locale/es/LC_MESSAGES/timekpr.mo usr/share/locale/es/LC_MESSAGES/
resource/locale/fr/LC_MESSAGES/timekpr.mo usr/share/locale/fr/LC_MESSAGES/
resource/locale/fr_CA/LC_MESSAGES/timekpr.mo usr/share/locale/fr_CA/LC_MESSAGES/
#resource/locale/hu/LC_MESSAGES/timekpr.mo usr/share/locale/hu/LC_MESSAGES/
resource/locale/it/LC_MESSAGES/timekpr.mo usr/share/locale/it/LC_MESSAGES/
resource/locale/lv/LC_MESSAGES/timekpr.mo usr/share/locale/lv/LC_MESSAGES/
resource/locale/nl/LC_MESSAGES/timekpr.mo usr/share/locale/nl/LC_MESSAGES/
resource/locale/pt/LC_MESSAGES/timekpr.mo usr/share/locale/pt/LC_MESSAGES/
resource/locale/ru/LC_MESSAGES/timekpr.mo usr/share/locale/ru/LC_MESSAGES/
timekpr-next/debian/control 000644 001750 001750 00000003152 15122253705 020023 0 ustar 00bezvfedu bezvfedu 000000 000000 Source: timekpr-next
Maintainer: Eduards Bezverhijs
Uploaders: Eduards Bezverhijs
Section: admin
Priority: optional
Build-Depends: debhelper (>= 7) | debhelper-compat (= 12),
dh-python | python-support,
gir1.2-gtk-3.0,
po-debconf,
python3 (>= 3.2),
python3-dbus,
python3-gi
Standards-Version: 3.9.5
Homepage: https://launchpad.net/timekpr-next
Rules-Requires-Root: no
Package: timekpr-next
Architecture: any
Depends: gir1.2-ayatanaappindicator3-0.1 | gir1.2-appindicator3-0.1,
gir1.2-gtk-3.0,
lxpolkit | lxqt-policykit | mate-polkit | policykit-1-gnome | polkit-kde-agent-1 | polkit-1-auth-agent,
pkexec | policykit-1,
python3 (>= 3.2),
python3-dbus,
python3-gi,
python3-psutil (>= 3.4),
systemd | systemd-shim,
${misc:Depends},
${python3:Depends}
Suggests: espeak,
gnome-shell-extension-appindicator,
python3-espeak,
python3-systemd
Conflicts: timekpr-next
Provides: timekpr
Replaces: timekpr,
timekpr-beta,
timekpr-next-beta
Description: Keep control of computer usage
Timekpr-nExT is a fresh, simple and easy-to-use time managing software
that helps optimizing time spent at computer for Your subordinates,
children or even for Yourself.
.
The software is targeted at parents or supervisors to optimize / limit
time spent at computer as they see fit.
.
Please report any bugs to Timekpr-nExT’s bug tracker on Launchpad at:
https://bugs.launchpad.net/timekpr-next
timekpr-next/spec/ 000775 001750 001750 00000000000 15122253261 016126 5 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/spec/timekpr-next.spec 000644 001750 001750 00000022635 15122253417 021442 0 ustar 00bezvfedu bezvfedu 000000 000000 %global debug_package %{nil}
%define _sitelib %( echo %{python3_sitelib} | cut -c 10- )
Name: timekpr-next
Version: 0.5.9
Release: 1.0%{?dist}
Summary: Keep control of computer usage
Group: System Environment/Daemons
License: GPLv3
URL: https://launchpad.net/timekpr-next
Source0: https://launchpad.net/%{name}/stable/%{version}/+download/%{name}-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-build
BuildRequires: ( python3 )
BuildRequires: ( desktop-file-utils )
BuildRequires: ( libappstream-glib or appstream-glib )
BuildRequires: ( systemd )
BuildRequires: ( sed )
BuildRequires: ( grep )
BuildRequires: ( python3-devel)
Requires: ( gtk3 >= 3.4 )
Requires: ( polkit or pkexec )
Requires: ( python3 )
Requires: ( python3-dbus or python3-dbus-python )
Requires: ( python3-gobject )
Requires: ( python3-psutil )
Requires: ( ( libindicator-gtk3 and libappindicator-gtk3 ) or ( typelib-1_0-Gtk-3_0 and ( ( libayatana-indicator3-7 and typelib-1_0-AyatanaAppIndicator3-0_1 ) or ( libindicator3-7 and typelib-1_0-AppIndicator3-0_1 ) ) ) )
Requires: ( gettext )
Suggests: ( python3-systemd )
Requires(post): ( systemd )
Requires(preun): ( systemd )
Requires(postun): ( systemd )
%description
Timekpr-nExT is a program that tracks and controls the computer usage
of your user accounts. You can limit their daily usage based on a
timed access duration and configure periods of day when they can or
cannot log in.
.
This may be used for parental control to limit the amount of screen time
a child spends in front of the computer.
.
Please report any bugs to Timekpr-nExT’s bug tracker on Launchpad at:
https://bugs.launchpad.net/timekpr-next
%prep
%setup -q -n %{name}
sed -i 's|python3/dist-packages|%{_sitelib}|g' bin/* client/*.py debian/install resource/server/systemd/timekpr.service server/timekprd.py
%build
%install
# remove all root before build
rm -rf $RPM_BUILD_ROOT
# install files
grep -v -e '^#' -e '^$' debian/install | sed -e 's|/$||' -e 's| lib/systemd/| usr/lib/systemd/|g' -e 's|^\(.\+/\)\(.*\) \(.*\)/\?$|mkdir -p %{buildroot}/\3 ; cp \1\2 %{buildroot}/\3|g' | sh -
# install pre/post files
mkdir mkdir -p %{buildroot}/%{_sharedstatedir}/timekpr
%__cp debian/postinst %{buildroot}/%{_sharedstatedir}/timekpr/%{name}.postinst
%__cp debian/postrm %{buildroot}/%{_sharedstatedir}/timekpr/%{name}.postrm
# appdata file
install -Dpm 644 resource/appstream/org.timekpr.%{name}.metainfo.xml %{buildroot}%{_datadir}/metainfo/org.timekpr.%{name}.metainfo.xml
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/org.timekpr.%{name}.metainfo.xml
%post
# reload units
systemctl daemon-reload
# post installation
%{_sharedstatedir}/timekpr/%{name}.postinst
# update mime / desktop
update-mime-database %{_datadir}/mime &> /dev/null || :
update-desktop-database &> /dev/null || :
%preun
# before removal
if [ $1 == 0 ];
then
%{_sharedstatedir}/timekpr/%{name}.postrm
fi
%postun
# update mime / desktop
update-mime-database %{_datadir}/mime &> /dev/null || :
update-desktop-database &> /dev/null || :
%files
# specific purpose files
%defattr(-,root,root,-)
%doc debian/changelog debian/copyright
%config(noreplace) /etc/timekpr/timekpr.conf
# package files
%{_bindir}/*
%{_datadir}/applications/*
%{_datadir}/icons/hicolor/128x128/apps/*
%{_datadir}/icons/hicolor/48x48/apps/*
%{_datadir}/icons/hicolor/64x64/apps/*
%{_datadir}/icons/hicolor/scalable/apps/*
%{_datadir}/locale/be/LC_MESSAGES/*
#%%{_datadir}/locale/cs/LC_MESSAGES/*
%{_datadir}/locale/de/LC_MESSAGES/*
%{_datadir}/locale/en_GB/LC_MESSAGES/*
%{_datadir}/locale/es/LC_MESSAGES/*
%{_datadir}/locale/fr/LC_MESSAGES/*
%{_datadir}/locale/fr_CA/LC_MESSAGES/*
#%%{_datadir}/locale/hu/LC_MESSAGES/*
%{_datadir}/locale/it/LC_MESSAGES/*
%{_datadir}/locale/lv/LC_MESSAGES/*
%{_datadir}/locale/nl/LC_MESSAGES/*
%{_datadir}/locale/pt/LC_MESSAGES/*
%{_datadir}/locale/ru/LC_MESSAGES/*
%{_datadir}/metainfo/*
%{_datadir}/polkit-1/actions/*
%{_datadir}/timekpr
%{_prefix}/lib/%{_sitelib}/timekpr
%{_prefix}/lib/systemd/system/*
%{_sharedstatedir}/timekpr
%{_sysconfdir}/dbus-1/system.d/*
%{_sysconfdir}/logrotate.d/*
%{_sysconfdir}/timekpr
%{_sysconfdir}/xdg/autostart/*
%changelog
* Mon Dec 22 2025 Eduards Bezverhijs - 0.5.9-1.0
- Updated spec file for version 0.5.9, release 1.0 (STABLE)
* Mon Nov 24 2025 Eduards Bezverhijs - 0.5.9-0.2
- Updated spec file for version 0.5.9, release 0.2 (BETA)
* Mon Nov 10 2025 Eduards Bezverhijs - 0.5.9-0.1
- Updated spec file for version 0.5.9, release 0.1 (BETA)
* Sat May 10 2025 Eduards Bezverhijs - 0.5.8-1.0
- Updated spec file for version 0.5.8, release 1.0 (STABLE)
* Tue Apr 29 2025 Eduards Bezverhijs - 0.5.8-0.3
- Updated spec file for version 0.5.8, release 0.3 (BETA)
* Mon Apr 28 2025 Eduards Bezverhijs - 0.5.8-0.2
- Updated spec file for version 0.5.8, release 0.2 (BETA)
* Mon Nov 25 2024 Eduards Bezverhijs - 0.5.8-0.1
- Updated spec file for version 0.5.8, release 0.1 (BETA)
* Thu May 30 2024 Eduards Bezverhijs - 0.5.7-1.0
- Updated spec file for version 0.5.7, release 1.0 (STABLE)
* Thu Apr 25 2024 Eduards Bezverhijs - 0.5.7-0.1
- Updated spec file for version 0.5.7, release 0.1 (BETA)
* Sun Mar 17 2024 Eduards Bezverhijs - 0.5.6-1.0
- Updated spec file for version 0.5.6, release 1.0 (STABLE)
* Fri Mar 8 2024 Eduards Bezverhijs - 0.5.6-0.6
- Updated spec file for version 0.5.6, release 0.6 (BETA)
* Thu Feb 29 2024 Eduards Bezverhijs - 0.5.6-0.5
- Updated spec file for version 0.5.6, release 0.5 (BETA)
* Tue Feb 20 2024 Eduards Bezverhijs - 0.5.6-0.4
- Updated spec file for version 0.5.6, release 0.4 (BETA)
* Sat Feb 10 2024 Eduards Bezverhijs - 0.5.6-0.3
- Updated spec file for version 0.5.6, release 0.3 (BETA)
* Thu Feb 8 2024 Eduards Bezverhijs - 0.5.6-0.2
- Updated spec file for version 0.5.6, release 0.2 (BETA)
* Wed Aug 30 2023 Eduards Bezverhijs - 0.5.6-0.1
- Updated spec file for version 0.5.6, release 0.1 (BETA)
* Wed Aug 30 2023 Eduards Bezverhijs - 0.5.5-0.4
- Updated spec file for version 0.5.5, release 0.4 (BETA)
* Sun Aug 27 2023 Eduards Bezverhijs - 0.5.5-0.3
- Updated spec file for version 0.5.5, release 0.3 (BETA)
* Thu Jun 22 2023 Eduards Bezverhijs - 0.5.5-0.2
- Updated spec file for version 0.5.5, release 0.2 (BETA)
* Sat Dec 31 2022 Eduards Bezverhijs - 0.5.5-0.1
- Updated spec file for version 0.5.5, release 0.1 (BETA)
* Wed Sep 14 2022 Eduards Bezverhijs - 0.5.4-0.4
- Updated spec file for version 0.5.4, release 0.4 (BETA)
* Thu Sep 8 2022 Eduards Bezverhijs - 0.5.4-0.3
- Updated spec file for version 0.5.4, release 0.3 (BETA)
* Mon Sep 5 2022 Eduards Bezverhijs - 0.5.4-0.2
- Updated spec file for version 0.5.4, release 0.2 (BETA)
* Wed Jun 15 2022 Eduards Bezverhijs - 0.5.4-0.1
- Updated spec file for version 0.5.4, release 0.1 (BETA)
* Wed Feb 16 2022 Eduards Bezverhijs - 0.5.3-0.2
- Updated spec file for version 0.5.3, release 0.2 (BETA)
* Tue Feb 15 2022 Eduards Bezverhijs - 0.5.3-0.1
- Updated spec file for version 0.5.3, release 0.1 (BETA)
* Tue Jun 22 2021 Eduards Bezverhijs - 0.5.2-0.5
- Updated spec file for version 0.5.2, release 0.5 (BETA)
* Wed Jun 16 2021 Eduards Bezverhijs - 0.5.2-0.4
- Updated spec file for version 0.5.2, release 0.4 (BETA)
* Wed Jun 9 2021 Eduards Bezverhijs - 0.5.2-0.3
- Updated spec file for version 0.5.2, release 0.3 (BETA)
* Mon Jun 7 2021 Eduards Bezverhijs - 0.5.2-0.2
- Updated spec file for version 0.5.2, release 0.2 (BETA)
* Fri May 7 2021 Eduards Bezverhijs - 0.5.2-0.1
- Updated spec file for version 0.5.2, release 0.1 (BETA)
* Fri Mar 5 2021 Eduards Bezverhijs - 0.5.1-0.10
- Updated spec file for version 0.5.1, release 0.10 (BETA)
* Thu Jan 7 2021 Eduards Bezverhijs - 0.5.0-8.0
- Updated spec file for version 0.5.0, release 8 (STABLE)
* Tue Dec 29 2020 Eduards Bezverhijs - 0.5.0-7.0
- Updated spec file for version 0.5.0, release 7 (STABLE)
* Thu Dec 17 2020 Eduards Bezverhijs - 0.5.0-4.0
- Updated spec file for version 0.5.0, release 4 (BETA)
* Tue Dec 1 2020 Eduards Bezverhijs - 0.5.0-3.0
- Updated spec file for version 0.5.0, release 3 (BETA)
* Wed Nov 18 2020 Eduards Bezverhijs - 0.5.0-2.0
- Updated spec file for version 0.5.0, release 2 (BETA)
* Sun Nov 1 2020 Eduards Bezverhijs - 0.5.0-1.0
- Updated spec file for version 0.5.0 (BETA)
* Sat Oct 31 2020 Eduards Bezverhijs - 0.4.4-1.0
- Updated spec file for version 0.4.4 (STABLE)
* Tue Sep 8 2020 Eduards Bezverhijs - 0.4.3-1.0
- Updated spec file for version 0.4.3 (STABLE)
* Tue Aug 18 2020 Eduards Bezverhijs - 0.4.2-1.0
- Updated spec file for version 0.4.2 (STABLE)
* Wed Jul 15 2020 Eduards Bezverhijs - 0.4.1-1.0
- Updated spec file for version 0.4.1 (STABLE)
* Fri Jul 10 2020 Eduards Bezverhijs - 0.4.0-1.0
- Initial version of the spec file (STABLE)
timekpr-next/spec/README 000644 001750 001750 00000006173 15122253261 017013 0 ustar 00bezvfedu bezvfedu 000000 000000 INSTALLATION INSTRUCTIONS
-------------------------
Fedora / openSUSE:
Use whichever GUI your distribution has or:
sudo rpm -Uhv
VERSIONING
----------
Release less than 1.0 is BETA, 1.0+ is STABLE, example:
BETA (release 0.6): timekpr-next-0.5.1-0.6.fc32.x86_64
STABLE (release 7.0): timekpr-next-0.5.0-7.0.fc32.x86_64
NOTES
-----
If You are using Gnome3 with Gnome Shell, please DO NOT forget to install this (otherwise icon will be hidden)!
Fedora:
sudo dnf install gnome-shell-extension-appindicator
openSUSE:
install "KStatusNotifierItem/AppIndicator Support" extension manually from https://extensions.gnome.org/
Enable extensions if needed (for Fedora install Gnome Tweaks application and enable said extension)
Let the user log out and in to see the timekpr-nExT in action.
-------------------------
REMOVAL instructions
--------------------
Fedora:
sudo dnf remove timekpr-next
openSUSE:
sudo zypper remove timekpr-next
-------------------------
BUILD instructions (please open terminal and install this in one go not leaving the terminal (unless You know what to do))!
---------------------------------------------------------------------------------------------------------------------------
# set version
Fedora / openSUSE:
export TNVER="0.5.9"
export TNREL="0.2"
# install build tools
Fedora:
sudo dnf install rpmdevtools
openSUSE:
sudo zypper install rpmdevtools rpm-build
# this sets up build tree (rpmbuild dir in home directory)
Fedora / openSUSE:
rpmdev-setuptree
# get the timekpr sources
Fedora / openSUSE:
wget https://launchpad.net/timekpr-next/stable/${TNVER}/+download/timekpr-next-${TNVER}.tar.gz -P $HOME/rpmbuild/SOURCES/
# got to source directory and get the spec file
Fedora / openSUSE:
cd $HOME/rpmbuild/SPECS/
tar xzf $HOME/rpmbuild/SOURCES/timekpr-next-${TNVER}.tar.gz --strip-components=2 timekpr-next/spec/timekpr-next.spec
# install build dependencies
Fedora:
sudo dnf builddep timekpr-next.spec
sudo dnf install libindicator-gtk3 libappindicator-gtk3 python3-psutil gettext
openSUSE:
sudo zypper install python3 desktop-file-utils appstream-glib systemd sed grep gtk3 python3-dbus-python python3-gobject python3-psutil libayatana-indicator3-7 gettext typelib-1_0-Gtk-3_0 typelib-1_0-AyatanaAppIndicator3-0_1
# build binary package
Fedora / openSUSE:
rpmbuild -bb timekpr-next.spec
# install needs to be adjusted to correct version of distro (example given for FC32)
# if installation complains about conflicts with existing package, please remove the old version of package
# and then install new one or use --force at the end of the command
Fedora / openSUSE:
sudo rpm -Uhv --force $HOME/rpmbuild/RPMS/$(arch)/timekpr-next-${TNVER}-${TNREL}*.x86_64.rpm
---------------------------------------------------------------------------------------------------------------------------
timekpr-next/spec/debian.install.2.spec.install.sh 000775 001750 001750 00000000734 13716566163 024135 0 ustar 00bezvfedu bezvfedu 000000 000000 #!/bin/bash
# convert debian install file to RPM install file list
grep -v -e '^#' -e '^$' ../debian/install | sort | sed \
-e 's|/\+$||' \
-e 's|^\(.\+/\)\(.\+\) \(.\+\)$|/\3|g' \
-e 's|\(.\+\)\(/timekpr\)/.\+$|\1\2|g' \
-e '/\/timekpr$/! s|$|/*|' \
-e 's|/usr/share/|%{_datadir}/|' \
-e 's|/usr/bin/|%{_bindir}/|' \
-e 's|/usr/|%{_prefix}/|' \
-e 's|/etc/|%{_sysconfdir}/|' \
-e 's|/var/lib/|%{_sharedstatedir}/|' \
-e 's|/lib/systemd/|%{_prefix}/lib/systemd/|' \
| sort -u
timekpr-next/README.md 000644 001750 001750 00000132321 15122253261 016453 0 ustar 00bezvfedu bezvfedu 000000 000000 # Timekpr-nExT
## Keep control of computer usage
Timekpr-nExT is a fresh, simple and easy-to-use screen-time management
application. It helps you optimize the time spent on the computer by your
children, subordinates, or even yourself.
The application is intended for parents or supervisors who want to limit or
shape computer usage in a way that suits their household or environment.
## Overview
Timekpr-nExT is designed to give you reliable control over computer usage. This
includes the ability to forcibly end user sessions and apply other restrictions
or limitations.
Please be responsible and inform your users that their activity is being timed
and that their session may end abruptly. Notifications can be configured to warn
them ahead of time, but ultimately it is up to the user to pay attention to
these warnings.
When setting up restrictions, supervisors should read the descriptions of all
options and take some time to explore the application before enforcing any
settings. Different combinations of options can create a very tailored
experience, but remember that Timekpr-nExT is a restrictive tool by design.
_**Note**: This README is updated along with the `beta` releases. If you do not
see a specific option in the `stable` series, please wait for its release or use
the `beta` version._
## Navigation guide
**This README** contains all the details you need, but it is quite large.
Below is a simple guide to help you find what you are looking for.
* About functionality:
- A short description of the [applications](#applications) with a
[typical use case](#typicalusecase).
*(This is likely the section most parents or supervisors will want to read.)*
- For a deeper understanding, see the
[detailed description of functionality](#detaileddescription):
- Information about the user's side of the software:
[client application](#clientapplication)
- Information about the supervisor's tools:
[administration application](#administrationapplication)
- Recently added important features:
- Alternative restriction types:
[suspend / lock / shutdown](#restrictionlockouttypes)
- The ability to limit specific applications or games:
[PlayTime functionality](#playtimeconfiguration)
- User-controlled notifications:
[user configurable notifications](#userconfigurablenotifications)
- Time periods where usage does not count toward limits:
["freeride" time periods](#freerideintervals)
- Logging to system journal:
[systemd journal](#logtojournald)
- CLI (console) options and file-based configuration:
[additional configuration possibilities](#additionalconfigpossibilities)
* **Installation guide**:
- Installation and removal
[instructions](#installation) for most popular Linux systems
* **Support the project**:
- You can support development by **[donating](#support)**
* Translations:
- Help translate Timekpr-nExT into your
[language](#translate)
* Disclaimer, questions, suggestions, and reporting bugs:
- The disclaimer is located
[here](#disclaimer), and information about questions, suggestions,
and bug reports is found [here](#bugs)
## Applications overview
Timekpr-nExT provides two user-facing applications that control how the system
manages computer time and restrictions.
#### Timekpr-nExT Client indicator icon
This small application places an indicator icon in the system notification area.
It shows remaining time, limits, and notifications to the user.

_An informational "padlock" icon appears in the system’s notification area. This
is what users automatically see when they log in._
#### Timekpr-nExT Client application
The client application opens when the user clicks the indicator icon and selects
**"Limits & configuration"**. It provides more detailed information as well as
useful configuration options.

_This application displays detailed time limits and allows users to adjust some
aspects of the functionality. It is intended for the end user._
#### Timekpr-nExT Administration application
This application is designed for supervisors or parents. It lets you configure
time limits for any user on the system and adjust technical settings for how
Timekpr-nExT behaves.

_The administration tool provides multiple configuration tabs so supervisors can
customize restrictions and user experience as needed._
#### Core of Timekpr-nExT
Behind the scenes, Timekpr-nExT includes a background service (often called a
"daemon"—not a demon!) that monitors the system and enforces all restrictions.
There is no screenshot for it, but rest assured: it is always running quietly in
the background, doing its job for you. ;)
## Description of functionality
This section explains how Timekpr-nExT works behind the scenes, rather than
listing every configuration option in detail.
**Please note:** Timekpr-nExT relies heavily on tool-tips for explaining
individual options. Hover your mouse over any setting to see its description.
These hints are very helpful for understanding what each option does.
### Overall features
Timekpr-nExT aims to be as precise and user-friendly as possible, while still
providing reliable restrictions.
A key part of this is predictable and accurate time tracking. By default,
Timekpr-nExT checks and updates the user’s time usage every 3 seconds. This
ensures precise accounting and provides clear information about remaining time.
If users want more or fewer notifications, they can adjust this themselves.
Time is not counted when it should not be. For example, Timekpr-nExT avoids
counting time when the session is inactive. A session is considered inactive
when:
- another user is using the computer via user switching, or
- the current user’s session is locked and/or the screensaver is active.
Time is also not counted when the computer is sleeping or shut down.
The rest depends on how the supervisor configures the system. Keep reading for a
complete overview of everything Timekpr-nExT can do.
### Administration application
The administration application is where supervisors or parents configure time
limits, restrictions, and various technical settings for Timekpr-nExT.
To open the administration tool, you must either run it as a superuser or add
yourself to the `timekpr` system group for password-less access. Most people use
superuser mode because it works immediately without additional setup.
Running it as superuser is simple: open your application menu, dock, or launcher
and choose **"(SU) Timekpr-nExT Control Panel (superuser mode)"**.
If you have added yourself to the `timekpr` system group, you can instead launch
**"Timekpr-nExT Control Panel"** without entering a password.
_**Be aware**: this will NOT work unless you are actually in the `timekpr` group, which
may require you to log out and back in to work or a simple restart will take care
of that._
_**Note**: The "(SU)" prefix exists because older Gnome3 versions would truncate long
application names, making the two modes look identical._
To add yourself to the `timekpr` group, you can use your desktop environment’s
user management tool, or run `sudo gpasswd -a $USER timekpr`
Replace `$USER` with the correct administrator account if needed. Do not add
your child’s or subordinate’s username. After adding yourself, you must log out
and back in for the change to take effect.
_**Please note**: Some configuration options are unavailable in password-less
mode. These advanced options are rarely needed and should only be used in
special situations. Running in superuser mode provides full access to all
settings._
### User configuration
To configure each user’s limits and restrictions, you must first select them from
the user list. The list is taken directly from your operating system, and the
initial configuration is created automatically. After that, the list is stored
in the configuration directory, and **the configuration remains even if the OS
user is deleted**.
This means you can re-create a system user without losing their Timekpr-nExT
settings.
_**Please note:** When you open the administration application, **no user is
pre-selected**. This is intentional to ensure the supervisor chooses the correct
user and avoids accidental misconfiguration._
#### Tab "Info & today"
As the name suggests, this tab shows how much time the user has already spent
today and how much time they still have available.
Some information is shown only when the user is currently logged in. Live data
is more accurate and lets you monitor activity in real time. When the user is
not logged in, Timekpr-nExT shows the saved state instead. This saved
information is always available but is refreshed less frequently.
Time adjustment controls allow supervisors to reward a user with extra time or,
if needed, reduce their available time for the day.
Remember that adding time does **not** override allowed time periods or the
maximum daily allowance. These rules remain in place regardless of adjustments.
_**Pro tip**: Move your mouse over any item to read its tool-tip for additional
details._
A special value called **"Continuous time available"** shows how much time the
user can stay logged in **without interruption**, based on their current daily
allowance and allowed time periods. However, it never counts more than “today +
tomorrow” combined.
Continuous time can stretch past midnight if you configure the allowed periods
correctly. For example, if today’s time period ends at 24:00 and tomorrow’s
period starts at 00:00, usage can continue smoothly across midnight.
#### Tab "Limit configuration"
This tab lets you configure a user’s time restrictions. You can mix and match
options to create the daily routine you want.
Before diving into the full explanation, here are the two most common setup
patterns:
* Allow the full day (24:00:00), but restrict access to specific intervals, e.g.
09:00–12:00, 13:00–16:00, and 19:00–21:00.
* Give a fixed amount of time per day (e.g. 08:00:00), and define the time range
during which they can use those hours, e.g. 09:00–21:00.
_**Please note:** Time values follow the ISO 8601 standard: Monday is the first
day of the week and the system uses 24-hour time._
---
##### Week day limits
This section configures each day of the week. You can enable or disable computer
usage for any day and set different time limits for each one.
Days must be enabled or disabled individually, but time limits can be edited for
multiple days at once. Select several days (use Ctrl+A for all), choose whether
you’re editing hours or minutes, and press the “+” or “–” buttons.
You may also edit a single day’s limit by clicking the value in the table and
typing it manually. This allows entering seconds too (e.g. 7 → 07:00:00,
7:1:15 → 07:01:15).
By default, every day is enabled and users have unlimited time available.
---
##### Hour intervals
Hour intervals define when the user is allowed to use the computer. These can be
configured per day, or for several days at once by selecting multiple entries.
To create an interval, click the **Create** button and then define the start and
end times by clicking the interval in the table. You can modify the interval
later if needed.
Shortcuts also work here: entering `7` becomes 07:00, and `7:15` becomes 07:15.
A special type of interval marked with **“∞”** (unaccounted time interval) means
that any time used in that period **does not count** toward the daily allowance.
This effectively becomes **free time**, even if the user has no regular time
left.
This is especially useful for scheduled activities like online classes or
homework sessions, where usage should not reduce personal computer time.
Intervals must **not** overlap in any way. However, intervals may touch
continuously (e.g. 07:00–13:00 and 13:00–14:30). Adjacent intervals might merge
automatically.
Keep in mind: if there is even a one-minute gap between intervals, the user will
face their configured restriction during the gap—for example, session
termination.
A design choice in Timekpr-nExT also ensures that a single hour cannot contain
more than one interval. A period may start or end inside an hour, but two
separate intervals cannot exist within the same hour (e.g. 07:15–09:15 and
09:45–14:30 are **not** allowed, but 07:15–09:00 and 09:45–14:30 **are**).
After setting intervals, click **Verify** so the application can check for
mistakes. If all is correct, you can apply the configuration. If not, conflicting
intervals will be highlighted.
By default, the entire day is available.
---
##### Weekly and monthly limits
This section lets you adjust weekly and monthly time allowances. Select a period,
choose a time unit (day, hour, or minute), and press the “+” or “–” buttons to
change the limits.
You may also edit a limit directly by clicking the value and typing it manually.
This allows editing seconds too (though it’s rarely necessary). Shortcuts work
as well: entering `7` creates `07:00:00:00` (days:hours:minutes:seconds), and
entering `6:10:5:11` results in `06:10:05:11`.
These limits work together with daily limits and hour intervals. The time the
user actually gets is **the smallest** of all limits combined. This means that
even if the daily limit has not been reached, the user may be restricted if they
have already used up their weekly or monthly allowance.
By default, the whole week and month are unrestricted. For most families, there’s
no need to adjust these values unless weekly or monthly control is desired.
---
#### PlayTime configuration
PlayTime is a screen time limiting feature for specific applications or
processes. In PlayTime terminology, these are called **activities**.
PlayTime expands the usual time tracking by adding control over how long certain
applications can run. Effectively, it works like a focused process monitor.
Users may run configured activities for the allowed amount of PlayTime. Once the
allowance is used up, those applications are terminated. Running them again ends
them immediately.
Keep in mind that PlayTime is generally treated as a **special time limit inside
the regular time limit**, so all normal rules apply—except when **override
mode** is enabled (explained later).
PlayTime was originally designed for games, but supervisors may monitor any
software, such as web browsers, chat apps, or even calculators.
_**Please note:** PlayTime time is still counted even during “∞” (unaccounted)
intervals—**unless override mode is enabled**._
---
##### PlayTime options
This section contains PlayTime controls, including the main on/off switch and
the PlayTime override mode.
PlayTime must be enabled **per user**. If it is not enabled for a user, it will
not work for them. You must also enable the
[PlayTime master switch](#playtimemasterswitch); without it, PlayTime will not
function system-wide.
By default, PlayTime is disabled.
Below is an overview of PlayTime behavior and modes.
The **standard mode** is restrictive. Certain activities can be used for a set
amount of time. Once the limit is reached, the application is closed. For
example, if Steam is configured and the PlayTime limit is one hour, Steam will
close after one hour and cannot be used again until the next day.
The **override mode** changes the concept entirely. In override mode, the usual
PlayTime limits are ignored. Time spent at the computer is counted **only when
PlayTime activities are running**. In other words, the rest of the time becomes
“free time.”
Using the Steam example:
The user can freely use the computer until they start Steam. Then the PlayTime
clock starts. If they close Steam before the time is fully used, the remaining
time is preserved, and general usage becomes unrestricted according to normal
rules.
The option **“Allowed during ‘∞’ intervals”** controls whether PlayTime
activities can run during unaccounted ("∞") intervals.
- If the option is **disabled**, activities cannot run during unaccounted time
intervals at all, even in override mode. Using Steam as an example, the user
will be unable to launch it during “∞” time.
- If the option is **enabled**, activities may run during unaccounted intervals.
In that case, PlayTime still counts usage normally.
- If override mode is enabled during an unaccounted interval, **everything is
free**: the user can use the activity with no limits, and no time will be
counted toward any allowance.
This option is useful when unaccounted intervals are used for activities like
online school. Supervisors may prevent certain apps from running during these
periods by disabling “Allowed during ‘∞’ intervals.”
Override mode is not immediately intuitive, but once understood, it can be very
useful for precise time accounting.
By default, the option is enabled.
---
##### PlayTime limits
PlayTime limits work similarly to standard time allowances. The interface is the
same, but these limits apply **only to PlayTime activities**.
If a day is disabled for PlayTime, the user cannot run any monitored activities;
they will close instantly.
_**Please note:** PlayTime limits do not apply when override mode is enabled._
---
##### PlayTime activities
This is the most important PlayTime section. Here you configure the activities
(processes) that should be monitored. This list is not a simple allowlist or
denylist—each activity requires a **process mask**.
Determining the correct mask may require using a system monitor tool. Any KDE or
GNOME “System Monitor” works well. You can also use a console or SSH.
Linux applications can be installed almost anywhere and often by the user
themselves—Steam, Lutris, Heroic, and many others install games in user-owned
directories. Applications also do not identify themselves as “games” or
“restricted programs,” so automatic detection is essentially impossible.
That is why the supervisor must choose which programs to monitor.
So here is a general guide on how to determine which processes or masks can be
used when configuring PlayTime activities.
If you are not familiar with the terminal, this may look intimidating at first.
However, you can use graphical system monitoring tools instead. KDE and GNOME
both provide “System Monitor” applications that work well for this purpose.
Look for columns such as **Process Name**, **Command**, or **Command Line**—these
are your best helpers. You can always search online or ask your community how to
identify an executable name.
The process mask for a PlayTime activity is usually just the executable name, or
the full command line if the
["Enhanced activity monitor"](#playtimeenhancedactivitymonitor) is enabled
(case-sensitive!). A simple terminal command such as `top -c -d 1` is
often enough to find the process you need.
Games typically use the most resources while running, so they appear near the
top of the list.
Look at the COMMAND column. If something looks like the activity you want to
limit, take the executable name only—without its full path or any arguments.
Example: If you see `/opt/discord-canary/DiscordCanary --type=renderer ...`
then the correct mask is just `DiscordCanary`. This is the part after the last
slash and before the arguments.
Some games behave poorly when switching windows (Alt-Tab). In that case, you can
connect via SSH from another device and check the process list there.
You may also enter a description for each activity. This description is shown to
the user instead of the mask. If no description is provided, the mask itself is
shown.
_**Please note**: Process masks can use RegExp (regular expressions), not
glob patterns. This is an expert feature and must be used carefully._
Always verify your RegExp. Incorrect expressions may terminate the wrong
processes or fail to match anything at all.
If a RegExp is invalid, Timekpr-nExT will treat it as a literal string.
For example, `*wine*` is not really a valid and useful RegExp, `.*wine.*` is
correct. If written incorrectly, the system will literally search for a process
named `*wine*`, which almost certainly does not exist.
RegExp support does not allow the symbols [], and you should avoid using
^ and $. Internally, Timekpr-nExT automatically anchors patterns, so a mask
like `.*wine.*` becomes similar to `^.*wine.*$` (or `/.*wine.*$` when matching
full command lines in enhanced mode).
PlayTime also uses smart caching and retrieves process lists efficiently. Only
processes belonging to the monitored user are checked, counted, or terminated.
PlayTime logic is active only when at least one user has PlayTime enabled and at
least one activity is configured.
---
#### Tab "Additional options"
This section provides extra per-user configuration options.
- **Track inactive sessions**: When enabled, every user session is accounted for,
even if it is locked or another user is using the computer. Use with caution.
- **Hide icon and notifications**: Hides the Timekpr-nExT client icon and almost
all notifications. Only critical notifications are shown. If enabled for an
unrestricted user, they may not even notice Timekpr-nExT is present.
- **Restriction & lockout type**: Determines what action is taken when a user’s
time runs out. Be careful when changing from the default.
---
##### Restriction / lockout types
**Terminate sessions**
Default and restrictive. User session is forcibly logged out immediately.
**Kill sessions**
Similar to terminate sessions. Attempts a soft kill, but effect is largely the
same.
**Shutdown computer**
Restrictive option. Shuts down the computer when user time expires. Use with
caution in multi-user environments.
**Suspend computer**
Lockout option. Suspends the computer without terminating sessions. More suited
for self-control rather than restricting usage. If the computer is woken with no
time left, it remains suspended until unlocked.
**Suspend / wakeup computer**
Lockout option. Similar to suspend, but computer wakes up automatically at the
next available time period for the day. Requires specifying allowed wakeup hour
interval. Dependent on BIOS/UEFI RTC wakeup support.
**Lock screen**
Lockout option. Locks the screen when time expires. If unlocked without available
time, it locks again shortly. Suited for self-control rather than full restriction.
---
### Timekpr-nExT configuration
This tab allows supervisors to configure technical behaviors. Usually, default
values are optimal.
#### Control options
**Final notification**
Forces one last notification when user time is almost up, even if the user has
disabled notifications.
**Termination time**
Specifies seconds left before applying the selected restriction/lockout. Allows
users to request more time before enforcement.
**Countdown time**
Specifies seconds left before starting a continuous countdown notification for
the final seconds.
**Poll interval**
Defines the frequency (in seconds) at which Timekpr-nExT updates user sessions
and time values. This effectively sets the resolution of time accounting.
**Save time**
Specifies how often (in seconds) Timekpr-nExT saves calculated time values to
disk. Minimizes potential loss during crashes.
**Log level**
Controls verbosity of logs:
- Level 1: minimal (saves space, minimal info)
- Level 2: standard (sufficient info for troubleshooting)
- Level 3: verbose, memory-intensive, mostly useful for developers
Log files are handled by `logrotate` and compressed, so space is not usually an
issue.
---
#### Tracked sessions
Timekpr-nExT tracks the following session types by default:
- `x11` (Xorg)
- `wayland`
- `mir` (less common)
Do not modify these unless you understand Linux session types.
#### Excluded sessions
By default, the following session types are **not tracked**:
- `tty` (console sessions via Ctrl+Alt+F[1-7])
- `unspecified` (other unknown types)
Modify only if necessary and with understanding of the system.
#### Excluded users
Allows supervisors to exclude **system users** from time accounting, such as
login managers. Do **not** enter regular users here, as it may cause errors.
#### Additional options (PlayTime)
Several options in this section are related to **PlayTime** functionality.
---
##### PlayTime enabled
Controls the **master switch for PlayTime**.
- Must be enabled for PlayTime to work globally.
- If disabled, no user activities will be tracked, regardless of individual PlayTime settings.
---
##### Enhanced activity monitor
Controls whether PlayTime checks the **first 512 characters of the full process command line**, including arguments, against registered activity/process masks.
- Without this option, only the executable path and name are checked.
- Enables advanced RegExp matching for complex scenarios, e.g., interpreted languages like Python or Java.
- Example: For Minecraft (a Java application), a process mask could be `.*java.*minecraft.*`.
- **Note:** Changes only apply to newly started processes.
---
### Client application
The Timekpr-nExT client shows time metrics and limited configuration options for the user.
- Users can view tooltips for detailed explanations of every value.
- Appearance may vary depending on the desktop environment ([differences](#desktopenvironmentdifferences)).
---
#### Daily limits
Displays:
- Daily time allowance
- Time period restrictions
- Time spent / time left
Key points:
- Icon color reflects remaining time (configurable by the user).
- Periods marked with "∞" do not count towards the daily limit.
- Unaccounted time intervals turn the icon gray while active.
---
#### Additional limits
Displays weekly and monthly time limits and usage so far.
---
#### PlayTime limits
Displays:
- Time allowance and activity list for PlayTime
- Type of PlayTime mode enabled
- Active activity count and descriptions
**Note:** Tab is visible only if supervisor enabled PlayTime for the user.
---
#### Notifications
Users can adjust notifications for time left:
- Set thresholds for standard and PlayTime notifications
- Assign priority levels
**Note:** PlayTime notifications appear only if enabled by supervisor. Tooltips provide detailed guidance.
---
#### Configuration
Allows users to tailor Timekpr-nExT behavior:
- Show seconds in icon
- Enable or disable limit change notifications
- Configure sound notifications (requires `python3-espeak` or similar)
- Adjust notification types and log level
**Note:** There may be a [sound notification bug](#quirkssound).
---
## Typical use case
Imagine Liane wants to limit computer time for Cartman because he spends too much time online talking to Kyle and Stan _(Kenny doesn’t count—he doesn’t have a computer)_.
She also wants to reserve 2 hours on Monday (15:00–17:00) for mandatory anger management classes.
Liane wants a flexible schedule within strict time windows for weekdays and holidays. Using Timekpr-nExT, she can configure it as follows:
---
**Configuration steps in Timekpr-nExT Administration:**
1. Select username **"cartman"** and open the **Limit configuration** tab.
2. **Monday:**
- Set daily time limit: **6 hours**
- Add interval **7:30–15:00**
- Add interval **15:00–17:00**, check the "**∞**" box (time not counted against limit)
- Add interval **17:00–21:00**
3. **Tuesday–Friday:**
- Set daily time limit: **6 hours**
- Add interval **7:30–21:00**
4. **Holidays (Saturday & Sunday):**
- Set daily time limit: **8 hours**
- Add interval **9:00–22:30**
5. Click **Apply daily limits** to save restrictions.
---
**Resulting behavior:**
- Cartman has **6 hours of computer time** Monday–Friday (7:30–21:00).
- **15:00–17:00** is free for mandatory classes (doesn’t consume daily limit).
- Cartman has **8 hours of computer time** during holidays (9:00–22:30).
- Cartman **cannot use the computer** outside the defined intervals or exceed his daily allowance.
---
> Typical setups are straightforward, but Timekpr-nExT supports more advanced configurations.
> For further details, see [Detailed information](#detaileddescription).
---
## Installation / removal
To start using Timekpr-nExT, you need to install it. There are two versions: **beta** (early access) and **stable** (production-ready).
> Tip: Logging out and back in after installation is highly recommended.
**Availability:**
- Ubuntu and derivatives: via **PPA**
- ArchLinux / Manjaro: via **AUR**
- Debian: native packages (may be outdated)
- Fedora: via **johanh's COPR repository**
- openSUSE: native packages (from 2025)
- Manual installation: Fedora 32+ and openSUSE Leap 15.2+
- NixOS: via **nixos-unstable** (and future nixos-25.11)
Installation via terminal is generally faster than GUI, but both methods are supported.
**NOTE**: I am not creating packages for manual installation anymore!
The ones which were created are for historical purposes or older installations only!!!
#### Stable
| Distribution | Stable install | Stable remove |
| :--- | :--- | :--- |
| Ubuntu & co (via PPA) | ```sudo add-apt-repository ppa:mjasnik/ppa``````sudo apt-get update``````sudo apt-get install timekpr-next``` | ```sudo apt-get remove --purge timekpr-next``` |
| ArchLinux & Manjaro (via AUR) | ```yay -S timekpr-next``` | ```sudo pacman -Rdd timekpr-next``` |
| Fedora | [copr repo](https://copr.fedorainfracloud.org/coprs/johanh/timekpr-next/) _(preferred)_**or**[manual installation](https://drive.google.com/drive/folders/1iN1wcPctGhd_OISqzWZ5DigFMVvgSGq9) _(README and packages)_| [copr repo](https://copr.fedorainfracloud.org/coprs/johanh/timekpr-next/) _(preferred)_**or**[manual uninstallation](https://drive.google.com/drive/folders/1iN1wcPctGhd_OISqzWZ5DigFMVvgSGq9) _(README)_ |
| openSUSE | [manual installation](https://drive.google.com/drive/folders/1iN1wcPctGhd_OISqzWZ5DigFMVvgSGq9)_(README and packages)_| [manual uninstallation](https://drive.google.com/drive/folders/1iN1wcPctGhd_OISqzWZ5DigFMVvgSGq9)_(README)_ |
| NixOS | set [services.timekpr.enable](https://search.nixos.org/options?channel=unstable&show=services.timekpr.enable) to `true` | set [services.timekpr.enable](https://search.nixos.org/options?channel=unstable&show=services.timekpr.enable) to `false` |
---
#### Beta
| Distribution | Beta install | Beta remove |
| :-- | :--: | --: |
| Ubuntu & co (via PPA) | ```sudo add-apt-repository ppa:mjasnik/ppa``````sudo apt-get update``````sudo apt-get install timekpr-next-beta``` | ```sudo apt-get remove --purge timekpr-next-beta``` |
| ArchLinux & Manjaro (via AUR) | ```yay -S timekpr-next-git``` | ```sudo pacman -Rdd timekpr-next-git``` |
| Fedora and openSUSE | [manual installation](https://drive.google.com/drive/folders/1iN1wcPctGhd_OISqzWZ5DigFMVvgSGq9)_(README and packages)_ | [manual uninstallation](https://drive.google.com/drive/folders/1iN1wcPctGhd_OISqzWZ5DigFMVvgSGq9)_(README)_ |
> **Note:** For ArchLinux and Manjaro, choose your preferred AUR helper if different from the examples above.
> Special thanks to **SanskritFritz** (ArchLinux community) for beta testing and maintaining packages.
> Thanks to **johanh** (Fedora community) for providing the COPR repo.
---
#### Debian
Timekpr-nExT is now available as a native Debian package thanks to **Sim (smntov)** and **Anthony Fok (foka)**.
Preferred method is same as Ubuntu:
| Stable install | Stable remove | Beta |
| :--- | :--- | :--- |
| ```sudo apt-get update``````sudo apt-get install timekpr-next``` | ```sudo apt-get remove --purge timekpr-next``` | Not available |
> **Note:** Packages built for Debian "sid" (unstable) may work on other versions like "buster" if dependencies are satisfied.
> Graphical installers like **KDE Discover** or **GNOME Software** can also be used.
> Always use a package created specifically for Debian for best compatibility.
## Compatibility
Timekpr-nExT **was** developed to be compatible with most Desktop Environments. During development, around 20 VMs **were** used to test major
changes, but not all possible setups are tested in the wild. Currently, testing is mostly on demand, based on bug reports or
developer discretion. In other words, nothing gets proactively tested until there is a reason.
Tested Desktop Environments include: **KDE, GNOME, Cinnamon, MATE, XFCE**
Tested distributions include: **Ubuntu, Manjaro/ArchLinux, Debian, Fedora, openSUSE**
> Please check the [nuances](#quirks) section for more detailed information.
> If you encounter an issue, see [this](#bugs) and file a bug report.
---
## Information Representation Differences
Desktop Environments handle visual representation differently. Below are some key differences:
### Icon Differences
- The system tray icon can appear differently across environments:
- **GNOME3 / Unity / XFCE**: show the icon with a detailed label beside it.
- **KDE / Deepin**: show only the icon; hovering reveals the time left.
### Extensions May Be Needed
- Some GNOME3-based distributions require an extension to display the icon.
- Recommended extension: [AppIndicator Support](https://extensions.gnome.org/extension/615/appindicator-support/)
### Notification Timeouts / Severity
- Certain desktop environments may override Timekpr-nExT’s notification timeout and severity settings.
- KDE Plasma respects custom settings the best. Other environments may enforce preconfigured values.
### Future of the Icon
- GNOME3 may eventually remove the status icon functionality. If this happens, a workaround will be investigated.
---
## Short and Very Technical Overview
> **Warning:** This section is highly technical and not necessary for regular users.
Timekpr-nExT relies heavily on **DBUS** for system interaction:
- Uses DBUS interfaces provided by the system.
- Exposes its own DBUS interfaces.
- Can integrate with any client application or control panel that supports DBUS.
This architecture allows Timekpr-nExT to be highly extensible for technical users or administrators seeking deeper integration.
### Interfaces Used
Timekpr-nExT relies on several system interfaces for its functionality:
1. **systemd login1 interface**
- Used for session management.
- Mandatory for Timekpr-nExT; without it, the software will not function.
- Documentation: [systemd-logind](https://www.freedesktop.org/wiki/Software/systemd/logind/)
2. **FreeDesktop screensaver interfaces**
- Used to detect idle time and inhibit screensavers.
- Documentation: [Idle Inhibition Spec](https://people.freedesktop.org/~hadess/idle-inhibition-spec/ch05.html)
3. **FreeDesktop notifications interface**
- Used to display notifications about time left.
- Documentation: [Notification Spec](https://specifications.freedesktop.org/notification-spec/latest/ar01s09.html)
4. **GNOME screensaver interface (fallback)**
- Used if FreeDesktop screensaver interfaces are unavailable.
- Documentation: [GNOME Screensaver](https://people.gnome.org/~mccann/gnome-screensaver/docs/gnome-screensaver.html)
> Timekpr-nExT connects to these DBUS objects once, caches them, and mainly calls them during execution for efficiency.
---
### Additional Configuration Possibilities
Timekpr-nExT can be configured through:
1. **Graphical interface (GUI)**
2. **Command-line interface (CLI)**
3. **Direct configuration file editing**
---
#### CLI (Command Line Interface) Mode
- The **Administration application** can be run in a terminal to manage users and configure limits, with functionality equivalent to the GUI.
- CLI usage is restricted to **user-level configuration only**.
- To explore CLI options, run `sudo timekpra --help`
**Security Note for CLI Usage**
CLI usage of Timekpr-nExT follows the same security rules as the graphical interface:
- You must be in the `timekpr` group,
- or execute the CLI as `root`,
- or use `sudo` to gain access.
Changes made in CLI or GUI are applied **immediately**, in real-time.
> **Note**: before version 0.5.9 CLI returned and accepted time in seconds only, since version 0.5.9 it accepts and can return (optionally) time as time string.
---
### Configuration by Files
Timekpr-nExT can also read configuration from files directly, which are applied at **save intervals** (default every 30 seconds).
**Important Notes:**
- Direct file editing is **not recommended**; prefer GUI or CLI.
- Configuration files have a **specific internal structure**. Improper edits can break functionality.
- If files are corrupted or unrecognizable, Timekpr-nExT will attempt to **salvage valid options** and **recreate defaults** for damaged entries.
### Configuration Files (Handle with Care)
| Purpose | File Location |
| --: | :-- |
| Main configuration file | `/etc/timekpr/timekpr.conf` |
| User configuration files (per user) | `/var/lib/timekpr/config/timekpr.*.conf` |
| User control files (per user) | `/var/lib/timekpr/work/timekpr.*.conf` |
| Client configuration file | `$HOME/.config/timekpr/timekpr.conf` |
---
### Log Files
Timekpr-nExT writes logs to track execution and performance. These files are generally **not meant for regular inspection** by users.
They mainly serve as a record in case something goes wrong and can be helpful for debugging.
- Log files contain mostly **technical details**; nothing sensitive except usernames (which can be obfuscated if necessary).
- From version `0.5.1`, log level changes take effect immediately—no restart required.
- Log size can be reduced by lowering log level in the administration app, though level 2 (default) is preferred for troubleshooting.
**Log File Locations**
| Logging Area | File Location |
| -- | :-- |
| Daemon log | `/var/log/timekpr.log` |
| Administration application logs | `/tmp/timekpra.*.log` |
| Client application logs | `/tmp/timekprc.*.log` |
---
**Note:** Since version 0.5.9, log entries may also be written to standard OS logging facilities if the right conditions are met.
Specifically, if the `python3-systemd` package is installed, logs are sent to `journald`; otherwise, they remain in the usual log file locations.
**Accessing Logs via System Journal**
To gather logs from `journalctl`, use the following commands:
| Logging Area | Description | Command (current boot) | Command (all time) |
| -- | -- | -- | :-- |
| Daemon log | main log file, needed for bug reports | `sudo journalctl -b -t timekprd > timekpr.log` | `sudo journalctl -t timekprd > timekpr.log` |
| Administration application logs | for issues involving admin app | `sudo journalctl -b -t timekpra > timekpra.log` | `sudo journalctl -t timekpra > timekpra.log` |
| Client application logs | for issues with user client app | `sudo journalctl -b -t timekprc > timekprc.log` | `sudo journalctl -t timekprc > timekprc.log` |
After gathering the log files, **compress them** (e.g., using `tar.gz` or `zip`) and attach them to bug reports for faster troubleshooting.
Without logs, I **cannot investigate issues** — there’s no way to magically see into your system without this information! In other
words - **my crystal ball is broken**!
---
### Quirks
Linux distributions vary widely in how they implement standards, so certain **quirks** may appear in Timekpr-nExT:
- Desktop Environments (DEs) may implement specifications partially or differently.
- Features might behave differently depending on the DE or distribution version.
- Quirks can affect **appearance, functionality, or notifications**.
> **Note:** These quirks may change as distributions and DEs evolve. Timekpr-nExT aims to support as many versions as possible, so some issues may persist on older versions.
#### Sound "bell" notifications
- If a user has enabled **Use sound "bell" for notifications**, critical notifications might not appear on the desktop.
- They are still registered in the notification system and can be reviewed there.
---
#### Blinking cursor / black screen after session termination
- Timekpr-nExT relies heavily on `login1` DBUS interfaces to terminate user sessions.
- On some systems, this can result in a **black screen or blinking cursor**, giving the impression that the system has frozen.
- This is often due to older installations or when the session does not switch to the login manager automatically.
- Workaround: Timekpr-nExT tries to detect and switch to the login manager after session termination.
- Using the **"kill sessions"** option in lockout/restriction types usually resolves the issue.
---
#### Idle time accounting while screen is locked
- Different Desktop Environments handle screen locking differently:
- **Gnome3-based DEs:** Screen must be locked and turned off for session to be considered inactive.
- **KDE, XFCE:** Locking the screen is sufficient.
- **Deepin:** Screen locking is incompatible due to deviations from FreeDesktop / Gnome standards.
---
#### Technical nuances
- These discrepancies can affect **time accounting** and session handling.
- Often, these issues cannot be fixed by Timekpr-nExT alone and depend on the DE or distribution version.
##### Time not accounted while user is active
- Rapid lid open/close can make `org.freedesktop.login1` report the session as inactive.
- Mitigation: Enable **inactive session tracking**, but results may vary.
##### Time not accounted when computer is locked
- Some DEs expose `org.freedesktop.ScreenSaver` incorrectly (`GetActive` always false or method not implemented).
- Some DEs implement `IdleHint` or `LockedHint` differently or not at all.
- If these interfaces are unreliable, idle time tracking may fail. Workarounds are limited.
##### Empty screen after logout
- Occasionally, logging out users via `org.freedesktop.login1.Manager` may result in a black screen if the DE does not handle session termination properly.
- Using **"kill sessions"** generally resolves this.
---
> **Summary:** Timekpr-nExT implements numerous workarounds to support popular DEs, but inconsistencies in Linux distributions can affect functionality. Many quirks are DE-specific and may require configuration adjustments or distribution updates.
## How You Can Help
A lot of effort went into developing Timekpr-nExT. If you appreciate the work and would like to contribute, you can do so via **PayPal** or **Bitcoin**:
- **PayPal (no account required):** [https://tinyurl.com/yc9x85v2](https://tinyurl.com/yc9x85v2)
- **Bitcoin address:** `bc1q57wapz6lxfyxex725x3gs7nntm3tazw2n96nk3`
You can also help by **translating Timekpr-nExT** into your language: [https://translations.launchpad.net/timekpr-next](https://translations.launchpad.net/timekpr-next)
---
## Disclaimer
Timekpr-nExT originated from the need to improve **timekpr-revived**, focusing on precise time accounting and proper session termination. Initially, it was developed for personal use (to monitor my kid’s computer usage), but that need no longer exists.
Key points:
- **Proactive feature development is limited:** Expect feature additions mainly from user suggestions or requests.
- **Testing on new distributions is limited:** Timekpr-nExT may not be actively tested on the latest OS versions unless bugs are reported.
- **Configurability:** Timekpr-nExT is flexible; simple needs can be met without complexity, but advanced configurations are possible.
If the application meets your needs, consider showing support [here](#support). Otherwise, suggest features or improvements—you may see them implemented in future updates.
---
## Suggestions and Bug Reporting
- **Register suggestions and bugs:** [https://bugs.launchpad.net/timekpr-next](https://bugs.launchpad.net/timekpr-next)
- **Ask questions:** [https://answers.launchpad.net/timekpr-next](https://answers.launchpad.net/timekpr-next)
- **Prefix beta bugs:** Include `BETA` in the bug title if the issue was found in a beta version.
You can also email suggestions/bugs to `edzis"replace this with at symbol"inbox"place a dot here"lv`
**REMEMBER: bug reporting without [log files](#logfiles) is useless!
Always provide log files to bug reports and questions section, if the issue you are having is related to unexpected behaviour!**
**Bug report tips:**
1. Describe the issue precisely and include steps to reproduce it.
2. Mention your OS distribution, version, and Desktop Environment.
3. Be ready to provide configuration and log files (no sensitive data except usernames).
Submitting bugs through Launchpad is preferred, but email reports are also accepted, all this may be addressed as time permits.
---
Thanks for choosing Timekpr-nExT!
timekpr-next/resource/ 000775 001750 001750 00000000000 13716566163 017042 5 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/resource/client/ 000775 001750 001750 00000000000 13476006650 020311 5 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/resource/client/forms/ 000775 001750 001750 00000000000 15122253261 021427 5 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/resource/client/forms/admin.glade 000644 001750 001750 00001004644 15122253261 023524 0 ustar 00bezvfedu bezvfedu 000000 000000
timekpr-next/resource/client/forms/client.glade 000644 001750 001750 00000365704 15122253204 023715 0 ustar 00bezvfedu bezvfedu 000000 000000
TrueFalse3gtk-newTrueFalse3gtk-new
low
Information
warning
Warning
important
Severe
critical
Critical
TrueFalse3gtk-deleteTrueFalse3gtk-delete131113011030110FalseTrueTimekpr-nExT clientFalsecenterTruetimekpr-clientnormalFalseTrue5555verticalFalseTrueexpandTrueFalsestartTrueFalseFalseStatus of Timekpr-nExT clientstart1010verticalTrueTrue0TrueTrue0TrueFalseendTruegtk-applyTrueFalseTrueTrueSave all changesTrueTrueTrue1gtk-close100TrueTrueTrueClose the windowTrueTrueTrue2TrueTrue1TrueTrue1TrueFalseTrueFalseverticalTrueFalseTrueFalsestart0TrueFalseIcon, isn't it?start302064timekpr-client6TrueTrue0TrueFalse5555TrueFalsecenterend155TrueFalseCurrent effective usernameUsername:startTrueTrue0TrueFalseCurrent effective usernameendTrueTrue1TrueTrue1FalseTrue0TrueFalseTrueTrueTrueFalsecenter5555TrueFalsestart10vertical5TrueFalsestartCurrent statisticsFalseTrue0TrueFalsestart5575TrueFalseTime spent this session or after Timekpr-nExT has been restartedendTime spent (session):True3000TrueFalseFormat: <days : hours : minutes : seconds>start00:00:00:00start10TrueFalseFormat: <days : hours : minutes : seconds>start00:00:00:0013TrueFalseTime inactive this session or after Timekpr-nExT has been restartedendTime inactive:True3001TrueFalseFormat: <days : hours : minutes : seconds>start00:00:00:0011TrueFalseContinuous time left to you. May span more than the current day.endContinuous time left:True3003TrueFalseTotal time available left today in a row, up to the end of the dayendTime left today:True3002TrueFalseFormat: <days : hours : minutes : seconds>start00:00:00:0012FalseTrue100TrueFalseTrueTrueFalseTrue5TrueFalseTrueTruein170TrueTrueThese are the days and limits that are available to youtimekprAllowedDaysDaysLSFalseFalse0Falsehorizontal01TrueFalsestartDays & Limits00FalseTrue0TrueFalseTrue5TrueFalseTrueTruein115TrueTrueThis shows the time intervals that are available for use.
Option "∞" indicates that time spent during this interval will not be accounted towards the daily limit, it will be accounted as idle instead.timekprAllowedDaysIntervalsLSFalseFalse0Falsehorizontal01TrueFalsestartIntervals00FalseTrue110TrueFalseDaily limitsFalseTrueFalsecenter555515TrueFalsevertical5TrueFalsestartAdditional statisticsFalseTrue0TrueFalse5575TrueFalseTime spent this weekendTime spent (week):00TrueFalseFormat: <days : hours : minutes : seconds>start00:00:00:00start10TrueFalseTime spent this monthendTime spent (month):01TrueFalseSelect whether inactive session time is counted.
If this is unchecked, time spent in console (not terminal emulator) login sessions and while the screen is locked is NOT taken into account.
This varies among desktop environments.endTrack inactive:02TrueFalseTrueFalseSelect whether inactive session time is counted. If this is unchecked, time spent in console (not terminal emulator) login sessions and while the screen is locked is NOT taken into account. This varies among desktop environments.0True12TrueFalseFormat: <days : hours : minutes : seconds>start00:00:00:00start11FalseTrue100TrueFalsevertical5TrueFalsestartAdditional limitsFalseTrue0TrueFalse5575TrueFalseTime limit for this week available to youendTime limit (week):00TrueFalseFormat: <days : hours : minutes : seconds>start500:00:00:00start10TrueFalseTime limit for this month available to youendTime limit (month):01TrueFalseFormat: <days : hours : minutes : seconds>start500:00:00:00start11FalseTrue1101TrueFalseAdditional limits1FalseTrueFalsecenter5555TrueFalsestart15vertical5TrueFalsestartCurrent PlayTime statisticsFalseTrue0TrueFalse5575TrueFalseTotal PlayTime available left todayendTime left today:03TrueFalseFormat: <days : hours : minutes : seconds>start00:00:00:0013TrueFalseTotal PlayTime spent todayendTime spent today:02TrueFalseFormat: <days : hours : minutes : seconds>start00:00:00:00start12TrueFalseTrueFalseThis option overrides the default time accounting.
If it's checked, the time is accounted only when activities in "Activity / application list" are running, the rest of the time is considered idle.0True10TrueFalseThis option overrides the default time accounting.
If it's checked, the time is accounted only when activities in "Activity / application list" are running, the rest of the time is considered idle.endTime limit override:fillTrue2000TrueFalseNumber of currently active PlayTime activitiesendRunning activities:04TrueFalseNumber of currently active PlayTime activitiesstart014TrueFalseAllow activities during unaccounted ("∞") time intervals.
This setting allows to use applications listed in "Activity / application list" during time intervals which are marked as unaccounted ("∞"), if the setting is not enabled, none of activities are allowed to run!endAllowed during "∞":fillTrue2501TrueFalseTrueFalseAllow activities during unaccounted ("∞") time intervals.
This setting allows to use applications listed in "Activity / application list" during time intervals which are marked as unaccounted ("∞"), if the setting is not enabled, none of activities are allowed to run!0True11FalseTrue100TrueFalse5TrueFalseTrueTruein170TrueTrueThese are days and limits that available to you as part of PlayTime activity restrictionstimekprPTAllowedDaysLimitsDaysLSFalseFalse0Falsehorizontal01TrueFalsestartDays & Limits0010TrueFalse5TrueFalseTrueTruein215TrueTrueThis shows activities / applications which are part of PlayTime restrictionstimekprPTAllowedDaysLimitsActsLSFalseFalse0Falsehorizontal01TrueFalsestartActivity / application list00202TrueFalsePlayTime limits2FalseTrueFalsecenter10105555TrueTrueFalse10TrueTrueTrueTruein25090TrueTrueConfiguration for personalized notifications about available time.
Please configure notifications as you see fit. However, please keep in mind that there will be two types of notifications that cannot be personalized: a final warning some time before time ends and a countdown notifications when time is about to end.
The configuration "Time" value indicates a value of time left when notification will be shown, the "Importance" option governs an icon colour and notification properties as follows.
Information - a green icon and an informational notification will be shown
Warning - a yellow icon and an informational notification will be shown
Severe - a red icon and important notification will be shown
Critical - a red icon and critical notification will be shown, this notification usually is shown over all open applications and stays open until dismissed, however this behaviour highly depends on Desktop Environment in useTimekprUserNotificationConfigLSFalse001TrueFalse5TrueFalseNotification configurationTrue25FalseTrue0TrueFalseTrueTrueTrueAdd new notification threshold to the listTimekprAddTrackedSessionsImageFalseTrue0TrueTrueTrueRemove notification threshold from the listTimekprRemoveTrackedSessionsImageFalseTrue1FalseTrueend10000TrueFalse10TrueTrueTrueTruein25090TrueTrueConfiguration for personalized notifications about available PlayTime.
Please configure notifications as you see fit.
The configuration "Time" value indicates a value of PlayTime left when notification will be shown, the "Importance" option governs an icon colour and notification properties as follows.
Information - a green icon and an informational notification will be shown
Warning - a yellow icon and an informational notification will be shown
Severe - a red icon and important notification will be shown
Critical - a red icon and critical notification will be shown, this notification usually is shown over all open applications and stays open until dismissed, however this behaviour highly depends on Desktop Environment in useTimekprUserPlayTimeNotificationConfigLSFalse001TrueFalse5TrueFalsePlayTime notification configurationTrue30FalseTrue0TrueFalseTrueTrueTrueAdd new PlayTime notification threshold to the listTimekprAddTrackedSessionsImage1FalseTrue0TrueTrueTrueRemove PlayTime notification threshold from the listTimekprRemoveTrackedSessionsImage1FalseTrue1FalseTrueend10010TrueFalse01TrueFalse113TrueFalseNotifications3FalseTrueFalsecentercenter555520TrueTrueFalseSelect whether to use speech notifications, if available.
You may be able to make speech notifications available by installing package "python3-espeak" or "python3-espeak-ng".0TrueTrueFalseUse speech notificationsTrue3503TrueFalseTrueTrueThis sets the logging level.
Please do not change this unless you know what you're doing.22timekprLogLevelLimitsADJ1TrueTrue1FalseTrue0TrueFalseThis sets the logging level.
Please do not change this unless you know what you're doing.5Logging levelfillTrue30FalseTrue113TrueTrueFalseSpecify whether to show a notification when limit configurations or allowance changes0TrueTrueFalseShow limit changesTrue3502TrueTrueFalseSpecify whether to show all notifications.
If unchecked, then only important ones are shown.0TrueTrueFalseShow all notificationsTrue3501TrueTrueFalseSpecify whether to show seconds in notification area.
Some desktop environments, like KDE5, do not support text besides notification icons.0TrueTrueFalseShow seconds in notification areaTrue3500TrueFalseTrueTrueThis sets how long a notification is shown for regular notifications about time left and configuration changes.
The value is specified in seconds.
A value of 0 means show until dismissed (not recommended for regular notifications).
Please note that the desktop environment you use may override this timeout, in which case this setting will not have any effect on notification.22timekprNotificationTimeoutADJ1TrueTrueFalseTrue0TrueFalseThis sets how long a notification is shown for regular notifications about time left and configuration changes.
The value is specified in seconds.
A value of 0 means show until dismissed (not recommended for regular notifications).
Please note that the desktop environment you use may override this timeout, in which case this setting will not have any effect on notification.5Notification timeout (sec)fillTrue30FalseTrue111TrueFalseTrueTrueThis sets how long a notification is shown for "Critical" notifications about time left (when the icon turns red).
The value is specified in seconds.
A value of 0 means show until dismissed.
Please note that the desktop environment you use may override this timeout, in which case this setting will not have any effect on notification.22timekprNotificationTimeoutCriticalADJ1TrueTrue1FalseTrue0TrueFalseThis sets how long a notification is shown for "Critical" notifications about time left (when the icon turns red).
The value is specified in seconds.
A value of 0 means show until dismissed.
Please note that the desktop environment you use may override this timeout, in which case this setting will not have any effect on notification.5Critical notification timeout (sec)fillTrue30FalseTrue112TrueTrueFalseSelect whether to use sound "bell" (short notification sound) to announce a new notification from Timekpr-nExT.
This works only for enabled notifications.
If this setting is not editable, your environment does not advertise sound notification support.0TrueTrueFalseUse sound "bell" for notificationsTrue45104TrueFalseConfiguration4FalseTrueTrue100FalseTrue1
timekpr-next/resource/client/forms/about.glade 000644 001750 001750 00000007305 15122253204 023537 0 ustar 00bezvfedu bezvfedu 000000 000000
FalseAbout Timekpr-nExTFalsecenter-alwaysTruetimekprdialogTimekpr-nExT0.0.1Copyright (c) 2018-2024 Eduards BezverhijsKeep control of computer usagehttps://launchpad.net/timekpr-nextTimekpr-nExTThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. In Debian, see file /usr/share/common-licenses/GPL-3Eduards Bezverhijs <edzis@inbox.lv>Eduards Bezverhijs <edzis@inbox.lv>
(Translations fine-tuning by JP Lord <jplord@gmail.com>)
(English fine-tuning by Phil Hudson <phil.hudson@iname.com>)TruecustomFalsevertical2FalsestartFalseFalse0TrueFalse64timekpr6TrueTrue1
timekpr-next/resource/launchers/ 000775 001750 001750 00000000000 14575617135 021026 5 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/resource/launchers/timekpr-admin.desktop 000600 001750 001750 00000001227 14575617135 025152 0 ustar 00bezvfedu bezvfedu 000000 000000 [Desktop Entry]
Version=1.0
Name=Timekpr-nExT Control Panel
Name[de]=Timekpr-nExT-Systemsteuerung
Name[fr]=Panneau de configuration Timekpr-nExT
Name[it]=Pannello di controllo Timekpr-nExT
Name[lv]=Timekpr-nExT vadības panelis
Comment=Keep control of computer usage
Comment[de]=Kontrolle der Computernutzung
Comment[fr]=Gardez le contrôle de l'utilisation de l'ordinateur
Comment[it]=Mantieni il controllo sull'utilizzo del computer
Comment[lv]=Uzturēt kontroli pār datora lietošanu
Exec=/usr/bin/timekpra
Icon=timekpr
Terminal=false
Type=Application
Categories=System;Settings;GTK;
StartupNotify=true
X-AppStream-Ignore=true
X-Ubuntu-Gettext-Domain=timekpr
timekpr-next/resource/launchers/timekpr-admin-su.desktop 000600 001750 001750 00000001444 14575617135 025600 0 ustar 00bezvfedu bezvfedu 000000 000000 [Desktop Entry]
Version=1.0
Name=(SU) Timekpr-nExT Control Panel (superuser mode)
Name[de]=(SU) Timekpr-nExT-Systemsteuerung (Superuser-Modus)
Name[fr]=(SU) Panneau de configuration Timekpr-nExT (mode superutilisateur)
Name[it]=(SU) Pannello di controllo Timekpr-nExT (modalità superutente)
Name[lv]=(SU) Timekpr-nExT vadības panelis (administratora režīms)
Comment=Keep control of computer usage
Comment[de]=Kontrolle der Computernutzung
Comment[fr]=Gardez le contrôle de l'utilisation de l'ordinateur
Comment[it]=Mantieni il controllo sull'utilizzo del computer
Comment[lv]=Uzturēt kontroli pār datora lietošanu
Exec=pkexec /usr/bin/timekpra
Icon=timekpr
Terminal=false
Type=Application
Categories=System;Settings;GTK;
StartupNotify=true
X-AppStream-Ignore=true
X-Ubuntu-Gettext-Domain=timekpr
timekpr-next/resource/launchers/timekpr-client.desktop 000600 001750 001750 00000001104 14575617135 025332 0 ustar 00bezvfedu bezvfedu 000000 000000 [Desktop Entry]
Version=1.0
Name=Timekpr-nExT Client
Name[de]=Timekpr-nExT-Client
Name[fr]=Client Timekpr-nExT
Name[it]=Client Timekpr-nExT
Name[lv]=Timekpr-nExT klients
Comment=Keep control of computer usage
Comment[de]=Kontrolle der Computernutzung
Comment[fr]=Gardez le contrôle de l'utilisation de l'ordinateur
Comment[it]=Mantieni il controllo sull'utilizzo del computer
Comment[lv]=Uzturēt kontroli pār datora lietošanu
Exec=/usr/bin/timekprc
Icon=timekpr-client
Terminal=false
Type=Application
Categories=
X-GNOME-Autostart-enabled=true
X-Ubuntu-Gettext-Domain=timekpr
timekpr-next/resource/locale/ 000775 001750 001750 00000000000 15122253261 020262 5 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/resource/locale/timekpr.pot 000644 001750 001750 00000174021 15122253261 022464 0 ustar 00bezvfedu bezvfedu 000000 000000 #, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Timekpr nExT\n"
"POT-Creation-Date: 2025-10-14 09:17+0300\n"
"PO-Revision-Date: 2019-03-28 09:50+0200\n"
"Last-Translator: Eduards Bezverhijs \n"
"Language-Team: Eduards Bezverhijs \n"
"Language: en_GB\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.6\n"
"X-Poedit-Basepath: ../..\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-KeywordsList: __:1,2\n"
"X-Poedit-SearchPath-0: common/constants/messages.py\n"
"X-Poedit-SearchPath-1: resource/client/forms\n"
#: common/constants/messages.py:31
msgid "==> print help, example"
msgstr ""
#: common/constants/messages.py:32
msgid "==> get saved user list from the server, example"
msgstr ""
#: common/constants/messages.py:33
msgid ""
"==> get user configuration and time information from the server, example"
msgstr ""
#: common/constants/messages.py:34
msgid "==> get realtime user time information from the server, example"
msgstr ""
#: common/constants/messages.py:35
msgid "==> set allowed days for the user, example"
msgstr ""
#. TRANSLATORS: please DO NOT translate the keyword "ALL"
#: common/constants/messages.py:37
msgid ""
"==> set allowed hours for the specified day, or \"ALL\" for every day, "
"optionally specify start and end minutes in brackets like this [x-y], "
"additionally specify ! in front of hour if it doesn't have to be accounted "
"(free time for user), example"
msgstr ""
#: common/constants/messages.py:38
msgid ""
"==> set time limits for all allowed days, the number of values must not "
"exceed the allowed days for the user, example"
msgstr ""
#: common/constants/messages.py:39
msgid "==> set time limit per week, example"
msgstr ""
#: common/constants/messages.py:40
msgid "==> set time limit per month, example"
msgstr ""
#: common/constants/messages.py:41
msgid "==> set whether to track inactive user sessions, example"
msgstr ""
#: common/constants/messages.py:42
msgid "==> set whether to hide tray icon and prevent notifications, example"
msgstr ""
#. TRANSLATORS: please DO NOT translate the keywords: "lock", "suspend", "suspendwake", "terminate", "kill", "shutdown"
#: common/constants/messages.py:44
msgid ""
"==> set restriction / lockout type (\"lock\" - lock session, \"suspend\" - "
"suspend the computer, \"suspendwake\" - suspend and wake up, \"terminate\" - "
"terminate sessions, \"kill\" - kill sessions, \"shutdown\" - shutdown the "
"computer), examples"
msgstr ""
#: common/constants/messages.py:45
msgid ""
"==> set time left for the user at the current moment of time: \"+\" (add "
"time), \"-\" (subtract time), \"=\" (set exact time available), example (add "
"one hour)"
msgstr ""
#: common/constants/messages.py:46
msgid "==> set whether PlayTime is enabled for the user, example"
msgstr ""
#: common/constants/messages.py:47
msgid ""
"==> set whether PlayTime must be accounted instead of normal activity, "
"example"
msgstr ""
#: common/constants/messages.py:48
msgid ""
"==> set whether PlayTime activities are allowed during unaccounted (\"∞\") "
"intervals, example"
msgstr ""
#: common/constants/messages.py:49
msgid "==> set allowed days for PlayTime activities, example"
msgstr ""
#: common/constants/messages.py:50
msgid ""
"==> set PlayTime limits for all allowed days, the number of values must not "
"exceed the allowed PlayTime allowed days for the user, example"
msgstr ""
#: common/constants/messages.py:51
msgid ""
"==> set PlayTime activity process masks, for which the time is accounted, "
"example"
msgstr ""
#: common/constants/messages.py:52
msgid ""
"==> set PlayTime left for the user at the current moment of time: \"+\" (add "
"time), \"-\" (subtract time), \"=\" (set exact time available), example (add "
"one hour)"
msgstr ""
#: common/constants/messages.py:55
msgid "Control sessions types are not passed"
msgstr ""
#: common/constants/messages.py:56
msgid "Control sessions types list is not correct"
msgstr ""
#: common/constants/messages.py:57
msgid "Control sessions types list is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:58
msgid "Excluded session types are not passed"
msgstr ""
#: common/constants/messages.py:59
msgid "Excluded session types list is not correct"
msgstr ""
#: common/constants/messages.py:60
msgid "Excluded session types list is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:61
msgid "Excluded user list is not passed"
msgstr ""
#: common/constants/messages.py:62
msgid "Excluded user list is not correct"
msgstr ""
#: common/constants/messages.py:63
msgid "Excluded user list is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:64
msgid "Final warning time is not passed"
msgstr ""
#: common/constants/messages.py:65
#, python-format
msgid "Final warning time \"%%s\" is not correct"
msgstr ""
#: common/constants/messages.py:66
#, python-format
msgid "Final warning time \"%%s\" is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:67
msgid "Final notification time is not passed"
msgstr ""
#: common/constants/messages.py:68
#, python-format
msgid "Final notification time \"%%s\" is not correct"
msgstr ""
#: common/constants/messages.py:69
#, python-format
msgid "Final notification time \"%%s\" is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:70
msgid "Termination time is not passed"
msgstr ""
#: common/constants/messages.py:71
#, python-format
msgid "Termination time \"%%s\" is not correct"
msgstr ""
#: common/constants/messages.py:72
#, python-format
msgid "Termination time \"%%s\" is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:73
msgid "Track inactive is not passed"
msgstr ""
#: common/constants/messages.py:74
#, python-format
msgid "Track inactive \"%%s\" is not correct"
msgstr ""
#: common/constants/messages.py:75
#, python-format
msgid "Track inactive \"%%s\" is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:76
msgid "Log level is not passed"
msgstr ""
#: common/constants/messages.py:77
#, python-format
msgid "Log level \"%%s\" is not correct"
msgstr ""
#: common/constants/messages.py:78
#, python-format
msgid "Log level \"%%s\" is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:79
msgid "Poll time is not passed"
msgstr ""
#: common/constants/messages.py:80
#, python-format
msgid "Poll time \"%%s\" is not correct"
msgstr ""
#: common/constants/messages.py:81
#, python-format
msgid "Poll time \"%%s\" is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:82
msgid "Save time is not passed"
msgstr ""
#: common/constants/messages.py:83
#, python-format
msgid "Save time \"%%s\" is not correct"
msgstr ""
#: common/constants/messages.py:84
#, python-format
msgid "Save time \"%%s\" is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:85
msgid "PlayTime flag is not passed"
msgstr ""
#: common/constants/messages.py:86
#, python-format
msgid "PlayTime flag \"%%s\" is not correct"
msgstr ""
#: common/constants/messages.py:87
#, python-format
msgid "PlayTime flag \"%%s\" is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:88
msgid "PlayTime enhanced activity monitor flag is not passed"
msgstr ""
#: common/constants/messages.py:89
#, python-format
msgid "PlayTime enhanced activity monitor flag \"%%s\" is not correct"
msgstr ""
#: common/constants/messages.py:90
#, python-format
msgid ""
"PlayTime enhanced activity monitor flag \"%%s\" is not correct and cannot be "
"set"
msgstr ""
#: common/constants/messages.py:93
#, python-format
msgid "User's \"%%s\" day number must be present"
msgstr ""
#: common/constants/messages.py:94
#, python-format
msgid "User's \"%%s\" day number must be between 1 and 7"
msgstr ""
#: common/constants/messages.py:95
#, python-format
msgid "User's \"%%s\" allowed hours are not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:96
#, python-format
msgid "User's \"%%s\" day list is not passed"
msgstr ""
#: common/constants/messages.py:97
#, python-format
msgid "User's \"%%s\" day list is not correct"
msgstr ""
#: common/constants/messages.py:98
#, python-format
msgid "User's \"%%s\" day list is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:99
#, python-format
msgid "User's \"%%s\" day limits list is not passed"
msgstr ""
#: common/constants/messages.py:100
#, python-format
msgid "User's \"%%s\" day limits list is not correct"
msgstr ""
#: common/constants/messages.py:101
#, python-format
msgid "User's \"%%s\" day limits list is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:102
#, python-format
msgid "User's \"%%s\" time operation can be one of these: - + ="
msgstr ""
#: common/constants/messages.py:103
#, python-format
msgid "User's \"%%s\" time limit is not correct"
msgstr ""
#: common/constants/messages.py:104
#, python-format
msgid "User's \"%%s\" time limit is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:105
#, python-format
msgid "User's \"%%s\" monthly allowance is not passed"
msgstr ""
#: common/constants/messages.py:106
#, python-format
msgid "User's \"%%s\" monthly allowance is not correct"
msgstr ""
#: common/constants/messages.py:107
#, python-format
msgid "User's \"%%s\" monthly allowance is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:108
#, python-format
msgid "User's \"%%s\" track inactive flag is not passed"
msgstr ""
#: common/constants/messages.py:109
#, python-format
msgid "User's \"%%s\" track inactive flag is not correct"
msgstr ""
#: common/constants/messages.py:110
#, python-format
msgid "User's \"%%s\" track inactive flag is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:111
#, python-format
msgid "User's \"%%s\" hide tray icon flag is not passed"
msgstr ""
#: common/constants/messages.py:112
#, python-format
msgid "User's \"%%s\" hide tray icon flag is not correct"
msgstr ""
#: common/constants/messages.py:113
#, python-format
msgid "User's \"%%s\" hide tray icon flag is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:114
#, python-format
msgid "User's \"%%s\" restriction / lockout type is not passed"
msgstr ""
#: common/constants/messages.py:115
#, python-format
msgid "User's \"%%s\" restriction / lockout type is not correct"
msgstr ""
#: common/constants/messages.py:116
#, python-format
msgid ""
"User's \"%%s\" restriction / lockout type is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:117
#, python-format
msgid "User's \"%%s\" weekly allowance is not passed"
msgstr ""
#: common/constants/messages.py:118
#, python-format
msgid "User's \"%%s\" weekly allowance is not correct"
msgstr ""
#: common/constants/messages.py:119
#, python-format
msgid "User's \"%%s\" weekly allowance is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:120
#, python-format
msgid "User's \"%%s\" PlayTime enable flag is not passed"
msgstr ""
#: common/constants/messages.py:121
#, python-format
msgid "User's \"%%s\" PlayTime enable flag is not correct"
msgstr ""
#: common/constants/messages.py:122
#, python-format
msgid "User's \"%%s\" PlayTime enable flag is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:123
#, python-format
msgid "User's \"%%s\" PlayTime override flag is not passed"
msgstr ""
#: common/constants/messages.py:124
#, python-format
msgid "User's \"%%s\" PlayTime override flag is not correct"
msgstr ""
#: common/constants/messages.py:125
#, python-format
msgid "User's \"%%s\" PlayTime override flag is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:126
#, python-format
msgid ""
"User's \"%%s\" PlayTime allowed during unaccounted intervals flag is not "
"passed"
msgstr ""
#: common/constants/messages.py:127
#, python-format
msgid ""
"User's \"%%s\" PlayTime allowed during unaccounted intervals flag is not "
"correct"
msgstr ""
#: common/constants/messages.py:128
#, python-format
msgid ""
"User's \"%%s\" PlayTime allowed during unaccounted intervals flag is not "
"correct and cannot be set"
msgstr ""
#: common/constants/messages.py:129
#, python-format
msgid "User's \"%%s\" PlayTime day list is not passed"
msgstr ""
#: common/constants/messages.py:130
#, python-format
msgid "User's \"%%s\" PlayTime day list is not correct"
msgstr ""
#: common/constants/messages.py:131
#, python-format
msgid "User's \"%%s\" PlayTime day list is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:132 common/constants/messages.py:135
#, python-format
msgid "User's \"%%s\" PlayTime day limits list is not passed"
msgstr ""
#: common/constants/messages.py:133 common/constants/messages.py:136
#, python-format
msgid "User's \"%%s\" PlayTime day limits list is not correct"
msgstr ""
#: common/constants/messages.py:134 common/constants/messages.py:137
#, python-format
msgid ""
"User's \"%%s\" PlayTime day limits list is not correct and cannot be set"
msgstr ""
#: common/constants/messages.py:138
#, python-format
msgid "User's \"%%s\" PlayTime operation can be one of these: - + ="
msgstr ""
#: common/constants/messages.py:139
#, python-format
msgid "User's \"%%s\" set PlayTime limit is not correct"
msgstr ""
#: common/constants/messages.py:140
#, python-format
msgid "User's \"%%s\" PlayTime time limit is not correct and cannot be set"
msgstr ""
#. TRANSLATORS: this message must be 80 symbols long at max
#: common/constants/messages.py:144
msgid ""
"Unexpected ERROR while loading configuration. Please inspect Timekpr-nExT "
"log files"
msgstr ""
#. TRANSLATORS: this message must be 80 symbols long at max
#: common/constants/messages.py:146
msgid ""
"Unexpected ERROR getting configuration. Please inspect Timekpr-nExT log files"
msgstr ""
#. TRANSLATORS: this message must be 80 symbols long at max
#: common/constants/messages.py:148
msgid ""
"Unexpected ERROR getting user configuration. Please inspect Timekpr-nExT log "
"files"
msgstr ""
#. TRANSLATORS: this message must be 80 symbols long at max
#: common/constants/messages.py:150
msgid ""
"Unexpected ERROR getting user list. Please inspect Timekpr-nExT log files"
msgstr ""
#. TRANSLATORS: this message must be 80 symbols long at max
#: common/constants/messages.py:152
msgid ""
"Unexpected ERROR updating configuration. Please inspect Timekpr-nExT log "
"files"
msgstr ""
#. TRANSLATORS: this message must be 80 symbols long at max
#: common/constants/messages.py:154
msgid ""
"Unexpected ERROR updating control. Please inspect Timekpr-nExT log files"
msgstr ""
#: common/constants/messages.py:155
#, python-format
msgid "User \"%%s\" configuration is not found"
msgstr ""
#: common/constants/messages.py:156
#, python-format
msgid "User \"%%s\" control file is not found"
msgstr ""
#: common/constants/messages.py:157
#, python-format
msgid "User \"%%s\" is not found"
msgstr ""
#: common/constants/messages.py:160
msgid "Connected"
msgstr ""
#: common/constants/messages.py:161
msgid "Connecting..."
msgstr ""
#: common/constants/messages.py:162
msgid "Failed to connect"
msgstr ""
#. TRANSLATORS: this message must be 80 symbols long at max
#: common/constants/messages.py:164
msgid ""
"Please reopen the application if you are superuser and Timekpr-nExT is "
"running"
msgstr ""
#: common/constants/messages.py:165
msgid "Started"
msgstr ""
#: common/constants/messages.py:166
msgid "User configuration retrieved"
msgstr ""
#: common/constants/messages.py:167
msgid "Configuration retrieved"
msgstr ""
#: common/constants/messages.py:168
msgid "Track inactive for user has been processed"
msgstr ""
#: common/constants/messages.py:169
msgid "Hide tray icon for user has been processed"
msgstr ""
#: common/constants/messages.py:170
msgid "Restriction / lockout type for user has been processed"
msgstr ""
#: common/constants/messages.py:171
msgid "Additional time for user has been processed"
msgstr ""
#: common/constants/messages.py:172
msgid "Additional PlayTime for user has been processed"
msgstr ""
#: common/constants/messages.py:173
msgid "Weekly and monthly limits for user have been processed"
msgstr ""
#: common/constants/messages.py:174
msgid "Allowed days for user have been processed"
msgstr ""
#: common/constants/messages.py:175
msgid "Day time limits for user have been processed"
msgstr ""
#: common/constants/messages.py:176
msgid "Allowed hours for user have been processed"
msgstr ""
#: common/constants/messages.py:177
msgid "Timekpr-nExT configuration has been saved"
msgstr ""
#: common/constants/messages.py:178
msgid "User time limits have been saved"
msgstr ""
#: common/constants/messages.py:179
msgid "User PlayTime limits have been saved"
msgstr ""
#: common/constants/messages.py:180
msgid "User additional options have been saved"
msgstr ""
#: common/constants/messages.py:181
msgid "Enable PlayTime for the user has been processed"
msgstr ""
#: common/constants/messages.py:182
msgid "PlayTime override flag for the user has been processed"
msgstr ""
#: common/constants/messages.py:183
msgid ""
"PlayTime allowed during unaccounted intervals flag for the user has been "
"processed"
msgstr ""
#: common/constants/messages.py:184
msgid "PlayTime allowed days for user have been processed"
msgstr ""
#: common/constants/messages.py:185
msgid "PlayTime day limits for user have been processed"
msgstr ""
#: common/constants/messages.py:186
msgid "PlayTime activities for user have been processed"
msgstr ""
#: common/constants/messages.py:187
msgid "Please select a day to set the limits"
msgstr ""
#: common/constants/messages.py:188
msgid "That interval overlaps with an existing one"
msgstr ""
#: common/constants/messages.py:189
msgid "That interval's start conflicts with an existing one"
msgstr ""
#: common/constants/messages.py:190
msgid "That interval's end conflicts with an existing one"
msgstr ""
#: common/constants/messages.py:191
msgid "That interval's start or end duplicates an existing one"
msgstr ""
#: common/constants/messages.py:192
msgid "Interval start cannot be the same as end"
msgstr ""
#: common/constants/messages.py:193
msgid "Interval's start cannot be later than end"
msgstr ""
#: common/constants/messages.py:194
msgid "Please select an hour interval to remove"
msgstr ""
#: common/constants/messages.py:195
msgid "Interval removed"
msgstr ""
#: common/constants/messages.py:196
msgid "Timekpr-nExT interface is not yet ready"
msgstr ""
#: common/constants/messages.py:199
msgid ""
"WARNING: Timekpr-nExT administration utility was asked to run in GUI mode, "
"but no displays are available, thus running in CLI..."
msgstr ""
#: common/constants/messages.py:200
msgid "The command is incorrect:"
msgstr ""
#: common/constants/messages.py:201
msgid "The usage of Timekpr-nExT admin client is as follows:"
msgstr ""
#: common/constants/messages.py:202
msgid "---=== NOTICE ===---"
msgstr ""
#: common/constants/messages.py:203
msgid "numeric time values are in seconds"
msgstr ""
#: common/constants/messages.py:204
msgid ""
"weekdays are numbered according to ISO 8601 (i.e. Monday is the first day, "
"format: 1-7)"
msgstr ""
#: common/constants/messages.py:205
msgid "hours are numbered according to ISO 8601 (i.e. 24h clock, format: 0-23)"
msgstr ""
#: common/constants/messages.py:207
#, python-format
msgid "%(n)s user in total:"
msgid_plural "%(n)s users in total:"
msgstr[0] ""
msgstr[1] ""
#: common/constants/messages.py:208
#, python-format
msgid "Configuration for user %s:"
msgstr ""
#: common/constants/messages.py:211
msgid "Time left..."
msgstr ""
#: common/constants/messages.py:212
msgid "Limits & Configuration"
msgstr ""
#: common/constants/messages.py:213
msgid "About"
msgstr ""
#: common/constants/messages.py:216 resource/client/forms/about.glade:16
msgid "Keep control of computer usage"
msgstr ""
#: common/constants/messages.py:217
msgid "Day"
msgstr ""
#: common/constants/messages.py:218
msgid "Enabled"
msgstr ""
#: common/constants/messages.py:219 common/constants/messages.py:225
msgid "Limit"
msgstr ""
#: common/constants/messages.py:220
msgid "From"
msgstr ""
#: common/constants/messages.py:221
msgid "from..."
msgstr ""
#: common/constants/messages.py:222
msgid "To"
msgstr ""
#: common/constants/messages.py:223
msgid "to..."
msgstr ""
#: common/constants/messages.py:224
msgid "Period"
msgstr ""
#: common/constants/messages.py:226
msgid "Weekly"
msgstr ""
#: common/constants/messages.py:227
msgid "Monthly"
msgstr ""
#: common/constants/messages.py:228 common/constants/messages.py:230
msgid "Session type"
msgstr ""
#: common/constants/messages.py:229 common/constants/messages.py:231
msgid "session type..."
msgstr ""
#: common/constants/messages.py:232
msgid "Username"
msgstr ""
#: common/constants/messages.py:233
msgid "username..."
msgstr ""
#: common/constants/messages.py:234
msgid "Process mask"
msgstr ""
#: common/constants/messages.py:235
msgid "executable mask..."
msgstr ""
#: common/constants/messages.py:236
msgid "Process description"
msgstr ""
#: common/constants/messages.py:237
msgid "process description..."
msgstr ""
#: common/constants/messages.py:238
msgid "Time"
msgstr ""
#: common/constants/messages.py:239
msgid "time..."
msgstr ""
#: common/constants/messages.py:240
msgid "Importance"
msgstr ""
#: common/constants/messages.py:241
msgid "importance..."
msgstr ""
#: common/constants/messages.py:244
msgid "Timekpr-nExT notification"
msgstr ""
#: common/constants/messages.py:245
msgid "Timekpr-nExT PlayTime notification"
msgstr ""
#: common/constants/messages.py:246
msgid "Your time is not limited today"
msgstr ""
#: common/constants/messages.py:247
msgid "Time allowance has changed, please note new time left!"
msgstr ""
#: common/constants/messages.py:248
msgid "Time limit configuration has changed, please note new configuration!"
msgstr ""
#: common/constants/messages.py:249
#, python-format
msgid "There is a problem connecting to Timekpr-nExT daemon (%%s)!"
msgstr ""
#: common/constants/messages.py:250
#, python-format
msgid "There is a problem communicating to Timekpr-nExT (%%s)!"
msgstr ""
#: common/constants/messages.py:251
#, python-format
msgid "Icon initialization error (%%s)!"
msgstr ""
#. TRANSLATORS: this is a part of message "Your time is up, you will be forcibly logged out in %s seconds", please translate accordingly
#: common/constants/messages.py:253
msgid "Your time is up, you will be forcibly logged out in"
msgstr ""
#. TRANSLATORS: this is a part of message "Your time is up, your computer will be forcibly shutdown in %s seconds", please translate accordingly
#: common/constants/messages.py:255
msgid "Your time is up, your computer will be forcibly shutdown in"
msgstr ""
#. TRANSLATORS: this is a part of message "Your time is up, your session will be forcibly locked in %s seconds", please translate accordingly
#: common/constants/messages.py:257
msgid "Your time is up, your session will be forcibly locked in"
msgstr ""
#. TRANSLATORS: this is a part of message ", Your computer will be forcibly suspended in %s seconds", please translate accordingly
#: common/constants/messages.py:259
msgid "Your time is up, your computer will be forcibly suspended in"
msgstr ""
#. TRANSLATORS: this is a part of message "Your time is up, you will be forcibly logged out in %s seconds", please translate accordingly
#: common/constants/messages.py:261
#, python-format
msgid "%(n)s second"
msgid_plural "%(n)s seconds"
msgstr[0] ""
msgstr[1] ""
#: common/constants/messages.py:262
msgid "Internal connection error, please check log files"
msgstr ""
#. TRANSLATORS: this is a part of message "You have %i hour(s), %i minute(s) and %i second(s) left" please translate accordingly
#: common/constants/messages.py:264
#, python-format
msgid "You have %(n)s hour"
msgid_plural "You have %(n)s hours"
msgstr[0] ""
msgstr[1] ""
#. TRANSLATORS: this is a part of message "You have %i hour(s), %i minute(s) and %i second(s) left" please translate accordingly
#: common/constants/messages.py:266
#, python-format
msgid "%(n)s minute"
msgid_plural "%(n)s minutes"
msgstr[0] ""
msgstr[1] ""
#. TRANSLATORS: this is a part of message "You have %i hour(s), %i minute(s) and %i second(s) left" please translate accordingly
#. TRANSLATORS: this is a part of message "You have %i hour(s), %i minute(s) and %i second(s) of PlayTime left" please translate accordingly
#: common/constants/messages.py:268 common/constants/messages.py:270
#, python-format
msgid "%(n)s second left"
msgid_plural "%(n)s seconds left"
msgstr[0] ""
msgstr[1] ""
#: common/constants/messages.py:270
#, python-format
msgid "%(n)s second of PlayTime left"
msgid_plural "%(n)s seconds of PlayTime left"
msgstr[0] ""
msgstr[1] ""
#: common/constants/messages.py:271
#, python-format
msgid ""
"Feature \"%%s\", which is used to detect idle time, cannot be enabled!\n"
"Idle / inactive time might not be accounted when screen is locked!"
msgstr ""
#: common/constants/messages.py:274
#, python-format
msgid "UNEXPECTED ERROR: %%s"
msgstr ""
#: common/constants/messages.py:275
#, python-format
msgid "PARAMETER PARSE ERROR (please check parameter validity): %%s"
msgstr ""
#: common/constants/messages.py:276
msgid "Command FAILED: access denied"
msgstr ""
#: common/constants/messages.py:277
msgid "Command FAILED: communication was not accepted"
msgstr ""
#: common/constants/messages.py:278
msgid "n/a"
msgstr ""
#: resource/client/forms/about.glade:7
msgid "About Timekpr-nExT"
msgstr ""
#: resource/client/forms/about.glade:18
msgid "Timekpr-nExT"
msgstr ""
#. Please enter GPL3 licence text in your language
#: resource/client/forms/about.glade:19
msgctxt "timekpr-next"
msgid ""
"This program is free software: you can redistribute it and/or modify it "
"under the terms of the GNU General Public License as published by the Free "
"Software Foundation, either version 3 of the License, or (at your option) "
"any later version.\n"
"\n"
"This program is distributed in the hope that it will be useful, but WITHOUT "
"ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or "
"FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for "
"more details.\n"
"\n"
"You should have received a copy of the GNU General Public License along with "
"this program. If not, see . In Debian, see "
"file /usr/share/common-licenses/GPL-3"
msgstr ""
#. Please fill in translator credits, each person in new line in format: name.surname@mail.xy, name@mail.xy, nick@mail.xy or so...
#: resource/client/forms/about.glade:25
msgid ""
"Eduards Bezverhijs \n"
"(Translations fine-tuning by JP Lord )\n"
"(English fine-tuning by Phil Hudson )"
msgstr ""
#: resource/client/forms/admin.glade:155
msgid "Remove excluded session type"
msgstr ""
#: resource/client/forms/admin.glade:385
msgid "Timekpr-nExT administration"
msgstr ""
#: resource/client/forms/admin.glade:403 resource/client/forms/admin.glade:409
#: resource/client/forms/client.glade:261
msgid "Icon, isn't it?"
msgstr ""
#: resource/client/forms/admin.glade:432
msgid "Notes, read carefully ;)"
msgstr ""
#: resource/client/forms/admin.glade:435
msgid ""
"This is the configuration app for Timekpr-nExT. It allows you to set time "
"limits for your individual users as well as general Timekpr-nExT options.\n"
"To use this application, you either have to execute it as superuser or have "
"to be part of the timekpr group.\n"
"Please note that the \"Timekpr-nExT Configuration\" is available in "
"superuser (administrator) mode only!\n"
"\n"
"Please configure carefully: do not lock yourself out!"
msgstr ""
#: resource/client/forms/admin.glade:465 resource/client/forms/admin.glade:3047
msgid "Users related configuration"
msgstr ""
#: resource/client/forms/admin.glade:476 resource/client/forms/client.glade:298
msgid "Username:"
msgstr ""
#: resource/client/forms/admin.glade:504 resource/client/forms/admin.glade:516
msgid "List of usernames registered on the system"
msgstr ""
#: resource/client/forms/admin.glade:530
msgid "Restore"
msgstr ""
#: resource/client/forms/admin.glade:535
msgid "Restore configuration from saved state"
msgstr ""
#: resource/client/forms/admin.glade:586
msgid "Information about time left / spent"
msgstr ""
#: resource/client/forms/admin.glade:610
msgid ""
"Continuous time left. May span more than the current day (realtime, "
"available when user is logged in)"
msgstr ""
#: resource/client/forms/admin.glade:612
msgid "Time left (actual):"
msgstr ""
#: resource/client/forms/admin.glade:626 resource/client/forms/admin.glade:657
#: resource/client/forms/admin.glade:688 resource/client/forms/admin.glade:732
#: resource/client/forms/admin.glade:763 resource/client/forms/admin.glade:794
#: resource/client/forms/client.glade:411
#: resource/client/forms/client.glade:425
#: resource/client/forms/client.glade:456
#: resource/client/forms/client.glade:502
#: resource/client/forms/client.glade:728
#: resource/client/forms/client.glade:786
#: resource/client/forms/client.glade:857
#: resource/client/forms/client.glade:885
#: resource/client/forms/client.glade:985
#: resource/client/forms/client.glade:1011
msgid "Format: "
msgstr ""
#: resource/client/forms/admin.glade:641
msgid ""
"How long the user was inactive since last login (realtime, available when "
"user is logged in)"
msgstr ""
#: resource/client/forms/admin.glade:643
msgid "Time inactive (actual):"
msgstr ""
#: resource/client/forms/admin.glade:672
msgid "Time available today (saved stated, not real-time)"
msgstr ""
#: resource/client/forms/admin.glade:674
msgid "Time left (today):"
msgstr ""
#: resource/client/forms/admin.glade:716
msgid "Time spent today (saved stated, not real-time)"
msgstr ""
#: resource/client/forms/admin.glade:718
msgid "Time spent (today):"
msgstr ""
#: resource/client/forms/admin.glade:747
msgid "Time spent this week (saved stated, not real-time)"
msgstr ""
#: resource/client/forms/admin.glade:749 resource/client/forms/client.glade:717
msgid "Time spent (week):"
msgstr ""
#: resource/client/forms/admin.glade:777
msgid "Time spent this month (saved stated, not real-time)"
msgstr ""
#: resource/client/forms/admin.glade:779 resource/client/forms/client.glade:744
msgid "Time spent (month):"
msgstr ""
#: resource/client/forms/admin.glade:861
msgid "Allowance adjustments"
msgstr ""
#: resource/client/forms/admin.glade:885
msgid "today's time"
msgstr ""
#: resource/client/forms/admin.glade:889
msgid "Choose this to adjust user's time allowance for today"
msgstr ""
#: resource/client/forms/admin.glade:901
msgid "PlayTime"
msgstr ""
#: resource/client/forms/admin.glade:905
msgid "Choose this to adjust user's PlayTime allowance for today"
msgstr ""
#: resource/client/forms/admin.glade:934
msgid "hr"
msgstr ""
#: resource/client/forms/admin.glade:945
msgid "The number of minutes to be adjusted for today's limit"
msgstr ""
#: resource/client/forms/admin.glade:961
msgid "The number of hours to be adjusted for today's limit"
msgstr ""
#: resource/client/forms/admin.glade:977
msgid "min"
msgstr ""
#: resource/client/forms/admin.glade:1004
msgid "Add specified time (reward)"
msgstr ""
#: resource/client/forms/admin.glade:1020
msgid "Subtract specified time (penalty)"
msgstr ""
#: resource/client/forms/admin.glade:1036
msgid "Set this specific time limit"
msgstr ""
#: resource/client/forms/admin.glade:1101
msgid "Information about PlayTime"
msgstr ""
#: resource/client/forms/admin.glade:1134
msgid ""
"Actual PlayTime available today (realtime, available when user is logged in)"
msgstr ""
#: resource/client/forms/admin.glade:1136
msgid "PlayTime left (actual):"
msgstr ""
#: resource/client/forms/admin.glade:1150
#: resource/client/forms/admin.glade:1181
#: resource/client/forms/admin.glade:1269
msgid "Format: "
msgstr ""
#: resource/client/forms/admin.glade:1165
msgid "PlayTime available today (saved stated, not real-time)"
msgstr ""
#: resource/client/forms/admin.glade:1167
msgid "PlayTime left (today):"
msgstr ""
#: resource/client/forms/admin.glade:1196
#: resource/client/forms/admin.glade:1212
msgid ""
"Active PlayTime activity count (realtime, available when user is logged in)"
msgstr ""
#: resource/client/forms/admin.glade:1198
msgid "Running activities (actual):"
msgstr ""
#: resource/client/forms/admin.glade:1253
msgid "PlayTime spent today (saved stated, not real-time)"
msgstr ""
#: resource/client/forms/admin.glade:1255
msgid "PlayTime spent (today):"
msgstr ""
#: resource/client/forms/admin.glade:1306
msgid ""
"Brief information about time spent and everything related to time management "
"for this day"
msgstr ""
#: resource/client/forms/admin.glade:1307
msgid "Info & Today"
msgstr ""
#: resource/client/forms/admin.glade:1348
msgid "This lets you configure time limits."
msgstr ""
#: resource/client/forms/admin.glade:1350
msgid "Week day limits"
msgstr ""
#. This is meant for very short (e.g. one letter) abbreviation of hours
#: resource/client/forms/admin.glade:1370
#: resource/client/forms/admin.glade:1869
#: resource/client/forms/admin.glade:2271
msgid "h"
msgstr ""
#: resource/client/forms/admin.glade:1374
#: resource/client/forms/admin.glade:2275
msgid "Choose hours to be adjusted for selected days."
msgstr ""
#. This is meant for very short (e.g. one letter) abbreviation of minutes
#: resource/client/forms/admin.glade:1386
#: resource/client/forms/admin.glade:1885
#: resource/client/forms/admin.glade:2287
msgid "m"
msgstr ""
#: resource/client/forms/admin.glade:1390
#: resource/client/forms/admin.glade:2291
msgid "Choose minutes to be adjusted for selected days."
msgstr ""
#: resource/client/forms/admin.glade:1410
msgid ""
"Increase daily time allowance by selected time unit (hours or minutes) for "
"selected days.\n"
"\n"
"Please note, that you may select more than one day for the adjustment!"
msgstr ""
#: resource/client/forms/admin.glade:1427
msgid ""
"Decrease daily time allowance by selected time unit (hours or minutes) for "
"selected days.\n"
"\n"
"Please note, that you may select more than one day for the adjustment!"
msgstr ""
#: resource/client/forms/admin.glade:1474
msgid ""
"Lists day of the week and additional information about the day's limit.\n"
"\n"
"Please enable / disable days for the user."
msgstr ""
#: resource/client/forms/admin.glade:1552
msgid ""
"Hour intervals for selected day available to the user.\n"
"Option \"∞\" indicates that time spent during this interval will not be "
"accounted towards the daily limit, it will be accounted as idle instead.\n"
"\n"
"Please note that if the day's limit ends at 24:00 and the next day's limit "
"starts at 00:00, then the user can work continuously past midnight.\n"
"\n"
"Please note that multiple intervals cannot be configured within the same "
"hour.\n"
"An interval can start or end or contain a specific hour, but not more than "
"once. This is by design, not a bug.\n"
"\n"
msgstr ""
#: resource/client/forms/admin.glade:1585
msgid "Hour intervals"
msgstr ""
#: resource/client/forms/admin.glade:1607
msgid ""
"Create a new time interval that will be available to the user.\n"
"\n"
"After creating the interval, please edit its start and end times directly in "
"interval list."
msgstr ""
#: resource/client/forms/admin.glade:1624
msgid "Delete the selected time interval from the available list of intervals."
msgstr ""
#: resource/client/forms/admin.glade:1692
msgid "enter hour intervals"
msgstr ""
#: resource/client/forms/admin.glade:1746
msgid "verify"
msgstr ""
#: resource/client/forms/admin.glade:1750
msgid ""
"Verify configured time intervals. This is a mandatory step to ensure that "
"intervals are correct.\n"
"\n"
"Intervals which have problems will be highlighted."
msgstr ""
#: resource/client/forms/admin.glade:1825
msgid "Weekly and monthly limits"
msgstr ""
#. This is meant for very short (e.g. one letter) abbreviation of days
#: resource/client/forms/admin.glade:1853
msgid "d"
msgstr ""
#: resource/client/forms/admin.glade:1857
msgid "Choose days to be adjusted for selected period."
msgstr ""
#: resource/client/forms/admin.glade:1873
msgid "Choose hours to be adjusted for selected period."
msgstr ""
#: resource/client/forms/admin.glade:1889
msgid "Choose minutes to be adjusted for selected period."
msgstr ""
#: resource/client/forms/admin.glade:1917
msgid ""
"Increase weekly or monthly time allowance by selected time unit (days or "
"hours or minutes) for the selected period."
msgstr ""
#: resource/client/forms/admin.glade:1931
msgid ""
"Decrease weekly or monthly time allowance by selected time unit (days or "
"hours or minutes) for the selected period."
msgstr ""
#: resource/client/forms/admin.glade:1980
msgid ""
"Weekly and monthly limits for the user.\n"
"\n"
"These limits are applied to user together with the rest of limit "
"configuration."
msgstr ""
#: resource/client/forms/admin.glade:2022
msgid "Apply daily limits"
msgstr ""
#: resource/client/forms/admin.glade:2027
msgid "Apply limit all changes made in this page"
msgstr ""
#: resource/client/forms/admin.glade:2048
msgid "Daily limit configuration for all week days"
msgstr ""
#: resource/client/forms/admin.glade:2049
msgid "Limit configuration"
msgstr ""
#: resource/client/forms/admin.glade:2070
msgid "Settings for PlayTime activities"
msgstr ""
#: resource/client/forms/admin.glade:2090
msgid "PlayTime options"
msgstr ""
#: resource/client/forms/admin.glade:2114
#: resource/client/forms/admin.glade:2153
msgid "Enable PlayTime for selected user"
msgstr ""
#: resource/client/forms/admin.glade:2116
msgid "Enable PlayTime:"
msgstr ""
#: resource/client/forms/admin.glade:2130
#: resource/client/forms/admin.glade:2167
msgid ""
"Enable PlayTime override for selected user.\n"
"\n"
"This setting overrides time accounting in a way that time is accounted only "
"when at least one of the applications on the PlayTime activity list are "
"running!\n"
"\n"
"This affects only time accounting, intervals are still fully enforced!\n"
"\n"
"If no processes are running, time is accounted as idle thus effectively it's "
"free time for user!"
msgstr ""
#: resource/client/forms/admin.glade:2138
msgid "Enable PlayTime override:"
msgstr ""
#: resource/client/forms/admin.glade:2186
#: resource/client/forms/admin.glade:2207
msgid ""
"Allow PlayTime activities during unaccounted (\"∞\") time intervals for "
"selected user.\n"
"\n"
"This setting allows the user to use applications configured in his PlayTime "
"activity list during time intervals which are marked as unaccounted "
"(\"∞\").\n"
"\n"
"If this setting is enabled, the use of activities will not be accounted "
"towards PlayTime limits, otherwise applications in PlayTime activity list "
"will be terminated as soon as they are started during unaccounted (\"∞\") "
"time intervals."
msgstr ""
#: resource/client/forms/admin.glade:2192
msgid "Allowed during \"∞\" intervals:"
msgstr ""
#: resource/client/forms/admin.glade:2251
msgid "This lets you configure PlayTime limits."
msgstr ""
#: resource/client/forms/admin.glade:2253
#: resource/client/forms/client.glade:1253
msgid "PlayTime limits"
msgstr ""
#: resource/client/forms/admin.glade:2311
msgid ""
"Increase daily PlayTime allowance by selected time unit (hours or minutes) "
"for selected days.\n"
"\n"
"Please note, that you may select more than one day for the adjustment!"
msgstr ""
#: resource/client/forms/admin.glade:2328
msgid ""
"Decrease daily PlayTime allowance by selected time unit (hours or minutes) "
"for selected days.\n"
"\n"
"Please note, that you may select more than one day for the adjustment!"
msgstr ""
#: resource/client/forms/admin.glade:2380
msgid ""
"Lists day of the week and additional information about the day's PlayTime "
"limit.\n"
"\n"
"Please enable / disable days for the user and set time allowance for "
"PlayTime activities."
msgstr ""
#: resource/client/forms/admin.glade:2431
msgid "PlayTime activities"
msgstr ""
#: resource/client/forms/admin.glade:2453
msgid ""
"Add new PlayTime activity.\n"
"\n"
"Please specify activity mask and user friendly description in the activity "
"list directly."
msgstr ""
#: resource/client/forms/admin.glade:2470
msgid "Remove selected entry from PlayTime activities."
msgstr ""
#: resource/client/forms/admin.glade:2514
msgid ""
"Please specify a list of full process (executable) names without path as "
"case sensitive strings to be monitored in the system.\n"
"\n"
"It's possible to specify RegExp masks for processes too, but please be very "
"careful about them as misusing this setting may lead to killing unwanted "
"processes for the user!\n"
"\n"
"NOTE: RegExp is an expert setting!"
msgstr ""
#: resource/client/forms/admin.glade:2556
msgid "Apply PlayTime configuration"
msgstr ""
#: resource/client/forms/admin.glade:2560
msgid "Apply PlayTime limits and configuration changes on this page"
msgstr ""
#: resource/client/forms/admin.glade:2579
msgid "PlayTime configuration"
msgstr ""
#: resource/client/forms/admin.glade:2604
msgid "Additional configuration options"
msgstr ""
#: resource/client/forms/admin.glade:2639
#: resource/client/forms/admin.glade:2662
msgid ""
"Select whether inactive session time is counted.\n"
"\n"
"If this is unchecked, time spent in console (not terminal emulator) login "
"sessions and while the screen is locked is NOT taken into account. This "
"varies among desktop environments."
msgstr ""
#: resource/client/forms/admin.glade:2643
msgid "Track inactive sessions:"
msgstr ""
#: resource/client/forms/admin.glade:2679
#: resource/client/forms/admin.glade:2702
msgid ""
"Select whether to show Timekpr-next's padlock icon and notifications to the "
"user.\n"
"\n"
"Please note that checking this will disable showing all information and "
"notifications to the user!"
msgstr ""
#: resource/client/forms/admin.glade:2683
msgid "Hide icon and notifications:"
msgstr ""
#: resource/client/forms/admin.glade:2754
msgid ""
"Select a restriction / lockout type for the user.\n"
"\n"
"NOTE: please be very careful, think ahead and read every options description "
"when changing this setting from default value!"
msgstr ""
#: resource/client/forms/admin.glade:2758
msgid "Restriction / lockout type:"
msgstr ""
#: resource/client/forms/admin.glade:2780
msgid "terminate sessions"
msgstr ""
#: resource/client/forms/admin.glade:2784
msgid ""
"When time ends, user sessions will be terminated.\n"
"\n"
"This option is a restriction!\n"
"\n"
"This is the default option and most likely is the one you need!"
msgstr ""
#: resource/client/forms/admin.glade:2802
msgid "kill sessions"
msgstr ""
#: resource/client/forms/admin.glade:2806
msgid ""
"When time ends, user sessions will be killed.\n"
"\n"
"This option is a restriction!\n"
"\n"
"Please evaluate whether you need this type of restriction!"
msgstr ""
#: resource/client/forms/admin.glade:2823
msgid "shutdown computer"
msgstr ""
#: resource/client/forms/admin.glade:2827
msgid ""
"When time ends, computer will be shut down.\n"
"\n"
"This option is a restriction!\n"
"\n"
"Please evaluate whether you need this type of restriction!"
msgstr ""
#: resource/client/forms/admin.glade:2845
msgid "suspend computer"
msgstr ""
#: resource/client/forms/admin.glade:2849
msgid ""
"When time ends, computer will be suspended (put to sleep).\n"
"\n"
"This option is not a restriction and is more suited for self control "
"purposes.\n"
"\n"
"When woken up when there is still no time left, computer screen will be "
"locked and put to sleep again after some time."
msgstr ""
#: resource/client/forms/admin.glade:2867
msgid "suspend / wakeup computer"
msgstr ""
#: resource/client/forms/admin.glade:2871
msgid ""
"When time ends, computer will be suspended (put to sleep) and will be woken "
"up at start of next available time interval for the user.\n"
"\n"
"This option is not a restriction and is more suited for self control "
"purposes.\n"
"\n"
"When woken up when there is still no time left, computer screen will be "
"locked and put to sleep again after some time."
msgstr ""
#: resource/client/forms/admin.glade:2889
msgid "lock screen"
msgstr ""
#: resource/client/forms/admin.glade:2893
msgid ""
"When time ends, computer screen will be locked.\n"
"\n"
"This option is not a restriction and is more suited for self control "
"purposes."
msgstr ""
#: resource/client/forms/admin.glade:2924
msgid ""
"Select a time interval when computer can be woken up automatically.\n"
"\n"
"Please note that this option is only effective if wake up by RealTime Clock "
"is supported and enabled in BIOS / UEFI!"
msgstr ""
#: resource/client/forms/admin.glade:2928
msgid "Wakeup hour interval:"
msgstr ""
#: resource/client/forms/admin.glade:2940
msgid ""
"Enter start hour for the automatic wakeup function.\n"
"\n"
"Please note that this option is only effective if wake up by RealTime Clock "
"is supported and enabled in BIOS / UEFI!"
msgstr ""
#: resource/client/forms/admin.glade:2959
msgid ""
"Enter end hour for the automatic wakeup function.\n"
"\n"
"Please note that this option is only effective if wake up by RealTime Clock "
"is supported and enabled in BIOS / UEFI!"
msgstr ""
#: resource/client/forms/admin.glade:2997
msgid "Apply configuration"
msgstr ""
#: resource/client/forms/admin.glade:3001
msgid "Apply additional configuration changes on this page"
msgstr ""
#: resource/client/forms/admin.glade:3020
msgid "Additional configuration"
msgstr ""
#: resource/client/forms/admin.glade:3021
#: resource/client/forms/admin.glade:3695
msgid "Additional options"
msgstr ""
#: resource/client/forms/admin.glade:3048
msgid "User Configuration"
msgstr ""
#: resource/client/forms/admin.glade:3058
msgid "Timekpr-nExT related configuration"
msgstr ""
#: resource/client/forms/admin.glade:3085
msgid "Control Timekpr-nExT Settings"
msgstr ""
#: resource/client/forms/admin.glade:3112
#: resource/client/forms/admin.glade:3264
msgid ""
"Timekpr-nExT log level.\n"
"\n"
"Please do not change this unless you have to. You likely won't find anything "
"pretty in the log files.\n"
"\n"
"Values are: 1 - standard, 2 - debug, 3 - extra debug"
msgstr ""
#: resource/client/forms/admin.glade:3132
#: resource/client/forms/admin.glade:3180
msgid ""
"Session polling time granularity which specifies how often user sessions and "
"activity are checked and accounted.\n"
"\n"
"3 - 5 seconds are optimal, don't change this if unsure."
msgstr ""
#: resource/client/forms/admin.glade:3136
msgid "Poll interval"
msgstr ""
#: resource/client/forms/admin.glade:3148
#: resource/client/forms/admin.glade:3251
msgid ""
"Specify the time in seconds when Timekpr-nExT starts continuous real-time "
"countdown before enforcing a restriction / lockout to user's sessions."
msgstr ""
#: resource/client/forms/admin.glade:3164
#: resource/client/forms/admin.glade:3214
msgid ""
"This specifies the rate in seconds at which actual user state is saved to "
"disk.\n"
"\n"
"To improve performance and still have great accuracy, Timekpr-nExT accounts "
"time in memory at \"Poll interval\" frequency, this setting defines a "
"frequency at which user state is saved to disk for permanent storage."
msgstr ""
#: resource/client/forms/admin.glade:3168
msgid "Save time"
msgstr ""
#: resource/client/forms/admin.glade:3198
#: resource/client/forms/admin.glade:3233
msgid ""
"Number of seconds left for user before enforcing a configured restriction / "
"lockout to his sessions.\n"
"\n"
"After this is reached and user is still active, the user's sessions will be "
"handled according to specified restriction / lockout, and almost nothing can "
"be done to prevent it."
msgstr ""
#: resource/client/forms/admin.glade:3202
msgid "Termination time"
msgstr ""
#: resource/client/forms/admin.glade:3253
msgid "Countdown time"
msgstr ""
#: resource/client/forms/admin.glade:3270
msgid "Log level"
msgstr ""
#: resource/client/forms/admin.glade:3281
#: resource/client/forms/admin.glade:3297
msgid ""
"Number of seconds left for user's available time before sending a final "
"critical notification that time is about to run out.\n"
"\n"
"NOTE: the rest of the notification times are configurable per user by user "
"in client application!"
msgstr ""
#: resource/client/forms/admin.glade:3285
msgid "Final notification"
msgstr ""
#: resource/client/forms/admin.glade:3339
msgid "Control Timekpr-nExT tracking items"
msgstr ""
#: resource/client/forms/admin.glade:3372
msgid "Tracked Sessions"
msgstr ""
#: resource/client/forms/admin.glade:3395
msgid "Add tracked session type to the list"
msgstr ""
#: resource/client/forms/admin.glade:3410
msgid "Remove tracked session type from the list"
msgstr ""
#: resource/client/forms/admin.glade:3439
msgid ""
"List of session types to be tracked.\n"
"\n"
"Please, do not change or experiment with this unless you actually (not "
"wishfully) know what you are doing."
msgstr ""
#: resource/client/forms/admin.glade:3474
msgid "Excluded Sessions"
msgstr ""
#: resource/client/forms/admin.glade:3497
msgid "Add an excluded session type to the list"
msgstr ""
#: resource/client/forms/admin.glade:3512
msgid "Remove an excluded session type from the list"
msgstr ""
#: resource/client/forms/admin.glade:3541
msgid ""
"List of sessions to be excluded from tracking.\n"
"\n"
"Please, do not change or experiment with this unless you actually (not "
"wishfully) know what you are doing."
msgstr ""
#: resource/client/forms/admin.glade:3576
msgid "Excluded Users"
msgstr ""
#: resource/client/forms/admin.glade:3599
msgid "Add a user to the exclusion list"
msgstr ""
#: resource/client/forms/admin.glade:3614
msgid "Remove a user from the exclusion list"
msgstr ""
#: resource/client/forms/admin.glade:3643
msgid ""
"List of users excluded from tracking.\n"
"\n"
"Please specify actual usernames, not real names here. For example, "
"\"jsmith\", not \"John Smith\"."
msgstr ""
#: resource/client/forms/admin.glade:3722
#: resource/client/forms/admin.glade:3741
#: resource/client/forms/admin.glade:3777
msgid ""
"This setting controls whether PlayTime functionality is enabled.\n"
"\n"
"This is a PlayTime master switch, if it's turned off, it's turned off for "
"everyone regardless of individual settings!"
msgstr ""
#: resource/client/forms/admin.glade:3726
msgid "PlayTime enabled:"
msgstr ""
#: resource/client/forms/admin.glade:3756
msgid ""
"This setting controls whether PlayTime activity monitor will use process "
"command line, including arguments, for monitoring processes (by default only "
"uses the process name).\n"
"\n"
"When this setting is enabled Timekpr-nExT will perform a match against full "
"command line up to 512 characters enabling enhanced process monitoring "
"capabilities.\n"
"\n"
"Please be careful and double check your RegExp patterns in each individual "
"user's PlayTime activity masks!"
msgstr ""
#: resource/client/forms/admin.glade:3762
msgid "Enhanced activity monitor:"
msgstr ""
#: resource/client/forms/admin.glade:3811
msgid "Apply Timekpr-nExT settings"
msgstr ""
#: resource/client/forms/admin.glade:3816
msgid "Apply all Timekpr-nExT settings at once"
msgstr ""
#: resource/client/forms/admin.glade:3836
msgid "Timekpr-nExT Administration Configuration"
msgstr ""
#: resource/client/forms/admin.glade:3837
msgid "Timekpr-nExT Configuration"
msgstr ""
#: resource/client/forms/admin.glade:3866
#: resource/client/forms/admin.glade:3883
msgid "Status of Timekpr-nExT admin client"
msgstr ""
#. This is one of the notification priorities
#: resource/client/forms/client.glade:27
msgid "Information"
msgstr ""
#. This is one of the notification priorities
#: resource/client/forms/client.glade:31
msgid "Warning"
msgstr ""
#. This is one of the notification priorities
#: resource/client/forms/client.glade:35
msgid "Severe"
msgstr ""
#. This is one of the notification priorities
#: resource/client/forms/client.glade:39
msgid "Critical"
msgstr ""
#: resource/client/forms/client.glade:133
msgid "Timekpr-nExT client"
msgstr ""
#: resource/client/forms/client.glade:164
msgid "Status of Timekpr-nExT client"
msgstr ""
#: resource/client/forms/client.glade:196
msgid "Save all changes"
msgstr ""
#: resource/client/forms/client.glade:213
msgid "Close the window"
msgstr ""
#: resource/client/forms/client.glade:297
#: resource/client/forms/client.glade:314
msgid "Current effective username"
msgstr ""
#: resource/client/forms/client.glade:371
msgid "Current statistics"
msgstr ""
#: resource/client/forms/client.glade:396
msgid "Time spent this session or after Timekpr-nExT has been restarted"
msgstr ""
#: resource/client/forms/client.glade:398
msgid "Time spent (session):"
msgstr ""
#: resource/client/forms/client.glade:441
msgid "Time inactive this session or after Timekpr-nExT has been restarted"
msgstr ""
#: resource/client/forms/client.glade:443
msgid "Time inactive:"
msgstr ""
#: resource/client/forms/client.glade:469
msgid "Continuous time left to you. May span more than the current day."
msgstr ""
#: resource/client/forms/client.glade:471
msgid "Continuous time left:"
msgstr ""
#: resource/client/forms/client.glade:487
msgid "Total time available left today in a row, up to the end of the day"
msgstr ""
#: resource/client/forms/client.glade:489
#: resource/client/forms/client.glade:974
msgid "Time left today:"
msgstr ""
#: resource/client/forms/client.glade:548
msgid "These are the days and limits that are available to you"
msgstr ""
#: resource/client/forms/client.glade:573
#: resource/client/forms/client.glade:1171
msgid "Days & Limits"
msgstr ""
#: resource/client/forms/client.glade:609
msgid ""
"This shows the time intervals that are available for use.\n"
"\n"
"Option \"∞\" indicates that time spent during this interval will not be "
"accounted towards the daily limit, it will be accounted as idle instead."
msgstr ""
#: resource/client/forms/client.glade:634
msgid "Intervals"
msgstr ""
#: resource/client/forms/client.glade:663
msgid "Daily limits"
msgstr ""
#: resource/client/forms/client.glade:691
msgid "Additional statistics"
msgstr ""
#: resource/client/forms/client.glade:715
msgid "Time spent this week"
msgstr ""
#: resource/client/forms/client.glade:742
msgid "Time spent this month"
msgstr ""
#: resource/client/forms/client.glade:755
msgid ""
"Select whether inactive session time is counted.\n"
"\n"
"If this is unchecked, time spent in console (not terminal emulator) login "
"sessions and while the screen is locked is NOT taken into account.\n"
"This varies among desktop environments."
msgstr ""
#: resource/client/forms/client.glade:760
msgid "Track inactive:"
msgstr ""
#: resource/client/forms/client.glade:773
msgid ""
"Select whether inactive session time is counted. If this is unchecked, time "
"spent in console (not terminal emulator) login sessions and while the screen "
"is locked is NOT taken into account. This varies among desktop environments."
msgstr ""
#: resource/client/forms/client.glade:820
#: resource/client/forms/client.glade:918
msgid "Additional limits"
msgstr ""
#: resource/client/forms/client.glade:844
msgid "Time limit for this week available to you"
msgstr ""
#: resource/client/forms/client.glade:846
msgid "Time limit (week):"
msgstr ""
#: resource/client/forms/client.glade:872
msgid "Time limit for this month available to you"
msgstr ""
#: resource/client/forms/client.glade:874
msgid "Time limit (month):"
msgstr ""
#: resource/client/forms/client.glade:948
msgid "Current PlayTime statistics"
msgstr ""
#: resource/client/forms/client.glade:972
msgid "Total PlayTime available left today"
msgstr ""
#: resource/client/forms/client.glade:998
msgid "Total PlayTime spent today"
msgstr ""
#: resource/client/forms/client.glade:1000
msgid "Time spent today:"
msgstr ""
#: resource/client/forms/client.glade:1027
#: resource/client/forms/client.glade:1042
msgid ""
"This option overrides the default time accounting.\n"
"\n"
"If it's checked, the time is accounted only when activities in \"Activity / "
"application list\" are running, the rest of the time is considered idle."
msgstr ""
#: resource/client/forms/client.glade:1046
msgid "Time limit override:"
msgstr ""
#: resource/client/forms/client.glade:1060
#: resource/client/forms/client.glade:1073
msgid "Number of currently active PlayTime activities"
msgstr ""
#: resource/client/forms/client.glade:1062
msgid "Running activities:"
msgstr ""
#: resource/client/forms/client.glade:1086
#: resource/client/forms/client.glade:1106
msgid ""
"Allow activities during unaccounted (\"∞\") time intervals.\n"
"\n"
"This setting allows to use applications listed in \"Activity / application "
"list\" during time intervals which are marked as unaccounted (\"∞\"), if the "
"setting is not enabled, none of activities are allowed to run!"
msgstr ""
#: resource/client/forms/client.glade:1090
msgid "Allowed during \"∞\":"
msgstr ""
#: resource/client/forms/client.glade:1148
msgid ""
"These are days and limits that available to you as part of PlayTime activity "
"restrictions"
msgstr ""
#: resource/client/forms/client.glade:1205
msgid ""
"This shows activities / applications which are part of PlayTime restrictions"
msgstr ""
#: resource/client/forms/client.glade:1228
msgid "Activity / application list"
msgstr ""
#: resource/client/forms/client.glade:1292
msgid ""
"Configuration for personalized notifications about available time.\n"
"\n"
"Please configure notifications as you see fit. However, please keep in mind "
"that there will be two types of notifications that cannot be personalized: a "
"final warning some time before time ends and a countdown notifications when "
"time is about to end.\n"
"\n"
"The configuration \"Time\" value indicates a value of time left when "
"notification will be shown, the \"Importance\" option governs an icon colour "
"and notification properties as follows.\n"
"\n"
"Information - a green icon and an informational notification will be shown\n"
"Warning - a yellow icon and an informational notification will be shown\n"
"Severe - a red icon and important notification will be shown\n"
"Critical - a red icon and critical notification will be shown, this "
"notification usually is shown over all open applications and stays open "
"until dismissed, however this behaviour highly depends on Desktop "
"Environment in use"
msgstr ""
#: resource/client/forms/client.glade:1325
msgid "Notification configuration"
msgstr ""
#: resource/client/forms/client.glade:1347
msgid "Add new notification threshold to the list"
msgstr ""
#: resource/client/forms/client.glade:1362
msgid "Remove notification threshold from the list"
msgstr ""
#: resource/client/forms/client.glade:1411
msgid ""
"Configuration for personalized notifications about available PlayTime.\n"
"\n"
"Please configure notifications as you see fit.\n"
"\n"
"The configuration \"Time\" value indicates a value of PlayTime left when "
"notification will be shown, the \"Importance\" option governs an icon colour "
"and notification properties as follows.\n"
"\n"
"Information - a green icon and an informational notification will be shown\n"
"Warning - a yellow icon and an informational notification will be shown\n"
"Severe - a red icon and important notification will be shown\n"
"Critical - a red icon and critical notification will be shown, this "
"notification usually is shown over all open applications and stays open "
"until dismissed, however this behaviour highly depends on Desktop "
"Environment in use"
msgstr ""
#: resource/client/forms/client.glade:1444
msgid "PlayTime notification configuration"
msgstr ""
#: resource/client/forms/client.glade:1466
msgid "Add new PlayTime notification threshold to the list"
msgstr ""
#: resource/client/forms/client.glade:1481
msgid "Remove PlayTime notification threshold from the list"
msgstr ""
#: resource/client/forms/client.glade:1540
msgid "Notifications"
msgstr ""
#: resource/client/forms/client.glade:1564
msgid ""
"Select whether to use speech notifications, if available.\n"
"\n"
"You may be able to make speech notifications available by installing package "
"\"python3-espeak\" or \"python3-espeak-ng\"."
msgstr ""
#: resource/client/forms/client.glade:1574
msgid "Use speech notifications"
msgstr ""
#: resource/client/forms/client.glade:1593
#: resource/client/forms/client.glade:1615
msgid ""
"This sets the logging level.\n"
"\n"
"Please do not change this unless you know what you're doing."
msgstr ""
#: resource/client/forms/client.glade:1619
msgid "Logging level"
msgstr ""
#: resource/client/forms/client.glade:1641
msgid ""
"Specify whether to show a notification when limit configurations or "
"allowance changes"
msgstr ""
#: resource/client/forms/client.glade:1649
msgid "Show limit changes"
msgstr ""
#: resource/client/forms/client.glade:1665
msgid ""
"Specify whether to show all notifications.\n"
"\n"
"If unchecked, then only important ones are shown."
msgstr ""
#: resource/client/forms/client.glade:1675
msgid "Show all notifications"
msgstr ""
#: resource/client/forms/client.glade:1691
msgid ""
"Specify whether to show seconds in notification area.\n"
"\n"
"Some desktop environments, like KDE5, do not support text besides "
"notification icons."
msgstr ""
#: resource/client/forms/client.glade:1701
msgid "Show seconds in notification area"
msgstr ""
#: resource/client/forms/client.glade:1720
#: resource/client/forms/client.glade:1744
msgid ""
"This sets how long a notification is shown for regular notifications about "
"time left and configuration changes.\n"
"\n"
"The value is specified in seconds.\n"
"A value of 0 means show until dismissed (not recommended for regular "
"notifications).\n"
"\n"
"Please note that the desktop environment you use may override this timeout, "
"in which case this setting will not have any effect on notification."
msgstr ""
#: resource/client/forms/client.glade:1751
msgid "Notification timeout (sec)"
msgstr ""
#: resource/client/forms/client.glade:1776
#: resource/client/forms/client.glade:1801
msgid ""
"This sets how long a notification is shown for \"Critical\" notifications "
"about time left (when the icon turns red).\n"
"\n"
"The value is specified in seconds.\n"
"A value of 0 means show until dismissed.\n"
"\n"
"Please note that the desktop environment you use may override this timeout, "
"in which case this setting will not have any effect on notification."
msgstr ""
#: resource/client/forms/client.glade:1808
msgid "Critical notification timeout (sec)"
msgstr ""
#: resource/client/forms/client.glade:1830
msgid ""
"Select whether to use sound \"bell\" (short notification sound) to announce "
"a new notification from Timekpr-nExT.\n"
"\n"
"This works only for enabled notifications.\n"
"\n"
"If this setting is not editable, your environment does not advertise sound "
"notification support."
msgstr ""
#: resource/client/forms/client.glade:1842
msgid "Use sound \"bell\" for notifications"
msgstr ""
#: resource/client/forms/client.glade:1862
msgid "Configuration"
msgstr ""
timekpr-next/resource/locale/es/ 000700 001750 001750 00000000000 14575617250 020672 5 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/resource/locale/es/LC_MESSAGES/ 000700 001750 001750 00000000000 15122253261 022442 5 ustar 00bezvfedu bezvfedu 000000 000000 timekpr-next/resource/locale/es/LC_MESSAGES/timekpr.po 000644 001750 001750 00000307040 15122253261 024473 0 ustar 00bezvfedu bezvfedu 000000 000000 # Spanish translation for timekpr-next
# Copyright (c) 2021 Rosetta Contributors and Canonical Ltd 2021
# This file is distributed under the same license as the timekpr-next package.
# FIRST AUTHOR , 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: timekpr-next\n"
"Report-Msgid-Bugs-To: FULL NAME \n"
"POT-Creation-Date: 2025-10-14 09:17+0300\n"
"PO-Revision-Date: 2025-10-14 19:18+0300\n"
"Last-Translator: Eduards Bezverhijs \n"
"Language-Team: Spanish \n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Launchpad-Export-Date: 2025-10-14 06:56+0000\n"
"X-Generator: Poedit 3.6\n"
#: common/constants/messages.py:31
msgid "==> print help, example"
msgstr "ejemplo, escribe ayuda"
#: common/constants/messages.py:32
msgid "==> get saved user list from the server, example"
msgstr "ejemplo, elige un usuario guardado de la lista desde el servidor"
#: common/constants/messages.py:33
msgid "==> get user configuration and time information from the server, example"
msgstr ""
"por ejemplo, consigue la configuración de usuario y la información horaria "
"desde el servidor"
#: common/constants/messages.py:34
msgid "==> get realtime user time information from the server, example"
msgstr "por ejemplo, obtenga información del usuario en tiempo real del servidor"
#: common/constants/messages.py:35
msgid "==> set allowed days for the user, example"
msgstr "ejemplo, fija los días permitidos para el usuario"
#. TRANSLATORS: please DO NOT translate the keyword "ALL"
#: common/constants/messages.py:37
msgid ""
"==> set allowed hours for the specified day, or \"ALL\" for every day, "
"optionally specify start and end minutes in brackets like this [x-y], "
"additionally specify ! in front of hour if it doesn't have to be accounted "
"(free time for user), example"
msgstr ""
"ejemplo, señala las horas permitidas para el día especificado, o \"ALL\" para "
"cada día. Opcionalmente especifica los minutos de comienzo y finalización en "
"corchetes así [x-y], además especifica ! delante de la hora si no tiene que ser "
"tenida en cuenta (horario libre para el usuario)"
#: common/constants/messages.py:38
msgid ""
"==> set time limits for all allowed days, the number of values must not exceed "
"the allowed days for the user, example"
msgstr ""
"ejemplo, ajusta los límites para todos los días permitidos, el número de "
"valores no debe exceder los días permitidos para el usuario"
#: common/constants/messages.py:39
msgid "==> set time limit per week, example"
msgstr "ejemplo, ajusta el límite de tiempo a la semana"
#: common/constants/messages.py:40
msgid "==> set time limit per month, example"
msgstr "ejemplo, ajusta el límite al mes"
#: common/constants/messages.py:41
msgid "==> set whether to track inactive user sessions, example"
msgstr "ejemplo, fija si seguir las sesiones sin actividad del usuario"
#: common/constants/messages.py:42
msgid "==> set whether to hide tray icon and prevent notifications, example"
msgstr "ejemplo, ajusta si ocultar el icono bandeja y evitar notificaciones"
#. TRANSLATORS: please DO NOT translate the keywords: "lock", "suspend", "suspendwake", "terminate", "kill", "shutdown"
#: common/constants/messages.py:44
msgid ""
"==> set restriction / lockout type (\"lock\" - lock session, \"suspend\" - "
"suspend the computer, \"suspendwake\" - suspend and wake up, \"terminate\" - "
"terminate sessions, \"kill\" - kill sessions, \"shutdown\" - shutdown the "
"computer), examples"
msgstr ""
"==> establecer restricción / tipo de bloqueo (\"lock\" - bloquear sesión, "
"\"suspend\" - suspender el ordenador, \"suspendwake\" - suspender y despertar, "
"\"terminate\" - terminar sesiones, \"kill\" - finalizar sesiones, \"shutdown\" "
"- apagar el ordenador), ejemplos"
#: common/constants/messages.py:45
msgid ""
"==> set time left for the user at the current moment of time: \"+\" (add time), "
"\"-\" (subtract time), \"=\" (set exact time available), example (add one hour)"
msgstr ""
"==> establecer el tiempo restante para el usuario en el momento actual de "
"tiempo: \"+\" (añadir tiempo), \"-\" (restar tiempo), \"=\" (establecer el "
"tiempo exacto disponible), ejemplo (añadir una hora)"
#: common/constants/messages.py:46
msgid "==> set whether PlayTime is enabled for the user, example"
msgstr "==> establecer si PlayTime está activado para el usuario, ejemplo"
#: common/constants/messages.py:47
msgid ""
"==> set whether PlayTime must be accounted instead of normal activity, example"
msgstr ""
"==> establecer si PlayTime debe contabilizarse en lugar de la actividad normal, "
"ejemplo"
#: common/constants/messages.py:48
msgid ""
"==> set whether PlayTime activities are allowed during unaccounted (\"∞\") "
"intervals, example"
msgstr ""
"==> establecer si se permiten las actividades de PlayTime durante los "
"intervalos no contabilizados (\"∞\"), ejemplo"
#: common/constants/messages.py:49
msgid "==> set allowed days for PlayTime activities, example"
msgstr ""
"==> establecer los días permitidos para las actividades de PlayTime, ejemplo"
#: common/constants/messages.py:50
msgid ""
"==> set PlayTime limits for all allowed days, the number of values must not "
"exceed the allowed PlayTime allowed days for the user, example"
msgstr ""
"==> establecer límites de PlayTime para todos los días permitidos, el número de "
"valores no debe exceder los días permitidos de PlayTime para el usuario, ejemplo"
#: common/constants/messages.py:51
msgid ""
"==> set PlayTime activity process masks, for which the time is accounted, "
"example"
msgstr ""
"==> establecer máscaras de proceso de actividad PlayTime, para el que se "
"contabiliza el tiempo, ejemplo"
#: common/constants/messages.py:52
msgid ""
"==> set PlayTime left for the user at the current moment of time: \"+\" (add "
"time), \"-\" (subtract time), \"=\" (set exact time available), example (add "
"one hour)"
msgstr ""
"==> establecer PlayTime restante para el usuario en el momento actual de "
"tiempo: \"+\" (añadir tiempo), \"-\" (restar tiempo), \"=\" (establecer el "
"tiempo exacto disponible), ejemplo (añadir una hora)"
#: common/constants/messages.py:55
msgid "Control sessions types are not passed"
msgstr "Los tipos de sesiones de control no pasan"
#: common/constants/messages.py:56
msgid "Control sessions types list is not correct"
msgstr "La lista de tipos de sesiones de control es incorrecta"
#: common/constants/messages.py:57
msgid "Control sessions types list is not correct and cannot be set"
msgstr ""
"La lista de tipos de sesiones de control no es correcta y no se puede configurar"
#: common/constants/messages.py:58
msgid "Excluded session types are not passed"
msgstr "Los tipos de sesión excluida no puede ser validada"
#: common/constants/messages.py:59
msgid "Excluded session types list is not correct"
msgstr "La lista de sesiones excluidas no es correcta"
#: common/constants/messages.py:60
msgid "Excluded session types list is not correct and cannot be set"
msgstr ""
"La lista de tipos de sesión excluidos no es correcta y no puede establecerse"
#: common/constants/messages.py:61
msgid "Excluded user list is not passed"
msgstr "La lista de usuarios excluidos no paso"
#: common/constants/messages.py:62
msgid "Excluded user list is not correct"
msgstr "La lista de usuarios excluidos no es correcta"
#: common/constants/messages.py:63
msgid "Excluded user list is not correct and cannot be set"
msgstr "La lista de usuarios excluidos no es correcta y no se puede establecer"
#: common/constants/messages.py:64
msgid "Final warning time is not passed"
msgstr "No se ha superado el tiempo de preaviso final"
#: common/constants/messages.py:65
#, python-format
msgid "Final warning time \"%%s\" is not correct"
msgstr "El tiempo final de aviso \"%%s\" no es correcto"
#: common/constants/messages.py:66
#, python-format
msgid "Final warning time \"%%s\" is not correct and cannot be set"
msgstr ""
"El tiempo de advertencia final \"%%s\" no es correcto y no puede establecerse"
#: common/constants/messages.py:67
msgid "Final notification time is not passed"
msgstr "No se ha superado el plazo de notificación final"
#: common/constants/messages.py:68
#, python-format
msgid "Final notification time \"%%s\" is not correct"
msgstr "La hora de notificación final \"%%s\" no es correcta"
#: common/constants/messages.py:69
#, python-format
msgid "Final notification time \"%%s\" is not correct and cannot be set"
msgstr ""
"La hora de notificación final \"%%s\" no es correcta y no puede establecerse"
#: common/constants/messages.py:70
msgid "Termination time is not passed"
msgstr "El tiempo de finalizacion no paso"
#: common/constants/messages.py:71
#, python-format
msgid "Termination time \"%%s\" is not correct"
msgstr "La hora de finalización \"%%s\" no es correcta"
#: common/constants/messages.py:72
#, python-format
msgid "Termination time \"%%s\" is not correct and cannot be set"
msgstr "La hora de finalización \"%%s\" no es correcta y no puede establecerse"
#: common/constants/messages.py:73
msgid "Track inactive is not passed"
msgstr "Pista inactiva no se pasa"
#: common/constants/messages.py:74
#, python-format
msgid "Track inactive \"%%s\" is not correct"
msgstr "Pista inactiva no se pasa"
#: common/constants/messages.py:75
#, python-format
msgid "Track inactive \"%%s\" is not correct and cannot be set"
msgstr "La pista inactiva \"%%s\" no es correcta y no se puede establecer"
#: common/constants/messages.py:76
msgid "Log level is not passed"
msgstr "No se pasa el nivel de registro"
#: common/constants/messages.py:77
#, python-format
msgid "Log level \"%%s\" is not correct"
msgstr "El nivel de registro \"%%s\" no es correcto"
#: common/constants/messages.py:78
#, python-format
msgid "Log level \"%%s\" is not correct and cannot be set"
msgstr "El nivel de registro \"%%s\" no es correcto y no se puede establecer"
#: common/constants/messages.py:79
msgid "Poll time is not passed"
msgstr "No ha transcurrido el tiempo de sondeo"
#: common/constants/messages.py:80
#, python-format
msgid "Poll time \"%%s\" is not correct"
msgstr "El tiempo de sondeo \"%%s\" no es correcto"
#: common/constants/messages.py:81
#, python-format
msgid "Poll time \"%%s\" is not correct and cannot be set"
msgstr "La hora de sondeo \"%%s\" no es correcta y no se puede establecer"
#: common/constants/messages.py:82
msgid "Save time is not passed"
msgstr "No se ha superado la hora de guardado"
#: common/constants/messages.py:83
#, python-format
msgid "Save time \"%%s\" is not correct"
msgstr "Guardar tiempo \"%%s\" no es correcto"
#: common/constants/messages.py:84
#, python-format
msgid "Save time \"%%s\" is not correct and cannot be set"
msgstr "El tiempo de guardado \"%%s\" no es correcto y no se puede establecer"
#: common/constants/messages.py:85
msgid "PlayTime flag is not passed"
msgstr "No se pasa la bandera PlayTime"
#: common/constants/messages.py:86
#, python-format
msgid "PlayTime flag \"%%s\" is not correct"
msgstr "El indicador PlayTime \"%%s\" no es correcto"
#: common/constants/messages.py:87
#, python-format
msgid "PlayTime flag \"%%s\" is not correct and cannot be set"
msgstr "El indicador PlayTime \"%%s\" no es correcto y no se puede establecer"
#: common/constants/messages.py:88
msgid "PlayTime enhanced activity monitor flag is not passed"
msgstr "No se pasa la bandera del monitor de actividad mejorada de PlayTime"
#: common/constants/messages.py:89
#, python-format
msgid "PlayTime enhanced activity monitor flag \"%%s\" is not correct"
msgstr "Bandera mejorada del monitor de actividad PlayTime \"%%s\" no es correcto"
#: common/constants/messages.py:90
#, python-format
msgid ""
"PlayTime enhanced activity monitor flag \"%%s\" is not correct and cannot be set"
msgstr ""
"El indicador \"%%s\" del monitor de actividad mejorado de PlayTime no es "
"correcto y no puede establecerse."
#: common/constants/messages.py:93
#, python-format
msgid "User's \"%%s\" day number must be present"
msgstr "El número de día \"%%s\" del usuario debe estar presente"
#: common/constants/messages.py:94
#, python-format
msgid "User's \"%%s\" day number must be between 1 and 7"
msgstr "El número de día \"%%s\" del usuario debe estar comprendido entre 1 y 7"
#: common/constants/messages.py:95
#, python-format
msgid "User's \"%%s\" allowed hours are not correct and cannot be set"
msgstr ""
"Las horas permitidas del usuario \"%%s\" no son correctas y no se pueden "
"establecer"
#: common/constants/messages.py:96
#, python-format
msgid "User's \"%%s\" day list is not passed"
msgstr "No se pasa la lista de días \"%%s\" del usuario"
#: common/constants/messages.py:97
#, python-format
msgid "User's \"%%s\" day list is not correct"
msgstr "La lista de días \"%%s\" del usuario no es correcta"
#: common/constants/messages.py:98
#, python-format
msgid "User's \"%%s\" day list is not correct and cannot be set"
msgstr ""
"La lista de días \"%%s\" del usuario no es correcta y no se puede establecer"
#: common/constants/messages.py:99
#, python-format
msgid "User's \"%%s\" day limits list is not passed"
msgstr "No se ha pasado la lista de límites diarios \"%%s\" del usuario"
#: common/constants/messages.py:100
#, python-format
msgid "User's \"%%s\" day limits list is not correct"
msgstr "La lista de límites diarios \"%%s\" del usuario no es correcta"
#: common/constants/messages.py:101
#, python-format
msgid "User's \"%%s\" day limits list is not correct and cannot be set"
msgstr ""
"La lista de límites diarios \"%%s\" del usuario no es correcta y no puede "
"establecerse"
#: common/constants/messages.py:102
#, python-format
msgid "User's \"%%s\" time operation can be one of these: - + ="
msgstr "La operación de tiempo \"%%s\" del usuario puede ser una de estas: - + ="
#: common/constants/messages.py:103
#, python-format
msgid "User's \"%%s\" time limit is not correct"
msgstr "El límite de tiempo \"%%s\" del usuario no es correcto"
#: common/constants/messages.py:104
#, python-format
msgid "User's \"%%s\" time limit is not correct and cannot be set"
msgstr ""
"El límite de tiempo \"%%s\" del usuario no es correcto y no puede establecerse"
#: common/constants/messages.py:105
#, python-format
msgid "User's \"%%s\" monthly allowance is not passed"
msgstr "No se ha superado la asignación mensual del usuario \"%%s"
#: common/constants/messages.py:106
#, python-format
msgid "User's \"%%s\" monthly allowance is not correct"
msgstr "La asignación mensual \"%%s\" del usuario no es correcta"
#: common/constants/messages.py:107
#, python-format
msgid "User's \"%%s\" monthly allowance is not correct and cannot be set"
msgstr ""
"La asignación mensual \"%%s\" del usuario no es correcta y no se puede "
"establecer"
#: common/constants/messages.py:108
#, python-format
msgid "User's \"%%s\" track inactive flag is not passed"
msgstr "No se ha pasado el indicador de vía inactiva \"%%s\" del usuario"
#: common/constants/messages.py:109
#, python-format
msgid "User's \"%%s\" track inactive flag is not correct"
msgstr "El indicador de vía inactiva \"%%s\" del usuario no es correcto"
#: common/constants/messages.py:110
#, python-format
msgid "User's \"%%s\" track inactive flag is not correct and cannot be set"
msgstr ""
"El indicador de vía inactiva \"%%s\" del usuario no es correcto y no se puede "
"establecer"
#: common/constants/messages.py:111
#, python-format
msgid "User's \"%%s\" hide tray icon flag is not passed"
msgstr ""
"No se ha pasado el indicador \"%%s\" de ocultar icono de bandeja del usuario"
#: common/constants/messages.py:112
#, python-format
msgid "User's \"%%s\" hide tray icon flag is not correct"
msgstr ""
"El indicador de icono de bandeja oculta \"%%s\" del usuario no es correcto"
#: common/constants/messages.py:113
#, python-format
msgid "User's \"%%s\" hide tray icon flag is not correct and cannot be set"
msgstr ""
"El indicador de icono de bandeja oculta \"%%s\" del usuario no es correcto y no "
"se puede establecer"
#: common/constants/messages.py:114
#, python-format
msgid "User's \"%%s\" restriction / lockout type is not passed"
msgstr "La restricción/tipo de bloqueo \"%%s\" del usuario no se ha superado"
#: common/constants/messages.py:115
#, python-format
msgid "User's \"%%s\" restriction / lockout type is not correct"
msgstr "El tipo de restricción/bloqueo \"%%s\" del usuario no es correcto"
#: common/constants/messages.py:116
#, python-format
msgid "User's \"%%s\" restriction / lockout type is not correct and cannot be set"
msgstr ""
"El tipo de restricción/bloqueo \"%%s\" del usuario no es correcto y no puede "
"establecerse"
#: common/constants/messages.py:117
#, python-format
msgid "User's \"%%s\" weekly allowance is not passed"
msgstr "La asignación semanal \"%%s\" del usuario no se ha superado"
#: common/constants/messages.py:118
#, python-format
msgid "User's \"%%s\" weekly allowance is not correct"
msgstr "La asignación semanal \"%%s\" del usuario no es correcta"
#: common/constants/messages.py:119
#, python-format
msgid "User's \"%%s\" weekly allowance is not correct and cannot be set"
msgstr ""
"La asignación semanal \"%%s\" del usuario no es correcta y no se puede "
"establecer"
#: common/constants/messages.py:120
#, python-format
msgid "User's \"%%s\" PlayTime enable flag is not passed"
msgstr ""
"No se ha pasado el indicador de activación de PlayTime \"%%s\" del usuario."
#: common/constants/messages.py:121
#, python-format
msgid "User's \"%%s\" PlayTime enable flag is not correct"
msgstr "El indicador de activación de PlayTime \"%%s\" del usuario no es correcto"
#: common/constants/messages.py:122
#, python-format
msgid "User's \"%%s\" PlayTime enable flag is not correct and cannot be set"
msgstr ""
"El indicador de activación de PlayTime \"%%s\" del usuario no es correcto y no "
"puede activarse."
#: common/constants/messages.py:123
#, python-format
msgid "User's \"%%s\" PlayTime override flag is not passed"
msgstr ""
"No se ha pasado el indicador \"%%s\" de anulación de PlayTime del usuario."
#: common/constants/messages.py:124
#, python-format
msgid "User's \"%%s\" PlayTime override flag is not correct"
msgstr "El indicador \"%%s\" de anulación de PlayTime del usuario no es correcto."
#: common/constants/messages.py:125
#, python-format
msgid "User's \"%%s\" PlayTime override flag is not correct and cannot be set"
msgstr ""
"El indicador de anulación de PlayTime \"%%s\" del usuario no es correcto y no "
"puede establecerse."
#: common/constants/messages.py:126
#, python-format
msgid ""
"User's \"%%s\" PlayTime allowed during unaccounted intervals flag is not passed"
msgstr ""
"El indicador \"%%s\" PlayTime allowed during unaccounted intervals no se ha "
"pasado"
#: common/constants/messages.py:127
#, python-format
msgid ""
"User's \"%%s\" PlayTime allowed during unaccounted intervals flag is not correct"
msgstr ""
"El indicador \"%%s\" PlayTime permitido durante intervalos no contabilizados no "
"es correcto"
#: common/constants/messages.py:128
#, python-format
msgid ""
"User's \"%%s\" PlayTime allowed during unaccounted intervals flag is not "
"correct and cannot be set"
msgstr ""
"El indicador \"%%s\" PlayTime allowed during unaccounted intervals del usuario "
"no es correcto y no se puede establecer"
#: common/constants/messages.py:129
#, python-format
msgid "User's \"%%s\" PlayTime day list is not passed"
msgstr "La lista de días de PlayTime \"%%s\" del usuario no se pasa"
#: common/constants/messages.py:130
#, python-format
msgid "User's \"%%s\" PlayTime day list is not correct"
msgstr "La lista de días de PlayTime \"%%s\" del usuario no es correcta"
#: common/constants/messages.py:131
#, python-format
msgid "User's \"%%s\" PlayTime day list is not correct and cannot be set"
msgstr ""
"La lista de días de PlayTime \"%%s\" del usuario no es correcta y no se puede "
"establecer"
#: common/constants/messages.py:132 common/constants/messages.py:135
#, python-format
msgid "User's \"%%s\" PlayTime day limits list is not passed"
msgstr ""
"La lista de días de PlayTime \"%%s\" del usuario no es correcta y no se puede "
"establecer"
#: common/constants/messages.py:133 common/constants/messages.py:136
#, python-format
msgid "User's \"%%s\" PlayTime day limits list is not correct"
msgstr ""
"La lista de límites de días de PlayTime \"%%s\" del usuario no es correcta"
#: common/constants/messages.py:134 common/constants/messages.py:137
#, python-format
msgid "User's \"%%s\" PlayTime day limits list is not correct and cannot be set"
msgstr ""
"La lista de límites de días de PlayTime \"%%s\" del usuario no es correcta y no "
"se puede establecer"
#: common/constants/messages.py:138
#, python-format
msgid "User's \"%%s\" PlayTime operation can be one of these: - + ="
msgstr "La operación \"%%s\" PlayTime del usuario puede ser una de estas: - + ="
#: common/constants/messages.py:139
#, python-format
msgid "User's \"%%s\" set PlayTime limit is not correct"
msgstr ""
"El límite de tiempo de reproducción establecido por el usuario \"%%s\" no es "
"correcto."
#: common/constants/messages.py:140
#, python-format
msgid "User's \"%%s\" PlayTime time limit is not correct and cannot be set"
msgstr ""
"El límite de tiempo de PlayTime \"%%s\" del usuario no es correcto y no puede "
"establecerse"
#. TRANSLATORS: this message must be 80 symbols long at max
#: common/constants/messages.py:144
msgid ""
"Unexpected ERROR while loading configuration. Please inspect Timekpr-nExT log "
"files"
msgstr ""
"ERROR inesperado al cargar la configuración. Por favor, inspeccione los "
"archivos de registro Timekpr-nExT"
#. TRANSLATORS: this message must be 80 symbols long at max
#: common/constants/messages.py:146
msgid ""
"Unexpected ERROR getting configuration. Please inspect Timekpr-nExT log files"
msgstr ""
"ERROR inesperado al obtener la configuración. Por favor, inspeccione los "
"archivos de registro Timekpr-nExT"
#. TRANSLATORS: this message must be 80 symbols long at max
#: common/constants/messages.py:148
msgid ""
"Unexpected ERROR getting user configuration. Please inspect Timekpr-nExT log "
"files"
msgstr ""
"ERROR inesperado al obtener la configuración de usuario. Por favor, inspeccione "
"los archivos de registro Timekpr-nExT"
#. TRANSLATORS: this message must be 80 symbols long at max
#: common/constants/messages.py:150
msgid "Unexpected ERROR getting user list. Please inspect Timekpr-nExT log files"
msgstr ""
"ERROR inesperado al obtener la lista de usuarios. Por favor, inspeccione los "
"archivos de registro Timekpr-nExT"
#. TRANSLATORS: this message must be 80 symbols long at max
#: common/constants/messages.py:152
msgid ""
"Unexpected ERROR updating configuration. Please inspect Timekpr-nExT log files"
msgstr ""
"ERROR inesperado al actualizar la configuración. Por favor, inspeccione los "
"archivos de registro Timekpr-nExT"
#. TRANSLATORS: this message must be 80 symbols long at max
#: common/constants/messages.py:154
msgid "Unexpected ERROR updating control. Please inspect Timekpr-nExT log files"
msgstr ""
"ERROR inesperado al actualizar el control. Por favor, inspeccione los archivos "
"de registro Timekpr-nExT"
#: common/constants/messages.py:155
#, python-format
msgid "User \"%%s\" configuration is not found"
msgstr "No se encuentra la configuración del usuario \"%%s"
#: common/constants/messages.py:156
#, python-format
msgid "User \"%%s\" control file is not found"
msgstr "No se encuentra el archivo de control de usuario \"%%s"
#: common/constants/messages.py:157
#, python-format
msgid "User \"%%s\" is not found"
msgstr "El usuario \"%%s\" no se encuentra"
#: common/constants/messages.py:160
msgid "Connected"
msgstr "Conectado"
#: common/constants/messages.py:161
msgid "Connecting..."
msgstr "Conectándose..."
#: common/constants/messages.py:162
msgid "Failed to connect"
msgstr "No se ha podido conectar"
#. TRANSLATORS: this message must be 80 symbols long at max
#: common/constants/messages.py:164
msgid ""
"Please reopen the application if you are superuser and Timekpr-nExT is running"
msgstr ""
"Vuelva a abrir la aplicación si es superusuario y Timekpr-nExT se está "
"ejecutando"
#: common/constants/messages.py:165
msgid "Started"
msgstr "Iniciada"
#: common/constants/messages.py:166
msgid "User configuration retrieved"
msgstr "Configuración de usuario recuperada"
#: common/constants/messages.py:167
msgid "Configuration retrieved"
msgstr "Configuración recuperada"
#: common/constants/messages.py:168
msgid "Track inactive for user has been processed"
msgstr "Se ha procesado la pista inactiva para el usuario"
#: common/constants/messages.py:169
msgid "Hide tray icon for user has been processed"
msgstr "Ocultar icono de bandeja para el usuario ha sido procesado"
#: common/constants/messages.py:170
msgid "Restriction / lockout type for user has been processed"
msgstr "Se ha procesado el tipo de restricción / bloqueo para el usuario"
#: common/constants/messages.py:171
msgid "Additional time for user has been processed"
msgstr "Se ha procesado el tiempo adicional para el usuario"
#: common/constants/messages.py:172
msgid "Additional PlayTime for user has been processed"
msgstr "Se ha procesado el PlayTime adicional para el usuario"
#: common/constants/messages.py:173
msgid "Weekly and monthly limits for user have been processed"
msgstr "Se han procesado los límites semanales y mensuales de los usuarios"
#: common/constants/messages.py:174
msgid "Allowed days for user have been processed"
msgstr "Se han procesado los días permitidos para el usuario"
#: common/constants/messages.py:175
msgid "Day time limits for user have been processed"
msgstr "Se han procesado los límites de tiempo por día para el usuario"
#: common/constants/messages.py:176
msgid "Allowed hours for user have been processed"
msgstr "Se han procesado las horas permitidas para el usuario"
#: common/constants/messages.py:177
msgid "Timekpr-nExT configuration has been saved"
msgstr "Se ha guardado la configuración de Timekpr-nExT"
#: common/constants/messages.py:178
msgid "User time limits have been saved"
msgstr "Se han guardado los límites de tiempo de los usuarios"
#: common/constants/messages.py:179
msgid "User PlayTime limits have been saved"
msgstr "Se han guardado los límites de PlayTime del usuario"
#: common/constants/messages.py:180
msgid "User additional options have been saved"
msgstr "Se han guardado las opciones adicionales del usuario"
#: common/constants/messages.py:181
msgid "Enable PlayTime for the user has been processed"
msgstr "Habilitar PlayTime para el usuario ha sido procesado"
#: common/constants/messages.py:182
msgid "PlayTime override flag for the user has been processed"
msgstr "Se ha procesado el indicador de anulación de PlayTime para el usuario"
#: common/constants/messages.py:183
msgid ""
"PlayTime allowed during unaccounted intervals flag for the user has been "
"processed"
msgstr ""
"Se ha procesado el indicador PlayTime permitido durante intervalos no "
"contabilizados para el usuario"
#: common/constants/messages.py:184
msgid "PlayTime allowed days for user have been processed"
msgstr "Se han procesado los días permitidos de PlayTime para el usuario"
#: common/constants/messages.py:185
msgid "PlayTime day limits for user have been processed"
msgstr "Se han procesado los límites de días de PlayTime para el usuario"
#: common/constants/messages.py:186
msgid "PlayTime activities for user have been processed"
msgstr "Se han procesado las actividades PlayTime del usuario"
#: common/constants/messages.py:187
msgid "Please select a day to set the limits"
msgstr "Seleccione un día para fijar los límites"
#: common/constants/messages.py:188
msgid "That interval overlaps with an existing one"
msgstr "Ese intervalo se solapa con otro ya existente"
#: common/constants/messages.py:189
msgid "That interval's start conflicts with an existing one"
msgstr "El inicio de ese intervalo entra en conflicto con otro ya existente."
#: common/constants/messages.py:190
msgid "That interval's end conflicts with an existing one"
msgstr "El final de ese intervalo entra en conflicto con otro ya existente."
#: common/constants/messages.py:191
msgid "That interval's start or end duplicates an existing one"
msgstr "El inicio o final de ese intervalo duplica uno existente"
#: common/constants/messages.py:192
msgid "Interval start cannot be the same as end"
msgstr "El inicio del intervalo no puede coincidir con el final"
#: common/constants/messages.py:193
msgid "Interval's start cannot be later than end"
msgstr "El inicio del intervalo no puede ser posterior al final"
#: common/constants/messages.py:194
msgid "Please select an hour interval to remove"
msgstr "Seleccione un intervalo de horas para eliminar"
#: common/constants/messages.py:195
msgid "Interval removed"
msgstr "Intervalo eliminado"
#: common/constants/messages.py:196
msgid "Timekpr-nExT interface is not yet ready"
msgstr "La interfaz Timekpr-nExT aún no está lista"
#: common/constants/messages.py:199
msgid ""
"WARNING: Timekpr-nExT administration utility was asked to run in GUI mode, but "
"no displays are available, thus running in CLI..."
msgstr ""
"ADVERTENCIA: Se ha solicitado que la utilidad de administración Timekpr-nExT se "
"ejecute en modo GUI, pero no hay pantallas disponibles, por lo que se ejecuta "
"en CLI..."
#: common/constants/messages.py:200
msgid "The command is incorrect:"
msgstr "El comando no es correcto."
#: common/constants/messages.py:201
msgid "The usage of Timekpr-nExT admin client is as follows:"
msgstr "El uso de Timekpr-nExT admin client es el siguiente:"
#: common/constants/messages.py:202
msgid "---=== NOTICE ===---"
msgstr "---=== AVISO ===---"
#: common/constants/messages.py:203
msgid "numeric time values are in seconds"
msgstr "los valores numéricos del tiempo se expresan en segundos"
#: common/constants/messages.py:204
msgid ""
"weekdays are numbered according to ISO 8601 (i.e. Monday is the first day, "
"format: 1-7)"
msgstr ""
"los días de la semana se numeran según la norma ISO 8601 (es decir, el lunes es "
"el primer día, formato: 1-7)"
#: common/constants/messages.py:205
msgid "hours are numbered according to ISO 8601 (i.e. 24h clock, format: 0-23)"
msgstr ""
"los días de la semana se numeran según la norma ISO 8601 (es decir, el lunes es "
"el primer día, formato: 1-7)"
#: common/constants/messages.py:207
#, python-format
msgid "%(n)s user in total:"
msgid_plural "%(n)s users in total:"
msgstr[0] "%(n)s usuario en total:"
msgstr[1] "%(n)s usuarios en total:"
#: common/constants/messages.py:208
#, python-format
msgid "Configuration for user %s:"
msgstr "Configuración para el usuario %s:"
#: common/constants/messages.py:211
msgid "Time left..."
msgstr "Tiempo restante..."
#: common/constants/messages.py:212
msgid "Limits & Configuration"
msgstr "Límites y configuración"
#: common/constants/messages.py:213
msgid "About"
msgstr "Acerca de"
#: common/constants/messages.py:216 resource/client/forms/about.glade:16
msgid "Keep control of computer usage"
msgstr "Mantener control del uso del equipo"
#: common/constants/messages.py:217
msgid "Day"
msgstr "Día"
#: common/constants/messages.py:218
msgid "Enabled"
msgstr "Activado"
#: common/constants/messages.py:219 common/constants/messages.py:225
msgid "Limit"
msgstr "Límite"
#: common/constants/messages.py:220
msgid "From"
msgstr "De"
#: common/constants/messages.py:221
msgid "from..."
msgstr "de..."
#: common/constants/messages.py:222
msgid "To"
msgstr "A"
#: common/constants/messages.py:223
msgid "to..."
msgstr "a..."
#: common/constants/messages.py:224
msgid "Period"
msgstr "Periodo"
#: common/constants/messages.py:226
msgid "Weekly"
msgstr "Semanal"
#: common/constants/messages.py:227
msgid "Monthly"
msgstr "Mensual"
#: common/constants/messages.py:228 common/constants/messages.py:230
msgid "Session type"
msgstr "Tipo de sesión"
#: common/constants/messages.py:229 common/constants/messages.py:231
msgid "session type..."
msgstr "tipo de sesión..."
#: common/constants/messages.py:232
msgid "Username"
msgstr "Usuario"
#: common/constants/messages.py:233
msgid "username..."
msgstr "nombre de usuario..."
#: common/constants/messages.py:234
msgid "Process mask"
msgstr "Máscara de proceso"
#: common/constants/messages.py:235
msgid "executable mask..."
msgstr "máscara ejecutable..."
#: common/constants/messages.py:236
msgid "Process description"
msgstr "Descripcción de proceso"
#: common/constants/messages.py:237
msgid "process description..."
msgstr "descripción del proceso..."
#: common/constants/messages.py:238
msgid "Time"
msgstr "Tiempo"
#: common/constants/messages.py:239
msgid "time..."
msgstr "tiempo..."
#: common/constants/messages.py:240
msgid "Importance"
msgstr "Importancia"
#: common/constants/messages.py:241
msgid "importance..."
msgstr "importancia..."
#: common/constants/messages.py:244
msgid "Timekpr-nExT notification"
msgstr "Notificación Timekpr-nExT"
#: common/constants/messages.py:245
msgid "Timekpr-nExT PlayTime notification"
msgstr "Notificación Timekpr-nExT PlayTime"
#: common/constants/messages.py:246
msgid "Your time is not limited today"
msgstr "Su tiempo no está limitado hoy"
#: common/constants/messages.py:247
msgid "Time allowance has changed, please note new time left!"
msgstr ""
"El tiempo disponible ha cambiado, ¡tenga en cuenta el nuevo tiempo restante!"
#: common/constants/messages.py:248
msgid "Time limit configuration has changed, please note new configuration!"
msgstr ""
"La configuración del límite de tiempo ha cambiado, ¡tenga en cuenta la nueva "
"configuración!"
#: common/constants/messages.py:249
#, python-format
msgid "There is a problem connecting to Timekpr-nExT daemon (%%s)!"
msgstr "¡Hay un problema de conexión con el demonio Timekpr-nExT (%%s)!"
#: common/constants/messages.py:250
#, python-format
msgid "There is a problem communicating to Timekpr-nExT (%%s)!"
msgstr "¡Hay un problema de comunicación con Timekpr-nExT (%%s)!"
#: common/constants/messages.py:251
#, python-format
msgid "Icon initialization error (%%s)!"
msgstr "¡Error de inicialización del icono (%%s)!"
#. TRANSLATORS: this is a part of message "Your time is up, you will be forcibly logged out in %s seconds", please translate accordingly
#: common/constants/messages.py:253
msgid "Your time is up, you will be forcibly logged out in"
msgstr "Su tiempo se ha acabado, será desconectado a la fuerza en"
#. TRANSLATORS: this is a part of message "Your time is up, your computer will be forcibly shutdown in %s seconds", please translate accordingly
#: common/constants/messages.py:255
msgid "Your time is up, your computer will be forcibly shutdown in"
msgstr "Su tiempo se ha acabado, su ordenador se apagará forzosamente en"
#. TRANSLATORS: this is a part of message "Your time is up, your session will be forcibly locked in %s seconds", please translate accordingly
#: common/constants/messages.py:257
msgid "Your time is up, your session will be forcibly locked in"
msgstr "Su tiempo se ha acabado, su sesión será bloqueada a la fuerza"
#. TRANSLATORS: this is a part of message ", Your computer will be forcibly suspended in %s seconds", please translate accordingly
#: common/constants/messages.py:259
msgid "Your time is up, your computer will be forcibly suspended in"
msgstr "Su tiempo se ha acabado, su sesión será bloqueada a la fuerza en"
#. TRANSLATORS: this is a part of message "Your time is up, you will be forcibly logged out in %s seconds", please translate accordingly
#: common/constants/messages.py:261
#, python-format
msgid "%(n)s second"
msgid_plural "%(n)s seconds"
msgstr[0] "%(n)s segundo"
msgstr[1] "%(n)s segundos"
#: common/constants/messages.py:262
msgid "Internal connection error, please check log files"
msgstr "Error interno de conexión, compruebe los archivos de registro"
#. TRANSLATORS: this is a part of message "You have %i hour(s), %i minute(s) and %i second(s) left" please translate accordingly
#: common/constants/messages.py:264
#, python-format
msgid "You have %(n)s hour"
msgid_plural "You have %(n)s hours"
msgstr[0] "Tienes %(n)s hora"
msgstr[1] "Tienes %(n)s horas"
#. TRANSLATORS: this is a part of message "You have %i hour(s), %i minute(s) and %i second(s) left" please translate accordingly
#: common/constants/messages.py:266
#, python-format
msgid "%(n)s minute"
msgid_plural "%(n)s minutes"
msgstr[0] "%(n)s minuto"
msgstr[1] "%(n)s minutos"
#. TRANSLATORS: this is a part of message "You have %i hour(s), %i minute(s) and %i second(s) left" please translate accordingly
#. TRANSLATORS: this is a part of message "You have %i hour(s), %i minute(s) and %i second(s) of PlayTime left" please translate accordingly
#: common/constants/messages.py:268 common/constants/messages.py:270
#, python-format
msgid "%(n)s second left"
msgid_plural "%(n)s seconds left"
msgstr[0] "%(n)s segundo restante"
msgstr[1] "%(n)s segundos restantes"
#: common/constants/messages.py:270
#, python-format
msgid "%(n)s second of PlayTime left"
msgid_plural "%(n)s seconds of PlayTime left"
msgstr[0] "%(n)s segundo de PlayTime restante"
msgstr[1] "%(n)s segundos de tiempo de juego restantes"
#: common/constants/messages.py:271
#, python-format
msgid ""
"Feature \"%%s\", which is used to detect idle time, cannot be enabled!\n"
"Idle / inactive time might not be accounted when screen is locked!"
msgstr ""
"La función \"%%s\", que se utiliza para detectar el tiempo de inactividad, no "
"puede activarse.\n"
"El tiempo de inactividad puede no contabilizarse cuando la pantalla está "
"bloqueada."
#: common/constants/messages.py:274
#, python-format
msgid "UNEXPECTED ERROR: %%s"
msgstr "ERROR INESPERADO: %%s"
#: common/constants/messages.py:275
#, python-format
msgid "PARAMETER PARSE ERROR (please check parameter validity): %%s"
msgstr ""
"Error de analisis de parametros (por favor, compruebe la validez del "
"parámetro): %%s"
#: common/constants/messages.py:276
msgid "Command FAILED: access denied"
msgstr "Comando FALLIDO: acceso denegado"
#: common/constants/messages.py:277
msgid "Command FAILED: communication was not accepted"
msgstr "Comando FALLIDO: no se ha aceptado la comunicación"
#: common/constants/messages.py:278
msgid "n/a"
msgstr "No aplica"
#: resource/client/forms/about.glade:7
msgid "About Timekpr-nExT"
msgstr "Acerca de Timekpr-nExT"
#: resource/client/forms/about.glade:18
msgid "Timekpr-nExT"
msgstr "Timekpr-nExT"
#. Please enter GPL3 licence text in your language
#: resource/client/forms/about.glade:19
msgctxt "timekpr-next"
msgid ""
"This program is free software: you can redistribute it and/or modify it under "
"the terms of the GNU General Public License as published by the Free Software "
"Foundation, either version 3 of the License, or (at your option) any later "
"version.\n"
"\n"
"This program is distributed in the hope that it will be useful, but WITHOUT ANY "
"WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A "
"PARTICULAR PURPOSE. See the GNU General Public License for more details.\n"
"\n"
"You should have received a copy of the GNU General Public License along with "
"this program. If not, see . In Debian, see file /"
"usr/share/common-licenses/GPL-3"
msgstr ""
"Este programa es software libre: puede redistribuirlo y/o modificarlo bajo los "
"términos de la Licencia General Pública GNU (GPL) publicados por la Free "
"Software Foundation en su versión 3 o en cualquier versión posterior.\n"
"\n"
"Este programa se distribuye con la esperanza de que sea útil, pero SIN NINGUNA "
"GARANTÍA; sin tan siquiera la garantía implícita de MERCADIBILIDAD o CALIDAD "
"PARA UN OBJETIVO CONCRETO. Mire la Licencia Pública General GNU para más "
"detalles.\n"
"\n"
"Debería haber recibido una copia de Licencia Pública General GNU con este "
"programa. Si no es así, entre en . En Debian "
"puede ver el archivo en /usr/share/common-licenses/GPL-3"
#. Please fill in translator credits, each person in new line in format: name.surname@mail.xy, name@mail.xy, nick@mail.xy or so...
#: resource/client/forms/about.glade:25
msgid ""
"Eduards Bezverhijs \n"
"(Translations fine-tuning by JP Lord )\n"
"(English fine-tuning by Phil Hudson )"
msgstr ""
"Eduards Bezverhijs \n"
"(Traducción revisada por Ivan Ledesma )\n"
"(Ajuste en Español por Ivan Ledesma )"
#: resource/client/forms/admin.glade:155
msgid "Remove excluded session type"
msgstr "Eliminar el tipo de sesión excluida"
#: resource/client/forms/admin.glade:385
msgid "Timekpr-nExT administration"
msgstr "Administración Timekpr-nExT"
#: resource/client/forms/admin.glade:403 resource/client/forms/admin.glade:409
#: resource/client/forms/client.glade:261
msgid "Icon, isn't it?"
msgstr "Icono, ¿verdad?"
#: resource/client/forms/admin.glade:432
msgid "Notes, read carefully ;)"
msgstr "Notas, lea atentamente ;)"
#: resource/client/forms/admin.glade:435
msgid ""
"This is the configuration app for Timekpr-nExT. It allows you to set time "
"limits for your individual users as well as general Timekpr-nExT options.\n"
"To use this application, you either have to execute it as superuser or have to "
"be part of the timekpr group.\n"
"Please note that the \"Timekpr-nExT Configuration\" is available in superuser "
"(administrator) mode only!\n"
"\n"
"Please configure carefully: do not lock yourself out!"
msgstr ""
"Esta es la aplicación de configuración de Timekpr-nExT. Le permite establecer "
"límites de tiempo para sus usuarios individuales, así como opciones generales "
"de Timekpr-nExT.\n"
"Para utilizar esta aplicación, debe ejecutarla como superusuario o formar parte "
"del grupo timekpr.\n"
"Tenga en cuenta que la \"Configuración de Timekpr-nExT\" sólo está disponible "
"en modo superusuario (administrador).\n"
"\n"
"Por favor, realice la configuración con cuidado: ¡no se bloquee!"
#: resource/client/forms/admin.glade:465 resource/client/forms/admin.glade:3047
msgid "Users related configuration"
msgstr "Configuración relacionada con los usuarios"
#: resource/client/forms/admin.glade:476 resource/client/forms/client.glade:298
msgid "Username:"
msgstr "Nombre de Usuario:"
#: resource/client/forms/admin.glade:504 resource/client/forms/admin.glade:516
msgid "List of usernames registered on the system"
msgstr "Lista de nombres de usuario registrados en el sistema"
#: resource/client/forms/admin.glade:530
msgid "Restore"
msgstr "Restaurar"
#: resource/client/forms/admin.glade:535
msgid "Restore configuration from saved state"
msgstr "Restaurar la configuración desde el estado guardado"
#: resource/client/forms/admin.glade:586
msgid "Information about time left / spent"
msgstr "Información sobre el tiempo transcurrido"
#: resource/client/forms/admin.glade:610
msgid ""
"Continuous time left. May span more than the current day (realtime, available "
"when user is logged in)"
msgstr ""
"Tiempo continuo restante. Puede abarcar más del día actual (en tiempo real, "
"disponible cuando el usuario está conectado)."
#: resource/client/forms/admin.glade:612
msgid "Time left (actual):"
msgstr "Tiempo restante (real):"
#: resource/client/forms/admin.glade:626 resource/client/forms/admin.glade:657
#: resource/client/forms/admin.glade:688 resource/client/forms/admin.glade:732
#: resource/client/forms/admin.glade:763 resource/client/forms/admin.glade:794
#: resource/client/forms/client.glade:411 resource/client/forms/client.glade:425
#: resource/client/forms/client.glade:456 resource/client/forms/client.glade:502
#: resource/client/forms/client.glade:728 resource/client/forms/client.glade:786
#: resource/client/forms/client.glade:857 resource/client/forms/client.glade:885
#: resource/client/forms/client.glade:985 resource/client/forms/client.glade:1011
msgid "Format: "
msgstr "Formato: "
#: resource/client/forms/admin.glade:641
msgid ""
"How long the user was inactive since last login (realtime, available when user "
"is logged in)"
msgstr ""
"Cuánto tiempo ha estado inactivo el usuario desde su último inicio de sesión "
"(en tiempo real, disponible cuando el usuario ha iniciado sesión)"
#: resource/client/forms/admin.glade:643
msgid "Time inactive (actual):"
msgstr "Tiempo inactivo (real):"
#: resource/client/forms/admin.glade:672
msgid "Time available today (saved stated, not real-time)"
msgstr "Tiempo disponible hoy (guardado indicado, no en tiempo real)"
#: resource/client/forms/admin.glade:674
msgid "Time left (today):"
msgstr "Tiempo restante (hoy):"
#: resource/client/forms/admin.glade:716
msgid "Time spent today (saved stated, not real-time)"
msgstr "Tiempo empleado hoy (guardado indicado, no en tiempo real)"
#: resource/client/forms/admin.glade:718
msgid "Time spent (today):"
msgstr "Tiempo empleado (hoy):"
#: resource/client/forms/admin.glade:747
msgid "Time spent this week (saved stated, not real-time)"
msgstr "Tiempo empleado esta semana (guardado indicado, no en tiempo real)"
#: resource/client/forms/admin.glade:749 resource/client/forms/client.glade:717
msgid "Time spent (week):"
msgstr "Tiempo empleado (semana):"
#: resource/client/forms/admin.glade:777
msgid "Time spent this month (saved stated, not real-time)"
msgstr "Tiempo empleado este mes (guardado indicado, no en tiempo real)"
#: resource/client/forms/admin.glade:779 resource/client/forms/client.glade:744
msgid "Time spent (month):"
msgstr "Tiempo empleado (mes):"
#: resource/client/forms/admin.glade:861
msgid "Allowance adjustments"
msgstr "Ajustes de las indemnizaciones"
#: resource/client/forms/admin.glade:885
msgid "today's time"
msgstr "la hora de hoy"
#: resource/client/forms/admin.glade:889
msgid "Choose this to adjust user's time allowance for today"
msgstr ""
"Seleccione esta opción para ajustar la asignación de tiempo del usuario para hoy"
#: resource/client/forms/admin.glade:901
msgid "PlayTime"
msgstr "PlayTime"
#: resource/client/forms/admin.glade:905
msgid "Choose this to adjust user's PlayTime allowance for today"
msgstr ""
"Seleccione esta opción para ajustar la asignación de PlayTime del usuario para "
"hoy"
#: resource/client/forms/admin.glade:934
msgid "hr"
msgstr "hr"
#: resource/client/forms/admin.glade:945
msgid "The number of minutes to be adjusted for today's limit"
msgstr "El número de minutos a ajustar para el límite de hoy"
#: resource/client/forms/admin.glade:961
msgid "The number of hours to be adjusted for today's limit"
msgstr "El número de horas que hay que ajustar para el límite de hoy"
#: resource/client/forms/admin.glade:977
msgid "min"
msgstr "min"
#: resource/client/forms/admin.glade:1004
msgid "Add specified time (reward)"
msgstr "Añadir tiempo especificado (recompensa)"
#: resource/client/forms/admin.glade:1020
msgid "Subtract specified time (penalty)"
msgstr "Restar el tiempo especificado (penalización)"
#: resource/client/forms/admin.glade:1036
msgid "Set this specific time limit"
msgstr "Establecer este límite de tiempo específico"
#: resource/client/forms/admin.glade:1101
msgid "Information about PlayTime"
msgstr "Información sobre PlayTime"
#: resource/client/forms/admin.glade:1134
msgid ""
"Actual PlayTime available today (realtime, available when user is logged in)"
msgstr ""
"Tiempo de juego real disponible hoy (tiempo real, disponible cuando el usuario "
"está conectado)"
#: resource/client/forms/admin.glade:1136
msgid "PlayTime left (actual):"
msgstr "Tiempo de juego restante (real):"
#: resource/client/forms/admin.glade:1150 resource/client/forms/admin.glade:1181
#: resource/client/forms/admin.glade:1269
msgid "Format: "
msgstr "Formato: "
#: resource/client/forms/admin.glade:1165
msgid "PlayTime available today (saved stated, not real-time)"
msgstr "PlayTime disponible hoy (guardado indicado, no en tiempo real)"
#: resource/client/forms/admin.glade:1167
msgid "PlayTime left (today):"
msgstr "Tiempo de juego restante (hoy):"
#: resource/client/forms/admin.glade:1196 resource/client/forms/admin.glade:1212
msgid ""
"Active PlayTime activity count (realtime, available when user is logged in)"
msgstr ""
"Recuento de la actividad PlayTime activa (en tiempo real, disponible cuando el "
"usuario está conectado)"
#: resource/client/forms/admin.glade:1198
msgid "Running activities (actual):"
msgstr "Actividades de carrera (reales):"
#: resource/client/forms/admin.glade:1253
msgid "PlayTime spent today (saved stated, not real-time)"
msgstr "PlayTime gastado hoy (guardado declarado, no en tiempo real)"
#: resource/client/forms/admin.glade:1255
msgid "PlayTime spent (today):"
msgstr "Tiempo de juego dedicado (hoy):"
#: resource/client/forms/admin.glade:1306
msgid ""
"Brief information about time spent and everything related to time management "
"for this day"
msgstr ""
"Breve información sobre el tiempo dedicado y todo lo relacionado con la gestión "
"del tiempo para este día"
#: resource/client/forms/admin.glade:1307
msgid "Info & Today"
msgstr "Información y actualidad"
#: resource/client/forms/admin.glade:1348
msgid "This lets you configure time limits."
msgstr "Permite configurar límites de tiempo."
#: resource/client/forms/admin.glade:1350
msgid "Week day limits"
msgstr "Límites de días de la semana"
#. This is meant for very short (e.g. one letter) abbreviation of hours
#: resource/client/forms/admin.glade:1370 resource/client/forms/admin.glade:1869
#: resource/client/forms/admin.glade:2271
msgid "h"
msgstr "h"
#: resource/client/forms/admin.glade:1374 resource/client/forms/admin.glade:2275
msgid "Choose hours to be adjusted for selected days."
msgstr "Elija las horas a ajustar para los días seleccionados."
#. This is meant for very short (e.g. one letter) abbreviation of minutes
#: resource/client/forms/admin.glade:1386 resource/client/forms/admin.glade:1885
#: resource/client/forms/admin.glade:2287
msgid "m"
msgstr "m"
#: resource/client/forms/admin.glade:1390 resource/client/forms/admin.glade:2291
msgid "Choose minutes to be adjusted for selected days."
msgstr "Elija los minutos a ajustar para los días seleccionados."
#: resource/client/forms/admin.glade:1410
msgid ""
"Increase daily time allowance by selected time unit (hours or minutes) for "
"selected days.\n"
"\n"
"Please note, that you may select more than one day for the adjustment!"
msgstr ""
"Aumentar el tiempo diario permitido en la unidad de tiempo seleccionada (horas "
"o minutos) para los días seleccionados.\n"
"\n"
"Please note, that you may select more than one day for the adjustment!"
#: resource/client/forms/admin.glade:1427
msgid ""
"Decrease daily time allowance by selected time unit (hours or minutes) for "
"selected days.\n"
"\n"
"Please note, that you may select more than one day for the adjustment!"
msgstr ""
"Disminuir el tiempo diario permitido en la unidad de tiempo seleccionada (horas "
"o minutos) para los días seleccionados.\n"
"\n"
"Tenga en cuenta que puede elegir más de un día para el ajuste."
#: resource/client/forms/admin.glade:1474
msgid ""
"Lists day of the week and additional information about the day's limit.\n"
"\n"
"Please enable / disable days for the user."
msgstr ""
"Muestra el día de la semana e información adicional sobre el límite del día.\n"
"\n"
"Por favor, active / desactive los días para el usuario."
#: resource/client/forms/admin.glade:1552
msgid ""
"Hour intervals for selected day available to the user.\n"
"Option \"∞\" indicates that time spent during this interval will not be "
"accounted towards the daily limit, it will be accounted as idle instead.\n"
"\n"
"Please note that if the day's limit ends at 24:00 and the next day's limit "
"starts at 00:00, then the user can work continuously past midnight.\n"
"\n"
"Please note that multiple intervals cannot be configured within the same hour.\n"
"An interval can start or end or contain a specific hour, but not more than "
"once. This is by design, not a bug.\n"
"\n"
msgstr ""
"Intervalos de horas del día seleccionado disponibles para el usuario.\n"
"La opción \"∞\" indica que el tiempo empleado durante este intervalo no se "
"tendrá en cuenta para el límite diario, sino que se contabilizará como "
"inactivo.\n"
"\n"
"Tenga en cuenta que si el límite del día termina a las 24:00 y el límite del "
"día siguiente comienza a las 00:00, entonces el usuario puede trabajar "
"continuamente pasada la medianoche.\n"
"\n"
"Tenga en cuenta que no se pueden configurar varios intervalos dentro de la "
"misma hora.\n"
"Un intervalo puede empezar o terminar o contener una hora específica, pero no "
"más de una vez. Esto es por diseño, no un error.\n"
"\n"
#: resource/client/forms/admin.glade:1585
msgid "Hour intervals"
msgstr "Intervalos de horas"
#: resource/client/forms/admin.glade:1607
msgid ""
"Create a new time interval that will be available to the user.\n"
"\n"
"After creating the interval, please edit its start and end times directly in "
"interval list."
msgstr ""
"Crea un nuevo intervalo de tiempo que estará disponible para el usuario.\n"
"\n"
"Después de crear el intervalo, edite sus horas de inicio y fin directamente en "
"la lista de intervalos."
#: resource/client/forms/admin.glade:1624
msgid "Delete the selected time interval from the available list of intervals."
msgstr ""
"Borra el intervalo de tiempo seleccionado de la lista de intervalos disponibles."
#: resource/client/forms/admin.glade:1692
msgid "enter hour intervals"
msgstr "introducir intervalos de horas"
#: resource/client/forms/admin.glade:1746
msgid "verify"
msgstr "verificar"
#: resource/client/forms/admin.glade:1750
msgid ""
"Verify configured time intervals. This is a mandatory step to ensure that "
"intervals are correct.\n"
"\n"
"Intervals which have problems will be highlighted."
msgstr ""
"Verifique los intervalos de tiempo configurados. Este es un paso obligatorio "
"para garantizar que los intervalos son correctos.\n"
"\n"
"Los intervalos que presenten problemas aparecerán resaltados."
#: resource/client/forms/admin.glade:1825
msgid "Weekly and monthly limits"
msgstr "Límites semanales y mensuales"
#. This is meant for very short (e.g. one letter) abbreviation of days
#: resource/client/forms/admin.glade:1853
msgid "d"
msgstr "día"
#: resource/client/forms/admin.glade:1857
msgid "Choose days to be adjusted for selected period."
msgstr "Elija los días a ajustar para el periodo seleccionado."
#: resource/client/forms/admin.glade:1873
msgid "Choose hours to be adjusted for selected period."
msgstr "Elija las horas a ajustar para el periodo seleccionado."
#: resource/client/forms/admin.glade:1889
msgid "Choose minutes to be adjusted for selected period."
msgstr "Elija los minutos a ajustar para el periodo seleccionado."
#: resource/client/forms/admin.glade:1917
msgid ""
"Increase weekly or monthly time allowance by selected time unit (days or hours "
"or minutes) for the selected period."
msgstr ""
"Aumentar la asignación de tiempo semanal o mensual en la unidad de tiempo "
"seleccionada (días u horas o minutos) para el periodo seleccionado."
#: resource/client/forms/admin.glade:1931
msgid ""
"Decrease weekly or monthly time allowance by selected time unit (days or hours "
"or minutes) for the selected period."
msgstr ""
"Disminuir la asignación de tiempo semanal o mensual en la unidad de tiempo "
"seleccionada (días u horas o minutos) para el periodo seleccionado."
#: resource/client/forms/admin.glade:1980
msgid ""
"Weekly and monthly limits for the user.\n"
"\n"
"These limits are applied to user together with the rest of limit configuration."
msgstr ""
"Límites semanales y mensuales para el usuario.\n"
"\n"
"Estos límites se aplican al usuario junto con el resto de la configuración de "
"límites."
#: resource/client/forms/admin.glade:2022
msgid "Apply daily limits"
msgstr "Aplicar límites diarios"
#: resource/client/forms/admin.glade:2027
msgid "Apply limit all changes made in this page"
msgstr "Aplicar limitar todos los cambios realizados en esta página"
#: resource/client/forms/admin.glade:2048
msgid "Daily limit configuration for all week days"
msgstr "Configuración del límite diario para todos los días de la semana"
#: resource/client/forms/admin.glade:2049
msgid "Limit configuration"
msgstr "Configuración de límites"
#: resource/client/forms/admin.glade:2070
msgid "Settings for PlayTime activities"
msgstr "Configuración de las actividades de PlayTime"
#: resource/client/forms/admin.glade:2090
msgid "PlayTime options"
msgstr "Opciones de PlayTime"
#: resource/client/forms/admin.glade:2114 resource/client/forms/admin.glade:2153
msgid "Enable PlayTime for selected user"
msgstr "Activar PlayTime para el usuario seleccionado"
#: resource/client/forms/admin.glade:2116
msgid "Enable PlayTime:"
msgstr "Activar PlayTime:"
#: resource/client/forms/admin.glade:2130 resource/client/forms/admin.glade:2167
msgid ""
"Enable PlayTime override for selected user.\n"
"\n"
"This setting overrides time accounting in a way that time is accounted only "
"when at least one of the applications on the PlayTime activity list are "
"running!\n"
"\n"
"This affects only time accounting, intervals are still fully enforced!\n"
"\n"
"If no processes are running, time is accounted as idle thus effectively it's "
"free time for user!"
msgstr ""
"Activar la anulación de PlayTime para el usuario seleccionado.\n"
"\n"
"Esta opción anula la contabilización del tiempo de forma que sólo se "
"contabiliza el tiempo cuando al menos una de las aplicaciones de la lista de "
"actividades de PlayTime está en ejecución.\n"
"\n"
"Esto sólo afecta a la contabilización del tiempo, los intervalos se siguen "
"aplicando en su totalidad.\n"
"\n"
"Si no hay procesos en ejecución, el tiempo se contabiliza como inactivo, por lo "
"que es tiempo libre para el usuario."
#: resource/client/forms/admin.glade:2138
msgid "Enable PlayTime override:"
msgstr "Activar la anulación de PlayTime:"
#: resource/client/forms/admin.glade:2186 resource/client/forms/admin.glade:2207
msgid ""
"Allow PlayTime activities during unaccounted (\"∞\") time intervals for "
"selected user.\n"
"\n"
"This setting allows the user to use applications configured in his PlayTime "
"activity list during time intervals which are marked as unaccounted (\"∞\").\n"
"\n"
"If this setting is enabled, the use of activities will not be accounted towards "
"PlayTime limits, otherwise applications in PlayTime activity list will be "
"terminated as soon as they are started during unaccounted (\"∞\") time "
"intervals."
msgstr ""
"Permitir actividades de PlayTime durante intervalos de tiempo no contabilizados "
"(\"∞\") para el usuario seleccionado.\n"
"\n"
"Esta opción permite al usuario utilizar aplicaciones configuradas en su lista "
"de actividades de PlayTime durante intervalos de tiempo marcados como no "
"contabilizados (\"∞\").\n"
"\n"
"Si esta opción está activada, el uso de las actividades no se tendrá en cuenta "
"para los límites de PlayTime; de lo contrario, las aplicaciones de la lista de "
"actividades de PlayTime se cerrarán en cuanto se inicien durante los intervalos "
"de tiempo no contabilizados (\"∞\")."
#: resource/client/forms/admin.glade:2192
msgid "Allowed during \"∞\" intervals:"
msgstr "Permitido durante intervalos \"∞\":"
#: resource/client/forms/admin.glade:2251
msgid "This lets you configure PlayTime limits."
msgstr "Permite configurar los límites de PlayTime."
#: resource/client/forms/admin.glade:2253 resource/client/forms/client.glade:1253
msgid "PlayTime limits"
msgstr "Límites de PlayTime"
#: resource/client/forms/admin.glade:2311
msgid ""
"Increase daily PlayTime allowance by selected time unit (hours or minutes) for "
"selected days.\n"
"\n"
"Please note, that you may select more than one day for the adjustment!"
msgstr ""
"Aumentar la asignación diaria de Tiempo de Juego en la unidad de tiempo "
"seleccionada (horas o minutos) para los días seleccionados.\n"
"\n"
"Tenga en cuenta que puede seleccionar más de un día para el ajuste."
#: resource/client/forms/admin.glade:2328
msgid ""
"Decrease daily PlayTime allowance by selected time unit (hours or minutes) for "
"selected days.\n"
"\n"
"Please note, that you may select more than one day for the adjustment!"
msgstr ""
"Disminuir la asignación diaria de Tiempo de Juego en la unidad de tiempo "
"seleccionada (horas o minutos) para los días seleccionados.\n"
"\n"
"Tenga en cuenta que puede seleccionar más de un día para el ajuste."
#: resource/client/forms/admin.glade:2380
msgid ""
"Lists day of the week and additional information about the day's PlayTime "
"limit.\n"
"\n"
"Please enable / disable days for the user and set time allowance for PlayTime "
"activities."
msgstr ""
"Muestra el día de la semana e información adicional sobre el límite de Tiempo "
"de Juego del día.\n"
"\n"
"Habilite/deshabilite los días para el usuario y establezca el tiempo permitido "
"para las actividades de PlayTime."
#: resource/client/forms/admin.glade:2431
msgid "PlayTime activities"
msgstr "Actividades PlayTime"
#: resource/client/forms/admin.glade:2453
msgid ""
"Add new PlayTime activity.\n"
"\n"
"Please specify activity mask and user friendly description in the activity list "
"directly."
msgstr ""
"Añada una nueva actividad PlayTime.\n"
"\n"
"Especifique la máscara de la actividad y una descripción fácil de usar en la "
"lista de actividades directamente."
#: resource/client/forms/admin.glade:2470
msgid "Remove selected entry from PlayTime activities."
msgstr "Eliminar la entrada seleccionada de las actividades de PlayTime."
#: resource/client/forms/admin.glade:2514
msgid ""
"Please specify a list of full process (executable) names without path as case "
"sensitive strings to be monitored in the system.\n"
"\n"
"It's possible to specify RegExp masks for processes too, but please be very "
"careful about them as misusing this setting may lead to killing unwanted "
"processes for the user!\n"
"\n"
"NOTE: RegExp is an expert setting!"
msgstr ""
"Por favor, especifique una lista de nombres completos de procesos (ejecutables) "
"sin ruta como cadenas sensibles a mayúsculas y minúsculas para ser "
"monitorizados en el sistema.\n"
"\n"
"También es posible especificar máscaras RegExp para los procesos, pero tenga "
"mucho cuidado con ellas, ya que el uso incorrecto de esta configuración puede "
"provocar que el usuario elimine procesos no deseados.\n"
"\n"
"NOTA: ¡RegExp es una configuración para expertos!"
#: resource/client/forms/admin.glade:2556
msgid "Apply PlayTime configuration"
msgstr "Aplicar la configuración de PlayTime"
#: resource/client/forms/admin.glade:2560
msgid "Apply PlayTime limits and configuration changes on this page"
msgstr ""
"Aplicar los límites de PlayTime y los cambios de configuración en esta página"
#: resource/client/forms/admin.glade:2579
msgid "PlayTime configuration"
msgstr "PlayTime configuracion"
#: resource/client/forms/admin.glade:2604
msgid "Additional configuration options"
msgstr "Opciones de configuración adicionales"
#: resource/client/forms/admin.glade:2639 resource/client/forms/admin.glade:2662
msgid ""
"Select whether inactive session time is counted.\n"
"\n"
"If this is unchecked, time spent in console (not terminal emulator) login "
"sessions and while the screen is locked is NOT taken into account. This varies "
"among desktop environments."
msgstr ""
"Seleccione si se cuenta el tiempo de sesión inactiva.\n"
"\n"
"Si esta opción no está seleccionada, NO se tiene en cuenta el tiempo que se "
"pasa en sesiones de inicio de sesión de consola (no emulador de terminal) y "
"mientras la pantalla está bloqueada. Esto varía según el entorno de escritorio."
#: resource/client/forms/admin.glade:2643
msgid "Track inactive sessions:"
msgstr "Seguimiento de las sesiones inactivas:"
#: resource/client/forms/admin.glade:2679 resource/client/forms/admin.glade:2702
msgid ""
"Select whether to show Timekpr-next's padlock icon and notifications to the "
"user.\n"
"\n"
"Please note that checking this will disable showing all information and "
"notifications to the user!"
msgstr ""
"Seleccione si desea mostrar el ícono de candado de Timekpr-next y las "
"notificaciones al usuario.\n"
"\n"
"Tenga en cuenta que marcar esta opción desactivará la visualización de toda la "
"información y notificaciones para el usuario."
#: resource/client/forms/admin.glade:2683
msgid "Hide icon and notifications:"
msgstr "Ocultar icono y notificaciones:"
#: resource/client/forms/admin.glade:2754
msgid ""
"Select a restriction / lockout type for the user.\n"
"\n"
"NOTE: please be very careful, think ahead and read every options description "
"when changing this setting from default value!"
msgstr ""
"Seleccione un tipo de restricción / bloqueo para el usuario.\n"
"\n"
"NOTA: ¡tenga mucho cuidado, piense con antelación y lea la descripción de cada "
"opción cuando cambie esta configuración del valor predeterminado!"
#: resource/client/forms/admin.glade:2758
msgid "Restriction / lockout type:"
msgstr "Tipo de restricción / bloqueo:"
#: resource/client/forms/admin.glade:2780
msgid "terminate sessions"
msgstr "terminar sesiones"
#: resource/client/forms/admin.glade:2784
msgid ""
"When time ends, user sessions will be terminated.\n"
"\n"
"This option is a restriction!\n"
"\n"
"This is the default option and most likely is the one you need!"
msgstr ""
"Cuando termine el tiempo, las sesiones de usuario se terminarán.\n"
"\n"
"¡Esta opción es una restricción!\n"
"\n"
"¡Esta es la opción por defecto y muy probablemente es la que usted necesita!"
#: resource/client/forms/admin.glade:2802
msgid "kill sessions"
msgstr "finalizar sesiones"
#: resource/client/forms/admin.glade:2806
msgid ""
"When time ends, user sessions will be killed.\n"
"\n"
"This option is a restriction!\n"
"\n"
"Please evaluate whether you need this type of restriction!"
msgstr ""
"Cuando se acabe el tiempo, las sesiones del usuario serán finalizadas.\n"
"\n"
"¡Esta opción es una restricción!\n"
"\n"
"Por favor, evalúe si necesita este tipo de restricción."
#: resource/client/forms/admin.glade:2823
msgid "shutdown computer"
msgstr "apagar ordenador"
#: resource/client/forms/admin.glade:2827
msgid ""
"When time ends, computer will be shut down.\n"
"\n"
"This option is a restriction!\n"
"\n"
"Please evaluate whether you need this type of restriction!"
msgstr ""
"Cuando termine el tiempo, el ordenador se apagará.\n"
"\n"
"¡Esta opción es una restricción!\n"
"\n"
"Por favor, evalúe si necesita este tipo de restricción."
#: resource/client/forms/admin.glade:2845
msgid "suspend computer"
msgstr "suspender ordenador"
#: resource/client/forms/admin.glade:2849
msgid ""
"When time ends, computer will be suspended (put to sleep).\n"
"\n"
"This option is not a restriction and is more suited for self control purposes.\n"
"\n"
"When woken up when there is still no time left, computer screen will be locked "
"and put to sleep again after some time."
msgstr ""
"Cuando termine el tiempo, el ordenador se suspenderá (se pondrá en reposo).\n"
"\n"
"Esta opción no es una restricción y es más adecuada para fines de autocontrol.\n"
"\n"
"Si se despierta cuando todavía no queda tiempo, la pantalla del ordenador se "
"bloqueará y se volverá a poner en reposo al cabo de un rato."
#: resource/client/forms/admin.glade:2867
msgid "suspend / wakeup computer"
msgstr "suspender / despertar el ordenador"
#: resource/client/forms/admin.glade:2871
msgid ""
"When time ends, computer will be suspended (put to sleep) and will be woken up "
"at start of next available time interval for the user.\n"
"\n"
"This option is not a restriction and is more suited for self control purposes.\n"
"\n"
"When woken up when there is still no time left, computer screen will be locked "
"and put to sleep again after some time."
msgstr ""
"Cuando termine el tiempo, el ordenador se suspenderá (se pondrá en reposo) y se "
"despertará al inicio del siguiente intervalo de tiempo disponible para el "
"usuario.\n"
"\n"
"Esta opción no es una restricción y es más adecuada para fines de autocontrol.\n"
"\n"
"Si se despierta cuando todavía no queda tiempo, la pantalla del ordenador se "
"bloqueará y se volverá a poner en reposo al cabo de un rato."
#: resource/client/forms/admin.glade:2889
msgid "lock screen"
msgstr "bloquear;pantalla"
#: resource/client/forms/admin.glade:2893
msgid ""
"When time ends, computer screen will be locked.\n"
"\n"
"This option is not a restriction and is more suited for self control purposes."
msgstr ""
"Cuando termine el tiempo, la pantalla del ordenador se bloqueará.\n"
"\n"
"Esta opción no es una restricción y es más adecuada para fines de autocontrol."
#: resource/client/forms/admin.glade:2924
msgid ""
"Select a time interval when computer can be woken up automatically.\n"
"\n"
"Please note that this option is only effective if wake up by RealTime Clock is "
"supported and enabled in BIOS / UEFI!"
msgstr ""
"Seleccione un intervalo de tiempo en el que el ordenador puede ser despertado "
"automáticamente.\n"
"\n"
"Tenga en cuenta que esta opción sólo es efectiva si el despertador por Reloj "
"RealTime está soportado y habilitado en BIOS / UEFI."
#: resource/client/forms/admin.glade:2928
msgid "Wakeup hour interval:"
msgstr "Intervalo de horas de despertador:"
#: resource/client/forms/admin.glade:2940
msgid ""
"Enter start hour for the automatic wakeup function.\n"
"\n"
"Please note that this option is only effective if wake up by RealTime Clock is "
"supported and enabled in BIOS / UEFI!"
msgstr ""
"Introduzca la hora de inicio para la función de despertador automático.\n"
"\n"
"Tenga en cuenta que esta opción sólo es efectiva si el despertador por RealTime "
"Clock está soportado y habilitado en BIOS / UEFI."
#: resource/client/forms/admin.glade:2959
msgid ""
"Enter end hour for the automatic wakeup function.\n"
"\n"
"Please note that this option is only effective if wake up by RealTime Clock is "
"supported and enabled in BIOS / UEFI!"
msgstr ""
"Introduzca la hora final para la función de despertador automático.\n"
"\n"
"Tenga en cuenta que esta opción sólo es efectiva si el despertador por RealTime "
"Clock está soportado y habilitado en BIOS / UEFI."
#: resource/client/forms/admin.glade:2997
msgid "Apply configuration"
msgstr "Aplicar Configuración"
#: resource/client/forms/admin.glade:3001
msgid "Apply additional configuration changes on this page"
msgstr "Aplicar cambios de configuración adicionales en esta página"
#: resource/client/forms/admin.glade:3020
msgid "Additional configuration"
msgstr "Configuración adicional"
#: resource/client/forms/admin.glade:3021 resource/client/forms/admin.glade:3695
msgid "Additional options"
msgstr "Opciones adicionales"
#: resource/client/forms/admin.glade:3048
msgid "User Configuration"
msgstr "Configuración de usuario"
#: resource/client/forms/admin.glade:3058
msgid "Timekpr-nExT related configuration"
msgstr "Configuración relacionada con Timekpr-nExT"
#: resource/client/forms/admin.glade:3085
msgid "Control Timekpr-nExT Settings"
msgstr "Control Ajustes Timekpr-nExT"
#: resource/client/forms/admin.glade:3112 resource/client/forms/admin.glade:3264
msgid ""
"Timekpr-nExT log level.\n"
"\n"
"Please do not change this unless you have to. You likely won't find anything "
"pretty in the log files.\n"
"\n"
"Values are: 1 - standard, 2 - debug, 3 - extra debug"
msgstr ""
"Nivel de registro Timekpr-nExT.\n"
"\n"
"Por favor, no cambie esto a menos que sea necesario. Es probable que no "
"encuentre nada bonito en los archivos de registro.\n"
"\n"
"Los valores son: 1 - estándar, 2 - depuración, 3 - depuración extra"
#: resource/client/forms/admin.glade:3132 resource/client/forms/admin.glade:3180
msgid ""
"Session polling time granularity which specifies how often user sessions and "
"activity are checked and accounted.\n"
"\n"
"3 - 5 seconds are optimal, don't change this if unsure."
msgstr ""
"Granularidad de tiempo de sondeo de sesión que especifica con qué frecuencia se "
"comprueban y contabilizan las sesiones y la actividad de los usuarios.\n"
"\n"
"3 - 5 segundos son óptimos, no cambie esto si no está seguro."
#: resource/client/forms/admin.glade:3136
msgid "Poll interval"
msgstr "Intervalo de sondeo"
#: resource/client/forms/admin.glade:3148 resource/client/forms/admin.glade:3251
msgid ""
"Specify the time in seconds when Timekpr-nExT starts continuous real-time "
"countdown before enforcing a restriction / lockout to user's sessions."
msgstr ""
"Especifique el tiempo en segundos en el que Timekpr-nExT inicia la cuenta atrás "
"continua en tiempo real antes de aplicar una restricción / bloqueo a las "
"sesiones del usuario."
#: resource/client/forms/admin.glade:3164 resource/client/forms/admin.glade:3214
msgid ""
"This specifies the rate in seconds at which actual user state is saved to "
"disk.\n"
"\n"
"To improve performance and still have great accuracy, Timekpr-nExT accounts "
"time in memory at \"Poll interval\" frequency, this setting defines a frequency "
"at which user state is saved to disk for permanent storage."
msgstr ""
"Especifica la frecuencia en segundos con la que el estado real del usuario se "
"guarda en el disco.\n"
"\n"
"Para mejorar el rendimiento y seguir teniendo una gran precisión, Timekpr-nExT "
"contabiliza el tiempo en memoria a la frecuencia de \"Intervalo de sondeo\", "
"este ajuste define una frecuencia a la que el estado de usuario se guarda en el "
"disco para su almacenamiento permanente."
#: resource/client/forms/admin.glade:3168
msgid "Save time"
msgstr "Ahorrar tiempo"
#: resource/client/forms/admin.glade:3198 resource/client/forms/admin.glade:3233
msgid ""
"Number of seconds left for user before enforcing a configured restriction / "
"lockout to his sessions.\n"
"\n"
"After this is reached and user is still active, the user's sessions will be "
"handled according to specified restriction / lockout, and almost nothing can be "
"done to prevent it."
msgstr ""
"Número de segundos que le quedan al usuario antes de aplicar una restricción / "
"bloqueo configurado a sus sesiones.\n"
"\n"
"Una vez alcanzado este tiempo y si el usuario sigue activo, las sesiones del "
"usuario se gestionarán de acuerdo con la restricción / bloqueo especificado, y "
"no se podrá hacer prácticamente nada para evitarlo."
#: resource/client/forms/admin.glade:3202
msgid "Termination time"
msgstr "Hora de finalización"
#: resource/client/forms/admin.glade:3253
msgid "Countdown time"
msgstr "Tiempo de cuenta atrás"
#: resource/client/forms/admin.glade:3270
msgid "Log level"
msgstr "Nivel de registro"
#: resource/client/forms/admin.glade:3281 resource/client/forms/admin.glade:3297
msgid ""
"Number of seconds left for user's available time before sending a final "
"critical notification that time is about to run out.\n"
"\n"
"NOTE: the rest of the notification times are configurable per user by user in "
"client application!"
msgstr ""
"Número de segundos que le quedan al usuario de tiempo disponible antes de "
"enviar una última notificación crítica de que el tiempo está a punto de "
"agotarse.\n"
"\n"
"NOTA: el resto de los tiempos de notificación son configurables por usuario en "
"la aplicación cliente."
#: resource/client/forms/admin.glade:3285
msgid "Final notification"
msgstr "Notificación final"
#: resource/client/forms/admin.glade:3339
msgid "Control Timekpr-nExT tracking items"
msgstr "Controlar los elementos de seguimiento Timekpr-nExT"
#: resource/client/forms/admin.glade:3372
msgid "Tracked Sessions"
msgstr "Controlar los elementos de seguimiento Timekpr-nExT"
#: resource/client/forms/admin.glade:3395
msgid "Add tracked session type to the list"
msgstr "Añadir a la lista el tipo de sesión rastreada"
#: resource/client/forms/admin.glade:3410
msgid "Remove tracked session type from the list"
msgstr "Eliminar de la lista el tipo de sesión rastreada"
#: resource/client/forms/admin.glade:3439
msgid ""
"List of session types to be tracked.\n"
"\n"
"Please, do not change or experiment with this unless you actually (not "
"wishfully) know what you are doing."
msgstr ""
"Lista de tipos de sesión que se rastrearán.\n"
"\n"
"Por favor, no cambies o experimentes con esto a menos que realmente (no por "
"deseo) sepas lo que estás haciendo."
#: resource/client/forms/admin.glade:3474
msgid "Excluded Sessions"
msgstr "Sesiones excluidas"
#: resource/client/forms/admin.glade:3497
msgid "Add an excluded session type to the list"
msgstr "Añadir un tipo de sesión excluido a la lista"
#: resource/client/forms/admin.glade:3512
msgid "Remove an excluded session type from the list"
msgstr "Eliminar de la lista un tipo de sesión excluido"
#: resource/client/forms/admin.glade:3541
msgid ""
"List of sessions to be excluded from tracking.\n"
"\n"
"Please, do not change or experiment with this unless you actually (not "
"wishfully) know what you are doing."
msgstr ""
"Lista de sesiones que deben excluirse del seguimiento.\n"
"\n"
"Por favor, no cambies o experimentes con esto a menos que realmente (no por "
"deseo) sepas lo que estás haciendo."
#: resource/client/forms/admin.glade:3576
msgid "Excluded Users"
msgstr "Usuarios excluidos"
#: resource/client/forms/admin.glade:3599
msgid "Add a user to the exclusion list"
msgstr "Añadir un usuario a la lista de exclusión"
#: resource/client/forms/admin.glade:3614
msgid "Remove a user from the exclusion list"
msgstr "Eliminar un usuario de la lista de exclusión"
#: resource/client/forms/admin.glade:3643
msgid ""
"List of users excluded from tracking.\n"
"\n"
"Please specify actual usernames, not real names here. For example, \"jsmith\", "
"not \"John Smith\"."
msgstr ""
"Lista de usuarios excluidos del seguimiento.\n"
"\n"
"Especifique aquí los nombres de usuario reales, no los verdaderos. Por ejemplo, "
"\"jsmith\", no \"John Smith\"."
#: resource/client/forms/admin.glade:3722 resource/client/forms/admin.glade:3741
#: resource/client/forms/admin.glade:3777
msgid ""
"This setting controls whether PlayTime functionality is enabled.\n"
"\n"
"This is a PlayTime master switch, if it's turned off, it's turned off for "
"everyone regardless of individual settings!"
msgstr ""
"Este ajuste controla si la funcionalidad PlayTime está activada.\n"
"\n"
"Se trata de un interruptor maestro de PlayTime, si está desactivado, se "
"desactiva para todos, independientemente de la configuración individual."
#: resource/client/forms/admin.glade:3726
msgid "PlayTime enabled:"
msgstr "PlayTime activado:"
#: resource/client/forms/admin.glade:3756
msgid ""
"This setting controls whether PlayTime activity monitor will use process "
"command line, including arguments, for monitoring processes (by default only "
"uses the process name).\n"
"\n"
"When this setting is enabled Timekpr-nExT will perform a match against full "
"command line up to 512 characters enabling enhanced process monitoring "
"capabilities.\n"
"\n"
"Please be careful and double check your RegExp patterns in each individual "
"user's PlayTime activity masks!"
msgstr ""
"Esta opción controla si el monitor de actividad de PlayTime utilizará la línea "
"de comandos del proceso, incluidos los argumentos, para monitorizar los "
"procesos (por defecto sólo utiliza el nombre del proceso).\n"
"\n"
"Cuando esta opción está activada, Timekpr-nExT realizará una comparación con la "
"línea de comandos completa de hasta 512 caracteres, lo que permite mejorar las "
"capacidades de supervisión de procesos.\n"
"\n"
"Tenga cuidado y compruebe los patrones RegExp en las máscaras de actividad "
"PlayTime de cada usuario."
#: resource/client/forms/admin.glade:3762
msgid "Enhanced activity monitor:"
msgstr "Monitor de actividad mejorado:"
#: resource/client/forms/admin.glade:3811
msgid "Apply Timekpr-nExT settings"
msgstr "Aplicar la configuración de Timekpr-nExT"
#: resource/client/forms/admin.glade:3816
msgid "Apply all Timekpr-nExT settings at once"
msgstr "Aplicar todos los ajustes de Timekpr-nExT a la vez"
#: resource/client/forms/admin.glade:3836
msgid "Timekpr-nExT Administration Configuration"
msgstr "Configuración de la administración de Timekpr-nExT"
#: resource/client/forms/admin.glade:3837
msgid "Timekpr-nExT Configuration"
msgstr "Configuración de Timekpr-nExT"
#: resource/client/forms/admin.glade:3866 resource/client/forms/admin.glade:3883
msgid "Status of Timekpr-nExT admin client"
msgstr ""
"Configuración de Timekpr-nExTaEstado del cliente administrador de Timekpr-nExT"
#. This is one of the notification priorities
#: resource/client/forms/client.glade:27
msgid "Information"
msgstr "Información"
#. This is one of the notification priorities
#: resource/client/forms/client.glade:31
msgid "Warning"
msgstr "Aviso"
#. This is one of the notification priorities
#: resource/client/forms/client.glade:35
msgid "Severe"
msgstr "Severo"
#. This is one of the notification priorities
#: resource/client/forms/client.glade:39
msgid "Critical"
msgstr "Crítico"
#: resource/client/forms/client.glade:133
msgid "Timekpr-nExT client"
msgstr "Cliente Timekpr-nExT"
#: resource/client/forms/client.glade:164
msgid "Status of Timekpr-nExT client"
msgstr "Estado del cliente Timekpr-nExT"
#: resource/client/forms/client.glade:196
msgid "Save all changes"
msgstr "Guardar todos los cambios"
#: resource/client/forms/client.glade:213
msgid "Close the window"
msgstr "Cerrar la ventana"
#: resource/client/forms/client.glade:297 resource/client/forms/client.glade:314
msgid "Current effective username"
msgstr "Nombre de usuario efectivo actual"
#: resource/client/forms/client.glade:371
msgid "Current statistics"
msgstr "Estadísticas actuales"
#: resource/client/forms/client.glade:396
msgid "Time spent this session or after Timekpr-nExT has been restarted"
msgstr "Tiempo transcurrido en esta sesión o tras reiniciar Timekpr-nExT"
#: resource/client/forms/client.glade:398
msgid "Time spent (session):"
msgstr "Tiempo empleado (sesión):"
#: resource/client/forms/client.glade:441
msgid "Time inactive this session or after Timekpr-nExT has been restarted"
msgstr "Tiempo inactivo en esta sesión o después de reiniciar Timekpr-nExT"
#: resource/client/forms/client.glade:443
msgid "Time inactive:"
msgstr "Tiempo inactivo:"
#: resource/client/forms/client.glade:469
msgid "Continuous time left to you. May span more than the current day."
msgstr "Tiempo continuo que le queda. Puede abarcar más que el día actual."
#: resource/client/forms/client.glade:471
msgid "Continuous time left:"
msgstr "Tiempo continuo restante:"
#: resource/client/forms/client.glade:487
msgid "Total time available left today in a row, up to the end of the day"
msgstr "Tiempo total disponible que queda hoy en una fila, hasta el final del día"
#: resource/client/forms/client.glade:489 resource/client/forms/client.glade:974
msgid "Time left today:"
msgstr "Hoy queda tiempo:"
#: resource/client/forms/client.glade:548
msgid "These are the days and limits that are available to you"
msgstr "Estos son los días y los límites de que dispone"
#: resource/client/forms/client.glade:573 resource/client/forms/client.glade:1171
msgid "Days & Limits"
msgstr "Días y límites"
#: resource/client/forms/client.glade:609
msgid ""
"This shows the time intervals that are available for use.\n"
"\n"
"Option \"∞\" indicates that time spent during this interval will not be "
"accounted towards the daily limit, it will be accounted as idle instead."
msgstr ""
"Esto muestra el tiempo de los intervalos disponibles para su uso\n"
"\n"
"La opción \"∞\" indica que el tiempo empleado durante este intervalo no se "
"tendrá en cuenta para el límite diario, sino que se contabilizará como inactivo."
#: resource/client/forms/client.glade:634
msgid "Intervals"
msgstr "Intervalos"
#: resource/client/forms/client.glade:663
msgid "Daily limits"
msgstr "Límites diarios"
#: resource/client/forms/client.glade:691
msgid "Additional statistics"
msgstr "Estadísticas adicionales"
#: resource/client/forms/client.glade:715
msgid "Time spent this week"
msgstr "Tiempo dedicado esta semana"
#: resource/client/forms/client.glade:742
msgid "Time spent this month"
msgstr "Tiempo dedicado este mes"
#: resource/client/forms/client.glade:755
msgid ""
"Select whether inactive session time is counted.\n"
"\n"
"If this is unchecked, time spent in console (not terminal emulator) login "
"sessions and while the screen is locked is NOT taken into account.\n"
"This varies among desktop environments."
msgstr ""
"Seleccione si se cuenta el tiempo de sesión inactiva.\n"
"\n"
"Si esta opción no está seleccionada, NO se tiene en cuenta el tiempo que se "
"pasa en sesiones de inicio de sesión de consola (no emulador de terminal) y "
"mientras la pantalla está bloqueada.\n"
"Esto varía según el entorno de escritorio."
#: resource/client/forms/client.glade:760
msgid "Track inactive:"
msgstr "Pista inactiva:"
#: resource/client/forms/client.glade:773
msgid ""
"Select whether inactive session time is counted. If this is unchecked, time "
"spent in console (not terminal emulator) login sessions and while the screen is "
"locked is NOT taken into account. This varies among desktop environments."
msgstr ""
"Seleccione si se cuenta el tiempo de sesión inactiva. Si esta opción no está "
"seleccionada, NO se tiene en cuenta el tiempo que se pasa en sesiones de inicio "
"de sesión de consola (no emulador de terminal) y mientras la pantalla está "
"bloqueada. Esto varía según el entorno de escritorio."
#: resource/client/forms/client.glade:820 resource/client/forms/client.glade:918
msgid "Additional limits"
msgstr "Límites adicionales"
#: resource/client/forms/client.glade:844
msgid "Time limit for this week available to you"
msgstr "Plazo disponible para esta semana"
#: resource/client/forms/client.glade:846
msgid "Time limit (week):"
msgstr "Plazo disponible para esta semana"
#: resource/client/forms/client.glade:872
msgid "Time limit for this month available to you"
msgstr "Plazo disponible para este mes"
#: resource/client/forms/client.glade:874
msgid "Time limit (month):"
msgstr "Plazo limite para este mes"
#: resource/client/forms/client.glade:948
msgid "Current PlayTime statistics"
msgstr "Estadísticas actuales de PlayTime"
#: resource/client/forms/client.glade:972
msgid "Total PlayTime available left today"
msgstr "Tiempo total de juego disponible que queda hoy"
#: resource/client/forms/client.glade:998
msgid "Total PlayTime spent today"
msgstr "Tiempo total de juego invertido hoy"
#: resource/client/forms/client.glade:1000
msgid "Time spent today:"
msgstr "Tiempo empleado hoy:"
#: resource/client/forms/client.glade:1027 resource/client/forms/client.glade:1042
msgid ""
"This option overrides the default time accounting.\n"
"\n"
"If it's checked, the time is accounted only when activities in \"Activity / "
"application list\" are running, the rest of the time is considered idle."
msgstr ""
"Esta opción anula la contabilización del tiempo por defecto.\n"
"\n"
"Si está marcada, el tiempo se contabiliza sólo cuando se están ejecutando las "
"actividades de la \"Lista de actividades/aplicaciones\", el resto del tiempo se "
"considera inactivo."
#: resource/client/forms/client.glade:1046
msgid "Time limit override:"
msgstr "Anulación del límite de tiempo:"
#: resource/client/forms/client.glade:1060 resource/client/forms/client.glade:1073
msgid "Number of currently active PlayTime activities"
msgstr "Número de actividades PlayTime actualmente activas"
#: resource/client/forms/client.glade:1062
msgid "Running activities:"
msgstr "Actividades en marcha:"
#: resource/client/forms/client.glade:1086 resource/client/forms/client.glade:1106
msgid ""
"Allow activities during unaccounted (\"∞\") time intervals.\n"
"\n"
"This setting allows to use applications listed in \"Activity / application "
"list\" during time intervals which are marked as unaccounted (\"∞\"), if the "
"setting is not enabled, none of activities are allowed to run!"
msgstr ""
"Permitir actividades durante intervalos de tiempo no contabilizados (\"∞\").\n"
"\n"
"Este ajuste permite utilizar las aplicaciones listadas en \"Actividad / lista "
"de aplicaciones\" durante los intervalos de tiempo marcados como no "
"contabilizados (\"∞\"), si el ajuste no está habilitado, ¡no se permitirá la "
"ejecución de ninguna actividad!"
#: resource/client/forms/client.glade:1090
msgid "Allowed during \"∞\":"
msgstr "Permitido durante \"∞\":"
#: resource/client/forms/client.glade:1148
msgid ""
"These are days and limits that available to you as part of PlayTime activity "
"restrictions"
msgstr ""
"Estos son los días y los límites que están a su disposición como parte de las "
"restricciones de la actividad PlayTime"
#: resource/client/forms/client.glade:1205
msgid ""
"This shows activities / applications which are part of PlayTime restrictions"
msgstr ""
"Muestra las actividades / aplicaciones que forman parte de las restricciones de "
"PlayTime."
#: resource/client/forms/client.glade:1228
msgid "Activity / application list"
msgstr "Lista de actividades / solicitudes"
#: resource/client/forms/client.glade:1292
msgid ""
"Configuration for personalized notifications about available time.\n"
"\n"
"Please configure notifications as you see fit. However, please keep in mind "
"that there will be two types of notifications that cannot be personalized: a "
"final warning some time before time ends and a countdown notifications when "
"time is about to end.\n"
"\n"
"The configuration \"Time\" value indicates a value of time left when "
"notification will be shown, the \"Importance\" option governs an icon colour "
"and notification properties as follows.\n"
"\n"
"Information - a green icon and an informational notification will be shown\n"
"Warning - a yellow icon and an informational notification will be shown\n"
"Severe - a red icon and important notification will be shown\n"
"Critical - a red icon and critical notification will be shown, this "
"notification usually is shown over all open applications and stays open until "
"dismissed, however this behaviour highly depends on Desktop Environment in use"
msgstr ""
"Configuración para notificaciones personalizadas sobre el tiempo disponible.\n"
"\n"
"Configure las notificaciones como mejor le parezca. Sin embargo, tenga en "
"cuenta que habrá dos tipos de notificaciones que no se podrán personalizar: un "
"aviso final un tiempo antes de que termine el tiempo y una notificación de "
"cuenta atrás cuando el tiempo esté a punto de terminar.\n"
"\n"
"El valor \"Tiempo\" de la configuración indica un valor de tiempo restante "
"cuando se mostrará la notificación, la opción \"Importancia\" rige un color de "
"icono y las propiedades de la notificación de la siguiente manera.\n"
"\n"
"Información: se mostrará un icono verde y una notificación informativa.\n"
"Advertencia: se mostrará un icono amarillo y una notificación informativa.\n"
"Grave - se mostrará un icono rojo y una notificación importante\n"
"Crítico: se mostrará un icono rojo y una notificación crítica. Esta "
"notificación suele mostrarse sobre todas las aplicaciones abiertas y permanece "
"abierta hasta que se cierra, aunque este comportamiento depende en gran medida "
"del entorno de escritorio utilizado."
#: resource/client/forms/client.glade:1325
msgid "Notification configuration"
msgstr "Configuración de las notificaciones"
#: resource/client/forms/client.glade:1347
msgid "Add new notification threshold to the list"
msgstr "Añadir un nuevo umbral de notificación a la lista"
#: resource/client/forms/client.glade:1362
msgid "Remove notification threshold from the list"
msgstr "Eliminar umbral de notificación de la lista"
#: resource/client/forms/client.glade:1411
msgid ""
"Configuration for personalized notifications about available PlayTime.\n"
"\n"
"Please configure notifications as you see fit.\n"
"\n"
"The configuration \"Time\" value indicates a value of PlayTime left when "
"notification will be shown, the \"Importance\" option governs an icon colour "
"and notification properties as follows.\n"
"\n"
"Information - a green icon and an informational notification will be shown\n"
"Warning - a yellow icon and an informational notification will be shown\n"
"Severe - a red icon and important notification will be shown\n"
"Critical - a red icon and critical notification will be shown, this "
"notification usually is shown over all open applications and stays open until "
"dismissed, however this behaviour highly depends on Desktop Environment in use"
msgstr ""
"Configuración para notificaciones personalizadas sobre el Tiempo de Juego "
"disponible.\n"
"\n"
"Configure las notificaciones como mejor le parezca.\n"
"\n"
"El valor de configuración \"Tiempo\" indica un valor de Tiempo de Juego "
"restante cuando se mostrará la notificación, la opción \"Importancia\" gobierna "
"un color de icono y propiedades de notificación como sigue.\n"
"\n"
"Información: se mostrará un icono verde y una notificación informativa.\n"
"Advertencia - se mostrará un icono amarillo y una notificación informativa\n"
"Grave - se mostrará un icono rojo y una notificación importante\n"
"Crítico: se mostrará un icono rojo y una notificación crítica. Esta "
"notificación suele mostrarse sobre todas las aplicaciones abiertas y permanece "
"abierta hasta que se cierra, aunque este comportamiento depende en gran medida "
"del entorno de escritorio utilizado."
#: resource/client/forms/client.glade:1444
msgid "PlayTime notification configuration"
msgstr "Configuración de notificaciones PlayTime"
#: resource/client/forms/client.glade:1466
msgid "Add new PlayTime notification threshold to the list"
msgstr "Configuración de notificaciones PlayTime"
#: resource/client/forms/client.glade:1481
msgid "Remove PlayTime notification threshold from the list"
msgstr "Eliminar el umbral de notificación de PlayTime de la lista"
#: resource/client/forms/client.glade:1540
msgid "Notifications"
msgstr "Notificaciones"
#: resource/client/forms/client.glade:1564
msgid ""
"Select whether to use speech notifications, if available.\n"
"\n"
"You may be able to make speech notifications available by installing package "
"\"python3-espeak\" or \"python3-espeak-ng\"."
msgstr ""
"Seleccione si desea utilizar las notificaciones de voz, si están disponibles.\n"
"\n"
"Puede hacer que las notificaciones de voz estén disponibles instalando el "
"paquete \"python3-espeak\" o \"python3-espeak-ng\"."
#: resource/client/forms/client.glade:1574
msgid "Use speech notifications"
msgstr "Utilizar notificaciones de voz"
#: resource/client/forms/client.glade:1593 resource/client/forms/client.glade:1615
msgid ""
"This sets the logging level.\n"
"\n"
"Please do not change this unless you know what you're doing."
msgstr ""
"Establece el nivel de registro.\n"
"\n"
"Por favor, no cambies esto a menos que sepas lo que estás haciendo."
#: resource/client/forms/client.glade:1619
msgid "Logging level"
msgstr "Nivel de registro"
#: resource/client/forms/client.glade:1641
msgid ""
"Specify whether to show a notification when limit configurations or allowance "
"changes"
msgstr ""
"Especifique si desea mostrar una notificación cuando se modifiquen las "
"configuraciones de límites o las autorizaciones"
#: resource/client/forms/client.glade:1649
msgid "Show limit changes"
msgstr "Mostrar cambios de límites"
#: resource/client/forms/client.glade:1665
msgid ""
"Specify whether to show all notifications.\n"
"\n"
"If unchecked, then only important ones are shown."
msgstr ""
"Specify whether to show all notifications.\n"
"\n"
"If unchecked, then only important ones are shown."
#: resource/client/forms/client.glade:1675
msgid "Show all notifications"
msgstr "Mostrar todas las notificaciones"
#: resource/client/forms/client.glade:1691
msgid ""
"Specify whether to show seconds in notification area.\n"
"\n"
"Some desktop environments, like KDE5, do not support text besides notification "
"icons."
msgstr ""
"Especifique si desea mostrar los segundos en el área de notificación.\n"
"\n"
"Algunos entornos de escritorio, como KDE5, no admiten texto además de los "
"iconos de notificación."
#: resource/client/forms/client.glade:1701
msgid "Show seconds in notification area"
msgstr "Mostrar segundos en el área de notificación"
#: resource/client/forms/client.glade:1720 resource/client/forms/client.glade:1744
msgid ""
"This sets how long a notification is shown for regular notifications about time "
"left and configuration changes.\n"
"\n"
"The value is specified in seconds.\n"
"A value of 0 means show until dismissed (not recommended for regular "
"notifications).\n"
"\n"
"Please note that the desktop environment you use may override this timeout, in "
"which case this setting will not have any effect on notification."
msgstr ""
"Establece durante cuánto tiempo se muestra una notificación para las "
"notificaciones regulares sobre el tiempo restante y los cambios de "
"configuración.\n"
"\n"
"El valor se especifica en segundos.\n"
"Un valor de 0 significa que se muestra hasta que se descarta (no recomendado "
"para notificaciones regulares).\n"
"\n"
"Tenga en cuenta que el entorno de escritorio que utilice puede anular este "
"tiempo de espera, en cuyo caso esta configuración no tendrá ningún efecto sobre "
"la notificación."
#: resource/client/forms/client.glade:1751
msgid "Notification timeout (sec)"
msgstr "Tiempo de espera de notificación (seg)"
#: resource/client/forms/client.glade:1776 resource/client/forms/client.glade:1801
msgid ""
"This sets how long a notification is shown for \"Critical\" notifications about "
"time left (when the icon turns red).\n"
"\n"
"The value is specified in seconds.\n"
"A value of 0 means show until dismissed.\n"
"\n"
"Please note that the desktop environment you use may override this timeout, in "
"which case this setting will not have any effect on notification."
msgstr ""
"Establece durante cuánto tiempo se muestra una notificación para las "
"notificaciones \"Críticas\" sobre el tiempo restante (cuando el icono se vuelve "
"rojo).\n"
"\n"
"El valor se especifica en segundos.\n"
"Un valor de 0 significa que se muestra hasta que se descarta.\n"
"\n"
"Tenga en cuenta que el entorno de escritorio que utilice puede anular este "
"tiempo de espera, en cuyo caso este ajuste no tendrá ningún efecto sobre la "
"notificación."
#: resource/client/forms/client.glade:1808
msgid "Critical notification timeout (sec)"
msgstr "Tiempo de espera de notificación crítica (seg)"
#: resource/client/forms/client.glade:1830
msgid ""
"Select whether to use sound \"bell\" (short notification sound) to announce a "
"new notification from Timekpr-nExT.\n"
"\n"
"This works only for enabled notifications.\n"
"\n"
"If this setting is not editable, your environment does not advertise sound "
"notification support."
msgstr ""
"Seleccione si desea utilizar el sonido \"campana\" (sonido de notificación "
"corto) para anunciar una nueva notificación de Timekpr-nExT.\n"
"\n"
"Esto sólo funciona para las notificaciones habilitadas.\n"
"\n"
"Si esta configuración no se puede editar, su entorno no anuncia la "
"compatibilidad con notificaciones sonoras."
#: resource/client/forms/client.glade:1842
msgid "Use sound \"bell\" for notifications"
msgstr "Utilizar \"timbre\" sonoro para las notificaciones"
#: resource/client/forms/client.glade:1862
msgid "Configuration"
msgstr "Configuración"
#~ msgid ""
#~ "==> set restriction / lockout type (\"lock\" - lock session, \"suspend\" - "
#~ "suspend the computer, \"suspendwake\" - suspend and wake up, \"terminate\" - "
#~ "terminate sessions, \"shutdown\" - shutdown the computer), examples"
#~ msgstr ""
#~ "==> establecer restricción / tipo de bloqueo (\"lock\" - bloquear sesión, "
#~ "\"suspend\" - suspender el ordenador, \"suspendwake\" - suspender y "
#~ "despertar, \"terminate\" - terminar sesiones, \"shutdown\" - apagar el "
#~ "ordenador), ejemplos"
#~ msgid "Copyright (c) 2018-2021 Eduards Bezverhijs"
#~ msgstr "Copyright (c) 2018-2021 Eduards Bezverhijs"
#~ msgid ""
#~ "Select whether to show Timekpr-next's padlock icon and notifications to the "
#~ "user.\n"
#~ "\n"
#~ "Please note that unchecking this will disable showing all information and "
#~ "notifications to the user!"
#~ msgstr ""
#~ "Seleccione si desea mostrar el icono del candado de Timekpr-next y las "
#~ "notificaciones al usuario.\n"
#~ "\n"
#~ "Tenga en cuenta que si desmarca esta opción, no se mostrará toda la "
#~ "información ni las notificaciones al usuario."
#~ msgid ""
#~ "Select whether to use speech notifications, if available.\n"
#~ "\n"
#~ "You may be able to make speech notifications available by installing package "
#~ "\"python3-espeak\"."
#~ msgstr ""
#~ "Seleccione si desea utilizar las notificaciones de voz, si están "
#~ "disponibles.\n"
#~ "\n"
#~ "Puede hacer que las notificaciones de voz estén disponibles instalando el "
#~ "paquete \"python3-espeak\"."
timekpr-next/resource/locale/es/LC_MESSAGES/timekpr.mo 000644 001750 001750 00000221640 15122253261 024471 0 ustar 00bezvfedu bezvfedu 000000 000000
Q % % ,% $ G% <