Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ IoTuring/Configurator/configurations.json*
IoTuring/Configurator/dontmoveconf.itg*
.venv
build
*.egg-info
*.egg-info
UserFiles/
67 changes: 10 additions & 57 deletions IoTuring/Configurator/ConfiguratorIO.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from IoTuring.Logger.LogObject import LogObject
from IoTuring.MyApp.App import App # App name
from IoTuring.MyApp.Directory import ConfigurationsDirectory

# macOS dep (in PyObjC)
try:
Expand All @@ -25,6 +26,7 @@
class ConfiguratorIO(LogObject):
def __init__(self):
self.directoryName = App.getName()
self.ConfigurationsDirectory = ConfigurationsDirectory()

def readConfigurations(self):
""" Returns configurations dictionary. If does not exist the file where it should be stored, return None. """
Expand All @@ -35,7 +37,7 @@ def readConfigurations(self):
self.Log(self.LOG_MESSAGE, "Loaded \"" + self.getFilePath() + "\"")
except:
self.Log(self.LOG_WARNING, "It seems you don't have a configuration yet. Use configuration mode (-c) to enable your favourite entities and warehouses.\
\nConfigurations will be saved in \"" + self.getFolderPath() + "\"")
\nConfigurations will be saved in \"" + self.ConfigurationsDirectory.getFolderPath() + "\"")
return config

def writeConfigurations(self, data):
Expand All @@ -51,64 +53,15 @@ def checkConfigurationFileExists(self):

def getFilePath(self):
""" Returns the path to the configurations file. """
return os.path.join(self.getFolderPath(), CONFIGURATION_FILE_NAME)
return os.path.join(self.ConfigurationsDirectory.getFolderPath(), CONFIGURATION_FILE_NAME)

def createFolderPathIfDoesNotExist(self):
""" Check if file exists, if not check if path exists: if not create both folder and file otherwise just the file """
if not os.path.exists(self.getFolderPath()):
if not os.path.exists(self.getFolderPath()):
os.makedirs(self.getFolderPath())
if not os.path.exists(self.ConfigurationsDirectory.getFolderPath()):
if not os.path.exists(self.ConfigurationsDirectory.getFolderPath()):
os.makedirs(self.ConfigurationsDirectory.getFolderPath())

def getFolderPath(self):
""" Returns the path to the configurations file. If the directory where the file
will be stored doesn't exist, it will be created. """

folderPath = self.defaultFolderPath()
try:
# Use path from environment variable if present, otherwise os specific folders, otherwise use default path
envvarPath = self.envvarFolderPath()
if envvarPath is not None:
folderPath = envvarPath
else:
_os = platform.system()
if _os == 'Darwin' and macos_support:
folderPath = self.macOSFolderPath()
folderPath = os.path.join(folderPath, self.directoryName)
elif _os == "Windows":
folderPath = self.windowsFolderPath()
folderPath = os.path.join(folderPath, self.directoryName)
elif _os == "Linux":
folderPath = self.linuxFolderPath()
folderPath = os.path.join(folderPath, self.directoryName)
except:
pass # default folder path will be used

# add slash if missing (for log reasons)
if not folderPath.endswith(os.sep):
folderPath += os.sep

return folderPath

def defaultFolderPath(self):
return os.path.dirname(inspect.getfile(ConfiguratorIO))

# https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html
def macOSFolderPath(self):
paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory,NSUserDomainMask,True)
basePath = (len(paths) > 0 and paths[0]) or NSTemporaryDirectory()
return basePath

# https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid
def windowsFolderPath(self):
return os.environ["APPDATA"]

# https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
def linuxFolderPath(self):
return os.environ["XDG_CONFIG_HOME"] if "XDG_CONFIG_HOME" in os.environ else os.path.join(os.environ["HOME"], ".config")

def envvarFolderPath(self):
return os.getenv(CONFIG_PATH_ENV_VAR)

