diff --git a/Gui/PyqcrmConf.qml b/Gui/PyqcrmConf.qml
index e95c337..d1f6ed9 100644
--- a/Gui/PyqcrmConf.qml
+++ b/Gui/PyqcrmConf.qml
@@ -143,7 +143,7 @@ Item
if (db['database']['DB_HOST'] === '' || db['database']['DB_PORT'] === '' ||
db['database']['DB_NAME'] === '' || db['database']['DB_USER'] === '' ||
db['database']['DB_PASS'] === '');
- else config.saveDbConf(company)
+ else config.saveDbConf(db)
}
function updateCompanyInfo()
diff --git a/lib/ConfigLoader.py b/lib/ConfigLoader.py
index 5867a34..5be28fc 100644
--- a/lib/ConfigLoader.py
+++ b/lib/ConfigLoader.py
@@ -1,4 +1,56 @@
-# This Python file uses the following encoding: utf-8
+"""! @brief Defines the configuration class."""
+##
+# @file ConfigLoader.py
+#
+# @brief Defines the ConfigLoader class.
+#
+# @section description_configloader Description
+# Defines the base class for the program configuration.
+# - ConfigLoader (base class)
+#
+# @section libraries_configloader Libraries/Modules
+# - os standard library
+# - Access to system specific information.
+# - urllib Python package
+# - Collects several modules for working with URLs.
+# - toml Python library
+# - A Python library for parsing and creating TOML.
+# - platformdirs Python package
+# - Determining appropriate platform-specific dirs.
+# - pathlib Python module
+# - Offers classes representing filesystem paths with semantics appropriate for different operating systems.
+# - shutil Python module
+# - Offers a number of high-level operations on files and collections of files.
+# - base64 Python standard library
+# - Provides functions for encoding binary data to printable ASCII characters and decoding such encodings back to binary data.
+# - QObject PySide6 class
+# - The base class of all Qt objects.
+# - Slot PySide6 function
+# - A function that is called in response to a particular signal.
+# - Signal PySide6 class
+# - Provides a way to declare and connect Qt signals in a pythonic way.
+# - Crypto Python package
+# - Provides cryptographic functionalities.
+# - DbManager local class
+# - Provides a singleton database connection for the entire program.
+# - UserManager local class
+# - Provides a model to handle users.
+# - PyqcrmFlags local ENUM
+# - Provides ENUMS to facilitate working in the program.
+# - Vermasseln local class
+# - Provides encryption functionality for the program.
+#
+# @section notes_configloader Notes
+# - Needs a database connection.
+#
+# @section todo_configloader TODO
+# - None.
+#
+# @section author_configloader Author(s)
+# - Created by Linuxero on 03/14/2025.
+# - Modified by Linuxero on 03/14/2025.
+#
+# Copyright (c) 2025 Schnaxero. All rights reserved.
import toml
from platformdirs import user_config_dir
from pathlib import Path
@@ -17,6 +69,10 @@ from .PyqcrmFlags import PyqcrmFlags
class ConfigLoader(QObject):
+ """! The ConfigLoader class.
+ Defines the class utilized by all different parts of the program.
+ Handles the local configuration of the whole program.
+ """
__config = None
__version = "0.1-alpha"
__check_enc_key = True
@@ -30,6 +86,8 @@ class ConfigLoader(QObject):
invalidEncryptionKey = Signal()
def __init__(self):
+ """! The ConfigLoader class initializer.
+ """
super().__init__()
# print(f"In {__file__} file, __init__()")
self.config_dir = user_config_dir() + '/pyqcrm'
@@ -44,6 +102,10 @@ class ConfigLoader(QObject):
@Slot(dict, result = bool)
def setConfig(self, app_config):
+ """! Prepares the configuration of the program.
+ @param app_config The configuration as a dictionary.
+ @return True on success, False on failure.
+ """
# print(f"In {__file__} file, setConfig()")
if not self.__config:
base_conf = self.__initializeConfig()
@@ -58,6 +120,8 @@ class ConfigLoader(QObject):
self.configurationReady.emit()
def __initializeConfig(self):
+ """! Creates the initial configuration of the program.
+ """
# print(f"In {__file__} file, __initializeConfig()")
self.__encrypt_key = b64encode(get_random_bytes(32)).decode("utf-8")
conf = f"[pyqcrm]\nVERSION = \"{self.__version}\"\n"
@@ -66,6 +130,10 @@ class ConfigLoader(QObject):
return conf
def __checkDbConnection(self, db_config):
+ """! Tests for a valid database connection.
+ @param db_config The configuration of the database connection as a dictionary.
+ @return True on success, False on failure.
+ """
# print(f"In {__file__} file, __checkDbConnection()")
con = DbManager(db_config['database']).getConnection()
if con:
@@ -77,6 +145,8 @@ class ConfigLoader(QObject):
def __saveConfig(self):
+ """! Saves the configuration of the program.
+ """
# print(f"In {__file__} file, saveConfig()")
try:
with open (self.config_dir + '/pyqcrm.toml', 'w') as f:
@@ -88,6 +158,9 @@ class ConfigLoader(QObject):
def __checkAdminUser(self):
+ """! Checks for a valid admin account of the program.
+ @return True on success, False on failure.
+ """
# print(f"In {__file__} file, __checkAdminUser()")
result = UserManager().checkAdmin()
if not result:
@@ -100,6 +173,10 @@ class ConfigLoader(QObject):
@Slot(dict, result= bool)
def addAdminUser(self, user_config):
+ """! Adds an admin account.
+ @param user_config The credentials of the admin account as a dictionary.
+ @return True on success, False on failure.
+ """
# print(f"In {__file__} file, addAdminUser()")
admin = UserManager(user_config["user"], PyqcrmFlags.ADMIN).createUser()
if not admin:
@@ -114,6 +191,12 @@ class ConfigLoader(QObject):
@Slot(str, str)
def __saveData(self, recovery_file, recovery_password, data):
+ """! Generic function to save backups to a file.
+ This function emits a configurationReady signal.
+ @param recovery_file The full path of the backup file.
+ @param recovery_password password to secure the backup file.
+ @param data The content of the backup file.
+ """
# print(f"In {__file__} file, __saveData()")
local = False
rp = self.__setRecoveryPassword(recovery_password)
@@ -133,6 +216,11 @@ class ConfigLoader(QObject):
@Slot(str, str)
def getRecoveryKey(self, recovery_file, recovery_password):
+ """! Loads the encryption key from a backup.
+ This function emits a configurationReady signal.
+ @param recovery_file The full path of the backup file.
+ @param recovery_password password to secure the backup file.
+ """
rec_file = urlparse(recovery_file)
rec_file = rec_file.path
if os.name == "nt":
@@ -150,6 +238,11 @@ class ConfigLoader(QObject):
print(str(e))
def __parseImport(self, rec_file, recovery_password):
+ """! Loads the content from a backup.
+ @param rec_file The full path of the backup file.
+ @param recovery_password password used to secure the backup file.
+ @return The content on success, None on failure.
+ """
local = False
with open(rec_file, "r") as f:
@@ -167,23 +260,40 @@ class ConfigLoader(QObject):
def __invalidateEncryptionKey(self):
+ """! Flag the encryption key as invalid.
+ """
# print(f"In {__file__} file, __invalidateEncryptionKey()")
self.__config['pyqcrm']['ENCRYPTION_KEY_VALID'] = 'No'
self.__saveConfig()
@Slot()
def checkEncryptionKey(self):
+ """! Checks the validity of the encryption key.
+ This function emits an invalidEncryptionKey signal.
+ """
# print(f"In {__file__} file, __checkEncryptionKey()")
if self.__config['pyqcrm']['ENCRYPTION_KEY_VALID'] == 'No':
self.invalidEncryptionKey.emit()
def __checkRecoveryPassword(self, recovery_password, password, salt):
+ """! Generic function to save backups to a file.
+ This function emits a configurationReady signal.
+ @param recovery_password The password from the backup file.
+ @param password The password used when creating the backup file.
+ @param salt A salt to hash the password.
+ @return A password.
+ """
# print(f"In {__file__} file, __checkRecoveryPassword()")
rp = self.__setRecoveryPassword(recovery_password, salt)
return rp[1] == password
@Slot(str, str) # todo: non local encryption
def importConfig(self, confile, password):
+ """! Generic function to import configuration from a backup.
+ This function emits a invalidEncryptionKey signal.
+ @param conffile The path of the backup file.
+ @param password The password used when creating the backup file.
+ """
confile = urlparse(confile)
confile = confile.path
if os.name == "nt":
@@ -206,6 +316,9 @@ class ConfigLoader(QObject):
def __configLoad(self):
+ """! Loads the program configuration.
+ This function emits a configurationReady signal.
+ """
# print(f"In {__file__} file, __configLoad()")
try:
with open (self.config_dir + '/pyqcrm.toml', 'r') as f:
@@ -221,6 +334,9 @@ class ConfigLoader(QObject):
def getConfig(self):
+ """! Returns the program configuration.
+ @return configuration as a toml file.
+ """
# print(f"In {__file__} file, getConfig()")
# print(self.__config)
return self.__config
@@ -239,17 +355,27 @@ class ConfigLoader(QObject):
@Slot(str, str)
def backupConfig(self, filename, password):
+ """! Saves the program configuration.
+ @param filename the path of the backup file.
+ @param password the password to secure the backup.
+ """
conf_file = toml.dumps(self.getConfig())
self.__saveData(filename, password, conf_file)
@Slot(dict)
def saveDbConf(self, db = None):
+ """! Saves/Upates the database configuration.
+ @param db Database configuration as a dictionary.
+ """
self.__config.update(db)
self.__saveConfig()
@Slot(result = dict)
def getDbConf(self):
+ """! Loads the database configuration.
+ @return Database configuration as a dictionary on success, None on failure.
+ """
try:
return self.__config['database']
except KeyError as ex:
@@ -258,11 +384,17 @@ class ConfigLoader(QObject):
@Slot(dict)
def saveCompanyInfo(self, company = None):
+ """! Saves/Upates the company information.
+ @param company Company configuration as a dictionary.
+ """
self.__config.update(company)
self.__saveConfig()
@Slot(result = dict)
def getCompanyInfo(self):
+ """! Loads the company information.
+ @return Company information as a dictionary on success, None on failure.
+ """
try:
return self.__config['company']
except KeyError as ex:
@@ -271,11 +403,17 @@ class ConfigLoader(QObject):
@Slot(dict)
def saveMiscConf(self, misc_conf = None):
+ """! Saves/Upates the miscellaneous configuration.
+ @param misc_conf Extra configuration as a dictionary.
+ """
self.__config.update(misc_conf)
self.__saveConfig()
@Slot(result = bool)
def systray(self):
+ """! Loads the system tray configuration.
+ @return boolean if system tray is set to be used, False can also be returned on failure.
+ """
try:
return self.__config['misc']['SYSTRAY']
except KeyError as ex:
@@ -285,6 +423,10 @@ class ConfigLoader(QObject):
@Slot(str, str)
def backupEncryptkey(self, filename, password):
+ """! Saves/Upates the encryption key.
+ @param filename Path to save the key.
+ @param password Password to secure the backup.
+ """
encrypt_key = self.__config['pyqcrm']['ENCRYPTION_KEY']
self.__saveData(filename, password, encrypt_key)
diff --git a/lib/DB/AddressDAO.py b/lib/DB/AddressDAO.py
index b2150bd..c2f7cf5 100644
--- a/lib/DB/AddressDAO.py
+++ b/lib/DB/AddressDAO.py
@@ -1,11 +1,47 @@
+"""! @brief Defines the low-level DAO class to handle addresses."""
+##
+# @file AddressDAO.py
+#
+# @brief Defines the AddressDAO class.
+#
+# @section description_addressdao Description
+# Defines the low-lever DAO class to handle CRUD operations on addresses.
+# - AddressDAO (DAO class)
+#
+# @section libraries_addressdao Libraries/Modules
+# - mariadb Python module
+# - MariaDB Connector/Python enables python programs to access MariaDB and MySQL databases, using an API which is compliant with the Python DB API 2.0 (PEP-249).
+# - json Python standard library
+# - JSON encoder and decoder.
+# - DbManager Local class
+# - Singleton class to provide for the database connection.
+#
+# @section notes_addressdao Notes
+# - None.
+#
+# @section todo_addressdao TODO
+# - None.
+#
+# @section author_addressdao Author(s)
+# - Created by Linuxero on 03/14/2025.
+# - Modified by Linuxero on 03/14/2025.
+#
+# Copyright (c) 2025 Schnaxero. All rights reserved.
+
from .DbManager import DbManager
import mariadb
import json
class AddressDAO:
+ """! The AddressDAO class.
+ Defines a low-level class utilized for DAO.
+ Handles the address CRUD operations.
+ """
__cur = None
def __init__(self):
+ """! The AddressDAO class initializer.
+ """
#print(f"*** File: {__file__}, init()")
self.__con = DbManager().getConnection()
@@ -16,6 +52,8 @@ class AddressDAO:
def __importPlz(self):
+ """! Utility function to import zipcodes from an external databases.
+ """
with open("pfad zur datei", "r") as plz:
postcodes = json.load(plz)
irgendwas = ""
@@ -37,6 +75,8 @@ class AddressDAO:
print("FINISHED")#
def __importCountry(self):
+ """! Utility function to import countries information from an external databases.
+ """
with open("pfad zur datei", "r") as country:
countries = json.load(country)
old = ""
@@ -67,6 +107,11 @@ class AddressDAO:
def getAddressData(self, all = True, zipcode = None):
+ """! Loads available addresses in the program.
+ @param all Filter to get specific addresses as a boolean.
+ @param zipcode Specific zipcode pattern.
+ @return Found addresses as a dictionary on success, None on failure.
+ """
try:
if self.__cur:
self.__cur.callproc("getAddress", (all, zipcode,))
diff --git a/lib/DB/AddressModel.py b/lib/DB/AddressModel.py
index aecd12f..bfdf65d 100644
--- a/lib/DB/AddressModel.py
+++ b/lib/DB/AddressModel.py
@@ -1,17 +1,63 @@
+"""! @brief Defines the model class to handle addresses."""
+##
+# @file AddressModel.py
+#
+# @brief Defines the AddressModel class.
+#
+# @section description_addressmodel Description
+# Defines the model class to handle CRUD operations on addresses.
+# - AddressModel (Model class)
+#
+# @section libraries_addressmodel Libraries/Modules
+# - QAbstractListModel PySid6 core Class
+# - Provides an abstract model that can be subclassed to create one-dimensional list models.
+# - QModelIndex PySid6 core Class
+# - Used to locate data in a data model.
+# - Slot PySide6 function
+# - A function that is called in response to a particular signal.
+# - Qt PySid6 core Class
+# - A namespace contains miscellaneous identifiers used throughout the Qt library.
+# - AddressDAO Local class
+# - Defines the low-lever DAO class to handle CRUD operations on addresses.
+#
+# @section notes_addressmodel Notes
+# - None.
+#
+# @section todo_addressmodel TODO
+# - None.
+#
+# @section author_addressmodel Author(s)
+# - Created by Linuxero on 03/14/2025.
+# - Modified by Linuxero on 03/14/2025.
+#
+# Copyright (c) 2025 Schnaxero. All rights reserved.
+
from PySide6.QtCore import QAbstractListModel, Qt, Slot, QModelIndex
from .AddressDAO import AddressDAO
from ..PyqcrmDataRoles import PyqcrmDataRoles
class AddressModel(QAbstractListModel):
-
+ """! The AddressModel class.
+ Defines a model class utilized to handle data.
+ Inherits from QAbstractListModel
+ Handles the address data operations.
+ """
def __init__(self):
+ """! The AddressModel class initializer.
+ """
super().__init__()
self.__address_data = AddressDAO().getAddressData()
def rowCount(self, parent = QModelIndex()):
+ """! Returns the number of rows under the given parent.
+ Ref. rowCount()
+ """
return len(self.__address_data)
def data(self, index, role = Qt.DisplayRole):
+ """! Returns the data stored under the given role for the item referred to by the index.
+ Ref. data()
+ """
row = index.row()
if role == Qt.DisplayRole:
data = self.__address_data[row][2]
@@ -22,6 +68,9 @@ class AddressModel(QAbstractListModel):
return None
def roleNames(self):
+ """! Returns the model’s role names.
+ Ref. roleNames()
+ """
return {
Qt.DisplayRole: b"display",
PyqcrmDataRoles.CITY_ROLE: b"city",
@@ -32,6 +81,11 @@ class AddressModel(QAbstractListModel):
@Slot(bool, str)
def getAddresses(self, all, zipcode):
+ """! Loads the addresses from the storage backend.
+ @param all Boolean to specify whether all addresses to be returned or not.
+ @param zipcode String to look up addresses following a specific zipcode.
+ @return Returns a dictionary containing the addresses.
+ """
data = AddressDAO().getAddressData(all, zipcode)
return data
diff --git a/lib/DB/BTypeModel.py b/lib/DB/BTypeModel.py
index 246dd66..859a5da 100644
--- a/lib/DB/BTypeModel.py
+++ b/lib/DB/BTypeModel.py
@@ -1,16 +1,63 @@
+"""! @brief Defines the model class to handle type of client."""
+##
+# @file BTypeModel.py
+#
+# @brief Defines the BTypeModel class.
+#
+# @section description_businesstypemodel Description
+# Defines the model class to handle CRUD operations on customers types.
+# - BTypeModel (Model class)
+#
+# @section libraries_businesstypemodel Libraries/Modules
+# - QAbstractListModel PySid6 core Class
+# - Provides an abstract model that can be subclassed to create one-dimensional list models.
+# - QModelIndex PySid6 core Class
+# - Used to locate data in a data model.
+# - Slot PySide6 function
+# - A function that is called in response to a particular signal.
+# - Qt PySid6 core Class
+# - A namespace contains miscellaneous identifiers used throughout the Qt library.
+# - BTypeDAO Local class
+# - Defines the low-lever DAO class to handle CRUD operations on customers types.
+#
+# @section notes_businesstypemodel Notes
+# - None.
+#
+# @section todo_businesstypemodel TODO
+# - None.
+#
+# @section author_businesstypemodel Author(s)
+# - Created by Linuxero on 03/14/2025.
+# - Modified by Linuxero on 03/14/2025.
+#
+# Copyright (c) 2025 Schnaxero. All rights reserved.
+
from PySide6.QtCore import QAbstractListModel, Qt, QModelIndex
from .BTypeDAO import BTypeDAO
class BTypeModel(QAbstractListModel):
+ """! The BTypeModel class.
+ Defines a model class utilized to handle data.
+ Inherits from QAbstractListModel
+ Handles the customers types data operations.
+ """
def __init__(self):
+ """! The AddressModel class initializer.
+ """
super().__init__()
self.__btype_data = BTypeDAO().getBType()
def rowCount(self, parent = QModelIndex()):
+ """! Returns the number of rows under the given parent.
+ Ref. rowCount()
+ """
return len(self.__btype_data)
def data(self, index, role = Qt.DisplayRole):
+ """! Returns the data stored under the given role for the item referred to by the index.
+ Ref. data()
+ """
row = index.row()
if role == Qt.DisplayRole:
data= self.__btype_data[row][1]
diff --git a/lib/DB/BusinessModel.py b/lib/DB/BusinessModel.py
index 36b188d..947bbb0 100644
--- a/lib/DB/BusinessModel.py
+++ b/lib/DB/BusinessModel.py
@@ -1,4 +1,41 @@
-# This Python file uses the following encoding: utf-8
+"""! @brief Defines the model class to handle customers."""
+##
+# @file BusinessModel.py
+#
+# @brief Defines the BusinessModel class.
+#
+# @section description_businessmodel Description
+# Defines the model class to handle CRUD operations on customers.
+# - BusinessModel (Model class)
+#
+# @section libraries_businessmodel Libraries/Modules
+# - QAbstractListModel PySid6 core Class
+# - Provides an abstract model that can be subclassed to create one-dimensional list models.
+# - QModelIndex PySid6 core Class
+# - Used to locate data in a data model.
+# - Slot PySide6 function
+# - A function that is called in response to a particular signal.
+# - Signal PySide6 class
+# - Provides a way to declare and connect Qt signals in a pythonic way.
+# - Qt PySid6 core Class
+# - A namespace contains miscellaneous identifiers used throughout the Qt library.
+# - BusinessDAO Local class
+# - Defines the low-lever DAO class to handle CRUD operations on customers.
+# - ConfigLoader Local class
+# - Defines the base class for the program configuration.
+#
+# @section notes_businessmodel Notes
+# - None.
+#
+# @section todo_businessmodel TODO
+# - None.
+#
+# @section author_businessmodel Author(s)
+# - Created by Linuxero on 03/14/2025.
+# - Modified by Linuxero on 03/14/2025.
+#
+# Copyright (c) 2025 Schnaxero. All rights reserved.
+
from PySide6.QtCore import QAbstractTableModel, QModelIndex, Qt, Slot, Signal
from .BusinessDAO import BusinessDAO
from ..PyqcrmFlags import PyqcrmFlags
@@ -62,6 +99,11 @@ from ..ConfigLoader import ConfigLoader
class BusinessModel(QAbstractTableModel):
+ """! The BusinessModel class.
+ Defines a model class utilized to handle data.
+ Inherits from QAbstractListModel
+ Handles the customers data operations.
+ """
__visible_index = {}
__visible_columns = None
__col_name = ""
@@ -70,6 +112,8 @@ class BusinessModel(QAbstractTableModel):
__business_dict = {'business':{}} #,'contact':{}}
def __init__(self):
+ """! The AddressModel class initializer.
+ """
super().__init__()
self.__business_dao = BusinessDAO()
self.__business_dao.newBusinessAdded.connect(self.__refreshView)
@@ -78,12 +122,17 @@ class BusinessModel(QAbstractTableModel):
self.__getData()
def __getData(self, criterion = "Alle"):
+ """! Returns the customers data according to the model.
+ @param criterion String to specify which customers are to be fetched.
+ """
self.beginResetModel()
rows, self.__visible_columns = self.__business_dao.getBusiness(self.__key, criterion)
self.__data = rows
self.endResetModel()
def __getBusinessInfo(self):
+ """! Fetches detailed information about a customer.
+ """
self.__business_dict['business']['id'] = self.__business[0][0]
self.__business_dict['business']['contactid'] = self.__business[0][1]
self.__business_dict['business']['company'] = self.__business[0][2]
@@ -100,18 +149,30 @@ class BusinessModel(QAbstractTableModel):
self.__business_dict['business']['city'] = self.__business[0][13]
def rowCount(self, parent= QModelIndex()):
+ """! Returns the number of rows under the given parent.
+ Ref. rowCount()
+ """
return len (self.__data)
def columnCount(self, parent= QModelIndex()):
+ """! Returns the number of columns for the children of the given parent.
+ Ref. columnCount()
+ """
return len(self.__visible_columns) - 1
def data(self, index, role = Qt.DisplayRole):
+ """! Returns the data stored under the given role for the item referred to by the index.
+ Ref. data()
+ """
if role == Qt.DisplayRole:
row = self.__data[index.row()]
return row[index.column() + 1]
return None
def headerData(self, section, orientation, role= Qt.DisplayRole):
+ """! Returns the data for the given role and section in the header with the specified orientation.
+ Ref. headerData()
+ """
if orientation == Qt.Horizontal and role ==Qt.DisplayRole:
self.__col_name = self.__visible_columns[section + 1]
return self.__col_name
@@ -132,6 +193,9 @@ class BusinessModel(QAbstractTableModel):
@Slot(int)
def onRowClicked(self, row):
+ """! Handles a selected customer from the GUI.
+ @param row The number of the customer in the data model.
+ """
#print(self.__data)
#print(f"Selected table row: {row}, corresponding DB ID: {self.__data[row][0]}")
if not self.__business_dict['business'] or self.__data[row][0] != self.__business_dict['business']['id']:
@@ -143,19 +207,31 @@ class BusinessModel(QAbstractTableModel):
@Slot(result = dict)
def getClientDetails(self):
+ """! Fetches a selected customer from the GUI.
+ @return A dictionary containing all information of a customer.
+ """
return self.__business_dict
@Slot(str)
def viewCriterion(self, criterion):
+ """! Updates the customers view.
+ @param criterion The criterion used to look for customers.
+ """
self.__getData(criterion)
@Slot(dict, int)
def addBusiness(self, business, contact_id):
+ """! Saves a customer view.
+ @param business The customer to be saved.
+ @param contact_id The contact person id if available.
+ """
self.__business_dao.addBusiness(business, contact_id)
@Slot()
def __refreshView(self):
+ """! Updates the customers view.
+ """
self.__getData()
@Slot(dict)
diff --git a/lib/Vermasseln.py b/lib/Vermasseln.py
index 67570cb..008e382 100644
--- a/lib/Vermasseln.py
+++ b/lib/Vermasseln.py
@@ -1,16 +1,58 @@
-# This Python file uses the following encoding: utf-8
+"""! @brief Defines the encryption class."""
+##
+# @file Vermasseln.py
+#
+# @brief Defines the Vermasseln class.
+#
+# @section description_vermasseln Description
+# Defines the base class for the program encryption mechansim.
+# - Vermasseln (base class)
+#
+# @section libraries_configloader Libraries/Modules
+# - platform Python standard library
+# - Access to underlying platform’s identifying data.
+# - base64 Python standard library
+# - Provides functions for encoding binary data to printable ASCII characters and decoding such encodings back to binary data.
+# - random Python module
+# - Implements pseudo-random number generators for various distributions.
+# - string Python standard library
+# - Common string operations.
+# - Crypto Python package
+# - Provides cryptographic functionalities.
+#
+# @section notes_vermasseln Notes
+# - None.
+#
+# @section todo_vermasseln TODO
+# - None.
+#
+# @section author_vermasseln Author(s)
+# - Created by Linuxero on 03/14/2025.
+# - Modified by Linuxero on 03/14/2025.
+#
+# Copyright (c) 2025 Schnaxero. All rights reserved.
+
from Crypto.Cipher import AES
from base64 import b64encode, b64decode
import platform
from Crypto.Hash import SHA256, SHA3_512
-from Crypto.Protocol.KDF import PBKDF2
-from Crypto.Random import get_random_bytes
+# from Crypto.Protocol.KDF import PBKDF2
+# from Crypto.Random import get_random_bytes
import random
import string
class Vermasseln:
+ """! The Vermasseln class.
+ Defines the class utilized by different parts of the program.
+ Handles the encryption/decryption of the whole program.
+ """
def oscarVermasseln(self, data, local= True):
+ """! Encrypts data.
+ @param data The data to encrypt.
+ @param local Is the encryption local to the host or not?
+ @return encrypted data.
+ """
b_data = data.encode("utf-8")
cipher = self.__vermasslungsKobold(local)
@@ -21,6 +63,11 @@ class Vermasseln:
return storable_data
def entschluesseln(self, data, local = True):
+ """! Decrypts data.
+ @param data The data to decrypt.
+ @param local Is the encryption local to the host or not?
+ @return decrypted data on success, None on failure.
+ """
try:
data_list = data.split(".")
encoded_data = [b64decode(x) for x in data_list]
@@ -38,6 +85,10 @@ class Vermasseln:
return decrypted_data
def __vermasslungsKobold(self, local = True):
+ """! Prepares the encryption key.
+ @param local Is the encryption local to the host or not?
+ @return encryption key.
+ """
key = platform.processor().encode("utf-8") if local else b"(==daniishtverhaftetwegensexy#)"
key = key[0:31]
hash_key = SHA256.new(key)
@@ -48,6 +99,11 @@ class Vermasseln:
@classmethod
def userPasswordHash(self, password, salt = None):
+ """! Hashes data.
+ @param password The data to hash.
+ @param salt The salt to use if available.
+ @return hashed data.
+ """
if not salt:
salt = "".join(random.choice(string.ascii_letters + string.digits) for i in range (32))
hash_pw = (salt + password).encode("utf-8")
diff --git a/main.py b/main.py
index e95c0cb..0b59450 100644
--- a/main.py
+++ b/main.py
@@ -1,4 +1,63 @@
# # !/home/linuxero/proj/tero/pyqcrm/.qtcreator/venv-3.13.1/bin/python3
+"""! @brief CRM for cleaning services written in Python, PySide6 and QtQuick."""
+##
+# @mainpage PYQCRM - Qt for Python CRM
+#
+# @section description_main Description
+# A CRM program for digitally manageing the business of cleaning
+# services company.
+#
+# @section notes_main Notes
+# - The project is built using QtCreator 6.8.x
+# - Minimum Python version 3.13.x
+# - PySide6
+#
+# Copyright (c) 2025 Schnaxero. All rights reserved.
+##
+# @file main.py
+#
+# @brief Main entry point of the program
+#
+# @section description_pyqcrm Description
+# Initialization of the program goes here. Various global functions called from the entry point.
+# The GUI application is set up and started here.
+#
+# @section libraries_main Libraries/Modules
+# - os standard library
+# - Access to environ, a mapping object of keys and values to set an environment variable.
+# - sys standard library
+# - Access to argv and system functions.
+# - logging standard library
+# - Access to logging functions.
+# - QLocalServer PySide6 class
+# - Provides a local socket based server.
+# - QLocalSocket PySide6 class
+# - Provides a local socket.
+# - QSystemTrayIcon PySide6 class
+# - Provides an icon for the application in the system tray.
+# - QIcon PySide6 class
+# - Provides scalable icons in different modes and states.
+# - QGuiApplication PySide6 class
+# - Manages the GUI application’s control flow and main settings.
+# - QQmlApplicationEngine PySide6 class
+# - Provides a convenient way to load an application from a single QML file.
+# - QIODevice PySide6 class
+# - Base interface class of all I/O devices in Qt.
+# - lib module (local)
+# - Backbone classes to run the program.
+#
+# @section notes_pyqcrm Notes
+# - Install dependencies (See requirements.txt).
+#
+# @section todo_pyqcrm TODO
+# - A lot of nice-to-haves.
+#
+# @section author_pyqcrm Author(s)
+# - Created by Linuxero on 03/14/2025.
+# - Modified by Linuxero on 03/14/2025.
+#
+# Copyright (c) 2025 Schnaxero. All rights reserved.
+# Imports
import os
import sys
import logging
@@ -20,7 +79,8 @@ from lib.DB.EmployeeModel import EmployeeModel
from lib.DB.ObjectModel import ObjectModel
from lib.Printers import Printers
-
+# Environment settings
+## Allow local file read.
os.environ['QML_XHR_ALLOW_FILE_READ'] = '1'
# [pyqcrm]
@@ -35,19 +95,40 @@ os.environ['QML_XHR_ALLOW_FILE_READ'] = '1'
# name=""
# type=""
-
+# Global Constants
+## Configuration availability.
bad_config = False
+
+## Database connection available.
db_con = False
+
+## The model class for address manipulation.
address_model = None
+
+## The model class for customer manipulation.
business_model = None
+
+## The model class for customer type manipulation.
business_type = None
+
+## The model class for contact manipulation.
contact_model = None
+
+## The model class for employee manipulation.
employee_model = None
+
+## The model class for object manipulation.
object_model = None
+
+## The class of available printers on the system.
printers = None
+
+## The logged-in user.
user = None
+# Functions
def initializeProgram():
+ """! Initializes the program."""
print(f"In {__file__} file, initializeProgram()")
global address_model, bad_config, business_model, user, business_type, contact_model, employee_model, object_model, db_con, printers
if not bad_config:
@@ -66,12 +147,14 @@ def initializeProgram():
publishContext()
def configReady():
+ """! Slot to respond to the validity of the configuration."""
global bad_config
bad_config = False
initializeProgram()
def publishContext():
+ """! Connect necessary modules to the QML GUI."""
# print(f"In {__file__} file, publishContext()")
global engine, address_model, bad_config, business_model, user, business_type, contact_model, object_model, employee_model, printers
engine.rootContext().setContextProperty("loggedin_user", user)
@@ -82,6 +165,7 @@ def publishContext():
engine.rootContext().setContextProperty("employee_model", employee_model)
engine.rootContext().setContextProperty("object_model", object_model)
+# Entry point of the program
if __name__ == "__main__":
#QResource.registerResource("rc_qml.py")
app = QGuiApplication(sys.argv)