#wip: Base built. Starting with controller.
This commit is contained in:
parent
91811acef4
commit
d0ad71bb46
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/Data
|
||||||
|
__pycache__
|
||||||
|
*.[Ss]ecrets.*
|
||||||
|
*.[Ss]ecret.*
|
||||||
4
JSON/CXCV.commands.json
Normal file
4
JSON/CXCV.commands.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[{
|
||||||
|
"names" : ["close", "exit", "quit", "bye", "shutdown"],
|
||||||
|
"action" : "cxcv.terminal.close"
|
||||||
|
}]
|
||||||
8
JSON/CXCV.settings.json
Normal file
8
JSON/CXCV.settings.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"default_settings_files" : ["/JSON/CXCV.settings.json"],
|
||||||
|
"secrets_settings_files" : ["/JSON/CXCV.secrets.json"],
|
||||||
|
"default_commands_files" : ["/JSON/CXCV.commands.json"],
|
||||||
|
"print_format" : "[{type}] {yyyy}{mm}{dd} {hh}{ii}{ss} [{line}]{file}({method}): {message}",
|
||||||
|
"exception_format" : " '{file}({method})[{line}]'{lines}\n\n{exception_message}",
|
||||||
|
"default_chunk_size" : 4096
|
||||||
|
}
|
||||||
3
JSON/I18N/CXCV.i18n.english.json
Normal file
3
JSON/I18N/CXCV.i18n.english.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"english" : {}
|
||||||
|
}
|
||||||
3
JSON/I18N/CXCV.i18n.espanol.json
Normal file
3
JSON/I18N/CXCV.i18n.espanol.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"espanol" : {}
|
||||||
|
}
|
||||||
3
JSON/I18N/CXCV.i18n.galego.json
Normal file
3
JSON/I18N/CXCV.i18n.galego.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"galego" : {}
|
||||||
|
}
|
||||||
17
Python/Abstracts/ControllerAbstract.py
Normal file
17
Python/Abstracts/ControllerAbstract.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Optional, Any
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
from Utils.Utils import Utils
|
||||||
|
|
||||||
|
class ControllerAbstract:
|
||||||
|
|
||||||
|
def __init__(self:Self,
|
||||||
|
cxcv:CXCVInterface,
|
||||||
|
key:str,
|
||||||
|
inputs:Optional[dict[str, Any|None]|tuple[Any|None, ...]|list[Any|None]] = None
|
||||||
|
) -> None:
|
||||||
|
self.cxcv:CXCVInterface = cxcv
|
||||||
|
self.key:str = key
|
||||||
|
self.via:str = Utils.get_value("via", inputs, "sqlite")
|
||||||
42
Python/Abstracts/DatabasesAbstract copy.py
Normal file
42
Python/Abstracts/DatabasesAbstract copy.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
from Models.ResultsModel import ResultsModel
|
||||||
|
from typing import Optional, Self, Any
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
class DatabasesAbstract(ABC):
|
||||||
|
|
||||||
|
def __init__(self:Self,
|
||||||
|
cxcv:CXCVInterface,
|
||||||
|
key:str,
|
||||||
|
inputs:dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]
|
||||||
|
) -> None:
|
||||||
|
self.cxcv:CXCVInterface = cxcv
|
||||||
|
self.key:str = key
|
||||||
|
self.type:str = "UNKNOWN"
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def connect(self:Self) -> bool:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def close(self:Self) -> bool:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def execute(self:Self,
|
||||||
|
sql:str,
|
||||||
|
parameters:Optional[dict[str, Any|None]] = None
|
||||||
|
) -> ResultsModel:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def execute_file(self:Self,
|
||||||
|
path:str,
|
||||||
|
parameters:Optional[dict[str, Any|None]] = None
|
||||||
|
) -> ResultsModel:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_id(self:Self, query:str, parameters:Optional[dict[str, Any|None]] = None) -> int|None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_last_id(self:Self) -> int|None:pass
|
||||||
42
Python/Abstracts/DatabasesAbstract.py
Normal file
42
Python/Abstracts/DatabasesAbstract.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
from Models.ResultsModel import ResultsModel
|
||||||
|
from typing import Optional, Self, Any
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
class DatabasesAbstract(ABC):
|
||||||
|
|
||||||
|
def __init__(self:Self,
|
||||||
|
cxcv:CXCVInterface,
|
||||||
|
key:str,
|
||||||
|
inputs:dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]
|
||||||
|
) -> None:
|
||||||
|
self.cxcv:CXCVInterface = cxcv
|
||||||
|
self.key:str = key
|
||||||
|
self.type:str = "UNKNOWN"
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def connect(self:Self) -> bool:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def close(self:Self) -> bool:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def execute(self:Self,
|
||||||
|
sql:str,
|
||||||
|
parameters:Optional[dict[str, Any|None]] = None
|
||||||
|
) -> ResultsModel:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def execute_file(self:Self,
|
||||||
|
path:str,
|
||||||
|
parameters:Optional[dict[str, Any|None]] = None
|
||||||
|
) -> ResultsModel:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_id(self:Self, query:str, parameters:Optional[dict[str, Any|None]] = None) -> int|None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_last_id(self:Self) -> int|None:pass
|
||||||
17
Python/Abstracts/ProcedureAbstract.py
Normal file
17
Python/Abstracts/ProcedureAbstract.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Optional, Any
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
|
||||||
|
class ProcedureAbstract:
|
||||||
|
|
||||||
|
def __init__(self:Self,
|
||||||
|
cxcv:CXCVInterface,
|
||||||
|
key:str,
|
||||||
|
via:str,
|
||||||
|
inputs:Optional[dict[str, Any|None]|tuple[Any|None, ...]|list[Any|None]] = None
|
||||||
|
) -> None:
|
||||||
|
self.cxcv:CXCVInterface = cxcv
|
||||||
|
self.key:str = key
|
||||||
|
self.via:str = via
|
||||||
16
Python/Abstracts/TableAbstract.py
Normal file
16
Python/Abstracts/TableAbstract.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Any
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
class TableAbstract(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get(self:Self) -> tuple[tuple[str]|None, tuple[tuple[Any|None, ...], ...]]:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_tuples(self:Self) -> tuple[tuple[Any|None, ...], ...]:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_dictionaries(self:Self) -> dict[str, Any|None]:pass
|
||||||
137
Python/Application/CXCV.py
Normal file
137
Python/Application/CXCV.py
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from os.path import dirname as directory_name, abspath as path_absolute
|
||||||
|
|
||||||
|
ROOT_PATH:str = directory_name(path_absolute(__file__))
|
||||||
|
|
||||||
|
from typing import Self, Any, Optional
|
||||||
|
from datetime import datetime
|
||||||
|
from re import Match as REMatch
|
||||||
|
from Drivers.FilesDriver import FilesDriver
|
||||||
|
from Managers.SettingsManager import SettingsManager
|
||||||
|
from Managers.I18NManager import I18NManager
|
||||||
|
from Managers.TerminalManager import TerminalManager
|
||||||
|
from Managers.ModelsManager import ModelsManager
|
||||||
|
from Managers.DatabasesManager import DatabasesManager
|
||||||
|
from Managers.ControllersManager import ControllersManager
|
||||||
|
from Managers.ProceduresManager import ProceduresManager
|
||||||
|
from Controllers.LogsController import LogsController
|
||||||
|
from Utils.Utils import Utils
|
||||||
|
from Utils.Patterns import RE
|
||||||
|
|
||||||
|
class CXCV:
|
||||||
|
|
||||||
|
def __init__(self:Self,
|
||||||
|
inputs:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]] = None
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
project_root_path:str = ROOT_PATH[:ROOT_PATH.index("/CXCV") + 6]
|
||||||
|
|
||||||
|
self.__print_format:str = "[{yyyy}-{mm}-{dd} {hh}:{ii}:{ss}] [{type}] {message}"
|
||||||
|
self.__exception_format:str = "\nException in '{file}({method})[{line}]'{lines}\n\n{exception_message}"
|
||||||
|
self.working:bool = True
|
||||||
|
|
||||||
|
self.files:FilesDriver = FilesDriver(self, ROOT_PATH)
|
||||||
|
self.settings:SettingsManager = SettingsManager(self, inputs)
|
||||||
|
|
||||||
|
self.__print_format = self.settings.get("print_format", None, self.__print_format)
|
||||||
|
self.__exception_format = self.settings.get("exception_format", None, self.__exception_format)
|
||||||
|
|
||||||
|
self.i18n:I18NManager = I18NManager(self)
|
||||||
|
self.terminal:TerminalManager = TerminalManager(self)
|
||||||
|
self.models:ModelsManager = ModelsManager(self)
|
||||||
|
self.databases:DatabasesManager = DatabasesManager(self)
|
||||||
|
self.controllers:ControllersManager = ControllersManager(self)
|
||||||
|
self.procedures:ProceduresManager = ProceduresManager(self)
|
||||||
|
|
||||||
|
self.terminal.start()
|
||||||
|
|
||||||
|
def close(self:Self) -> None:
|
||||||
|
self.working = False
|
||||||
|
|
||||||
|
def print(self:Self,
|
||||||
|
_type:str,
|
||||||
|
string:str,
|
||||||
|
inputs:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]] = None,
|
||||||
|
i:int = 0
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
date:datetime = datetime.now()
|
||||||
|
own:dict[str, Any|None] = {
|
||||||
|
"raw_type" : _type,
|
||||||
|
"type" : _type.upper()[:4],
|
||||||
|
"i18n" : Utils.get_strings(string)[0],
|
||||||
|
"message" : self.i18n.get(string, inputs),
|
||||||
|
**Utils.get_action_data(i + 1),
|
||||||
|
**Utils.get_dictionary(inputs)
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
own["file"] = own["file"][str(own["file"]).index("/CXCV/"):]
|
||||||
|
except Exception as exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
while len(own["type"]) < 4:
|
||||||
|
own["type"] = " " + own["type"] if len(own["type"]) % 2 else own["type"] + " "
|
||||||
|
|
||||||
|
for key in ("year", "month", "day", "hour", "minute", "second"):
|
||||||
|
|
||||||
|
k:str = key[0] if key != "minute" else "i"
|
||||||
|
|
||||||
|
own[k] = own[key] = getattr(date, key)
|
||||||
|
own[k + k] = ("00" + str(own[key] % 100))[-2:]
|
||||||
|
|
||||||
|
own["yyyy"] = ("0000" + str(own["year"]))[-4:]
|
||||||
|
|
||||||
|
if "end" in own:
|
||||||
|
own["message"] += own["end"]
|
||||||
|
|
||||||
|
print(Utils.string_variables(self.__print_format, own))
|
||||||
|
|
||||||
|
if not self.controllers:
|
||||||
|
return
|
||||||
|
|
||||||
|
controller:LogsController|None = self.controllers.get(
|
||||||
|
LogsController, "logs"
|
||||||
|
)
|
||||||
|
|
||||||
|
controller and own["type"] != "EXCE" and controller.set_log(own["i18n"], (
|
||||||
|
1 if own["type"] == "EXCE" else
|
||||||
|
2 if own["type"] == "WARN" else
|
||||||
|
3 if own["type"] == "ERRO" else
|
||||||
|
0), inputs or {}, i)
|
||||||
|
|
||||||
|
def exception(self,
|
||||||
|
exception:Exception,
|
||||||
|
message:Optional[str|list|tuple] = None,
|
||||||
|
inputs:Optional[dict[str, Any]|list|tuple] = None,
|
||||||
|
i:Optional[int] = 0
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
lines:list[str]|None = Utils.get_trace(exception)
|
||||||
|
line_matches:REMatch[str]|None = RE.EXCEPTION.match(lines[-1])
|
||||||
|
data:dict[str, Any|None] = {
|
||||||
|
**{key : value for subset in (inputs if isinstance(inputs, (list, tuple)) else (inputs,)) for key, value in (subset if isinstance(subset, dict) else {}).items()},
|
||||||
|
**Utils.get_action_data(1),
|
||||||
|
"lines" : "".join("\n " + RE.BREAK_LINES.split(line.strip())[0] for line in lines),
|
||||||
|
"exception_message" : str(exception),
|
||||||
|
"method" : line_matches.group(3),
|
||||||
|
"line" : line_matches.group(2),
|
||||||
|
"file" : line_matches.group(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
data["end"] = Utils.string_variables(self.__exception_format, data)
|
||||||
|
|
||||||
|
if message:
|
||||||
|
|
||||||
|
self.print("exception", message, data, None, i + 1)
|
||||||
|
|
||||||
|
if not self.controllers:
|
||||||
|
return
|
||||||
|
|
||||||
|
controller:LogsController|None = self.controllers.get(
|
||||||
|
LogsController, "logs"
|
||||||
|
)
|
||||||
|
|
||||||
|
controller and controller.set_exception(exception, message, inputs or {}, None, i)
|
||||||
39
Python/Controllers/LogsController.py
Normal file
39
Python/Controllers/LogsController.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Any, Optional
|
||||||
|
from Abstracts.ControllerAbstract import ControllerAbstract
|
||||||
|
from Interfaces.Procedures.LogsProcedureInterface import LogsProcedureInterface
|
||||||
|
from Utils.Utils import Utils
|
||||||
|
|
||||||
|
class LogsController(ControllerAbstract):
|
||||||
|
|
||||||
|
def set_log(self:Self,
|
||||||
|
message:str,
|
||||||
|
error:int = 0,
|
||||||
|
parameters:dict[str, Any|None] = {},
|
||||||
|
i:int = 0
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
data:dict[str, Any|None] = Utils.get_action_data(i + 2)
|
||||||
|
|
||||||
|
self.cxcv.procedures.get(
|
||||||
|
LogsProcedureInterface, (self.key, self.via)
|
||||||
|
).set_log("CXCV", data["file"], data["method"], data["line"], message, error, parameters)
|
||||||
|
|
||||||
|
def set_exception(self:Self,
|
||||||
|
exception:Exception,
|
||||||
|
message:str,
|
||||||
|
parameters:dict[str, Any|None] = {},
|
||||||
|
status:Optional[str] = None,
|
||||||
|
i:int = 0
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
trace:list[str] = Utils.get_trace(exception)
|
||||||
|
data:dict[str, Any|None] = Utils.get_action_data(i + 2)
|
||||||
|
|
||||||
|
self.cxcv.procedures.get(
|
||||||
|
LogsProcedureInterface, (self.key, self.via)
|
||||||
|
).set_exception("CXCV", data["file"], data["method"], data["line"], message, str(exception), "\n".join(
|
||||||
|
"- " + line for line in trace
|
||||||
|
), parameters, status)
|
||||||
45
Python/Controllers/OrdersController.py
Normal file
45
Python/Controllers/OrdersController.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Any, Optional
|
||||||
|
from Abstracts.ControllerAbstract import ControllerAbstract
|
||||||
|
from Models.FileModel import FileModel
|
||||||
|
from Models.HashModel import HashModel
|
||||||
|
from Interfaces.FilesInterface import FilesInterface
|
||||||
|
from Utils.Utils import Utils
|
||||||
|
|
||||||
|
class OrdersController(ControllerAbstract):
|
||||||
|
|
||||||
|
def execute(self:Self,
|
||||||
|
inputs:dict[str, Any|None]|tuple[Any|None, ...]|list[Any|None]
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
file_name:str
|
||||||
|
_from:FilesInterface = FileModel.get_driver(self.cxcv, FileModel.get_inputs({
|
||||||
|
"mode" : "rb"
|
||||||
|
}, Utils.get_value("from", inputs)))
|
||||||
|
_to:FilesInterface = FileModel.get_driver(self.cxcv, FileModel.get_inputs({
|
||||||
|
"mode" : "wb"
|
||||||
|
}, Utils.get_value("to", inputs)))
|
||||||
|
from_path:str = Utils.get_value("from_path", inputs)
|
||||||
|
to_path:str = Utils.get_value("to_path", inputs)
|
||||||
|
tries:tuple[int, ...] = tuple(range(Utils.get_value("tries", inputs, 3)))
|
||||||
|
action:str = Utils.get_value("action", inputs, "copy")
|
||||||
|
remove_from:bool = action in ("move", "cut", "remove")
|
||||||
|
save_to:bool = action in ("copy", "move", "cut")
|
||||||
|
|
||||||
|
for file_name in _from.list():
|
||||||
|
|
||||||
|
try_i:int
|
||||||
|
|
||||||
|
for try_i in tries:
|
||||||
|
|
||||||
|
_from.open(from_path + "/" + file_name)
|
||||||
|
_to.reset(to_path + "/" + file_name)
|
||||||
|
|
||||||
|
if _from.is_file:
|
||||||
|
pass
|
||||||
|
elif _from.is_directory:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
break
|
||||||
191
Python/Drivers/FilesDriver.py
Normal file
191
Python/Drivers/FilesDriver.py
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Any, Callable
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
from Utils.Patterns import RE
|
||||||
|
from Utils.Utils import Utils
|
||||||
|
from os.path import exists as path_exists, isfile as is_file, isdir as is_directory
|
||||||
|
from os import mkdir as make_directory, remove as remove_file
|
||||||
|
from os import listdir as list_directory, stat as get_stat, stat_result as StatResult
|
||||||
|
from shutil import rmtree as remove_directory
|
||||||
|
|
||||||
|
class FilesDriver:
|
||||||
|
|
||||||
|
def __init__(self:Self, cxcv:CXCVInterface, root_path:str) -> None:
|
||||||
|
self.cxcv:CXCVInterface = cxcv
|
||||||
|
|
||||||
|
self.root_paths:tuple[str, ...] = ("", root_path)
|
||||||
|
self.slash:str = "/" if "/" in root_path else "\\"
|
||||||
|
|
||||||
|
for _ in range(2):
|
||||||
|
self.root_paths += (RE.LAST_PATH_ITEM.sub(r'', self.root_paths[-1]),)
|
||||||
|
|
||||||
|
def fix_path(self:Self, path:str) -> str:
|
||||||
|
return RE.SLASHES.sub(self.slash, path)
|
||||||
|
|
||||||
|
def exists(self:Self, path:str, analyze_roots:bool = True) -> bool:
|
||||||
|
if analyze_roots:
|
||||||
|
return self.get_absolute_path(path) is not None
|
||||||
|
return path_exists(path)
|
||||||
|
|
||||||
|
def is_file(self:Self, path:str, analyze_roots:bool = True) -> bool:
|
||||||
|
if analyze_roots and (path := self.get_absolute_path(path)) is None:
|
||||||
|
return False
|
||||||
|
return is_file(path)
|
||||||
|
|
||||||
|
def is_directory(self:Self, path:str, analyze_roots:bool = True) -> bool:
|
||||||
|
if analyze_roots and (path := self.get_absolute_path(path)) is None:
|
||||||
|
return False
|
||||||
|
return is_directory(path)
|
||||||
|
|
||||||
|
def get_absolute_path(self:Self, path:str) -> str|None:
|
||||||
|
|
||||||
|
root:str
|
||||||
|
|
||||||
|
for root in self.root_paths:
|
||||||
|
absolute_path:str = self.fix_path((root + self.slash if root else "") + path)
|
||||||
|
if self.exists(absolute_path, False):
|
||||||
|
return absolute_path
|
||||||
|
return None
|
||||||
|
|
||||||
|
def load(self:Self, path:str, mode:str = "r") -> str|bytes|None:
|
||||||
|
try:
|
||||||
|
|
||||||
|
absolute_path:str = self.get_absolute_path(path)
|
||||||
|
|
||||||
|
if absolute_path is not None and is_file(absolute_path):
|
||||||
|
with open(absolute_path, mode) as file:
|
||||||
|
return file.read()
|
||||||
|
|
||||||
|
except Exception as exception:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
def load_by_chunks(self:Self,
|
||||||
|
path:str,
|
||||||
|
callback:Callable[[bytes], None],
|
||||||
|
chunk_size:int = 4096
|
||||||
|
) -> None:
|
||||||
|
try:
|
||||||
|
|
||||||
|
absolute_path:str = self.get_absolute_path(path)
|
||||||
|
|
||||||
|
if absolute_path is not None and is_file(absolute_path):
|
||||||
|
with open(absolute_path, "rb") as file:
|
||||||
|
while True:
|
||||||
|
chunk:bytes = file.read(chunk_size)
|
||||||
|
if not chunk:
|
||||||
|
break
|
||||||
|
callback(chunk)
|
||||||
|
|
||||||
|
except Exception as exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def save(self:Self, path:str, data:str|bytes, mode:str = "w") -> bool:
|
||||||
|
try:
|
||||||
|
self.prepare_path(self.get_directory_path(path))
|
||||||
|
with open(self.fix_path(path), mode) as file:
|
||||||
|
file.write(data)
|
||||||
|
return True
|
||||||
|
except Exception as exception:
|
||||||
|
self.cxcv.exception(exception, "files_driver_save_exception", {
|
||||||
|
"path" : path,
|
||||||
|
"mode" : mode,
|
||||||
|
"length" : len(data) if data is not None else 0
|
||||||
|
})
|
||||||
|
return False
|
||||||
|
|
||||||
|
def remove(self:Self, path:str) -> bool:
|
||||||
|
if self.exists(path := self.fix_path(path), False):
|
||||||
|
try:
|
||||||
|
if self.is_file(path, False):
|
||||||
|
remove_file(path)
|
||||||
|
elif self.is_directory(path, False):
|
||||||
|
remove_directory(path)
|
||||||
|
return True
|
||||||
|
except Exception as exception:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
|
def load_json(self:Self,
|
||||||
|
inputs:Any|None,
|
||||||
|
only_dictionaries:bool = True
|
||||||
|
) -> tuple[dict[str, Any|None]|tuple[Any|None, ...]|list[Any|None], ...]:
|
||||||
|
|
||||||
|
items:tuple[dict[str, Any|None]|tuple[Any|None, ...]|list[Any|None], ...] = ()
|
||||||
|
|
||||||
|
if isinstance(inputs, dict):
|
||||||
|
items += (inputs,)
|
||||||
|
elif isinstance(inputs, list) or isinstance(inputs, tuple):
|
||||||
|
if only_dictionaries:
|
||||||
|
|
||||||
|
subinputs:Any|None
|
||||||
|
|
||||||
|
for subinputs in inputs:
|
||||||
|
items += self.load_json(subinputs, only_dictionaries)
|
||||||
|
|
||||||
|
else:
|
||||||
|
items += (inputs,)
|
||||||
|
elif isinstance(inputs, str):
|
||||||
|
|
||||||
|
json:dict[str, Any|None]|tuple[Any|None, ...]|list[Any|None]|None = Utils.json_decode(inputs)
|
||||||
|
|
||||||
|
items += (
|
||||||
|
self.load_json(Utils.json_decode(self.load(inputs, "r")), only_dictionaries) if json is None else
|
||||||
|
self.load_json(json, only_dictionaries))
|
||||||
|
|
||||||
|
return items
|
||||||
|
|
||||||
|
def get_directory_path(self:Self, path:str) -> str:
|
||||||
|
return RE.LAST_PATH_ITEM.sub(r'', self.fix_path(path))
|
||||||
|
|
||||||
|
def prepare_path(self:Self, path:str) -> bool:
|
||||||
|
try:
|
||||||
|
|
||||||
|
parts:list[str] = RE.SLASHES.split(path)
|
||||||
|
directory:str = "/" if self.slash == "/" else ""
|
||||||
|
part:str
|
||||||
|
|
||||||
|
for part in parts:
|
||||||
|
if part:
|
||||||
|
directory += parts[0] + self.slash
|
||||||
|
self.exists(directory, False) or make_directory(directory)
|
||||||
|
parts = parts[1:]
|
||||||
|
|
||||||
|
return True
|
||||||
|
except Exception as exception:
|
||||||
|
print(exception)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def list(self:Self, path:str) -> list[str]:
|
||||||
|
|
||||||
|
items:list[str] = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
absolute_path:str = self.get_absolute_path(path)
|
||||||
|
|
||||||
|
if absolute_path is not None and is_directory(absolute_path):
|
||||||
|
items = list_directory(absolute_path)
|
||||||
|
|
||||||
|
except Exception as exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return items
|
||||||
|
|
||||||
|
def get_stat(self:Self, path:str) -> StatResult|None:
|
||||||
|
|
||||||
|
stat:StatResult|None = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
absolute_path:str = self.get_absolute_path(path)
|
||||||
|
|
||||||
|
if absolute_path is not None and self.exists(absolute_path, False):
|
||||||
|
stat = get_stat(absolute_path)
|
||||||
|
|
||||||
|
except Exception as exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return stat
|
||||||
106
Python/Drivers/LocalFileDriver.py
Normal file
106
Python/Drivers/LocalFileDriver.py
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import IO, Callable, Self, Optional, Any
|
||||||
|
from os import stat_result as StatResult, listdir as list_directory, remove as remove_path, stat as get_stat
|
||||||
|
from os.path import exists as path_exists
|
||||||
|
from stat import S_ISDIR as is_directory, S_ISREG as is_file
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
from Interfaces.FilesInterface import FilesInterface
|
||||||
|
from Managers.EventsManager import EventsManager
|
||||||
|
from Models.HashModel import HashModel
|
||||||
|
from Utils.Utils import Utils
|
||||||
|
|
||||||
|
class LocalFileDriver(FilesInterface):
|
||||||
|
|
||||||
|
def __init__(self:Self,
|
||||||
|
cxcv:Any,
|
||||||
|
path:str,
|
||||||
|
inputs:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]] = None
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
self.cxcv:CXCVInterface = cxcv
|
||||||
|
self.path:str = path
|
||||||
|
self.chunk_size:int = self.cxcv.settings.get(("chunk_size", "default_chunk_size"), inputs, 4096)
|
||||||
|
self.__file:IO[Any]|None = None
|
||||||
|
self.mode:str = self.cxcv.settings.get(("mode", "file_mode", "default_file_mode"), inputs, "rb")
|
||||||
|
self.__closed:bool = False
|
||||||
|
self.on_load_chunk:EventsManager = EventsManager()
|
||||||
|
callback:Callable[[bytes], bool]|None = Utils.get_value("on_load_chunk", inputs)
|
||||||
|
self.__autoclose:bool = self.cxcv.settings.get("auto_close", inputs, True)
|
||||||
|
self.reusable:bool = self.cxcv.settings.get("reusable", inputs, False)
|
||||||
|
self.exists:bool = path_exists(self.path)
|
||||||
|
self.is_file:bool = False
|
||||||
|
self.is_directory:bool = False
|
||||||
|
self.size:int = 0
|
||||||
|
self.hash:HashModel = None
|
||||||
|
stat:StatResult
|
||||||
|
|
||||||
|
try:
|
||||||
|
stat = get_stat(self.path)
|
||||||
|
self.exists = True
|
||||||
|
self.is_file = is_file(stat.st_mode)
|
||||||
|
self.is_directory = is_directory(stat.st_mode)
|
||||||
|
self.size = stat.st_size
|
||||||
|
except Exception as exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
callback and self.on_load_chunk.add(callback)
|
||||||
|
|
||||||
|
self.cxcv.settings.get("auto_open", inputs, True) and self.open()
|
||||||
|
|
||||||
|
def open(self:Self, new_path:Optional[str] = None) -> Self:
|
||||||
|
|
||||||
|
if new_path or self.reusable:
|
||||||
|
if new_path:
|
||||||
|
self.path = new_path
|
||||||
|
if self.__file and not self.__closed:
|
||||||
|
self.__file.close()
|
||||||
|
self.__closed = False
|
||||||
|
elif self.__file is not None:
|
||||||
|
return self
|
||||||
|
|
||||||
|
self.__file = open(self.path, mode = self.mode)
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def close(self:Self) -> Self:
|
||||||
|
|
||||||
|
if not self.__closed:
|
||||||
|
self.__closed = True
|
||||||
|
self.hash = None
|
||||||
|
self.is_directory = False
|
||||||
|
self.is_file = False
|
||||||
|
self.exists = False
|
||||||
|
self.size = 0
|
||||||
|
self.__file and self.__file.close()
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def load(self:Self, all:bool = False) -> bytes:
|
||||||
|
|
||||||
|
data:bytes = self.__file.read(self.size if all else self.chunk_size)
|
||||||
|
|
||||||
|
if data:
|
||||||
|
self.on_load_chunk.execute(data)
|
||||||
|
elif self.__autoclose:
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def save(self:Self, chunk:Optional[bytes|bool] = None) -> None:
|
||||||
|
if chunk:
|
||||||
|
self.__file.write(chunk)
|
||||||
|
elif self.__autoclose:
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def list(self:Self) -> list[str]:
|
||||||
|
return list_directory(self.path) if self.is_directory else []
|
||||||
|
|
||||||
|
def remove(self:Self) -> None:
|
||||||
|
remove_path(self.path)
|
||||||
|
|
||||||
|
def reset(self:Self) -> Self:
|
||||||
|
|
||||||
|
self.close()
|
||||||
|
self.open()
|
||||||
|
|
||||||
|
return self
|
||||||
122
Python/Drivers/SFTPFileDriver.py
Normal file
122
Python/Drivers/SFTPFileDriver.py
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Callable, Self, Optional, Any
|
||||||
|
from paramiko import SFTPFile, SSHClient, SFTPClient, AutoAddPolicy
|
||||||
|
from os import stat_result as StatResult
|
||||||
|
from stat import S_ISDIR as is_directory, S_ISREG as is_file
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
from Interfaces.FilesInterface import FilesInterface
|
||||||
|
from Managers.EventsManager import EventsManager
|
||||||
|
from Models.HashModel import HashModel
|
||||||
|
from Utils.Utils import Utils
|
||||||
|
|
||||||
|
class SFTPFileDriver(FilesInterface):
|
||||||
|
|
||||||
|
def __init__(self:Self,
|
||||||
|
cxcv:Any,
|
||||||
|
path:str,
|
||||||
|
inputs:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]] = None
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
self.cxcv:CXCVInterface = cxcv
|
||||||
|
self.__client:SSHClient = SSHClient()
|
||||||
|
self.path:str = path
|
||||||
|
self.chunk_size:int = self.cxcv.settings.get(("chunk_size", "default_chunk_size"), inputs, 4096)
|
||||||
|
self.__file:SFTPFile|None = None
|
||||||
|
self.mode:str = self.cxcv.settings.get("file_mode", inputs, "rb")
|
||||||
|
self.__closed:bool = False
|
||||||
|
self.on_load_chunk:EventsManager = EventsManager()
|
||||||
|
callback:Callable[[bytes], bool]|None = Utils.get_value("on_load_chunk", inputs)
|
||||||
|
self.__autoclose:bool = self.cxcv.settings.get("auto_close", inputs, True)
|
||||||
|
self.reusable:bool = self.cxcv.settings.get("reusable", inputs, True)
|
||||||
|
self.exists:bool = False
|
||||||
|
self.is_file:bool = False
|
||||||
|
self.is_directory:bool = False
|
||||||
|
self.size:int = 0
|
||||||
|
self.hash:HashModel = None
|
||||||
|
self.__sftp:SFTPClient
|
||||||
|
stat:StatResult
|
||||||
|
|
||||||
|
self.__client.set_missing_host_key_policy(AutoAddPolicy())
|
||||||
|
self.__client.connect(
|
||||||
|
hostname = Utils.get_value(("host", "default_host", "default_sftp_host"), inputs, "localhost"),
|
||||||
|
port = Utils.get_value(("port", "default_port", "default_sftp_port"), inputs, 22),
|
||||||
|
username = Utils.get_value(("user", "default_user", "default_sftp_user"), inputs),
|
||||||
|
password = Utils.get_value(("password", "default_password", "default_sftp_password"), inputs)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.__sftp = self.__client.open_sftp()
|
||||||
|
|
||||||
|
try:
|
||||||
|
stat = self.__sftp.stat(self.path)
|
||||||
|
self.exists = True
|
||||||
|
self.is_file = is_file(stat.st_mode)
|
||||||
|
self.is_directory = is_directory(stat.st_mode)
|
||||||
|
self.size = stat.st_size
|
||||||
|
except Exception as exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
callback and self.on_load_chunk.add(callback)
|
||||||
|
|
||||||
|
self.cxcv.settings.get("auto_open", inputs, True) and self.open()
|
||||||
|
|
||||||
|
def open(self:Self, new_path:Optional[str] = None) -> Self:
|
||||||
|
|
||||||
|
if new_path or self.reusable:
|
||||||
|
if new_path:
|
||||||
|
self.path = new_path
|
||||||
|
if self.__file and not self.__closed:
|
||||||
|
self.__file.close()
|
||||||
|
self.__closed = False
|
||||||
|
elif self.__file is not None:
|
||||||
|
return self
|
||||||
|
|
||||||
|
self.__file = self.__sftp.file(self.path, mode = self.mode)
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def close(self:Self) -> Self:
|
||||||
|
|
||||||
|
if not self.__closed:
|
||||||
|
|
||||||
|
self.__closed = True
|
||||||
|
|
||||||
|
self.hash = None
|
||||||
|
self.is_directory = False
|
||||||
|
self.is_file = False
|
||||||
|
self.exists = False
|
||||||
|
self.size = 0
|
||||||
|
self.__file and self.__file.close()
|
||||||
|
self.__sftp.close()
|
||||||
|
self.__client.close()
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def load(self:Self, all:bool = False) -> bytes:
|
||||||
|
|
||||||
|
data:bytes = self.__file.read(self.size if all else self.chunk_size)
|
||||||
|
|
||||||
|
if data:
|
||||||
|
self.on_load_chunk.execute(data)
|
||||||
|
elif self.__autoclose:
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def save(self:Self, chunk:Optional[bytes|bool] = None) -> None:
|
||||||
|
if chunk:
|
||||||
|
self.__file.write(chunk)
|
||||||
|
elif self.__autoclose:
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def list(self:Self) -> list[str]:
|
||||||
|
return self.__sftp.listdir(self.path) if self.is_directory else []
|
||||||
|
|
||||||
|
def remove(self:Self) -> None:
|
||||||
|
self.__sftp.remove(self.path)
|
||||||
|
|
||||||
|
def reset(self:Self) -> Self:
|
||||||
|
|
||||||
|
self.close()
|
||||||
|
self.open()
|
||||||
|
|
||||||
|
return self
|
||||||
178
Python/Drivers/SQLiteDriver.py
Normal file
178
Python/Drivers/SQLiteDriver.py
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Any, Optional
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
from Models.ResultsModel import ResultsModel
|
||||||
|
from Abstracts.DatabasesAbstract import DatabasesAbstract
|
||||||
|
from Utils.Patterns import RE
|
||||||
|
from Utils.Utils import Utils
|
||||||
|
from sqlite3 import Connection, Cursor, connect as sqlite_connect
|
||||||
|
from re import Match as REMatch
|
||||||
|
|
||||||
|
class SQLiteDriver(DatabasesAbstract):
|
||||||
|
|
||||||
|
def __init__(self:Self,
|
||||||
|
cxcv:CXCVInterface,
|
||||||
|
key:str,
|
||||||
|
inputs:dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]
|
||||||
|
) -> None:
|
||||||
|
super().__init__(cxcv, key, inputs)
|
||||||
|
self.__connection:Connection|None = None
|
||||||
|
self.__path:str = Utils.get_value("path", inputs)
|
||||||
|
build_file:str|None = Utils.get_value("builder_file", inputs)
|
||||||
|
self.__in_use:bool = False
|
||||||
|
|
||||||
|
self.connect()
|
||||||
|
|
||||||
|
build_file is None or self.execute_file(build_file)
|
||||||
|
|
||||||
|
def connect(self:Self) -> bool:
|
||||||
|
if self.__connection is None:
|
||||||
|
try:
|
||||||
|
|
||||||
|
directory:str = self.cxcv.files.get_directory_path(self.__path)
|
||||||
|
|
||||||
|
self.cxcv.files.prepare_path(directory)
|
||||||
|
self.__connection = sqlite_connect(self.__path, check_same_thread = False)
|
||||||
|
|
||||||
|
except Exception as exception:
|
||||||
|
self.cxcv.exception(exception, "database_driver_connection_exception")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def close(self:Self) -> bool:
|
||||||
|
try:
|
||||||
|
if self.__connection:
|
||||||
|
self.__connection.execute("PRAGMA wal_checkpoint(FULL);")
|
||||||
|
self.__connection.close()
|
||||||
|
self.__connection = None
|
||||||
|
except Exception as exception:
|
||||||
|
self.cxcv.exception(exception, "database_driver_close_exception")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def build_match(matches:REMatch, parameters:dict[str, Any|None]) -> str:
|
||||||
|
|
||||||
|
value:Any|None = parameters.get(matches.group(1), matches.group(0))
|
||||||
|
|
||||||
|
return (
|
||||||
|
"null" if value is None else
|
||||||
|
"'" + str(value).replace("'", "''") + "'" if isinstance(value, str) else
|
||||||
|
str(value))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def prepare(cls:type[Self],
|
||||||
|
sql:str,
|
||||||
|
parameters:Optional[dict[str, Any|None]] = None,
|
||||||
|
builder:Optional[dict[str, Any|None]] = None
|
||||||
|
) -> list[str]:
|
||||||
|
|
||||||
|
queries:list[str] = [""]
|
||||||
|
has_parameters:bool = parameters is not None
|
||||||
|
|
||||||
|
if builder:
|
||||||
|
sql = Utils.string_variables(sql, builder)
|
||||||
|
|
||||||
|
while len(sql):
|
||||||
|
|
||||||
|
comment:str|None
|
||||||
|
fragment:str|None
|
||||||
|
string:str|None
|
||||||
|
separator:str|None
|
||||||
|
matches:REMatch = RE.SQL_LITE.search(sql)
|
||||||
|
|
||||||
|
comment, fragment, string, separator = matches.groups()
|
||||||
|
|
||||||
|
if comment is None:
|
||||||
|
if separator is None:
|
||||||
|
queries[-1] += fragment or string or ""
|
||||||
|
else:
|
||||||
|
queries[-1] = queries[-1].strip()
|
||||||
|
if len(queries[-1]):
|
||||||
|
queries[-1] += ";"
|
||||||
|
queries.append("")
|
||||||
|
|
||||||
|
sql = sql[matches.end():]
|
||||||
|
|
||||||
|
return [(
|
||||||
|
RE.STRING_VARIABLE.sub(
|
||||||
|
lambda matches:cls.build_match(matches, parameters),
|
||||||
|
subsql
|
||||||
|
) if has_parameters else
|
||||||
|
subsql) for subsql in queries if len(subsql)]
|
||||||
|
|
||||||
|
def execute(self:Self,
|
||||||
|
query:str,
|
||||||
|
parameters:Optional[dict[str, Any|None]] = None,
|
||||||
|
builder:Optional[dict[str, Any|None]] = None
|
||||||
|
) -> ResultsModel:
|
||||||
|
|
||||||
|
while self.__in_use and self.cxcv.working:
|
||||||
|
self.cxcv.wait(.01, .1)
|
||||||
|
self.__in_use = True
|
||||||
|
|
||||||
|
cursor:Cursor = self.__connection.cursor()
|
||||||
|
results:ResultsModel = ResultsModel()
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
for subquery in self.prepare(query, parameters, builder):
|
||||||
|
|
||||||
|
order:str = subquery[:7].lower()
|
||||||
|
|
||||||
|
try:
|
||||||
|
cursor.execute(subquery)
|
||||||
|
except Exception as exception:
|
||||||
|
print(subquery)
|
||||||
|
self.cxcv.exception(exception, "database_driver_execute_subquery_exception", {
|
||||||
|
"subquery" : subquery
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
|
||||||
|
if order == "insert ":
|
||||||
|
self.__in_use = False
|
||||||
|
results.ids.append(self.get_last_id())
|
||||||
|
elif order in ("select ", "with "):
|
||||||
|
results.set(cursor.fetchall(), Type=tuple(column[0] for column in cursor.description))
|
||||||
|
|
||||||
|
self.__connection.commit()
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
except Exception as exception:
|
||||||
|
self.cxcv.exception(exception, "database_driver_execute_exception", {
|
||||||
|
"query" : query,
|
||||||
|
"parameters" : parameters
|
||||||
|
})
|
||||||
|
|
||||||
|
self.__in_use = False
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def execute_file(self:Self,
|
||||||
|
path:str,
|
||||||
|
parameters:Optional[dict[str, Any|None]] = None,
|
||||||
|
builder:Optional[dict[str, Any|None]] = None
|
||||||
|
) -> ResultsModel:
|
||||||
|
return self.execute(self.cxcv.files.load(path, "r"), parameters, builder)
|
||||||
|
|
||||||
|
def get(self:Self,
|
||||||
|
query:str,
|
||||||
|
parameters:Optional[dict[str, Any|None]] = None,
|
||||||
|
builder:Optional[dict[str, Any|None]] = None
|
||||||
|
) -> Any|None:
|
||||||
|
return self.execute(query, parameters, builder).get(0)
|
||||||
|
|
||||||
|
def get_id(self:Self,
|
||||||
|
query:str,
|
||||||
|
parameters:Optional[dict[str, Any|None]] = None,
|
||||||
|
builder:Optional[dict[str, Any|None]] = None
|
||||||
|
) -> int|None:
|
||||||
|
|
||||||
|
value:Any|None = self.get(query, parameters, builder)
|
||||||
|
|
||||||
|
return int(value) if value is not None else None
|
||||||
|
|
||||||
|
def get_last_id(self:Self) -> int|None:
|
||||||
|
return self.get_id("select last_insert_rowid();")
|
||||||
15
Python/Interfaces/Abstracts/ControllerAbstractInterface.py
Normal file
15
Python/Interfaces/Abstracts/ControllerAbstractInterface.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Optional, Any
|
||||||
|
from abc import ABC
|
||||||
|
|
||||||
|
class ControllerAbstractInterface(ABC):
|
||||||
|
|
||||||
|
def __init__(self:Self,
|
||||||
|
cxcv:Any,
|
||||||
|
key:str,
|
||||||
|
inputs:Optional[dict[str, Any|None]|tuple[Any|None, ...]|list[Any|None]] = None
|
||||||
|
) -> None:
|
||||||
|
self.key:str = None
|
||||||
|
self.via:str = None
|
||||||
40
Python/Interfaces/Abstracts/DatabasesAbstractInterface.py
Normal file
40
Python/Interfaces/Abstracts/DatabasesAbstractInterface.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from Models.ResultsModel import ResultsModel
|
||||||
|
from typing import Optional, Self, Any
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
class DatabasesAbstractInterface(ABC):
|
||||||
|
|
||||||
|
def __init__(self:Self,
|
||||||
|
cxcv:Any,
|
||||||
|
key:str,
|
||||||
|
inputs:dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]
|
||||||
|
) -> None:
|
||||||
|
self.key:str = key
|
||||||
|
self.type:str = "UNKNOWN"
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def connect(self:Self) -> bool:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def close(self:Self) -> bool:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def execute(self:Self,
|
||||||
|
sql:str,
|
||||||
|
parameters:Optional[dict[str, Any|None]] = None
|
||||||
|
) -> ResultsModel:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def execute_file(self:Self,
|
||||||
|
path:str,
|
||||||
|
parameters:Optional[dict[str, Any|None]] = None
|
||||||
|
) -> ResultsModel:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_id(self:Self, query:str, parameters:Optional[dict[str, Any|None]] = None) -> int|None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_last_id(self:Self) -> int|None:pass
|
||||||
16
Python/Interfaces/Abstracts/ProcedureAbstractInterface.py
Normal file
16
Python/Interfaces/Abstracts/ProcedureAbstractInterface.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Optional, Any
|
||||||
|
from abc import ABC
|
||||||
|
|
||||||
|
class ProcedureAbstractInterface(ABC):
|
||||||
|
|
||||||
|
def __init__(self:Self,
|
||||||
|
cxcv:Any,
|
||||||
|
key:str,
|
||||||
|
via:str,
|
||||||
|
inputs:Optional[dict[str, Any|None]|tuple[Any|None, ...]|list[Any|None]] = None
|
||||||
|
) -> None:
|
||||||
|
self.key:str = key
|
||||||
|
self.via:str = via
|
||||||
47
Python/Interfaces/Application/CXCVInterface.py
Normal file
47
Python/Interfaces/Application/CXCVInterface.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Any, Optional
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from Interfaces.Drivers.FilesDriverInterface import FilesDriverInterface
|
||||||
|
from Interfaces.Managers.SettingsManagerInterface import SettingsManagerInterface
|
||||||
|
from Interfaces.Managers.I18NManagerInterface import I18NManagerInterface
|
||||||
|
from Interfaces.Managers.TerminalManagerInterface import TerminalManagerInterface
|
||||||
|
from Interfaces.Managers.ModelsManagerInterface import ModelsManagerInterface
|
||||||
|
from Interfaces.Managers.DatabasesManagerInterface import DatabasesManagerInterface
|
||||||
|
from Interfaces.Managers.ControllersManagerInterface import ControllersManagerInterface
|
||||||
|
from Interfaces.Managers.ProceduresManagerInterface import ProceduresManagerInterface
|
||||||
|
|
||||||
|
class CXCVInterface(ABC):
|
||||||
|
|
||||||
|
def __init__(self:Self,
|
||||||
|
inputs:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]] = None
|
||||||
|
) -> None:
|
||||||
|
self.working:bool = None
|
||||||
|
self.files:FilesDriverInterface = None
|
||||||
|
self.settings:SettingsManagerInterface = None
|
||||||
|
self.i18n:I18NManagerInterface = None
|
||||||
|
self.terminal:TerminalManagerInterface = None
|
||||||
|
self.models:ModelsManagerInterface = None
|
||||||
|
self.databases:DatabasesManagerInterface = None
|
||||||
|
self.controllers:ControllersManagerInterface = None
|
||||||
|
self.procedures:ProceduresManagerInterface = None
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def close(self:Self) -> None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def print(self:Self,
|
||||||
|
_type:str,
|
||||||
|
message:str|list[str]|tuple[str, ...],
|
||||||
|
inputs:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]] = None,
|
||||||
|
i:int = 0
|
||||||
|
) -> None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def exception(self:Self,
|
||||||
|
exception:Exception,
|
||||||
|
message:str|list[str]|tuple[str, ...],
|
||||||
|
inputs:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]] = None,
|
||||||
|
i:int = 0
|
||||||
|
) -> None:pass
|
||||||
47
Python/Interfaces/Drivers/FilesDriverInterface.py
Normal file
47
Python/Interfaces/Drivers/FilesDriverInterface.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Any, Callable
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
class FilesDriverInterface(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def fix_path(self:Self, path:str) -> str:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def exists(self:Self, path:str, analyze_roots:bool = True) -> bool:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def is_file(self:Self, path:str, analyze_roots:bool = True) -> bool:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def is_directory(self:Self, path:str, analyze_roots:bool = True) -> bool:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_absolute_path(self:Self, path:str) -> str|None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def load(self:Self, path:str, mode:str = "r") -> bool:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def load_by_chunks(self:Self,
|
||||||
|
path:str,
|
||||||
|
callback:Callable[[bytes], None],
|
||||||
|
chunk_size:int = 4096
|
||||||
|
) -> None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def save(self:Self, path:str, data:str|bytes, mode:str = "w") -> bool:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def load_json(self:Self,
|
||||||
|
inputs:Any|None,
|
||||||
|
only_dictionaries:bool = True
|
||||||
|
) -> tuple[dict[str, Any|None]|tuple[Any|None, ...]|list[Any|None], ...]:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_directory_path(self:Self, path:str) -> str:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def prepare_path(self:Self, path:str) -> bool:pass
|
||||||
43
Python/Interfaces/FilesInterface.py
Normal file
43
Python/Interfaces/FilesInterface.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Any, Optional, Self
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from Models.HashModel import HashModel
|
||||||
|
|
||||||
|
class FilesInterface(ABC):
|
||||||
|
|
||||||
|
def __init__(self:Self,
|
||||||
|
cxcv:Any,
|
||||||
|
path:str,
|
||||||
|
inputs:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]] = None
|
||||||
|
) -> None:
|
||||||
|
self.is_file:bool = None
|
||||||
|
self.is_directory:bool = None
|
||||||
|
self.exists:bool = None
|
||||||
|
self.size:int = None
|
||||||
|
self.path:str = None
|
||||||
|
self.chunk_size:int = None
|
||||||
|
self.reusable:bool = None
|
||||||
|
self.hash:HashModel = None
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def open(self:Self, new_path:Optional[str] = None) -> Self:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def close(self:Self) -> Self:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def load(self:Self, all:bool = False) -> bytes:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def save(self:Self, chunk:Optional[bytes|bool] = None) -> None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def list(self:Self) -> list[str]:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def remove(self:Self) -> None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def reset(self:Self) -> Self:pass
|
||||||
16
Python/Interfaces/Managers/ControllersManagerInterface.py
Normal file
16
Python/Interfaces/Managers/ControllersManagerInterface.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Any, TypeVar
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from Interfaces.Abstracts.ControllerAbstractInterface import ControllerAbstractInterface
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
class ControllersManagerInterface(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get(self:Self, Type:type[T], scrapper:str|T) -> T|None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:pass
|
||||||
14
Python/Interfaces/Managers/DatabasesManagerInterface.py
Normal file
14
Python/Interfaces/Managers/DatabasesManagerInterface.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Any
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from Interfaces.Abstracts.DatabasesAbstractInterface import DatabasesAbstractInterface
|
||||||
|
|
||||||
|
class DatabasesManagerInterface(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get(self:Self, database:str|DatabasesAbstractInterface) -> DatabasesAbstractInterface|None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:pass
|
||||||
17
Python/Interfaces/Managers/I18NManagerInterface.py
Normal file
17
Python/Interfaces/Managers/I18NManagerInterface.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Any, Optional, Self
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
class I18NManagerInterface(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get(self:Self,
|
||||||
|
texts:str|list[str]|tuple[str, ...],
|
||||||
|
inputs:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]] = None,
|
||||||
|
default:str = ""
|
||||||
|
) -> str:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:pass
|
||||||
19
Python/Interfaces/Managers/ModelsManagerInterface.py
Normal file
19
Python/Interfaces/Managers/ModelsManagerInterface.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Any, TypeVar
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
T = TypeVar('T')
|
||||||
|
|
||||||
|
class ModelsManagerInterface(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get(self:Self, Type:type[T], key:str) -> type[T]|None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def add(self:Self,
|
||||||
|
Type:type[T],
|
||||||
|
inputs:dict[str, Any]|list[Any|None]|tuple[Any|None, ...]|str,
|
||||||
|
overwrite:bool = False
|
||||||
|
) -> None:pass
|
||||||
15
Python/Interfaces/Managers/ProceduresManagerInterface.py
Normal file
15
Python/Interfaces/Managers/ProceduresManagerInterface.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Any, TypeVar
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
class ProceduresManagerInterface(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get(self:Self, Type:type[T], procedure:tuple[str, str]|T) -> T|None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:pass
|
||||||
20
Python/Interfaces/Managers/SettingsManagerInterface.py
Normal file
20
Python/Interfaces/Managers/SettingsManagerInterface.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Any, Optional, Self
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
class SettingsManagerInterface(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get(self:Self,
|
||||||
|
keys:str|list[str]|tuple[str, ...],
|
||||||
|
inputs:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]] = None,
|
||||||
|
default:Any|None = None
|
||||||
|
) -> Any|None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def add_secrets(self:Self, inputs:Any|None, overwrite:bool = False) -> None:pass
|
||||||
16
Python/Interfaces/Managers/TerminalManagerInterface.py
Normal file
16
Python/Interfaces/Managers/TerminalManagerInterface.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Any
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
class TerminalManagerInterface(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_i_command(self, names:str|list[str]|tuple[str, ...]) -> int|None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:pass
|
||||||
|
|
||||||
|
|
||||||
|
def close(self:Self, parameters:dict[str, Any|None], *arguments:list[Any|None]) -> None:pass
|
||||||
31
Python/Interfaces/Procedures/LogsProcedureInterface.py
Normal file
31
Python/Interfaces/Procedures/LogsProcedureInterface.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Optional, Self, Any
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
class LogsProcedureInterface(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def set_log(self:Self,
|
||||||
|
application:str,
|
||||||
|
file:str,
|
||||||
|
method:str,
|
||||||
|
line:int,
|
||||||
|
message:str,
|
||||||
|
error:int = 0,
|
||||||
|
parameters:dict[str, Any|None] = {}
|
||||||
|
) -> None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def set_exception(self:Self,
|
||||||
|
application:str,
|
||||||
|
file:str,
|
||||||
|
method:str,
|
||||||
|
line:int,
|
||||||
|
message:str,
|
||||||
|
exception:str,
|
||||||
|
trace:str,
|
||||||
|
parameters:dict[str, Any|None] = {},
|
||||||
|
status:Optional[str] = None
|
||||||
|
) -> None:pass
|
||||||
56
Python/Managers/ControllersManager.py
Normal file
56
Python/Managers/ControllersManager.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Any, TypeVar
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
from Abstracts.ControllerAbstract import ControllerAbstract
|
||||||
|
from Utils.Utils import Utils
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
class ControllersManager:
|
||||||
|
|
||||||
|
def __init__(self:Self, cxcv:CXCVInterface) -> None:
|
||||||
|
|
||||||
|
key:str
|
||||||
|
|
||||||
|
self.cxcv:CXCVInterface = cxcv
|
||||||
|
self.__controllers:dict[str, ControllerAbstract] = {}
|
||||||
|
|
||||||
|
for key in ("default_controllers_models", "controllers_models"):
|
||||||
|
self.cxcv.models.add(ControllerAbstract, self.cxcv.settings.get(key), True)
|
||||||
|
|
||||||
|
for key in (
|
||||||
|
"default_controllers_files", "default_controllers",
|
||||||
|
"controllers_files", "controllers"
|
||||||
|
):
|
||||||
|
self.add(self.cxcv.settings.get(key), True)
|
||||||
|
|
||||||
|
def get(self:Self, Type:type[T], controller:str|T) -> T|None:
|
||||||
|
return (
|
||||||
|
controller if isinstance(controller, Type) else
|
||||||
|
self.__controllers[controller] if (
|
||||||
|
controller in self.__controllers and
|
||||||
|
isinstance(self.__controllers[controller], Type)
|
||||||
|
) else
|
||||||
|
None)
|
||||||
|
|
||||||
|
def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:
|
||||||
|
|
||||||
|
subinputs:dict[str, ControllerAbstract]
|
||||||
|
|
||||||
|
for subinputs in self.cxcv.files.load_json(inputs):
|
||||||
|
|
||||||
|
key:str
|
||||||
|
subinputs:dict[str, Any|None]
|
||||||
|
|
||||||
|
for key, subinputs in subinputs.items():
|
||||||
|
if overwrite or key not in self.__controllers:
|
||||||
|
|
||||||
|
ControllerModel:type[ControllerAbstract]|None = self.cxcv.models.get(
|
||||||
|
ControllerAbstract,
|
||||||
|
Utils.get_value("model", subinputs, key)
|
||||||
|
)
|
||||||
|
|
||||||
|
if ControllerModel is not None:
|
||||||
|
self.__controllers[key] = ControllerModel(self.cxcv, key, subinputs)
|
||||||
51
Python/Managers/DatabasesManager.py
Normal file
51
Python/Managers/DatabasesManager.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Any
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
from Abstracts.DatabasesAbstract import DatabasesAbstract
|
||||||
|
from Utils.Utils import Utils
|
||||||
|
|
||||||
|
class DatabasesManager:
|
||||||
|
|
||||||
|
def __init__(self:Self, cxcv:CXCVInterface) -> None:
|
||||||
|
|
||||||
|
key:str
|
||||||
|
|
||||||
|
self.cxcv:CXCVInterface = cxcv
|
||||||
|
self.__databases:dict[str, DatabasesAbstract] = {}
|
||||||
|
|
||||||
|
for key in ("default_databases_models", "databases_models"):
|
||||||
|
self.cxcv.models.add(DatabasesAbstract, self.cxcv.settings.get(key), True)
|
||||||
|
|
||||||
|
for key in (
|
||||||
|
"default_databases_files", "default_databases",
|
||||||
|
"databases_files", "databases"
|
||||||
|
):
|
||||||
|
self.add(self.cxcv.settings.get(key), True)
|
||||||
|
|
||||||
|
def get(self:Self, database:str|DatabasesAbstract) -> DatabasesAbstract|None:
|
||||||
|
return (
|
||||||
|
database if isinstance(database, DatabasesAbstract) else
|
||||||
|
self.__databases[database] if database in self.__databases else
|
||||||
|
None)
|
||||||
|
|
||||||
|
def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:
|
||||||
|
|
||||||
|
subinputs:dict[str, DatabasesAbstract]
|
||||||
|
|
||||||
|
for subinputs in self.cxcv.files.load_json(inputs):
|
||||||
|
|
||||||
|
key:str
|
||||||
|
subinputs:dict[str, Any|None]
|
||||||
|
|
||||||
|
for key, subinputs in subinputs.items():
|
||||||
|
if overwrite or key not in self.__databases:
|
||||||
|
|
||||||
|
DatabaseModel:type[DatabasesAbstract]|None = self.cxcv.models.get(
|
||||||
|
DatabasesAbstract,
|
||||||
|
Utils.get_value("model", subinputs, key)
|
||||||
|
)
|
||||||
|
|
||||||
|
if DatabaseModel is not None:
|
||||||
|
self.__databases[key] = DatabaseModel(self.cxcv, key, subinputs)
|
||||||
46
Python/Managers/EventsManager.py
Normal file
46
Python/Managers/EventsManager.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Callable, Any
|
||||||
|
|
||||||
|
class EventsManager:
|
||||||
|
|
||||||
|
def __init__(self:Self) -> None:
|
||||||
|
self.__events:list[Callable[[Any|None], Any|None]] = []
|
||||||
|
|
||||||
|
def execute(self:Self, *arguments:tuple[Any|None, ...]) -> tuple[Any|None]:
|
||||||
|
|
||||||
|
results:tuple[Any|None] = ()
|
||||||
|
|
||||||
|
for event in self.__events:
|
||||||
|
if event is not None:
|
||||||
|
results += (event(*arguments),)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def add(self:Self,
|
||||||
|
event:Callable[[Any|None], Any|None]
|
||||||
|
) -> int|None:
|
||||||
|
|
||||||
|
i:int|None = None
|
||||||
|
|
||||||
|
if callable(event):
|
||||||
|
|
||||||
|
l:int = len(self.__events)
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while i < l and self.__events[i] is not None:
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
if i < l:
|
||||||
|
self.__events[i] = event
|
||||||
|
else:
|
||||||
|
self.__events.append(event)
|
||||||
|
|
||||||
|
return i
|
||||||
|
|
||||||
|
def remove(self:Self, i:int) -> bool:
|
||||||
|
if 0 <= i < len(self.__events) and self.__events[i] is not None:
|
||||||
|
self.__events[i] = None
|
||||||
|
return True
|
||||||
|
return False
|
||||||
82
Python/Managers/I18NManager.py
Normal file
82
Python/Managers/I18NManager.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Any, Optional, Self
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
from Utils.Utils import Utils
|
||||||
|
|
||||||
|
class I18NManager:
|
||||||
|
|
||||||
|
def __init__(self:Self, cxcv:CXCVInterface) -> None:
|
||||||
|
|
||||||
|
key:str
|
||||||
|
|
||||||
|
self.cxcv:CXCVInterface = cxcv
|
||||||
|
self.__sentences:dict[str, dict[str, str|list[str]|tuple[str, ...]]] = {}
|
||||||
|
self.__language:str = "english"
|
||||||
|
|
||||||
|
for key in ("default_i18n_files", "default_i18n", "i18n_files", "i18n"):
|
||||||
|
self.add(self.cxcv.settings.get(key), True)
|
||||||
|
|
||||||
|
def __get(self:Self,
|
||||||
|
texts:str|list[str]|tuple[str, ...],
|
||||||
|
inputs:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]] = None,
|
||||||
|
default:str = ""
|
||||||
|
) -> Any|None:
|
||||||
|
|
||||||
|
keys:list[str] = Utils.get_keys(texts)
|
||||||
|
|
||||||
|
if len(keys):
|
||||||
|
|
||||||
|
language:str
|
||||||
|
done:tuple[str, ...] = ()
|
||||||
|
|
||||||
|
for language in (self.__language, *tuple(self.__sentences.keys())):
|
||||||
|
if language in done:
|
||||||
|
continue
|
||||||
|
done += (language,)
|
||||||
|
|
||||||
|
if language in self.__sentences:
|
||||||
|
for key in keys:
|
||||||
|
if key in self.__sentences[language]:
|
||||||
|
return self.__sentences[language][key]
|
||||||
|
|
||||||
|
if len(texts := Utils.get_strings(texts)):
|
||||||
|
return texts[0]
|
||||||
|
return default
|
||||||
|
|
||||||
|
def get(self:Self,
|
||||||
|
texts:str|list[str]|tuple[str, ...],
|
||||||
|
inputs:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]] = None,
|
||||||
|
default:str = ""
|
||||||
|
) -> str:
|
||||||
|
|
||||||
|
text:str|list[str]|tuple[str, ...] = self.__get(texts, inputs, default)
|
||||||
|
|
||||||
|
return Utils.string_variables((
|
||||||
|
text if isinstance(text, str) else
|
||||||
|
" ".join(text) if isinstance(text, (list, tuple)) else
|
||||||
|
str(text)), inputs)
|
||||||
|
|
||||||
|
def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:
|
||||||
|
|
||||||
|
dictionary:dict[str, Any|None]
|
||||||
|
|
||||||
|
for dictionary in self.cxcv.files.load_json(inputs):
|
||||||
|
|
||||||
|
language:str
|
||||||
|
sentences:Any|None
|
||||||
|
|
||||||
|
for language, sentences in dictionary.items():
|
||||||
|
if not isinstance(sentences, dict):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if language not in self.__sentences:
|
||||||
|
self.__sentences[language] = {}
|
||||||
|
|
||||||
|
key:str
|
||||||
|
value:Any|None
|
||||||
|
|
||||||
|
for key, value in sentences.items():
|
||||||
|
if overwrite or key not in self.__sentences[language]:
|
||||||
|
self.__sentences[language][key] = value
|
||||||
42
Python/Managers/ModelsManager.py
Normal file
42
Python/Managers/ModelsManager.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Any, TypeVar
|
||||||
|
from inspect import isclass
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
|
||||||
|
T = TypeVar('T')
|
||||||
|
|
||||||
|
class ModelsManager:
|
||||||
|
|
||||||
|
def __init__(self:Self, cxcv:CXCVInterface) -> None:
|
||||||
|
self.cxcv:CXCVInterface = cxcv
|
||||||
|
self.__models:dict[str, Any] = {}
|
||||||
|
|
||||||
|
def get(self:Self, Type:type[T], model:str|type[T]) -> type[T]|None:
|
||||||
|
return (
|
||||||
|
model if isclass(model) and issubclass(model, Type) else
|
||||||
|
self.__models[model] if (
|
||||||
|
model in self.__models and
|
||||||
|
issubclass(self.__models[model], Type)
|
||||||
|
) else
|
||||||
|
None)
|
||||||
|
|
||||||
|
def add(self:Self,
|
||||||
|
Type:type[T],
|
||||||
|
inputs:dict[str, Any]|list[Any|None]|tuple[Any|None, ...]|str,
|
||||||
|
overwrite:bool = False
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
subinputs:dict[str, Any]
|
||||||
|
|
||||||
|
for subinputs in self.cxcv.files.load_json(inputs):
|
||||||
|
|
||||||
|
key:str
|
||||||
|
Model:type[T]
|
||||||
|
|
||||||
|
for key, Model in subinputs.items():
|
||||||
|
if issubclass(Model, Type) and (
|
||||||
|
overwrite or key not in self.__models
|
||||||
|
):
|
||||||
|
self.__models[key] = Model
|
||||||
69
Python/Managers/ProceduresManager.py
Normal file
69
Python/Managers/ProceduresManager.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Any, TypeVar
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
from Abstracts.ProcedureAbstract import ProcedureAbstract
|
||||||
|
from Utils.Utils import Utils
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
class ProceduresManager:
|
||||||
|
|
||||||
|
def __init__(self:Self, cxcv:CXCVInterface) -> None:
|
||||||
|
|
||||||
|
key:str
|
||||||
|
self.cxcv:CXCVInterface = cxcv
|
||||||
|
self.__procedures:dict[str, dict[str, ProcedureAbstract]] = {}
|
||||||
|
|
||||||
|
for key in ("default_procedures_models", "procedures_models"):
|
||||||
|
self.cxcv.models.add(ProcedureAbstract, self.cxcv.settings.get(key), True)
|
||||||
|
|
||||||
|
for key in (
|
||||||
|
"default_procedures_files", "default_procedures",
|
||||||
|
"procedures_files", "procedures"
|
||||||
|
):
|
||||||
|
self.add(self.cxcv.settings.get(key), True)
|
||||||
|
|
||||||
|
def get(self:Self, Type:type[T], procedure:tuple[str, str]|T) -> T|None:
|
||||||
|
if isinstance(procedure, tuple):
|
||||||
|
|
||||||
|
key:str
|
||||||
|
via:str
|
||||||
|
|
||||||
|
key, via = procedure
|
||||||
|
|
||||||
|
return self.__procedures[key][via] if (
|
||||||
|
key in self.__procedures and
|
||||||
|
via in self.__procedures[key] and
|
||||||
|
isinstance(self.__procedures[key][via], Type)
|
||||||
|
) else None
|
||||||
|
return procedure if isinstance(procedure, T) else None
|
||||||
|
|
||||||
|
def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:
|
||||||
|
|
||||||
|
subinputs:dict[str, dict[str, ProcedureAbstract]]
|
||||||
|
|
||||||
|
for subinputs in self.cxcv.files.load_json(inputs):
|
||||||
|
|
||||||
|
via:str
|
||||||
|
vias:dict[str, ProcedureAbstract|str]
|
||||||
|
|
||||||
|
for key, vias in subinputs.items():
|
||||||
|
|
||||||
|
key:str
|
||||||
|
subinputs:dict[str, Any|None]
|
||||||
|
|
||||||
|
if key not in self.__procedures:
|
||||||
|
self.__procedures[key] = {}
|
||||||
|
|
||||||
|
for via, subinputs in vias.items():
|
||||||
|
if overwrite or via not in self.__procedures[key]:
|
||||||
|
|
||||||
|
ProcedureModel:type[ProcedureAbstract] = self.cxcv.models.get(
|
||||||
|
ProcedureAbstract,
|
||||||
|
Utils.get_value("model", subinputs, key)
|
||||||
|
)
|
||||||
|
|
||||||
|
if ProcedureModel is not None:
|
||||||
|
self.__procedures[key][via] = ProcedureModel(self.cxcv, key, via, subinputs)
|
||||||
67
Python/Managers/SettingsManager.py
Normal file
67
Python/Managers/SettingsManager.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Any, Optional, Self
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
from Utils.Utils import Utils
|
||||||
|
|
||||||
|
class SettingsManager:
|
||||||
|
|
||||||
|
__DEFAULT_SETTINGS:dict[str, Any|None] = {
|
||||||
|
"sql_builder_file" : "/SQLite/CXCV.lite.sql",
|
||||||
|
"database_file_path" : "/Data/CXCV.sqlite.db",
|
||||||
|
"default_settings_files" : ["/JSON/CXCV.settings.json"],
|
||||||
|
"default_secrets_files" : ["/JSON/CXCV.secrets.json"]
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self:Self,
|
||||||
|
cxcv:CXCVInterface,
|
||||||
|
inputs:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]] = None
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
key:str
|
||||||
|
|
||||||
|
self.cxcv:CXCVInterface = cxcv
|
||||||
|
self.__inputs:dict[str, Any|None] = Utils.get_dictionary(inputs)
|
||||||
|
self.__settings:dict[str, Any|None] = {}
|
||||||
|
self.__secrets:dict[str, Any|None] = {}
|
||||||
|
|
||||||
|
for key in ("default_settings_files", "default_settings", "settings_files", "settings"):
|
||||||
|
self.add(self.get(key), True)
|
||||||
|
for key in ("default_secrets_files", "default_secrets", "secrets_files", "secrets"):
|
||||||
|
self.add_secrets(self.get(key), True)
|
||||||
|
|
||||||
|
def get(self:Self,
|
||||||
|
keys:str|list[str]|tuple[str, ...],
|
||||||
|
inputs:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]] = None,
|
||||||
|
default:Any|None = None
|
||||||
|
) -> Any|None:
|
||||||
|
return Utils.get_value(keys, (
|
||||||
|
inputs, self.__inputs, self.__secrets, self.__settings, self.__DEFAULT_SETTINGS
|
||||||
|
), default)
|
||||||
|
|
||||||
|
def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:
|
||||||
|
|
||||||
|
dictionary:dict[str, Any|None]
|
||||||
|
|
||||||
|
for dictionary in self.cxcv.files.load_json(inputs):
|
||||||
|
|
||||||
|
key:str
|
||||||
|
value:Any|None
|
||||||
|
|
||||||
|
for key, value in dictionary.items():
|
||||||
|
if overwrite or key not in self.__settings:
|
||||||
|
self.__settings[key] = value
|
||||||
|
|
||||||
|
def add_secrets(self:Self, inputs:Any|None, overwrite:bool = False) -> None:
|
||||||
|
|
||||||
|
dictionary:dict[str, Any|None]
|
||||||
|
|
||||||
|
for dictionary in self.cxcv.files.load_json(inputs):
|
||||||
|
|
||||||
|
key:str
|
||||||
|
value:Any|None
|
||||||
|
|
||||||
|
for key, value in dictionary.items():
|
||||||
|
if overwrite or key not in self.__secrets:
|
||||||
|
self.__secrets[key] = value
|
||||||
106
Python/Managers/TerminalManager.py
Normal file
106
Python/Managers/TerminalManager.py
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Any
|
||||||
|
from threading import Thread
|
||||||
|
from re import Match as REMatch
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
from Models.CommandsModel import CommandsModel
|
||||||
|
from Utils.Patterns import RE
|
||||||
|
from Utils.Utils import Utils
|
||||||
|
|
||||||
|
class TerminalManager:
|
||||||
|
|
||||||
|
def __init__(self:Self, cxcv:CXCVInterface) -> None:
|
||||||
|
|
||||||
|
key:str
|
||||||
|
|
||||||
|
self.cxcv:CXCVInterface = cxcv
|
||||||
|
self.__started:bool = False
|
||||||
|
self.__commands:tuple[CommandsModel, ...] = ()
|
||||||
|
self.__thread:Thread = Thread(target=self.__execute)
|
||||||
|
|
||||||
|
def start(self:Self) -> None:
|
||||||
|
if self.__started:
|
||||||
|
return
|
||||||
|
self.__started = True
|
||||||
|
|
||||||
|
for key in ("default_commands_files", "default_commands", "commands_files", "commands"):
|
||||||
|
self.add(self.cxcv.settings.get(key), True)
|
||||||
|
|
||||||
|
self.__thread.start()
|
||||||
|
|
||||||
|
def __execute(self:Self) -> None:
|
||||||
|
while self.cxcv.working:
|
||||||
|
|
||||||
|
order:str = input().strip()
|
||||||
|
|
||||||
|
if not order:
|
||||||
|
continue
|
||||||
|
|
||||||
|
matches:REMatch = RE.COMMAND.match(order)
|
||||||
|
|
||||||
|
if not matches:
|
||||||
|
continue
|
||||||
|
|
||||||
|
name:str = matches.group(1)
|
||||||
|
arguments:list[Any|None] = []
|
||||||
|
parameters:dict[str, Any|None] = {}
|
||||||
|
arguments_block:str|None = (matches.group(2) or "").strip()
|
||||||
|
done:bool = False
|
||||||
|
|
||||||
|
if arguments_block:
|
||||||
|
|
||||||
|
block:tuple[str|None, ...]
|
||||||
|
|
||||||
|
for block in RE.get_all(RE.COMMAND_ARGUMENT, arguments_block):
|
||||||
|
|
||||||
|
key:str = Utils.get_from(block, (1, 5, 6, 7))
|
||||||
|
value:str|None = Utils.get_from(block, (2, 3, 4))
|
||||||
|
|
||||||
|
if value is not None:
|
||||||
|
parameters[key] = value.strip()
|
||||||
|
else:
|
||||||
|
arguments += [key,]
|
||||||
|
|
||||||
|
for command in self.__commands:
|
||||||
|
if (done := command.execute(name, parameters, *arguments)):
|
||||||
|
break
|
||||||
|
|
||||||
|
done or print(f"Command '{name}' not found.")
|
||||||
|
|
||||||
|
def get_i_command(self, names:str|list[str]|tuple[str, ...]) -> int|None:
|
||||||
|
for i, command in enumerate(self.__commands):
|
||||||
|
if command.check(names):
|
||||||
|
return i
|
||||||
|
return None
|
||||||
|
|
||||||
|
def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:
|
||||||
|
if isinstance(inputs, CommandsModel):
|
||||||
|
|
||||||
|
i:int|None = self.get_i_command(inputs)
|
||||||
|
|
||||||
|
if i is not None:
|
||||||
|
overwrite and self.__commands[i].update(inputs)
|
||||||
|
else:
|
||||||
|
self.__commands += (inputs,)
|
||||||
|
|
||||||
|
elif isinstance(inputs, dict):
|
||||||
|
if "names" in inputs and isinstance(inputs["names"], (str, list, tuple)):
|
||||||
|
|
||||||
|
names:str|list[str]|tuple[str, ...] = inputs["names"]
|
||||||
|
action:callable[[dict[str, Any|None], list[Any|None]], None]|None = inputs["action"] if "action" in inputs else None
|
||||||
|
i:int|None = self.get_i_command(names)
|
||||||
|
|
||||||
|
if i is not None:
|
||||||
|
overwrite and self.__commands[i].update(names, action)
|
||||||
|
else:
|
||||||
|
self.__commands += (CommandsModel(self.cxcv, names, action),)
|
||||||
|
|
||||||
|
else:
|
||||||
|
for command in self.cxcv.files.load_json(inputs, True):
|
||||||
|
if isinstance(command, (CommandsModel, dict)):
|
||||||
|
self.add(command, overwrite)
|
||||||
|
|
||||||
|
def close(self:Self, parameters:dict[str, Any|None], *arguments:list[Any|None]) -> None:
|
||||||
|
self.cxcv.close()
|
||||||
83
Python/Models/CommandsModel.py
Normal file
83
Python/Models/CommandsModel.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Self, Callable, Any, Optional
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
from Utils.Utils import Utils
|
||||||
|
|
||||||
|
class CommandsModel:
|
||||||
|
|
||||||
|
def __set_action(self:Self,
|
||||||
|
action:Callable[[dict[str, Any|None], list[Any|None]], None]|str|None
|
||||||
|
) -> None:
|
||||||
|
if callable(action):
|
||||||
|
self.__action = action
|
||||||
|
elif isinstance(action, str):
|
||||||
|
|
||||||
|
parts:list[str] = action.split(".")
|
||||||
|
item:Any|None = (
|
||||||
|
self.cxcv if parts[0] == "cxcv" else
|
||||||
|
None)
|
||||||
|
|
||||||
|
if item:
|
||||||
|
for part in parts[1:]:
|
||||||
|
if hasattr(item, part):
|
||||||
|
item = getattr(item, part)
|
||||||
|
else:
|
||||||
|
item = None
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
item = eval(action)
|
||||||
|
|
||||||
|
if callable(item):
|
||||||
|
self.__action = item
|
||||||
|
|
||||||
|
def __init__(self:Self,
|
||||||
|
cxcv:CXCVInterface,
|
||||||
|
names:str|list[str]|tuple[str, ...],
|
||||||
|
action:Callable[[dict[str, Any|None], list[Any|None]], None]|str|None = None
|
||||||
|
) -> None:
|
||||||
|
self.cxcv:CXCVInterface = cxcv
|
||||||
|
self.__names:list[str] = Utils.get_keys(names)
|
||||||
|
self.__action:Callable[[dict[str, Any|None], list[Any|None]], None]|None = None
|
||||||
|
|
||||||
|
self.__set_action(action)
|
||||||
|
|
||||||
|
def get_names(self:Self) -> list[str]:
|
||||||
|
return [*self.__names]
|
||||||
|
|
||||||
|
def get_action(self:Self) -> Callable[[dict[str, Any|None], list[Any|None]], None]|None:
|
||||||
|
return self.__action
|
||||||
|
|
||||||
|
def check(self:Self, names:str|list[str]|tuple[str, ...]) -> bool:
|
||||||
|
return Utils.get_keys(names)[0] == self.__names[0]
|
||||||
|
|
||||||
|
def update(self:Self,
|
||||||
|
names:str|list[str]|tuple[str, ...]|Self|None = None,
|
||||||
|
action:Optional[Callable[[dict[str, Any|None], list[Any|None]], None]] = None
|
||||||
|
) -> bool:
|
||||||
|
|
||||||
|
if isinstance(names, CommandsModel):
|
||||||
|
action = names.get_action()
|
||||||
|
names = names.get_names()
|
||||||
|
|
||||||
|
names = Utils.get_keys(names)
|
||||||
|
|
||||||
|
if names[0] == self.__names[0]:
|
||||||
|
for name in names[1:]:
|
||||||
|
if name not in self.__names:
|
||||||
|
self.__names += [name,]
|
||||||
|
if action is not None and action != self.__action:
|
||||||
|
self.__set_action(action)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def execute(self:Self,
|
||||||
|
name:str,
|
||||||
|
parameters:dict[str, Any|None],
|
||||||
|
*arguments:list[Any|None]
|
||||||
|
) -> bool:
|
||||||
|
if name in self.__names and self.__action is not None:
|
||||||
|
self.__action(parameters, *arguments)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
70
Python/Models/FileModel.py
Normal file
70
Python/Models/FileModel.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Any, Self
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
from Models.HashModel import HashModel
|
||||||
|
from Drivers.LocalFileDriver import LocalFileDriver
|
||||||
|
from Drivers.SFTPFileDriver import SFTPFileDriver
|
||||||
|
from Interfaces.FilesInterface import FilesInterface
|
||||||
|
from Utils.Utils import Utils
|
||||||
|
from Utils.Patterns import RE
|
||||||
|
|
||||||
|
class FileModel:
|
||||||
|
|
||||||
|
def __init__(self:Self, cxcv:CXCVInterface, path:str, chunk_size:int = 4096) -> None:
|
||||||
|
self.cxcv:CXCVInterface = cxcv
|
||||||
|
self.path:str = path
|
||||||
|
self.size:int = 0
|
||||||
|
self.hash:HashModel = HashModel(self.cxcv, chunk_size)
|
||||||
|
|
||||||
|
def update(self:Self, chunk:bytes) -> None:
|
||||||
|
self.size += len(chunk)
|
||||||
|
self.hash.update(chunk)
|
||||||
|
|
||||||
|
def check_integrity(self:Self, other:type[Self]) -> bool:
|
||||||
|
|
||||||
|
other:FileModel = other
|
||||||
|
|
||||||
|
return (
|
||||||
|
self.size == other.size and
|
||||||
|
self.hash.md5 == other.hash.md5 and
|
||||||
|
self.hash.sha1 == other.hash.sha1 and
|
||||||
|
self.hash.sha256 == other.hash.sha256 and
|
||||||
|
self.hash.blake2b == other.hash.blake2b and
|
||||||
|
self.hash.crc32 == other.hash.crc32
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_inputs(*inputs:list[str|dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]]) -> dict[str, Any|None]:
|
||||||
|
return Utils.get_dictionary([(
|
||||||
|
{"path" : subinputs} if isinstance(subinputs, str) else
|
||||||
|
subinputs) for subinputs in inputs])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_driver(
|
||||||
|
cxcv:CXCVInterface,
|
||||||
|
inputs:str|dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]
|
||||||
|
) -> FilesInterface:
|
||||||
|
|
||||||
|
if isinstance(inputs, str):
|
||||||
|
|
||||||
|
user:str|None = None
|
||||||
|
password:str|None = None
|
||||||
|
host:str|None = None
|
||||||
|
port:int|None = None
|
||||||
|
path:str = ""
|
||||||
|
|
||||||
|
user, password, host, port, path = RE.FILE_PATH.match(inputs).groups()
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
"user": user,
|
||||||
|
"password": password,
|
||||||
|
"host": host,
|
||||||
|
"port": int(port) if port else None,
|
||||||
|
"path": path
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
SFTPFileDriver if "host" in inputs else
|
||||||
|
LocalFileDriver)(cxcv, Utils.get_value("path", inputs, "."), inputs)
|
||||||
70
Python/Models/HashModel.py
Normal file
70
Python/Models/HashModel.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from hashlib import md5, sha1, sha256, blake2b
|
||||||
|
from zlib import crc32
|
||||||
|
from typing import Self, Protocol
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
|
||||||
|
class HashAlgorithm(Protocol):
|
||||||
|
def update(self:Self, data:bytes) -> None: ...
|
||||||
|
def hexdigest(self:Self) -> str: ...
|
||||||
|
|
||||||
|
class CRC32HashAlgorithm(HashAlgorithm):
|
||||||
|
|
||||||
|
def __init__(self:Self) -> None:
|
||||||
|
self.__value:int = 0
|
||||||
|
|
||||||
|
def update(self:Self, data:bytes) -> None:
|
||||||
|
self.__value = crc32(data, self.__value)
|
||||||
|
|
||||||
|
def hexdigest(self:Self) -> str:
|
||||||
|
return f"{self.__value & 0xFFFFFFFF:08x}"
|
||||||
|
|
||||||
|
class HashModel():
|
||||||
|
|
||||||
|
def __init__(self:Self, cxcv:CXCVInterface, data:bytes = b"", chunk_size:int = 4096) -> None:
|
||||||
|
|
||||||
|
self.cxcv:CXCVInterface = cxcv
|
||||||
|
self.__chunk_size:int = chunk_size
|
||||||
|
self.md5:str = ""
|
||||||
|
self.sha1:str = ""
|
||||||
|
self.sha256:str = ""
|
||||||
|
self.blake2b:str = ""
|
||||||
|
self.crc32:str = ""
|
||||||
|
self.__hashes:list[HashAlgorithm] = ["md5", "sha1", "sha256", "blake2b", "crc32"]
|
||||||
|
self.__algorithms:list[HashAlgorithm] = [
|
||||||
|
method() for method in (md5, sha1, sha256, blake2b, CRC32HashAlgorithm)
|
||||||
|
]
|
||||||
|
|
||||||
|
len(data) and self.update(data)
|
||||||
|
|
||||||
|
def __update_chunk(self:Self, chunk:bytes) -> None:
|
||||||
|
for hash_algorithm in self.__algorithms:
|
||||||
|
hash_algorithm.update(chunk)
|
||||||
|
|
||||||
|
def update(self:Self, data:bytes|str) -> None:
|
||||||
|
|
||||||
|
done:bool = False
|
||||||
|
|
||||||
|
if isinstance(data, bytes):
|
||||||
|
|
||||||
|
while data:
|
||||||
|
|
||||||
|
chunk:bytes = data[:self.__chunk_size]
|
||||||
|
|
||||||
|
data = data[self.__chunk_size:]
|
||||||
|
self.__update_chunk(chunk)
|
||||||
|
|
||||||
|
done = True
|
||||||
|
|
||||||
|
elif isinstance(data, str):
|
||||||
|
self.cxcv.files.load_by_chunks(data, self.__update_chunk, self.__chunk_size)
|
||||||
|
done = True
|
||||||
|
|
||||||
|
if done:
|
||||||
|
|
||||||
|
name:str
|
||||||
|
|
||||||
|
for name in self.__hashes:
|
||||||
|
setattr(self, name, getattr(self.__algorithms[self.__hashes.index(name)], "hexdigest")())
|
||||||
83
Python/Models/ResultsModel.py
Normal file
83
Python/Models/ResultsModel.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Optional, Self, Any
|
||||||
|
from Abstracts.TableAbstract import TableAbstract
|
||||||
|
|
||||||
|
class TableModel(TableAbstract):
|
||||||
|
|
||||||
|
def __init__(self:Self,
|
||||||
|
data:tuple[tuple[Any|None, ...], ...],
|
||||||
|
map:Optional[tuple[str, ...]] = None
|
||||||
|
) -> None:
|
||||||
|
self.data:tuple[tuple[Any|None, ...], ...] = data
|
||||||
|
self.map:tuple[str, ...]|None = map
|
||||||
|
|
||||||
|
def get(self:Self) -> tuple[tuple[str]|None, tuple[tuple[Any|None, ...], ...]]:
|
||||||
|
return self.map, self.data
|
||||||
|
|
||||||
|
def get_tuples(self:Self) -> tuple[tuple[Any|None, ...], ...]:
|
||||||
|
return self.data
|
||||||
|
|
||||||
|
def get_dictionaries(self:Self) -> list[dict[str, Any|None]]:
|
||||||
|
|
||||||
|
result:list[dict[str, Any|None]] = []
|
||||||
|
|
||||||
|
if len(self.data):
|
||||||
|
|
||||||
|
names:tuple[str, ...] = self.map or tuple()
|
||||||
|
l:int = len(self.data[0])
|
||||||
|
data:tuple[Any|None, ...]
|
||||||
|
|
||||||
|
while len(names) < l:
|
||||||
|
names += (f"column_{len(names)}",)
|
||||||
|
|
||||||
|
for data in self.data:
|
||||||
|
|
||||||
|
i:int
|
||||||
|
key:str
|
||||||
|
row:dict[str, Any|None] = {}
|
||||||
|
|
||||||
|
for i, key in enumerate(names):
|
||||||
|
row[key] = data[i] if i < len(data) else None
|
||||||
|
|
||||||
|
result.append(row)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get(self:Self, column:int|str, tuple:int = 0) -> Any|None:
|
||||||
|
|
||||||
|
if isinstance(column, str):
|
||||||
|
|
||||||
|
if self.map is not None and column in self.map:
|
||||||
|
column = self.map.index(column)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if 0 <= tuple < len(self.data) and 0 <= column < len(self.data[tuple]):
|
||||||
|
return self.data[tuple][column]
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
class ResultsModel:
|
||||||
|
|
||||||
|
def __init__(self:Self) -> None:
|
||||||
|
self.tables:list[TableModel] = []
|
||||||
|
self.variables:dict[str, Any|None] = {}
|
||||||
|
self.ids:list[int] = []
|
||||||
|
|
||||||
|
def set(self:Self,
|
||||||
|
data:tuple[tuple[Any|None, ...], ...],
|
||||||
|
variables:dict[str, Any|None]|None = None,
|
||||||
|
Type:Optional[type[TableAbstract]|tuple[str, ...]] = None
|
||||||
|
) -> None:
|
||||||
|
self.tables.append(
|
||||||
|
TableModel(data, Type) if Type is None or isinstance(Type, tuple) else
|
||||||
|
Type(data))
|
||||||
|
if variables is not None:
|
||||||
|
self.variables.update(variables)
|
||||||
|
|
||||||
|
def get(self:Self, column:int|str, tuple:int = 0, table:int = 0) -> Any|None:
|
||||||
|
return (
|
||||||
|
self.tables[table].get(column, tuple) if 0 <= table < len(self.tables) else
|
||||||
|
None)
|
||||||
94
Python/Procedures/SQLite/LogsSQLiteProcedure.py
Normal file
94
Python/Procedures/SQLite/LogsSQLiteProcedure.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Optional, Self, Any
|
||||||
|
from hashlib import sha256
|
||||||
|
from Interfaces.Application.CXCVInterface import CXCVInterface
|
||||||
|
from Abstracts.ProcedureAbstract import ProcedureAbstract
|
||||||
|
from Interfaces.Procedures.LogsProcedureInterface import LogsProcedureInterface
|
||||||
|
from Drivers.SQLiteDriver import SQLiteDriver
|
||||||
|
from Utils.Utils import Utils
|
||||||
|
|
||||||
|
class LogsSQLiteProcedure(ProcedureAbstract, LogsProcedureInterface):
|
||||||
|
|
||||||
|
def __init__(self:Self,
|
||||||
|
cxcv:CXCVInterface,
|
||||||
|
key:str,
|
||||||
|
via:str,
|
||||||
|
inputs:Optional[dict[str, Any|None]|tuple[Any|None, ...]|list[Any|None]] = None
|
||||||
|
) -> None:
|
||||||
|
super().__init__(cxcv, key, via, inputs)
|
||||||
|
|
||||||
|
self.__database:SQLiteDriver = self.cxcv.databases.get("logs")
|
||||||
|
|
||||||
|
def __set_item(self:Self, table:str, column:str, data:str, hashed:bool = False) -> int:
|
||||||
|
|
||||||
|
parameters:dict[str, Any|None] = {
|
||||||
|
"hash": sha256(data.encode("utf-8")).hexdigest() if hashed else "",
|
||||||
|
"data" : data
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.__database.get_id((
|
||||||
|
"select id from " + table + " where date_out is null and " + (
|
||||||
|
"hash = {hash}" if hashed else
|
||||||
|
column + " = {data}") + ";"
|
||||||
|
), parameters) or self.__database.execute((
|
||||||
|
"insert into " + table + "(hash, " + column + ") values({hash}, {data});" if hashed else
|
||||||
|
"insert into " + table + "(" + column + ") values({data});"), parameters).ids[0]
|
||||||
|
|
||||||
|
def __get_action_data(self:Self, application:str, file:str, method:str) -> dict[str, Any|None]:
|
||||||
|
|
||||||
|
parameters:dict[str, Any|None] = {
|
||||||
|
"application": self.__set_item("Applications", "name", application),
|
||||||
|
"language" : self.__set_item("Languages", "name", "Python"),
|
||||||
|
"file": self.__set_item("Files", "path", file),
|
||||||
|
"method": method
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.__database.get_id((
|
||||||
|
"select id from Methods where date_out is null and application = {application} and language = {language} and file = {file} and name = {method};"
|
||||||
|
), parameters) or self.__database.execute((
|
||||||
|
"insert into Methods(application, language, file, name) values({application}, {language}, {file}, {method});"
|
||||||
|
), parameters).ids[0]
|
||||||
|
|
||||||
|
def set_log(self:Self,
|
||||||
|
application:str,
|
||||||
|
file:str,
|
||||||
|
method:str,
|
||||||
|
line:int,
|
||||||
|
message:str,
|
||||||
|
error:int = 0,
|
||||||
|
parameters:dict[str, Any|None] = {}
|
||||||
|
) -> None:
|
||||||
|
self.__database.execute((
|
||||||
|
"insert into Logs(method, message, parameters, line, error) values({method}, {message}, {parameters}, {line}, {error});"
|
||||||
|
), {
|
||||||
|
"method" : self.__get_action_data(application, file, method),
|
||||||
|
"line" : line,
|
||||||
|
"message": self.__set_item("Messages", "message", message, True),
|
||||||
|
"parameters": self.__set_item("Parameters", "data", Utils.json_encode(parameters), True),
|
||||||
|
"error" : error
|
||||||
|
})
|
||||||
|
|
||||||
|
def set_exception(self:Self,
|
||||||
|
application:str,
|
||||||
|
file:str,
|
||||||
|
method:str,
|
||||||
|
line:int,
|
||||||
|
message:str,
|
||||||
|
exception:str,
|
||||||
|
trace:str,
|
||||||
|
parameters:dict[str, Any|None] = {},
|
||||||
|
status:Optional[str] = None
|
||||||
|
) -> None:
|
||||||
|
self.__database.execute((
|
||||||
|
"insert into Exceptions(method, message, exception, parameters, trace, line, status) values({method}, {message}, {exception}, {parameters}, {trace}, {line}, {status});"
|
||||||
|
), {
|
||||||
|
"method" : self.__get_action_data(application, file, method),
|
||||||
|
"line" : line,
|
||||||
|
"message": self.__set_item("Messages", "message", message, True),
|
||||||
|
"exception" : self.__set_item("Messages", "message", exception, True),
|
||||||
|
"parameters": self.__set_item("Parameters", "data", Utils.json_encode(parameters), True),
|
||||||
|
"trace": self.__set_item("Traces", "trace", Utils.json_encode(trace), True),
|
||||||
|
"status" : status
|
||||||
|
})
|
||||||
32
Python/Utils/Patterns.py
Normal file
32
Python/Utils/Patterns.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from re import Pattern as REPattern, compile as re_compile, IGNORECASE as RE_IGNORECASE, Match as REMatch
|
||||||
|
|
||||||
|
class RE:
|
||||||
|
KEY:REPattern = re_compile(r'^[a-z_][a-z0-9_]*$', RE_IGNORECASE)
|
||||||
|
SLASHES:REPattern = re_compile(r'[\\/]+')
|
||||||
|
LAST_PATH_ITEM:REPattern = re_compile(r'[\\/][^\\/]+[\\/]?$')
|
||||||
|
STRING_VARIABLE:REPattern = re_compile(r'\{([a-z_][a-z0-9_]*)\}', RE_IGNORECASE)
|
||||||
|
COMMAND:REPattern = re_compile(r'^([^ \s]+)(?:[ \t]+(.*))?$')
|
||||||
|
COMMAND_ARGUMENT:REPattern = re_compile(r'([^\s"\'=]+)(?:=(?:"((?:[^\\"]+|\\.)*)"|\'((?:[^\\\']+|\\.)*)\'|([^\s]+))?)?|"((?:[^\\"]+|\\.)*)"|\'((?:[^\\\']+|\\.)*)\'')
|
||||||
|
TO_KEY:REPattern = re_compile(r'[^a-z0-9_]+', RE_IGNORECASE)
|
||||||
|
EXCEPTION:REPattern = re_compile(r'^\s*File "([^"]+)", line ([0-9]+), in ([^\n]+)(.*|[\r\n]*)*$')
|
||||||
|
BREAK_LINES:REPattern = re_compile(r'\r\n|[\r\n]')
|
||||||
|
SQL_LITE:REPattern = re_compile(r'(\-{2}[^\r\n]+|\/\*(?:(?!(?:\*\/))(?:.|[\r\n]+))+(?:\*\/)?)|([^;"\']+)|("(?:(?:[^"]+|""|[\r\n]+)*)"|\'(?:(?:[^\']+|\'{2}|[\r\n]+)*)\')|(;)')
|
||||||
|
FILE_PATH:REPattern = re_compile(r'^(?:([^\@\:]+)?(?:\:([^\@]*))?\@)?(?:([^\:]+)(?:\:([0-9]+))?\:)?(.+)$')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_all(
|
||||||
|
regex:REPattern,
|
||||||
|
string:str
|
||||||
|
) -> list[tuple[str|None, ...]]:
|
||||||
|
|
||||||
|
blocks:list[tuple[str|None, ...]] = []
|
||||||
|
matches:REMatch|None
|
||||||
|
|
||||||
|
while (matches := regex.search(string)) is not None:
|
||||||
|
blocks.append(matches.groups())
|
||||||
|
string = string[matches.end():]
|
||||||
|
|
||||||
|
return blocks
|
||||||
165
Python/Utils/Utils.py
Normal file
165
Python/Utils/Utils.py
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Any, Self, Optional
|
||||||
|
from inspect import stack as get_stack, FrameInfo
|
||||||
|
from traceback import format_stack as trace_format_stack, extract_tb as extract_traceback
|
||||||
|
from json import loads as json_decode, dumps as json_encode
|
||||||
|
from re import Match as REMatch
|
||||||
|
from Utils.Patterns import RE
|
||||||
|
|
||||||
|
class Utils:
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_keys(cls:type[Self], *items:list[Any|None]) -> list[str]:
|
||||||
|
|
||||||
|
keys:list[str] = []
|
||||||
|
item:Any|None
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
if isinstance(item, (list, tuple)):
|
||||||
|
keys += cls.get_keys(*item)
|
||||||
|
elif isinstance(item, str) and RE.KEY.match(item):
|
||||||
|
keys += [item]
|
||||||
|
|
||||||
|
return keys
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_dictionaries(cls:type[Self], *items:list[Any|None]) -> list[dict[str, Any|None]]:
|
||||||
|
|
||||||
|
dictionaries:list[dict[str, Any|None]] = []
|
||||||
|
item:Any|None
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
if isinstance(item, (list, tuple)):
|
||||||
|
dictionaries += cls.get_dictionaries(*item)
|
||||||
|
elif isinstance(item, dict):
|
||||||
|
dictionaries += [item]
|
||||||
|
|
||||||
|
return dictionaries
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_dictionary(cls:type[Self], *items:list[Any|None]) -> dict[str, Any|None]:
|
||||||
|
|
||||||
|
dictionary:dict[str, Any|None] = {}
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
if isinstance(item, (list, tuple)):
|
||||||
|
dictionary = {**cls.get_dictionary(*item), **dictionary}
|
||||||
|
elif isinstance(item, dict):
|
||||||
|
dictionary = {**item, **dictionary}
|
||||||
|
|
||||||
|
return dictionary
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_value(cls:type[Self],
|
||||||
|
keys:str|list[str]|tuple[str, ...],
|
||||||
|
inputs:dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...],
|
||||||
|
default:Optional[Any] = None
|
||||||
|
) -> Any|None:
|
||||||
|
if len(keys := cls.get_keys(keys)):
|
||||||
|
|
||||||
|
subinputs:dict[str, Any|None]
|
||||||
|
|
||||||
|
for subinputs in cls.get_dictionaries(inputs):
|
||||||
|
for key in keys:
|
||||||
|
if key in subinputs:
|
||||||
|
return subinputs[key]
|
||||||
|
return default
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_strings(cls:type[Self], *items:list[Any|None]) -> list[str]:
|
||||||
|
|
||||||
|
strings:list[str] = []
|
||||||
|
item:Any|None
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
if isinstance(item, (list, tuple)):
|
||||||
|
strings += cls.get_strings(*item)
|
||||||
|
elif isinstance(item, str):
|
||||||
|
strings += [item]
|
||||||
|
|
||||||
|
return strings
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_keys(cls:type[Self], *items:list[Any|None]) -> list[str]:
|
||||||
|
|
||||||
|
keys:list[str] = []
|
||||||
|
item:Any|None
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
if isinstance(item, (list, tuple)):
|
||||||
|
keys += cls.get_keys(*item)
|
||||||
|
elif isinstance(item, str) and RE.KEY.match(item):
|
||||||
|
keys += [item]
|
||||||
|
|
||||||
|
return keys
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def string_variables(cls:type[Self],
|
||||||
|
string:str,
|
||||||
|
variables:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]] = None,
|
||||||
|
default:str|None = None
|
||||||
|
) -> str:
|
||||||
|
|
||||||
|
variables = cls.get_dictionary(variables)
|
||||||
|
|
||||||
|
def callback(matches:REMatch) -> str:
|
||||||
|
|
||||||
|
key:str = matches.group(1)
|
||||||
|
|
||||||
|
if key in variables:
|
||||||
|
return str(variables[key])
|
||||||
|
return matches.group(0) if default is None else default
|
||||||
|
|
||||||
|
return RE.STRING_VARIABLE.sub(callback, string)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def json_decode(data:str) -> dict[str, Any|None]|tuple[Any|None, ...]|list[Any|None]|None:
|
||||||
|
try:
|
||||||
|
return json_decode(data)
|
||||||
|
except Exception as exception:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def json_encode(data:dict[str, Any|None]|tuple[Any|None, ...]|list[Any|None]|None) -> str|None:
|
||||||
|
try:
|
||||||
|
return json_encode(data)
|
||||||
|
except Exception as exception:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_from(
|
||||||
|
item:list[Any|None]|tuple[Any|None, ...],
|
||||||
|
indexes:int|list[int]|tuple[int, ...],
|
||||||
|
default:Optional[Any] = None
|
||||||
|
) -> Any|None:
|
||||||
|
|
||||||
|
l:int = len(item)
|
||||||
|
i:int
|
||||||
|
|
||||||
|
for i in indexes if isinstance(indexes, (list, tuple)) else (indexes,):
|
||||||
|
if i >= l:
|
||||||
|
break
|
||||||
|
if item[i] is not None:
|
||||||
|
return item[i]
|
||||||
|
return default
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_action_data(i:int = 0) -> dict[str, str|int]:
|
||||||
|
|
||||||
|
stack:FrameInfo = get_stack()[1 + i]
|
||||||
|
|
||||||
|
return {
|
||||||
|
"file" : stack.filename,
|
||||||
|
"method" : stack.function,
|
||||||
|
"line" : stack.lineno
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_trace(exception:Optional[Exception] = None) -> list[str]:
|
||||||
|
return trace_format_stack()[:-2] + (
|
||||||
|
extract_traceback(exception.__traceback__).format() if exception else
|
||||||
|
[])
|
||||||
36
Python/cxcv.py
Normal file
36
Python/cxcv.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from Application.CXCV import CXCV
|
||||||
|
from typing import Any
|
||||||
|
from Controllers.LogsController import LogsController
|
||||||
|
from Procedures.SQLite.LogsSQLiteProcedure import LogsSQLiteProcedure
|
||||||
|
from Drivers.SQLiteDriver import SQLiteDriver
|
||||||
|
|
||||||
|
inputs:dict[str, Any|None] = {
|
||||||
|
"default_databases_models" : {
|
||||||
|
"sqlite" : SQLiteDriver
|
||||||
|
},
|
||||||
|
"default_controllers_models" : {
|
||||||
|
"logs_controller" : LogsController,
|
||||||
|
},
|
||||||
|
"default_procedures_models" : {
|
||||||
|
"logs_procedure_sqlite" : LogsSQLiteProcedure,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
from secrets import secrets as custom_secrets
|
||||||
|
|
||||||
|
for key, value in dict(custom_secrets).items():
|
||||||
|
if key not in inputs or isinstance(inputs[key], dict):
|
||||||
|
inputs[key] = value
|
||||||
|
elif isinstance(value, dict):
|
||||||
|
for subkey, subvalue in value.items():
|
||||||
|
inputs[key][subkey] = subvalue
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
cxcv:CXCV = CXCV()
|
||||||
91
README.md
91
README.md
@ -4,22 +4,95 @@ Proyecto para gestionar un control completo sobre movimientos y copiados de fich
|
|||||||
|
|
||||||
> **NOTA**: Para gestionar control de los Logs se está valorando si hacer uso de Logs en texto plano; o crear una pequeña SQLite para garantizar control de búsqueda eficiente; o ambos.
|
> **NOTA**: Para gestionar control de los Logs se está valorando si hacer uso de Logs en texto plano; o crear una pequeña SQLite para garantizar control de búsqueda eficiente; o ambos.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
|
||||||
|
F{{"@FOREACH archivo IN origen"}}
|
||||||
|
A[Archivo]
|
||||||
|
C[Caché]
|
||||||
|
D[Destino]
|
||||||
|
H([Hash])
|
||||||
|
s[Sí]
|
||||||
|
n[No]
|
||||||
|
|
||||||
|
l{"Es cargado en"}
|
||||||
|
b{Crea}
|
||||||
|
g{"Lo guarda en"}
|
||||||
|
c{Comprueba}
|
||||||
|
d{Da}
|
||||||
|
e{"Elimina y coge siguiente"}
|
||||||
|
o{"¿Ok?"}
|
||||||
|
r{Reintenta}
|
||||||
|
|
||||||
|
F --> d
|
||||||
|
d --> A
|
||||||
|
A --> l
|
||||||
|
l --> C
|
||||||
|
C --> b
|
||||||
|
b --> H
|
||||||
|
C --> g
|
||||||
|
g --> D
|
||||||
|
D --> c
|
||||||
|
c --> H
|
||||||
|
H --> o
|
||||||
|
o --> s
|
||||||
|
s --> e
|
||||||
|
e --> A
|
||||||
|
o --> n
|
||||||
|
n --> r
|
||||||
|
r --> A
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# Objetivos
|
||||||
|
|
||||||
|
* [ ] Organizar el árbol de directorios del proyecto y fijar elementos.
|
||||||
|
* [ ] Generar la base Python del mismo.
|
||||||
|
* [ ] Generar los Drivers de comunicaciones.
|
||||||
|
* [ ] Gestionar la operativa.
|
||||||
|
* [ ] Gestionar los argumentos de entrada por comandos.
|
||||||
|
* [ ] Generar la I18N.
|
||||||
|
* [ ] Generar las configuraciones y Testear los Secrets.
|
||||||
|
* [ ] Crear el SQLite de creación.
|
||||||
|
* [ ] Generar manual de instrucciones.
|
||||||
|
* [ ] Hacer testeo en entorno real.
|
||||||
|
|
||||||
# JAM
|
# JAM
|
||||||
|
|
||||||
Este proyecto está orientado a ser como una JAM para tener máxima motivación. Las normas y demás se expondrán cuando tenga permiso propio a empezar con dicho proyecto, mientras tanto, simplemente explicaré a continuación su motivación y los objetivos personales/profesionales que he de cumplir para poder llevar a cabo dicho proyecto.
|
Este proyecto está orientado a ser como una JAM para tener máxima motivación. Las normas y demás se expondrán cuando tenga permiso propio a empezar con dicho proyecto, mientras tanto, simplemente explicaré a continuación su motivación y los objetivos personales/profesionales que he de cumplir para poder llevar a cabo dicho proyecto.
|
||||||
|
|
||||||
> **IMPORTANTE**: Este proyecto tiene un objetivo final para una gestión, la cual es de caracter profesional/privada, motivo por la cual, no se mostrará ni se usará como ejemplo, así como tampoco se indicarán los nombres reales y ni se usará el entorno real final, pero sí se mencionarán objetivos del mismo en un entorno simulado controlado totalmente ageno y no referenciable.
|
> **IMPORTANTE**: Este proyecto tiene un objetivo final para una gestión, la cual es de caracter profesional/privada, motivo por la cual, no se mostrará ni se usará como ejemplo, así como tampoco se indicarán los nombres reales y ni se usará el entorno real final, pero sí se mencionarán objetivos del mismo en un entorno simulado controlado totalmente ageno y no referenciable.
|
||||||
|
|
||||||
|
## Día 1
|
||||||
|
|
||||||
Para poder realizar esta JAM, primero he de acabar los siguientes objetivos:
|
Para poder realizar esta JAM, primero he de acabar los siguientes objetivos:
|
||||||
|
|
||||||
- [ ] Crear los generadores de Tests de:
|
* [-] Crear los generadores de Tests de:
|
||||||
- [ ] Estructura de la Ordenanza Municipal de Normalización Lingüística del Concello de Ferrol.
|
* [X] Estructura de la Ordenanza Municipal de Normalización Lingüística del Concello de Ferrol.
|
||||||
- [ ] Tests acordes al contenido. Por simplicidad, se unificará todo en uno.
|
* [-] Tests acordes al contenido. Por simplicidad, se unificará todo en uno.
|
||||||
- *Aplicar referencias.*
|
* *Aplicar referencias.*
|
||||||
- [ ] Estructura de la Ley 3/1983 de Normalización Lingüística.
|
* [-] Estructura de la Ley 3/1983 de Normalización Lingüística.
|
||||||
- [ ] Generar los Tests acordes al Capítulo 5 del Título Preliminar.
|
* [ ] Generar los Tests acordes al Capítulo 5 del Título Preliminar.
|
||||||
- [ ] Organizar el tema 25 y catalogar la información cara GLPi, Nagios y Zabbix.
|
* [ ] Organizar el tema 25 y catalogar la información cara GLPi, Nagios y Zabbix.
|
||||||
- [ ] Repasar la Ley 39/2015 y Constitución.
|
* [-] Repasar la Ley 39/2015 y Constitución.
|
||||||
- [ ] Piscina de 19:00 a 20:00.
|
* [ ] Piscina de 19:00 a 20:00.
|
||||||
|
- *No pude pues se me pilló un poco el lomo.*
|
||||||
|
|
||||||
|
> **IMPORTANTE**: Todo ha de cumplirse hoy. Si no es el caso, otro día se marcarán otros objetivos.
|
||||||
|
|
||||||
|
> *No conseguí completar los objetivos.*
|
||||||
|
|
||||||
|
## Día 2
|
||||||
|
|
||||||
|
Para poder realizar esta JAM, primero he de acabar los siguientes objetivos:
|
||||||
|
|
||||||
|
* [X] Crear los generadores de Tests de:
|
||||||
|
* [X] Terminar los Tests acordes al contenido. Por simplicidad, se unificará todo en uno.
|
||||||
|
* *Aplicar referencias.*
|
||||||
|
* [X] Estructura de la Ley 3/1983 de Normalización Lingüística.
|
||||||
|
* [X] Generar los Tests acordes al Capítulo 5 del Título Preliminar.
|
||||||
|
* [-] Organizar el tema 25 y catalogar la información cara GLPi, Nagios y Zabbix.
|
||||||
|
* [X] Repasar el tema de la Unión Europea y Constitución.
|
||||||
|
* [X] Hacer pilates y estiramientos de 19:00 a 20:00 y luego salir a andar un poco.
|
||||||
|
|
||||||
> **IMPORTANTE**: Todo ha de cumplirse hoy. Si no es el caso, otro día se marcarán otros objetivos.
|
> **IMPORTANTE**: Todo ha de cumplirse hoy. Si no es el caso, otro día se marcarán otros objetivos.
|
||||||
167
SQLite/Logs.lite.sql
Normal file
167
SQLite/Logs.lite.sql
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
pragma foreign_keys = on;
|
||||||
|
|
||||||
|
-- Level 0.
|
||||||
|
create table if not exists Applications(
|
||||||
|
id integer primary key autoincrement,
|
||||||
|
'name' varchar(64) not null unique,
|
||||||
|
date_in datetime default current_timestamp,
|
||||||
|
date_out datetime null,
|
||||||
|
unique('name')
|
||||||
|
);
|
||||||
|
|
||||||
|
create table if not exists Languages(
|
||||||
|
id integer primary key autoincrement,
|
||||||
|
'name' varchar(64) not null unique,
|
||||||
|
date_in datetime default current_timestamp,
|
||||||
|
date_out datetime null,
|
||||||
|
unique('name')
|
||||||
|
);
|
||||||
|
|
||||||
|
create table if not exists Files(
|
||||||
|
id integer primary key autoincrement,
|
||||||
|
'path' varchar(256) not null unique,
|
||||||
|
date_in datetime default current_timestamp,
|
||||||
|
date_out datetime null,
|
||||||
|
unique('path')
|
||||||
|
);
|
||||||
|
|
||||||
|
create table if not exists Traces(
|
||||||
|
id integer primary key autoincrement,
|
||||||
|
'hash' varchar(256) not null,
|
||||||
|
trace text not null,
|
||||||
|
date_in datetime default current_timestamp,
|
||||||
|
date_out datetime null,
|
||||||
|
unique('hash')
|
||||||
|
);
|
||||||
|
|
||||||
|
create table if not exists Messages(
|
||||||
|
id integer primary key autoincrement,
|
||||||
|
'hash' varchar(256) not null,
|
||||||
|
'message' text not null,
|
||||||
|
date_in datetime default current_timestamp,
|
||||||
|
date_out datetime null,
|
||||||
|
unique('hash')
|
||||||
|
);
|
||||||
|
|
||||||
|
create table if not exists Parameters(
|
||||||
|
id integer primary key autoincrement,
|
||||||
|
'hash' varchar(256) not null,
|
||||||
|
'data' text not null,
|
||||||
|
date_in datetime default current_timestamp,
|
||||||
|
date_out datetime null,
|
||||||
|
unique('hash')
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Level 1.
|
||||||
|
create table if not exists Methods(
|
||||||
|
id integer primary key autoincrement,
|
||||||
|
'application' integer not null,
|
||||||
|
'language' integer not null,
|
||||||
|
'file' integer null,
|
||||||
|
'name' varchar(64) not null,
|
||||||
|
date_in datetime default current_timestamp,
|
||||||
|
date_out datetime null,
|
||||||
|
foreign key ('application') references Applications(id) on delete restrict on update restrict,
|
||||||
|
foreign key ('language') references Languages(id) on delete restrict on update restrict,
|
||||||
|
foreign key ('file') references Files(id) on delete restrict on update restrict
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Level 2.
|
||||||
|
create table if not exists Logs(
|
||||||
|
id integer primary key autoincrement,
|
||||||
|
'method' integer not null,
|
||||||
|
'message' integer not null,
|
||||||
|
parameters integer not null,
|
||||||
|
'line' integer not null,
|
||||||
|
error integer not null,
|
||||||
|
date_in datetime default current_timestamp,
|
||||||
|
date_out datetime null,
|
||||||
|
foreign key ('method') references Methods(id) on delete restrict on update restrict,
|
||||||
|
foreign key ('message') references Messages(id) on delete restrict on update restrict,
|
||||||
|
foreign key (parameters) references Parameters(id) on delete restrict on update restrict
|
||||||
|
);
|
||||||
|
|
||||||
|
create table if not exists Exceptions(
|
||||||
|
id integer primary key autoincrement,
|
||||||
|
'method' integer not null,
|
||||||
|
trace integer not null,
|
||||||
|
'message' integer not null,
|
||||||
|
exception integer not null,
|
||||||
|
parameters integer not null,
|
||||||
|
'line' integer not null,
|
||||||
|
'status' varchar(16) null,
|
||||||
|
date_in datetime default current_timestamp,
|
||||||
|
date_out datetime null,
|
||||||
|
foreign key ('method') references Methods(id) on delete restrict on update restrict,
|
||||||
|
foreign key ('trace') references Traces(id) on delete restrict on update restrict,
|
||||||
|
foreign key ('message') references Messages(id) on delete restrict on update restrict,
|
||||||
|
foreign key ('exception') references Messages(id) on delete restrict on update restrict,
|
||||||
|
foreign key (parameters) references Parameters(id) on delete restrict on update restrict
|
||||||
|
);
|
||||||
|
|
||||||
|
drop view if exists MethodsView;
|
||||||
|
create view MethodsView as select
|
||||||
|
methods.id as id,
|
||||||
|
languages.id as language_id,
|
||||||
|
applications.id as application_id,
|
||||||
|
files.id as file_id,
|
||||||
|
applications.name as 'application',
|
||||||
|
languages.name as 'language',
|
||||||
|
files.path as 'file',
|
||||||
|
methods.name as 'name'
|
||||||
|
from Methods methods
|
||||||
|
join Applications applications on methods.application = applications.id
|
||||||
|
join Languages languages on methods.language = languages.id
|
||||||
|
join Files files on methods.file = files.id
|
||||||
|
where
|
||||||
|
methods.date_out is null and
|
||||||
|
applications.date_out is null and
|
||||||
|
languages.date_out is null and
|
||||||
|
files.date_out is null;
|
||||||
|
|
||||||
|
drop view if exists LogsView;
|
||||||
|
create view LogsView as select
|
||||||
|
logs.id as id,
|
||||||
|
methods.application as 'application',
|
||||||
|
methods.language as 'language',
|
||||||
|
methods.file as 'file',
|
||||||
|
methods.name as method,
|
||||||
|
logs.line as 'line',
|
||||||
|
messages.message as 'message',
|
||||||
|
parameters.data as parameters,
|
||||||
|
logs.error as error,
|
||||||
|
logs.date_in as date_in
|
||||||
|
from Logs logs
|
||||||
|
join MethodsView methods on logs.method = methods.id
|
||||||
|
join Messages messages on logs.message = messages.id
|
||||||
|
join Parameters parameters on logs.parameters = parameters.id
|
||||||
|
where
|
||||||
|
logs.date_out is null and
|
||||||
|
messages.date_out is null and
|
||||||
|
parameters.date_out is null;
|
||||||
|
|
||||||
|
drop view if exists ExceptionsView;
|
||||||
|
create view ExceptionsView as select
|
||||||
|
exceptions.id as id,
|
||||||
|
methods.application as 'application',
|
||||||
|
methods.language as 'language',
|
||||||
|
methods.file as 'file',
|
||||||
|
methods.name as method,
|
||||||
|
exceptions.line as 'line',
|
||||||
|
messages.message as 'message',
|
||||||
|
traces.trace as 'trace',
|
||||||
|
exceptions_message.message as 'exception',
|
||||||
|
parameters.data as parameters,
|
||||||
|
exceptions.date_in as date_in
|
||||||
|
from Exceptions exceptions
|
||||||
|
join MethodsView methods on exceptions.method = methods.id
|
||||||
|
join Messages messages on exceptions.message = messages.id
|
||||||
|
join Messages exceptions_message on exceptions.exception = exceptions_message.id
|
||||||
|
join Traces traces on exceptions.trace = traces.id
|
||||||
|
join Parameters parameters on exceptions.parameters = parameters.id
|
||||||
|
where
|
||||||
|
exceptions.date_out is null and
|
||||||
|
messages.date_out is null and
|
||||||
|
exceptions_message.date_out is null and
|
||||||
|
traces.date_out is null and
|
||||||
|
parameters.date_out is null;
|
||||||
3
Tools/run.sh
Executable file
3
Tools/run.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
cd `dirname $(readlink -f "$0")`/../Python
|
||||||
|
python3 "cxcv.py"
|
||||||
Loading…
Reference in New Issue
Block a user