From b838f3c5fee6dd8726c511ee4334fea947bb94bc Mon Sep 17 00:00:00 2001 From: Dmitriy Q Date: Fri, 7 Jul 2023 17:57:34 +0000 Subject: [PATCH 01/22] Translated using Weblate (Russian) Currently translated at 100.0% (116 of 116 strings) Translation: Safe Eyes/Translations Translate-URL: https://hosted.weblate.org/projects/safe-eyes/translations/ru/ --- safeeyes/config/locale/ru/LC_MESSAGES/safeeyes.po | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/safeeyes/config/locale/ru/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/ru/LC_MESSAGES/safeeyes.po index 17f57d1d..34b55c8c 100644 --- a/safeeyes/config/locale/ru/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/ru/LC_MESSAGES/safeeyes.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-03-22 16:41+0000\n" -"Last-Translator: AHOHNMYC \n" +"PO-Revision-Date: 2023-07-08 18:47+0000\n" +"Last-Translator: Dmitriy Q \n" "Language-Team: Russian \n" "Language: ru\n" @@ -16,7 +16,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 4.16.2-dev\n" +"X-Generator: Weblate 5.0-dev\n" # Short break msgid "Tightly close your eyes" @@ -56,15 +56,15 @@ msgstr "Откиньтесь на спинку стула и расслабьт # Commandline arg description msgid "show the about dialog" -msgstr "О программе" +msgstr "показать окно \"о программе\"" # Commandline arg description msgid "disable the currently running safeeyes instance" -msgstr "Отключить Safe Eyes" +msgstr "отключить текущий запущенный экземпляр Safe Eyes" # Commandline arg description msgid "enable the currently running safeeyes instance" -msgstr "Активировать Safe Eyes" +msgstr "активировать текущий запущенный экземпляр Safe Eyes" # Commandline arg description msgid "quit the running safeeyes instance and exit" @@ -72,7 +72,7 @@ msgstr "Закрыть Safe Eyes и выйти" # Commandline arg description msgid "show the settings dialog" -msgstr "Настройки" +msgstr "показать окно настроек" # Commandline arg description msgid "start safeeyes in debug mode" From dc0a470c34c76d990690a15f6590387ebdb3ee93 Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Thu, 3 Aug 2023 17:16:06 +0200 Subject: [PATCH 02/22] Make GitHub Actions check translation files (#534) * validate_po.py: Fix typo "varialbes" * validate_po.py: Exit with code 1 on error * validate_po.py: Process locales in alphabetical order * validate_po.py: Report on progress * Make GitHub Actions check translation files --- .github/workflows/translations.yml | 31 ++++++++++++++++++++++++++++++ validate_po.py | 15 +++++++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/translations.yml diff --git a/.github/workflows/translations.yml b/.github/workflows/translations.yml new file mode 100644 index 00000000..0a1cde13 --- /dev/null +++ b/.github/workflows/translations.yml @@ -0,0 +1,31 @@ +name: Check translations + +# Drop permissions to minimum for security +permissions: + contents: read + +on: + pull_request: + push: + workflow_dispatch: + +jobs: + check_translations: + name: Check translations + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Python 3.11 + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Install runtime dependencies + run: | + python3 -m pip install --upgrade pip setuptools wheel + python3 -m pip install polib + + - name: Check translations + run: | + python3 validate_po.py diff --git a/validate_po.py b/validate_po.py index 7865a6d2..f37150a4 100644 --- a/validate_po.py +++ b/validate_po.py @@ -19,17 +19,24 @@ import os import polib +import sys -def validate_po(locale, path): +def validate_po(locale: str, path: str) -> bool: + success = True po = polib.pofile(path) for entry in po: if entry.msgstr and (entry.msgid.count("%") != entry.msgstr.count("%")): - print("Number of varialbes mismatched in " + locale) + print("Number of variables mismatched in " + locale) print(entry.msgid + " -> " + entry.msgstr) print() + success = False + return success +success = True locales = os.listdir('safeeyes/config/locale') -for locale in locales: +for locale in sorted(locales): path = os.path.join('safeeyes/config/locale', locale, "LC_MESSAGES/safeeyes.po") if os.path.isfile(path): - validate_po(locale, path) \ No newline at end of file + print('Validating translation %s...' % path) + success = validate_po(locale, path) and success +sys.exit(0 if success else 1) From b04d49ed349dce6c50168207fd59e040e997bf5c Mon Sep 17 00:00:00 2001 From: Salif Mehmed Date: Fri, 4 Aug 2023 16:58:26 +0000 Subject: [PATCH 03/22] Translated using Weblate (Bulgarian) Currently translated at 7.7% (9 of 116 strings) Translation: Safe Eyes/Translations Translate-URL: https://hosted.weblate.org/projects/safe-eyes/translations/bg/ --- .../config/locale/bg/LC_MESSAGES/safeeyes.po | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/safeeyes/config/locale/bg/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/bg/LC_MESSAGES/safeeyes.po index b78ee945..4c9ff093 100644 --- a/safeeyes/config/locale/bg/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/bg/LC_MESSAGES/safeeyes.po @@ -6,19 +6,20 @@ msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" +"PO-Revision-Date: 2023-08-05 18:48+0000\n" +"Last-Translator: Salif Mehmed \n" +"Language-Team: Bulgarian \n" "Language: bg\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 1.8.7.1\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.0-dev\n" # Short break msgid "Tightly close your eyes" -msgstr "" +msgstr "Затворете плътно очи" # Short break msgid "Roll your eyes a few times to each side" @@ -92,7 +93,7 @@ msgstr "" # About dialog msgid "Close" -msgstr "" +msgstr "Затвори" # Description in about dialog # Safe Eyes protects your eyes from eye strain (asthenopia) by reminding you to take breaks while you're working long hours at the computer @@ -103,15 +104,15 @@ msgstr "" # About dialog msgid "License" -msgstr "" +msgstr "Лиценз" # Break screen msgid "Skip" -msgstr "" +msgstr "Пропусни" # Break screen msgid "Postpone" -msgstr "" +msgstr "Отложи" # Settings dialog msgid "Break duration (in seconds)" @@ -171,7 +172,7 @@ msgstr "" # Settings dialog msgid "Options" -msgstr "" +msgstr "Опции" # Settings dialog msgid "Short Breaks" @@ -283,7 +284,7 @@ msgstr "" # Settings dialog msgid "Remove" -msgstr "" +msgstr "Премахване" # Settings dialog msgid "Discard" @@ -419,7 +420,7 @@ msgstr "" #: plugins/trayicon msgid "About" -msgstr "" +msgstr "Относно" #: plugins/trayicon msgid "Disable Safe Eyes" @@ -465,7 +466,7 @@ msgstr "" #: plugins/trayicon msgid "Settings" -msgstr "" +msgstr "Настройки" #: plugins/trayicon msgid "Take a break now" From 068771d5fdc301af9005d296d1832b0a7bc5c16f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20=C5=A0alka?= Date: Wed, 9 Aug 2023 18:56:58 +0000 Subject: [PATCH 04/22] Translated using Weblate (Slovak) Currently translated at 100.0% (116 of 116 strings) Translation: Safe Eyes/Translations Translate-URL: https://hosted.weblate.org/projects/safe-eyes/translations/sk/ --- safeeyes/config/locale/sk/LC_MESSAGES/safeeyes.po | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/safeeyes/config/locale/sk/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/sk/LC_MESSAGES/safeeyes.po index a38b73fb..7556efc8 100644 --- a/safeeyes/config/locale/sk/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/sk/LC_MESSAGES/safeeyes.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2022-04-17 16:06+0000\n" -"Last-Translator: menom \n" +"PO-Revision-Date: 2023-08-10 19:53+0000\n" +"Last-Translator: Milan Šalka \n" "Language-Team: Slovak \n" "Language: sk\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" -"X-Generator: Weblate 4.12-dev\n" +"X-Generator: Weblate 5.0-dev\n" # Short break msgid "Tightly close your eyes" @@ -105,7 +105,7 @@ msgid "" msgstr "" "Safe Eyes vám pripomenie, aby ste si urobili prestávku počas dlhých hodín " "stravených za počítačom. Chráni tak vaše oči pred prílišným namáhaním " -"(asthenopiou)." +"(asthenopiou)" # About dialog msgid "License" From 5a993a4d60dc5d43cfed1a017b6155d1c0b5522c Mon Sep 17 00:00:00 2001 From: Ilario Gelmetti Date: Fri, 18 Aug 2023 17:15:30 +0200 Subject: [PATCH 05/22] Fix broken startup symlinks (#531) * utility: create_startup_entry check link health * model: init always run create_startup_entry * utility: initialize_safeeyes don't call create_startup_entry * utility: add force option to create_startup_entry * model: do not create autostart, only repair broken * utility: fix comment --- safeeyes/model.py | 2 ++ safeeyes/utility.py | 48 +++++++++++++++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/safeeyes/model.py b/safeeyes/model.py index 63c88e8f..34d5150b 100644 --- a/safeeyes/model.py +++ b/safeeyes/model.py @@ -303,6 +303,8 @@ def __init__(self, init=True): # self.__force_upgrade = ['long_breaks', 'short_breaks'] if init: + # if create_startup_entry finds a broken autostart symlink, it will repair it + utility.create_startup_entry(force=False) if self.__user_config is None: utility.initialize_safeeyes() self.__user_config = self.__system_config diff --git a/safeeyes/utility.py b/safeeyes/utility.py index 76803e50..5b6cadd9 100644 --- a/safeeyes/utility.py +++ b/safeeyes/utility.py @@ -391,27 +391,51 @@ def initialize_safeeyes(): shutil.copy2(SYSTEM_STYLE_SHEET_PATH, STYLE_SHEET_PATH) os.chmod(STYLE_SHEET_PATH, 0o777) - create_startup_entry() - + # initialize_safeeyes gets called when the configuration file is not present, which happens just after installation or manual deletion of .config/safeeyes/safeeyes.json file. In these cases, we want to force the creation of a startup entry + create_startup_entry(force=True) -def create_startup_entry(): +def create_startup_entry(force=False): """ Create start up entry. """ startup_dir_path = os.path.join(HOME_DIRECTORY, '.config/autostart') startup_entry = os.path.join(startup_dir_path, 'io.github.slgobinath.SafeEyes.desktop') + # until SafeEyes 2.1.5 the startup entry had another name + # https://github.com/slgobinath/SafeEyes/commit/684d16265a48794bb3fd670da67283fe4e2f591b#diff-0863348c2143a4928518a4d3661f150ba86d042bf5320b462ea2e960c36ed275L398 + obsolete_entry = os.path.join(startup_dir_path, 'safeeyes.desktop') - # Create the folder if not exist - mkdir(startup_dir_path) + create_link = False - # Remove existing files - delete(startup_entry) + if force: + # if force is True, just create the link + create_link = True + else: + # if force is False, we want to avoid creating the startup symlink if it was manually deleted by the user, we want to create it only if a broken one is found + if os.path.islink(startup_entry): + # if the link exists, check if it is broken + try: + os.stat(startup_entry) + except FileNotFoundError: + # a FileNotFoundError will get thrown if the startup symlink is broken + create_link = True - # Create the new startup entry - try: - os.symlink(SYSTEM_DESKTOP_FILE, startup_entry) - except OSError: - logging.error("Failed to create startup entry at %s" % startup_entry) + if os.path.islink(obsolete_entry): + # if a link with the old naming exists, delete it and create a new one + create_link = True + delete(obsolete_entry) + + if create_link: + # Create the folder if not exist + mkdir(startup_dir_path) + + # Remove existing files + delete(startup_entry) + + # Create the new startup entry + try: + os.symlink(SYSTEM_DESKTOP_FILE, startup_entry) + except OSError: + logging.error("Failed to create startup entry at %s" % startup_entry) def initialize_platform(): From a33f3172415812add2a9ff3b0dd064ac58759f43 Mon Sep 17 00:00:00 2001 From: Novak Urosevic Date: Wed, 11 Oct 2023 12:53:30 +0000 Subject: [PATCH 06/22] Translated using Weblate (Serbian) Currently translated at 100.0% (116 of 116 strings) Translation: Safe Eyes/Translations Translate-URL: https://hosted.weblate.org/projects/safe-eyes/translations/sr/ --- .../config/locale/sr/LC_MESSAGES/safeeyes.po | 252 +++++++++--------- 1 file changed, 132 insertions(+), 120 deletions(-) diff --git a/safeeyes/config/locale/sr/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/sr/LC_MESSAGES/safeeyes.po index 5b323a14..fa06b35b 100644 --- a/safeeyes/config/locale/sr/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/sr/LC_MESSAGES/safeeyes.po @@ -6,94 +6,97 @@ msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" +"PO-Revision-Date: 2023-10-13 04:15+0000\n" +"Last-Translator: Novak Urosevic \n" +"Language-Team: Serbian \n" "Language: sr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 2.3\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 5.1-dev\n" # Short break msgid "Tightly close your eyes" -msgstr "" +msgstr "Чврсто затворите очи" # Short break msgid "Roll your eyes a few times to each side" -msgstr "" +msgstr "Колутајте очима „исцртавајући“ број 8 неколико пута" # Short break msgid "Rotate your eyes in clockwise direction" -msgstr "" +msgstr "Колутајте очи у круг у смеру казаљке на сату" # Short break msgid "Rotate your eyes in counterclockwise direction" -msgstr "" +msgstr "Колутајте очи у круг у смеру супротном од казаљке на сату" # Short break msgid "Blink your eyes" -msgstr "" +msgstr "Трепћите очима" # Short break msgid "Focus on a point in the far distance" -msgstr "" +msgstr "Усмерите поглед на тачку у даљини" # Short break msgid "Have some water" -msgstr "" +msgstr "Попијте воду" # Long break msgid "Walk for a while" -msgstr "" +msgstr "Протегните се" # Long break msgid "Lean back at your seat and relax" -msgstr "" +msgstr "Завалите се у фотељу и опустите се" # Commandline arg description msgid "show the about dialog" -msgstr "" +msgstr "Прикажи о нама прозор" # Commandline arg description msgid "disable the currently running safeeyes instance" -msgstr "" +msgstr "Онемогућите тренутну сесију safeeyes-a" # Commandline arg description msgid "enable the currently running safeeyes instance" -msgstr "" +msgstr "Омогућите тренутну сесију safeeyes-a" # Commandline arg description msgid "quit the running safeeyes instance and exit" -msgstr "" +msgstr "Искључите тренутну сесију safeeyes-а и затворите програм" # Commandline arg description msgid "show the settings dialog" -msgstr "" +msgstr "Прикажи прозор са подешавањима" # Commandline arg description msgid "start safeeyes in debug mode" -msgstr "" +msgstr "Покрените safeeyes у режиму за исправљање грешака" # Commandline arg description msgid "print the status of running safeeyes instance and exit" -msgstr "" +msgstr "Одштампајте статус тренутне сесије safeeyes-a и напустите" # Status message msgid "Safe Eyes is not running" -msgstr "" +msgstr "Safe Eyes није укључен" # RPC not enabled message msgid "" "Safe Eyes is running without an RPC server. Turn it on to use command-line " "arguments." msgstr "" +"Safe Eyes ради без RPC (Remote Procedure Call) сервера. Укључите га како " +"бисте користили командне-линије." # About dialog msgid "Close" -msgstr "" +msgstr "Затворите" # Description in about dialog # Safe Eyes protects your eyes from eye strain (asthenopia) by reminding you to take breaks while you're working long hours at the computer @@ -101,396 +104,405 @@ msgid "" "Safe Eyes protects your eyes from eye strain (asthenopia) by reminding you " "to take breaks while you're working long hours at the computer" msgstr "" +"Safe Eyes штити Ваше очи од напрезања (astenopija) подсећајући Вас да " +"правите паузе током вишесатне употребе компјутера" # About dialog msgid "License" -msgstr "" +msgstr "Лиценца" # Break screen msgid "Skip" -msgstr "" +msgstr "Прескочите" # Break screen msgid "Postpone" -msgstr "" +msgstr "Одложите" # Settings dialog msgid "Break duration (in seconds)" -msgstr "" +msgstr "Време трајања паузе (у секундама)" # Settings dialog msgid "Interval between two breaks (in minutes)" -msgstr "" +msgstr "Временски период између две паузе (у минутима)" # Settings dialog msgid "Time to prepare for a break (in seconds)" -msgstr "" +msgstr "Време припреме за паузу (у секундама)" # Settings dialog msgid "Keyboard shortcuts disabled period (in seconds)" -msgstr "" +msgstr "Временски период искључења пречица тастатуре (у секундама)" # Settings dialog msgid "Postponement duration (in minutes)" -msgstr "" +msgstr "Дужина трајања Одлагања (у минутима)" # Settings dialog msgid "Show breaks in random order" -msgstr "" +msgstr "Прикажите паузе насумичним редоследом" # Settings dialog msgid "Strict break (No way to skip breaks)" -msgstr "" +msgstr "Обавезна пауза (без могућности одлагања пауза)" # Settings dialog msgid "Allow postponing breaks" -msgstr "" +msgstr "Дозволи одлагање пауза" # Settings dialog msgid "Persist the internal state" -msgstr "" +msgstr "Задржи интерни статус" # Settings dialog msgid "Use RPC server to receive runtime commands" -msgstr "" +msgstr "Користите RPC сервер како бисте примили runtime команде" # Settings dialog msgid "Without the RPC server, command-line commands may not work" -msgstr "" +msgstr "Без RPC сервера, унос путем командних-линија можда неће бити успешан" # Settings dialog msgid "Long break interval must be a multiple of short break interval" msgstr "" +"Интервали између дугих пауза морају бити неколико пута дужи од интервала " +"између кратких пауза" # Settings dialog msgid "Reset" -msgstr "" +msgstr "Поново покрените" # Settings dialog msgid "Are you sure you want to reset all settings to default?" msgstr "" +"Да ли сигурно желите да вратите сва подешавања на подразумевана подешавања?" # Settings dialog msgid "Options" -msgstr "" +msgstr "Опције" # Settings dialog msgid "Short Breaks" -msgstr "" +msgstr "Кратке Паузе" # Settings dialog msgid "Long Breaks" -msgstr "" +msgstr "Дуге Паузе" # Settings dialog msgid "Delete" -msgstr "" +msgstr "Обришите" # Settings dialog msgid "Are you sure you want to delete this break?" -msgstr "" +msgstr "Да ли сигурно желите да обришете ову паузу?" # Settings dialog msgid "You can't undo this action." -msgstr "" +msgstr "Не можете опозвати радњу." # Settings dialog msgid "Break" -msgstr "" +msgstr "Пауза" # Settings dialog msgid "Breaks" -msgstr "" +msgstr "Паузе" # Settings dialog msgid "Plugins" -msgstr "" +msgstr "Додаци" # Settings dialog msgid "Type" -msgstr "" +msgstr "Укуцајте" # Settings dialog msgid "Short" -msgstr "" +msgstr "Кратка" # Settings dialog msgid "Long" -msgstr "" +msgstr "Дуга" # Settings dialog msgid "Image" -msgstr "" +msgstr "Слика" # Settings dialog msgid "Select" -msgstr "" +msgstr "Изаберите" # Settings dialog msgid "Please select an image" -msgstr "" +msgstr "Молимо Вас изаберите слику" # Settings dialog msgid "Duration" -msgstr "" +msgstr "Дужина трајања" # Settings dialog msgid "Time to wait" -msgstr "" +msgstr "Време чекања" # Settings dialog msgid "Override" -msgstr "" +msgstr "Преписати" # Settings dialog msgid "Time (in seconds)" -msgstr "" +msgstr "Време (у секундама)" # Settings dialog msgid "Time (in minutes)" -msgstr "" +msgstr "Време (у минутима)" # Settings dialog msgid "Break Settings" -msgstr "" +msgstr "Подешавање паузе" # Settings dialog msgid "Plugin Settings" -msgstr "" +msgstr "Подешавање додатака" # Settings dialog msgid "Plugin does not support %s desktop environment" -msgstr "" +msgstr "Додатак не подржава % радну површину" # Settings dialog msgid "Please install the Python module '%s'" -msgstr "" +msgstr "Молимо Вас, преузмите модул '%s' за Пајтон" # Settings dialog msgid "Please install the command-line tool '%s'" -msgstr "" +msgstr "Молимо Вас, преузмите алатку командне-линије '%s'" # Settings dialog msgid "Invalid cron expression '%s'" -msgstr "" +msgstr "Неправилан формат за извршавање cron посла '%s'" # Settings dialog msgid "Please add the resource %(resource)s to %(config_resource)s directory" msgstr "" +"Молимо Вас, додајте ресурс %(resource)s у %(config_resource)s директоријум" # Settings dialog msgid "New Break" -msgstr "" +msgstr "Нова Пауза" # Settings dialog msgid "Remove" -msgstr "" +msgstr "Уклоните" # Settings dialog msgid "Discard" -msgstr "" +msgstr "Одбаците" # Settings dialog msgid "Save" -msgstr "" +msgstr "Сачувајте" # plugin/audiblealert msgid "Audible Alert" -msgstr "" +msgstr "Звучно Обавештење" # plugin/audiblealert msgid "Play audible alert before and after breaks" -msgstr "" +msgstr "Пустите звучно обавештење пре и после пауза" # plugin/audiblealert msgid "Play audible alert before breaks" -msgstr "" +msgstr "Пустите звучно обавештење пре пауза" # plugin/audiblealert msgid "Play audible alert after breaks" -msgstr "" +msgstr "Пустите звучно овавештење након пауза" # plugin/donotdisturb msgid "Do Not Disturb" -msgstr "" +msgstr "Не Узнемиравај" # plugin/donotdisturb msgid "Skip break if the active window is in fullscreen mode" -msgstr "" +msgstr "Прескочите паузу уколико је тренутни радни прозор у пуном екрану" # plugin/donotdisturb msgid "Do not interrupt these windows anytime" -msgstr "" +msgstr "Никада не ометајте ове радне прозоре" # plugin/donotdisturb msgid "Interrupt these windows regardless of their state" -msgstr "" +msgstr "Ометајте ове радне прозоре без обзира на њихово стање" # plugin/donotdisturb msgid "Switch the interruptible windows to normal mode" -msgstr "" +msgstr "Промените променљив радни прозор у нормалан режим" # plugin/donotdisturb msgid "Do not disturb while on battery" -msgstr "" +msgstr "Не Узнемиравај док компјутер ради са батерије" # plugin/healthstats msgid "Health Statistics" -msgstr "" +msgstr "Здравствена статистика" # plugin/healthstats msgid "Show statistics based on how you use Safe Eyes" -msgstr "" +msgstr "Покажи статистику на основу Ваше употребе Safe Eyes-a" # plugin/healthstats msgid "Statistics reset interval (cron expression)" -msgstr "" +msgstr "Интервал ресетовања Података (cron послова)" # plugin/notification msgid "Notification" -msgstr "" +msgstr "Обавештење" # plugin/notification msgid "Show a system notification before breaks" -msgstr "" +msgstr "Покажите системско обавештење пре почетка пауза" # plugin/notification msgid "Ready for a short break in %s seconds" -msgstr "" +msgstr "Спремите се за кратку паузу у року од %s секунди" # plugin/notification msgid "Ready for a long break in %s seconds" -msgstr "" +msgstr "Спремите се за дугу паузу у року од %s секунди" # plugin/screensaver msgid "Screensaver" -msgstr "" +msgstr "Чувар екрана" # plugin/screensaver msgid "Lock the screen after long breaks by starting screensaver" msgstr "" +"Закључајте екран за време дугих паузи тако што ћете укључити чувар екрана" # plugin/screensaver msgid "Custom screensaver command" -msgstr "" +msgstr "Команда за чувар екрана по жељи" # plugin/screensaver msgid "Minimum seconds to skip without screensaver" -msgstr "" +msgstr "Најмањи број секунди за прескакање без чувара екрана" # plugin/screensaver msgid "Lock screen" -msgstr "" +msgstr "Закључајте екран" # plugin/smartpause msgid "Smart Pause" -msgstr "" +msgstr "Паметна Пауза" # plugin/smartpause msgid "Pause Safe Eyes if the system is idle" -msgstr "" +msgstr "Зауставите Safe-Eyes уколико систем мирује" # plugin/smartpause msgid "Minimum idle time to pause Safe Eyes (in seconds)" msgstr "" +"Најкраћи временски период потребан за заустављање Safe Eyes-a (у секундама)" # plugin/smartpause msgid "Interpret idle time equivalent to upcoming break duration as a break" msgstr "" +"Протумачите време мировања еквивалентно трајању предстојеће паузе као паузу" # plugin/smartpause msgid "Postpone the next break until the system becomes idle" -msgstr "" +msgstr "Одложите наредну паузу док систем не постане неактиван" #: plugins/trayicon msgid "Tray Icon" -msgstr "" +msgstr "Иконица на панелу" #: plugins/trayicon msgid "Show a tray icon in the notification area" -msgstr "" +msgstr "Покажите иконицу на панелу у Обавештењима" #: plugins/trayicon msgid "Show next break time in tray icon" -msgstr "" +msgstr "Покажите време следеће паузе на иконици на панелу" #: plugins/trayicon msgid "Allow disabling Safe Eyes" -msgstr "" +msgstr "Дозволи онемогућавање Safe Eyes-a" #: plugins/trayicon msgid "About" -msgstr "" +msgstr "О програму" #: plugins/trayicon msgid "Disable Safe Eyes" -msgstr "" +msgstr "Онемогући Safe Eyes" #: plugins/trayicon msgid "Disabled until %s" -msgstr "" +msgstr "Онемогућено до %s" #: plugins/trayicon msgid "Disabled until restart" -msgstr "" +msgstr "Онемогућено до ресетовања" #: plugins/trayicon msgid "Enable Safe Eyes" -msgstr "" +msgstr "Омогући Safe Eyes" #: plugins/trayicon msgid "For %d Hour" msgid_plural "For %d Hours" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "сат" +msgstr[1] "сати" +msgstr[2] "сати" #: plugins/trayicon msgid "For %d Minute" msgid_plural "For %d Minutes" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "минут" +msgstr[1] "минута" +msgstr[2] "минута" #: plugins/trayicon msgid "For %d Second" msgid_plural "For %d Seconds" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "секунда" +msgstr[1] "секунде" +msgstr[2] "секунди" #: plugins/trayicon msgid "Next break at %s" -msgstr "" +msgstr "Наредна пауза је у %s" #: plugins/trayicon msgid "No Breaks Available" -msgstr "" +msgstr "Нема доступних Пауза" #: plugins/trayicon msgid "Settings" -msgstr "" +msgstr "Подешавања" #: plugins/trayicon msgid "Take a break now" -msgstr "" +msgstr "Сада направи паузу" #: plugins/trayicon msgid "Until restart" -msgstr "" +msgstr "До ресетовања" #: plugins/trayicon msgid "Quit" -msgstr "" +msgstr "Изађите" # plugin/mediacontrol msgid "Media Control" -msgstr "" +msgstr "Контрола медија" # plugin/mediacontrol msgid "Pause media players from the break screen" -msgstr "" +msgstr "Зауставите рад медијских плејера са екрана Паузе" # plugin/mediacontrol msgid "Pause media" -msgstr "" +msgstr "Паузирај медије" From 776a8b81de01a4a290fb2d596f5004309d56de47 Mon Sep 17 00:00:00 2001 From: akhil_s Date: Sun, 1 Oct 2023 02:33:12 +0530 Subject: [PATCH 07/22] Fix 'initialize_logging' function spelling --- safeeyes/__main__.py | 2 +- safeeyes/utility.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/safeeyes/__main__.py b/safeeyes/__main__.py index 524ad96d..2eab4cd7 100755 --- a/safeeyes/__main__.py +++ b/safeeyes/__main__.py @@ -111,7 +111,7 @@ def main(): args = parser.parse_args() # Initialize the logging - utility.intialize_logging(args.debug) + utility.initialize_logging(args.debug) utility.initialize_platform() config = Config() diff --git a/safeeyes/utility.py b/safeeyes/utility.py index 5b6cadd9..e145d4dc 100644 --- a/safeeyes/utility.py +++ b/safeeyes/utility.py @@ -515,7 +515,7 @@ def replace_style_sheet(): os.chmod(STYLE_SHEET_PATH, 0o777) -def intialize_logging(debug): +def initialize_logging(debug): """ Initialize the logging framework using the Safe Eyes specific configurations. """ From c37e9db6dba27e36afc5a865c5a17e27e322e23a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Jel=C3=ADnek?= Date: Mon, 30 Oct 2023 15:49:42 +0000 Subject: [PATCH 08/22] Translated using Weblate (Czech) Currently translated at 100.0% (116 of 116 strings) Translation: Safe Eyes/Translations Translate-URL: https://hosted.weblate.org/projects/safe-eyes/translations/cs/ --- safeeyes/config/locale/cs/LC_MESSAGES/safeeyes.po | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/safeeyes/config/locale/cs/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/cs/LC_MESSAGES/safeeyes.po index fe7a627a..381d1694 100644 --- a/safeeyes/config/locale/cs/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/cs/LC_MESSAGES/safeeyes.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2022-03-06 20:59+0000\n" -"Last-Translator: Pavel Borecki \n" +"PO-Revision-Date: 2023-10-31 16:04+0000\n" +"Last-Translator: Kryštof Jelínek \n" "Language-Team: Czech \n" "Language: cs\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" -"X-Generator: Weblate 4.12-dev\n" +"X-Generator: Weblate 5.2-dev\n" # Short break msgid "Tightly close your eyes" @@ -23,15 +23,15 @@ msgstr "Zavřete oči" # Short break msgid "Roll your eyes a few times to each side" -msgstr "Zakroužete očima (několikrát na každou stranu)" +msgstr "Zakružte očima (několikrát na každou stranu)" # Short break msgid "Rotate your eyes in clockwise direction" -msgstr "Zakroužete očima ve směru hodinových ručiček" +msgstr "Zakružte očima ve směru hodinových ručiček" # Short break msgid "Rotate your eyes in counterclockwise direction" -msgstr "Zakroužete očima proti směru hodinových ručiček" +msgstr "Zakružte očima proti směru hodinových ručiček" # Short break msgid "Blink your eyes" From e8a254d74e01add639cd2b1d64e80c1c6bd2ff2e Mon Sep 17 00:00:00 2001 From: Eryk Michalak Date: Sun, 29 Oct 2023 15:09:26 +0000 Subject: [PATCH 09/22] Translated using Weblate (Polish) Currently translated at 100.0% (116 of 116 strings) Translation: Safe Eyes/Translations Translate-URL: https://hosted.weblate.org/projects/safe-eyes/translations/pl/ --- .../config/locale/pl/LC_MESSAGES/safeeyes.po | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/safeeyes/config/locale/pl/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/pl/LC_MESSAGES/safeeyes.po index 87a063f8..d0a5ffed 100644 --- a/safeeyes/config/locale/pl/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/pl/LC_MESSAGES/safeeyes.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2020-04-03 10:36+0000\n" -"Last-Translator: Michal Biesiada \n" +"PO-Revision-Date: 2023-10-31 16:04+0000\n" +"Last-Translator: Eryk Michalak \n" "Language-Team: Polish \n" "Language: pl\n" @@ -16,7 +16,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 4.0-dev\n" +"X-Generator: Weblate 5.2-dev\n" # Short break msgid "Tightly close your eyes" @@ -141,7 +141,7 @@ msgstr "Czas przełożenia (w minutach)" # Settings dialog msgid "Show breaks in random order" -msgstr "" +msgstr "Wyświetlaj przerwy w losowej kolejności" # Settings dialog msgid "Strict break (No way to skip breaks)" @@ -169,11 +169,11 @@ msgstr "Długa przerwa musi być poprzedzona kilkoma krótkimi" # Settings dialog msgid "Reset" -msgstr "Zresetuj ustawienia" +msgstr "Resetuj" # Settings dialog msgid "Are you sure you want to reset all settings to default?" -msgstr "Czy jesteś pewien, że chcesz przywrócić ustawienia domyślne?" +msgstr "Czy chcesz przywrócić ustawienia domyślne?" # Settings dialog msgid "Options" @@ -193,11 +193,11 @@ msgstr "Usuń" # Settings dialog msgid "Are you sure you want to delete this break?" -msgstr "Czy jesteś pewien, że chcesz usunąć tę przerwę?" +msgstr "Czy na pewno chcesz usunąć tę przerwę?" # Settings dialog msgid "You can't undo this action." -msgstr "Nie możesz cofnąć tej akcji." +msgstr "Nie można cofnąć tej operacji." # Settings dialog msgid "Break" @@ -277,7 +277,7 @@ msgstr "Proszę zainstalować wiersz poleceń '%s'" # Settings dialog msgid "Invalid cron expression '%s'" -msgstr "" +msgstr "Nieprawidłowe wyrażenie cron '%s'" # Settings dialog msgid "Please add the resource %(resource)s to %(config_resource)s directory" @@ -349,7 +349,7 @@ msgstr "Pokaż statystyki oparte o Safe Eyes" # plugin/healthstats msgid "Statistics reset interval (cron expression)" -msgstr "" +msgstr "Okres resetowania statystyk (wyrażenie cron)" # plugin/notification msgid "Notification" @@ -480,7 +480,7 @@ msgstr "Ustawienia" #: plugins/trayicon msgid "Take a break now" -msgstr "Przerwa" +msgstr "Zrób sobie teraz przerwę" #: plugins/trayicon msgid "Until restart" From 0cce71070b2c8e6bcbef02471b068c21de894415 Mon Sep 17 00:00:00 2001 From: Almaz Mannanov Date: Fri, 24 Nov 2023 23:12:17 +0000 Subject: [PATCH 10/22] Translated using Weblate (Russian) Currently translated at 100.0% (116 of 116 strings) Translation: Safe Eyes/Translations Translate-URL: https://hosted.weblate.org/projects/safe-eyes/translations/ru/ --- safeeyes/config/locale/ru/LC_MESSAGES/safeeyes.po | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/safeeyes/config/locale/ru/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/ru/LC_MESSAGES/safeeyes.po index 34b55c8c..f9fc80b0 100644 --- a/safeeyes/config/locale/ru/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/ru/LC_MESSAGES/safeeyes.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-07-08 18:47+0000\n" -"Last-Translator: Dmitriy Q \n" +"PO-Revision-Date: 2023-11-26 00:02+0000\n" +"Last-Translator: Almaz Mannanov \n" "Language-Team: Russian \n" "Language: ru\n" @@ -16,7 +16,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 5.0-dev\n" +"X-Generator: Weblate 5.2.1-rc\n" # Short break msgid "Tightly close your eyes" @@ -290,7 +290,7 @@ msgstr "Новый перерыв" # Settings dialog msgid "Remove" -msgstr "Удалить" +msgstr "Стереть" # Settings dialog msgid "Discard" From 9d30def057718c09db12a5d5389151aa893be590 Mon Sep 17 00:00:00 2001 From: AdamPS <5490095+AdamPS@users.noreply.github.com> Date: Thu, 2 Feb 2023 19:19:41 +0000 Subject: [PATCH 11/22] Display next long break #374 --- safeeyes/config/safeeyes.json | 1 + safeeyes/core.py | 10 ++++++++++ safeeyes/model.py | 4 ++++ safeeyes/plugins/trayicon/config.json | 8 +++++++- safeeyes/plugins/trayicon/plugin.py | 23 ++++++++++++++++++----- safeeyes/safeeyes.py | 1 + 6 files changed, 41 insertions(+), 6 deletions(-) diff --git a/safeeyes/config/safeeyes.json b/safeeyes/config/safeeyes.json index 5774c8bc..dfa48199 100644 --- a/safeeyes/config/safeeyes.json +++ b/safeeyes/config/safeeyes.json @@ -77,6 +77,7 @@ "version": "0.0.3", "settings": { "show_time_in_tray": false, + "show_long_time_in_tray": false, "allow_disabling": true, "disable_options": [{ "time": 30, diff --git a/safeeyes/core.py b/safeeyes/core.py index 00a7f980..a73592c1 100644 --- a/safeeyes/core.py +++ b/safeeyes/core.py @@ -131,6 +131,16 @@ def postpone(self, duration=-1): logging.debug("Postpone the break for %d seconds", self.postpone_duration) self.context['postponed'] = True + def get_break_time(self, break_type = None): + """ + Returns the next break time + """ + break_obj = self.break_queue.get_break(break_type) + if not break_obj: + return False + time = self.scheduled_next_break_time + datetime.timedelta(minutes=break_obj.time - self.break_queue.get_break().time) + return time + def take_break(self, break_type = None): """ Calling this method stops the scheduler and show the next break screen diff --git a/safeeyes/model.py b/safeeyes/model.py index 34d5150b..fe8e026f 100644 --- a/safeeyes/model.py +++ b/safeeyes/model.py @@ -120,8 +120,12 @@ def get_break(self, break_type = None): return self.__current_break if break_type == BreakType.LONG_BREAK: + if self.__long_queue is None: + return None; return self.__long_queue[self.__current_long] + if self.__short_queue is None: + return None; return self.__short_queue[self.__current_short] def is_long_break(self): diff --git a/safeeyes/plugins/trayicon/config.json b/safeeyes/plugins/trayicon/config.json index 28d1b352..5ed2e5b1 100644 --- a/safeeyes/plugins/trayicon/config.json +++ b/safeeyes/plugins/trayicon/config.json @@ -18,6 +18,12 @@ "type": "BOOL", "default": false }, + { + "id": "show_long_time_in_tray", + "label": "Show only long breaks for tray icon time", + "type": "BOOL", + "default": false + }, { "id": "allow_disabling", "label": "Allow disabling Safe Eyes", @@ -48,4 +54,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/safeeyes/plugins/trayicon/plugin.py b/safeeyes/plugins/trayicon/plugin.py index 061535a5..9970afe3 100644 --- a/safeeyes/plugins/trayicon/plugin.py +++ b/safeeyes/plugins/trayicon/plugin.py @@ -57,6 +57,7 @@ def __init__(self, context, plugin_config): self.on_disable = context['api']['disable_safeeyes'] self.take_break = context['api']['take_break'] self.has_breaks = context['api']['has_breaks'] + self.get_break_time = context['api']['get_break_time'] self.plugin_config = plugin_config self.date_time = None self.active = True @@ -126,7 +127,7 @@ def __init__(self, context, plugin_config): # Add the sub menu to the enable/disable menu self.item_disable.set_submenu(self.sub_menu_disable) - # Settings menu item + # Manual break menu item self.item_manual_break = Gtk.MenuItem() self.sub_menu_manual_next_break = Gtk.MenuItem() @@ -215,7 +216,7 @@ def set_labels(self): self.indicator.set_label('', '') self.indicator.set_icon("io.github.slgobinath.SafeEyes-disabled") else: - self.item_info.set_label(_('No Breaks Available')) + self.item_info.set_label(_('No breaks available')) self.indicator.set_label('', '') self.indicator.set_icon("io.github.slgobinath.SafeEyes-disabled") self.item_info.set_sensitive(breaks_found and self.active) @@ -282,13 +283,25 @@ def __set_next_break_info(self): """ A private method to be called within this class to update the next break information using self.dateTime. """ - formatted_time = utility.format_time(self.date_time) - message = _('Next break at %s') % (formatted_time) + formatted_time = utility.format_time(self.get_break_time()) + long_time = self.get_break_time(BreakType.LONG_BREAK) + + if long_time: + long_time = utility.format_time(long_time) + if long_time == formatted_time: + message = _('Next long break at %s') % (long_time) + else: + message = _('Next breaks at %s/%s') % (formatted_time, long_time) + else: + message = _('Next break at %s') % (formatted_time) + # Update the menu item label utility.execute_main_thread(self.item_info.set_label, message) + # Update the tray icon label if self.plugin_config.get('show_time_in_tray', False): - self.indicator.set_label(formatted_time, '') + show_long = long_time and self.plugin_config.get('show_long_time_in_tray', False) + self.indicator.set_label(long_time if show_long else formatted_time, '') else: self.indicator.set_label('', '') diff --git a/safeeyes/safeeyes.py b/safeeyes/safeeyes.py index 7b88553e..ea9330f5 100644 --- a/safeeyes/safeeyes.py +++ b/safeeyes/safeeyes.py @@ -96,6 +96,7 @@ def __init__(self, system_locale, config): self.context['api']['take_break'] = self.take_break self.context['api']['has_breaks'] = self.safe_eyes_core.has_breaks self.context['api']['postpone'] = self.safe_eyes_core.postpone + self.context['api']['get_break_time'] = self.safe_eyes_core.get_break_time self.plugins_manager.init(self.context, self.config) atexit.register(self.persist_session) From dd190d0543c120eb2e053dbdd951dd7c6c4e90d5 Mon Sep 17 00:00:00 2001 From: AdamPS <5490095+AdamPS@users.noreply.github.com> Date: Sat, 4 Feb 2023 11:59:19 +0000 Subject: [PATCH 12/22] Update comment with list of methods --- safeeyes/plugin_manager.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/safeeyes/plugin_manager.py b/safeeyes/plugin_manager.py index 6dfaa7a0..e35c6921 100644 --- a/safeeyes/plugin_manager.py +++ b/safeeyes/plugin_manager.py @@ -27,18 +27,28 @@ The plugin.py can have following methods but all are optional: - description() If a custom description has to be displayed, use this function - - on_init(context, safeeyes_config, plugin_config) + - init(context, safeeyes_config, plugin_config) Initialize the plugin. Will be called after loading and after every changes in configuration - on_start() Executes when Safe Eyes is enabled - on_stop() Executes when Safe Eyes is disabled + - on_exit() + Executes before Safe Eyes exits + - on_pre_break(break_obj) + Executes at the start of the prepare time for a break + - on_start_break(break_obj) + Executes when a break starts + - on_stop_break() + Executes when a break stops + - on_countdown(countdown, seconds) + Executes every second throughout a break + - update_next_break(break_obj, break_time) + Executes when the next break changes - enable() Executes once the plugin.py is loaded as a module - disable() Executes if the plugin is disabled at the runtime by the user - - on_exit() - Executes before Safe Eyes exits """ import importlib From edb5b70b5300f0732cf6f653244354de280d82ac Mon Sep 17 00:00:00 2001 From: AdamPS <5490095+AdamPS@users.noreply.github.com> Date: Wed, 29 Nov 2023 13:59:21 +0000 Subject: [PATCH 13/22] Improvements to health statistics #549 --- safeeyes/config/safeeyes.json | 2 +- .../plugins/healthstats/dependency_checker.py | 16 -- safeeyes/plugins/healthstats/plugin.py | 228 ++++++++---------- 3 files changed, 99 insertions(+), 147 deletions(-) diff --git a/safeeyes/config/safeeyes.json b/safeeyes/config/safeeyes.json index dfa48199..a67490e7 100644 --- a/safeeyes/config/safeeyes.json +++ b/safeeyes/config/safeeyes.json @@ -122,7 +122,7 @@ "enabled": false, "version": "0.0.2", "settings": { - "statistics_reset_interval": 24 + "statistics_reset_cron": "0 0 * * *" } }, { diff --git a/safeeyes/plugins/healthstats/dependency_checker.py b/safeeyes/plugins/healthstats/dependency_checker.py index 16aa1b4b..4453819e 100644 --- a/safeeyes/plugins/healthstats/dependency_checker.py +++ b/safeeyes/plugins/healthstats/dependency_checker.py @@ -19,22 +19,6 @@ import datetime from safeeyes import utility -def _get_next_reset_time(current_time, statistics_reset_cron): - import croniter - try: - cron = croniter.croniter(statistics_reset_cron, current_time) - return cron.get_next(datetime.datetime) - except: - # Error in getting the next reset time - return None - def validate(plugin_config, plugin_settings): if not utility.module_exist("croniter"): return _("Please install the Python module '%s'") % "croniter" - - # Validate the cron expression - statistics_reset_cron = plugin_settings.get('statistics_reset_cron', '0 0 * * *') - if _get_next_reset_time(datetime.datetime.now(), statistics_reset_cron) is None: - return _("Invalid cron expression '%s'") % statistics_reset_cron - else: - return None diff --git a/safeeyes/plugins/healthstats/plugin.py b/safeeyes/plugins/healthstats/plugin.py index 041bf980..e1cae38a 100644 --- a/safeeyes/plugins/healthstats/plugin.py +++ b/safeeyes/plugins/healthstats/plugin.py @@ -25,188 +25,156 @@ import logging context = None -no_of_skipped_breaks = 0 -no_of_breaks = 0 session = None -safe_eyes_start_time = datetime.datetime.now() -total_idle_time = 0 -last_screen_time = -1 -statistics_reset_cron = '0 0 * * *' # Every midnight -time_to_reset_break = datetime.datetime.now() +statistics_reset_cron = None +default_statistics_reset_cron = '0 0 * * *' # Every midnight next_reset_time = None -enabled = True +start_time = None def init(ctx, safeeyes_config, plugin_config): """ Initialize the plugin. """ - global enabled global context global session - global no_of_skipped_breaks - global no_of_breaks global statistics_reset_cron - global safe_eyes_start_time - global total_idle_time - global last_screen_time - global next_reset_time logging.debug('Initialize Health Stats plugin') context = ctx - statistics_reset_cron = plugin_config.get('statistics_reset_cron', '0 0 * * *') - # Compute the next reset time - next_reset_time = _get_next_reset_time(datetime.datetime.now(), statistics_reset_cron) - enabled = next_reset_time is not None - - if not enabled: - # There is an error in the cron expression - logging.error("Error in parsing the cron expression `" + statistics_reset_cron + "`. Health Stats plugin is disabled.") - return + statistics_reset_cron = plugin_config.get('statistics_reset_cron', default_statistics_reset_cron) if session is None: # Read the session - session = context['session']['plugin'].get('healthstats', None) - if session is None: - session = {'no_of_skipped_breaks': 0, - 'no_of_breaks': 0, - 'safe_eyes_start_time': safe_eyes_start_time.strftime("%Y-%m-%d %H:%M:%S"), - 'total_idle_time': 0, - 'last_screen_time': -1, - 'next_reset_time': next_reset_time.strftime("%Y-%m-%d %H:%M:%S")} - context['session']['plugin']['healthstats'] = session - no_of_skipped_breaks = session.get('no_of_skipped_breaks', 0) - no_of_breaks = session.get('no_of_breaks', 0) - total_idle_time = session.get('total_idle_time', 0) - last_screen_time = session.get('last_screen_time', -1) - str_time = session.get('safe_eyes_start_time', None) - str_next_reset_time = session.get('next_reset_time', None) - if str_time: - safe_eyes_start_time = datetime.datetime.strptime(str_time, "%Y-%m-%d %H:%M:%S") - if str_next_reset_time: - next_reset_time = datetime.datetime.strptime(str_time, "%Y-%m-%d %H:%M:%S") - - _reset_stats() + defaults = { + 'breaks': 0, + 'skipped_breaks': 0, + 'screen_time': 0, + 'total_breaks': 0, + 'total_skipped_breaks': 0, + 'total_screen_time': 0, + 'total_resets': 0, + } + session = context['session']['plugin'].get('healthstats', {}) | defaults + if 'no_of_breaks' in session: + # Ignore old format session. + session = defaults + context['session']['plugin']['healthstats'] = session + + _get_next_reset_time() -def on_stop_break(): - """ - After the break, check if it is skipped. - """ - # Check if the plugin is enabled - if not enabled: - return - global no_of_skipped_breaks +def on_stop_break(): + # Check if break was skipped. + global session if context['skipped']: - no_of_skipped_breaks += 1 - session['no_of_skipped_breaks'] = no_of_skipped_breaks + session['skipped_breaks'] += 1 + + # Screen time is starting again. + on_start() + + +def on_start_break(break_obj): + global session + session['breaks'] += 1 + + # Screen time has stoped. + on_stop() + + +def on_stop(): + global start_time + _reset_stats() + if start_time: + screen_time = datetime.datetime.now() - start_time + session['screen_time'] += round(screen_time.total_seconds()) + start_time = None def get_widget_title(break_obj): """ Return the widget title. """ - # Check if the plugin is enabled - if not enabled: - return "" - - global no_of_breaks - no_of_breaks += 1 - session['no_of_breaks'] = no_of_breaks - session['safe_eyes_start_time'] = safe_eyes_start_time.strftime("%Y-%m-%d %H:%M:%S") - session['total_idle_time'] = total_idle_time - session['last_screen_time'] = last_screen_time return _('Health Statistics') def _reset_stats(): - global no_of_breaks - global safe_eyes_start_time - global total_idle_time - global no_of_skipped_breaks - global last_screen_time - global next_reset_time + global session # Check if the reset time has passed - current_time = datetime.datetime.now() - total_duration_sec = (current_time - safe_eyes_start_time).total_seconds() - if current_time >= next_reset_time: - logging.debug("Resetting the health statistics") - # Reset statistics - if safe_eyes_start_time < next_reset_time: - # Safe Eyes is running even before the reset time - # Consider the reset time as the new start time - safe_eyes_start_time = next_reset_time - total_duration_sec = (current_time - safe_eyes_start_time).total_seconds() + if next_reset_time and datetime.datetime.now() >= next_reset_time: + logging.info("Resetting the health statistics") # Update the next_reset_time - next_reset_time = _get_next_reset_time(current_time, statistics_reset_cron) - - last_screen_time = round((total_duration_sec - total_idle_time) / 60) - total_idle_time = 0 - no_of_breaks = 0 - no_of_skipped_breaks = 0 - session['no_of_breaks'] = 0 - session['no_of_skipped_breaks'] = 0 - session['safe_eyes_start_time'] = safe_eyes_start_time.strftime("%Y-%m-%d %H:%M:%S") - session['total_idle_time'] = total_idle_time - session['last_screen_time'] = last_screen_time - session['next_reset_time'] = next_reset_time.strftime("%Y-%m-%d %H:%M:%S") + _get_next_reset_time() - return total_duration_sec + # Reset statistics + session['total_breaks'] += session['breaks'] + session['total_skipped_breaks'] += session['skipped_breaks'] + session['total_screen_time'] += session['screen_time'] + session['total_resets'] += 1 + session['breaks'] = 0 + session['skipped_breaks'] = 0 + session['screen_time'] = 0 def get_widget_content(break_obj): """ Return the statistics. """ - # Check if the plugin is enabled - if not enabled: - return "" - - total_duration_sec = _reset_stats() - screen_time = round((total_duration_sec - total_idle_time) / 60) - hours, minutes = divmod(screen_time, 60) - time_format = '{:02d}:{:02d}'.format(hours, minutes) - if hours > 6 or round((no_of_skipped_breaks / no_of_breaks), 1) >= 0.2: + global next_reset_time + resets = session['total_resets'] + if session['screen_time'] > 21600 or (session['breaks'] and session['skipped_breaks'] / session['breaks']) >= 0.2: # Unhealthy behavior -> Red broken heart heart = '💔️' else: # Healthy behavior -> Green heart heart = '💚' - if last_screen_time < 0: - screen_time_diff = '' - else: - hrs_diff, mins_diff = divmod(abs(screen_time - last_screen_time), 60) - symbol = '' - if screen_time > last_screen_time: - symbol = '+' - elif screen_time < last_screen_time: - symbol = '-' - screen_time_diff = ' ( {}{:02d}:{:02d} )'.format(symbol, hrs_diff, mins_diff) - return "{}\tBREAKS: {}\tSKIPPED: {}\tSCREEN TIME: {}{}".format(heart, no_of_breaks, no_of_skipped_breaks, time_format, screen_time_diff) + + content = [ + heart, + f"BREAKS: {session['breaks']}", + f"SKIPPED: {session['skipped_breaks']}", + f"SCREEN TIME: {_format_interval(session['screen_time'])}", + ] + + if resets: + content[1] += f" [{round(session['total_breaks'] / resets, 1)}]" + content[2] += f" [{round(session['total_skipped_breaks'] / resets, 1)}]" + content[3] += f" [{_format_interval(session['total_screen_time'] / resets)}]" + + content = "\t".join(content) + if resets: + content += f"\n\t[] = average of {resets} reset(s)" + if next_reset_time is None: + content += f"\n\tSettings error in statistics reset interval: {statistics_reset_cron}" + return content def on_start(): """ - Add the idle period to the total idle time. + Track the start time. """ - # Check if the plugin is enabled - if not enabled: - return "" - + global start_time _reset_stats() - global total_idle_time - # idle_period is provided by Smart Pause plugin - total_idle_time += context.get('idle_period', 0) - session['total_idle_time'] = total_idle_time + start_time = datetime.datetime.now() + + +def _get_next_reset_time(): + global next_reset_time + global session -def _get_next_reset_time(current_time, statistics_reset_cron): try: - cron = croniter.croniter(statistics_reset_cron, current_time) - next_time = cron.get_next(datetime.datetime) - logging.debug("Health stats will be reset at " + next_time.strftime("%Y-%m-%d %H:%M:%S")) - return next_time + cron = croniter.croniter(statistics_reset_cron, datetime.datetime.now()) + next_reset_time = cron.get_next(datetime.datetime) + session['next_reset_time'] = next_reset_time.strftime("%Y-%m-%d %H:%M:%S") + logging.debug("Health stats will be reset at " + session['next_reset_time']) except: - # Error in getting the next reset time - return None \ No newline at end of file + logging.error("Error in statistics reset expression: " + statistics_reset_cron) + next_reset_time = None + + +def _format_interval(seconds): + screen_time = round(seconds / 60) + hours, minutes = divmod(screen_time, 60) + return '{:02d}:{:02d}'.format(hours, minutes) From cbfc0cc13300d85f39111d498fc0158dad14d99a Mon Sep 17 00:00:00 2001 From: AdamPS <5490095+AdamPS@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:57:37 +0000 Subject: [PATCH 14/22] Local function names should match #550 --- safeeyes/plugins/smartpause/plugin.py | 20 ++++++++++---------- safeeyes/plugins/trayicon/plugin.py | 10 +++++----- safeeyes/ui/break_screen.py | 12 ++++++------ 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/safeeyes/plugins/smartpause/plugin.py b/safeeyes/plugins/smartpause/plugin.py index 4427c316..242740b5 100644 --- a/safeeyes/plugins/smartpause/plugin.py +++ b/safeeyes/plugins/smartpause/plugin.py @@ -35,8 +35,8 @@ lock = threading.Lock() active = False idle_time = 0 -enable_safe_eyes = None -disable_safe_eyes = None +enable_safeeyes = None +disable_safeeyes = None smart_pause_activated = False idle_start_time = None next_break_time = None @@ -150,8 +150,8 @@ def init(ctx, safeeyes_config, plugin_config): Initialize the plugin. """ global context - global enable_safe_eyes - global disable_safe_eyes + global enable_safeeyes + global disable_safeeyes global postpone global idle_time global short_break_interval @@ -163,8 +163,8 @@ def init(ctx, safeeyes_config, plugin_config): global use_swayidle logging.debug('Initialize Smart Pause plugin') context = ctx - enable_safe_eyes = context['api']['enable_safeeyes'] - disable_safe_eyes = context['api']['disable_safeeyes'] + enable_safeeyes = context['api']['enable_safeeyes'] + disable_safeeyes = context['api']['disable_safeeyes'] postpone = context['api']['postpone'] idle_time = plugin_config['idle_time'] interpret_idle_as_break = plugin_config['interpret_idle_as_break'] @@ -197,7 +197,7 @@ def __start_idle_monitor(): smart_pause_activated = True idle_start_time = datetime.datetime.now() - datetime.timedelta(seconds=system_idle_time) logging.info('Pause Safe Eyes due to system idle') - disable_safe_eyes(None) + disable_safeeyes(None) elif system_idle_time < idle_time and context['state'] == State.STOPPED and idle_start_time is not None: logging.info('Resume Safe Eyes due to user activity') smart_pause_activated = False @@ -214,12 +214,12 @@ def __start_idle_monitor(): # This method runs in a thread since the start. # It may run before next_break is initialized in the update_next_break method next_break = next_break_time + idle_period - enable_safe_eyes(next_break.timestamp()) + enable_safeeyes(next_break.timestamp()) else: - enable_safe_eyes() + enable_safeeyes() else: # User is idle for more than the time between two breaks - enable_safe_eyes() + enable_safeeyes() def on_start(): diff --git a/safeeyes/plugins/trayicon/plugin.py b/safeeyes/plugins/trayicon/plugin.py index 9970afe3..f8ba1e37 100644 --- a/safeeyes/plugins/trayicon/plugin.py +++ b/safeeyes/plugins/trayicon/plugin.py @@ -53,8 +53,8 @@ def __init__(self, context, plugin_config): self.on_show_settings = context['api']['show_settings'] self.on_show_about = context['api']['show_about'] self.quit = context['api']['quit'] - self.on_enable = context['api']['enable_safeeyes'] - self.on_disable = context['api']['disable_safeeyes'] + self.enable_safeeyes = context['api']['enable_safeeyes'] + self.disable_safeeyes = context['api']['disable_safeeyes'] self.take_break = context['api']['take_break'] self.has_breaks = context['api']['has_breaks'] self.get_break_time = context['api']['get_break_time'] @@ -324,7 +324,7 @@ def on_enable_clicked(self, *args): if not self.active: with self.lock: self.enable_ui() - self.on_enable() + self.enable_safeeyes() # Notify all schedulers self.idle_condition.acquire() self.idle_condition.notify_all() @@ -342,13 +342,13 @@ def on_disable_clicked(self, *args): time_to_wait = args[1] if time_to_wait <= 0: info = _('Disabled until restart') - self.on_disable(info) + self.disable_safeeyes(info) self.wakeup_time = None self.item_info.set_label(info) else: self.wakeup_time = datetime.datetime.now() + datetime.timedelta(minutes=time_to_wait) info = _('Disabled until %s') % utility.format_time(self.wakeup_time) - self.on_disable(info) + self.disable_safeeyes(info) self.item_info.set_label(info) utility.start_thread(self.__schedule_resume, time_minutes=time_to_wait) diff --git a/safeeyes/ui/break_screen.py b/safeeyes/ui/break_screen.py index 873c6ef9..d1a2c25f 100644 --- a/safeeyes/ui/break_screen.py +++ b/safeeyes/ui/break_screen.py @@ -41,7 +41,7 @@ class BreakScreen: This class reads the break_screen.glade and build the user interface. """ - def __init__(self, context, on_skip, on_postpone, style_sheet_path): + def __init__(self, context, on_skipped, on_postponed, style_sheet_path): self.context = context self.count_labels = [] self.display = Display() @@ -50,8 +50,8 @@ def __init__(self, context, on_skip, on_postpone, style_sheet_path): self.is_pretified = False self.keycode_shortcut_postpone = 65 self.keycode_shortcut_skip = 9 - self.on_postpone = on_postpone - self.on_skip = on_skip + self.on_postponed = on_postponed + self.on_skipped = on_skipped self.shortcut_disable_time = 2 self.strict_break = False self.windows = [] @@ -77,8 +77,8 @@ def skip_break(self): Skip the break from the break screen """ logging.info("User skipped the break") - # Must call on_skip before close to lock screen before closing the break screen - self.on_skip() + # Must call on_skipped before close to lock screen before closing the break screen + self.on_skipped() self.close() def postpone_break(self): @@ -86,7 +86,7 @@ def postpone_break(self): Postpone the break from the break screen """ logging.info("User postponed the break") - self.on_postpone() + self.on_postponed() self.close() def on_window_delete(self, *args): From 777d48603d4fb9a8baa118314a9073f6f580bd1c Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig <1498135+alyssarosenzweig@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:01:58 -0400 Subject: [PATCH 15/22] Fix with python3.12 (#544) imp is deprecated in 3.4 and removed in 3.12. this caused safe eyes to break on upgrading to fedora 39 Signed-off-by: Alyssa Rosenzweig --- safeeyes/utility.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/safeeyes/utility.py b/safeeyes/utility.py index e145d4dc..39761400 100644 --- a/safeeyes/utility.py +++ b/safeeyes/utility.py @@ -21,7 +21,6 @@ """ import errno -import imp import inspect import importlib import json @@ -355,7 +354,7 @@ def module_exist(module): Check wther the given Python module exists or not. """ try: - imp.find_module(module) + importlib.util.find_spec(module) return True except ImportError: return False From 903d407faf58871fd84756ec76d6b5c2543f6540 Mon Sep 17 00:00:00 2001 From: AdamPS <5490095+AdamPS@users.noreply.github.com> Date: Thu, 30 Nov 2023 17:06:20 +0000 Subject: [PATCH 16/22] Improvements to "interpret idle as break" #551 --- safeeyes/config/safeeyes.json | 1 - safeeyes/core.py | 18 +++++++++--------- safeeyes/model.py | 13 +++++++------ safeeyes/plugins/smartpause/config.json | 6 ------ safeeyes/plugins/smartpause/plugin.py | 13 +++---------- safeeyes/safeeyes.py | 6 +++--- 6 files changed, 22 insertions(+), 35 deletions(-) diff --git a/safeeyes/config/safeeyes.json b/safeeyes/config/safeeyes.json index a67490e7..a41d0f0f 100644 --- a/safeeyes/config/safeeyes.json +++ b/safeeyes/config/safeeyes.json @@ -104,7 +104,6 @@ "version": "0.0.3", "settings": { "idle_time": 5, - "interpret_idle_as_break": false, "postpone_if_active": false } }, diff --git a/safeeyes/core.py b/safeeyes/core.py index a73592c1..2154a2aa 100644 --- a/safeeyes/core.py +++ b/safeeyes/core.py @@ -96,7 +96,7 @@ def start(self, next_break_time=-1, reset_breaks=False): self.scheduled_next_break_timestamp = int(next_break_time) utility.start_thread(self.__scheduler_job) - def stop(self): + def stop(self, is_resting=False): """ Stop Safe Eyes if it is running. """ @@ -110,7 +110,7 @@ def stop(self): self.waiting_condition.acquire() self.running = False if self.context['state'] != State.QUIT: - self.context['state'] = State.STOPPED + self.context['state'] = State.RESTING if (is_resting) else State.STOPPED self.waiting_condition.notify_all() self.waiting_condition.release() @@ -174,7 +174,7 @@ def __take_break(self, break_type = None): self.running = False self.waiting_condition.notify_all() self.waiting_condition.release() - time.sleep(1) # Wait for 1 sec to ensure the sceduler is dead + time.sleep(1) # Wait for 1 sec to ensure the scheduler is dead self.running = True if break_type is not None and self.break_queue.get_break().type != break_type: @@ -188,7 +188,6 @@ def __scheduler_job(self): if not self.running: return - self.context['state'] = State.WAITING # Convert to seconds time_to_wait = self.break_queue.get_break().time * 60 current_time = datetime.datetime.now() @@ -200,20 +199,21 @@ def __scheduler_job(self): time_to_wait = self.postpone_duration self.context['postponed'] = False - elif self.paused_time > -1 and self.break_queue.is_long_break(): - # Safe Eyes was paused earlier and next break is long + elif self.context['state'] == State.RESTING and self.paused_time > -1: + # Safe Eyes was resting paused_duration = int(current_timestamp - self.paused_time) self.paused_time = -1 - if paused_duration > self.break_queue.get_break().duration: - logging.info('Skip next long break due to the pause longer than break duration') + if paused_duration > self.break_queue.get_break(BreakType.LONG_BREAK).duration: + logging.info('Skip next long break due to the pause %ds longer than break duration', paused_duration) # Skip the next long break - self.break_queue.next() + self.break_queue.reset() if current_timestamp < self.scheduled_next_break_timestamp: time_to_wait = round(self.scheduled_next_break_timestamp - current_timestamp) self.scheduled_next_break_timestamp = -1 self.scheduled_next_break_time = current_time + datetime.timedelta(seconds=time_to_wait) + self.context['state'] = State.WAITING utility.execute_main_thread(self.__fire_on_update_next_break, self.scheduled_next_break_time) # Wait for the pre break warning period diff --git a/safeeyes/model.py b/safeeyes/model.py index fe8e026f..453761df 100644 --- a/safeeyes/model.py +++ b/safeeyes/model.py @@ -258,12 +258,13 @@ class State(Enum): """ Possible states of Safe Eyes. """ - START = 0, - WAITING = 1, - PRE_BREAK = 2, - BREAK = 3, - STOPPED = 4, - QUIT = 5 + START = 0, # Starting scheduler + WAITING = 1, # User is working (waiting for next break) + PRE_BREAK = 2, # Preparing for break + BREAK = 3, # Break + STOPPED = 4, # Disabled + QUIT = 5, # Quitting + RESTING = 6 # Resting (natural break) class EventHook: diff --git a/safeeyes/plugins/smartpause/config.json b/safeeyes/plugins/smartpause/config.json index 3615dc19..9ca35a69 100644 --- a/safeeyes/plugins/smartpause/config.json +++ b/safeeyes/plugins/smartpause/config.json @@ -20,12 +20,6 @@ "max": 3600, "min": 5 }, - { - "id": "interpret_idle_as_break", - "label": "Interpret idle time equivalent to upcoming break duration as a break", - "type": "BOOL", - "default": false - }, { "id": "postpone_if_active", "label": "Postpone the next break until the system becomes idle", diff --git a/safeeyes/plugins/smartpause/plugin.py b/safeeyes/plugins/smartpause/plugin.py index 242740b5..296fe516 100644 --- a/safeeyes/plugins/smartpause/plugin.py +++ b/safeeyes/plugins/smartpause/plugin.py @@ -43,7 +43,6 @@ next_break_duration = 0 short_break_interval = 0 waiting_time = 2 -interpret_idle_as_break = False is_wayland_and_gnome = False use_swayidle = False @@ -157,7 +156,6 @@ def init(ctx, safeeyes_config, plugin_config): global short_break_interval global long_break_duration global waiting_time - global interpret_idle_as_break global postpone_if_active global is_wayland_and_gnome global use_swayidle @@ -167,7 +165,6 @@ def init(ctx, safeeyes_config, plugin_config): disable_safeeyes = context['api']['disable_safeeyes'] postpone = context['api']['postpone'] idle_time = plugin_config['idle_time'] - interpret_idle_as_break = plugin_config['interpret_idle_as_break'] postpone_if_active = plugin_config['postpone_if_active'] short_break_interval = safeeyes_config.get( 'short_break_interval') * 60 # Convert to seconds @@ -197,18 +194,14 @@ def __start_idle_monitor(): smart_pause_activated = True idle_start_time = datetime.datetime.now() - datetime.timedelta(seconds=system_idle_time) logging.info('Pause Safe Eyes due to system idle') - disable_safeeyes(None) - elif system_idle_time < idle_time and context['state'] == State.STOPPED and idle_start_time is not None: + disable_safeeyes(None, True) + elif system_idle_time < idle_time and context['state'] == State.RESTING and idle_start_time is not None: logging.info('Resume Safe Eyes due to user activity') smart_pause_activated = False idle_period = (datetime.datetime.now() - idle_start_time) idle_seconds = idle_period.total_seconds() context['idle_period'] = idle_seconds - if interpret_idle_as_break and idle_seconds >= next_break_duration: - # User is idle for break duration and wants to consider it as a break - logging.debug("Idle for %d seconds, long break %d", idle_seconds, long_break_duration) - enable_safe_eyes(-1, idle_seconds >= long_break_duration) - elif idle_seconds < short_break_interval: + if idle_seconds < short_break_interval: # Credit back the idle time if next_break_time is not None: # This method runs in a thread since the start. diff --git a/safeeyes/safeeyes.py b/safeeyes/safeeyes.py index ea9330f5..0a7bcaea 100644 --- a/safeeyes/safeeyes.py +++ b/safeeyes/safeeyes.py @@ -158,7 +158,7 @@ def handle_suspend_callback(self, sleeping): if self.active: logging.info("Stop Safe Eyes due to system suspend") self.plugins_manager.stop() - self.safe_eyes_core.stop() + self.safe_eyes_core.stop(True) else: # Resume from sleep if self.active and self.safe_eyes_core.has_breaks(): @@ -239,14 +239,14 @@ def enable_safeeyes(self, scheduled_next_break_time=-1, reset_breaks=False): self.safe_eyes_core.start(scheduled_next_break_time, reset_breaks) self.plugins_manager.start() - def disable_safeeyes(self, status=None): + def disable_safeeyes(self, status=None, is_resting = False): """ Listen to tray icon disable action and send the signal to core. """ if self.active: self.active = False self.plugins_manager.stop() - self.safe_eyes_core.stop() + self.safe_eyes_core.stop(is_resting) if status is None: status = _('Disabled until restart') self._status = status From 3e89aa4fcfdf34580536f2c6b50e5d178750791e Mon Sep 17 00:00:00 2001 From: AdamPS <5490095+AdamPS@users.noreply.github.com> Date: Wed, 20 Dec 2023 20:08:11 +0000 Subject: [PATCH 17/22] Improvements to "interpret idle as break" #551 (correction) --- safeeyes/core.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/safeeyes/core.py b/safeeyes/core.py index 2154a2aa..582277e6 100644 --- a/safeeyes/core.py +++ b/safeeyes/core.py @@ -188,18 +188,10 @@ def __scheduler_job(self): if not self.running: return - # Convert to seconds - time_to_wait = self.break_queue.get_break().time * 60 current_time = datetime.datetime.now() current_timestamp = current_time.timestamp() - if self.context['postponed']: - # Previous break was postponed - logging.info('Prepare for postponed break') - time_to_wait = self.postpone_duration - self.context['postponed'] = False - - elif self.context['state'] == State.RESTING and self.paused_time > -1: + if self.context['state'] == State.RESTING and self.paused_time > -1: # Safe Eyes was resting paused_duration = int(current_timestamp - self.paused_time) self.paused_time = -1 @@ -208,9 +200,18 @@ def __scheduler_job(self): # Skip the next long break self.break_queue.reset() - if current_timestamp < self.scheduled_next_break_timestamp: + if self.context['postponed']: + # Previous break was postponed + logging.info('Prepare for postponed break') + time_to_wait = self.postpone_duration + self.context['postponed'] = False + elif current_timestamp < self.scheduled_next_break_timestamp: + # Non-standard break was set. time_to_wait = round(self.scheduled_next_break_timestamp - current_timestamp) self.scheduled_next_break_timestamp = -1 + else: + # Use next break, convert to seconds + time_to_wait = self.break_queue.get_break().time * 60 self.scheduled_next_break_time = current_time + datetime.timedelta(seconds=time_to_wait) self.context['state'] = State.WAITING From a59f3770dc3717154d768a124bf790f2526aa01f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BA=8B=E6=82=9FBigELK176?= Date: Mon, 29 Jan 2024 13:40:48 +0000 Subject: [PATCH 18/22] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (116 of 116 strings) Translation: Safe Eyes/Translations Translate-URL: https://hosted.weblate.org/projects/safe-eyes/translations/zh_Hant/ --- safeeyes/config/locale/zh_TW/LC_MESSAGES/safeeyes.po | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/safeeyes/config/locale/zh_TW/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/zh_TW/LC_MESSAGES/safeeyes.po index 3d983bd5..25afdf70 100644 --- a/safeeyes/config/locale/zh_TW/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/zh_TW/LC_MESSAGES/safeeyes.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-01-06 13:14+0000\n" +"PO-Revision-Date: 2024-01-30 14:01+0000\n" "Last-Translator: 麋悟BigELK176 \n" "Language-Team: Chinese (Traditional) \n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 4.4.1-dev\n" +"X-Generator: Weblate 5.4-dev\n" # Short break msgid "Tightly close your eyes" @@ -136,7 +136,7 @@ msgstr "推遲休息時長(分鐘)" # Settings dialog msgid "Show breaks in random order" -msgstr "" +msgstr "顯示休息隨機順序" # Settings dialog msgid "Strict break (No way to skip breaks)" From 4f7cbc4d936de6c54bb1add004e158fdc3642fd9 Mon Sep 17 00:00:00 2001 From: Salif Mehmed Date: Tue, 6 Feb 2024 16:30:18 +0000 Subject: [PATCH 19/22] Translated using Weblate (Bulgarian) Currently translated at 37.0% (43 of 116 strings) Translation: Safe Eyes/Translations Translate-URL: https://hosted.weblate.org/projects/safe-eyes/translations/bg/ --- .../config/locale/bg/LC_MESSAGES/safeeyes.po | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/safeeyes/config/locale/bg/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/bg/LC_MESSAGES/safeeyes.po index 4c9ff093..e07f8485 100644 --- a/safeeyes/config/locale/bg/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/bg/LC_MESSAGES/safeeyes.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-08-05 18:48+0000\n" +"PO-Revision-Date: 2024-02-07 05:56+0000\n" "Last-Translator: Salif Mehmed \n" "Language-Team: Bulgarian \n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.0-dev\n" +"X-Generator: Weblate 5.4-dev\n" # Short break msgid "Tightly close your eyes" @@ -23,35 +23,35 @@ msgstr "Затворете плътно очи" # Short break msgid "Roll your eyes a few times to each side" -msgstr "" +msgstr "Завъртете очи няколко пъти на всяка страна" # Short break msgid "Rotate your eyes in clockwise direction" -msgstr "" +msgstr "Завъртете очите си по посока на часовниковата стрелка" # Short break msgid "Rotate your eyes in counterclockwise direction" -msgstr "" +msgstr "Завъртете очите си обратно на часовниковата стрелка" # Short break msgid "Blink your eyes" -msgstr "" +msgstr "Мигайте с очи" # Short break msgid "Focus on a point in the far distance" -msgstr "" +msgstr "Фокусирайте се върху точка в далечината" # Short break msgid "Have some water" -msgstr "" +msgstr "Пийте вода" # Long break msgid "Walk for a while" -msgstr "" +msgstr "Разходете се известно време" # Long break msgid "Lean back at your seat and relax" -msgstr "" +msgstr "Облегнете се и се отпуснете" # Commandline arg description msgid "show the about dialog" @@ -93,7 +93,7 @@ msgstr "" # About dialog msgid "Close" -msgstr "Затвори" +msgstr "Затваряне" # Description in about dialog # Safe Eyes protects your eyes from eye strain (asthenopia) by reminding you to take breaks while you're working long hours at the computer @@ -108,19 +108,19 @@ msgstr "Лиценз" # Break screen msgid "Skip" -msgstr "Пропусни" +msgstr "Пропускане" # Break screen msgid "Postpone" -msgstr "Отложи" +msgstr "Отлагане" # Settings dialog msgid "Break duration (in seconds)" -msgstr "" +msgstr "Продължителност на почивките (в секунди)" # Settings dialog msgid "Interval between two breaks (in minutes)" -msgstr "" +msgstr "Интервал между две почивки (в минути)" # Settings dialog msgid "Time to prepare for a break (in seconds)" @@ -176,15 +176,15 @@ msgstr "Опции" # Settings dialog msgid "Short Breaks" -msgstr "" +msgstr "Кратки почивки" # Settings dialog msgid "Long Breaks" -msgstr "" +msgstr "Дълги почивки" # Settings dialog msgid "Delete" -msgstr "" +msgstr "Изтриване" # Settings dialog msgid "Are you sure you want to delete this break?" @@ -196,11 +196,11 @@ msgstr "" # Settings dialog msgid "Break" -msgstr "" +msgstr "Почивка" # Settings dialog msgid "Breaks" -msgstr "" +msgstr "Почивки" # Settings dialog msgid "Plugins" @@ -244,11 +244,11 @@ msgstr "" # Settings dialog msgid "Time (in seconds)" -msgstr "" +msgstr "Време (в секунди)" # Settings dialog msgid "Time (in minutes)" -msgstr "" +msgstr "Време (в минути)" # Settings dialog msgid "Break Settings" @@ -280,7 +280,7 @@ msgstr "" # Settings dialog msgid "New Break" -msgstr "" +msgstr "Нова почивка" # Settings dialog msgid "Remove" @@ -288,11 +288,11 @@ msgstr "Премахване" # Settings dialog msgid "Discard" -msgstr "" +msgstr "Отхвърляне" # Settings dialog msgid "Save" -msgstr "" +msgstr "Запазване" # plugin/audiblealert msgid "Audible Alert" @@ -312,7 +312,7 @@ msgstr "" # plugin/donotdisturb msgid "Do Not Disturb" -msgstr "" +msgstr "Не безпокой" # plugin/donotdisturb msgid "Skip break if the active window is in fullscreen mode" @@ -348,7 +348,7 @@ msgstr "" # plugin/notification msgid "Notification" -msgstr "" +msgstr "Известие" # plugin/notification msgid "Show a system notification before breaks" @@ -424,45 +424,45 @@ msgstr "Относно" #: plugins/trayicon msgid "Disable Safe Eyes" -msgstr "" +msgstr "Изключване на Safe Eyes" #: plugins/trayicon msgid "Disabled until %s" -msgstr "" +msgstr "Изключено до %s" #: plugins/trayicon msgid "Disabled until restart" -msgstr "" +msgstr "Изключено до рестартиране" #: plugins/trayicon msgid "Enable Safe Eyes" -msgstr "" +msgstr "Включване на Safe Eyes" #: plugins/trayicon msgid "For %d Hour" msgid_plural "For %d Hours" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "За %d Час" +msgstr[1] "За %d Часа" #: plugins/trayicon msgid "For %d Minute" msgid_plural "For %d Minutes" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "За %d Минута" +msgstr[1] "За %d Минути" #: plugins/trayicon msgid "For %d Second" msgid_plural "For %d Seconds" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "За %d Секунда" +msgstr[1] "За %d Секунди" #: plugins/trayicon msgid "Next break at %s" -msgstr "" +msgstr "Следваща почивка в %s" #: plugins/trayicon msgid "No Breaks Available" -msgstr "" +msgstr "Няма налични почивки" #: plugins/trayicon msgid "Settings" @@ -470,15 +470,15 @@ msgstr "Настройки" #: plugins/trayicon msgid "Take a break now" -msgstr "" +msgstr "Направете почивка сега" #: plugins/trayicon msgid "Until restart" -msgstr "" +msgstr "До рестартиране" #: plugins/trayicon msgid "Quit" -msgstr "" +msgstr "Изход" # plugin/mediacontrol msgid "Media Control" From 766e9a97959d18b0731bbadfe271834c0c72ddbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D1=96=D0=B9?= Date: Wed, 14 Feb 2024 12:23:12 +0000 Subject: [PATCH 20/22] Translated using Weblate (Ukrainian) Currently translated at 100.0% (116 of 116 strings) Translation: Safe Eyes/Translations Translate-URL: https://hosted.weblate.org/projects/safe-eyes/translations/uk/ --- safeeyes/config/locale/uk/LC_MESSAGES/safeeyes.po | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/safeeyes/config/locale/uk/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/uk/LC_MESSAGES/safeeyes.po index 3431ca37..0b5d8d18 100644 --- a/safeeyes/config/locale/uk/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/uk/LC_MESSAGES/safeeyes.po @@ -6,17 +6,17 @@ msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-08-18 00:37+0000\n" -"Last-Translator: Tymofii Lytvynenko \n" +"PO-Revision-Date: 2024-02-15 13:01+0000\n" +"Last-Translator: Сергій \n" "Language-Team: Ukrainian \n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 4.8-dev\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 5.4-dev\n" # Short break msgid "Tightly close your eyes" @@ -48,7 +48,7 @@ msgstr "Випийте води" # Long break msgid "Walk for a while" -msgstr "Трохи походіть" +msgstr "Прогуляйтесь" # Long break msgid "Lean back at your seat and relax" @@ -289,7 +289,7 @@ msgstr "Нова перерва" # Settings dialog msgid "Remove" -msgstr "Видалити" +msgstr "Вилучити" # Settings dialog msgid "Discard" From 00c33908a3e861f19d0fcb2df54ecaf48b93e66a Mon Sep 17 00:00:00 2001 From: aerowolf Date: Tue, 20 Feb 2024 09:53:19 +0000 Subject: [PATCH 21/22] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (116 of 116 strings) Translation: Safe Eyes/Translations Translate-URL: https://hosted.weblate.org/projects/safe-eyes/translations/zh_Hans/ --- .../locale/zh_CN/LC_MESSAGES/safeeyes.po | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/safeeyes/config/locale/zh_CN/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/zh_CN/LC_MESSAGES/safeeyes.po index 1dae31a4..7a054f6a 100644 --- a/safeeyes/config/locale/zh_CN/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/zh_CN/LC_MESSAGES/safeeyes.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-10-10 05:05+0000\n" -"Last-Translator: Frank.wu \n" +"PO-Revision-Date: 2024-02-21 10:01+0000\n" +"Last-Translator: aerowolf \n" "Language-Team: Chinese (Simplified) \n" "Language: zh_CN\n" @@ -15,23 +15,23 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 4.9-dev\n" +"X-Generator: Weblate 5.5-dev\n" # Short break msgid "Tightly close your eyes" -msgstr "闭上您的眼睛休息一会" +msgstr "闭上眼睛休息一下" # Short break msgid "Roll your eyes a few times to each side" -msgstr "向两边各翻几下眼睛" +msgstr "左右转动眼球" # Short break msgid "Rotate your eyes in clockwise direction" -msgstr "按顺时针方向旋转你的眼球" +msgstr "顺时针方向转动眼球" # Short break msgid "Rotate your eyes in counterclockwise direction" -msgstr "按逆时针方向旋转你的眼球" +msgstr "逆时针方向转动眼球" # Short break msgid "Blink your eyes" @@ -39,11 +39,11 @@ msgstr "眨眨眼" # Short break msgid "Focus on a point in the far distance" -msgstr "望远:将视线聚焦到远处物体上" +msgstr "远眺:看看尽可能远的远处" # Short break msgid "Have some water" -msgstr "喝一点水" +msgstr "喝点水吧" # Long break msgid "Walk for a while" @@ -51,7 +51,7 @@ msgstr "站起来走走" # Long break msgid "Lean back at your seat and relax" -msgstr "靠在椅背上休息一会" +msgstr "靠在椅背上休息一下" # Commandline arg description msgid "show the about dialog" @@ -59,15 +59,15 @@ msgstr "显示“关于”对话框" # Commandline arg description msgid "disable the currently running safeeyes instance" -msgstr "禁用当前正在运行的Safe Eyes" +msgstr "暂停当前正在运行的Safe Eyes" # Commandline arg description msgid "enable the currently running safeeyes instance" -msgstr "启用当前正在运行的Safe Eyes" +msgstr "恢复当前正在运行的Safe Eyes" # Commandline arg description msgid "quit the running safeeyes instance and exit" -msgstr "退出正在运行的Safe Eeyes" +msgstr "退出Safe Eeyes" # Commandline arg description msgid "show the settings dialog" @@ -79,17 +79,17 @@ msgstr "以调试模式运行Safe Eyes" # Commandline arg description msgid "print the status of running safeeyes instance and exit" -msgstr "打印Safe Eyes运行状态后退出" +msgstr "显示Safe Eyes运行状态后退出" # Status message msgid "Safe Eyes is not running" -msgstr "Safe Eyes 尚未运行" +msgstr "Safe Eyes 没有运行" # RPC not enabled message msgid "" "Safe Eyes is running without an RPC server. Turn it on to use command-line " "arguments." -msgstr "Safe Eyes 正在没有RPC服务器的情况下运行。打开它以使用命令行参数。" +msgstr "Safe Eyes 正在没有RPC服务器的情况下运行。打开RPC服务器可以使用命令行参数。" # About dialog msgid "Close" @@ -106,7 +106,7 @@ msgstr "" # About dialog msgid "License" -msgstr "许可证" +msgstr "许可" # Break screen msgid "Skip" @@ -142,7 +142,7 @@ msgstr "以随机顺序显示休息" # Settings dialog msgid "Strict break (No way to skip breaks)" -msgstr "坚持休息(无法跳过)" +msgstr "坚持休息(不允许跳过)" # Settings dialog msgid "Allow postponing breaks" @@ -150,7 +150,7 @@ msgstr "允许推迟休息" # Settings dialog msgid "Persist the internal state" -msgstr "维持内部状态" +msgstr "支持内部状态" # Settings dialog msgid "Use RPC server to receive runtime commands" @@ -414,11 +414,11 @@ msgstr "在通知区域显示托盘图标" #: plugins/trayicon msgid "Show next break time in tray icon" -msgstr "在托盘图标上显示下次休息时间" +msgstr "在托盘图标中显示下次休息时间" #: plugins/trayicon msgid "Allow disabling Safe Eyes" -msgstr "允许禁用Safe Eyes" +msgstr "允许暂停Safe Eyes" #: plugins/trayicon msgid "About" @@ -426,19 +426,19 @@ msgstr "关于" #: plugins/trayicon msgid "Disable Safe Eyes" -msgstr "禁用 Safe Eyes" +msgstr "暂停 Safe Eyes" #: plugins/trayicon msgid "Disabled until %s" -msgstr "禁用软件直到 %s" +msgstr "暂停,直到 %s" #: plugins/trayicon msgid "Disabled until restart" -msgstr "禁用软件直到重启" +msgstr "暂停,直到重启" #: plugins/trayicon msgid "Enable Safe Eyes" -msgstr "启用 Safe Eyes" +msgstr "恢复 Safe Eyes" #: plugins/trayicon msgid "For %d Hour" @@ -457,7 +457,7 @@ msgstr[0] "%d 秒" #: plugins/trayicon msgid "Next break at %s" -msgstr "下次休息在 %s" +msgstr "下次休息将在 %s" #: plugins/trayicon msgid "No Breaks Available" From 97b456770960b38a178b5f1dd0eb7da4cdb0c42e Mon Sep 17 00:00:00 2001 From: Archisman Panigrahi Date: Mon, 29 Apr 2024 02:11:12 -0400 Subject: [PATCH 22/22] New version with python 3.12 support --- debian/changelog | 6 ++++++ safeeyes/glade/about_dialog.glade | 2 +- .../platform/io.github.slgobinath.SafeEyes.metainfo.xml | 2 +- safeeyes/safeeyes.py | 2 +- setup.py | 4 ++-- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/debian/changelog b/debian/changelog index 27e607ee..5e839b86 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +safeeyes (2.1.7) noble; urgency=medium + + * Support Python 3.12 + + -- Archisman Panigrahi Mon, 29 Apr 2024 02:09:48 -0400 + safeeyes (2.1.6-0) lunar; urgency=high * Support Python 3.11 diff --git a/safeeyes/glade/about_dialog.glade b/safeeyes/glade/about_dialog.glade index 16780f76..6ae93348 100644 --- a/safeeyes/glade/about_dialog.glade +++ b/safeeyes/glade/about_dialog.glade @@ -71,7 +71,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.center 10 10 - Safe Eyes 2.1.5 + Safe Eyes 2.1.7 center diff --git a/safeeyes/platform/io.github.slgobinath.SafeEyes.metainfo.xml b/safeeyes/platform/io.github.slgobinath.SafeEyes.metainfo.xml index 0aa25a62..5acc7e27 100644 --- a/safeeyes/platform/io.github.slgobinath.SafeEyes.metainfo.xml +++ b/safeeyes/platform/io.github.slgobinath.SafeEyes.metainfo.xml @@ -47,7 +47,7 @@ https://slgobinath.github.io/SafeEyes/ - + diff --git a/safeeyes/safeeyes.py b/safeeyes/safeeyes.py index 0a7bcaea..223c80b0 100644 --- a/safeeyes/safeeyes.py +++ b/safeeyes/safeeyes.py @@ -40,7 +40,7 @@ gi.require_version('Gtk', '3.0') from gi.repository import Gtk -SAFE_EYES_VERSION = "2.1.6" +SAFE_EYES_VERSION = "2.1.7" class SafeEyes: diff --git a/setup.py b/setup.py index c7bc490f..7afaca50 100644 --- a/setup.py +++ b/setup.py @@ -78,14 +78,14 @@ def __package_data(): setuptools.setup( name="safeeyes", - version="2.1.6", + version="2.1.7", description="Protect your eyes from eye strain using this continuous breaks reminder.", long_description=long_description, long_description_content_type="text/markdown", author="Gobinath Loganathan", author_email="slgobinath@gmail.com", url="https://github.com/slgobinath/SafeEyes", - download_url="https://github.com/slgobinath/SafeEyes/archive/v2.1.6.tar.gz", + download_url="https://github.com/slgobinath/SafeEyes/archive/v2.1.7.tar.gz", packages=setuptools.find_packages(), package_data={'safeeyes': __package_data()}, data_files=__data_files(),