fix: corregir bugs que costaban minutos y tapaban errores
- google_calendar: RefreshError se referenciaba como google.oauth2.credentials.exceptions.RefreshError, ruta que no existe. Al fallar un refresh de token saltaba un AttributeError que ocultaba el error real y rompia el re-auth. Ahora se importa de google.auth.exceptions. - google_calendar: getEvent() comprobaba si habia un evento activo en ese instante. El daemon planifica de madrugada, asi que una ausencia de 09:00 a 18:00 no se detectaba y se fichaba igual. Ahora acepta un dia y comprueba solapamiento. Sin argumento mantiene el comportamiento anterior. - google_calendar: datetime.utcnow() esta deprecado en Python 3.12+. - timenet_manager: "args.basedtime and not args.type" ignoraba -t 0 (Entrada) al combinarlo con -bt, porque not 0 es True. Ahora es "args.type is None". - timenet_manager: updateVersion() asignaba el objeto Response al header Tn-V en vez de su texto. - timenet_manager: geo.accuracy se parseaba y se podia sobrescribir por CLI pero nunca se enviaba en el payload de cp/checks. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import google.oauth2.credentials
|
from google.auth.exceptions import RefreshError
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from google.auth.transport.requests import Request
|
from google.auth.transport.requests import Request
|
||||||
@@ -7,7 +7,7 @@ from google_auth_oauthlib.flow import InstalledAppFlow
|
|||||||
from googleapiclient.discovery import build
|
from googleapiclient.discovery import build
|
||||||
from googleapiclient.errors import HttpError
|
from googleapiclient.errors import HttpError
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@@ -84,7 +84,7 @@ class GoogleCalendar:
|
|||||||
logger.info("Refrescando credenciales...")
|
logger.info("Refrescando credenciales...")
|
||||||
self.creds.refresh(Request())
|
self.creds.refresh(Request())
|
||||||
logger.info("Credenciales refrescadas correctamente")
|
logger.info("Credenciales refrescadas correctamente")
|
||||||
except google.oauth2.credentials.exceptions.RefreshError as e:
|
except RefreshError as e:
|
||||||
logger.warning(f"Error al refrescar credenciales: {str(e)}")
|
logger.warning(f"Error al refrescar credenciales: {str(e)}")
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -100,7 +100,7 @@ class GoogleCalendar:
|
|||||||
try:
|
try:
|
||||||
logger.info("Intentando refrescar credenciales expiradas...")
|
logger.info("Intentando refrescar credenciales expiradas...")
|
||||||
self.refreshAuth()
|
self.refreshAuth()
|
||||||
except google.oauth2.credentials.exceptions.RefreshError as rerror:
|
except RefreshError as rerror:
|
||||||
logger.warning(f"No se pudo refrescar credenciales: {str(rerror)}, solicitando nueva autenticación")
|
logger.warning(f"No se pudo refrescar credenciales: {str(rerror)}, solicitando nueva autenticación")
|
||||||
self.serverAuth()
|
self.serverAuth()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -127,17 +127,36 @@ class GoogleCalendar:
|
|||||||
logger.error(f"Error al construir servicio de Google Calendar: {str(e)}", exc_info=True)
|
logger.error(f"Error al construir servicio de Google Calendar: {str(e)}", exc_info=True)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def getNow(self):
|
def getNow(self, day=None):
|
||||||
return datetime.utcnow().strftime('%Y-%m-%dT00:00:00Z')
|
d = day or datetime.now(timezone.utc)
|
||||||
|
return d.strftime('%Y-%m-%dT00:00:00Z')
|
||||||
|
|
||||||
def getNowAfterOneDay(self):
|
def getNowAfterOneDay(self, day=None):
|
||||||
return (datetime.utcnow() + relativedelta(days=1)).strftime('%Y-%m-%dT00:00:00Z')
|
d = day or datetime.now(timezone.utc)
|
||||||
|
return (d + relativedelta(days=1)).strftime('%Y-%m-%dT00:00:00Z')
|
||||||
|
|
||||||
def getEvent(self):
|
@staticmethod
|
||||||
|
def _affects(start, end, day):
|
||||||
|
"""Con `day`: el evento solapa ese dia. Sin `day`: esta activo ahora mismo."""
|
||||||
|
s0 = datetime.fromisoformat(start)
|
||||||
|
e0 = datetime.fromisoformat(end)
|
||||||
|
if day is None:
|
||||||
|
return s0.timestamp() <= datetime.now().timestamp() <= e0.timestamp()
|
||||||
|
d0 = datetime(day.year, day.month, day.day, tzinfo=s0.tzinfo)
|
||||||
|
d1 = d0 + relativedelta(days=1)
|
||||||
|
return s0 < d1 and e0 > d0
|
||||||
|
|
||||||
|
def getEvent(self, day=None):
|
||||||
|
"""Ausencia personal que afecta a `day` (por defecto, ahora mismo).
|
||||||
|
|
||||||
|
Con `day` se pregunta si el evento CUBRE ese dia, no si esta activo en este
|
||||||
|
instante: el daemon planifica de madrugada y una ausencia de 09:00 a 18:00
|
||||||
|
tiene que detectarse igual.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
logger.info("Obteniendo eventos de Google Calendar...")
|
logger.info("Obteniendo eventos de Google Calendar...")
|
||||||
now = self.getNow()
|
now = self.getNow(day)
|
||||||
now_after_one_day = self.getNowAfterOneDay()
|
now_after_one_day = self.getNowAfterOneDay(day)
|
||||||
logger.info(f"Buscando eventos entre {now} y {now_after_one_day}")
|
logger.info(f"Buscando eventos entre {now} y {now_after_one_day}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -185,8 +204,7 @@ class GoogleCalendar:
|
|||||||
end = event['end'].get('dateTime', event['end'].get('date'))
|
end = event['end'].get('dateTime', event['end'].get('date'))
|
||||||
logger.debug(f"Evaluando evento: {event.get('summary', 'Sin título')} - {start} a {end}")
|
logger.debug(f"Evaluando evento: {event.get('summary', 'Sin título')} - {start} a {end}")
|
||||||
|
|
||||||
if datetime.fromisoformat(start).timestamp() <= datetime.now().timestamp() <= datetime.fromisoformat(
|
if self._affects(start, end, day):
|
||||||
end).timestamp():
|
|
||||||
logger.info(f"Evento activo encontrado: {event.get('summary', 'Sin título')}")
|
logger.info(f"Evento activo encontrado: {event.get('summary', 'Sin título')}")
|
||||||
if event.get('organizer', {}).get('displayName') == 'autoficher':
|
if event.get('organizer', {}).get('displayName') == 'autoficher':
|
||||||
result = 'Forzado desde autoficher - ' + event.get("summary", "Sin Título")
|
result = 'Forzado desde autoficher - ' + event.get("summary", "Sin Título")
|
||||||
|
|||||||
@@ -180,7 +180,11 @@ class timenetManager:
|
|||||||
self.headers["dStr"] = "\"" + now.strftime("%d/%m/%Y %H:%M:%S") + "\""
|
self.headers["dStr"] = "\"" + now.strftime("%d/%m/%Y %H:%M:%S") + "\""
|
||||||
|
|
||||||
def updateVersion(self):
|
def updateVersion(self):
|
||||||
self.headers[strings.tnv_string] = self.getVersion()
|
response = self.getVersion()
|
||||||
|
if response.status_code != 200:
|
||||||
|
logger.warning(f"No se pudo obtener la version: status_code={response.status_code}")
|
||||||
|
return
|
||||||
|
self.headers[strings.tnv_string] = response.text.replace("\"", "")
|
||||||
|
|
||||||
def getVersion(self):
|
def getVersion(self):
|
||||||
self.updateTime()
|
self.updateTime()
|
||||||
@@ -258,7 +262,7 @@ class timenetManager:
|
|||||||
# Determinar tipo de fichaje
|
# Determinar tipo de fichaje
|
||||||
typ = 0
|
typ = 0
|
||||||
try:
|
try:
|
||||||
if args.basedtime and not args.type:
|
if args.basedtime and args.type is None:
|
||||||
hour = datetime.now().strftime("%H")
|
hour = datetime.now().strftime("%H")
|
||||||
logger.info(f"Hora actual: {hour}")
|
logger.info(f"Hora actual: {hour}")
|
||||||
|
|
||||||
@@ -312,6 +316,7 @@ class timenetManager:
|
|||||||
"date": self.headers[strings.tnd_string],
|
"date": self.headers[strings.tnd_string],
|
||||||
"geoLatitude": config.get('geo', {}).get('latitude', ''),
|
"geoLatitude": config.get('geo', {}).get('latitude', ''),
|
||||||
"geoLongitude": config.get('geo', {}).get('longitude', ''),
|
"geoLongitude": config.get('geo', {}).get('longitude', ''),
|
||||||
|
"geoAccuracy": config.get('geo', {}).get('accuracy', ''),
|
||||||
"geoError": "",
|
"geoError": "",
|
||||||
"dStr": self.headers["dStr"].replace("\"", "")
|
"dStr": self.headers["dStr"].replace("\"", "")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user