Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5132004
Python3.8, docker in readme
infeeeee Dec 3, 2023
a0787c0
Open config in editor
infeeeee Dec 3, 2023
6cf6978
Fix OsD.GetEnv
infeeeee Dec 3, 2023
19dcb59
LogLevel exception, small Configurator changes
infeeeee Dec 3, 2023
344ae1e
Check if entity supported in configurator and before initialize
infeeeee Dec 13, 2023
4e761d8
Merge remote-tracking branch 'upstream/main' into CheckEntitySupport
infeeeee Dec 13, 2023
444274a
ActiveWindow SystemSupport
infeeeee Dec 22, 2023
4973500
Update Fanspeed, Lock, Notify
infeeeee Dec 22, 2023
118f3d3
Fix temperature OS check, change configurator message
richibrics Dec 23, 2023
9b12a96
Power entity systemsupport
infeeeee Dec 27, 2023
21276dd
Show unsupported entities in menu, terminal size handling
infeeeee Dec 28, 2023
264ab80
AppSettings retry
infeeeee Dec 30, 2023
254d0e6
Configuration classes, related changes
infeeeee Dec 31, 2023
720a7c5
Merge branch 'main' into AppSettings2
infeeeee Dec 31, 2023
5318c78
Fix test
infeeeee Dec 31, 2023
b9332f6
AppSettings Singleton
infeeeee Dec 31, 2023
fe3f999
Add documentation to new methods, update version to main
infeeeee Jan 4, 2024
75554ff
Merge remote-tracking branch 'upstream/main' into AppSettings2
infeeeee Jan 12, 2024
1d88b3e
Edit configurations, Separate LogSettings AppSettings
infeeeee Jan 13, 2024
2a29178
Merge branch 'main' into AppSettings2
infeeeee Jan 13, 2024
d5c09fd
Finished Logger settings
infeeeee Jan 14, 2024
6756b67
Fix test, consts
infeeeee Jan 14, 2024
6b64308
Remove unused import
infeeeee Jan 14, 2024
1fd5522
Change loglevel on Log init
infeeeee Jan 20, 2024
a68fb80
SettingsClassManager and SettingsManager
infeeeee Feb 24, 2024
e6cf143
Cleanup, documentation
infeeeee Feb 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 88 additions & 72 deletions IoTuring/ClassManager/ClassManager.py
Original file line number Diff line number Diff line change
@@ -1,107 +1,123 @@
from __future__ import annotations

import os
from pathlib import Path
from os import path
import importlib.util
import importlib.machinery
import sys
import inspect
from pathlib import Path

from IoTuring.Logger.LogObject import LogObject

# from IoTuring.ClassManager import consts

# This is a parent class
class ClassManager(LogObject):
"""Base class for ClassManagers

This class is used to find and load classes without importing them
The important this is that the class is inside a folder that exactly the same name of the Class and of the file (obviously not talking about extensions)
"""

# Implement subclasses in this way:
# Set up these class variables in subclasses:
classesRelativePath = None # Change in subclasses

# def __init__(self):
# ClassManager.__init__(self)
# self.baseClass = Entity : Select the class to find
# self.GetModulesFilename(consts.ENTITIES_PATH) : Select path where it should look for classes and add all classes to found list
def __init__(self) -> None:

# This class is used to find and load classes without importing them
# The important this is that the class is inside a folder that exactly the same name of the Class and of the file (obviously not talking about extensions)
classmanager_file_path = sys.modules[self.__class__.__module__].__file__
if not classmanager_file_path:
raise Exception("Error getting path: " +
str(classmanager_file_path))

self.rootPath = Path(classmanager_file_path).parents[1]

# Store loaded classes here:
self.loadedClasses = []

# Collect paths
self.moduleFilePaths = self.GetModuleFilePaths()

def GetModuleFilePaths(self) -> list[Path]:
"""Get the paths of of python files of this class"""

if not self.classesRelativePath:
raise Exception("Path to deployments not defined")

classesRootPath = self.rootPath.joinpath(self.classesRelativePath)

if not classesRootPath.exists:
raise Exception(f"Path does not exist: {classesRootPath}")

self.Log(self.LOG_DEVELOPMENT,
f'Looking for python files in "{classesRootPath}"...')

python_files = classesRootPath.rglob("*.py")

# TO check if a py files is in a folder !!!! with the same name !!! (same without extension)
filepaths = [f for f in python_files if f.stem == f.parent.stem]

self.Log(self.LOG_DEVELOPMENT,
f"Found {str(len(filepaths))} modules files")

return filepaths

def GetClassFromName(self, wantedName: str) -> type | None:
"""Get the class of given name, and load it

Args:
wantedName (str): The name to look for

Returns:
type | None: The class if found, None if not found
"""

# Check from already loaded classes:
module_class = next(
(m for m in self.loadedClasses if m.__name__ == wantedName), None)

if module_class:
return module_class

modulePath = next(
(m for m in self.moduleFilePaths if m.stem == wantedName), None)

if modulePath:

loadedModule = self.LoadModule(modulePath)
loadedClass = self.GetClassFromModule(loadedModule)
self.loadedClasses.append(loadedClass)
return loadedClass

