diff --git a/.gitignore b/.gitignore
index 13d1490a..a2f9a4d3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,7 +9,6 @@ __pycache__/
# Distribution / packaging
.Python
-build/
develop-eggs/
dist/
downloads/
@@ -129,3 +128,6 @@ dmypy.json
# Pyre type checker
.pyre/
+# IDE
+.vscode/
+
diff --git a/.idea/dictionaries/sven.xml b/.idea/dictionaries/sven.xml
new file mode 100644
index 00000000..6b8908ae
--- /dev/null
+++ b/.idea/dictionaries/sven.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 00000000..f9ea47c4
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 00000000..105ce2da
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 00000000..74c32a17
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 00000000..3430c667
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/sh_common_py_lib.iml b/.idea/sh_common_py_lib.iml
new file mode 100644
index 00000000..7f4b6244
--- /dev/null
+++ b/.idea/sh_common_py_lib.iml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/shelf/Uncommitted_changes_before_Update_at_26_11_2020_10_36_[Default_Changelist]/shelved.patch b/.idea/shelf/Uncommitted_changes_before_Update_at_26_11_2020_10_36_[Default_Changelist]/shelved.patch
new file mode 100644
index 00000000..bb3b0e06
--- /dev/null
+++ b/.idea/shelf/Uncommitted_changes_before_Update_at_26_11_2020_10_36_[Default_Changelist]/shelved.patch
@@ -0,0 +1,61 @@
+Index: src/tests/publishing/publisher.py
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>import os\r\nimport shutil\r\nimport unittest\r\n\r\nfrom sh_edraft.configuration import ApplicationHost\r\nfrom sh_edraft.logging import Logger\r\nfrom sh_edraft.logging.base import LoggerBase\r\nfrom sh_edraft.logging.model import LoggingSettings\r\nfrom sh_edraft.publishing import Publisher\r\nfrom sh_edraft.publishing.base import PublisherBase\r\nfrom sh_edraft.publishing.model import Template\r\nfrom sh_edraft.publishing.model.publish_settings_model import PublishSettingsModel\r\nfrom sh_edraft.source_code.model import Version\r\nfrom sh_edraft.time.model import TimeFormatSettings\r\n\r\n\r\nclass PublisherTest(unittest.TestCase):\r\n\r\n def _config(self):\r\n self._log_settings = LoggingSettings()\r\n self._log_settings.from_dict({\r\n \"Path\": \"logs/\",\r\n \"Filename\": \"log_$start_time.log\",\r\n \"ConsoleLogLevel\": \"TRACE\",\r\n \"FileLogLevel\": \"TRACE\"\r\n })\r\n\r\n self._time_format_settings = TimeFormatSettings()\r\n self._time_format_settings.from_dict({\r\n \"DateFormat\": \"%Y-%m-%d\",\r\n \"TimeFormat\": \"%H:%M:%S\",\r\n \"DateTimeFormat\": \"%Y-%m-%d %H:%M:%S.%f\",\r\n \"DateTimeLogFormat\": \"%Y-%m-%d_%H-%M-%S\"\r\n })\r\n\r\n self._version = Version(2020, 12, 5).to_dict()\r\n templates = [\r\n Template(\r\n '../../publish_templates/*_template.txt',\r\n '*',\r\n '',\r\n '',\r\n '2020',\r\n 'sh-edraft.de',\r\n 'MIT',\r\n ', see LICENSE for more details.',\r\n '',\r\n 'Sven Heidemann',\r\n self._version\r\n ),\r\n Template(\r\n '../../publish_templates/*_template.txt',\r\n 'sh_edraft',\r\n 'common python library',\r\n 'Library to share common classes and models used at sh-edraft.de',\r\n '2020',\r\n 'sh-edraft.de',\r\n 'MIT',\r\n ', see LICENSE for more details.',\r\n '',\r\n 'Sven Heidemann',\r\n self._version\r\n )\r\n ]\r\n\r\n self._source_path = '../'\r\n self._dist_path = '../../dist'\r\n\r\n self._publish_settings_model = PublishSettingsModel()\r\n self._publish_settings_model.from_dict({\r\n \"SourcePath\": self._source_path,\r\n \"DistPath\": self._dist_path,\r\n \"Templates\": templates,\r\n \"IncludedFiles\": [],\r\n \"ExcludedFiles\": [],\r\n \"TemplateEnding\": \"_template.txt\",\r\n })\r\n\r\n def setUp(self):\r\n self._config()\r\n\r\n self._app_host = ApplicationHost()\r\n self._logger = Logger(self._log_settings, self._time_format_settings, self._app_host)\r\n self._logger.create()\r\n\r\n def tearDown(self):\r\n if os.path.isdir(self._log_settings.path):\r\n shutil.rmtree(self._log_settings.path)\r\n\r\n def test_create(self):\r\n publisher: Publisher = Publisher(self._logger, self._publish_settings_model)\r\n self.assertIsNotNone(publisher)\r\n\r\n publisher.create()\r\n self.assertTrue(os.path.isdir(self._dist_path))\r\n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+--- src/tests/publishing/publisher.py (revision 9dbad20e9714ffa1546bb2d1b2f8b5ea8e854369)
++++ src/tests/publishing/publisher.py (date 1606383223452)
+@@ -36,8 +36,8 @@
+ self._version = Version(2020, 12, 5).to_dict()
+ templates = [
+ Template(
+- '../../publish_templates/*_template.txt',
+- '*',
++ '../../publish_templates/all_template.txt',
++ 'all',
+ '',
+ '',
+ '2020',
+@@ -49,7 +49,7 @@
+ self._version
+ ),
+ Template(
+- '../../publish_templates/*_template.txt',
++ '../../publish_templates/all_template.txt',
+ 'sh_edraft',
+ 'common python library',
+ 'Library to share common classes and models used at sh-edraft.de',
+Index: src/sh_edraft/publishing/publisher.py
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>import os\r\nimport shutil\r\nfrom string import Template as stringTemplate\r\n\r\nfrom sh_edraft.logging.base.logger_base import LoggerBase\r\nfrom sh_edraft.publishing.base.publisher_base import PublisherBase\r\nfrom sh_edraft.publishing.model.publish_settings_model import PublishSettingsModel\r\nfrom sh_edraft.publishing.model.template import Template\r\n\r\n\r\nclass Publisher(PublisherBase):\r\n\r\n def __init__(self, logger: LoggerBase, publish_settings: PublishSettingsModel):\r\n super().__init__(logger, publish_settings)\r\n\r\n @property\r\n def source_path(self) -> str:\r\n return self._publish_settings.source_path\r\n\r\n @property\r\n def dist_path(self):\r\n return self._publish_settings.dist_path\r\n\r\n def _get_template_output(self, t: Template, name: str, imports: str) -> str:\r\n self._logger.trace(__name__, f'Started {__name__}._get_template_output')\r\n try:\r\n if t.file_content == '':\r\n raise Exception(f'Template is empty: {t.template_path}')\r\n\r\n self._logger.trace(__name__, f'Stopped {__name__}._get_template_output')\r\n return stringTemplate(t.file_content).substitute(\r\n Name=name,\r\n Description=t.description,\r\n LongDescription=t.long_description,\r\n CopyrightDate=t.copyright_date,\r\n CopyrightName=t.copyright_name,\r\n LicenseName=t.license_name,\r\n LicenseDescription=t.license_description,\r\n Title=t.title if t.title is not None and t.title != '' else name,\r\n Author=t.author,\r\n Version=t.version.to_str(),\r\n Major=t.version.major,\r\n Minor=t.version.minor,\r\n Micro=t.version.micro,\r\n Imports=imports\r\n )\r\n except Exception as e:\r\n self._logger.fatal(__name__, f'Cannot read Template: {t.template_path}', e)\r\n self._logger.trace(__name__, f'Stopped {__name__}._get_template_output')\r\n\r\n def _read_source_path(self):\r\n self._logger.trace(__name__, f'Started {__name__}._read_source_path')\r\n for r, d, f in os.walk(self._publish_settings.source_path):\r\n for file in f:\r\n if file.endswith('.py') or file in self._publish_settings.included_files:\r\n self._publish_settings.included_files.append(os.path.join(r, file))\r\n\r\n self._logger.trace(__name__, f'Stopped {__name__}._read_source_path')\r\n\r\n def _read_templates(self):\r\n self._logger.trace(__name__, f'Started {__name__}._read_templates')\r\n for t in self._publish_settings.templates:\r\n output_template: str = ''\r\n if not os.path.isfile(t.template_path):\r\n self._logger.fatal(__name__, f'Template not found: {t.template_path}')\r\n\r\n with open(t.template_path) as template:\r\n t.file_content = template.read()\r\n template.close()\r\n if t.file_content == '':\r\n self._logger.fatal(__name__, f'Template is empty: {t.template_path}')\r\n\r\n self._logger.trace(__name__, f'Stopped {__name__}._read_templates')\r\n\r\n def _create_dist_path(self):\r\n self._logger.trace(__name__, f'Started {__name__}._create_dist_path')\r\n if os.path.isdir(self._publish_settings.dist_path):\r\n try:\r\n shutil.rmtree(self._publish_settings.dist_path)\r\n self._logger.info(__name__, f'Deleted {self._publish_settings.dist_path}')\r\n except Exception as e:\r\n self._logger.fatal(__name__, f'Cannot delete old dist directory', e)\r\n\r\n if not os.path.isdir(self._publish_settings.dist_path):\r\n try:\r\n os.makedirs(self._publish_settings.dist_path)\r\n self._logger.debug(__name__, f'Created directories: {self._publish_settings.dist_path}')\r\n self._logger.info(__name__, f'Created dist directory')\r\n except Exception as e:\r\n self._logger.fatal(__name__, f'Cannot create dist directory', e)\r\n\r\n self._logger.trace(__name__, f'Stopped {__name__}._create_dist_path')\r\n\r\n @staticmethod\r\n def _get_template_name_from_dirs(file: str) -> str:\r\n dirs = os.path.dirname(file).split('/')\r\n for d in dirs:\r\n if d.__contains__('.'):\r\n dirs.remove(d)\r\n\r\n if len(dirs) == 0:\r\n return os.path.basename(file)\r\n else:\r\n return '.'.join(dirs)\r\n\r\n def _write_templates(self):\r\n self._logger.trace(__name__, f'Started {__name__}._write_templates')\r\n for template in self._publish_settings.templates:\r\n for file in self._publish_settings.included_files:\r\n if os.path.basename(file) == '__init__.py' and file not in self._publish_settings.excluded_files:\r\n template_name = template.name\r\n if template.name == '*' or template.name == '':\r\n template_name = self._get_template_name_from_dirs(file)\r\n else:\r\n name = self._get_template_name_from_dirs(file)\r\n\r\n if name.__contains__('.'):\r\n if template.name != name.split('.')[len(name.split('.')) - 1]:\r\n continue\r\n\r\n else:\r\n if template.name != name:\r\n continue\r\n\r\n try:\r\n module_file_lines: list[str] = []\r\n module_py_lines: list[str] = []\r\n imports = ''\r\n with open(file, 'r') as py_file:\r\n module_file_lines = py_file.readlines()\r\n py_file.close()\r\n\r\n if len(module_file_lines) == 0:\r\n with open(file, 'w+') as py_file:\r\n py_file.write(self._get_template_output(template, template_name, '# imports:'))\r\n py_file.close()\r\n self._logger.debug(__name__, f'Written to {file}')\r\n else:\r\n is_started = False\r\n for line in module_file_lines:\r\n if line.__contains__('# imports'):\r\n is_started = True\r\n\r\n if (line.__contains__('from') or line.__contains__('import')) and is_started:\r\n module_py_lines.append(line.replace('\\n', ''))\r\n\r\n if len(module_py_lines) > 0:\r\n imports = '\\n'.join(module_py_lines)\r\n\r\n with open(file, 'w+') as py_file:\r\n py_file.write(self._get_template_output(template, template_name, imports))\r\n py_file.close()\r\n self._logger.debug(__name__, f'Written to {file}')\r\n\r\n except Exception as e:\r\n self._logger.error(__name__, f'Cannot write to file: {file}', e)\r\n\r\n self._logger.info(__name__, f'Written to all included modules')\r\n self._logger.trace(__name__, f'Stopped {__name__}._write_templates')\r\n\r\n def _copy_all_included_files(self):\r\n self._logger.trace(__name__, f'Started {__name__}._copy_all_included_files')\r\n dist_path = self._publish_settings.dist_path\r\n if self._publish_settings.dist_path.endswith('/'):\r\n dist_path = dist_path[:len(dist_path) - 1]\r\n\r\n for file in self._publish_settings.included_files:\r\n is_file_excluded = False\r\n if file in self._publish_settings.excluded_files:\r\n is_file_excluded = True\r\n else:\r\n for excluded in self._publish_settings.excluded_files:\r\n if file.__contains__(excluded):\r\n is_file_excluded = True\r\n\r\n if not is_file_excluded:\r\n output_file = ''\r\n\r\n if file.startswith('..'):\r\n output_file = file.replace('..', '')\r\n elif file.startswith('.'):\r\n output_file = file.replace('.', '', 1)\r\n\r\n output_file = f'{dist_path}{output_file}'\r\n output_path = os.path.dirname(output_file)\r\n\r\n try:\r\n if not os.path.isdir(output_path):\r\n os.makedirs(output_path)\r\n except Exception as e:\r\n self._logger.error(__name__, f'Cannot create directories: {output_path}', e)\r\n\r\n try:\r\n shutil.copy(file, output_file)\r\n except Exception as e:\r\n self._logger.error(__name__, f'Cannot copy file: {file} to {output_path}', e)\r\n\r\n self._logger.debug(__name__, f'Copied {file} to {output_path}')\r\n\r\n self._logger.info(__name__, f'Copied all included files')\r\n self._logger.trace(__name__, f'Stopped {__name__}._copy_all_included_files')\r\n\r\n def include(self, path: str):\r\n self._logger.trace(__name__, f'Started {__name__}.include')\r\n self._publish_settings.included_files.append(path)\r\n self._logger.trace(__name__, f'Stopped {__name__}.include')\r\n\r\n def exclude(self, path: str):\r\n self._logger.trace(__name__, f'Started {__name__}.exclude')\r\n self._publish_settings.excluded_files.append(path)\r\n self._logger.trace(__name__, f'Stopped {__name__}.exclude')\r\n\r\n def create(self):\r\n self._logger.trace(__name__, f'Started {__name__}.create')\r\n if not self._publish_settings.dist_path.endswith('/'):\r\n self._publish_settings.dist_path += '/'\r\n\r\n self._read_source_path()\r\n self._read_templates()\r\n self._create_dist_path()\r\n self._logger.trace(__name__, f'Stopped {__name__}.create')\r\n\r\n def publish(self):\r\n self._logger.trace(__name__, f'Started {__name__}.publish')\r\n self._write_templates()\r\n self._copy_all_included_files()\r\n self._logger.trace(__name__, f'Stopped {__name__}.publish')\r\n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+--- src/sh_edraft/publishing/publisher.py (revision 9dbad20e9714ffa1546bb2d1b2f8b5ea8e854369)
++++ src/sh_edraft/publishing/publisher.py (date 1606383234093)
+@@ -109,7 +109,7 @@
+ for file in self._publish_settings.included_files:
+ if os.path.basename(file) == '__init__.py' and file not in self._publish_settings.excluded_files:
+ template_name = template.name
+- if template.name == '*' or template.name == '':
++ if template.name == 'all' or template.name == '':
+ template_name = self._get_template_name_from_dirs(file)
+ else:
+ name = self._get_template_name_from_dirs(file)
+Index: publish_templates/*_template.txt
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+># -*- coding: utf-8 -*-\r\n\r\n\"\"\"\r\n$Name $Description\r\n~~~~~~~~~~~~~~~~~~~\r\n\r\n$LongDescription\r\n\r\n:copyright: (c) $CopyrightDate $CopyrightName\r\n:license: $LicenseName$LicenseDescription\r\n\r\n\"\"\"\r\n\r\n__title__ = '$Title'\r\n__author__ = '$Author'\r\n__license__ = '$LicenseName'\r\n__copyright__ = 'Copyright (c) $CopyrightDate $CopyrightName'\r\n__version__ = '$Version'\r\n\r\nfrom collections import namedtuple\r\n\r\n$Imports\r\n\r\nVersionInfo = namedtuple('VersionInfo', 'major minor micro')\r\nversion_info = VersionInfo(major=$Major, minor=$Minor, micro=$Micro)\r\n
+===================================================================
+--- publish_templates/*_template.txt (revision 9dbad20e9714ffa1546bb2d1b2f8b5ea8e854369)
++++ publish_templates/all_template.txt (date 1606383191684)
+@@ -22,4 +22,4 @@
+ $Imports
+
+ VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+-version_info = VersionInfo(major=$Major, minor=$Minor, micro=$Micro)
++version_info = VersionInfo(major=$Major, minor=$Minor, micro=$Micro)
+\ No newline at end of file
diff --git a/.idea/shelf/Uncommitted_changes_before_Update_at_26_11_2020_10_36__Default_Changelist_.xml b/.idea/shelf/Uncommitted_changes_before_Update_at_26_11_2020_10_36__Default_Changelist_.xml
new file mode 100644
index 00000000..49edc74a
--- /dev/null
+++ b/.idea/shelf/Uncommitted_changes_before_Update_at_26_11_2020_10_36__Default_Changelist_.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..94a25f7f
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 00000000..8106a0a5
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,868 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1605881914521
+
+
+ 1605881914521
+
+
+ 1607285697331
+
+
+
+ 1607285697331
+
+
+ 1607620265254
+
+
+
+ 1607620265254
+
+
+ 1607712129507
+
+
+
+ 1607712129507
+
+
+ 1607971885250
+
+
+
+ 1607971885250
+
+
+ 1607976456889
+
+
+
+ 1607976456889
+
+
+ 1607976626173
+
+
+
+ 1607976626173
+
+
+ 1608037411172
+
+
+
+ 1608037411172
+
+
+ 1608047595029
+
+
+
+ 1608047595029
+
+
+ 1608047655667
+
+
+
+ 1608047655667
+
+
+ 1608048544558
+
+
+
+ 1608048544558
+
+
+ 1608049672925
+
+
+
+ 1608049672925
+
+
+ 1608049772153
+
+
+
+ 1608049772153
+
+
+ 1608059323858
+
+
+
+ 1608059323858
+
+
+ 1608059501153
+
+
+
+ 1608059501153
+
+
+ 1608066984496
+
+
+
+ 1608066984496
+
+
+ 1608070231599
+
+
+
+ 1608070231599
+
+
+ 1608122289003
+
+
+
+ 1608122289003
+
+
+ 1608125743068
+
+
+
+ 1608125743068
+
+
+ 1608127752645
+
+
+
+ 1608127752645
+
+
+ 1608127998033
+
+
+
+ 1608127998034
+
+
+ 1608128026922
+
+
+
+ 1608128026922
+
+
+ 1608133651981
+
+
+
+ 1608133651982
+
+
+ 1608136663213
+
+
+
+ 1608136663213
+
+
+ 1608137669182
+
+
+
+ 1608137669182
+
+
+ 1608138851088
+
+
+
+ 1608138851088
+
+
+ 1608138986801
+
+
+
+ 1608138986801
+
+
+ 1608139067953
+
+
+
+ 1608139067953
+
+
+ 1608139239527
+
+
+
+ 1608139239527
+
+
+ 1608139414722
+
+
+
+ 1608139414722
+
+
+ 1608139502304
+
+
+
+ 1608139502304
+
+
+ 1608139612476
+
+
+
+ 1608139612476
+
+
+ 1608383809354
+
+
+
+ 1608383809355
+
+
+ 1608383870849
+
+
+
+ 1608383870849
+
+
+ 1608472168532
+
+
+
+ 1608472168532
+
+
+ 1608472183294
+
+
+
+ 1608472183294
+
+
+ 1608474022759
+
+
+
+ 1608474022759
+
+
+ 1608474032244
+
+
+
+ 1608474032244
+
+
+ 1608474289859
+
+
+
+ 1608474289859
+
+
+ 1608813096023
+
+
+
+ 1608813096023
+
+
+ 1608813152266
+
+
+
+ 1608813152266
+
+
+ 1608822597300
+
+
+
+ 1608822597311
+
+
+ 1608823061488
+
+
+
+ 1608823061489
+
+
+ 1608894745271
+
+
+
+ 1608894745274
+
+
+ 1608896199033
+
+
+
+ 1608896199034
+
+
+ 1608896235005
+
+
+
+ 1608896235006
+
+
+ 1608901608928
+
+
+
+ 1608901608930
+
+
+ 1608921175852
+
+
+
+ 1608921175853
+
+
+ 1608986131753
+
+
+
+ 1608986131755
+
+
+ 1611431001647
+
+
+
+ 1611431001649
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ file://$PROJECT_DIR$/src/tests/publish_test/publisher_test.py
+ 50
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/cli.md b/docs/cli.md
new file mode 100644
index 00000000..f2f2291e
--- /dev/null
+++ b/docs/cli.md
@@ -0,0 +1,9 @@
+prefix: cpl
+commands:
+ new:
+ app
+ base
+ class
+ configmodel
+ enum
+ service
\ No newline at end of file
diff --git a/docs/install.md b/docs/install.md
new file mode 100644
index 00000000..54c105b4
--- /dev/null
+++ b/docs/install.md
@@ -0,0 +1,2 @@
+python setup.py install # for install
+python setup.py sdist bdist_wheel # for build
\ No newline at end of file
diff --git a/docs/structure.md b/docs/structure.md
new file mode 100644
index 00000000..71f2f0ea
--- /dev/null
+++ b/docs/structure.md
@@ -0,0 +1,10 @@
+- sh_edraft
+ - common # Contains Interfaces and models for the hole library
+ - interface
+ - model
+ - configuration # Contains classes for app configuration by JSON, ENV vars and arguments
+ - discord # Contains classes for better use of discord.py
+ - logging # Contains classes for logging
+ - mailing # Contains classes for mailing
+ - messenger # Contains classes for sh_messenger_server client
+ - service # Contains classes to provide and use the services defined in this library
\ No newline at end of file
diff --git a/docs/todo.md b/docs/todo.md
new file mode 100644
index 00000000..5d5a99b8
--- /dev/null
+++ b/docs/todo.md
@@ -0,0 +1,2 @@
+- create logger
+- use logger in publisher
\ No newline at end of file
diff --git a/publish_templates/all_template.txt b/publish_templates/all_template.txt
new file mode 100644
index 00000000..3b24f44f
--- /dev/null
+++ b/publish_templates/all_template.txt
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+
+"""
+$Name $Description
+~~~~~~~~~~~~~~~~~~~
+
+$LongDescription
+
+:copyright: (c) $CopyrightDate $CopyrightName
+:license: $LicenseName$LicenseDescription
+
+"""
+
+__title__ = '$Title'
+__author__ = '$Author'
+__license__ = '$LicenseName'
+__copyright__ = 'Copyright (c) $CopyrightDate $CopyrightName'
+__version__ = '$Version'
+
+from collections import namedtuple
+
+$Imports
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=$Major, minor=$Minor, micro=$Micro)
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 00000000..afb24683
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,29 @@
+aiohttp==3.6.3
+async-timeout==3.0.1
+attrs==20.3.0
+certifi==2020.11.8
+chardet==3.0.4
+click==7.1.2
+dateutils==0.6.12
+discord==1.0.1
+discord.py==1.5.1
+Flask==1.1.2
+idna==2.10
+itsdangerous==1.1.0
+Jinja2==2.11.2
+keyboard==0.13.5
+MarkupSafe==1.1.1
+multidict==4.7.6
+mysql-connector==2.2.9
+overloading==0.5.0
+python-dateutil==2.8.1
+pytz==2020.4
+six==1.15.0
+SQLAlchemy==1.3.20
+termcolor==1.1.0
+urllib3==1.26.2
+Werkzeug==1.0.1
+yarl==1.5.1
+setuptools~=49.2.1
+pyfiglet~=0.8.post1
+tabulate~=0.8.7
\ No newline at end of file
diff --git a/src/MANIFEST.in b/src/MANIFEST.in
new file mode 100644
index 00000000..cb32f474
--- /dev/null
+++ b/src/MANIFEST.in
@@ -0,0 +1,2 @@
+include ../ README
+recursive-include sh_edraft *.txt
\ No newline at end of file
diff --git a/src/build.json b/src/build.json
new file mode 100644
index 00000000..68883b77
--- /dev/null
+++ b/src/build.json
@@ -0,0 +1,66 @@
+{
+ "TimeFormatSettings": {
+ "DateFormat": "%Y-%m-%d",
+ "TimeFormat": "%H:%M:%S",
+ "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f",
+ "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S"
+ },
+ "LoggingSettings": {
+ "Path": "../build/logs/",
+ "Filename": "log_$start_time.log",
+ "ConsoleLogLevel": "INFO",
+ "FileLogLevel": "TRACE"
+ },
+ "PublishSettings": {
+ "SourcePath": "./",
+ "DistPath": "../build/dist",
+ "Templates": [
+ {
+ "TemplatePath": "../publish_templates/all_template.txt",
+ "Name": "all",
+ "Description": "",
+ "LongDescription": "",
+ "CopyrightDate": "2020",
+ "CopyrightName": "sh-edraft.de",
+ "LicenseName": "MIT",
+ "LicenseDescription": ", see LICENSE for more details.",
+ "Title": "",
+ "Author": "Sven Heidemann",
+ "Version": {
+ "Major": 2020,
+ "Minor": 12,
+ "Micro": 10
+ }
+ },
+ {
+ "TemplatePath": "../publish_templates/all_template.txt",
+ "Name": "sh_edraft",
+ "Description": "common python library",
+ "LongDescription": "Library to share common classes and models used at sh-edraft.de",
+ "CopyrightDate": "2020",
+ "CopyrightName": "sh-edraft.de",
+ "LicenseName": "MIT",
+ "LicenseDescription": ", see LICENSE for more details.",
+ "Title": "",
+ "Author": "Sven Heidemann",
+ "Version": {
+ "Major": 2020,
+ "Minor": 12,
+ "Micro": 10
+ }
+ }
+ ],
+ "IncludedFiles": [
+ "./MANIFEST.in",
+ "../LICENSE",
+ "../README.md",
+ "../requirements.txt",
+ "sh_edraft/cli/cpl_cli/templates"
+ ],
+ "ExcludedFiles": [
+ "./tests",
+ "./tests_dev"
+ ],
+ "TemplateEnding": "_template.txt"
+ }
+}
diff --git a/src/setup.py b/src/setup.py
new file mode 100644
index 00000000..a2e40b12
--- /dev/null
+++ b/src/setup.py
@@ -0,0 +1,29 @@
+import setuptools
+
+setuptools.setup(
+ name='sh_edraft',
+ version='2020.0.1',
+ packages=setuptools.find_packages(exclude=["tests*"]),
+ url='https://www.sh-edraft.de',
+ license='MIT',
+ author='Sven Heidemann',
+ author_email='edraft.sh@gmail.com',
+ include_package_data=True,
+ description='sh-edraft python common lib',
+ python_requires='>=3.8',
+ install_requires=[
+ 'discord.py',
+ 'flask',
+ 'mysql-connector',
+ 'SQLAlchemy',
+ 'termcolor',
+ 'pyfiglet',
+ 'tabulate',
+ 'smtplib'
+ ],
+ entry_points={
+ 'console_scripts': [
+ 'cpl = sh_edraft.cli.cpl_cli.cli:main'
+ ]
+ }
+)
diff --git a/src/sh_edraft/__init__.py b/src/sh_edraft/__init__.py
new file mode 100644
index 00000000..a74b7597
--- /dev/null
+++ b/src/sh_edraft/__init__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft common python library
+~~~~~~~~~~~~~~~~~~~
+
+Library to share common classes and models used at sh-edraft.de
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/cli/__init__.py b/src/sh_edraft/cli/__init__.py
new file mode 100644
index 00000000..d604c174
--- /dev/null
+++ b/src/sh_edraft/cli/__init__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.cli
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.cli'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.10'
+
+from collections import namedtuple
+
+# imports:
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=10)
diff --git a/src/sh_edraft/cli/command/__init__.py b/src/sh_edraft/cli/command/__init__.py
new file mode 100644
index 00000000..5d5abc0b
--- /dev/null
+++ b/src/sh_edraft/cli/command/__init__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.cli.command
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.cli.command'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.10'
+
+from collections import namedtuple
+
+# imports:
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=10)
diff --git a/src/sh_edraft/cli/command/base/__init__.py b/src/sh_edraft/cli/command/base/__init__.py
new file mode 100644
index 00000000..bf79046d
--- /dev/null
+++ b/src/sh_edraft/cli/command/base/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.cli.command.base
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.cli.command.base'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.10'
+
+from collections import namedtuple
+
+# imports:
+from .command_base import CommandBase
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=10)
diff --git a/src/sh_edraft/cli/command/base/command_base.py b/src/sh_edraft/cli/command/base/command_base.py
new file mode 100644
index 00000000..76319ba7
--- /dev/null
+++ b/src/sh_edraft/cli/command/base/command_base.py
@@ -0,0 +1,15 @@
+from abc import ABC, abstractmethod
+
+
+class CommandBase(ABC):
+
+ @abstractmethod
+ def __init__(self):
+ self._aliases: list[str] = []
+
+ @property
+ def aliases(self):
+ return self._aliases
+
+ @abstractmethod
+ def run(self, args: list[str]): pass
diff --git a/src/sh_edraft/cli/cpl_cli/__init__.py b/src/sh_edraft/cli/cpl_cli/__init__.py
new file mode 100644
index 00000000..d2709a11
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.cli.cpl_cli
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.cli.cpl_cli'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.10'
+
+from collections import namedtuple
+
+# imports:
+from .cli import CLI
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=10)
diff --git a/src/sh_edraft/cli/cpl_cli/cli.py b/src/sh_edraft/cli/cpl_cli/cli.py
new file mode 100644
index 00000000..f0728d13
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/cli.py
@@ -0,0 +1,42 @@
+import sys
+import traceback
+
+from sh_edraft.cli.cpl_cli.commands.build.build import Build
+from sh_edraft.cli.cpl_cli.commands.help import Help
+from sh_edraft.cli.cpl_cli.commands.new import New
+from sh_edraft.cli.cpl_cli.commands.publish.publish import Publish
+from sh_edraft.cli.cpl_cli.commands.version import Version
+from sh_edraft.cli.interpreter.interpreter import Interpreter
+from sh_edraft.console.console import Console
+
+
+class CLI:
+
+ def __init__(self):
+ self._interpreter = Interpreter()
+
+ def setup(self):
+ self._interpreter.add_command(Build())
+ self._interpreter.add_command(Help())
+ self._interpreter.add_command(New())
+ self._interpreter.add_command(Publish())
+ self._interpreter.add_command(Version())
+
+ def main(self):
+ string = ' '.join(sys.argv[1:])
+ try:
+ self._interpreter.interpret(string)
+ except Exception as e:
+ tb = traceback.format_exc()
+ Console.error(str(e), tb)
+ Console.error('Run \'cpl help\'')
+
+
+def main():
+ cli = CLI()
+ cli.setup()
+ cli.main()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/src/sh_edraft/cli/cpl_cli/commands/__init__.py b/src/sh_edraft/cli/cpl_cli/commands/__init__.py
new file mode 100644
index 00000000..a1d5ec40
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/commands/__init__.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.cli.cpl_cli.commands
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.cli.cpl_cli.commands'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.10'
+
+from collections import namedtuple
+
+# imports:
+from .version import Version
+from .help import Help
+from .new import New
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=10)
diff --git a/src/sh_edraft/cli/cpl_cli/commands/build/__init__.py b/src/sh_edraft/cli/cpl_cli/commands/build/__init__.py
new file mode 100644
index 00000000..6a057cac
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/commands/build/__init__.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.cli.cpl_cli.commands.build
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.cli.cpl_cli.commands.build'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.10'
+
+from collections import namedtuple
+
+# imports:
+from .app import BuildApp
+from .build import Build
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=10)
diff --git a/src/sh_edraft/cli/cpl_cli/commands/build/app.py b/src/sh_edraft/cli/cpl_cli/commands/build/app.py
new file mode 100644
index 00000000..4c0b9833
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/commands/build/app.py
@@ -0,0 +1,47 @@
+from typing import Optional
+
+from sh_edraft.configuration.base.configuration_base import ConfigurationBase
+from sh_edraft.hosting.application_host import ApplicationHost
+from sh_edraft.hosting.base.application_base import ApplicationBase
+from sh_edraft.logging.logger import Logger
+from sh_edraft.logging.base.logger_base import LoggerBase
+from sh_edraft.publish.publisher import Publisher
+from sh_edraft.publish.base.publisher_base import PublisherBase
+from sh_edraft.service.providing.base.service_provider_base import ServiceProviderBase
+
+
+class BuildApp(ApplicationBase):
+
+ def __init__(self):
+ ApplicationBase.__init__(self)
+
+ self._app_host: Optional[ApplicationHost] = None
+ self._services: Optional[ServiceProviderBase] = None
+ self._configuration: Optional[ConfigurationBase] = None
+ self._logger: Optional[LoggerBase] = None
+ self._publisher: Optional[PublisherBase] = None
+
+ def create_application_host(self):
+ self._app_host = ApplicationHost()
+ self._configuration = self._app_host.configuration
+ self._services = self._app_host.services
+
+ def create_configuration(self):
+ self._configuration.add_json_file(f'build.json')
+
+ def create_services(self):
+ # Add and create logger
+ self._services.add_singleton(LoggerBase, Logger)
+ self._logger = self._services.get_service(LoggerBase)
+
+ # Add and create publisher
+ self._services.add_singleton(PublisherBase, Publisher)
+ self._publisher: Publisher = self._services.get_service(PublisherBase)
+
+ def main(self):
+ self._logger.header(f'{self._configuration.environment.application_name}:')
+ self._logger.debug(__name__, f'Host: {self._configuration.environment.host_name}')
+ self._logger.debug(__name__, f'Environment: {self._configuration.environment.environment_name}')
+ self._logger.debug(__name__, f'Customer: {self._configuration.environment.customer}')
+ self._publisher.create()
+ self._publisher.build()
diff --git a/src/sh_edraft/cli/cpl_cli/commands/build/build.py b/src/sh_edraft/cli/cpl_cli/commands/build/build.py
new file mode 100644
index 00000000..f22eb436
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/commands/build/build.py
@@ -0,0 +1,23 @@
+from sh_edraft.cli.command.base.command_base import CommandBase
+from sh_edraft.cli.cpl_cli.commands.build.app import BuildApp
+from sh_edraft.console.console import Console
+
+
+class Build(CommandBase):
+
+ def __init__(self):
+ CommandBase.__init__(self)
+ self._app = BuildApp()
+
+ self._aliases.append('-b')
+ self._aliases.append('-B')
+
+ def run(self, args: list[str]):
+ if len(args) > 0:
+ Console.error(f'Invalid arguments {args}')
+ Console.error('Run \'cpl help\'')
+
+ self._app.create_application_host()
+ self._app.create_configuration()
+ self._app.create_services()
+ self._app.main()
diff --git a/src/sh_edraft/cli/cpl_cli/commands/help.py b/src/sh_edraft/cli/cpl_cli/commands/help.py
new file mode 100644
index 00000000..6ea89066
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/commands/help.py
@@ -0,0 +1,27 @@
+from sh_edraft.cli.command.base.command_base import CommandBase
+from sh_edraft.console.console import Console
+
+
+class Help(CommandBase):
+
+ def __init__(self):
+ CommandBase.__init__(self)
+ self._aliases.append('-h')
+ self._aliases.append('-H')
+
+ def run(self, args: list[str]):
+ Console.write_line('Available Commands:')
+ commands = [
+ ['build (-b|-B)', 'Prepares files for publishing into an output directory named dist/ at the given output path. Must be executed from within a workspace directory.'],
+ ['help (-h|-H)', 'Lists available commands and their short descriptions.'],
+ ['new', 'Creates a new file or package.'],
+ ['publish (-p|-P)', 'Prepares files for publishing into an output directory named dist/ at the given output path and executes setup.py. Must be executed from within a workspace directory.'],
+ ['version (-v|-V)', 'Outputs CPL CLI version.']
+ ]
+ for name, description in commands:
+ Console.set_foreground_color('blue')
+ Console.write(f'\n\t{name} ')
+ Console.set_foreground_color('default')
+ Console.write(f'{description}')
+
+ Console.write('\n')
diff --git a/src/sh_edraft/cli/cpl_cli/commands/new.py b/src/sh_edraft/cli/cpl_cli/commands/new.py
new file mode 100644
index 00000000..90ebb690
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/commands/new.py
@@ -0,0 +1,97 @@
+import os
+
+from sh_edraft.cli.command.base.command_base import CommandBase
+from sh_edraft.console.console import Console
+
+
+class New(CommandBase):
+
+ def __init__(self):
+ CommandBase.__init__(self)
+
+ def run(self, args: list[str]):
+ rel_path = f'{os.path.dirname(__file__)}/../'
+ if len(args) == 0:
+ Console.error(f'Expected arguments {args}')
+ Console.error('Run \'cpl help\'')
+ return
+
+ elif len(args) != 2:
+ Console.error(f'Invalid arguments {args}')
+ Console.error('Run \'cpl help\'')
+ return
+
+ if not os.path.isdir(f'{rel_path}/templates/{args[0]}'):
+ Console.error(f'Unexpected argument {args[0]}')
+ Console.error('Run \'cpl help\'')
+
+ sub_args = args[1:]
+
+ if len(sub_args) != 1:
+ Console.error(f'Unexpected argument {sub_args[1]}')
+ Console.error('Run \'cpl help\'')
+
+ if not (sub_args[0].startswith('.') or sub_args[0].startswith('/')):
+ full_path = f'./{sub_args[0]}'
+ else:
+ full_path = sub_args[0]
+
+ name = os.path.basename(full_path)
+ path = os.path.dirname(full_path)
+
+ if args[0] in ['base', 'class', 'configmodel', 'enum', 'service']:
+ if not os.path.isdir(path):
+ os.makedirs(path)
+ else:
+ if not os.path.isdir(full_path):
+ os.makedirs(full_path)
+
+ for r, d, f in os.walk(f'{rel_path}/templates/{args[0]}'):
+ for file in f:
+ template_content = ''
+ with open(f'{r}/{file}') as template:
+ template_content = template.read()
+ template.close()
+
+ file = file.replace('txt', 'py')
+ if args[0] in ['base', 'class', 'configmodel', 'enum', 'service']:
+ suffix = None
+
+ if args[0] == 'base':
+ suffix = 'base'
+
+ elif args[0] == 'configmodel':
+ suffix = 'settings'
+
+ elif args[0] == 'service':
+ suffix = 'service'
+
+ if suffix is not None:
+ file_path = f'{path}/{name}_{suffix}.py'
+ else:
+ file_path = f'{path}/{name}.py'
+ else:
+ file_path = f'{full_path}/{file}'
+
+ with open(file_path, 'w+') as pyfile:
+ if name[0].islower():
+ name = f'{name[0].upper()}{name[1:]}'
+
+ if args[0] == 'base':
+ template_content = template_content.replace('$Name', f'{name}Base')
+ pyfile.write(template_content)
+
+ elif args[0] == 'configmodel':
+ template_content = template_content.replace('$Name', f'{name}Settings')
+ pyfile.write(template_content)
+
+ elif args[0] == 'service':
+ template_content = template_content.replace('$Name', f'{name}Service')
+ template_content = template_content.replace('$Base', f'{name}Base')
+ pyfile.write(template_content)
+
+ else:
+ template_content = template_content.replace('$Name', name)
+ pyfile.write(template_content)
+
+ pyfile.close()
diff --git a/src/sh_edraft/cli/cpl_cli/commands/publish/__init__.py b/src/sh_edraft/cli/cpl_cli/commands/publish/__init__.py
new file mode 100644
index 00000000..d367c84a
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/commands/publish/__init__.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.cli.cpl_cli.commands.publish
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.cli.cpl_cli.commands.publish'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.10'
+
+from collections import namedtuple
+
+# imports:
+from .app import PublishApp
+from .publish import Publish
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=10)
diff --git a/src/sh_edraft/cli/cpl_cli/commands/publish/app.py b/src/sh_edraft/cli/cpl_cli/commands/publish/app.py
new file mode 100644
index 00000000..2a0f6cec
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/commands/publish/app.py
@@ -0,0 +1,48 @@
+from typing import Optional
+
+from sh_edraft.configuration.base.configuration_base import ConfigurationBase
+from sh_edraft.hosting.application_host import ApplicationHost
+from sh_edraft.hosting.base.application_base import ApplicationBase
+from sh_edraft.logging.logger import Logger
+from sh_edraft.logging.base.logger_base import LoggerBase
+from sh_edraft.publish.publisher import Publisher
+from sh_edraft.publish.base.publisher_base import PublisherBase
+from sh_edraft.service.providing.base.service_provider_base import ServiceProviderBase
+
+
+class PublishApp(ApplicationBase):
+
+ def __init__(self):
+ ApplicationBase.__init__(self)
+
+ self._app_host: Optional[ApplicationHost] = None
+ self._services: Optional[ServiceProviderBase] = None
+ self._configuration: Optional[ConfigurationBase] = None
+ self._logger: Optional[LoggerBase] = None
+ self._publisher: Optional[PublisherBase] = None
+
+ def create_application_host(self):
+ self._app_host = ApplicationHost()
+ self._configuration = self._app_host.configuration
+ self._services = self._app_host.services
+
+ def create_configuration(self):
+ self._configuration.add_json_file(f'build.json')
+
+ def create_services(self):
+ # Add and create logger
+ self._services.add_singleton(LoggerBase, Logger)
+ self._logger = self._services.get_service(LoggerBase)
+
+ # Add and create publisher
+ self._services.add_singleton(PublisherBase, Publisher)
+ self._publisher: Publisher = self._services.get_service(PublisherBase)
+
+ def main(self):
+ self._logger.header(f'{self._configuration.environment.application_name}:')
+ self._logger.debug(__name__, f'Host: {self._configuration.environment.host_name}')
+ self._logger.debug(__name__, f'Environment: {self._configuration.environment.environment_name}')
+ self._logger.debug(__name__, f'Customer: {self._configuration.environment.customer}')
+ self._publisher.create()
+ self._publisher.build()
+ self._publisher.publish()
diff --git a/src/sh_edraft/cli/cpl_cli/commands/publish/publish.py b/src/sh_edraft/cli/cpl_cli/commands/publish/publish.py
new file mode 100644
index 00000000..7e72ae3a
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/commands/publish/publish.py
@@ -0,0 +1,23 @@
+from sh_edraft.cli.command.base.command_base import CommandBase
+from sh_edraft.cli.cpl_cli.commands.publish.app import PublishApp
+from sh_edraft.console.console import Console
+
+
+class Publish(CommandBase):
+
+ def __init__(self):
+ CommandBase.__init__(self)
+ self._app = PublishApp()
+
+ self._aliases.append('-p')
+ self._aliases.append('-P')
+
+ def run(self, args: list[str]):
+ if len(args) > 0:
+ Console.error(f'Invalid arguments {args}')
+ Console.error('Run \'cpl help\'')
+
+ self._app.create_application_host()
+ self._app.create_configuration()
+ self._app.create_services()
+ self._app.main()
diff --git a/src/sh_edraft/cli/cpl_cli/commands/version.py b/src/sh_edraft/cli/cpl_cli/commands/version.py
new file mode 100644
index 00000000..737a69c7
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/commands/version.py
@@ -0,0 +1,42 @@
+import pkgutil
+import sys
+import platform
+
+import pkg_resources
+
+import sh_edraft
+from sh_edraft import cli
+from sh_edraft.cli.command.base.command_base import CommandBase
+from sh_edraft.console.console import Console
+
+
+class Version(CommandBase):
+
+ def __init__(self):
+ CommandBase.__init__(self)
+ self._aliases.append('-v')
+ self._aliases.append('-V')
+
+ def run(self, args: list[str]):
+ Console.set_foreground_color('yellow')
+ Console.banner('CPL CLI')
+ Console.set_foreground_color('default')
+ Console.write_line(f'Common Python Library CLI: {cli.__version__}')
+ Console.write_line(f'Python: {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}')
+ Console.write_line(f'OS: {platform.system()} {platform.processor()}')
+
+ Console.write_line('CPL:')
+ packages = []
+ for importer, modname, is_pkg in pkgutil.iter_modules(sh_edraft.__path__):
+ module = importer.find_module(modname).load_module(modname)
+ packages.append([f'{modname}', module.__version__])
+
+ Console.table(['Name', 'Version'], packages)
+
+ Console.write_line('\nPython Packages:')
+ packages = []
+ deps = dict(tuple(str(ws).split()) for ws in pkg_resources.working_set)
+ for p in deps:
+ packages.append([p, deps[p]])
+
+ Console.table(['Name', 'Version'], packages)
diff --git a/src/sh_edraft/cli/cpl_cli/templates/app/__init__.txt b/src/sh_edraft/cli/cpl_cli/templates/app/__init__.txt
new file mode 100644
index 00000000..2e12875a
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/templates/app/__init__.txt
@@ -0,0 +1 @@
+# imports:
\ No newline at end of file
diff --git a/src/sh_edraft/cli/cpl_cli/templates/app/build.json b/src/sh_edraft/cli/cpl_cli/templates/app/build.json
new file mode 100644
index 00000000..c6ea10bc
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/templates/app/build.json
@@ -0,0 +1,22 @@
+{
+ "TimeFormatSettings": {
+ "DateFormat": "%Y-%m-%d",
+ "TimeFormat": "%H:%M:%S",
+ "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f",
+ "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S"
+ },
+ "LoggingSettings": {
+ "Path": "build/logs/",
+ "Filename": "log_$start_time.log",
+ "ConsoleLogLevel": "INFO",
+ "FileLogLevel": "INFO"
+ },
+ "PublishSettings": {
+ "SourcePath": "./",
+ "DistPath": "build/dist",
+ "Templates": [],
+ "IncludedFiles": [],
+ "ExcludedFiles": [],
+ "TemplateEnding": "_template.txt"
+ }
+}
diff --git a/src/sh_edraft/cli/cpl_cli/templates/app/main.txt b/src/sh_edraft/cli/cpl_cli/templates/app/main.txt
new file mode 100644
index 00000000..081d79dd
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/templates/app/main.txt
@@ -0,0 +1,8 @@
+from program import Program
+
+if __name__ == '__main__':
+ program = Program()
+ program.create_application_host()
+ program.create_configuration()
+ program.create_services()
+ program.main()
diff --git a/src/sh_edraft/cli/cpl_cli/templates/app/program.txt b/src/sh_edraft/cli/cpl_cli/templates/app/program.txt
new file mode 100644
index 00000000..890c6b73
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/templates/app/program.txt
@@ -0,0 +1,43 @@
+from typing import Optional
+
+from sh_edraft.configuration.base import ConfigurationBase
+from sh_edraft.hosting import ApplicationHost
+from sh_edraft.hosting.base import ApplicationBase
+from sh_edraft.logging import Logger
+from sh_edraft.logging.base import LoggerBase
+from sh_edraft.service.providing.base import ServiceProviderBase
+
+
+class Program(ApplicationBase):
+
+ def __init__(self):
+ ApplicationBase.__init__(self)
+
+ self._app_host: Optional[ApplicationHost] = None
+ self._services: Optional[ServiceProviderBase] = None
+ self._configuration: Optional[ConfigurationBase] = None
+ self._logger: Optional[LoggerBase] = None
+
+ def create_application_host(self):
+ self._app_host = ApplicationHost()
+ self._configuration = self._app_host.configuration
+ self._services = self._app_host.services
+
+ def create_configuration(self):
+ self._configuration.add_environment_variables('PYTHON_')
+ self._configuration.add_environment_variables('CPL_')
+ self._configuration.add_argument_variables()
+ self._configuration.add_json_file(f'appsettings.json')
+ self._configuration.add_json_file(f'appsettings.{self._configuration.environment.environment_name}.json', optional=True)
+ self._configuration.add_json_file(f'appsettings.{self._configuration.environment.host_name}.json', optional=True)
+
+ def create_services(self):
+ # Add and create logger
+ self._services.add_singleton(LoggerBase, Logger)
+ self._logger = self._services.get_service(LoggerBase)
+
+ def main(self):
+ self._logger.header(f'{self._configuration.environment.application_name}:')
+ self._logger.debug(__name__, f'Host: {self._configuration.environment.host_name}')
+ self._logger.debug(__name__, f'Environment: {self._configuration.environment.environment_name}')
+ self._logger.debug(__name__, f'Customer: {self._configuration.environment.customer}')
diff --git a/src/sh_edraft/cli/cpl_cli/templates/base/base.txt b/src/sh_edraft/cli/cpl_cli/templates/base/base.txt
new file mode 100644
index 00000000..1940e8ab
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/templates/base/base.txt
@@ -0,0 +1,10 @@
+from abc import ABC, abstractmethod
+
+
+class $Name(ABC):
+
+ @abstractmethod
+ def __init__(self): pass
+
+ @abstractmethod
+ def create(self): pass
diff --git a/src/sh_edraft/cli/cpl_cli/templates/class/class.txt b/src/sh_edraft/cli/cpl_cli/templates/class/class.txt
new file mode 100644
index 00000000..d22b7581
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/templates/class/class.txt
@@ -0,0 +1,4 @@
+class $Name:
+
+ def __init__(self):
+ pass
diff --git a/src/sh_edraft/cli/cpl_cli/templates/configmodel/model.txt b/src/sh_edraft/cli/cpl_cli/templates/configmodel/model.txt
new file mode 100644
index 00000000..ca37a816
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/templates/configmodel/model.txt
@@ -0,0 +1,20 @@
+import traceback
+
+from sh_edraft.configuration.base import ConfigurationModelBase
+from sh_edraft.console import Console
+from sh_edraft.console.model import ForegroundColor
+
+
+class $Name(ConfigurationModelBase):
+
+ def __init__(self):
+ ConfigurationModelBase.__init__(self)
+
+ def from_dict(self, settings: dict):
+ try:
+ pass
+ except Exception as e:
+ Console.set_foreground_color(ForegroundColor.red)
+ Console.write_line(f'[ ERROR ] [ {__name__} ]: Reading error in {self.__name__} settings')
+ Console.write_line(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}')
+ Console.set_foreground_color(ForegroundColor.default)
diff --git a/src/sh_edraft/cli/cpl_cli/templates/enum/enum.txt b/src/sh_edraft/cli/cpl_cli/templates/enum/enum.txt
new file mode 100644
index 00000000..a7aef900
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/templates/enum/enum.txt
@@ -0,0 +1,6 @@
+from enum import Enum
+
+
+class $Name(Enum):
+
+ pass
diff --git a/src/sh_edraft/cli/cpl_cli/templates/service/service.txt b/src/sh_edraft/cli/cpl_cli/templates/service/service.txt
new file mode 100644
index 00000000..d2add199
--- /dev/null
+++ b/src/sh_edraft/cli/cpl_cli/templates/service/service.txt
@@ -0,0 +1,6 @@
+class $Name($Base):
+
+ def __init__(self):
+ TestBase.__init__(self)
+
+ self.create()
diff --git a/src/sh_edraft/cli/interpreter/__init__.py b/src/sh_edraft/cli/interpreter/__init__.py
new file mode 100644
index 00000000..7f342630
--- /dev/null
+++ b/src/sh_edraft/cli/interpreter/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.cli.interpreter
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.cli.interpreter'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.10'
+
+from collections import namedtuple
+
+# imports:
+from .interpreter import Interpreter
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=10)
diff --git a/src/sh_edraft/cli/interpreter/interpreter.py b/src/sh_edraft/cli/interpreter/interpreter.py
new file mode 100644
index 00000000..d3e580e4
--- /dev/null
+++ b/src/sh_edraft/cli/interpreter/interpreter.py
@@ -0,0 +1,33 @@
+from sh_edraft.cli.command.base.command_base import CommandBase
+from sh_edraft.console.console import Console
+
+
+class Interpreter:
+
+ def __init__(self):
+ self._commands: list[CommandBase] = []
+
+ def add_command(self, command: CommandBase):
+ self._commands.append(command)
+
+ def remove_command(self, command: CommandBase):
+ self._commands.remove(command)
+
+ def interpret(self, input_string: str):
+ input_list = input_string.split(' ')
+ command = input_list[0]
+ if command is None or command == '':
+ Console.error(f'Expected command')
+ Console.error('Run \'cpl help\'')
+ return
+
+ args = input_list[1:] if len(input_list) > 1 else []
+
+ cmd = next(
+ (cmd for cmd in self._commands if type(cmd).__name__.lower() == command or command in cmd.aliases),
+ None)
+ if cmd is not None:
+ cmd.run(args)
+ else:
+ Console.error(f'Unexpected command {command}')
+ Console.error('Run \'cpl help\'')
diff --git a/src/sh_edraft/coding/__init__.py b/src/sh_edraft/coding/__init__.py
new file mode 100644
index 00000000..74fd8f6b
--- /dev/null
+++ b/src/sh_edraft/coding/__init__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.coding
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.coding'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/coding/model/__init__.py b/src/sh_edraft/coding/model/__init__.py
new file mode 100644
index 00000000..ccdd0f5a
--- /dev/null
+++ b/src/sh_edraft/coding/model/__init__.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.coding.model
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.coding.model'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .version import Version
+from .version_enum import VersionEnum
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/coding/model/version.py b/src/sh_edraft/coding/model/version.py
new file mode 100644
index 00000000..be2d5a3d
--- /dev/null
+++ b/src/sh_edraft/coding/model/version.py
@@ -0,0 +1,46 @@
+from typing import Optional
+
+from sh_edraft.coding.model.version_enum import VersionEnum
+from sh_edraft.configuration.base.configuration_model_base import ConfigurationModelBase
+
+
+class Version(ConfigurationModelBase):
+
+ def __init__(
+ self,
+ major: int = None,
+ minor: int = None,
+ micro: float = None
+ ):
+ ConfigurationModelBase.__init__(self)
+
+ self._major: Optional[int] = major
+ self._minor: Optional[int] = minor
+ self._micro: Optional[int] = micro
+
+ @property
+ def major(self) -> int:
+ return self._major
+
+ @property
+ def minor(self) -> int:
+ return self._minor
+
+ @property
+ def micro(self) -> float:
+ return self._micro
+
+ def to_str(self) -> str:
+ return f'{self._major}.{self._minor}.{self._micro}'
+
+ def from_dict(self, settings: dict):
+ self._major = int(settings[VersionEnum.Major.value])
+ self._minor = int(settings[VersionEnum.Minor.value])
+ self._micro = int(settings[VersionEnum.Micro.value])
+
+ def to_dict(self) -> dict:
+ return {
+ VersionEnum.Major.value: self._major,
+ VersionEnum.Minor.value: self._minor,
+ VersionEnum.Micro.value: self._micro
+ }
diff --git a/src/sh_edraft/coding/model/version_enum.py b/src/sh_edraft/coding/model/version_enum.py
new file mode 100644
index 00000000..8c294c1e
--- /dev/null
+++ b/src/sh_edraft/coding/model/version_enum.py
@@ -0,0 +1,8 @@
+from enum import Enum
+
+
+class VersionEnum(Enum):
+
+ Major = 'Major'
+ Minor = 'Minor'
+ Micro = 'Micro'
diff --git a/src/sh_edraft/configuration/__init__.py b/src/sh_edraft/configuration/__init__.py
new file mode 100644
index 00000000..21979d7f
--- /dev/null
+++ b/src/sh_edraft/configuration/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.configuration
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.configuration'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .configuration import Configuration
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/configuration/base/__init__.py b/src/sh_edraft/configuration/base/__init__.py
new file mode 100644
index 00000000..28444895
--- /dev/null
+++ b/src/sh_edraft/configuration/base/__init__.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.configuration.base
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.configuration.base'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .configuration_base import ConfigurationBase
+from .configuration_model_base import ConfigurationModelBase
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/configuration/base/configuration_base.py b/src/sh_edraft/configuration/base/configuration_base.py
new file mode 100644
index 00000000..bf44a8c1
--- /dev/null
+++ b/src/sh_edraft/configuration/base/configuration_base.py
@@ -0,0 +1,34 @@
+from abc import abstractmethod, ABC
+from collections import Callable
+from typing import Type
+
+from sh_edraft.configuration.base.configuration_model_base import ConfigurationModelBase
+from sh_edraft.environment.base.environment_base import EnvironmentBase
+
+
+class ConfigurationBase(ABC):
+
+ @abstractmethod
+ def __init__(self): pass
+
+ @property
+ @abstractmethod
+ def environment(self) -> EnvironmentBase: pass
+
+ @abstractmethod
+ def add_environment_variables(self, prefix: str): pass
+
+ @abstractmethod
+ def add_argument_variables(self): pass
+
+ @abstractmethod
+ def add_json_file(self, name: str, optional: bool = None): pass
+
+ @abstractmethod
+ def add_configuration(self, key_type: type, value: object): pass
+
+ @abstractmethod
+ def get_configuration(self, search_type: Type[ConfigurationModelBase]) -> Callable[ConfigurationModelBase]: pass
+
+ @abstractmethod
+ def create(self): pass
diff --git a/src/sh_edraft/configuration/base/configuration_model_base.py b/src/sh_edraft/configuration/base/configuration_model_base.py
new file mode 100644
index 00000000..9772a648
--- /dev/null
+++ b/src/sh_edraft/configuration/base/configuration_model_base.py
@@ -0,0 +1,10 @@
+from abc import ABC, abstractmethod
+
+
+class ConfigurationModelBase(ABC):
+
+ @abstractmethod
+ def __init__(self): pass
+
+ @abstractmethod
+ def from_dict(self, settings: dict): pass
diff --git a/src/sh_edraft/configuration/configuration.py b/src/sh_edraft/configuration/configuration.py
new file mode 100644
index 00000000..f4007ada
--- /dev/null
+++ b/src/sh_edraft/configuration/configuration.py
@@ -0,0 +1,121 @@
+import json
+import os
+import sys
+
+from sh_edraft.configuration.base.configuration_model_base import ConfigurationModelBase
+from sh_edraft.configuration.base.configuration_base import ConfigurationBase
+from sh_edraft.configuration.model.configuration_variable_name import ConfigurationVariableName
+from sh_edraft.environment.base.environment_base import EnvironmentBase
+from sh_edraft.environment.hosting_environment import HostingEnvironment
+from sh_edraft.environment.model.environment_name import EnvironmentName
+from sh_edraft.console.console import Console
+from sh_edraft.console.model import ForegroundColor
+
+
+class Configuration(ConfigurationBase):
+
+ def __init__(self):
+ ConfigurationBase.__init__(self)
+
+ self._hosting_environment = HostingEnvironment()
+ self._config: dict[type, ConfigurationModelBase] = {}
+
+ @property
+ def environment(self) -> EnvironmentBase:
+ return self._hosting_environment
+
+ @staticmethod
+ def _print_info(name: str, message: str):
+ Console.set_foreground_color(ForegroundColor.green)
+ Console.write_line(f'[{name}] {message}')
+ Console.set_foreground_color(ForegroundColor.default)
+
+ @staticmethod
+ def _print_warn(name: str, message: str):
+ Console.set_foreground_color(ForegroundColor.yellow)
+ Console.write_line(f'[{name}] {message}')
+ Console.set_foreground_color(ForegroundColor.default)
+
+ @staticmethod
+ def _print_error(name: str, message: str):
+ Console.set_foreground_color(ForegroundColor.red)
+ Console.write_line(f'[{name}] {message}')
+ Console.set_foreground_color(ForegroundColor.default)
+
+ def _set_variable(self, name: str, value: str):
+ if name == ConfigurationVariableName.environment.value:
+ self._hosting_environment.environment_name = EnvironmentName(value)
+
+ elif name == ConfigurationVariableName.name.value:
+ self._hosting_environment.application_name = value
+
+ elif name == ConfigurationVariableName.customer.value:
+ self._hosting_environment.customer = value
+
+ def add_environment_variables(self, prefix: str):
+ for variable in ConfigurationVariableName.to_list():
+ var_name = f'{prefix}{variable}'
+ if var_name in [key.upper() for key in os.environ.keys()]:
+ self._set_variable(variable, os.environ[var_name])
+
+ def add_argument_variables(self):
+ for arg in sys.argv[1:]:
+ try:
+ argument = arg.split('--')[1].split('=')[0].upper()
+ value = arg.split('=')[1]
+
+ if argument not in ConfigurationVariableName.to_list():
+ raise Exception(f'Invalid argument name: {argument}')
+
+ self._set_variable(argument, value)
+ except Exception as e:
+ self._print_error(__name__, f'Invalid argument: {arg} -> {e}')
+ exit()
+
+ def add_json_file(self, name: str, optional: bool = None):
+ if self._hosting_environment.content_root_path.endswith('/') and not name.startswith('/'):
+ file_path = f'{self._hosting_environment.content_root_path}{name}'
+ else:
+ file_path = f'{self._hosting_environment.content_root_path}/{name}'
+
+ if not os.path.isfile(file_path):
+ if not optional:
+ self._print_error(__name__, f'File not found: {file_path}')
+ exit()
+
+ self._print_warn(__name__, f'Not Loaded config file: {file_path}')
+ return None
+
+ config_from_file = self._load_json_file(file_path)
+ for sub in ConfigurationModelBase.__subclasses__():
+ for key, value in config_from_file.items():
+ if sub.__name__ == key:
+ configuration = sub()
+ configuration.from_dict(value)
+ self.add_configuration(sub, configuration)
+
+ def _load_json_file(self, file: str) -> dict:
+ try:
+ # open config file, create if not exists
+ with open(file, encoding='utf-8') as cfg:
+ # load json
+ json_cfg = json.load(cfg)
+ self._print_info(__name__, f'Loaded config file: {file}')
+ return json_cfg
+ except Exception as e:
+ self._print_error(__name__, f'Cannot load config file: {file}! -> {e}')
+ return {}
+
+ def add_configuration(self, key_type: type, value: ConfigurationModelBase):
+ self._config[key_type] = value
+
+ def get_configuration(self, search_type: type) -> ConfigurationModelBase:
+ if search_type not in self._config:
+ raise Exception(f'Config model by type {search_type} not found')
+
+ for config_model in self._config:
+ if config_model == search_type:
+ return self._config[config_model]
+
+ def create(self):
+ pass
diff --git a/src/sh_edraft/configuration/model/__init__.py b/src/sh_edraft/configuration/model/__init__.py
new file mode 100644
index 00000000..ae7cc417
--- /dev/null
+++ b/src/sh_edraft/configuration/model/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.configuration.model
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.configuration.model'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .configuration_variable_name import ConfigurationVariableName
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/configuration/model/configuration_variable_name.py b/src/sh_edraft/configuration/model/configuration_variable_name.py
new file mode 100644
index 00000000..c89deb88
--- /dev/null
+++ b/src/sh_edraft/configuration/model/configuration_variable_name.py
@@ -0,0 +1,12 @@
+from enum import Enum
+
+
+class ConfigurationVariableName(Enum):
+
+ environment = 'ENVIRONMENT'
+ name = 'NAME'
+ customer = 'CUSTOMER'
+
+ @staticmethod
+ def to_list():
+ return [var.value for var in ConfigurationVariableName]
diff --git a/src/sh_edraft/console/__init__.py b/src/sh_edraft/console/__init__.py
new file mode 100644
index 00000000..632019e8
--- /dev/null
+++ b/src/sh_edraft/console/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.console
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.console'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .console import Console
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/console/console.py b/src/sh_edraft/console/console.py
new file mode 100644
index 00000000..14964866
--- /dev/null
+++ b/src/sh_edraft/console/console.py
@@ -0,0 +1,205 @@
+import os
+from typing import Union, Optional
+
+import pyfiglet
+from tabulate import tabulate
+from termcolor import colored
+
+from sh_edraft.console.model.background_color import BackgroundColor
+from sh_edraft.console.model.foreground_color import ForegroundColor
+
+
+class Console:
+ _is_first_write = True
+
+ _background_color: BackgroundColor = BackgroundColor.default
+ _foreground_color: ForegroundColor = ForegroundColor.default
+ _x: Optional[int] = None
+ _y: Optional[int] = None
+ _disabled: bool = False
+
+ """
+ Properties
+ """
+
+ @classmethod
+ @property
+ def background_color(cls) -> str:
+ return str(cls._background_color.value)
+
+ @classmethod
+ @property
+ def foreground_color(cls) -> str:
+ return str(cls._foreground_color.value)
+
+ """
+ Settings
+ """
+
+ @classmethod
+ def set_background_color(cls, color: Union[BackgroundColor, str]):
+ if type(color) is str:
+ cls._background_color = BackgroundColor[color]
+ else:
+ cls._background_color = color
+
+ @classmethod
+ def set_foreground_color(cls, color: Union[ForegroundColor, str]):
+
+ if type(color) is str:
+ cls._foreground_color = ForegroundColor[color]
+ else:
+ cls._foreground_color = color
+
+ @classmethod
+ def reset_cursor_position(cls):
+ cls._x = None
+ cls._y = None
+
+ @classmethod
+ def set_cursor_position(cls, x: int, y: int):
+ cls._x = x
+ cls._y = y
+
+ """
+ Useful protected methods
+ """
+
+ @classmethod
+ def _output(cls, string: str, x: int = None, y: int = None, end='\n'):
+ if cls._is_first_write:
+ cls._is_first_write = False
+
+ args = []
+ colored_args = []
+
+ if x is not None and y is not None:
+ args.append(f'\033[{x};{y}H')
+ elif cls._x is not None and cls._y is not None:
+ args.append(f'\033[{cls._x};{cls._y}H')
+
+ colored_args.append(string)
+ if cls._foreground_color != ForegroundColor.default and cls._background_color == BackgroundColor.default:
+ colored_args.append(cls._foreground_color.value)
+ elif cls._foreground_color == ForegroundColor.default and cls._background_color != BackgroundColor.default:
+ colored_args.append(cls._background_color.value)
+ elif cls._foreground_color != ForegroundColor.default and cls._background_color != BackgroundColor.default:
+ colored_args.append(cls._foreground_color.value)
+ colored_args.append(cls._background_color.value)
+
+ args.append(colored(*colored_args))
+ print(*args, end=end)
+
+ """
+ Useful public methods
+ """
+
+ @classmethod
+ def banner(cls, string: str):
+ if cls._disabled:
+ return
+
+ ascii_banner = pyfiglet.figlet_format(string)
+ cls.write_line(ascii_banner)
+
+ @classmethod
+ def clear(cls):
+ os.system('cls' if os.name == 'nt' else 'clear')
+
+ @classmethod
+ def close(cls):
+ if cls._disabled:
+ return
+
+ Console.reset()
+ Console.write('\n\n\nPress any key to continue...')
+ Console.read_line()
+ exit()
+
+ @classmethod
+ def disable(cls):
+ cls._disabled = True
+
+ @classmethod
+ def error(cls, string: str, tb: str = None):
+ if cls._disabled:
+ return
+
+ cls.set_foreground_color('red')
+ if tb is not None:
+ cls.write_line(f'{string} -> {tb}')
+ else:
+ cls.write_line(string)
+ cls.set_foreground_color('default')
+
+ @classmethod
+ def enable(cls):
+ cls._disabled = False
+
+ @classmethod
+ def read(cls, output: str = None) -> str:
+ if output is not None:
+ cls.write(output)
+
+ return input()[0]
+
+ @classmethod
+ def read_line(cls, output: str = None) -> str:
+ if cls._disabled:
+ return ''
+
+ if output is not None:
+ cls.write(output)
+
+ return input()
+
+ @classmethod
+ def reset(cls):
+ cls._background_color = BackgroundColor.default
+ cls._foreground_color = ForegroundColor.default
+
+ @classmethod
+ def table(cls, header: list[str], values: list[list[str]]):
+ if cls._disabled:
+ return
+
+ table = tabulate(values, headers=header)
+
+ Console.write_line(table)
+ Console.write('\n')
+
+ @classmethod
+ def write(cls, *args):
+ if cls._disabled:
+ return
+
+ string = ' '.join(map(str, args))
+ cls._output(string, end='')
+
+ @classmethod
+ def write_at(cls, x: int, y: int, *args):
+ if cls._disabled:
+ return
+
+ string = ' '.join(map(str, args))
+ cls._output(string, x, y, end='')
+
+ @classmethod
+ def write_line(cls, *args):
+ if cls._disabled:
+ return
+
+ string = ' '.join(map(str, args))
+ if not cls._is_first_write:
+ cls._output('')
+ cls._output(string, end='')
+
+ @classmethod
+ def write_line_at(cls, x: int, y: int, *args):
+ if cls._disabled:
+ return
+
+ string = ' '.join(map(str, args))
+ if not cls._is_first_write:
+ cls._output('', end='')
+ cls._output(string, x, y, end='')
diff --git a/src/sh_edraft/console/model/__init__.py b/src/sh_edraft/console/model/__init__.py
new file mode 100644
index 00000000..3df50dae
--- /dev/null
+++ b/src/sh_edraft/console/model/__init__.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.console.model
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.console.model'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .background_color import BackgroundColor
+from .foreground_color import ForegroundColor
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/console/model/background_color.py b/src/sh_edraft/console/model/background_color.py
new file mode 100644
index 00000000..f4947e30
--- /dev/null
+++ b/src/sh_edraft/console/model/background_color.py
@@ -0,0 +1,14 @@
+from enum import Enum
+
+
+class BackgroundColor(Enum):
+
+ default = 'on_default'
+ grey = 'on_grey'
+ red = 'on_red'
+ green = 'on_green'
+ yellow = 'on_yellow'
+ blue = 'on_blue'
+ magenta = 'on_magenta'
+ cyan = 'on_cyan'
+ white = 'on_white'
diff --git a/src/sh_edraft/console/model/foreground_color.py b/src/sh_edraft/console/model/foreground_color.py
new file mode 100644
index 00000000..463d4ae7
--- /dev/null
+++ b/src/sh_edraft/console/model/foreground_color.py
@@ -0,0 +1,14 @@
+from enum import Enum
+
+
+class ForegroundColor(Enum):
+
+ default = 'default'
+ grey = 'grey'
+ red = 'red'
+ green = 'green'
+ yellow = 'yellow'
+ blue = 'blue'
+ magenta = 'magenta'
+ cyan = 'cyan'
+ white = 'white'
diff --git a/src/sh_edraft/database/__init__.py b/src/sh_edraft/database/__init__.py
new file mode 100644
index 00000000..7bb526a4
--- /dev/null
+++ b/src/sh_edraft/database/__init__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.database
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.database'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/database/connection/__init__.py b/src/sh_edraft/database/connection/__init__.py
new file mode 100644
index 00000000..5f3406df
--- /dev/null
+++ b/src/sh_edraft/database/connection/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.database.connection
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.database.connection'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .database_connection import DatabaseConnection
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/database/connection/base/__init__.py b/src/sh_edraft/database/connection/base/__init__.py
new file mode 100644
index 00000000..492da1d6
--- /dev/null
+++ b/src/sh_edraft/database/connection/base/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.database.connection.base
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.database.connection.base'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .database_connection_base import DatabaseConnectionBase
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/database/connection/base/database_connection_base.py b/src/sh_edraft/database/connection/base/database_connection_base.py
new file mode 100644
index 00000000..9bc31427
--- /dev/null
+++ b/src/sh_edraft/database/connection/base/database_connection_base.py
@@ -0,0 +1,21 @@
+from abc import abstractmethod, ABC
+
+from sqlalchemy import engine
+from sqlalchemy.orm import Session
+
+
+class DatabaseConnectionBase(ABC):
+
+ @abstractmethod
+ def __init__(self): pass
+
+ @property
+ @abstractmethod
+ def engine(self) -> engine: pass
+
+ @property
+ @abstractmethod
+ def session(self) -> Session: pass
+
+ @abstractmethod
+ def connect(self, connection_string: str): pass
diff --git a/src/sh_edraft/database/connection/database_connection.py b/src/sh_edraft/database/connection/database_connection.py
new file mode 100644
index 00000000..b3397113
--- /dev/null
+++ b/src/sh_edraft/database/connection/database_connection.py
@@ -0,0 +1,56 @@
+from typing import Optional
+
+from sqlalchemy import engine, create_engine
+from sqlalchemy.orm import Session, sessionmaker
+
+from sh_edraft.database.connection.base.database_connection_base import DatabaseConnectionBase
+from sh_edraft.database.model.database_settings import DatabaseSettings
+from sh_edraft.console.console import Console
+from sh_edraft.console.model.foreground_color import ForegroundColor
+
+
+class DatabaseConnection(DatabaseConnectionBase):
+
+ def __init__(self, database_settings: DatabaseSettings):
+ DatabaseConnectionBase.__init__(self)
+
+ self._db_settings = database_settings
+
+ self._engine: Optional[engine] = None
+ self._session: Optional[Session] = None
+ self._credentials: Optional[str] = None
+
+ @property
+ def engine(self) -> engine:
+ return self._engine
+
+ @property
+ def session(self) -> Session:
+ return self._session
+
+ def connect(self, connection_string: str):
+ try:
+ self._engine = create_engine(connection_string)
+
+ if self._db_settings.encoding is not None:
+ self._engine.encoding = self._db_settings.encoding
+
+ if self._db_settings.case_sensitive is not None:
+ self._engine.case_sensitive = self._db_settings.case_sensitive
+
+ if self._db_settings.echo is not None:
+ self._engine.echo = self._db_settings.echo
+
+ self._engine.connect()
+
+ db_session = sessionmaker(bind=self._engine)
+ self._session = db_session()
+ Console.set_foreground_color(ForegroundColor.green)
+ Console.write_line(f'[{__name__}] Connected to database')
+ Console.set_foreground_color(ForegroundColor.default)
+ except Exception as e:
+ Console.set_foreground_color(ForegroundColor.red)
+ Console.write_line(f'[{__name__}] Database connection failed -> {e}')
+ Console.set_foreground_color(ForegroundColor.default)
+ exit()
+
diff --git a/src/sh_edraft/database/context/__init__.py b/src/sh_edraft/database/context/__init__.py
new file mode 100644
index 00000000..1f5bc196
--- /dev/null
+++ b/src/sh_edraft/database/context/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.database.context
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.database.context'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .database_context import DatabaseContext
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/database/context/base/__init__.py b/src/sh_edraft/database/context/base/__init__.py
new file mode 100644
index 00000000..63d9bb64
--- /dev/null
+++ b/src/sh_edraft/database/context/base/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.database.context.base
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.database.context.base'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .database_context_base import DatabaseContextBase
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/database/context/base/database_context_base.py b/src/sh_edraft/database/context/base/database_context_base.py
new file mode 100644
index 00000000..b8294e64
--- /dev/null
+++ b/src/sh_edraft/database/context/base/database_context_base.py
@@ -0,0 +1,27 @@
+from abc import abstractmethod
+
+from sqlalchemy import engine
+from sqlalchemy.orm import Session
+
+from sh_edraft.service.base.service_base import ServiceBase
+
+
+class DatabaseContextBase(ServiceBase):
+
+ @abstractmethod
+ def __init__(self):
+ ServiceBase.__init__(self)
+
+ @property
+ @abstractmethod
+ def engine(self) -> engine: pass
+
+ @property
+ @abstractmethod
+ def session(self) -> Session: pass
+
+ @abstractmethod
+ def connect(self, connection_string: str): pass
+
+ @abstractmethod
+ def _create_tables(self): pass
diff --git a/src/sh_edraft/database/context/database_context.py b/src/sh_edraft/database/context/database_context.py
new file mode 100644
index 00000000..436ed53e
--- /dev/null
+++ b/src/sh_edraft/database/context/database_context.py
@@ -0,0 +1,50 @@
+from sqlalchemy import engine, Table
+from sqlalchemy.orm import Session
+
+from sh_edraft.database.connection.database_connection import DatabaseConnection
+from sh_edraft.database.connection.base.database_connection_base import DatabaseConnectionBase
+from sh_edraft.database.context.base.database_context_base import DatabaseContextBase
+from sh_edraft.database.model.dbmodel import DBModel
+from sh_edraft.database.model.database_settings import DatabaseSettings
+from sh_edraft.console.console import Console
+from sh_edraft.console.model.foreground_color import ForegroundColor
+
+
+class DatabaseContext(DatabaseContextBase):
+
+ def __init__(self, database_settings: DatabaseSettings):
+ DatabaseContextBase.__init__(self)
+
+ self._db: DatabaseConnectionBase = DatabaseConnection(database_settings)
+ self._tables: list[Table] = []
+
+ @property
+ def engine(self) -> engine:
+ return self._db.engine
+
+ @property
+ def session(self) -> Session:
+ return self._db.session
+
+ def create(self):
+ pass
+
+ def connect(self, connection_string: str):
+ self._db.connect(connection_string)
+ self._create_tables()
+
+ def _create_tables(self):
+ try:
+ for subclass in DBModel.__subclasses__():
+ self._tables.append(subclass.__table__)
+
+ DBModel.metadata.drop_all(self._db.engine, self._tables)
+ DBModel.metadata.create_all(self._db.engine, self._tables, checkfirst=True)
+ Console.set_foreground_color(ForegroundColor.green)
+ Console.write_line(f'[{__name__}] Created tables')
+ Console.set_foreground_color(ForegroundColor.default)
+ except Exception as e:
+ Console.set_foreground_color(ForegroundColor.red)
+ Console.write_line(f'[{__name__}] Creating tables failed -> {e}')
+ Console.set_foreground_color(ForegroundColor.default)
+ exit()
diff --git a/src/sh_edraft/database/model/__init__.py b/src/sh_edraft/database/model/__init__.py
new file mode 100644
index 00000000..de87e001
--- /dev/null
+++ b/src/sh_edraft/database/model/__init__.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.database.model
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.database.model'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .database_settings import DatabaseSettings
+from .database_settings_name import DatabaseSettingsName
+from .dbmodel import DBModel
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/database/model/database_settings.py b/src/sh_edraft/database/model/database_settings.py
new file mode 100644
index 00000000..aed39e5f
--- /dev/null
+++ b/src/sh_edraft/database/model/database_settings.py
@@ -0,0 +1,78 @@
+import traceback
+from typing import Optional
+
+from sh_edraft.configuration.base.configuration_model_base import ConfigurationModelBase
+from sh_edraft.database.model.database_settings_name import DatabaseSettingsName
+from sh_edraft.console.console import Console
+from sh_edraft.console.model.foreground_color import ForegroundColor
+
+
+class DatabaseSettings(ConfigurationModelBase):
+
+ def __init__(self):
+ ConfigurationModelBase.__init__(self)
+
+ self._connection_string: Optional[str] = None
+ self._credentials: Optional[str] = None
+ self._encoding: Optional[str] = None
+ self._case_sensitive: Optional[bool] = None
+ self._echo: Optional[bool] = None
+
+ @property
+ def connection_string(self) -> str:
+ return self._connection_string
+
+ @connection_string.setter
+ def connection_string(self, connection_string: str):
+ self._connection_string = connection_string
+
+ @property
+ def credentials(self) -> str:
+ return self._credentials
+
+ @credentials.setter
+ def credentials(self, credentials: str):
+ self._credentials = credentials
+
+ @property
+ def encoding(self) -> str:
+ return self._encoding
+
+ @encoding.setter
+ def encoding(self, encoding: str) -> None:
+ self._encoding = encoding
+
+ @property
+ def case_sensitive(self) -> bool:
+ return self._case_sensitive
+
+ @case_sensitive.setter
+ def case_sensitive(self, case_sensitive: bool) -> None:
+ self._case_sensitive = case_sensitive
+
+ @property
+ def echo(self) -> bool:
+ return self._echo
+
+ @echo.setter
+ def echo(self, echo: bool) -> None:
+ self._echo = echo
+
+ def from_dict(self, settings: dict):
+ try:
+ self._connection_string = settings[DatabaseSettingsName.connection_string.value]
+ self._credentials = settings[DatabaseSettingsName.credentials.value]
+
+ if DatabaseSettingsName.encoding.value in settings:
+ self._encoding = settings[DatabaseSettingsName.encoding.value]
+
+ if DatabaseSettingsName.case_sensitive.value in settings:
+ self._case_sensitive = bool(settings[DatabaseSettingsName.case_sensitive.value])
+
+ if DatabaseSettingsName.echo.value in settings:
+ self._echo = bool(settings[DatabaseSettingsName.echo.value])
+ except Exception as e:
+ Console.set_foreground_color(ForegroundColor.red)
+ Console.write_line(f'[ ERROR ] [ {__name__} ]: Reading error in {self.__name__} settings')
+ Console.write_line(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}')
+ Console.set_foreground_color(ForegroundColor.default)
diff --git a/src/sh_edraft/database/model/database_settings_name.py b/src/sh_edraft/database/model/database_settings_name.py
new file mode 100644
index 00000000..57ece941
--- /dev/null
+++ b/src/sh_edraft/database/model/database_settings_name.py
@@ -0,0 +1,10 @@
+from enum import Enum
+
+
+class DatabaseSettingsName(Enum):
+
+ connection_string = 'ConnectionString'
+ credentials = 'Credentials'
+ encoding = 'Encoding'
+ case_sensitive = 'CaseSensitive'
+ echo = 'Echo'
diff --git a/src/sh_edraft/database/model/dbmodel.py b/src/sh_edraft/database/model/dbmodel.py
new file mode 100644
index 00000000..145da02b
--- /dev/null
+++ b/src/sh_edraft/database/model/dbmodel.py
@@ -0,0 +1,3 @@
+from sqlalchemy.ext.declarative import declarative_base
+
+DBModel: declarative_base = declarative_base()
diff --git a/src/sh_edraft/environment/__init__.py b/src/sh_edraft/environment/__init__.py
new file mode 100644
index 00000000..ac993870
--- /dev/null
+++ b/src/sh_edraft/environment/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.environment
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.environment'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .hosting_environment import HostingEnvironment
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/environment/base/__init__.py b/src/sh_edraft/environment/base/__init__.py
new file mode 100644
index 00000000..36b8b351
--- /dev/null
+++ b/src/sh_edraft/environment/base/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.environment.base
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.environment.base'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .environment_base import EnvironmentBase
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/environment/base/environment_base.py b/src/sh_edraft/environment/base/environment_base.py
new file mode 100644
index 00000000..f5dc9266
--- /dev/null
+++ b/src/sh_edraft/environment/base/environment_base.py
@@ -0,0 +1,43 @@
+from abc import ABC, abstractmethod
+
+
+class EnvironmentBase(ABC):
+
+ @abstractmethod
+ def __init__(self): pass
+
+ @property
+ @abstractmethod
+ def environment_name(self) -> str: pass
+
+ @environment_name.setter
+ @abstractmethod
+ def environment_name(self, environment_name: str): pass
+
+ @property
+ @abstractmethod
+ def application_name(self) -> str: pass
+
+ @application_name.setter
+ @abstractmethod
+ def application_name(self, application_name: str): pass
+
+ @property
+ @abstractmethod
+ def customer(self) -> str: pass
+
+ @customer.setter
+ @abstractmethod
+ def customer(self, customer: str): pass
+
+ @property
+ @abstractmethod
+ def content_root_path(self) -> str: pass
+
+ @content_root_path.setter
+ @abstractmethod
+ def content_root_path(self, content_root_path: str): pass
+
+ @property
+ @abstractmethod
+ def host_name(self) -> str: pass
diff --git a/src/sh_edraft/environment/hosting_environment.py b/src/sh_edraft/environment/hosting_environment.py
new file mode 100644
index 00000000..2f2021ff
--- /dev/null
+++ b/src/sh_edraft/environment/hosting_environment.py
@@ -0,0 +1,52 @@
+from socket import gethostname
+from typing import Optional
+
+from sh_edraft.environment.base.environment_base import EnvironmentBase
+from sh_edraft.environment.model.environment_name import EnvironmentName
+
+
+class HostingEnvironment(EnvironmentBase):
+
+ def __init__(self, name: EnvironmentName = EnvironmentName.production, crp: str = './'):
+ EnvironmentBase.__init__(self)
+
+ self._environment_name: Optional[EnvironmentName] = name
+ self._app_name: Optional[str] = None
+ self._customer: Optional[str] = None
+ self._content_root_path: Optional[str] = crp
+
+ @property
+ def environment_name(self) -> str:
+ return str(self._environment_name.value)
+
+ @environment_name.setter
+ def environment_name(self, environment_name: str):
+ self._environment_name = EnvironmentName(environment_name)
+
+ @property
+ def application_name(self) -> str:
+ return self._app_name if self._app_name is not None else ''
+
+ @application_name.setter
+ def application_name(self, application_name: str):
+ self._app_name = application_name
+
+ @property
+ def customer(self) -> str:
+ return self._customer if self._customer is not None else ''
+
+ @customer.setter
+ def customer(self, customer: str):
+ self._customer = customer
+
+ @property
+ def content_root_path(self) -> str:
+ return self._content_root_path
+
+ @content_root_path.setter
+ def content_root_path(self, content_root_path: str):
+ self._content_root_path = content_root_path
+
+ @property
+ def host_name(self):
+ return gethostname()
diff --git a/src/sh_edraft/environment/model/__init__.py b/src/sh_edraft/environment/model/__init__.py
new file mode 100644
index 00000000..59266f74
--- /dev/null
+++ b/src/sh_edraft/environment/model/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.environment.model
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.environment.model'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .environment_name import EnvironmentName
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/environment/model/environment_name.py b/src/sh_edraft/environment/model/environment_name.py
new file mode 100644
index 00000000..18abcbb5
--- /dev/null
+++ b/src/sh_edraft/environment/model/environment_name.py
@@ -0,0 +1,9 @@
+from enum import Enum
+
+
+class EnvironmentName(Enum):
+
+ production = 'production'
+ staging = 'staging'
+ testing = 'testing'
+ development = 'development'
diff --git a/src/sh_edraft/hosting/__init__.py b/src/sh_edraft/hosting/__init__.py
new file mode 100644
index 00000000..d650f7e0
--- /dev/null
+++ b/src/sh_edraft/hosting/__init__.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.hosting
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.hosting'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .application_host import ApplicationHost
+from .application_runtime import ApplicationRuntime
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/hosting/application_host.py b/src/sh_edraft/hosting/application_host.py
new file mode 100644
index 00000000..1c5ed011
--- /dev/null
+++ b/src/sh_edraft/hosting/application_host.py
@@ -0,0 +1,46 @@
+import atexit
+from datetime import datetime
+
+from sh_edraft.configuration.configuration import Configuration
+from sh_edraft.configuration.base.configuration_base import ConfigurationBase
+from sh_edraft.hosting.base.application_runtime_base import ApplicationRuntimeBase
+from sh_edraft.hosting.application_runtime import ApplicationRuntime
+from sh_edraft.hosting.base.application_host_base import ApplicationHostBase
+from sh_edraft.service.providing.service_provider import ServiceProvider
+from sh_edraft.service.providing.base.service_provider_base import ServiceProviderBase
+from sh_edraft.console.console import Console
+
+
+class ApplicationHost(ApplicationHostBase):
+
+ def __init__(self):
+ ApplicationHostBase.__init__(self)
+
+ # Init
+ self._config = Configuration()
+ self._app_runtime = ApplicationRuntime(self._config)
+ self._services = ServiceProvider(self._app_runtime)
+
+ # Create
+ self._config.create()
+ self._services.create()
+
+ # Set vars
+ self._start_time: datetime = datetime.now()
+ self._end_time: datetime = datetime.now()
+
+ atexit.register(Console.close)
+
+ @property
+ def configuration(self) -> ConfigurationBase:
+ return self._config
+
+ @property
+ def application_runtime(self) -> ApplicationRuntimeBase:
+ return self._app_runtime
+
+ @property
+ def services(self) -> ServiceProviderBase:
+ return self._services
+
+ def create(self): pass
diff --git a/src/sh_edraft/hosting/application_runtime.py b/src/sh_edraft/hosting/application_runtime.py
new file mode 100644
index 00000000..5e903bb5
--- /dev/null
+++ b/src/sh_edraft/hosting/application_runtime.py
@@ -0,0 +1,38 @@
+from datetime import datetime
+
+from sh_edraft.configuration.base.configuration_base import ConfigurationBase
+from sh_edraft.hosting.base.application_runtime_base import ApplicationRuntimeBase
+
+
+class ApplicationRuntime(ApplicationRuntimeBase):
+
+ def __init__(self, config: ConfigurationBase):
+ ApplicationRuntimeBase.__init__(self)
+
+ self._app_configuration = config
+ self._start_time: datetime = datetime.now()
+ self._end_time: datetime = datetime.now()
+
+ @property
+ def configuration(self) -> ConfigurationBase:
+ return self._app_configuration
+
+ @property
+ def start_time(self) -> datetime:
+ return self._start_time
+
+ @start_time.setter
+ def start_time(self, start_time: datetime):
+ self._start_time = start_time
+
+ @property
+ def end_time(self) -> datetime:
+ return self._end_time
+
+ @end_time.setter
+ def end_time(self, end_time: datetime):
+ self._end_time = end_time
+
+ @property
+ def date_time_now(self) -> datetime:
+ return datetime.now()
diff --git a/src/sh_edraft/hosting/base/__init__.py b/src/sh_edraft/hosting/base/__init__.py
new file mode 100644
index 00000000..47f46211
--- /dev/null
+++ b/src/sh_edraft/hosting/base/__init__.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.hosting.base
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.hosting.base'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .application_base import ApplicationBase
+from .application_host_base import ApplicationHostBase
+from .application_runtime_base import ApplicationRuntimeBase
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/hosting/base/application_base.py b/src/sh_edraft/hosting/base/application_base.py
new file mode 100644
index 00000000..00f72997
--- /dev/null
+++ b/src/sh_edraft/hosting/base/application_base.py
@@ -0,0 +1,19 @@
+from abc import ABC, abstractmethod
+
+
+class ApplicationBase(ABC):
+
+ @abstractmethod
+ def __init__(self): pass
+
+ @abstractmethod
+ def create_application_host(self): pass
+
+ @abstractmethod
+ def create_configuration(self): pass
+
+ @abstractmethod
+ def create_services(self): pass
+
+ @abstractmethod
+ def main(self): pass
diff --git a/src/sh_edraft/hosting/base/application_host_base.py b/src/sh_edraft/hosting/base/application_host_base.py
new file mode 100644
index 00000000..fb816ccb
--- /dev/null
+++ b/src/sh_edraft/hosting/base/application_host_base.py
@@ -0,0 +1,26 @@
+from abc import ABC, abstractmethod
+
+from sh_edraft.configuration.base.configuration_base import ConfigurationBase
+from sh_edraft.hosting.base.application_runtime_base import ApplicationRuntimeBase
+from sh_edraft.service.providing.base.service_provider_base import ServiceProviderBase
+
+
+class ApplicationHostBase(ABC):
+
+ @abstractmethod
+ def __init__(self): pass
+
+ @property
+ @abstractmethod
+ def configuration(self) -> ConfigurationBase: pass
+
+ @property
+ @abstractmethod
+ def application_runtime(self) -> ApplicationRuntimeBase: pass
+
+ @property
+ @abstractmethod
+ def services(self) -> ServiceProviderBase: pass
+
+ @abstractmethod
+ def create(self): pass
diff --git a/src/sh_edraft/hosting/base/application_runtime_base.py b/src/sh_edraft/hosting/base/application_runtime_base.py
new file mode 100644
index 00000000..fd644270
--- /dev/null
+++ b/src/sh_edraft/hosting/base/application_runtime_base.py
@@ -0,0 +1,34 @@
+from abc import ABC, abstractmethod
+from datetime import datetime
+
+from sh_edraft.configuration.base.configuration_base import ConfigurationBase
+
+
+class ApplicationRuntimeBase(ABC):
+
+ @abstractmethod
+ def __init__(self): pass
+
+ @property
+ @abstractmethod
+ def configuration(self) -> ConfigurationBase: pass
+
+ @property
+ @abstractmethod
+ def start_time(self) -> datetime: pass
+
+ @start_time.setter
+ @abstractmethod
+ def start_time(self, start_time: datetime): pass
+
+ @property
+ @abstractmethod
+ def end_time(self): pass
+
+ @end_time.setter
+ @abstractmethod
+ def end_time(self, end_time: datetime): pass
+
+ @property
+ @abstractmethod
+ def date_time_now(self) -> datetime: pass
diff --git a/src/sh_edraft/hosting/model/__init__.py b/src/sh_edraft/hosting/model/__init__.py
new file mode 100644
index 00000000..2ff0878e
--- /dev/null
+++ b/src/sh_edraft/hosting/model/__init__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.hosting.model
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.hosting.model'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/logging/__init__.py b/src/sh_edraft/logging/__init__.py
new file mode 100644
index 00000000..80fdec21
--- /dev/null
+++ b/src/sh_edraft/logging/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.logging
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.logging'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .logger import Logger
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/logging/base/__init__.py b/src/sh_edraft/logging/base/__init__.py
new file mode 100644
index 00000000..83da99e0
--- /dev/null
+++ b/src/sh_edraft/logging/base/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.logging.base
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.logging.base'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .logger_base import LoggerBase
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/logging/base/logger_base.py b/src/sh_edraft/logging/base/logger_base.py
new file mode 100644
index 00000000..2dc5de50
--- /dev/null
+++ b/src/sh_edraft/logging/base/logger_base.py
@@ -0,0 +1,31 @@
+from abc import abstractmethod
+
+from sh_edraft.service.base.service_base import ServiceBase
+
+
+class LoggerBase(ServiceBase):
+
+ @abstractmethod
+ def __init__(self):
+ ServiceBase.__init__(self)
+
+ @abstractmethod
+ def header(self, string: str): pass
+
+ @abstractmethod
+ def trace(self, name: str, message: str): pass
+
+ @abstractmethod
+ def debug(self, name: str, message: str): pass
+
+ @abstractmethod
+ def info(self, name: str, message: str): pass
+
+ @abstractmethod
+ def warn(self, name: str, message: str): pass
+
+ @abstractmethod
+ def error(self, name: str, message: str, ex: Exception = None): pass
+
+ @abstractmethod
+ def fatal(self, name: str, message: str, ex: Exception = None): pass
diff --git a/src/sh_edraft/logging/logger.py b/src/sh_edraft/logging/logger.py
new file mode 100644
index 00000000..6668132e
--- /dev/null
+++ b/src/sh_edraft/logging/logger.py
@@ -0,0 +1,195 @@
+import datetime
+import os
+import traceback
+from string import Template
+
+from sh_edraft.hosting.base.application_runtime_base import ApplicationRuntimeBase
+from sh_edraft.logging.base.logger_base import LoggerBase
+from sh_edraft.logging.model.logging_settings import LoggingSettings
+from sh_edraft.logging.model.logging_level import LoggingLevel
+from sh_edraft.time.model.time_format_settings import TimeFormatSettings
+from sh_edraft.console.console import Console
+from sh_edraft.console.model.foreground_color import ForegroundColor
+
+
+class Logger(LoggerBase):
+
+ def __init__(self, logging_settings: LoggingSettings, time_format: TimeFormatSettings, app_runtime: ApplicationRuntimeBase):
+ LoggerBase.__init__(self)
+
+ self._app_runtime = app_runtime
+ self._log_settings: LoggingSettings = logging_settings
+ self._time_format_settings: TimeFormatSettings = time_format
+
+ self._log = Template(self._log_settings.filename).substitute(
+ date_time_now=self._app_runtime.date_time_now.strftime(self._time_format_settings.date_time_format),
+ start_time=self._app_runtime.start_time.strftime(self._time_format_settings.date_time_log_format)
+ )
+ self._path = self._log_settings.path
+ self._level = self._log_settings.level
+ self._console = self._log_settings.console
+
+ self.create()
+
+ def _get_datetime_now(self) -> str:
+ try:
+ return datetime.datetime.now().strftime(self._time_format_settings.date_time_format)
+ except Exception as e:
+ self.error(__name__, 'Cannot get time', ex=e)
+
+ def _get_date(self) -> str:
+ try:
+ return datetime.datetime.now().strftime(self._time_format_settings.date_format)
+ except Exception as e:
+ self.error(__name__, 'Cannot get date', ex=e)
+
+ def create(self) -> None:
+ """ path """
+ try:
+ # check if log file path exists
+ if not os.path.exists(self._path):
+ os.makedirs(self._path)
+ except Exception as e:
+ self._fatal_console(__name__, 'Cannot create log dir', ex=e)
+
+ """ create new log file """
+ try:
+ # open log file, create if not exists
+ path = f'{self._path}{self._log}'
+ f = open(path, "w+")
+ Console.write_line(f'[{__name__}]: Using log file: {path}')
+ f.close()
+ except Exception as e:
+ self._fatal_console(__name__, 'Cannot open log file', ex=e)
+
+ def _append_log(self, string):
+ try:
+ # open log file and append always
+ if not os.path.isdir(self._path):
+ self._fatal_console(__name__, 'Log directory not found')
+
+ with open(self._path + self._log, "a+", encoding="utf-8") as f:
+ f.write(string + '\n')
+ f.close()
+ except Exception as e:
+ self._fatal_console(__name__, f'Cannot append log file, message: {string}', ex=e)
+
+ def _get_string(self, name: str, level: LoggingLevel, message: str) -> str:
+ log_level = level.name
+ return f'<{self._get_datetime_now()}> [ {log_level} ] [ {name} ]: {message}'
+
+ def header(self, string: str):
+ # append log and print message
+ self._append_log(string)
+ Console.set_foreground_color(ForegroundColor.default)
+ Console.write_line(string)
+ Console.set_foreground_color(ForegroundColor.default)
+
+ def trace(self, name: str, message: str):
+ output = self._get_string(name, LoggingLevel.TRACE, message)
+
+ # check if message can be written to log
+ if self._level.value >= LoggingLevel.TRACE.value:
+ self._append_log(output)
+
+ # check if message can be shown in console
+ if self._console.value >= LoggingLevel.TRACE.value:
+ Console.set_foreground_color(ForegroundColor.green)
+ Console.write_line(output)
+ Console.set_foreground_color(ForegroundColor.default)
+
+ def debug(self, name: str, message: str):
+ output = self._get_string(name, LoggingLevel.DEBUG, message)
+
+ # check if message can be written to log
+ if self._level.value >= LoggingLevel.DEBUG.value:
+ self._append_log(output)
+
+ # check if message can be shown in console
+ if self._console.value >= LoggingLevel.DEBUG.value:
+ Console.set_foreground_color(ForegroundColor.green)
+ Console.write_line(output)
+ Console.set_foreground_color(ForegroundColor.default)
+
+ def info(self, name: str, message: str):
+ output = self._get_string(name, LoggingLevel.INFO, message)
+
+ # check if message can be written to log
+ if self._level.value >= LoggingLevel.INFO.value:
+ self._append_log(output)
+
+ # check if message can be shown in console
+ if self._console.value >= LoggingLevel.INFO.value:
+ Console.set_foreground_color(ForegroundColor.green)
+ Console.write_line(output)
+ Console.set_foreground_color(ForegroundColor.default)
+
+ def warn(self, name: str, message: str):
+ output = self._get_string(name, LoggingLevel.WARN, message)
+
+ # check if message can be written to log
+ if self._level.value >= LoggingLevel.WARN.value:
+ self._append_log(output)
+
+ # check if message can be shown in console
+ if self._console.value >= LoggingLevel.WARN.value:
+ Console.set_foreground_color(ForegroundColor.yellow)
+ Console.write_line(output)
+ Console.set_foreground_color(ForegroundColor.default)
+
+ def error(self, name: str, message: str, ex: Exception = None):
+ output = ''
+ if ex is not None:
+ tb = traceback.format_exc()
+ self.error(name, message)
+ output = self._get_string(name, LoggingLevel.ERROR, f'{ex} -> {tb}')
+ else:
+ output = self._get_string(name, LoggingLevel.ERROR, message)
+
+ # check if message can be written to log
+ if self._level.value >= LoggingLevel.ERROR.value:
+ self._append_log(output)
+
+ # check if message can be shown in console
+ if self._console.value >= LoggingLevel.ERROR.value:
+ Console.set_foreground_color(ForegroundColor.red)
+ Console.write_line(output)
+ Console.set_foreground_color(ForegroundColor.default)
+
+ def fatal(self, name: str, message: str, ex: Exception = None):
+ output = ''
+ if ex is not None:
+ tb = traceback.format_exc()
+ self.error(name, message)
+ output = self._get_string(name, LoggingLevel.FATAL, f'{ex} -> {tb}')
+ else:
+ output = self._get_string(name, LoggingLevel.FATAL, message)
+
+ # check if message can be written to log
+ if self._level.value >= LoggingLevel.FATAL.value:
+ self._append_log(output)
+
+ # check if message can be shown in console
+ if self._console.value >= LoggingLevel.FATAL.value:
+ Console.set_foreground_color(ForegroundColor.red)
+ Console.write_line(output)
+ Console.set_foreground_color(ForegroundColor.default)
+
+ exit()
+
+ def _fatal_console(self, name: str, message: str, ex: Exception = None):
+ output = ''
+ if ex is not None:
+ tb = traceback.format_exc()
+ self.error(name, message)
+ output = self._get_string(name, LoggingLevel.ERROR, f'{ex} -> {tb}')
+ else:
+ output = self._get_string(name, LoggingLevel.ERROR, message)
+
+ # check if message can be shown in console
+ if self._console.value >= LoggingLevel.FATAL.value:
+ Console.set_foreground_color(ForegroundColor.red)
+ Console.write_line(output)
+ Console.set_foreground_color(ForegroundColor.default)
+
+ exit()
diff --git a/src/sh_edraft/logging/model/__init__.py b/src/sh_edraft/logging/model/__init__.py
new file mode 100644
index 00000000..e7bcd608
--- /dev/null
+++ b/src/sh_edraft/logging/model/__init__.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.logging.model
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.logging.model'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .logging_level import LoggingLevel
+from .logging_settings import LoggingSettings
+from .logging_settings_name import LoggingSettingsName
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/logging/model/logging_level.py b/src/sh_edraft/logging/model/logging_level.py
new file mode 100644
index 00000000..ceb7b4c7
--- /dev/null
+++ b/src/sh_edraft/logging/model/logging_level.py
@@ -0,0 +1,12 @@
+from enum import Enum
+
+
+class LoggingLevel(Enum):
+
+ OFF = 0 # Nothing
+ FATAL = 1 # Error that cause exit
+ ERROR = 2 # Non fatal error
+ WARN = 3 # Error that can later be fatal
+ INFO = 4 # Normal information's
+ DEBUG = 5 # Detailed app state
+ TRACE = 6 # Detailed app information's
diff --git a/src/sh_edraft/logging/model/logging_settings.py b/src/sh_edraft/logging/model/logging_settings.py
new file mode 100644
index 00000000..563369cc
--- /dev/null
+++ b/src/sh_edraft/logging/model/logging_settings.py
@@ -0,0 +1,62 @@
+import traceback
+from typing import Optional
+
+from sh_edraft.configuration.base.configuration_model_base import ConfigurationModelBase
+from sh_edraft.logging.model.logging_settings_name import LoggingSettingsName
+from sh_edraft.logging.model.logging_level import LoggingLevel
+from sh_edraft.console.console import Console
+from sh_edraft.console.model.foreground_color import ForegroundColor
+
+
+class LoggingSettings(ConfigurationModelBase):
+
+ def __init__(self):
+ ConfigurationModelBase.__init__(self)
+ self._path: Optional[str] = None
+ self._filename: Optional[str] = None
+ self._console: Optional[LoggingLevel] = None
+ self._level: Optional[LoggingLevel] = None
+
+ @property
+ def path(self) -> str:
+ return self._path
+
+ @path.setter
+ def path(self, path: str) -> None:
+ self._path = path
+
+ @property
+ def filename(self) -> str:
+ return self._filename
+
+ @filename.setter
+ def filename(self, filename: str) -> None:
+ self._filename = filename
+
+ @property
+ def console(self) -> LoggingLevel:
+ return self._console
+
+ @console.setter
+ def console(self, console: LoggingLevel) -> None:
+ self._console = console
+
+ @property
+ def level(self) -> LoggingLevel:
+ return self._level
+
+ @level.setter
+ def level(self, level: LoggingLevel) -> None:
+ self._level = level
+
+ def from_dict(self, settings: dict):
+ try:
+ self._path = settings[LoggingSettingsName.path.value]
+ self._filename = settings[LoggingSettingsName.filename.value]
+ self._console = LoggingLevel[settings[LoggingSettingsName.console_level.value]]
+ self._level = LoggingLevel[settings[LoggingSettingsName.file_level.value]]
+ except Exception as e:
+ Console.set_foreground_color(ForegroundColor.red)
+ Console.write_line(f'[ ERROR ] [ {__name__} ]: Reading error in {self.__name__} settings')
+ Console.write_line(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}')
+ Console.set_foreground_color(ForegroundColor.default)
diff --git a/src/sh_edraft/logging/model/logging_settings_name.py b/src/sh_edraft/logging/model/logging_settings_name.py
new file mode 100644
index 00000000..323e5735
--- /dev/null
+++ b/src/sh_edraft/logging/model/logging_settings_name.py
@@ -0,0 +1,9 @@
+from enum import Enum
+
+
+class LoggingSettingsName(Enum):
+
+ path = 'Path'
+ filename = 'Filename'
+ console_level = 'ConsoleLogLevel'
+ file_level = 'FileLogLevel'
diff --git a/src/sh_edraft/mailing/__init__.py b/src/sh_edraft/mailing/__init__.py
new file mode 100644
index 00000000..db3f827b
--- /dev/null
+++ b/src/sh_edraft/mailing/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.mailing
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.mailing'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.10'
+
+from collections import namedtuple
+
+# imports:
+from .email_client import EMailClient
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=10)
diff --git a/src/sh_edraft/mailing/base/__init__.py b/src/sh_edraft/mailing/base/__init__.py
new file mode 100644
index 00000000..587aab2c
--- /dev/null
+++ b/src/sh_edraft/mailing/base/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.mailing.base
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.mailing.base'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.10'
+
+from collections import namedtuple
+
+# imports:
+from .email_client_base import EMailClientBase
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=10)
diff --git a/src/sh_edraft/mailing/base/email_client_base.py b/src/sh_edraft/mailing/base/email_client_base.py
new file mode 100644
index 00000000..bbb98879
--- /dev/null
+++ b/src/sh_edraft/mailing/base/email_client_base.py
@@ -0,0 +1,17 @@
+from abc import abstractmethod
+
+from sh_edraft.mailing.model.email import EMail
+from sh_edraft.service.base.service_base import ServiceBase
+
+
+class EMailClientBase(ServiceBase):
+
+ @abstractmethod
+ def __init__(self):
+ ServiceBase.__init__(self)
+
+ @abstractmethod
+ def connect(self): pass
+
+ @abstractmethod
+ def send_mail(self, email: EMail): pass
diff --git a/src/sh_edraft/mailing/email_client.py b/src/sh_edraft/mailing/email_client.py
new file mode 100644
index 00000000..cc6296ee
--- /dev/null
+++ b/src/sh_edraft/mailing/email_client.py
@@ -0,0 +1,71 @@
+import ssl
+from smtplib import SMTP
+from typing import Optional
+
+from sh_edraft.environment.base.environment_base import EnvironmentBase
+from sh_edraft.logging.base.logger_base import LoggerBase
+from sh_edraft.mailing.base.email_client_base import EMailClientBase
+from sh_edraft.mailing.model.email import EMail
+from sh_edraft.mailing.model.email_client_settings import EMailClientSettings
+from sh_edraft.service.base.service_base import ServiceBase
+from sh_edraft.utils.credential_manager import CredentialManager
+
+
+class EMailClient(EMailClientBase):
+
+ def __init__(self, environment: EnvironmentBase, logger: LoggerBase, mail_settings: EMailClientSettings):
+ ServiceBase.__init__(self)
+
+ self._environment = environment
+ self._mail_settings = mail_settings
+ self._logger = logger
+
+ self._server: Optional[SMTP] = None
+
+ self.create()
+
+ def create(self):
+ self._logger.trace(__name__, f'Started {__name__}.create')
+ self.connect()
+ self._logger.trace(__name__, f'Stopped {__name__}.create')
+
+ def connect(self):
+ self._logger.trace(__name__, f'Started {__name__}.connect')
+ try:
+ self._logger.debug(__name__, f'Try to connect to {self._mail_settings.host}:{self._mail_settings.port}')
+ self._server = SMTP(self._mail_settings.host, self._mail_settings.port)
+ self._logger.info(__name__, f'Connected to {self._mail_settings.host}:{self._mail_settings.port}')
+
+ self._logger.debug(__name__, 'Try to start tls')
+ self._server.starttls(context=ssl.create_default_context())
+ self._logger.info(__name__, 'Started tls')
+ except Exception as e:
+ self._logger.error(__name__, 'Cannot connect to mail server', e)
+
+ self._logger.trace(__name__, f'Stopped {__name__}.connect')
+
+ def login(self):
+ self._logger.trace(__name__, f'Started {__name__}.login')
+ try:
+ self._logger.debug(__name__, f'Try to login {self._mail_settings.user_name}@{self._mail_settings.host}:{self._mail_settings.port}')
+ self._server.login(self._mail_settings.user_name, CredentialManager.decrypt(self._mail_settings.credentials))
+ self._logger.info(__name__, f'Logged on as {self._mail_settings.user_name} to {self._mail_settings.host}:{self._mail_settings.port}')
+ except Exception as e:
+ self._logger.error(__name__, 'Cannot login to mail server', e)
+
+ self._logger.trace(__name__, f'Stopped {__name__}.login')
+
+ def send_mail(self, email: EMail):
+ self._logger.trace(__name__, f'Started {__name__}.send_mail')
+ try:
+ self.login()
+ email.body += f'\n\nDies ist eine automatische E-Mail.' \
+ f'\nGesendet von {self._environment.application_name}-{self._environment.environment_name}@{self._environment.host_name} für ' \
+ f'{self._environment.customer}.'
+
+ self._logger.debug(__name__, f'Try to send email to {email.receiver_list}')
+ self._server.sendmail(self._mail_settings.user_name, email.receiver_list, email.get_content(self._mail_settings.user_name))
+ self._logger.info(__name__, f'Sent email to {email.receiver_list}')
+ except Exception as e:
+ self._logger.error(__name__, f'Cannot send mail to {email.receiver_list}', e)
+ self._logger.trace(__name__, f'Stopped {__name__}.send_mail')
diff --git a/src/sh_edraft/mailing/model/__init__.py b/src/sh_edraft/mailing/model/__init__.py
new file mode 100644
index 00000000..ee1b8eb3
--- /dev/null
+++ b/src/sh_edraft/mailing/model/__init__.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.mailing.model
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.mailing.model'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.10'
+
+from collections import namedtuple
+
+# imports:
+from .email import EMail
+from .email_client_settings_name import EMailClientSettingsName
+from .email_client_settings import EMailClientSettings
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=10)
diff --git a/src/sh_edraft/mailing/model/email.py b/src/sh_edraft/mailing/model/email.py
new file mode 100644
index 00000000..de49639d
--- /dev/null
+++ b/src/sh_edraft/mailing/model/email.py
@@ -0,0 +1,86 @@
+import re
+
+
+class EMail:
+
+ def __init__(self, header: list[str] = None, subject: str = None, body: str = None, transceiver: str = None, receiver: list[str] = None):
+ self._header: list[str] = header
+
+ self._subject: str = subject
+ self._body: str = body
+
+ self._transceiver: str = transceiver
+ self._receiver: list[str] = receiver
+
+ @property
+ def header(self) -> str:
+ return '\r\n'.join(self._header)
+
+ @property
+ def header_list(self) -> list[str]:
+ return self._header
+
+ @header.setter
+ def header(self, header: list[str]):
+ self._header = header
+
+ @property
+ def subject(self) -> str:
+ return self._subject
+
+ @subject.setter
+ def subject(self, subject: str):
+ self._subject = subject
+
+ @property
+ def body(self) -> str:
+ return self._body
+
+ @body.setter
+ def body(self, body: str):
+ self._body = body
+
+ @property
+ def transceiver(self) -> str:
+ return self._transceiver
+
+ @transceiver.setter
+ def transceiver(self, transceiver: str):
+ if self.check_mail(transceiver):
+ self._transceiver = transceiver
+ else:
+ raise Exception(f'Invalid email: {transceiver}')
+
+ @property
+ def receiver(self) -> str:
+ return ','.join(self._receiver)
+
+ @property
+ def receiver_list(self) -> list[str]:
+ return self._receiver
+
+ @receiver.setter
+ def receiver(self, receiver: list[str]):
+ self._receiver = receiver
+
+ @staticmethod
+ def check_mail(address: str) -> bool:
+ return bool(re.search('^\\w+([.-]?\\w+)*@\\w+([.-]?\\w+)*(.\\w{2,3})+$', address))
+
+ def add_header(self, header: str):
+ if self._header is None:
+ self._header = []
+
+ self._header.append(header)
+
+ def add_receiver(self, receiver: str):
+ if self._receiver is None:
+ self._receiver = []
+
+ if self.check_mail(receiver):
+ self._receiver.append(receiver)
+ else:
+ raise Exception(f'Invalid email: {receiver}')
+
+ def get_content(self, transceiver: str):
+ return str(f'From: {transceiver}\r\nTo: {self.receiver}\r\n{self.header}\r\nSubject: {self.subject}\r\n{self.body}').encode('utf-8')
diff --git a/src/sh_edraft/mailing/model/email_client_settings.py b/src/sh_edraft/mailing/model/email_client_settings.py
new file mode 100644
index 00000000..d9755919
--- /dev/null
+++ b/src/sh_edraft/mailing/model/email_client_settings.py
@@ -0,0 +1,59 @@
+import traceback
+
+from sh_edraft.configuration.base.configuration_model_base import ConfigurationModelBase
+from sh_edraft.console.console import Console
+from sh_edraft.mailing.model.email_client_settings_name import EMailClientSettingsName
+
+
+class EMailClientSettings(ConfigurationModelBase):
+
+ def __init__(self):
+ ConfigurationModelBase.__init__(self)
+
+ self._host: str = ''
+ self._port: int = 0
+ self._user_name: str = ''
+ self._credentials: str = ''
+
+ @property
+ def host(self) -> str:
+ return self._host
+
+ @host.setter
+ def host(self, host: str) -> None:
+ self._host = host
+
+ @property
+ def port(self) -> int:
+ return self._port
+
+ @port.setter
+ def port(self, port: int) -> None:
+ self._port = port
+
+ @property
+ def user_name(self) -> str:
+ return self._user_name
+
+ @user_name.setter
+ def user_name(self, user_name: str) -> None:
+ self._user_name = user_name
+
+ @property
+ def credentials(self) -> str:
+ return self._credentials
+
+ @credentials.setter
+ def credentials(self, credentials: str) -> None:
+ self._credentials = credentials
+
+ def from_dict(self, settings: dict):
+ try:
+ self._host = settings[EMailClientSettingsName.host.value]
+ self._port = settings[EMailClientSettingsName.port.value]
+ self._user_name = settings[EMailClientSettingsName.user_name.value]
+ self._credentials = settings[EMailClientSettingsName.credentials.value]
+ except Exception as e:
+ Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {self.__name__} settings')
+ Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}')
+
diff --git a/src/sh_edraft/mailing/model/email_client_settings_name.py b/src/sh_edraft/mailing/model/email_client_settings_name.py
new file mode 100644
index 00000000..665c64b6
--- /dev/null
+++ b/src/sh_edraft/mailing/model/email_client_settings_name.py
@@ -0,0 +1,9 @@
+from enum import Enum
+
+
+class EMailClientSettingsName(Enum):
+
+ host = 'Host'
+ port = 'Port'
+ user_name = 'UserName'
+ credentials = 'Credentials'
diff --git a/src/sh_edraft/publish/base/publisher_base.py b/src/sh_edraft/publish/base/publisher_base.py
new file mode 100644
index 00000000..092dec3d
--- /dev/null
+++ b/src/sh_edraft/publish/base/publisher_base.py
@@ -0,0 +1,30 @@
+from abc import abstractmethod
+
+from sh_edraft.service.base.service_base import ServiceBase
+
+
+class PublisherBase(ServiceBase):
+
+ @abstractmethod
+ def __init__(self):
+ ServiceBase.__init__(self)
+
+ @property
+ @abstractmethod
+ def source_path(self) -> str: pass
+
+ @property
+ @abstractmethod
+ def dist_path(self) -> str: pass
+
+ @abstractmethod
+ def include(self, path: str): pass
+
+ @abstractmethod
+ def exclude(self, path: str): pass
+
+ @abstractmethod
+ def build(self): pass
+
+ @abstractmethod
+ def publish(self): pass
diff --git a/src/sh_edraft/publish/model/publish_settings_model.py b/src/sh_edraft/publish/model/publish_settings_model.py
new file mode 100644
index 00000000..3a1f8ce5
--- /dev/null
+++ b/src/sh_edraft/publish/model/publish_settings_model.py
@@ -0,0 +1,90 @@
+import traceback
+from typing import Optional
+
+from sh_edraft.configuration.base.configuration_model_base import ConfigurationModelBase
+from sh_edraft.publish.model.template import Template
+from sh_edraft.publish.model.publish_settings_name import PublishSettingsName
+from sh_edraft.console.console import Console
+from sh_edraft.console.model.foreground_color import ForegroundColor
+
+
+class PublishSettings(ConfigurationModelBase):
+
+ def __init__(self):
+ ConfigurationModelBase.__init__(self)
+
+ self._source_path: Optional[str] = None
+ self._dist_path: Optional[str] = None
+ self._templates: list[Template] = []
+
+ self._included_files: list[str] = []
+ self._excluded_files: list[str] = []
+
+ self._template_ending: Optional[str] = None
+
+ @property
+ def source_path(self) -> str:
+ return self._source_path
+
+ @source_path.setter
+ def source_path(self, source_path: str):
+ self._source_path = source_path
+
+ @property
+ def dist_path(self) -> str:
+ return self._dist_path
+
+ @dist_path.setter
+ def dist_path(self, dist_path: str):
+ self._dist_path = dist_path
+
+ @property
+ def templates(self) -> list[Template]:
+ return self._templates
+
+ @templates.setter
+ def templates(self, templates: list[Template]):
+ self._templates = templates
+
+ @property
+ def included_files(self) -> list[str]:
+ return self._included_files
+
+ @included_files.setter
+ def included_files(self, included_files: list[str]):
+ self._included_files = included_files
+
+ @property
+ def excluded_files(self) -> list[str]:
+ return self._excluded_files
+
+ @excluded_files.setter
+ def excluded_files(self, excluded_files: list[str]):
+ self._excluded_files = excluded_files
+
+ @property
+ def template_ending(self) -> str:
+ return self._template_ending
+
+ @template_ending.setter
+ def template_ending(self, template_ending: str):
+ self._template_ending = template_ending
+
+ def from_dict(self, settings: dict):
+ try:
+ self._source_path = settings[PublishSettingsName.source_path.value]
+ self._dist_path = settings[PublishSettingsName.dist_path.value]
+ for template in settings[PublishSettingsName.templates.value]:
+ temp = Template()
+ temp.from_dict(template)
+ self._templates.append(temp)
+
+ self._included_files = settings[PublishSettingsName.included_files.value]
+ self._excluded_files = settings[PublishSettingsName.excluded_files.value]
+ self._template_ending = settings[PublishSettingsName.template_ending.value]
+ except Exception as e:
+ Console.set_foreground_color(ForegroundColor.red)
+ Console.write_line(
+ f'[ ERROR ] [ {__name__} ]: Reading error in {PublishSettingsName.publish.value} settings')
+ Console.write_line(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}')
+ Console.set_foreground_color(ForegroundColor.default)
diff --git a/src/sh_edraft/publish/model/publish_settings_name.py b/src/sh_edraft/publish/model/publish_settings_name.py
new file mode 100644
index 00000000..80033c68
--- /dev/null
+++ b/src/sh_edraft/publish/model/publish_settings_name.py
@@ -0,0 +1,12 @@
+from enum import Enum
+
+
+class PublishSettingsName(Enum):
+
+ publish = 'Publish'
+ source_path = 'SourcePath'
+ dist_path = 'DistPath'
+ templates = 'Templates'
+ included_files = 'IncludedFiles'
+ excluded_files = 'ExcludedFiles'
+ template_ending = 'TemplateEnding'
diff --git a/src/sh_edraft/publish/model/template.py b/src/sh_edraft/publish/model/template.py
new file mode 100644
index 00000000..b98a4738
--- /dev/null
+++ b/src/sh_edraft/publish/model/template.py
@@ -0,0 +1,122 @@
+from typing import Optional
+
+from sh_edraft.coding.model.version import Version
+from sh_edraft.configuration.base.configuration_model_base import ConfigurationModelBase
+from sh_edraft.publish.model.template_enum import TemplateEnum
+
+
+class Template(ConfigurationModelBase):
+
+ def __init__(
+ self,
+ template_path: Optional[str] = None,
+ name: Optional[str] = None,
+ description: Optional[str] = None,
+ long_description: Optional[str] = None,
+ copyright_date: Optional[str] = None,
+ copyright_name: Optional[str] = None,
+ license_name: Optional[str] = None,
+ license_description: Optional[str] = None,
+ title: Optional[str] = None,
+ author: Optional[str] = None,
+ version: Optional[Version] = Version()
+ ):
+ ConfigurationModelBase.__init__(self)
+ self._template_path: Optional[str] = template_path
+ self._name: Optional[str] = name
+ self._description: Optional[str] = description
+ self._long_description: Optional[str] = long_description
+ self._copyright_date: Optional[str] = copyright_date
+ self._copyright_name: Optional[str] = copyright_name
+ self._license_name: Optional[str] = license_name
+ self._license_description: Optional[str] = license_description
+ self._title: Optional[str] = title
+ self._author: Optional[str] = author
+
+ self._version: Optional[Version] = version
+
+ self._file_content: Optional[str] = None
+
+ @property
+ def template_path(self) -> Optional[str]:
+ return self._template_path
+
+ @property
+ def name(self) -> Optional[str]:
+ return self._name
+
+ @property
+ def description(self) -> Optional[str]:
+ return self._description
+
+ @property
+ def long_description(self) -> Optional[str]:
+ return self._long_description
+
+ @property
+ def copyright_date(self) -> Optional[str]:
+ return self._copyright_date
+
+ @property
+ def copyright_name(self) -> Optional[str]:
+ return self._copyright_name
+
+ @property
+ def license_name(self) -> Optional[str]:
+ return self._license_name
+
+ @property
+ def license_description(self) -> Optional[str]:
+ return self._license_description
+
+ @property
+ def title(self) -> Optional[str]:
+ return self._title
+
+ @property
+ def author(self) -> Optional[str]:
+ return self._author
+
+ @property
+ def version(self) -> Optional[Version]:
+ return self._version
+
+ @property
+ def file_content(self) -> Optional[str]:
+ return self._file_content
+
+ @file_content.setter
+ def file_content(self, file_content: Optional[str]):
+ self._file_content = file_content
+
+ def from_dict(self, settings: dict):
+ self._template_path = settings[TemplateEnum.TemplatePath.value]
+ self._name = settings[TemplateEnum.Name.value]
+ self._description = settings[TemplateEnum.Description.value]
+ self._long_description = settings[TemplateEnum.LongDescription.value]
+ self._copyright_date = settings[TemplateEnum.CopyrightDate.value]
+ self._copyright_name = settings[TemplateEnum.CopyrightName.value]
+ self._license_name = settings[TemplateEnum.LicenseName.value]
+ self._license_description = settings[TemplateEnum.LicenseDescription.value]
+ self._title = settings[TemplateEnum.Title.value]
+ self._author = settings[TemplateEnum.Author.value]
+ self._version.from_dict(settings[TemplateEnum.Version.value])
+
+ def to_dict(self) -> dict:
+ version: Optional[dict] = None
+ if self._version is not None:
+ version = self._version.to_dict()
+
+ return {
+ TemplateEnum.TemplatePath.value: self._template_path,
+ TemplateEnum.Name.value: self._name,
+ TemplateEnum.Description.value: self._description,
+ TemplateEnum.LongDescription.value: self._long_description,
+ TemplateEnum.CopyrightDate.value: self._copyright_date,
+ TemplateEnum.CopyrightName.value: self._copyright_name,
+ TemplateEnum.LicenseName.value: self._license_name,
+ TemplateEnum.LicenseDescription.value: self._license_description,
+ TemplateEnum.Title.value: self._title,
+ TemplateEnum.Author.value: self._author,
+ TemplateEnum.Version.value: version
+ }
diff --git a/src/sh_edraft/publish/model/template_enum.py b/src/sh_edraft/publish/model/template_enum.py
new file mode 100644
index 00000000..f628c4ce
--- /dev/null
+++ b/src/sh_edraft/publish/model/template_enum.py
@@ -0,0 +1,16 @@
+from enum import Enum
+
+
+class TemplateEnum(Enum):
+
+ TemplatePath = 'TemplatePath'
+ Name = 'Name'
+ Description = 'Description'
+ LongDescription = 'LongDescription'
+ CopyrightDate = 'CopyrightDate'
+ CopyrightName = 'CopyrightName'
+ LicenseName = 'LicenseName'
+ LicenseDescription = 'LicenseDescription'
+ Title = 'Title'
+ Author = 'Author'
+ Version = 'Version'
diff --git a/src/sh_edraft/publish/publisher.py b/src/sh_edraft/publish/publisher.py
new file mode 100644
index 00000000..2ddd2b15
--- /dev/null
+++ b/src/sh_edraft/publish/publisher.py
@@ -0,0 +1,279 @@
+import os
+import shutil
+from string import Template as stringTemplate
+
+from setuptools import sandbox
+
+from sh_edraft.logging.base.logger_base import LoggerBase
+from sh_edraft.publish.base.publisher_base import PublisherBase
+from sh_edraft.publish.model.publish_settings_model import PublishSettings
+from sh_edraft.publish.model.template import Template
+
+
+class Publisher(PublisherBase):
+
+ def __init__(self, logger: LoggerBase, publish_settings: PublishSettings):
+ PublisherBase.__init__(self)
+
+ self._logger: LoggerBase = logger
+ self._publish_settings: PublishSettings = publish_settings
+
+ self._included_files: list[str] = []
+
+ @property
+ def source_path(self) -> str:
+ return self._publish_settings.source_path
+
+ @property
+ def dist_path(self):
+ return self._publish_settings.dist_path
+
+ def _get_template_output(self, t: Template, name: str, imports: str) -> str:
+ self._logger.trace(__name__, f'Started {__name__}._get_template_output')
+ try:
+ if t.file_content == '':
+ raise Exception(f'Template is empty: {t.template_path}')
+
+ self._logger.trace(__name__, f'Stopped {__name__}._get_template_output')
+ return stringTemplate(t.file_content).substitute(
+ Name=name,
+ Description=t.description,
+ LongDescription=t.long_description,
+ CopyrightDate=t.copyright_date,
+ CopyrightName=t.copyright_name,
+ LicenseName=t.license_name,
+ LicenseDescription=t.license_description,
+ Title=t.title if t.title is not None and t.title != '' else name,
+ Author=t.author,
+ Version=t.version.to_str(),
+ Major=t.version.major,
+ Minor=t.version.minor,
+ Micro=t.version.micro,
+ Imports=imports
+ )
+ except Exception as e:
+ self._logger.fatal(__name__, f'Cannot read Template: {t.template_path}', e)
+ self._logger.trace(__name__, f'Stopped {__name__}._get_template_output')
+
+ def _read_source_path(self):
+ self._logger.trace(__name__, f'Started {__name__}._read_source_path')
+ included_files = self._publish_settings.included_files
+ for included in included_files:
+ if os.path.isdir(included):
+ self._publish_settings.included_files.remove(included)
+
+ for r, d, f in os.walk(included):
+ for file in f:
+ rel_path = os.path.relpath(r)
+ if not rel_path.startswith('.'):
+ rel_path = f'./{rel_path}'
+
+ file_path = os.path.join(self._publish_settings.source_path, r, file)
+ if os.path.isfile(file_path):
+ self._included_files.append(file_path)
+ elif os.path.isfile(os.path.join(rel_path, file)):
+ self._included_files.append(os.path.join(rel_path, file))
+ else:
+ self._logger.fatal(__name__, f'File not found: {file}')
+
+ elif os.path.isfile(included):
+ self._included_files.append(included)
+ else:
+ self._logger.fatal(__name__, f'File not found: {included}')
+
+ for r, d, f in os.walk(self._publish_settings.source_path):
+ for file in f:
+ is_file_excluded = False
+ if os.path.join(r, file) in self._publish_settings.excluded_files:
+ is_file_excluded = True
+ else:
+ for excluded in self._publish_settings.excluded_files:
+ if os.path.join(r, file).__contains__(excluded):
+ is_file_excluded = True
+
+ if not is_file_excluded and file.endswith('.py') or file in self._publish_settings.included_files:
+ self._included_files.append(os.path.join(r, file))
+
+ self._logger.trace(__name__, f'Stopped {__name__}._read_source_path')
+
+ def _read_templates(self):
+ self._logger.trace(__name__, f'Started {__name__}._read_templates')
+ for t in self._publish_settings.templates:
+ output_template: str = ''
+ if not os.path.isfile(t.template_path):
+ self._logger.fatal(__name__, f'Template not found: {t.template_path}')
+
+ with open(t.template_path) as template:
+ t.file_content = template.read()
+ template.close()
+ if t.file_content == '':
+ self._logger.fatal(__name__, f'Template is empty: {t.template_path}')
+
+ self._logger.trace(__name__, f'Stopped {__name__}._read_templates')
+
+ def _create_dist_path(self):
+ self._logger.trace(__name__, f'Started {__name__}._create_dist_path')
+ if os.path.isdir(self._publish_settings.dist_path):
+ try:
+ shutil.rmtree(self._publish_settings.dist_path)
+ self._logger.info(__name__, f'Deleted {self._publish_settings.dist_path}')
+ except Exception as e:
+ self._logger.fatal(__name__, f'Cannot delete old dist directory', e)
+
+ if not os.path.isdir(self._publish_settings.dist_path):
+ try:
+ os.makedirs(self._publish_settings.dist_path)
+ self._logger.debug(__name__, f'Created directories: {self._publish_settings.dist_path}')
+ self._logger.info(__name__, f'Created dist directory')
+ except Exception as e:
+ self._logger.fatal(__name__, f'Cannot create dist directory', e)
+
+ self._logger.trace(__name__, f'Stopped {__name__}._create_dist_path')
+
+ @staticmethod
+ def _get_template_name_from_dirs(file: str) -> str:
+ dirs = os.path.dirname(file).split('/')
+ for d in dirs:
+ if d.__contains__('.'):
+ dirs.remove(d)
+
+ if len(dirs) == 0:
+ return os.path.basename(file)
+ else:
+ return '.'.join(dirs)
+
+ def _write_templates(self):
+ self._logger.trace(__name__, f'Started {__name__}._write_templates')
+ for template in self._publish_settings.templates:
+ for file in self._included_files:
+ if os.path.basename(file) == '__init__.py' and file not in self._publish_settings.excluded_files:
+ template_name = template.name
+ if template.name == 'all' or template.name == '':
+ template_name = self._get_template_name_from_dirs(file)
+ else:
+ name = self._get_template_name_from_dirs(file)
+
+ if name.__contains__('.'):
+ if template.name != name.split('.')[len(name.split('.')) - 1]:
+ continue
+
+ else:
+ if template.name != name:
+ continue
+
+ try:
+ module_file_lines: list[str] = []
+ module_py_lines: list[str] = []
+ imports = ''
+ with open(file, 'r') as py_file:
+ module_file_lines = py_file.readlines()
+ py_file.close()
+
+ if len(module_file_lines) == 0:
+ with open(file, 'w+') as py_file:
+ py_file.write(self._get_template_output(template, template_name, '# imports:'))
+ py_file.close()
+ self._logger.debug(__name__, f'Written to {file}')
+ else:
+ is_started = False
+ for line in module_file_lines:
+ if line.__contains__('# imports'):
+ is_started = True
+
+ if (line.__contains__('from') or line.__contains__('import')) and is_started:
+ module_py_lines.append(line.replace('\n', ''))
+
+ if len(module_py_lines) > 0:
+ imports = '\n'.join(module_py_lines)
+
+ with open(file, 'w+') as py_file:
+ py_file.write(self._get_template_output(template, template_name, imports))
+ py_file.close()
+ self._logger.debug(__name__, f'Written to {file}')
+
+ except Exception as e:
+ self._logger.error(__name__, f'Cannot write to file: {file}', e)
+
+ self._logger.info(__name__, f'Written to all included modules')
+ self._logger.trace(__name__, f'Stopped {__name__}._write_templates')
+
+ def _copy_all_included_files(self):
+ self._logger.trace(__name__, f'Started {__name__}._copy_all_included_files')
+ dist_path = self._publish_settings.dist_path
+ if self._publish_settings.dist_path.endswith('/'):
+ dist_path = dist_path[:len(dist_path) - 1]
+
+ for file in self._included_files:
+ is_file_excluded = False
+ if file in self._publish_settings.excluded_files:
+ is_file_excluded = True
+ else:
+ for excluded in self._publish_settings.excluded_files:
+ if file.__contains__(excluded):
+ is_file_excluded = True
+
+ if not is_file_excluded:
+ output_file = ''
+
+ if file.startswith('..'):
+ output_file = file.replace('..', '')
+ elif file.startswith('.'):
+ output_file = file.replace('.', '', 1)
+
+ if output_file.__contains__('..'):
+ output_file = os.path.join(dist_path, os.path.basename(file))
+ else:
+ output_file = f'{dist_path}{output_file}'
+
+ output_path = os.path.dirname(output_file)
+
+ try:
+ if not os.path.isdir(output_path):
+ os.makedirs(output_path, exist_ok=True)
+ except Exception as e:
+ self._logger.error(__name__, f'Cannot create directories: {output_path}', e)
+
+ try:
+ shutil.copy(file, output_file)
+ except Exception as e:
+ self._logger.error(__name__, f'Cannot copy file: {file} to {output_path}', e)
+
+ self._logger.debug(__name__, f'Copied {file} to {output_path}')
+
+ self._logger.info(__name__, f'Copied all included files')
+ self._logger.trace(__name__, f'Stopped {__name__}._copy_all_included_files')
+
+ def include(self, path: str):
+ self._logger.trace(__name__, f'Started {__name__}.include')
+ self._publish_settings.included_files.append(path)
+ self._logger.trace(__name__, f'Stopped {__name__}.include')
+
+ def exclude(self, path: str):
+ self._logger.trace(__name__, f'Started {__name__}.exclude')
+ self._publish_settings.excluded_files.append(path)
+ self._logger.trace(__name__, f'Stopped {__name__}.exclude')
+
+ def create(self):
+ self._logger.trace(__name__, f'Started {__name__}.create')
+ if not self._publish_settings.dist_path.endswith('/'):
+ self._publish_settings.dist_path += '/'
+
+ self._read_source_path()
+ self._read_templates()
+ self._create_dist_path()
+ self._logger.trace(__name__, f'Stopped {__name__}.create')
+
+ def build(self):
+ self._logger.trace(__name__, f'Started {__name__}.build')
+ self._write_templates()
+ self._copy_all_included_files()
+ self._logger.trace(__name__, f'Stopped {__name__}.build')
+
+ def publish(self):
+ self._logger.trace(__name__, f'Started {__name__}.publish')
+ setup_py = os.path.join(self._publish_settings.dist_path, 'setup.py')
+ if not os.path.isfile(setup_py):
+ self._logger.fatal(__name__, f'setup.py not found in {self._publish_settings.dist_path}')
+
+ sandbox.run_setup(os.path.abspath(setup_py), ['sdist', 'bdist_wheel'])
+ self._logger.trace(__name__, f'Stopped {__name__}.publish')
diff --git a/src/sh_edraft/publishing/__init__.py b/src/sh_edraft/publishing/__init__.py
new file mode 100644
index 00000000..62c88c4e
--- /dev/null
+++ b/src/sh_edraft/publishing/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.publishing
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.publishing'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.10'
+
+from collections import namedtuple
+
+# imports:
+from .publisher import Publisher
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=10)
diff --git a/src/sh_edraft/publishing/base/__init__.py b/src/sh_edraft/publishing/base/__init__.py
new file mode 100644
index 00000000..a462ce7c
--- /dev/null
+++ b/src/sh_edraft/publishing/base/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.publishing.base
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.publishing.base'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.10'
+
+from collections import namedtuple
+
+# imports:
+from .publisher_base import PublisherBase
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=10)
diff --git a/src/sh_edraft/publishing/model/__init__.py b/src/sh_edraft/publishing/model/__init__.py
new file mode 100644
index 00000000..54283917
--- /dev/null
+++ b/src/sh_edraft/publishing/model/__init__.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.publishing.model
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.publishing.model'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.10'
+
+from collections import namedtuple
+
+# imports:
+from .template import Template
+from .template_enum import TemplateEnum
+from .publish_settings_model import PublishSettings
+from .publish_settings_name import PublishSettingsName
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=10)
diff --git a/src/sh_edraft/service/__init__.py b/src/sh_edraft/service/__init__.py
new file mode 100644
index 00000000..7d487f52
--- /dev/null
+++ b/src/sh_edraft/service/__init__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.service
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.service'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/service/base/__init__.py b/src/sh_edraft/service/base/__init__.py
new file mode 100644
index 00000000..73148fb0
--- /dev/null
+++ b/src/sh_edraft/service/base/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.service.base
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.service.base'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .service_base import ServiceBase
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/service/base/service_base.py b/src/sh_edraft/service/base/service_base.py
new file mode 100644
index 00000000..e135c2a9
--- /dev/null
+++ b/src/sh_edraft/service/base/service_base.py
@@ -0,0 +1,10 @@
+from abc import ABC, abstractmethod
+
+
+class ServiceBase(ABC):
+
+ @abstractmethod
+ def __init__(self): pass
+
+ @abstractmethod
+ def create(self): pass
diff --git a/src/sh_edraft/service/providing/__init__.py b/src/sh_edraft/service/providing/__init__.py
new file mode 100644
index 00000000..a532dc9d
--- /dev/null
+++ b/src/sh_edraft/service/providing/__init__.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.service.providing
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.service.providing'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.10'
+
+from collections import namedtuple
+
+# imports:
+from .service_provider import ServiceProvider
+from .service_provider import ServiceProviderBase
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=10)
diff --git a/src/sh_edraft/service/providing/base/__init__.py b/src/sh_edraft/service/providing/base/__init__.py
new file mode 100644
index 00000000..740e8631
--- /dev/null
+++ b/src/sh_edraft/service/providing/base/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.service.providing.base
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.service.providing.base'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .service_provider_base import ServiceProviderBase
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/service/providing/base/service_provider_base.py b/src/sh_edraft/service/providing/base/service_provider_base.py
new file mode 100644
index 00000000..28c986f0
--- /dev/null
+++ b/src/sh_edraft/service/providing/base/service_provider_base.py
@@ -0,0 +1,33 @@
+from abc import abstractmethod, ABC
+from collections import Callable
+from typing import Type
+
+from sh_edraft.database.context.base.database_context_base import DatabaseContextBase
+from sh_edraft.service.base.service_base import ServiceBase
+
+
+class ServiceProviderBase(ABC):
+
+ @abstractmethod
+ def __init__(self): pass
+
+ @abstractmethod
+ def add_db_context(self, db_context: Type[DatabaseContextBase]): pass
+
+ @abstractmethod
+ def get_db_context(self) -> Callable[DatabaseContextBase]: pass
+
+ @abstractmethod
+ def add_transient(self, service_type: Type[ServiceBase], service: Type[ServiceBase]): pass
+
+ @abstractmethod
+ def add_scoped(self, service_type: Type[ServiceBase], service: Type[ServiceBase]): pass
+
+ @abstractmethod
+ def add_singleton(self, service_type: Type[ServiceBase], service: Callable[ServiceBase]): pass
+
+ @abstractmethod
+ def get_service(self, instance_type: Type[ServiceBase]) -> Callable[ServiceBase]: pass
+
+ @abstractmethod
+ def remove_service(self, instance_type: type): pass
diff --git a/src/sh_edraft/service/providing/model/__init__.py b/src/sh_edraft/service/providing/model/__init__.py
new file mode 100644
index 00000000..779edb8d
--- /dev/null
+++ b/src/sh_edraft/service/providing/model/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.service.providing.model
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.service.providing.model'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .provide_state import ProvideState
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/service/providing/model/provide_state.py b/src/sh_edraft/service/providing/model/provide_state.py
new file mode 100644
index 00000000..52d0af05
--- /dev/null
+++ b/src/sh_edraft/service/providing/model/provide_state.py
@@ -0,0 +1,18 @@
+from typing import Type
+
+from sh_edraft.service.base.service_base import ServiceBase
+
+
+class ProvideState:
+
+ def __init__(self, service: Type[ServiceBase] = None, args: tuple = None):
+ self._service: Type[ServiceBase] = service
+ self._args: tuple = args
+
+ @property
+ def service(self):
+ return self._service
+
+ @property
+ def args(self) -> tuple:
+ return self._args
diff --git a/src/sh_edraft/service/providing/service_provider.py b/src/sh_edraft/service/providing/service_provider.py
new file mode 100644
index 00000000..2ba39e4c
--- /dev/null
+++ b/src/sh_edraft/service/providing/service_provider.py
@@ -0,0 +1,95 @@
+from collections import Callable
+from inspect import signature, Parameter
+from typing import Type, Optional
+
+from sh_edraft.configuration.base.configuration_model_base import ConfigurationModelBase
+from sh_edraft.database.context.base.database_context_base import DatabaseContextBase
+from sh_edraft.environment.base import EnvironmentBase
+from sh_edraft.hosting.base.application_runtime_base import ApplicationRuntimeBase
+from sh_edraft.service.providing.base.service_provider_base import ServiceProviderBase
+from sh_edraft.service.base.service_base import ServiceBase
+
+
+class ServiceProvider(ServiceProviderBase):
+
+ def __init__(self, app_runtime: ApplicationRuntimeBase):
+ ServiceProviderBase.__init__(self)
+ self._app_runtime: ApplicationRuntimeBase = app_runtime
+ self._database_context: Optional[DatabaseContextBase] = None
+
+ self._transient_services: dict[Type[ServiceBase], Type[ServiceBase]] = {}
+ self._scoped_services: dict[Type[ServiceBase], Type[ServiceBase]] = {}
+ self._singleton_services: dict[Type[ServiceBase], ServiceBase] = {}
+
+ def create(self): pass
+
+ def _create_instance(self, service: Callable[ServiceBase]) -> ServiceBase:
+ sig = signature(service.__init__)
+ params = []
+ for param in sig.parameters.items():
+ parameter = param[1]
+ if parameter.name != 'self' and parameter.annotation != Parameter.empty:
+ if issubclass(parameter.annotation, ApplicationRuntimeBase):
+ params.append(self._app_runtime)
+
+ elif issubclass(parameter.annotation, EnvironmentBase):
+ params.append(self._app_runtime.configuration.environment)
+
+ elif issubclass(parameter.annotation, DatabaseContextBase):
+ params.append(self._database_context)
+
+ elif issubclass(parameter.annotation, ServiceBase):
+ params.append(self.get_service(parameter.annotation))
+
+ elif issubclass(parameter.annotation, ConfigurationModelBase):
+ params.append(self._app_runtime.configuration.get_configuration(parameter.annotation))
+
+ return service(*params)
+
+ def add_db_context(self, db_context: Type[DatabaseContextBase]):
+ self._database_context = self._create_instance(db_context)
+
+ def get_db_context(self) -> Callable[DatabaseContextBase]:
+ return self._database_context
+
+ def add_transient(self, service_type: Type[ServiceBase], service: Type[ServiceBase]):
+ self._transient_services[service_type] = service
+
+ def add_scoped(self, service_type: Type[ServiceBase], service: Type[ServiceBase]):
+ self._scoped_services[service_type] = service
+
+ def add_singleton(self, service_type: Type[ServiceBase], service: Callable[ServiceBase]):
+ for known_service in self._singleton_services:
+ if type(known_service) == service_type:
+ raise Exception(f'Service with type {service_type} already exists')
+
+ self._singleton_services[service_type] = self._create_instance(service)
+
+ def get_service(self, instance_type: Type[ServiceBase]) -> Callable[ServiceBase]:
+ for service in self._transient_services:
+ if service == instance_type and isinstance(self._transient_services[service], type(instance_type)):
+ return self._create_instance(self._transient_services[service])
+
+ for service in self._scoped_services:
+ if service == instance_type and isinstance(self._scoped_services[service], type(instance_type)):
+ return self._create_instance(self._scoped_services[service])
+
+ for service in self._singleton_services:
+ if service == instance_type and isinstance(self._singleton_services[service], instance_type):
+ return self._singleton_services[service]
+
+ def remove_service(self, instance_type: Type[ServiceBase]):
+ for service in self._transient_services:
+ if service == instance_type and isinstance(self._transient_services[service], type(instance_type)):
+ del self._transient_services[service]
+ return
+
+ for service in self._scoped_services:
+ if service == instance_type and isinstance(self._scoped_services[service], type(instance_type)):
+ del self._scoped_services[service]
+ return
+
+ for service in self._singleton_services:
+ if service == instance_type and isinstance(self._singleton_services[service], instance_type):
+ del self._singleton_services[service]
+ return
diff --git a/src/sh_edraft/time/__init__.py b/src/sh_edraft/time/__init__.py
new file mode 100644
index 00000000..d8460af8
--- /dev/null
+++ b/src/sh_edraft/time/__init__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.time
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.time'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/time/model/__init__.py b/src/sh_edraft/time/model/__init__.py
new file mode 100644
index 00000000..1056120c
--- /dev/null
+++ b/src/sh_edraft/time/model/__init__.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.time.model
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.time.model'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .time_format_settings import TimeFormatSettings
+from .time_format_settings_names import TimeFormatSettingsNames
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/time/model/time_format_settings.py b/src/sh_edraft/time/model/time_format_settings.py
new file mode 100644
index 00000000..e4be7511
--- /dev/null
+++ b/src/sh_edraft/time/model/time_format_settings.py
@@ -0,0 +1,61 @@
+import traceback
+from typing import Optional
+
+from sh_edraft.configuration.base.configuration_model_base import ConfigurationModelBase
+from sh_edraft.time.model.time_format_settings_names import TimeFormatSettingsNames
+from sh_edraft.console.console import Console
+from sh_edraft.console.model.foreground_color import ForegroundColor
+
+
+class TimeFormatSettings(ConfigurationModelBase):
+
+ def __init__(self):
+ ConfigurationModelBase.__init__(self)
+ self._date_format: Optional[str] = None
+ self._time_format: Optional[str] = None
+ self._date_time_format: Optional[str] = None
+ self._date_time_log_format: Optional[str] = None
+
+ @property
+ def date_format(self) -> str:
+ return self._date_format
+
+ @date_format.setter
+ def date_format(self, date_format: str) -> None:
+ self._date_format = date_format
+
+ @property
+ def time_format(self) -> str:
+ return self._time_format
+
+ @time_format.setter
+ def time_format(self, time_format: str):
+ self._time_format = time_format
+
+ @property
+ def date_time_format(self) -> str:
+ return self._date_time_format
+
+ @date_time_format.setter
+ def date_time_format(self, date_time_format: str) -> None:
+ self._date_time_format = date_time_format
+
+ @property
+ def date_time_log_format(self):
+ return self._date_time_log_format
+
+ @date_time_log_format.setter
+ def date_time_log_format(self, date_time_now_format: str) -> None:
+ self._date_time_log_format = date_time_now_format
+
+ def from_dict(self, settings: dict):
+ try:
+ self._date_format = settings[TimeFormatSettingsNames.date_format.value]
+ self._time_format = settings[TimeFormatSettingsNames.time_format.value]
+ self._date_time_format = settings[TimeFormatSettingsNames.date_time_format.value]
+ self._date_time_log_format = settings[TimeFormatSettingsNames.date_time_log_format.value]
+ except Exception as e:
+ Console.set_foreground_color(ForegroundColor.red)
+ Console.write_line(f'[ ERROR ] [ {__name__} ]: Reading error in {self.__name__} settings')
+ Console.write_line(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}')
+ Console.set_foreground_color(ForegroundColor.default)
diff --git a/src/sh_edraft/time/model/time_format_settings_names.py b/src/sh_edraft/time/model/time_format_settings_names.py
new file mode 100644
index 00000000..3858de10
--- /dev/null
+++ b/src/sh_edraft/time/model/time_format_settings_names.py
@@ -0,0 +1,9 @@
+from enum import Enum
+
+
+class TimeFormatSettingsNames(Enum):
+
+ date_format = 'DateFormat'
+ time_format = 'TimeFormat'
+ date_time_format = 'DateTimeFormat'
+ date_time_log_format = 'DateTimeLogFormat'
diff --git a/src/sh_edraft/utils/__init__.py b/src/sh_edraft/utils/__init__.py
new file mode 100644
index 00000000..ab011b6e
--- /dev/null
+++ b/src/sh_edraft/utils/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+"""
+sh_edraft.utils
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'sh_edraft.utils'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+from .credential_manager import CredentialManager
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/sh_edraft/utils/credential_manager.py b/src/sh_edraft/utils/credential_manager.py
new file mode 100644
index 00000000..7f9977d1
--- /dev/null
+++ b/src/sh_edraft/utils/credential_manager.py
@@ -0,0 +1,17 @@
+import base64
+
+
+class CredentialManager:
+
+ @staticmethod
+ def encrypt(string: str) -> str:
+ return base64.b64encode(string.encode('utf-8')).decode('utf-8')
+
+ @staticmethod
+ def decrypt(string: str) -> str:
+ return base64.b64decode(string).decode('utf-8')
+
+ @staticmethod
+ def build_string(string: str, credentials: str):
+ return string.replace('$credentials', CredentialManager.decrypt(credentials))
+
diff --git a/src/tests/__init__.py b/src/tests/__init__.py
new file mode 100644
index 00000000..924bb32a
--- /dev/null
+++ b/src/tests/__init__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+
+"""
+tests
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'tests'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/tests/appsettings.edrafts-lapi.json b/src/tests/appsettings.edrafts-lapi.json
new file mode 100644
index 00000000..9af6f6c0
--- /dev/null
+++ b/src/tests/appsettings.edrafts-lapi.json
@@ -0,0 +1,20 @@
+{
+ "TimeFormatSettings": {
+ "DateFormat": "%Y-%m-%d",
+ "TimeFormat": "%H:%M:%S",
+ "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f",
+ "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S"
+ },
+ "LoggingSettings": {
+ "Path": "logs/",
+ "Filename": "log_$start_time.log",
+ "ConsoleLogLevel": "TRACE",
+ "FileLogLevel": "TRACE"
+ },
+ "EMailClientSettings": {
+ "Host": "mail.sh-edraft.de",
+ "Port": "587",
+ "UserName": "dev-srv@sh-edraft.de",
+ "Credentials": "RmBOQX1eNFYiYjgsSid3fV1nelc2WA=="
+ }
+}
\ No newline at end of file
diff --git a/src/tests/appsettings.edrafts-pc.json b/src/tests/appsettings.edrafts-pc.json
new file mode 100644
index 00000000..9af6f6c0
--- /dev/null
+++ b/src/tests/appsettings.edrafts-pc.json
@@ -0,0 +1,20 @@
+{
+ "TimeFormatSettings": {
+ "DateFormat": "%Y-%m-%d",
+ "TimeFormat": "%H:%M:%S",
+ "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f",
+ "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S"
+ },
+ "LoggingSettings": {
+ "Path": "logs/",
+ "Filename": "log_$start_time.log",
+ "ConsoleLogLevel": "TRACE",
+ "FileLogLevel": "TRACE"
+ },
+ "EMailClientSettings": {
+ "Host": "mail.sh-edraft.de",
+ "Port": "587",
+ "UserName": "dev-srv@sh-edraft.de",
+ "Credentials": "RmBOQX1eNFYiYjgsSid3fV1nelc2WA=="
+ }
+}
\ No newline at end of file
diff --git a/src/tests/appsettings.json b/src/tests/appsettings.json
new file mode 100644
index 00000000..fd8ddf6c
--- /dev/null
+++ b/src/tests/appsettings.json
@@ -0,0 +1,15 @@
+{
+ "TimeFormatSettings": {
+ "DateFormat": "%Y-%m-%d",
+ "TimeFormat": "%H:%M:%S",
+ "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f",
+ "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S"
+ },
+
+ "LoggingSettings": {
+ "Path": "logs/",
+ "Filename": "log_$start_time.log",
+ "ConsoleLogLevel": "ERROR",
+ "FileLogLevel": "WARN"
+ }
+}
\ No newline at end of file
diff --git a/src/tests/appsettings.testing.json b/src/tests/appsettings.testing.json
new file mode 100644
index 00000000..d134912e
--- /dev/null
+++ b/src/tests/appsettings.testing.json
@@ -0,0 +1,14 @@
+{
+ "LoggingSettings": {
+ "Path": "logs/",
+ "Filename": "log_$start_time.log",
+ "ConsoleLogLevel": "TRACE",
+ "FileLogLevel": "TRACE"
+ },
+
+ "DatabaseSettings": {
+ "ConnectionString": "mysql+mysqlconnector://sh_cpl:$credentials@localhost/sh_cpl",
+ "Credentials": "MHZhc0Y2bjhKc1VUMWV0Qw==",
+ "Encoding": "utf8mb4"
+ }
+}
\ No newline at end of file
diff --git a/src/tests/build.json b/src/tests/build.json
new file mode 100644
index 00000000..3921c95f
--- /dev/null
+++ b/src/tests/build.json
@@ -0,0 +1,59 @@
+{
+ "TimeFormatSettings": {
+ "DateFormat": "%Y-%m-%d",
+ "TimeFormat": "%H:%M:%S",
+ "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f",
+ "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S"
+ },
+ "LoggingSettings": {
+ "Path": "../../build_test/logs/",
+ "Filename": "log_$start_time.log",
+ "ConsoleLogLevel": "INFO",
+ "FileLogLevel": "TRACE"
+ },
+ "PublishSettings": {
+ "SourcePath": "./",
+ "DistPath": "../../build_test/dist",
+ "Templates": [
+ {
+ "TemplatePath": "../../publish_templates/all_template.txt",
+ "Name": "all",
+ "Description": "",
+ "LongDescription": "",
+ "CopyrightDate": "2020",
+ "CopyrightName": "sh-edraft.de",
+ "LicenseName": "MIT",
+ "LicenseDescription": ", see LICENSE for more details.",
+ "Title": "",
+ "Author": "Sven Heidemann",
+ "Version": {
+ "Major": 2020,
+ "Minor": 12,
+ "Micro": 10
+ }
+ },
+ {
+ "TemplatePath": "../../publish_templates/all_template.txt",
+ "Name": "sh_edraft",
+ "Description": "common python library",
+ "LongDescription": "Library to share common classes and models used at sh-edraft.de",
+ "CopyrightDate": "2020",
+ "CopyrightName": "sh-edraft.de",
+ "LicenseName": "MIT",
+ "LicenseDescription": ", see LICENSE for more details.",
+ "Title": "",
+ "Author": "Sven Heidemann",
+ "Version": {
+ "Major": 2020,
+ "Minor": 12,
+ "Micro": 10
+ }
+ }
+ ],
+ "IncludedFiles": [
+ ],
+ "ExcludedFiles": [
+ ],
+ "TemplateEnding": "_template.txt"
+ }
+}
diff --git a/src/tests/publish_test/__init__.py b/src/tests/publish_test/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/tests/publish_test/publisher_test.py b/src/tests/publish_test/publisher_test.py
new file mode 100644
index 00000000..06f95519
--- /dev/null
+++ b/src/tests/publish_test/publisher_test.py
@@ -0,0 +1,52 @@
+import os
+import unittest
+
+from sh_edraft.console import Console
+from sh_edraft.hosting import ApplicationHost
+from sh_edraft.logging import Logger
+from sh_edraft.logging.base import LoggerBase
+from sh_edraft.publish import Publisher
+from sh_edraft.publish.base import PublisherBase
+from sh_edraft.publish.model import PublishSettings
+
+
+class PublisherTest(unittest.TestCase):
+
+ def setUp(self):
+ Console.disable()
+ self._app_host = ApplicationHost()
+ self._configuration = self._app_host.configuration
+ self._services = self._app_host.services
+
+ self._configuration.add_environment_variables('CPL_')
+ self._configuration.add_argument_variables()
+ self._configuration.add_json_file(f'build.json')
+
+ self._services.add_singleton(LoggerBase, Logger)
+ self._services.add_singleton(PublisherBase, Publisher)
+ self._publisher: Publisher = self._services.get_service(PublisherBase)
+
+ def test_include(self):
+ value = './test.py'
+ self._publisher.include(value)
+ self.assertTrue(value in self._publisher._publish_settings.included_files)
+
+ def test_exclude(self):
+ value = './test.py'
+ self._publisher.exclude(value)
+ self.assertTrue(value in self._publisher._publish_settings.excluded_files)
+
+ def test_create(self):
+ self._publisher.create()
+ self.assertTrue(os.path.isdir(self._configuration.get_configuration(PublishSettings).dist_path))
+
+ def test_build(self):
+ self._publisher.create()
+ self._publisher.build()
+ self.assertTrue(os.path.isdir(self._configuration.get_configuration(PublishSettings).dist_path))
+
+ def test_publish(self):
+ self._publisher.create()
+ self._publisher.build()
+ self._publisher.publish()
+ self.assertTrue(os.path.isdir(self._configuration.get_configuration(PublishSettings).dist_path))
diff --git a/src/tests/service_test/__init__.py b/src/tests/service_test/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/tests/service_test/provider_test.py b/src/tests/service_test/provider_test.py
new file mode 100644
index 00000000..96fef8eb
--- /dev/null
+++ b/src/tests/service_test/provider_test.py
@@ -0,0 +1,79 @@
+import unittest
+
+from sh_edraft.console import Console
+from sh_edraft.database.context import DatabaseContext
+from sh_edraft.hosting import ApplicationHost
+from sh_edraft.logging import Logger
+from sh_edraft.logging.base import LoggerBase
+
+
+class ProviderTest(unittest.TestCase):
+
+ def setUp(self):
+ Console.disable()
+ self._app_host = ApplicationHost()
+ self._configuration = self._app_host.configuration
+ self._services = self._app_host.services
+
+ self._configuration.add_environment_variables('CPL_')
+ self._configuration.add_argument_variables()
+ self._configuration.add_json_file(f'appsettings.json')
+ self._configuration.add_json_file(f'appsettings.{self._configuration.environment.environment_name}.json')
+ self._configuration.add_json_file(f'appsettings.{self._configuration.environment.host_name}.json', optional=True)
+
+ def test_get_db_context(self):
+ self._services.add_db_context(DatabaseContext)
+ db: DatabaseContext = self._services.get_db_context()
+
+ self.assertIsNotNone(db)
+
+ def test_get_service_singleton(self):
+ self._services.add_singleton(LoggerBase, Logger)
+ logger = self._services.get_service(LoggerBase)
+
+ self.assertIsNotNone(logger)
+
+ def test_get_service_scoped(self):
+ self._services.add_scoped(LoggerBase, Logger)
+ logger = self._services.get_service(LoggerBase)
+
+ self.assertIsNotNone(logger)
+
+ def test_get_service_transient(self):
+ self._services.add_transient(LoggerBase, Logger)
+ logger = self._services.get_service(LoggerBase)
+
+ self.assertIsNotNone(logger)
+
+ def test_remove_service_singleton(self):
+ self._services.add_singleton(LoggerBase, Logger)
+ logger = self._services.get_service(LoggerBase)
+
+ self.assertIsNotNone(logger)
+
+ self._services.remove_service(LoggerBase)
+ logger = self._services.get_service(LoggerBase)
+
+ self.assertIsNone(logger)
+
+ def test_remove_service_scoped(self):
+ self._services.add_scoped(LoggerBase, Logger)
+ logger = self._services.get_service(LoggerBase)
+
+ self.assertIsNotNone(logger)
+
+ self._services.remove_service(LoggerBase)
+ logger = self._services.get_service(LoggerBase)
+
+ self.assertIsNone(logger)
+
+ def test_remove_service_transient(self):
+ self._services.add_transient(LoggerBase, Logger)
+ logger = self._services.get_service(LoggerBase)
+
+ self.assertIsNotNone(logger)
+
+ self._services.remove_service(LoggerBase)
+ logger = self._services.get_service(LoggerBase)
+
+ self.assertIsNone(logger)
diff --git a/src/tests/setup.py b/src/tests/setup.py
new file mode 100644
index 00000000..b9da5bf5
--- /dev/null
+++ b/src/tests/setup.py
@@ -0,0 +1,24 @@
+import setuptools
+
+setuptools.setup(
+ name='sh_edraft_unittests',
+ version='2020.0.1',
+ packages=setuptools.find_packages(exclude=["tests*"]),
+ url='https://www.sh-edraft.de',
+ license='MIT',
+ author='Sven Heidemann',
+ author_email='edraft.sh@gmail.com',
+ include_package_data=True,
+ description='sh-edraft python common lib unittest',
+ python_requires='>=3.8',
+ install_requires=[
+ 'discord.py',
+ 'flask',
+ 'mysql-connector',
+ 'SQLAlchemy',
+ 'termcolor',
+ 'pyfiglet',
+ 'tabulate',
+ 'smtplib'
+ ]
+)
diff --git a/src/tests/time_test/__init__.py b/src/tests/time_test/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/tests/time_test/time_format_settings_test.py b/src/tests/time_test/time_format_settings_test.py
new file mode 100644
index 00000000..9bc5347a
--- /dev/null
+++ b/src/tests/time_test/time_format_settings_test.py
@@ -0,0 +1,26 @@
+import unittest
+
+from sh_edraft.time.model import TimeFormatSettingsNames, TimeFormatSettings
+
+
+class TimeFormatSettingsTest(unittest.TestCase):
+
+ def setUp(self): pass
+
+ def test_from_dict(self):
+ test_dict = {
+ TimeFormatSettingsNames.date_format.value: '%H:%M:%S',
+ TimeFormatSettingsNames.time_format.value: '%Y-%m-%d',
+ TimeFormatSettingsNames.date_time_format.value: '%Y-%m-%d %H:%M:%S.%f',
+ TimeFormatSettingsNames.date_time_log_format.value: '%Y-%m-%d_%H-%M-%S'
+ }
+
+ settings = TimeFormatSettings()
+ settings.from_dict(test_dict)
+
+ self.assertIsNotNone(settings)
+
+ self.assertEqual(test_dict[TimeFormatSettingsNames.date_format.value], settings.date_format)
+ self.assertEqual(test_dict[TimeFormatSettingsNames.time_format.value], settings.time_format)
+ self.assertEqual(test_dict[TimeFormatSettingsNames.date_time_format.value], settings.date_time_format)
+ self.assertEqual(test_dict[TimeFormatSettingsNames.date_time_log_format.value], settings.date_time_log_format)
diff --git a/src/tests/utils_test/__init__.py b/src/tests/utils_test/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/tests/utils_test/credential_manager_test.py b/src/tests/utils_test/credential_manager_test.py
new file mode 100644
index 00000000..204f1283
--- /dev/null
+++ b/src/tests/utils_test/credential_manager_test.py
@@ -0,0 +1,37 @@
+import base64
+import unittest
+
+from sh_edraft.utils import CredentialManager
+
+
+class CredentialManagerTest(unittest.TestCase):
+
+ def setUp(self): pass
+
+ def test_encode(self):
+ test_string = 'Hello World'
+ expected_test_result = base64.b64encode(test_string.encode('utf-8')).decode('utf-8')
+
+ test_result = CredentialManager.encrypt(test_string)
+
+ self.assertIsNotNone(test_result)
+ self.assertEqual(expected_test_result, test_result)
+
+ def test_decode(self):
+ test_string = 'SGVsbG8gV29ybGQ='
+ expected_test_result = base64.b64decode(test_string).decode('utf-8')
+
+ test_result = CredentialManager.decrypt(test_string)
+
+ self.assertIsNotNone(test_result)
+ self.assertEqual(expected_test_result, test_result)
+
+ def test_build_string(self):
+ test_string = 'String is $credentials'
+ test_credentials = 'SGVsbG8gV29ybGQ='
+ expected_test_result = test_string.replace('$credentials', base64.b64decode(test_credentials).decode('utf-8'))
+
+ test_result = CredentialManager.build_string(test_string, test_credentials)
+
+ self.assertIsNotNone(test_result)
+ self.assertEqual(expected_test_result, test_result)
diff --git a/src/tests_dev/__init__.py b/src/tests_dev/__init__.py
new file mode 100644
index 00000000..caf789c0
--- /dev/null
+++ b/src/tests_dev/__init__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+
+"""
+tests_dev
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'tests_dev'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/tests_dev/appsettings.development.json b/src/tests_dev/appsettings.development.json
new file mode 100644
index 00000000..d134912e
--- /dev/null
+++ b/src/tests_dev/appsettings.development.json
@@ -0,0 +1,14 @@
+{
+ "LoggingSettings": {
+ "Path": "logs/",
+ "Filename": "log_$start_time.log",
+ "ConsoleLogLevel": "TRACE",
+ "FileLogLevel": "TRACE"
+ },
+
+ "DatabaseSettings": {
+ "ConnectionString": "mysql+mysqlconnector://sh_cpl:$credentials@localhost/sh_cpl",
+ "Credentials": "MHZhc0Y2bjhKc1VUMWV0Qw==",
+ "Encoding": "utf8mb4"
+ }
+}
\ No newline at end of file
diff --git a/src/tests_dev/appsettings.edrafts-lapi.json b/src/tests_dev/appsettings.edrafts-lapi.json
new file mode 100644
index 00000000..0b2e194a
--- /dev/null
+++ b/src/tests_dev/appsettings.edrafts-lapi.json
@@ -0,0 +1,63 @@
+{
+ "TimeFormatSettings": {
+ "DateFormat": "%Y-%m-%d",
+ "TimeFormat": "%H:%M:%S",
+ "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f",
+ "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S"
+ },
+ "LoggingSettings": {
+ "Path": "logs/",
+ "Filename": "log_$start_time.log",
+ "ConsoleLogLevel": "TRACE",
+ "FileLogLevel": "TRACE"
+ },
+ "EMailClientSettings": {
+ "Host": "mail.sh-edraft.de",
+ "Port": "587",
+ "UserName": "dev-srv@sh-edraft.de",
+ "Credentials": "RmBOQX1eNFYiYjgsSid3fV1nelc2WA=="
+ },
+ "PublishSettings": {
+ "SourcePath": "../",
+ "DistPath": "../../dist",
+ "Templates": [
+ {
+ "TemplatePath": "../../publish_templates/all_template.txt",
+ "Name": "all",
+ "Description": "",
+ "LongDescription": "",
+ "CopyrightDate": "2020",
+ "CopyrightName": "sh-edraft.de",
+ "LicenseName": "MIT",
+ "LicenseDescription": ", see LICENSE for more details.",
+ "Title": "",
+ "Author": "Sven Heidemann",
+ "Version": {
+ "Major": 2020,
+ "Minor": 12,
+ "Micro": 9
+ }
+ },
+ {
+ "TemplatePath": "../../publish_templates/all_template.txt",
+ "Name": "sh_edraft",
+ "Description": "common python library",
+ "LongDescription": "Library to share common classes and models used at sh-edraft.de",
+ "CopyrightDate": "2020",
+ "CopyrightName": "sh-edraft.de",
+ "LicenseName": "MIT",
+ "LicenseDescription": ", see LICENSE for more details.",
+ "Title": "",
+ "Author": "Sven Heidemann",
+ "Version": {
+ "Major": 2020,
+ "Minor": 12,
+ "Micro": 9
+ }
+ }
+ ],
+ "IncludedFiles": [],
+ "ExcludedFiles": [],
+ "TemplateEnding": "_template.txt"
+ }
+}
\ No newline at end of file
diff --git a/src/tests_dev/appsettings.edrafts-pc.json b/src/tests_dev/appsettings.edrafts-pc.json
new file mode 100644
index 00000000..0b2e194a
--- /dev/null
+++ b/src/tests_dev/appsettings.edrafts-pc.json
@@ -0,0 +1,63 @@
+{
+ "TimeFormatSettings": {
+ "DateFormat": "%Y-%m-%d",
+ "TimeFormat": "%H:%M:%S",
+ "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f",
+ "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S"
+ },
+ "LoggingSettings": {
+ "Path": "logs/",
+ "Filename": "log_$start_time.log",
+ "ConsoleLogLevel": "TRACE",
+ "FileLogLevel": "TRACE"
+ },
+ "EMailClientSettings": {
+ "Host": "mail.sh-edraft.de",
+ "Port": "587",
+ "UserName": "dev-srv@sh-edraft.de",
+ "Credentials": "RmBOQX1eNFYiYjgsSid3fV1nelc2WA=="
+ },
+ "PublishSettings": {
+ "SourcePath": "../",
+ "DistPath": "../../dist",
+ "Templates": [
+ {
+ "TemplatePath": "../../publish_templates/all_template.txt",
+ "Name": "all",
+ "Description": "",
+ "LongDescription": "",
+ "CopyrightDate": "2020",
+ "CopyrightName": "sh-edraft.de",
+ "LicenseName": "MIT",
+ "LicenseDescription": ", see LICENSE for more details.",
+ "Title": "",
+ "Author": "Sven Heidemann",
+ "Version": {
+ "Major": 2020,
+ "Minor": 12,
+ "Micro": 9
+ }
+ },
+ {
+ "TemplatePath": "../../publish_templates/all_template.txt",
+ "Name": "sh_edraft",
+ "Description": "common python library",
+ "LongDescription": "Library to share common classes and models used at sh-edraft.de",
+ "CopyrightDate": "2020",
+ "CopyrightName": "sh-edraft.de",
+ "LicenseName": "MIT",
+ "LicenseDescription": ", see LICENSE for more details.",
+ "Title": "",
+ "Author": "Sven Heidemann",
+ "Version": {
+ "Major": 2020,
+ "Minor": 12,
+ "Micro": 9
+ }
+ }
+ ],
+ "IncludedFiles": [],
+ "ExcludedFiles": [],
+ "TemplateEnding": "_template.txt"
+ }
+}
\ No newline at end of file
diff --git a/src/tests_dev/appsettings.json b/src/tests_dev/appsettings.json
new file mode 100644
index 00000000..fd8ddf6c
--- /dev/null
+++ b/src/tests_dev/appsettings.json
@@ -0,0 +1,15 @@
+{
+ "TimeFormatSettings": {
+ "DateFormat": "%Y-%m-%d",
+ "TimeFormat": "%H:%M:%S",
+ "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f",
+ "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S"
+ },
+
+ "LoggingSettings": {
+ "Path": "logs/",
+ "Filename": "log_$start_time.log",
+ "ConsoleLogLevel": "ERROR",
+ "FileLogLevel": "WARN"
+ }
+}
\ No newline at end of file
diff --git a/src/tests_dev/db/__init__.py b/src/tests_dev/db/__init__.py
new file mode 100644
index 00000000..44b89a68
--- /dev/null
+++ b/src/tests_dev/db/__init__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+
+"""
+tests_dev.db
+~~~~~~~~~~~~~~~~~~~
+
+
+
+:copyright: (c) 2020 sh-edraft.de
+:license: MIT, see LICENSE for more details.
+
+"""
+
+__title__ = 'tests_dev.db'
+__author__ = 'Sven Heidemann'
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) 2020 sh-edraft.de'
+__version__ = '2020.12.9'
+
+from collections import namedtuple
+
+# imports:
+
+VersionInfo = namedtuple('VersionInfo', 'major minor micro')
+version_info = VersionInfo(major=2020, minor=12, micro=9)
diff --git a/src/tests_dev/db/city.py b/src/tests_dev/db/city.py
new file mode 100644
index 00000000..baf07ded
--- /dev/null
+++ b/src/tests_dev/db/city.py
@@ -0,0 +1,14 @@
+from sqlalchemy import Column, Integer, String
+
+from sh_edraft.database.model import DBModel
+
+
+class City(DBModel):
+ __tablename__ = 'Cities'
+ Id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
+ Name = Column(String(64), nullable=False)
+ ZIP = Column(String(5), nullable=False)
+
+ def __init__(self, name: str, zip_code: str):
+ self.Name = name
+ self.ZIP = zip_code
diff --git a/src/tests_dev/db/user.py b/src/tests_dev/db/user.py
new file mode 100644
index 00000000..e840df86
--- /dev/null
+++ b/src/tests_dev/db/user.py
@@ -0,0 +1,18 @@
+from sqlalchemy import Column, Integer, String, ForeignKey
+from sqlalchemy.orm import relationship
+
+from sh_edraft.database.model import DBModel
+from tests_dev.db.city import City as CityModel
+
+
+class User(DBModel):
+ __tablename__ = 'Users'
+ Id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
+ Name = Column(String(64), nullable=False)
+ City_Id = Column(Integer, ForeignKey('Cities.Id'), nullable=False)
+ City = relationship("City")
+
+ def __init__(self, name: str, city: CityModel):
+ self.Name = name
+ self.City_Id = city.Id
+ self.City = city
diff --git a/src/tests_dev/db/user_repo.py b/src/tests_dev/db/user_repo.py
new file mode 100644
index 00000000..a1b3cffc
--- /dev/null
+++ b/src/tests_dev/db/user_repo.py
@@ -0,0 +1,23 @@
+from sh_edraft.database.context.base import DatabaseContextBase
+from tests_dev.db.city import City
+from tests_dev.db.user import User
+from tests_dev.db.user_repo_base import UserRepoBase
+
+
+class UserRepo(UserRepoBase):
+
+ def __init__(self, db_context: DatabaseContextBase):
+ UserRepoBase.__init__(self)
+
+ self._session = db_context.session
+ self._user_query = db_context.session.query(User)
+
+ def create(self): pass
+
+ def add_test_user(self):
+ city = City('Haren', '49733')
+ city2 = City('Meppen', '49716')
+ self._session.add(city2)
+ user = User('TestUser', city)
+ self._session.add(user)
+ self._session.commit()
diff --git a/src/tests_dev/db/user_repo_base.py b/src/tests_dev/db/user_repo_base.py
new file mode 100644
index 00000000..c48be0b2
--- /dev/null
+++ b/src/tests_dev/db/user_repo_base.py
@@ -0,0 +1,10 @@
+from abc import abstractmethod
+
+from sh_edraft.service.base import ServiceBase
+
+
+class UserRepoBase(ServiceBase):
+
+ @abstractmethod
+ def __init__(self):
+ ServiceBase.__init__(self)
diff --git a/src/tests_dev/main.py b/src/tests_dev/main.py
new file mode 100644
index 00000000..cfb45c60
--- /dev/null
+++ b/src/tests_dev/main.py
@@ -0,0 +1,8 @@
+from tests_dev.program import Program
+
+if __name__ == '__main__':
+ program = Program()
+ program.create_application_host()
+ program.create_configuration()
+ program.create_services()
+ program.main()
diff --git a/src/tests_dev/program.py b/src/tests_dev/program.py
new file mode 100644
index 00000000..e08eadbe
--- /dev/null
+++ b/src/tests_dev/program.py
@@ -0,0 +1,87 @@
+from typing import Optional
+
+from sh_edraft.configuration.base import ConfigurationBase
+from sh_edraft.console import Console
+from sh_edraft.database.context import DatabaseContext
+from sh_edraft.database.model import DatabaseSettings
+from sh_edraft.hosting import ApplicationHost
+from sh_edraft.hosting.base import ApplicationBase
+from sh_edraft.logging import Logger
+from sh_edraft.logging.base import LoggerBase
+from sh_edraft.mailing.base import EMailClientBase
+from sh_edraft.mailing import EMailClient
+from sh_edraft.mailing.model import EMail
+from sh_edraft.service.providing.base import ServiceProviderBase
+from sh_edraft.utils import CredentialManager
+
+from tests_dev.db.user_repo import UserRepo
+from tests_dev.db.user_repo_base import UserRepoBase
+
+
+class Program(ApplicationBase):
+
+ def __init__(self):
+ ApplicationBase.__init__(self)
+
+ self._app_host: Optional[ApplicationHost] = None
+ self._services: Optional[ServiceProviderBase] = None
+ self._configuration: Optional[ConfigurationBase] = None
+ self._logger: Optional[LoggerBase] = None
+ self._mailer: Optional[EMailClientBase] = None
+
+ def create_application_host(self):
+ self._app_host = ApplicationHost()
+ self._configuration = self._app_host.configuration
+ self._services = self._app_host.services
+
+ def create_configuration(self):
+ self._configuration.add_environment_variables('PYTHON_')
+ self._configuration.add_environment_variables('CPL_')
+ self._configuration.add_argument_variables()
+ self._configuration.add_json_file(f'appsettings.json')
+ self._configuration.add_json_file(f'appsettings.{self._configuration.environment.environment_name}.json')
+ self._configuration.add_json_file(f'appsettings.{self._configuration.environment.host_name}.json', optional=True)
+
+ def create_services(self):
+ # Create and connect to database
+ db_settings: DatabaseSettings = self._configuration.get_configuration(DatabaseSettings)
+ self._services.add_db_context(DatabaseContext)
+ db: DatabaseContext = self._services.get_db_context()
+ db.connect(CredentialManager.build_string(db_settings.connection_string, db_settings.credentials))
+
+ self._services.add_scoped(UserRepoBase, UserRepo)
+
+ # Add and create logger
+ self._services.add_singleton(LoggerBase, Logger)
+ self._logger = self._services.get_service(LoggerBase)
+
+ self._services.add_singleton(EMailClientBase, EMailClient)
+ self._mailer = self._services.get_service(EMailClientBase)
+
+ def test_send_mail(self):
+ mail = EMail()
+ mail.add_header('Mime-Version: 1.0')
+ mail.add_header('Content-Type: text/plain; charset=utf-8')
+ mail.add_header('Content-Transfer-Encoding: quoted-printable')
+ mail.add_receiver('edraft.sh@gmail.com')
+ mail.add_receiver('edraft@sh-edraft.de')
+ mail.subject = f'Test - {self._configuration.environment.host_name}'
+ mail.body = 'Dies ist ein Test :D'
+ self._mailer.send_mail(mail)
+
+ def test_console(self):
+ self._logger.debug(__name__, 'Started console test')
+ Console.write_line('Hello World')
+ Console.write('\nName: ')
+ Console.write_line('Hello', Console.read_line())
+ Console.clear()
+ Console.write_at(5, 5, 'at 5, 5')
+ Console.write_at(10, 10, 'at 10, 10')
+
+ def main(self):
+ self._logger.header(f'{self._configuration.environment.application_name}:')
+ self._logger.debug(__name__, f'Host: {self._configuration.environment.host_name}')
+ self._logger.debug(__name__, f'Environment: {self._configuration.environment.environment_name}')
+ self._logger.debug(__name__, f'Customer: {self._configuration.environment.customer}')
+ self._services.get_service(UserRepoBase).add_test_user()
+ self.test_console()