# In versions prior to 2022.12.2, the configurations file was stored in the same folder as this file
def oldFolderPath(self):
return os.path.dirname(inspect.getfile(ConfiguratorIO))
Expand All @@ -123,7 +76,7 @@ def checkConfigurationFileInOldLocation(self):
"""

# This check is not done if old path = current chosen path (also check if ending slash is present)
if self.oldFolderPath() == self.getFolderPath() or self.oldFolderPath() == self.getFolderPath()[:-1]:
if self.oldFolderPath() == self.ConfigurationsDirectory.getFolderPath() or self.oldFolderPath() == self.ConfigurationsDirectory.getFolderPath()[:-1]:
return

# Exit check if no config. in old directory or if there is also the dont move file with it.
Expand All @@ -139,8 +92,8 @@ def checkConfigurationFileInOldLocation(self):
# create folder if not exists
self.createFolderPathIfDoesNotExist()
# copy file from old to new location
os.rename(os.path.join(self.oldFolderPath(), CONFIGURATION_FILE_NAME), os.path.join(self.getFolderPath(), CONFIGURATION_FILE_NAME))
self.Log(self.LOG_MESSAGE, "Copied into \"" + os.path.join(self.getFolderPath(), CONFIGURATION_FILE_NAME) + "\"")
os.rename(os.path.join(self.oldFolderPath(), CONFIGURATION_FILE_NAME), os.path.join(self.ConfigurationsDirectory.getFolderPath(), CONFIGURATION_FILE_NAME))
self.Log(self.LOG_MESSAGE, "Copied into \"" + os.path.join(self.ConfigurationsDirectory.getFolderPath(), CONFIGURATION_FILE_NAME) + "\"")
else:
# create dont move file
with open(os.path.join(self.oldFolderPath(), DONT_MOVE_FILE_FILENAME), "w") as f:
Expand Down
37 changes: 20 additions & 17 deletions IoTuring/Logger/Logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
import IoTuring.Logger.consts as consts
from IoTuring.Logger.Colors import Colors
import sys
import os # to access directory functions
import inspect # to get this file path
import os
from datetime import datetime # for logging purpose and filename
import json # to print a dict easily
import threading # to lock the file descriptor
from IoTuring.MyApp.Directory import LogsDirectory
from IoTuring.MyApp.App import App
# Singleton pattern used


Expand All @@ -17,7 +18,7 @@ class Logger():

lock = threading.Lock()

log_filename = ""
log_path = ""
log_file_descriptor = None

def __init__(self) -> None:
Expand All @@ -31,25 +32,26 @@ def __init__(self) -> None:
self.terminalSupportsColors = Logger.checkTerminalSupportsColors()

# Prepare the log
self.SetLogFilename()
self.SetLogPath()
# Open the file descriptor
self.GetLogFileDescriptor()

def SetLogFilename(self) -> str:
""" Set filename with timestamp and also call setup folder """
def SetLogPath(self) -> str:
""" Set filename with timestamp and also call setup folder to create log folder if does not exist (and get folder for the file)"""
dateTimeObj = datetime.now()
self.log_filename = os.path.join(
self.SetupFolder(), dateTimeObj.strftime(consts.LOG_FILENAME_FORMAT).replace(":", "_"))
return self.log_filename
self.log_path = os.path.join(
self.SetupFolder(), dateTimeObj.strftime(consts.LOG_FILENAME_FORMAT.format(App.getName())).replace(":", "_"))
print(self.log_path)
return self.log_path

def SetupFolder(self) -> str:
""" Check if exists (or create) the folder of logs inside this file's folder """
thisFolder = os.path.dirname(inspect.getfile(Logger))
newFolder = os.path.join(thisFolder, consts.LOGS_FOLDER)
if not os.path.exists(newFolder):
os.mkdir(newFolder)

return newFolder
""" Check if exists (or create) the folder of logs using LogsDirectory class """
print("here")
logsFolder = LogsDirectory().getFolderPath()
if not os.path.exists(logsFolder):
os.mkdir(logsFolder)
print("return",logsFolder)
return logsFolder

