Added api & route handling
Some checks failed
Build on push / prepare (push) Successful in 9s
Build on push / core (push) Successful in 19s
Build on push / query (push) Successful in 19s
Build on push / dependency (push) Successful in 17s
Build on push / application (push) Successful in 15s
Build on push / database (push) Successful in 18s
Build on push / mail (push) Successful in 19s
Build on push / translation (push) Successful in 23s
Build on push / auth (push) Successful in 16s
Build on push / api (push) Failing after 14s

This commit is contained in:
2025-09-19 21:03:33 +02:00
parent 1a67318091
commit ddc62dfb9a
34 changed files with 568 additions and 42 deletions

View File

@@ -2,6 +2,7 @@ import inspect
import json
import os
import sys
from inspect import isclass
from typing import Any
from cpl.core.configuration.configuration_model_abc import ConfigurationModelABC
@@ -126,7 +127,11 @@ class Configuration:
@classmethod
def get(cls, key: Any, default: D = None) -> T | D:
if inspect.isclass(key):
key = key.__name__
key_name = key.__name__ if inspect.isclass(key) else key
return cls._config.get(key, default)
result = cls._config.get(key_name, default)
if issubclass(key, ConfigurationModelABC) and result == default:
result = key()
cls.set(key, result)
return result

View File

@@ -49,7 +49,7 @@ class ConfigurationModelABC(ABC):
String.first_to_lower(field),
String.to_camel_case(field),
String.to_snake_case(field),
String.first_to_upper(String.to_camel_case(field)),
String.to_pascal_case(field),
]
value = None
@@ -64,7 +64,8 @@ class ConfigurationModelABC(ABC):
env_field = field.upper()
if self._env_prefix:
env_field = f"{self._env_prefix}_{env_field}"
value = Environment.get(env_field, cast_type)
value = cast(Environment.get(env_field, str), cast_type)
if value is None and required:
raise ValueError(f"{field} is required")

View File

@@ -1,4 +1,4 @@
from .logger import Logger
from .logger_abc import LoggerABC
from .log_level_enum import LogLevel
from .logging_settings import LogSettings
from .log_level import LogLevel
from .log_settings import LogSettings

View File

@@ -1,7 +1,7 @@
from typing import Optional
from cpl.core.configuration.configuration_model_abc import ConfigurationModelABC
from cpl.core.log.log_level_enum import LogLevel
from cpl.core.log.log_level import LogLevel
class LogSettings(ConfigurationModelABC):
@@ -10,9 +10,9 @@ class LogSettings(ConfigurationModelABC):
self,
src: Optional[dict] = None,
):
ConfigurationModelABC.__init__(self, src)
ConfigurationModelABC.__init__(self, src, "LOG")
self.option("path", str, default="logs")
self.option("filename", str, default="app.log")
self.option("console_level", LogLevel, default=LogLevel.info)
self.option("console", LogLevel, default=LogLevel.info)
self.option("level", LogLevel, default=LogLevel.info)

View File

@@ -3,13 +3,12 @@ import traceback
from datetime import datetime
from cpl.core.console import Console
from cpl.core.log.log_level_enum import LogLevel
from cpl.core.log.log_level import LogLevel
from cpl.core.log.logger_abc import LoggerABC
from cpl.core.typing import Messages, Source
class Logger(LoggerABC):
_level = LogLevel.info
_levels = [x for x in LogLevel]
# ANSI color codes for different log levels
@@ -36,6 +35,13 @@ class Logger(LoggerABC):
self._file_prefix = file_prefix
self._create_log_dir()
@property
def _settings(self):
from cpl.core.configuration.configuration import Configuration
from cpl.core.log.log_settings import LogSettings
return Configuration.get(LogSettings)
@property
def log_file(self):
return f"logs/{self._file_prefix}_{datetime.now().strftime('%Y-%m-%d')}.log"
@@ -65,23 +71,32 @@ class Logger(LoggerABC):
f"{log_file.split('.log')[0]}_{datetime.now().strftime('%H-%M-%S')}.log",
)
def _write_log_to_file(self, content: str):
def _should_log(self, input_level: LogLevel, settings_level: LogLevel) -> bool:
return self._levels.index(input_level) >= self._levels.index(settings_level)
def _write_log_to_file(self, level: LogLevel, content: str):
if not self._should_log(level, self._settings.level):
return
file = self.log_file
self._ensure_file_size(file)
with open(file, "a") as log_file:
log_file.write(content + "\n")
log_file.close()
def _write_to_console(self, level: LogLevel, content: str):
if not self._should_log(level, self._settings.console):
return
Console.write_line(f"{self._COLORS.get(level, '\033[0m')}{content}\033[0m")
def _log(self, level: LogLevel, *messages: Messages):
try:
if self._levels.index(level) < self._levels.index(self._level):
return
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
formatted_message = self._format_message(level.value, timestamp, *messages)
self._write_log_to_file(formatted_message)
Console.write_line(f"{self._COLORS.get(level, '\033[0m')}{formatted_message}\033[0m")
self._write_log_to_file(level, formatted_message)
self._write_to_console(level, formatted_message)
except Exception as e:
print(f"Error while logging: {e} -> {traceback.format_exc()}")

