Agregado de Date
This commit is contained in:
parent
6ebc81d0b9
commit
a85434d492
|
@ -1,5 +1,41 @@
|
|||
valor = 500
|
||||
k = valor * symbols('Hz') / symbols('mmS')
|
||||
# Italiano
|
||||
faltan_italiano = 51
|
||||
hechas_italiano = 21
|
||||
total_italiano = faltan_italiano+hechas_italiano
|
||||
|
||||
valor_mmS = 100 * symbols('mmS')
|
||||
speed = k * valor_mmS
|
||||
# Matematica
|
||||
faltan_mate = 59
|
||||
hechas_mate = 6
|
||||
total_mate = faltan_mate+hechas_mate
|
||||
|
||||
# Matematica Libro
|
||||
faltan_mate2 = 25
|
||||
hechas_mate2 = 0
|
||||
total__mate2 = hechas_mate2+faltan_mate2
|
||||
|
||||
# Ingles
|
||||
faltan_english = 48
|
||||
hechas_english = 3
|
||||
total__english = hechas_english + faltan_english
|
||||
|
||||
# Resumen
|
||||
total = total_italiano+total_mate+total__mate2+total__english
|
||||
hecho = hechas_italiano + hechas_mate + hechas_mate2 + hechas_english
|
||||
faltan = total - hecho
|
||||
|
||||
# Estado actual de dias
|
||||
dias_pasados = 28-6
|
||||
dias_faltan = 31+31+14+2
|
||||
|
||||
# Promedio actual
|
||||
hecho/dias_pasados
|
||||
|
||||
# Minimo para terminar
|
||||
hacer_por_dia = faltan/dias_faltan
|
||||
|
||||
# Minimo para terminar
|
||||
promedio_ideal = total/(dias_pasados+dias_faltan)
|
||||
|
||||
|
||||
Date('23/4/25')=Date('23/2/25') + x
|
||||
x=?
|
||||
|
|
|
@ -407,6 +407,13 @@ class PureAlgebraicEngine:
|
|||
eval_context = self._get_complete_context()
|
||||
result_obj = sympify(expression_str, locals=eval_context)
|
||||
|
||||
# NUEVA FUNCIONALIDAD: Aplicar simplificación simbólica automática en asignaciones
|
||||
if hasattr(result_obj, 'free_symbols') and result_obj.free_symbols and self.equations:
|
||||
simplified_obj = self._apply_symbolic_simplification(result_obj)
|
||||
if simplified_obj != result_obj:
|
||||
result_obj = simplified_obj
|
||||
self.logger.debug(f"Simplificación simbólica aplicada en asignación: {expression_str} → {result_obj}")
|
||||
|
||||
# 1. ASIGNACIÓN AL CONTEXTO (para evaluación directa)
|
||||
self.symbol_table[var_name] = result_obj
|
||||
self.variables.add(var_name)
|
||||
|
@ -509,6 +516,13 @@ class PureAlgebraicEngine:
|
|||
eval_context = self._get_complete_context()
|
||||
result_obj = sympify(line, locals=eval_context)
|
||||
|
||||
# NUEVA FUNCIONALIDAD: Aplicar simplificación simbólica automática
|
||||
if hasattr(result_obj, 'free_symbols') and result_obj.free_symbols and self.equations:
|
||||
simplified_obj = self._apply_symbolic_simplification(result_obj)
|
||||
if simplified_obj != result_obj:
|
||||
result_obj = simplified_obj
|
||||
self.logger.debug(f"Simplificación simbólica aplicada: {line} → {result_obj}")
|
||||
|
||||
# Si es un PlotResult, asegurar que tenga la línea original
|
||||
if isinstance(result_obj, PlotResult) and not result_obj.original_expression:
|
||||
result_obj.original_expression = line
|
||||
|
@ -600,15 +614,33 @@ class PureAlgebraicEngine:
|
|||
|
||||
# Verificar que el resultado no sea problemático
|
||||
if final_value != var_symbol and not isinstance(final_value, (sp.logic.boolalg.BooleanTrue, sp.logic.boolalg.BooleanFalse)):
|
||||
# NUEVA FUNCIONALIDAD: Aplicar simplificación simbólica al resultado final
|
||||
if hasattr(final_value, 'free_symbols') and final_value.free_symbols and self.equations:
|
||||
simplified_final = self._apply_symbolic_simplification(final_value)
|
||||
if simplified_final != final_value:
|
||||
self.logger.debug(f"Simplificación aplicada en resultado final: {final_value} → {simplified_final}")
|
||||
final_value = simplified_final
|
||||
return Eq(var_symbol, final_value)
|
||||
else:
|
||||
# Si hay problema con la resolución, devolver simbólico
|
||||
# Aplicar simplificación también al valor simbólico
|
||||
if hasattr(solution_value, 'free_symbols') and solution_value.free_symbols and self.equations:
|
||||
simplified_solution = self._apply_symbolic_simplification(solution_value)
|
||||
if simplified_solution != solution_value:
|
||||
self.logger.debug(f"Simplificación aplicada en solución simbólica: {solution_value} → {simplified_solution}")
|
||||
solution_value = simplified_solution
|
||||
return Eq(var_symbol, solution_value)
|
||||
else:
|
||||
# Hay variables con valores simbólicos, devolver ecuación simbólica
|
||||
return Eq(var_symbol, solution_value)
|
||||
else:
|
||||
# No hay variables sin resolver, es ya un valor final
|
||||
# NUEVA FUNCIONALIDAD: Aplicar simplificación simbólica al valor final
|
||||
if hasattr(solution_value, 'free_symbols') and solution_value.free_symbols and self.equations:
|
||||
simplified_solution = self._apply_symbolic_simplification(solution_value)
|
||||
if simplified_solution != solution_value:
|
||||
self.logger.debug(f"Simplificación aplicada en valor final: {solution_value} → {simplified_solution}")
|
||||
solution_value = simplified_solution
|
||||
return Eq(var_symbol, solution_value)
|
||||
else:
|
||||
# Si no hay solución en las ecuaciones, verificar en symbol_table
|
||||
|
@ -617,6 +649,12 @@ class PureAlgebraicEngine:
|
|||
value = self.symbol_table[var_name]
|
||||
# Si el valor en symbol_table es diferente de la variable, devolverlo
|
||||
if value != var_symbol:
|
||||
# NUEVA FUNCIONALIDAD: Aplicar simplificación simbólica también aquí
|
||||
if hasattr(value, 'free_symbols') and value.free_symbols and self.equations:
|
||||
simplified_value = self._apply_symbolic_simplification(value)
|
||||
if simplified_value != value:
|
||||
self.logger.debug(f"Simplificación aplicada en solve: {value} → {simplified_value}")
|
||||
value = simplified_value
|
||||
return Eq(var_symbol, value)
|
||||
|
||||
# Si no hay información, devolver ecuación con la variable sin resolver
|
||||
|
@ -715,6 +753,85 @@ class PureAlgebraicEngine:
|
|||
self.logger.debug(f"Error resolviendo {var_symbol}: {e}")
|
||||
return var_symbol
|
||||
|
||||
def _apply_symbolic_simplification(self, expression):
|
||||
"""
|
||||
Aplica simplificación simbólica automática usando ecuaciones definidas
|
||||
|
||||
Esta función sustituye símbolos por sus definiciones de ecuaciones cuando es posible,
|
||||
permitiendo simplificaciones como: mS = mmS/1000, entonces 50000*Hz*mS/mmS → 50*Hz
|
||||
"""
|
||||
try:
|
||||
current_expr = expression
|
||||
max_iterations = 5 # Evitar bucles infinitos
|
||||
|
||||
for iteration in range(max_iterations):
|
||||
substituted = current_expr
|
||||
substitution_made = False
|
||||
|
||||
# Obtener símbolos libres de la expresión actual
|
||||
free_symbols = substituted.free_symbols
|
||||
|
||||
# Para cada símbolo en la expresión, buscar si hay una ecuación que lo defina
|
||||
for symbol in free_symbols:
|
||||
symbol_definition = self._find_symbol_definition(symbol)
|
||||
|
||||
if symbol_definition is not None and symbol_definition != symbol:
|
||||
# Aplicar la sustitución
|
||||
new_expr = substituted.subs(symbol, symbol_definition)
|
||||
|
||||
# Simplificar después de la sustitución
|
||||
simplified = sp.simplify(new_expr)
|
||||
|
||||
if simplified != substituted:
|
||||
substituted = simplified
|
||||
substitution_made = True
|
||||
self.logger.debug(f"Símbolo {symbol} sustituido por {symbol_definition}")
|
||||
|
||||
# Si no se hicieron sustituciones, terminar
|
||||
if not substitution_made:
|
||||
break
|
||||
|
||||
current_expr = substituted
|
||||
|
||||
return current_expr
|
||||
|
||||
except Exception as e:
|
||||
self.logger.debug(f"Error en simplificación simbólica: {e}")
|
||||
return expression
|
||||
|
||||
def _find_symbol_definition(self, symbol):
|
||||
"""
|
||||
Busca la definición de un símbolo en las ecuaciones del sistema
|
||||
|
||||
Returns:
|
||||
La expresión que define al símbolo, o None si no se encuentra
|
||||
"""
|
||||
try:
|
||||
# Buscar en ecuaciones donde el símbolo aparece solo en el lado izquierdo
|
||||
for eq in self.equations:
|
||||
if hasattr(eq, 'lhs') and hasattr(eq, 'rhs'):
|
||||
# Caso 1: símbolo = expresión
|
||||
if eq.lhs == symbol:
|
||||
return eq.rhs
|
||||
|
||||
# Caso 2: expresión = símbolo
|
||||
elif eq.rhs == symbol:
|
||||
return eq.lhs
|
||||
|
||||
# Buscar en symbol_table como respaldo
|
||||
symbol_name = str(symbol)
|
||||
if symbol_name in self.symbol_table:
|
||||
value = self.symbol_table[symbol_name]
|
||||
# Solo usar si el valor es diferente del símbolo mismo
|
||||
if value != symbol:
|
||||
return value
|
||||
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
self.logger.debug(f"Error buscando definición de {symbol}: {e}")
|
||||
return None
|
||||
|
||||
def _resolve_iteratively(self, expression, max_iterations=10):
|
||||
"""Resuelve una expresión iterativamente sustituyendo valores conocidos"""
|
||||
try:
|
||||
|
|
|
@ -315,10 +315,21 @@ class HybridCalculatorPySide6(QMainWindow):
|
|||
self.latex_button.setChecked(True)
|
||||
self.latex_panel_visible = True
|
||||
|
||||
# Configurar tamaños
|
||||
# Configurar tamaños con límites
|
||||
latex_width = self.settings.get("latex_panel_width", 300)
|
||||
total_width = self.width()
|
||||
|
||||
# CORRECCIÓN: Aplicar límites para evitar crecimiento descontrolado
|
||||
min_latex_width = 200
|
||||
max_latex_width = min(600, total_width * 0.5) # Máximo 50% del ancho total
|
||||
latex_width = max(min_latex_width, min(latex_width, max_latex_width))
|
||||
|
||||
main_width = total_width - latex_width - 50 # 50 para el botón
|
||||
# Asegurar que main_width no sea negativo
|
||||
if main_width < 200:
|
||||
main_width = 200
|
||||
latex_width = total_width - main_width - 50
|
||||
|
||||
self.secondary_splitter.setSizes([main_width, latex_width])
|
||||
|
||||
# Actualizar panel con ecuaciones pendientes
|
||||
|
@ -346,11 +357,17 @@ class HybridCalculatorPySide6(QMainWindow):
|
|||
|
||||
def closeEvent(self, event):
|
||||
"""Maneja el cierre de la aplicación"""
|
||||
# Guardar ancho del panel LaTeX si está visible
|
||||
# Guardar ancho del panel LaTeX si está visible con límites de validación
|
||||
if self.latex_panel_visible and self.secondary_splitter.count() > 1:
|
||||
sizes = self.secondary_splitter.sizes()
|
||||
if len(sizes) > 1:
|
||||
latex_width = sizes[1]
|
||||
|
||||
# CORRECCIÓN: Validar y limitar el ancho antes de guardarlo
|
||||
min_latex_width = 200
|
||||
max_latex_width = 600
|
||||
latex_width = max(min_latex_width, min(latex_width, max_latex_width))
|
||||
|
||||
self.settings_manager.set_setting("latex_panel_width", latex_width)
|
||||
|
||||
# Delegar al settings manager
|
||||
|
|
|
@ -0,0 +1,374 @@
|
|||
"""
|
||||
Módulo de manejo de fechas para el sistema de tipos
|
||||
Permite crear y manipular fechas con aritmética de días y semanas, soporte para
|
||||
operaciones simbólicas a través de SymPy, y utilidades comunes como cálculo de
|
||||
fines de semana, días hábiles y diferencias entre fechas.
|
||||
|
||||
Ejemplo de uso:
|
||||
>>> d = Date("10/5/25") # 10 de mayo de 2025
|
||||
>>> d + 7 # 17/5/25
|
||||
>>> d.day_of_week() # 'Sábado'
|
||||
>>> d.is_weekend() # True
|
||||
>>> d.next_business_day() # 12/5/25
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
from typing import List, Tuple, Optional, Union
|
||||
import sympy
|
||||
|
||||
from app.class_base import (
|
||||
ClassBase,
|
||||
) # noqa: F401 (aún no se usa, pero se importa para coherencia)
|
||||
from app.sympy_Base import SympyClassBase
|
||||
|
||||
__all__ = [
|
||||
"Class_Date",
|
||||
"Date", # alias de conveniencia
|
||||
"Today", # nueva función constructora
|
||||
]
|
||||
|
||||
|
||||
def _parse_date_str(date_str: str) -> datetime.date:
|
||||
"""Convierte una cadena a objeto ``datetime.date``.
|
||||
|
||||
Acepta los formatos comunes:
|
||||
- DD/MM/YY (se asume 2000-2099 para YY < 70 y 1900-1999 para el resto)
|
||||
- DD/MM/YYYY
|
||||
- YYYY-MM-DD (ISO-8601)
|
||||
|
||||
Asimismo se admiten separadores «/», «-» o «.».
|
||||
"""
|
||||
date_str = date_str.strip()
|
||||
|
||||
# Intentar ISO-8601 directamente
|
||||
try:
|
||||
return datetime.date.fromisoformat(date_str)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Reemplazar posibles separadores por «/» para simplificar
|
||||
normalised = date_str.replace("-", "/").replace(".", "/")
|
||||
parts = normalised.split("/")
|
||||
if len(parts) != 3:
|
||||
raise ValueError(f"Formato de fecha desconocido: '{date_str}'")
|
||||
|
||||
# Detectar si el primer campo es año (ISO) o día
|
||||
if len(parts[0]) == 4: # AAAA/MM/DD
|
||||
year = int(parts[0])
|
||||
month = int(parts[1])
|
||||
day = int(parts[2])
|
||||
else: # DD/MM/AA o DD/MM/YYYY
|
||||
day = int(parts[0])
|
||||
month = int(parts[1])
|
||||
year_part = parts[2]
|
||||
if len(year_part) == 2:
|
||||
yy = int(year_part)
|
||||
year = 2000 + yy if yy < 70 else 1900 + yy
|
||||
else:
|
||||
year = int(year_part)
|
||||
return datetime.date(year, month, day)
|
||||
|
||||
|
||||
class Class_Date(SympyClassBase):
|
||||
"""Clase de fecha con aritmética de días y semanas.
|
||||
|
||||
Internamente la fecha se almacena como el ordinal devuelto por
|
||||
:py:meth:`datetime.date.toordinal`, lo que facilita la aritmética con
|
||||
números enteros (p.ej. añadir días).
|
||||
"""
|
||||
|
||||
_op_priority = 20 # preferencia alta en operaciones de SymPy
|
||||
|
||||
def __init__(self, value: Union[str, datetime.date, int, sympy.Expr]):
|
||||
# Manejar expresiones SymPy (p. ej. símbolos o enteros simbólicos)
|
||||
if isinstance(value, sympy.Expr) and not isinstance(value, sympy.Integer):
|
||||
# Modo simbólico puro (no intentamos resolver a fecha real)
|
||||
self._ordinal_expr = value
|
||||
self._has_symbols = True
|
||||
date_repr = str(value)
|
||||
else:
|
||||
# Convertir todos los casos a «datetime.date» y ordinal entero
|
||||
if isinstance(value, datetime.date):
|
||||
date_obj = value
|
||||
elif isinstance(value, str):
|
||||
date_obj = _parse_date_str(value)
|
||||
elif isinstance(value, (int, sympy.Integer)):
|
||||
# Se interpreta como ordinal absoluto
|
||||
date_obj = datetime.date.fromordinal(int(value))
|
||||
else:
|
||||
raise TypeError(
|
||||
"value debe ser str, datetime.date o entero (ordinal), "
|
||||
f"recibido: {type(value)}"
|
||||
)
|
||||
|
||||
self._ordinal = date_obj.toordinal()
|
||||
self._has_symbols = False
|
||||
self._date = date_obj
|
||||
self._ordinal_expr = sympy.Integer(self._ordinal)
|
||||
date_repr = self._date.isoformat()
|
||||
|
||||
super().__init__(self._ordinal_expr, date_repr)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Representación
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def __repr__(self): # noqa: D401
|
||||
return f"Date('{self.__str__()}')"
|
||||
|
||||
def __str__(self): # noqa: D401
|
||||
if self._has_symbols:
|
||||
return str(self._ordinal_expr)
|
||||
return self._date.strftime("%d/%m/%y")
|
||||
|
||||
def _sympystr(self, printer): # pragma: no cover
|
||||
return str(self)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Conversión
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def to_datetime(self) -> datetime.date:
|
||||
"""Devuelve la fecha como objeto ``datetime.date``."""
|
||||
if self._has_symbols:
|
||||
raise ValueError(
|
||||
"La fecha es simbólica y no se puede convertir a 'datetime.date'."
|
||||
)
|
||||
return self._date
|
||||
|
||||
def to_ordinal(self) -> int:
|
||||
"""Devuelve el ordinal (días desde 01-01-0001)."""
|
||||
return int(self._ordinal_expr)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Aritmética
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def __add__(self, other):
|
||||
"""Añadir días o expresiones simbólicas: ``fecha + n`` o ``fecha + símbolo``."""
|
||||
# Soporte para expresiones SymPy (símbolos, enteros simbólicos, etc.)
|
||||
if isinstance(other, sympy.Expr):
|
||||
new_expr = self._ordinal_expr + other
|
||||
return Class_Date(new_expr)
|
||||
if hasattr(other, "__int__"):
|
||||
return self.add_days(int(other))
|
||||
return NotImplemented
|
||||
|
||||
__radd__ = __add__
|
||||
|
||||
def __sub__(self, other):
|
||||
"""Resta días o devuelve diferencia: ``fecha - n``, ``fecha - símbolo``, o diferencia entre fechas."""
|
||||
if isinstance(other, sympy.Expr):
|
||||
# Restar expresión simbólica -> nueva fecha simbólica
|
||||
new_expr = self._ordinal_expr - other
|
||||
return Class_Date(new_expr)
|
||||
if hasattr(other, "__int__"):
|
||||
return self.add_days(-int(other))
|
||||
if isinstance(other, Class_Date):
|
||||
# Diferencia (puede ser simbólica)
|
||||
return self._ordinal_expr - other._ordinal_expr
|
||||
return NotImplemented
|
||||
|
||||
def add_days(self, n: int) -> "Class_Date":
|
||||
"""Devuelve una nueva fecha desplazada *n* días."""
|
||||
if self._has_symbols:
|
||||
new_expr = self._ordinal_expr + n
|
||||
return Class_Date(new_expr)
|
||||
else:
|
||||
new_date = self._date + datetime.timedelta(days=n)
|
||||
return Class_Date(new_date)
|
||||
|
||||
def add_weeks(self, n: int) -> "Class_Date":
|
||||
"""Devuelve una nueva fecha desplazada *n* semanas."""
|
||||
return self.add_days(n * 7)
|
||||
|
||||
def next_day(self) -> "Class_Date":
|
||||
return self.add_days(1)
|
||||
|
||||
def previous_day(self) -> "Class_Date":
|
||||
return self.add_days(-1)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Información de calendario
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
_WEEKDAY_NAMES_ES = [
|
||||
"Lunes",
|
||||
"Martes",
|
||||
"Miércoles",
|
||||
"Jueves",
|
||||
"Viernes",
|
||||
"Sábado",
|
||||
"Domingo",
|
||||
]
|
||||
|
||||
def day_of_week(self) -> str:
|
||||
"""Nombre del día de la semana en español (Lunes-Domingo)."""
|
||||
if self._has_symbols:
|
||||
raise ValueError(
|
||||
"La fecha es simbólica; no se puede determinar el día de la semana."
|
||||
)
|
||||
weekday = self._date.weekday() # 0 = lunes
|
||||
return self._WEEKDAY_NAMES_ES[weekday]
|
||||
|
||||
def is_weekend(self) -> bool:
|
||||
if self._has_symbols:
|
||||
raise ValueError(
|
||||
"La fecha es simbólica; no se puede determinar si es fin de semana."
|
||||
)
|
||||
return self._date.weekday() >= 5
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Días hábiles
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def is_business_day(self, holidays: Optional[List[datetime.date]] = None) -> bool:
|
||||
"""Indica si es día laborable (L-V) y no festivo.
|
||||
|
||||
Args:
|
||||
holidays: lista opcional de festivos como ``datetime.date``.
|
||||
"""
|
||||
if self._has_symbols:
|
||||
raise ValueError(
|
||||
"La fecha es simbólica; no se puede evaluar si es laborable."
|
||||
)
|
||||
if self.is_weekend():
|
||||
return False
|
||||
if holidays and self._date in holidays:
|
||||
return False
|
||||
return True
|
||||
|
||||
def next_business_day(
|
||||
self, holidays: Optional[List[datetime.date]] = None
|
||||
) -> "Class_Date":
|
||||
"""Devuelve el siguiente día laborable."""
|
||||
candidate = self.next_day()
|
||||
while not candidate.is_business_day(holidays):
|
||||
candidate = candidate.next_day()
|
||||
return candidate
|
||||
|
||||
def business_days_between(
|
||||
self, other: "Class_Date", holidays: Optional[List[datetime.date]] = None
|
||||
) -> int:
|
||||
"""Cuenta días laborables entre dos fechas (excluye la inicial, incluye la final si procede)."""
|
||||
if not isinstance(other, Class_Date):
|
||||
raise TypeError("other debe ser Class_Date")
|
||||
start_ord = min(self.to_ordinal(), other.to_ordinal()) + 1
|
||||
end_ord = max(self.to_ordinal(), other.to_ordinal())
|
||||
count = 0
|
||||
for ord_val in range(start_ord, end_ord + 1):
|
||||
d = Class_Date(ord_val)
|
||||
if d.is_business_day(holidays):
|
||||
count += 1
|
||||
return count
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Diferencias
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def days_between(self, other: "Class_Date") -> int:
|
||||
"""Número absoluto de días entre dos fechas."""
|
||||
if not isinstance(other, Class_Date):
|
||||
raise TypeError("other debe ser Class_Date")
|
||||
return abs(self - other)
|
||||
|
||||
def weeks_between(self, other: "Class_Date") -> float:
|
||||
"""Número de semanas (con decimales) entre dos fechas."""
|
||||
return self.days_between(other) / 7
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Comparaciones y ecuaciones simbólicas
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def __eq__(self, other): # type: ignore[override]
|
||||
"""Devuelve una ecuación SymPy en vez de comparar booleanamente cuando se usa con objetos Date o expresiones SymPy."""
|
||||
if isinstance(other, Class_Date):
|
||||
return sympy.Eq(self._ordinal_expr, other._ordinal_expr)
|
||||
if isinstance(other, sympy.Expr):
|
||||
return sympy.Eq(self._ordinal_expr, other)
|
||||
return NotImplemented
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Helper para autocompletado & paneles GUI
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@staticmethod
|
||||
def Helper(input_str): # pragma: no cover
|
||||
if input_str.strip().lower().startswith("date("):
|
||||
return (
|
||||
"Ej: date('10/5/25'), date('2025-05-10')\n" # noqa: E501
|
||||
"Funciones: add_days(), add_weeks(), next_business_day(), day_of_week(), is_weekend(), is_business_day(), days_between()…"
|
||||
)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def PopupFunctionList(): # pragma: no cover
|
||||
return [
|
||||
("add_days", "Suma N días"),
|
||||
("add_weeks", "Suma N semanas"),
|
||||
("next_day", "Día siguiente"),
|
||||
("previous_day", "Día anterior"),
|
||||
("next_business_day", "Siguiente día hábil"),
|
||||
("is_business_day", "¿Es día laborable?"),
|
||||
("day_of_week", "Nombre del día de la semana"),
|
||||
("is_weekend", "¿Es fin de semana?"),
|
||||
("days_between", "Días entre dos fechas"),
|
||||
("weeks_between", "Semanas entre dos fechas"),
|
||||
("to_datetime", "Convertir a datetime.date"),
|
||||
("to_ordinal", "Ordinal (nº de días)"),
|
||||
]
|
||||
|
||||
def today(cls) -> "Class_Date":
|
||||
"""Fecha actual (según *datetime.date.today()*)."""
|
||||
return cls(datetime.date.today())
|
||||
|
||||
# Alias estático para consistencia con otras clases
|
||||
Today = classmethod(today)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Alias de usuario final — ajustado al sistema de tokenización existente
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
|
||||
def Date(value: Union[str, datetime.date, int, sympy.Expr]): # noqa: N802
|
||||
"""Alias que imita la sintaxis ``date('…')`` empleada por el usuario."""
|
||||
return Class_Date(value)
|
||||
|
||||
|
||||
def Today(): # noqa: N802
|
||||
"""Construye un objeto Date con la fecha actual (hoy)."""
|
||||
return Class_Date.today()
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Registro para el sistema de tipos dinámico
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
|
||||
def register_classes_in_module(): # pragma: no cover
|
||||
"""Devuelve la lista de clases para el registro automático."""
|
||||
return [
|
||||
(
|
||||
"Date",
|
||||
Class_Date,
|
||||
"SympyClassBase",
|
||||
{
|
||||
"add_lowercase": True, # permite usar ``date`` minúscula
|
||||
"supports_brackets": False,
|
||||
"description": "Fechas con aritmética y utilidades de calendario",
|
||||
},
|
||||
),
|
||||
(
|
||||
"Today",
|
||||
Today,
|
||||
"function",
|
||||
{
|
||||
"add_lowercase": True,
|
||||
"supports_brackets": False,
|
||||
"description": "Fecha actual (hoy)",
|
||||
},
|
||||
),
|
||||
]
|
Loading…
Reference in New Issue