def GetMessageDatetimeString(self) -> str:
now = datetime.now()
Expand Down Expand Up @@ -155,7 +157,8 @@ def ColoredPrint(self, string, level) -> None:

def GetLogFileDescriptor(self) -> TextIOWrapper:
if self.log_file_descriptor is None:
self.log_file_descriptor = open(self.log_filename, "a")
self.log_file_descriptor = open(self.log_path, "a")
self.Log(self.LOG_MESSAGE, "Logger", "Log saved in " + self.log_path, printToConsole=True, writeToFile=False)

return self.log_file_descriptor

Expand Down
2 changes: 1 addition & 1 deletion IoTuring/Logger/consts.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
LOGS_FOLDER = "Logs"
LOG_FILENAME_FORMAT = "Log_%Y-%m-%d_%H:%M:%S.log"
LOG_FILENAME_FORMAT = "{}_%Y-%m-%d_%H:%M:%S.log" # first place for App Name
MESSAGE_DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'


Expand Down
1 change: 0 additions & 1 deletion IoTuring/MyApp/App.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import inspect
from IoTuring.Logger.Logger import Logger
from importlib_metadata import metadata
import os

Expand Down
29 changes: 29 additions & 0 deletions IoTuring/MyApp/Directory/ConfigurationsDirectory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import os
from .GetDirectory import GetDirectory

# macOS dep (in PyObjC)
try:
from AppKit import *
from Foundation import *
macos_support = True
except:
macos_support = False

class ConfigurationsDirectory(GetDirectory):
PATH_ENVVAR_NAME = "IOTURING_CONFIG_DIR" # Environment variable name
SUBFOLDER_NAME = "Configurations" # Subfolder name for configurations in default folder

# https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html
def _macOSFolderPath(self):
paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory,NSUserDomainMask,True)
basePath = (len(paths) > 0 and paths[0]) or NSTemporaryDirectory()
return basePath

# https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid
def _windowsFolderPath(self):
return os.environ["APPDATA"]

# https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
def _linuxFolderPath(self):
return os.environ["XDG_CONFIG_HOME"] if "XDG_CONFIG_HOME" in os.environ else os.path.join(os.environ["HOME"], ".config")

94 changes: 94 additions & 0 deletions IoTuring/MyApp/Directory/GetDirectory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import platform
import os
import sys
from importlib_metadata import metadata


class GetDirectory():
""" Class to get the path to the folder where things are stored in the system.
It will try to get the path from the environment variable IOTURING_PATH, if it is not set
it will use the default path for the OS.

There are 3 os-specific methods to get the default path.

Path selection is done in the following order:
1. Environment variable
2. OS-specific method
3. Default path

Once the path is obtained, it will be joined with the directory name of the application.

e.g.:
- by ENVVAR: CHOSEN_PATH
- Windows: C:\\Users\\username\\AppData\\Roaming\\ + IoTuring
- macOS: /Users/username/Library/Application Support/ + IoTuring/
- Linux: /home/username/.config/ + IoTuring/
- if error: default path

In addition, if default path is used and SUBFOLDER_NAME is set, it will be joined with the path.

e.g.:
- by ENVVAR: CHOSEN_PATH - like above
- Windows: C:\\Users\\username\\AppData\\Roaming\\ + IoTuring - like above
- if error: default path + SUBFOLDER_NAME
"""

PATH_ENVVAR_NAME = "IOTURING_PATH" # Environment variable name, default one
SUBFOLDER_NAME = None

APP_NAME = metadata('IoTuring')['Name']

def getFolderPath(self):
""" Returns the path to the folder where the application will store its data. """
folderPath = self._defaultFolderPath()
try:
# Use path from environment variable if present, otherwise os specific folders, otherwise use default path
envvarPath = self._envvarFolderPath()
if envvarPath is not None:
folderPath = envvarPath
else:
_os = platform.system()
if _os == 'Darwin':
folderPath = self._macOSFolderPath()
folderPath = os.path.join(folderPath, self.APP_NAME)
elif _os == "Windows":
folderPath = self._windowsFolderPath()
folderPath = os.path.join(folderPath, self.APP_NAME)
elif _os == "Linux":
folderPath = self._linuxFolderPath()
folderPath = os.path.join(folderPath, self.APP_NAME)
except:
folderPath = self._defaultFolderPath() # default folder path will be used

# add slash if missing (for log reasons)
if not folderPath.endswith(os.sep):
folderPath += os.sep

return folderPath


def _macOSFolderPath(self):
""" Returns the path to the folder where the application will store its data in macOS """
raise NotImplementedError("_macOSFolderPath() is not implemented yet")

def _windowsFolderPath(self):
""" Returns the path to the folder where the application will store its data in Windows """
raise NotImplementedError("_windowsFolderPath() is not implemented yet")

def _linuxFolderPath(self):
""" Returns the path to the folder where the application will store its data in Linux """
raise NotImplementedError("_linuxFolderPath() is not implemented yet")

def _envvarFolderPath(self):
""" Returns the path to the folder where the application will store its data from the environment variable; None if not set.
The environment variable name must be stored in self.PATH_ENVVAR_NAME """
# Get path from environment variable
return os.environ.get(self.PATH_ENVVAR_NAME)

def _defaultFolderPath(self):
""" Returns the path to the folder where the application will store its data in the default path.
If not overriden, it will return IoTuring install directory + "UserFiles/" + [SUBFOLDER_NAME if not None]"""
if self.SUBFOLDER_NAME is None:
return os.path.join(os.path.dirname(os.path.realpath(sys.modules['__main__'].__file__)), "UserFiles")
else:
return os.path.join(os.path.dirname(os.path.realpath(sys.modules['__main__'].__file__)), "UserFiles", self.SUBFOLDER_NAME)
32 changes: 32 additions & 0 deletions IoTuring/MyApp/Directory/LogsDirectory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import os
from .GetDirectory import GetDirectory

# macOS dep (in PyObjC)
try:
from AppKit import *
from Foundation import *
macos_support = True
except:
macos_support = False

class LogsDirectory(GetDirectory):
PATH_ENVVAR_NAME = "IOTURING_LOG_DIR" # Environment variable name
SUBFOLDER_NAME = "Logs" # Subfolder name for configurations in default folder

# get the folder where macos stores all the application log files
def _macOSFolderPath(self):
return os.path.join(NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, True)[0], "Logs")

# get the folder on windows where to store application log file
def _windowsFolderPath(self):
# return joined local app data folder and "Logs" subfolder
return os.path.join(os.environ["LOCALAPPDATA"], "Logs")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return os.path.join(os.environ["LOCALAPPDATA"], "Logs")
return os.path.join(os.environ["LOCALAPPDATA"], "IoTuring", "Logs")

Here as well, create a separate folder for IoTuring


# get the folder where linux stores all the application log files
def _linuxFolderPath(self):
# There is no standard in the XDG spec for logs, so place it in $XDG_CACHE_HOME, and fallback to $HOME/.cache
if "XDG_CACHE_HOME" in os.environ:
return os.path.join(os.environ["XDG_CACHE_HOME"], "Logs")
else:
return os.path.join(os.environ["HOME"], ".cache", "Logs")
Comment on lines +28 to +31
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if "XDG_CACHE_HOME" in os.environ:
return os.path.join(os.environ["XDG_CACHE_HOME"], "Logs")
else:
return os.path.join(os.environ["HOME"], ".cache", "Logs")
if "XDG_CACHE_HOME" in os.environ:
return os.path.join(os.environ["XDG_CACHE_HOME"], "IoTuring", "Logs")
else:
return os.path.join(os.environ["HOME"], ".cache", "IoTuring", "Logs")

These folders don't exist by default


2 changes: 2 additions & 0 deletions IoTuring/MyApp/Directory/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .ConfigurationsDirectory import ConfigurationsDirectory
from .LogsDirectory import LogsDirectory