class ClassManager(LogObject):
def __init__(self):
self.modulesFilename = []
module_path = sys.modules[self.__class__.__module__].__file__
if not module_path:
raise Exception("Error getting path: " + str(module_path))
else:
self.mainPath = path.dirname(path.abspath(module_path))
# THIS MUST BE IMPLEMENTED IN SUBCLASSES, IS THE CLASS I WANT TO SEARCH !!!!
self.baseClass = None

def GetClassFromName(self, wantedName) -> type | None:
# From name, load the correct module and extract the class
for module in self.modulesFilename: # Search the module file
moduleName = self.ModuleNameFromPath(module)
# Check if the module name matches the given name
if wantedName == moduleName:
# Load the module
loadedModule = self.LoadModule(module)
# Now get the class
return self.GetClassFromModule(loadedModule)
return None

def LoadModule(self, path): # Get module and load it from the path
return None

def LoadModule(self, module_path: Path): # Get module and load it from the path
try:
loader = importlib.machinery.SourceFileLoader(
self.ModuleNameFromPath(path), path)
module_path.stem, str(module_path))
spec = importlib.util.spec_from_loader(loader.name, loader)

if not spec:
raise Exception("Spec not found")

module = importlib.util.module_from_spec(spec)
loader.exec_module(module)
moduleName = os.path.split(path)[1][:-3]
sys.modules[moduleName] = module
sys.modules[module_path.stem] = module
return module
except Exception as e:
self.Log(self.LOG_ERROR, "Error while loading module " +
path + ": " + str(e))
self.Log(self.LOG_ERROR,
f"Error while loading module {module_path.stem}: {str(e)}")

# From the module passed, I search for a Class that has className=moduleName
def GetClassFromModule(self, module):
for name, obj in inspect.getmembers(module):
if inspect.isclass(obj):
if(name == module.__name__):
if (name == module.__name__):
return obj
raise Exception(f"No class found: {module.__name__}")

# List files in the _path directory and get only files in subfolders
def GetModulesFilename(self, _path):
classesRootPath = path.join(self.mainPath, _path)
if os.path.exists(classesRootPath):
self.Log(self.LOG_DEVELOPMENT,
"Looking for python files in \"" + _path + os.sep + "\"...")
result = list(Path(classesRootPath).rglob("*.py"))
entities = []
for file in result:
filename = str(file)
# TO check if a py files is in a folder !!!! with the same name !!! (same without extension)
pathList = filename.split(os.sep)
if len(pathList) >= 2:
if pathList[len(pathList)-1][:-3] == pathList[len(pathList)-2]:
entities.append(filename)

self.modulesFilename = self.modulesFilename + entities
self.Log(self.LOG_DEVELOPMENT, "Found " +
str(len(entities)) + " modules files")

def ModuleNameFromPath(self, path):
classname = os.path.split(path)
return classname[1][:-3]

def ListAvailableClassesNames(self) -> list:
res = []
for py in self.modulesFilename:
res.append(path.basename(py).split(".py")[0])
return res

def ListAvailableClasses(self) -> list:
return [self.GetClassFromName(n) for n in self.ListAvailableClassesNames()]
"""Get all classes of this ClassManager

Returns:
list: The list of classes
"""

return [self.GetClassFromName(f.stem) for f in self.moduleFilePaths]
9 changes: 2 additions & 7 deletions IoTuring/ClassManager/EntityClassManager.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
from IoTuring.ClassManager.ClassManager import ClassManager
from IoTuring.ClassManager import consts
from IoTuring.Entity.Entity import Entity


# Class to load Entities from the Entitties dir and get them from name
# Class to load Entities from the Entitties dir
class EntityClassManager(ClassManager):
def __init__(self):
ClassManager.__init__(self)
self.baseClass = Entity
self.GetModulesFilename(consts.ENTITIES_PATH)
# self.GetModulesFilename(consts.CUSTOM_ENTITIES_PATH) # TODO Decide if I'll use customs
classesRelativePath = consts.ENTITIES_PATH
10 changes: 10 additions & 0 deletions IoTuring/ClassManager/SettingsClassManager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from IoTuring.ClassManager.ClassManager import ClassManager
from IoTuring.ClassManager import consts


# Class to load Entities from the Entitties dir
class SettingsClassManager(ClassManager):

classesRelativePath = consts.SETTINGS_PATH


10 changes: 4 additions & 6 deletions IoTuring/ClassManager/WarehouseClassManager.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
from IoTuring.ClassManager.ClassManager import ClassManager
from IoTuring.ClassManager import consts
from IoTuring.Warehouse.Warehouse import Warehouse


# Class to load Entities from the Entitties dir and get them from name
# Class to load Warehouses from the Warehouses dir

class WarehouseClassManager(ClassManager):
def __init__(self):
ClassManager.__init__(self)
self.baseClass = Warehouse
self.GetModulesFilename(consts.WAREHOUSES_PATH)

classesRelativePath = consts.WAREHOUSES_PATH
5 changes: 3 additions & 2 deletions IoTuring/ClassManager/consts.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
ENTITIES_PATH = "../Entity/Deployments/"
WAREHOUSES_PATH = "../Warehouse/Deployments/"
ENTITIES_PATH = "Entity/Deployments"
WAREHOUSES_PATH = "Warehouse/Deployments"
SETTINGS_PATH = "Settings/Deployments"
Loading