bosos-dev-tools 0.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 bjorngun
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,136 @@
1
+ Metadata-Version: 2.1
2
+ Name: bosos-dev-tools
3
+ Version: 0.0.2
4
+ Summary: Dev Tools is a collection of utility tools for Python developers, designed to simplify debugging, logging, and monitoring tasks. This package includes custom logging handlers, decorators for measuring execution time, and a progress bar utility to enhance the development workflow.
5
+ Author-email: Björn Gunnarsson <bosos3@hotmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2024 bjorngun
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Source, https://github.com/bjorngun/developer-tools
29
+ Project-URL: Tracker, https://github.com/bjorngun/developer-tools/issues
30
+ Keywords: development,tools,logging,decorators
31
+ Classifier: Development Status :: 3 - Alpha
32
+ Classifier: Programming Language :: Python :: 3
33
+ Classifier: Programming Language :: Python :: 3.12
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: Topic :: Software Development :: Build Tools
36
+ Classifier: License :: OSI Approved :: MIT License
37
+ Classifier: Operating System :: OS Independent
38
+ Requires-Python: >=3.12
39
+ Description-Content-Type: text/markdown
40
+ License-File: LICENSE
41
+ Requires-Dist: pyodbc
42
+ Requires-Dist: python-dotenv
43
+
44
+ # Bosos Dev Tools
45
+
46
+ Bosos Dev Tools is a collection of utility tools for Python developers, designed to simplify debugging, logging, and monitoring tasks. This package includes custom logging handlers, decorators for measuring execution time, and a progress bar utility to enhance the development workflow.
47
+
48
+ ## Features
49
+
50
+ - **Custom Logging Handlers**: Log messages to various destinations, including databases, with customizable formats.
51
+ - **Timing Decorators**: Easily measure the execution time of your functions with minimal code changes.
52
+ - **Progress Bar Utility**: Visualize the progress of long-running operations in the console.
53
+ - **Debug Tools**: Check if debug or timing modes are enabled via environment variables.
54
+
55
+ ## Installation
56
+
57
+ You can install the package via pip:
58
+
59
+ ```sh
60
+ pip install bosos-dev-tools
61
+ ```
62
+
63
+ ## Usage
64
+
65
+ ### Custom Logging Handler
66
+
67
+ The `LogDBHandler` allows you to log messages directly to a database.
68
+
69
+ ``` py
70
+ import logging
71
+ from dev_tools.custom_handlers import LogDBHandler
72
+
73
+ logger = logging.getLogger('test_logger')
74
+ db_handler = LogDBHandler(db_table='test_table')
75
+ logger.addHandler(db_handler)
76
+ logger.setLevel(logging.INFO)
77
+
78
+ logger.info('This is a test log message.')
79
+ ```
80
+
81
+ ### Timing Decorator
82
+
83
+ Use the `timing_decorator` to measure the execution time of functions.
84
+
85
+ ``` py
86
+ from dev_tools.custom_decorators import timing_decorator
87
+
88
+ @timing_decorator
89
+ def example_function():
90
+ for i in range(1000000):
91
+ pass
92
+
93
+ example_function()
94
+ ```
95
+
96
+ ### Progress Bar
97
+
98
+ Use the `progress_bar` to measure the execution time of functions.
99
+
100
+ ``` py
101
+ from dev_tools.progress_bar import progress_bar
102
+
103
+ for item in progress_bar(range(10)):
104
+ pass
105
+ ```
106
+
107
+ ### Debug Tools
108
+
109
+ Check if debug or timing modes are enabled via environment variables.
110
+ Use the `logger_setup` to set up your logging settings at the beginning of the script.
111
+
112
+ ``` py
113
+ from dev_tools.debug_tools import is_debug_on, is_timing_on
114
+
115
+ print('Is debug on:', is_debug_on())
116
+ print('Is timing on:', is_timing_on())
117
+ ```
118
+
119
+ ``` py
120
+ from dev_tools.debug_tools import logger_setup
121
+
122
+ def main():
123
+ logger_setup()
124
+
125
+ if __name__ == '__main__':
126
+ main()
127
+ ```
128
+
129
+ ## License
130
+
131
+ This project is licensed under the MIT License. See the [LICENSE](https://github.com/bjorngun/developer-tools/blob/main/LICENSE) file for more details.
132
+
133
+ ## Links
134
+
135
+ - **Source Code**: [GitHub Repository](https://github.com/bjorngun/developer-tools)
136
+ - **Issue Tracker**: [GitHub Issues](https://github.com/bjorngun/developer-tools/issues)
@@ -0,0 +1,93 @@
1
+ # Bosos Dev Tools
2
+
3
+ Bosos Dev Tools is a collection of utility tools for Python developers, designed to simplify debugging, logging, and monitoring tasks. This package includes custom logging handlers, decorators for measuring execution time, and a progress bar utility to enhance the development workflow.
4
+
5
+ ## Features
6
+
7
+ - **Custom Logging Handlers**: Log messages to various destinations, including databases, with customizable formats.
8
+ - **Timing Decorators**: Easily measure the execution time of your functions with minimal code changes.
9
+ - **Progress Bar Utility**: Visualize the progress of long-running operations in the console.
10
+ - **Debug Tools**: Check if debug or timing modes are enabled via environment variables.
11
+
12
+ ## Installation
13
+
14
+ You can install the package via pip:
15
+
16
+ ```sh
17
+ pip install bosos-dev-tools
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ### Custom Logging Handler
23
+
24
+ The `LogDBHandler` allows you to log messages directly to a database.
25
+
26
+ ``` py
27
+ import logging
28
+ from dev_tools.custom_handlers import LogDBHandler
29
+
30
+ logger = logging.getLogger('test_logger')
31
+ db_handler = LogDBHandler(db_table='test_table')
32
+ logger.addHandler(db_handler)
33
+ logger.setLevel(logging.INFO)
34
+
35
+ logger.info('This is a test log message.')
36
+ ```
37
+
38
+ ### Timing Decorator
39
+
40
+ Use the `timing_decorator` to measure the execution time of functions.
41
+
42
+ ``` py
43
+ from dev_tools.custom_decorators import timing_decorator
44
+
45
+ @timing_decorator
46
+ def example_function():
47
+ for i in range(1000000):
48
+ pass
49
+
50
+ example_function()
51
+ ```
52
+
53
+ ### Progress Bar
54
+
55
+ Use the `progress_bar` to measure the execution time of functions.
56
+
57
+ ``` py
58
+ from dev_tools.progress_bar import progress_bar
59
+
60
+ for item in progress_bar(range(10)):
61
+ pass
62
+ ```
63
+
64
+ ### Debug Tools
65
+
66
+ Check if debug or timing modes are enabled via environment variables.
67
+ Use the `logger_setup` to set up your logging settings at the beginning of the script.
68
+
69
+ ``` py
70
+ from dev_tools.debug_tools import is_debug_on, is_timing_on
71
+
72
+ print('Is debug on:', is_debug_on())
73
+ print('Is timing on:', is_timing_on())
74
+ ```
75
+
76
+ ``` py
77
+ from dev_tools.debug_tools import logger_setup
78
+
79
+ def main():
80
+ logger_setup()
81
+
82
+ if __name__ == '__main__':
83
+ main()
84
+ ```
85
+
86
+ ## License
87
+
88
+ This project is licensed under the MIT License. See the [LICENSE](https://github.com/bjorngun/developer-tools/blob/main/LICENSE) file for more details.
89
+
90
+ ## Links
91
+
92
+ - **Source Code**: [GitHub Repository](https://github.com/bjorngun/developer-tools)
93
+ - **Issue Tracker**: [GitHub Issues](https://github.com/bjorngun/developer-tools/issues)
@@ -0,0 +1,36 @@
1
+ [build-system]
2
+ requires = ['setuptools>=42', 'wheel']
3
+ build-backend = 'setuptools.build_meta'
4
+
5
+ [project]
6
+ name = 'bosos-dev-tools'
7
+ version = '0.0.2'
8
+ description = 'Dev Tools is a collection of utility tools for Python developers, designed to simplify debugging, logging, and monitoring tasks. This package includes custom logging handlers, decorators for measuring execution time, and a progress bar utility to enhance the development workflow.'#TODO
9
+ authors = [{ name = 'Björn Gunnarsson', email = 'bosos3@hotmail.com' }]
10
+ license = { file = 'LICENSE' }
11
+ readme = 'README.md'
12
+ keywords = ['development', 'tools', 'logging', 'decorators']
13
+ classifiers = [
14
+ 'Development Status :: 3 - Alpha',
15
+ 'Programming Language :: Python :: 3',
16
+ 'Programming Language :: Python :: 3.12',
17
+ 'Intended Audience :: Developers',
18
+ 'Topic :: Software Development :: Build Tools',
19
+ 'License :: OSI Approved :: MIT License',
20
+ 'Operating System :: OS Independent',
21
+ ]
22
+ requires-python = '>=3.12'
23
+ dependencies = [
24
+ 'pyodbc',
25
+ 'python-dotenv',
26
+ ]
27
+
28
+ [project.urls]
29
+ Source = 'https://github.com/bjorngun/developer-tools'
30
+ Tracker = 'https://github.com/bjorngun/developer-tools/issues'
31
+
32
+ [tool.setuptools.packages.find]
33
+ where = ['src']
34
+
35
+ [tool.setuptools]
36
+ package-dir = {'' = 'src'}
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,136 @@
1
+ Metadata-Version: 2.1
2
+ Name: bosos-dev-tools
3
+ Version: 0.0.2
4
+ Summary: Dev Tools is a collection of utility tools for Python developers, designed to simplify debugging, logging, and monitoring tasks. This package includes custom logging handlers, decorators for measuring execution time, and a progress bar utility to enhance the development workflow.
5
+ Author-email: Björn Gunnarsson <bosos3@hotmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2024 bjorngun
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Source, https://github.com/bjorngun/developer-tools
29
+ Project-URL: Tracker, https://github.com/bjorngun/developer-tools/issues
30
+ Keywords: development,tools,logging,decorators
31
+ Classifier: Development Status :: 3 - Alpha
32
+ Classifier: Programming Language :: Python :: 3
33
+ Classifier: Programming Language :: Python :: 3.12
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: Topic :: Software Development :: Build Tools
36
+ Classifier: License :: OSI Approved :: MIT License
37
+ Classifier: Operating System :: OS Independent
38
+ Requires-Python: >=3.12
39
+ Description-Content-Type: text/markdown
40
+ License-File: LICENSE
41
+ Requires-Dist: pyodbc
42
+ Requires-Dist: python-dotenv
43
+
44
+ # Bosos Dev Tools
45
+
46
+ Bosos Dev Tools is a collection of utility tools for Python developers, designed to simplify debugging, logging, and monitoring tasks. This package includes custom logging handlers, decorators for measuring execution time, and a progress bar utility to enhance the development workflow.
47
+
48
+ ## Features
49
+
50
+ - **Custom Logging Handlers**: Log messages to various destinations, including databases, with customizable formats.
51
+ - **Timing Decorators**: Easily measure the execution time of your functions with minimal code changes.
52
+ - **Progress Bar Utility**: Visualize the progress of long-running operations in the console.
53
+ - **Debug Tools**: Check if debug or timing modes are enabled via environment variables.
54
+
55
+ ## Installation
56
+
57
+ You can install the package via pip:
58
+
59
+ ```sh
60
+ pip install bosos-dev-tools
61
+ ```
62
+
63
+ ## Usage
64
+
65
+ ### Custom Logging Handler
66
+
67
+ The `LogDBHandler` allows you to log messages directly to a database.
68
+
69
+ ``` py
70
+ import logging
71
+ from dev_tools.custom_handlers import LogDBHandler
72
+
73
+ logger = logging.getLogger('test_logger')
74
+ db_handler = LogDBHandler(db_table='test_table')
75
+ logger.addHandler(db_handler)
76
+ logger.setLevel(logging.INFO)
77
+
78
+ logger.info('This is a test log message.')
79
+ ```
80
+
81
+ ### Timing Decorator
82
+
83
+ Use the `timing_decorator` to measure the execution time of functions.
84
+
85
+ ``` py
86
+ from dev_tools.custom_decorators import timing_decorator
87
+
88
+ @timing_decorator
89
+ def example_function():
90
+ for i in range(1000000):
91
+ pass
92
+
93
+ example_function()
94
+ ```
95
+
96
+ ### Progress Bar
97
+
98
+ Use the `progress_bar` to measure the execution time of functions.
99
+
100
+ ``` py
101
+ from dev_tools.progress_bar import progress_bar
102
+
103
+ for item in progress_bar(range(10)):
104
+ pass
105
+ ```
106
+
107
+ ### Debug Tools
108
+
109
+ Check if debug or timing modes are enabled via environment variables.
110
+ Use the `logger_setup` to set up your logging settings at the beginning of the script.
111
+
112
+ ``` py
113
+ from dev_tools.debug_tools import is_debug_on, is_timing_on
114
+
115
+ print('Is debug on:', is_debug_on())
116
+ print('Is timing on:', is_timing_on())
117
+ ```
118
+
119
+ ``` py
120
+ from dev_tools.debug_tools import logger_setup
121
+
122
+ def main():
123
+ logger_setup()
124
+
125
+ if __name__ == '__main__':
126
+ main()
127
+ ```
128
+
129
+ ## License
130
+
131
+ This project is licensed under the MIT License. See the [LICENSE](https://github.com/bjorngun/developer-tools/blob/main/LICENSE) file for more details.
132
+
133
+ ## Links
134
+
135
+ - **Source Code**: [GitHub Repository](https://github.com/bjorngun/developer-tools)
136
+ - **Issue Tracker**: [GitHub Issues](https://github.com/bjorngun/developer-tools/issues)
@@ -0,0 +1,18 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/bosos_dev_tools.egg-info/PKG-INFO
5
+ src/bosos_dev_tools.egg-info/SOURCES.txt
6
+ src/bosos_dev_tools.egg-info/dependency_links.txt
7
+ src/bosos_dev_tools.egg-info/requires.txt
8
+ src/bosos_dev_tools.egg-info/top_level.txt
9
+ src/dev_tools/__init__.py
10
+ src/dev_tools/custom_decorators.py
11
+ src/dev_tools/custom_handlers.py
12
+ src/dev_tools/debug_tools.py
13
+ src/dev_tools/progress_bar.py
14
+ src/tests/__init__.py
15
+ src/tests/test_custom_decorators.py
16
+ src/tests/test_custom_handlers.py
17
+ src/tests/test_debug_tools.py
18
+ src/tests/test_progress_bar.py
@@ -0,0 +1,2 @@
1
+ pyodbc
2
+ python-dotenv
@@ -0,0 +1,2 @@
1
+ dev_tools
2
+ tests
@@ -0,0 +1,12 @@
1
+ from dev_tools.progress_bar import progress_bar
2
+ from dev_tools.custom_decorators import timing_decorator
3
+ from dev_tools.custom_handlers import LogDBHandler
4
+ from dev_tools.debug_tools import is_debug_on, logger_setup
5
+
6
+ __all__ = [
7
+ "logger_setup",
8
+ "is_debug_on",
9
+ "progress_bar",
10
+ "timing_decorator",
11
+ "LogDBHandler",
12
+ ]
@@ -0,0 +1,25 @@
1
+ import time
2
+ from typing import Any, Callable
3
+ from functools import wraps
4
+
5
+ from dev_tools.debug_tools import is_timing_on
6
+
7
+
8
+ def timing_decorator(func: Callable) -> Callable:
9
+ @wraps(func)
10
+ def wrapper(*args, **kwargs) -> Any:
11
+ start_time = time.time()
12
+ result = func(*args, **kwargs)
13
+ end_time = time.time()
14
+ elapsed_time = end_time - start_time
15
+
16
+ # Print elapsed time
17
+ print(f"Elapsed time for {func.__name__}: {elapsed_time:.2f} seconds")
18
+
19
+ # Check if timing is enabled and log if applicable
20
+ if is_timing_on() and args and hasattr(args[0], 'logger'):
21
+ logger = getattr(args[0], 'logger')
22
+ logger.info(f"Elapsed time for {func.__name__}: {elapsed_time:.2f} seconds")
23
+
24
+ return result
25
+ return wrapper
@@ -0,0 +1,119 @@
1
+ import logging
2
+ import os
3
+ from datetime import datetime
4
+ import pyodbc
5
+ import getpass
6
+ from pathlib import Path
7
+
8
+
9
+ class LogDBHandler(logging.Handler):
10
+ """Customized logging handler that puts logs to the database."""
11
+
12
+ class SQLLogConnection:
13
+ """Connection class for a SQL database."""
14
+
15
+ def __init__(self):
16
+ """Initializes connection to a database."""
17
+ try:
18
+ connection_string = (f'SERVER={os.getenv("LOGGER_DB_SERVER")};'
19
+ f'DATABASE={os.getenv("LOGGER_DB_NAME")};'
20
+ f'DRIVER={os.getenv("LOGGER_SQL_DRIVER")};'
21
+ 'Trusted_Connection=yes;')
22
+
23
+ conn = pyodbc.connect(
24
+ connection_string,
25
+ autocommit=True,
26
+ TrustServerCertificate='YES',
27
+ )
28
+ self.cursor = conn.cursor()
29
+ except pyodbc.Error as e:
30
+ logging.getLogger('sql_logger').error(f"Error connecting to database: {e}")
31
+ self.cursor = None
32
+
33
+ def insert(self, table: str, columns: list[str], values: list):
34
+ """Executes Insert statement in the connected database.
35
+
36
+ Args:
37
+ table (str): Name of the table being inserted into
38
+ columns (list[str]): List of names of the columns that are being inserted into
39
+ values (list): List of the values
40
+ """
41
+ if self.cursor is None:
42
+ logging.getLogger('sql_logger').error("No database connection available.")
43
+ return
44
+
45
+ table = f'[dbo].[{table}]'
46
+ header = ', '.join([f'[{x}]' for x in columns])
47
+ parameters = ', '.join(['?']*len(columns))
48
+
49
+ try:
50
+ self.cursor.execute(
51
+ f'INSERT INTO {table} ({header}) VALUES({parameters})', values)
52
+ except pyodbc.Error as e:
53
+ logging.getLogger('sql_logger').error(f"Error executing insert statement: {e}")
54
+ except TypeError as e:
55
+ logging.getLogger('sql_logger').error(f"Type error with provided values: {e}")
56
+ except Exception as e:
57
+ logging.getLogger('sql_logger').error(f"Unexpected error: {e}")
58
+
59
+ # Maximum length for the log message to be stored in the database
60
+ MAX_LOG_LEN = 2048
61
+ # Message to append if the log message is truncated
62
+ MAX_LOG_MSG = '... too long, check the local logs to see the full msg'
63
+
64
+ def __init__(self, db_table: str):
65
+ """Initializes the handler and the SQL connection."""
66
+ logging.Handler.__init__(self)
67
+ self.sql_connection = self.SQLLogConnection()
68
+ self.db_table = db_table
69
+
70
+ def emit(self, record):
71
+ """Emits a record to the database."""
72
+
73
+ # Ensure message is a string and escape quotes if necessary
74
+ try:
75
+ log_message = str(record.msg).replace("'", "''")
76
+ except Exception as e:
77
+ logging.getLogger('sql_logger').error(f"Error processing log message: {e}")
78
+ return
79
+
80
+ # Truncate the log message if it exceeds the maximum length
81
+ if len(log_message) > self.MAX_LOG_LEN:
82
+ log_message = f'{log_message[:self.MAX_LOG_LEN - len(self.MAX_LOG_MSG)]}{self.MAX_LOG_MSG}'
83
+
84
+ # Set current time
85
+ current_time = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f")
86
+
87
+ # Database columns for the log entry
88
+ db_columns = [
89
+ 'log_level',
90
+ 'log_levelname',
91
+ 'log_module',
92
+ 'log_func',
93
+ 'log',
94
+ 'script',
95
+ 'created_at',
96
+ 'created_by',
97
+ 'pathname',
98
+ 'process_id',
99
+ ]
100
+
101
+ # Values for the log entry
102
+ row_values = [
103
+ record.levelno,
104
+ record.levelname,
105
+ record.name,
106
+ record.funcName,
107
+ log_message,
108
+ os.getenv("SCRIPT_NAME", Path.cwd().name),
109
+ current_time,
110
+ getpass.getuser(),
111
+ record.pathname,
112
+ os.getpid(),
113
+ ]
114
+
115
+ # Insert the log entry into the database
116
+ try:
117
+ self.sql_connection.insert(self.db_table, db_columns, row_values)
118
+ except Exception as e:
119
+ logging.getLogger('sql_logger').error(f"Error inserting log into database: {e}")
@@ -0,0 +1,103 @@
1
+ import atexit
2
+ from datetime import datetime
3
+ import logging
4
+ import logging.config
5
+ import os
6
+ import sys
7
+ from pathlib import Path
8
+ from dotenv import load_dotenv
9
+
10
+ from .custom_handlers import LogDBHandler
11
+
12
+
13
+ def is_debug_on() -> bool:
14
+ """Determine if debug mode is enabled."""
15
+ return os.getenv('DEBUG', 'False').lower() in ['true', '1', 't', 'yes']
16
+
17
+ def is_timing_on() -> bool:
18
+ """Determine if timing mode is enabled."""
19
+ return os.getenv('TIMING', 'False').lower() in ['true', '1', 't', 'yes']
20
+
21
+ def is_database_logging_on() -> bool:
22
+ """Determine if database logging mode is enabled."""
23
+ return os.getenv('LOGGER_DATABASE', 'False').lower() in ['true', '1', 't', 'yes']
24
+
25
+ def log_exit_code():
26
+ """Log the exit code of the script."""
27
+ logger = logging.getLogger(__name__)
28
+ exit_code = 0 if sys.exc_info() == (None, None, None) else 1
29
+ logger.info(f'Exit code: {exit_code}')
30
+
31
+ def default_logging_config(logger_file_path: str):
32
+ """Default logging configuration."""
33
+ return {
34
+ 'version': 1,
35
+ 'disable_existing_loggers': False,
36
+ 'formatters': {
37
+ 'simple': {
38
+ 'format': '%(levelname)s | %(name)s | %(message)s',
39
+ 'datefmt': '%Y-%m-%dT%H:%M:%S',
40
+ },
41
+ 'complex': {
42
+ 'format': '%(levelname)s | %(asctime)s | %(name)s | %(funcName)s | %(message)s',
43
+ 'datefmt': '%Y-%m-%dT%H:%M:%S',
44
+ },
45
+ },
46
+ 'handlers': {
47
+ 'screen': {
48
+ 'class': 'logging.StreamHandler',
49
+ 'formatter': 'simple',
50
+ 'level': 'INFO' if is_debug_on() else 'WARNING',
51
+ 'stream': 'ext://sys.stdout',
52
+ },
53
+ 'file': {
54
+ 'class': 'logging.FileHandler',
55
+ 'formatter': 'complex',
56
+ 'level': 'DEBUG' if is_debug_on() else 'INFO',
57
+ 'filename': logger_file_path,
58
+ },
59
+ },
60
+ 'root': {
61
+ 'handlers': ['screen', 'file'],
62
+ 'level': 'DEBUG' if is_debug_on() else 'INFO',
63
+ },
64
+ }
65
+
66
+ def logger_setup():
67
+ """Set up logging configuration based on environment variables and debug mode."""
68
+ atexit.register(log_exit_code)
69
+
70
+ #Loads up all the environment variables
71
+ load_dotenv()
72
+ debug = is_debug_on()
73
+ today = datetime.now()
74
+
75
+ logger_conf_path = Path(os.getenv('LOGGER_CONF_PATH', 'logging.conf'))
76
+ if debug:
77
+ logger_conf_path = Path(os.getenv('LOGGER_CONF_DEV_PATH', 'logging_dev.conf'))
78
+ logger_folder_path = f'{os.getenv("LOGGER_PATH", "./logs")}/{today.strftime("%Y-%m-%d")}'
79
+
80
+ try:
81
+ Path(logger_folder_path).mkdir(parents=True, exist_ok=True)
82
+ except Exception as e:
83
+ print(f'Error creating log directory {Path(logger_folder_path)}: {e}', file=sys.stderr)
84
+ sys.exit(1)
85
+
86
+ logger_file_path = f'{logger_folder_path}/{today.strftime("%Y-%m-%dT%H%M%S")}.log'
87
+ logger_db_table = os.getenv('LOGGER_DB_TABLE', 'python_transfer_data_log')
88
+ if logger_conf_path.exists():
89
+ try:
90
+ logging.config.fileConfig(logger_conf_path, defaults={
91
+ 'logfilename': logger_file_path,
92
+ })
93
+ except Exception as e:
94
+ print(f'Error setting up logging configuration from {logger_conf_path}: {e}', file=sys.stderr)
95
+ sys.exit(1)
96
+ else:
97
+ logging.config.dictConfig(default_logging_config(logger_file_path))
98
+ if (is_database_logging_on()):
99
+ log_db_handler = LogDBHandler(logger_db_table)
100
+ logging.getLogger('').addHandler(log_db_handler)
101
+
102
+ logger = logging.getLogger(__name__)
103
+ logger.info(f'Setting up logger for {os.getenv("SCRIPT_NAME", Path.cwd().name)}')
@@ -0,0 +1,86 @@
1
+ import logging
2
+ import time
3
+ from collections.abc import Iterable
4
+ from dev_tools.debug_tools import is_debug_on, is_timing_on
5
+
6
+
7
+ def progress_bar(
8
+ iterable: Iterable,
9
+ prefix: str = "",
10
+ suffix: str = "",
11
+ decimals: int = 1,
12
+ length: int = 50,
13
+ fill: str = "█",
14
+ print_end: str = "\r",
15
+ ) -> Iterable:
16
+ """
17
+ Call in a loop to create terminal progress bar
18
+ @params:
19
+ iterable - Required : iterable object (Iterable)
20
+ prefix - Optional : prefix string (Str)
21
+ suffix - Optional : suffix string (Str)
22
+ decimals - Optional : positive number of decimals in percent complete (Int)
23
+ length - Optional : character length of bar (Int)
24
+ fill - Optional : bar fill character (Str)
25
+ print_end - Optional : end character (e.g. "\r", "\r\n") (Str)
26
+ """
27
+
28
+ debug = is_debug_on()
29
+ timing = is_timing_on()
30
+ total = len(iterable)
31
+ start_time = time.time()
32
+ logger = logging.getLogger(__name__)
33
+ errors = {}
34
+
35
+ def print_progress_bar(iteration: int) -> None:
36
+ if not debug or total == 0:
37
+ return
38
+
39
+ percent = f"{100 * (iteration / float(total)):.{decimals}f}"
40
+ filled_length = int(length * iteration // total)
41
+ bar = fill * filled_length + "-" * (length - filled_length)
42
+ time_str = get_timing_str(iteration)
43
+
44
+ try:
45
+ print(f"\r{prefix} |{bar}| {percent}% {suffix}{time_str}{'':<10}", end=print_end)
46
+ except UnicodeEncodeError:
47
+ if "UnicodeEncodeError" not in errors:
48
+ errors["UnicodeEncodeError"] = True
49
+ logger.exception("Progress bar is not able to print, DEBUG = %s", debug)
50
+ logging.exception("Progress bar is not able to print, DEBUG = %s", debug)
51
+
52
+ def get_timing_str(iteration: int) -> str:
53
+ if not timing:
54
+ return ""
55
+
56
+ et = time.time() - start_time
57
+ et_str = f"Elapsed time: {format_time(et)}"
58
+ eta_str = "Time remaining: N/A"
59
+ if iteration > 0 and et > 0:
60
+ eta_seconds = et / (iteration / float(total)) - et
61
+ eta_str = f"Time remaining: {format_time(eta_seconds)}"
62
+
63
+ return f" |--{et_str} - {eta_str}--|"
64
+
65
+ def format_time(seconds) -> str:
66
+ minutes = int(seconds // 60)
67
+ seconds = seconds % 60
68
+
69
+ hours = int(minutes // 60)
70
+ minutes = minutes % 60
71
+
72
+ time_str = ""
73
+ if hours > 0:
74
+ time_str += f"{hours}h "
75
+ if minutes > 0:
76
+ time_str += f"{minutes}m "
77
+ time_str += f"{seconds:.2f}s"
78
+ return time_str
79
+
80
+ print_progress_bar(0)
81
+ for i, item in enumerate(iterable):
82
+ yield item
83
+ print_progress_bar(i + 1)
84
+ # Print New Line on Complete
85
+ if debug and total > 0:
86
+ print()
File without changes
@@ -0,0 +1,14 @@
1
+ import unittest
2
+ from dev_tools.custom_decorators import timing_decorator
3
+
4
+ class TestTimingDecorator(unittest.TestCase):
5
+ def test_timing_decorator(self):
6
+ @timing_decorator
7
+ def sample_function(x, y):
8
+ return x + y
9
+
10
+ result = sample_function(1, 2)
11
+ self.assertEqual(result, 3)
12
+
13
+ if __name__ == '__main__':
14
+ unittest.main()
@@ -0,0 +1,22 @@
1
+ import unittest
2
+ from unittest.mock import patch
3
+ import logging
4
+ from dev_tools.custom_handlers import LogDBHandler
5
+
6
+ class TestLogDBHandler(unittest.TestCase):
7
+ @patch('dev_tools.custom_handlers.pyodbc.connect')
8
+ def test_emit(self, mock_connect):
9
+ mock_connect.return_value.cursor.return_value.execute.return_value = None
10
+
11
+ logger = logging.getLogger('test_logger')
12
+ db_handler = LogDBHandler(db_table='test_table')
13
+ logger.addHandler(db_handler)
14
+ logger.setLevel(logging.INFO)
15
+
16
+ with self.assertLogs('test_logger', level='INFO') as log:
17
+ logger.info('This is a test log message')
18
+
19
+ self.assertIn('This is a test log message', log.output[0])
20
+
21
+ if __name__ == '__main__':
22
+ unittest.main()
@@ -0,0 +1,22 @@
1
+ import unittest
2
+ import os
3
+ from unittest.mock import patch
4
+ from dev_tools.debug_tools import is_debug_on, is_timing_on, logger_setup
5
+
6
+ class TestDebugTools(unittest.TestCase):
7
+ @patch.dict(os.environ, {'DEBUG': 'true', 'TIMING': 'true'})
8
+ def test_is_debug_on(self):
9
+ self.assertTrue(is_debug_on())
10
+
11
+ @patch.dict(os.environ, {'DEBUG': 'false', 'TIMING': 'true'})
12
+ def test_is_timing_on(self):
13
+ self.assertTrue(is_timing_on())
14
+
15
+ @patch('dev_tools.debug_tools.logging.config.fileConfig')
16
+ @patch('dev_tools.debug_tools.logging.config.dictConfig')
17
+ def test_logger_setup(self, mock_dictConfig, mock_fileConfig):
18
+ logger_setup()
19
+ mock_dictConfig.assert_called()
20
+
21
+ if __name__ == '__main__':
22
+ unittest.main()
@@ -0,0 +1,18 @@
1
+ import unittest
2
+ import os
3
+ from unittest.mock import patch
4
+ from dev_tools.progress_bar import progress_bar
5
+
6
+ class TestProgressBar(unittest.TestCase):
7
+
8
+ @patch('builtins.print')
9
+ @patch.dict(os.environ, {'DEBUG': 'true', 'TIMING': 'true'})
10
+ def test_progress_bar(self, mock_print):
11
+ items = list(range(10))
12
+ result = list(progress_bar(items))
13
+
14
+ self.assertEqual(result, items)
15
+ self.assertTrue(mock_print.called)
16
+
17
+ if __name__ == '__main__':
18
+ unittest.main()