View File

@@ -1,6 +1,6 @@
from abc import abstractmethod, ABC
from cpl.core.log.log_level_enum import LogLevel
from cpl.core.log.log_level import LogLevel
from cpl.core.typing import Messages

View File

@@ -3,6 +3,7 @@ from typing import Type, Any
from cpl.core.typing import T
def _cast_enum(value: str, enum_type: Type[Enum]) -> Enum:
try:
return enum_type(value)
@@ -36,6 +37,7 @@ def _cast_enum(value: str, enum_type: Type[Enum]) -> Enum:
raise ValueError(f"Cannot cast value '{value}' to enum '{enum_type.__name__}'")
def cast(value: Any, cast_type: Type[T], list_delimiter: str = ",") -> T:
"""
Cast a value to a specified type.
@@ -44,12 +46,12 @@ def cast(value: Any, cast_type: Type[T], list_delimiter: str = ",") -> T:
:param str list_delimiter: The delimiter to split the value into a list. Defaults to ",".
:return:
"""
if value is None:
return None
if cast_type == bool:
return value.lower() in ["true", "1", "yes", "on"]
if issubclass(cast_type, Enum):
return _cast_enum(value, cast_type)
if (cast_type if not hasattr(cast_type, "__origin__") else cast_type.__origin__) == list:
if not (value.startswith("[") and value.endswith("]")) and list_delimiter not in value:
raise ValueError("List values must be enclosed in square brackets or use a delimiter.")
@@ -61,4 +63,7 @@ def cast(value: Any, cast_type: Type[T], list_delimiter: str = ",") -> T:
subtype = cast_type.__args__[0] if hasattr(cast_type, "__args__") else None
return [subtype(item) if subtype is not None else item for item in value]
return cast_type(value)
if isinstance(cast_type, type) and issubclass(cast_type, Enum):
return _cast_enum(value, cast_type)
return cast_type(value)

View File

@@ -38,6 +38,9 @@ def get_value(
return value
try:
cast(cast_type, value, list_delimiter)
cast(value, cast_type, list_delimiter)
except (ValueError, TypeError):
from cpl.core.log import Logger
Logger(__name__).debug(f"Failed to cast value '{value}' to type '{cast_type.__name__}'")
return default

View File

@@ -18,10 +18,35 @@ class String:
String converted to CamelCase
"""
s = String.to_snake_case(s)
words = s.split('_')
return words[0] + ''.join(word.title() for word in words[1:])
# return re.sub(r"(?<!^)(?=[A-Z])", "_", s).lower()
parts = re.split(r"[^a-zA-Z0-9]+", s.strip())
parts = [p for p in parts if p]
if not parts:
return ""
return parts[0].lower() + "".join(word.capitalize() for word in parts[1:])
@staticmethod
def to_pascal_case(s: str) -> str:
r"""Converts string to pascal case
Parameter:
chars: :class:`str`
String to convert
Returns:
String converted to PascalCase
"""
parts = re.split(r"[^a-zA-Z0-9]+", s.strip())
parts = [p for p in parts if p]
if not parts:
return ""
return "".join(word.capitalize() for word in parts)
@staticmethod
def to_snake_case(chars: str) -> str: