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
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:
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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()}")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user