Add isRinging query to check alarm state via API

Features:
- New GraphQL query 'isRinging' returns true/false if wecker is active
- Checks the wecker process via PID file (PID_FILE in common.py)

DRY refactoring:
- Extract PID_FILE into shared common.py module
- Both wecker.py and api/schema.py import from common
- DRY enforcement tests verify identity (is) not just equality

Tests:
- test_is_ringing_returns_false_when_not_running
- test_is_ringing_returns_true_when_running
- test_pid_file_defined_once_across_modules (DRY enforcement)
- test_pid_file_same_shared_constant_in_api (DRY enforcement)
- Cleaned up unused imports in test_single_instance.py

Docs:
- Updated README.md with isRinging query documentation
This commit is contained in:
Markus Graf
2026-05-19 15:15:05 +02:00
parent 29aff04e33
commit 558c4ff5b6
6 changed files with 98 additions and 5 deletions
+19
View File
@@ -4,6 +4,7 @@ import os
import sys
from pathlib import Path
from api.crontab_manager import CrontabManager
from common import PID_FILE
def get_manager():
@@ -11,6 +12,20 @@ def get_manager():
return CrontabManager(tabfile=tabfile)
def is_wecker_ringing() -> bool:
"""Check if the wecker process is currently running via the PID file."""
if not os.path.exists(PID_FILE):
return False
try:
pid: int
with open(PID_FILE) as f:
pid = int(f.read().strip())
os.kill(pid, 0)
return True
except (ValueError, ProcessLookupError, PermissionError, OSError):
return False
@strawberry.type
class Alarm:
id: str
@@ -21,6 +36,10 @@ class Alarm:
@strawberry.type
class Query:
@strawberry.field
def is_ringing(self) -> bool:
return is_wecker_ringing()
@strawberry.field
def get_alarms(self) -> List[Alarm]:
manager = get_manager()