maikol-utils 0.0.1__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.
- maikol-utils-0.0.1/LICENSE +21 -0
- maikol-utils-0.0.1/PKG-INFO +87 -0
- maikol-utils-0.0.1/README.md +79 -0
- maikol-utils-0.0.1/maikol-utils/__init__.py +3 -0
- maikol-utils-0.0.1/maikol-utils/file_utils.py +76 -0
- maikol-utils-0.0.1/maikol-utils/print_utils.py +142 -0
- maikol-utils-0.0.1/maikol-utils/time_tracker.py +267 -0
- maikol-utils-0.0.1/maikol_utils.egg-info/PKG-INFO +87 -0
- maikol-utils-0.0.1/maikol_utils.egg-info/SOURCES.txt +11 -0
- maikol-utils-0.0.1/maikol_utils.egg-info/dependency_links.txt +1 -0
- maikol-utils-0.0.1/maikol_utils.egg-info/top_level.txt +1 -0
- maikol-utils-0.0.1/setup.cfg +4 -0
- maikol-utils-0.0.1/setup.py +13 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Miquel Gomez Corral
|
|
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,87 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: maikol-utils
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Python module with some utils for every day code that I've usefull lately while working. Print, print colors, print warnings, print errors, save files, load files, clear bash
|
|
5
|
+
Author: Miquel GΓ³mez
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
|
|
9
|
+
# Welcoome to maikol-utils π§°
|
|
10
|
+
maikol-utils is a lightweight and reusable Python utility package designed to speed up and clean up your everyday scripting and automation workflows.
|
|
11
|
+
|
|
12
|
+
Whether you're handling JSON files, managing the file system, cleaning up terminal outputs, or improving logging with colored messages and structured separators β this module brings a collection of practical tools ready to use.
|
|
13
|
+
# π§βπ» Author
|
|
14
|
+
Developed by [Miquel GΓ³mez](https://miquelgc.net) β Python developer.
|
|
15
|
+
Hand-picked utilities from real-world scripting and automation tasks.
|
|
16
|
+
|
|
17
|
+
# π Features
|
|
18
|
+
- πΉ Filesystem helpers: Easily check, load, save, and create files/directories.
|
|
19
|
+
- π¨οΈ Colorful terminal prints: Add colored, formatted, or warning messages to your logs.
|
|
20
|
+
- π Visual bash separators: Make your CLI outputs more structured and readable.
|
|
21
|
+
- π§Ή Terminal cleanup: Clear or update printed lines in-place for dynamic feedback.
|
|
22
|
+
|
|
23
|
+
# π¦ Installation
|
|
24
|
+
|
|
25
|
+
- `pip install maikol-utils`
|
|
26
|
+
- Clone it from [github](https://github.com/MiquelGomezCorral/maikol-utils).
|
|
27
|
+
|
|
28
|
+
### Examples of each function at the repo notebook
|
|
29
|
+
- This [notebook](https://github.com/MiquelGomezCorral/maikol-utils/blob/main/usage_examples.ipynb)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# π Usage
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from maikol_utils import save_json, load_json, print_separator, print_warn, clear_bash
|
|
36
|
+
|
|
37
|
+
# Save data to JSON
|
|
38
|
+
save_json("outputs/data.json", {"name": "maikol"})
|
|
39
|
+
|
|
40
|
+
# Load safely from JSON
|
|
41
|
+
data = load_json("outputs/data.json")
|
|
42
|
+
|
|
43
|
+
# Print a section title with visual separator
|
|
44
|
+
print_separator("Processing Data", sep_type="SUPER")
|
|
45
|
+
|
|
46
|
+
# Warn with color
|
|
47
|
+
print_warn("This file is missing some fields!")
|
|
48
|
+
|
|
49
|
+
# Clean last 2 terminal lines
|
|
50
|
+
clear_bash(2)
|
|
51
|
+
```
|
|
52
|
+
# π Functions Overview
|
|
53
|
+
### π File system
|
|
54
|
+
```python
|
|
55
|
+
save_json(path, content) β Save Python object to JSON.
|
|
56
|
+
|
|
57
|
+
load_json(path) β Load JSON or return empty dict if not found.
|
|
58
|
+
|
|
59
|
+
check_dirs_existance(paths) β Assert if dirs exist.
|
|
60
|
+
|
|
61
|
+
make_dirs(paths) β Create dirs if not present.
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
### π¨ Terminal Printing
|
|
66
|
+
```python
|
|
67
|
+
print_separator(text, sep_type) β Print formatted section headers.
|
|
68
|
+
|
|
69
|
+
print_color(text, color) β Print in red, green, blue, etc.
|
|
70
|
+
|
|
71
|
+
print_warn(text) β Print warnings wrapped in β οΈ emojis.
|
|
72
|
+
|
|
73
|
+
print_status(msg) β Overwrite previous line with dynamic status.
|
|
74
|
+
|
|
75
|
+
clear_bash(n) β Clear n lines above in terminal.
|
|
76
|
+
|
|
77
|
+
print_clear_bash(text, n) β Clear then print a new message.
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# π§ Roadmap
|
|
82
|
+
|
|
83
|
+
- Add async support for I/O.
|
|
84
|
+
- Option to customize color themes.
|
|
85
|
+
- CLI preview tool for bash formatting.
|
|
86
|
+
|
|
87
|
+
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Welcoome to maikol-utils π§°
|
|
2
|
+
maikol-utils is a lightweight and reusable Python utility package designed to speed up and clean up your everyday scripting and automation workflows.
|
|
3
|
+
|
|
4
|
+
Whether you're handling JSON files, managing the file system, cleaning up terminal outputs, or improving logging with colored messages and structured separators β this module brings a collection of practical tools ready to use.
|
|
5
|
+
# π§βπ» Author
|
|
6
|
+
Developed by [Miquel GΓ³mez](https://miquelgc.net) β Python developer.
|
|
7
|
+
Hand-picked utilities from real-world scripting and automation tasks.
|
|
8
|
+
|
|
9
|
+
# π Features
|
|
10
|
+
- πΉ Filesystem helpers: Easily check, load, save, and create files/directories.
|
|
11
|
+
- π¨οΈ Colorful terminal prints: Add colored, formatted, or warning messages to your logs.
|
|
12
|
+
- π Visual bash separators: Make your CLI outputs more structured and readable.
|
|
13
|
+
- π§Ή Terminal cleanup: Clear or update printed lines in-place for dynamic feedback.
|
|
14
|
+
|
|
15
|
+
# π¦ Installation
|
|
16
|
+
|
|
17
|
+
- `pip install maikol-utils`
|
|
18
|
+
- Clone it from [github](https://github.com/MiquelGomezCorral/maikol-utils).
|
|
19
|
+
|
|
20
|
+
### Examples of each function at the repo notebook
|
|
21
|
+
- This [notebook](https://github.com/MiquelGomezCorral/maikol-utils/blob/main/usage_examples.ipynb)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# π Usage
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from maikol_utils import save_json, load_json, print_separator, print_warn, clear_bash
|
|
28
|
+
|
|
29
|
+
# Save data to JSON
|
|
30
|
+
save_json("outputs/data.json", {"name": "maikol"})
|
|
31
|
+
|
|
32
|
+
# Load safely from JSON
|
|
33
|
+
data = load_json("outputs/data.json")
|
|
34
|
+
|
|
35
|
+
# Print a section title with visual separator
|
|
36
|
+
print_separator("Processing Data", sep_type="SUPER")
|
|
37
|
+
|
|
38
|
+
# Warn with color
|
|
39
|
+
print_warn("This file is missing some fields!")
|
|
40
|
+
|
|
41
|
+
# Clean last 2 terminal lines
|
|
42
|
+
clear_bash(2)
|
|
43
|
+
```
|
|
44
|
+
# π Functions Overview
|
|
45
|
+
### π File system
|
|
46
|
+
```python
|
|
47
|
+
save_json(path, content) β Save Python object to JSON.
|
|
48
|
+
|
|
49
|
+
load_json(path) β Load JSON or return empty dict if not found.
|
|
50
|
+
|
|
51
|
+
check_dirs_existance(paths) β Assert if dirs exist.
|
|
52
|
+
|
|
53
|
+
make_dirs(paths) β Create dirs if not present.
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
### π¨ Terminal Printing
|
|
58
|
+
```python
|
|
59
|
+
print_separator(text, sep_type) β Print formatted section headers.
|
|
60
|
+
|
|
61
|
+
print_color(text, color) β Print in red, green, blue, etc.
|
|
62
|
+
|
|
63
|
+
print_warn(text) β Print warnings wrapped in β οΈ emojis.
|
|
64
|
+
|
|
65
|
+
print_status(msg) β Overwrite previous line with dynamic status.
|
|
66
|
+
|
|
67
|
+
clear_bash(n) β Clear n lines above in terminal.
|
|
68
|
+
|
|
69
|
+
print_clear_bash(text, n) β Clear then print a new message.
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# π§ Roadmap
|
|
74
|
+
|
|
75
|
+
- Add async support for I/O.
|
|
76
|
+
- Option to customize color themes.
|
|
77
|
+
- CLI preview tool for bash formatting.
|
|
78
|
+
|
|
79
|
+
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from .print_utils import print_warn
|
|
6
|
+
|
|
7
|
+
def save_json(save_path: str, content: Any):
|
|
8
|
+
"""
|
|
9
|
+
Saves a Python object as a JSON file.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
save_path (str): Full path including file name to save the JSON.
|
|
13
|
+
content (Any): The content to save (must be JSON-serializable).
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
Any: The original content.
|
|
17
|
+
"""
|
|
18
|
+
print(f"Saving output at {save_path}...")
|
|
19
|
+
|
|
20
|
+
# Extra safety for empty paths
|
|
21
|
+
dir_path = os.path.dirname(save_path)
|
|
22
|
+
if dir_path:
|
|
23
|
+
os.makedirs(dir_path, exist_ok=True)
|
|
24
|
+
|
|
25
|
+
with open(save_path, "w", encoding="utf-8") as out_json:
|
|
26
|
+
json.dump(content, out_json, indent=4)
|
|
27
|
+
|
|
28
|
+
return content
|
|
29
|
+
|
|
30
|
+
def load_json(save_path: str) -> Any:
|
|
31
|
+
"""
|
|
32
|
+
Loads JSON content from a file. Returns an empty dict if the file does not exist.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
save_path (str): Full path including file name to read the JSON from.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Any: The loaded content, or an empty dict if the file does not exist.
|
|
39
|
+
"""
|
|
40
|
+
if not os.path.exists(save_path):
|
|
41
|
+
print_warn(f"NO FILE AT {save_path}. Returning empty dict...")
|
|
42
|
+
return dict()
|
|
43
|
+
|
|
44
|
+
print(f"Loading output from {save_path}...")
|
|
45
|
+
with open(save_path, "r", encoding="utf-8") as out_json:
|
|
46
|
+
content = json.load(out_json)
|
|
47
|
+
|
|
48
|
+
return content
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def check_dirs_existance(directories: list[str]) -> None:
|
|
54
|
+
"""
|
|
55
|
+
Checks if all specified directories exist.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
directories (list[str]): List of directory paths to check.
|
|
59
|
+
|
|
60
|
+
Raises:
|
|
61
|
+
KeyError: If any of the directories do not exist.
|
|
62
|
+
"""
|
|
63
|
+
missing = [d for d in directories if not os.path.exists(d)]
|
|
64
|
+
if missing:
|
|
65
|
+
raise KeyError(f"Some paths were not found: {missing}")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def make_dirs(directories: list[str]) -> None:
|
|
69
|
+
"""
|
|
70
|
+
Creates the specified directories if they do not already exist.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
directories (list[str]): List of directory paths to create.
|
|
74
|
+
"""
|
|
75
|
+
for directory in directories:
|
|
76
|
+
os.makedirs(directory, exist_ok=True)
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
# ==========================================================================================
|
|
4
|
+
# GENERAL
|
|
5
|
+
# ==========================================================================================
|
|
6
|
+
|
|
7
|
+
separators = {
|
|
8
|
+
"separator_short" : "_"*32,
|
|
9
|
+
"separator_normal": "_"*64,
|
|
10
|
+
"separator_long" : "_"*128,
|
|
11
|
+
"separator_super" : "="*128,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
colors = {
|
|
15
|
+
"red": "\033[31m",
|
|
16
|
+
"green": "\033[32m",
|
|
17
|
+
"yellow": "\033[33m",
|
|
18
|
+
"blue": "\033[34m",
|
|
19
|
+
"white": "\033[0m",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
Colors = Literal["red", "green", "blue", "yellow", "white"]
|
|
23
|
+
|
|
24
|
+
def print_separator(text: str, sep_type: Literal["SHORT", "NORMAL", "LONG", "SUPER", "START"] = "NORMAL") -> None:
|
|
25
|
+
"""Prints a text with a line that separes the bash outputs. The size of this line is controled by sep_type
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
text (str): Text to print.
|
|
29
|
+
sep_type (Literal['SHORT', 'NORMAL', 'LONG', 'SUPER'], optional): Type of the separation line. Defaults to "NORMAL".
|
|
30
|
+
"""
|
|
31
|
+
if sep_type == "SHORT":
|
|
32
|
+
sep = separators["separator_short"]
|
|
33
|
+
elif sep_type == "NORMAL":
|
|
34
|
+
sep = separators["separator_normal"]
|
|
35
|
+
elif sep_type == "LONG":
|
|
36
|
+
sep = separators["separator_long"]
|
|
37
|
+
elif sep_type == "SUPER" or sep_type == "START":
|
|
38
|
+
sep = separators["separator_super"]
|
|
39
|
+
else:
|
|
40
|
+
sep = separator_normal
|
|
41
|
+
print_warn("WARNING: No separator with that label")
|
|
42
|
+
|
|
43
|
+
if sep_type == "SUPER":
|
|
44
|
+
print(sep)
|
|
45
|
+
print(f"{text:^{len(sep)}}")
|
|
46
|
+
print(sep + "\n")
|
|
47
|
+
elif sep_type == "START":
|
|
48
|
+
print_color(sep + "\n", color="blue")
|
|
49
|
+
print_color(f"{text:^{len(sep)}}\n", color="blue")
|
|
50
|
+
print_color(sep + "\n", color="blue")
|
|
51
|
+
else:
|
|
52
|
+
print(sep)
|
|
53
|
+
print(f"{text:^{len(sep)}}\n")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def print_color(text: str, color: Colors = "white", print_text: bool = True) -> str:
|
|
57
|
+
"""Prints the text with a certain color
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
text (str): Text to print
|
|
61
|
+
color (Literal['red', 'green', 'blue', 'white'], optional): Color to use. Defaults to "white".
|
|
62
|
+
print_text bool: Whether or not to print the color text (if false it will return it)
|
|
63
|
+
|
|
64
|
+
Return:
|
|
65
|
+
str: Text with colors
|
|
66
|
+
"""
|
|
67
|
+
if color == "red":
|
|
68
|
+
color = colors["color_red"]
|
|
69
|
+
elif color == "green":
|
|
70
|
+
color = colors["color_green"]
|
|
71
|
+
elif color == "blue":
|
|
72
|
+
color = colors["color_blue"]
|
|
73
|
+
elif color == "yellow":
|
|
74
|
+
color = colors["color_yellow"]
|
|
75
|
+
else:
|
|
76
|
+
color = colors["color_reset"]
|
|
77
|
+
|
|
78
|
+
text: str = f"{color}{text}{colors["color_reset"]}"
|
|
79
|
+
|
|
80
|
+
if print_text:
|
|
81
|
+
print(f"{text}")
|
|
82
|
+
|
|
83
|
+
return text
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def print_warn(text: str, color: Colors = "yellow") -> str:
|
|
87
|
+
"""Adds the text between teh following emoji β οΈ...β οΈ
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
text (str): Text to print in warn
|
|
91
|
+
color (Colors, optional): Color of the warning text. Defaults to "yellow".
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
str: Text with color and emojis
|
|
95
|
+
"""
|
|
96
|
+
return print_color(f"β οΈ{text}β οΈ", color=color)
|
|
97
|
+
|
|
98
|
+
def print_error(text: str, color: Colors = "red") -> str:
|
|
99
|
+
"""Adds the text between teh following emoji β...β
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
text (str): Text to print in warn
|
|
103
|
+
color (Colors, optional): Color of the error text. Defaults to "red".
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
str: Text with color and emojis
|
|
107
|
+
"""
|
|
108
|
+
return print_color(f"β{text}β", color=color)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# ==========================================================================================
|
|
112
|
+
# CLEAR LINES
|
|
113
|
+
# ==========================================================================================
|
|
114
|
+
def print_status(msg: str):
|
|
115
|
+
"""Prints a dynamic status message on the same terminal line.
|
|
116
|
+
|
|
117
|
+
Useful for updating progress or status in-place (e.g. during loops),
|
|
118
|
+
preventing multiple lines of output.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
msg (str): Message to display.
|
|
122
|
+
"""
|
|
123
|
+
clear_line = " " * 200 # assume max 200 chars per line
|
|
124
|
+
print(f"\r{clear_line}\r{msg}", end="", flush=True)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def clear_bash(n_lines: int = 1) -> None:
|
|
128
|
+
"""Cleans the bash output by removing the last n lines.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
n_lines (int, optional): Number of lines to remove. Defaults to 1.
|
|
132
|
+
"""
|
|
133
|
+
print("\033[F\033[K"*n_lines, end="") # Move cursor up one line and clear that line
|
|
134
|
+
|
|
135
|
+
def print_clear_bash(text: str, n_lines: int = 1) -> None:
|
|
136
|
+
"""Cleans the bash output by removing the last n lines.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
n_lines (int, optional): Number of lines to remove. Defaults to 1.
|
|
140
|
+
"""
|
|
141
|
+
clear_bash(n_lines)
|
|
142
|
+
print(text)
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"""Module with all the Time traking utils functions."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
import json
|
|
5
|
+
from typing import Tuple, Optional, TextIO
|
|
6
|
+
|
|
7
|
+
from .print_utils import print_separator, print_warn
|
|
8
|
+
|
|
9
|
+
# =================================================
|
|
10
|
+
# HELPER FUNCTIONS
|
|
11
|
+
# =================================================
|
|
12
|
+
|
|
13
|
+
def parse_seconds_to_minutes(sec: float) -> str:
|
|
14
|
+
"""
|
|
15
|
+
Parses a duration in seconds into a formatted string with hours, minutes, and seconds.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
sec (float): Number of seconds.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
str: Formatted time string (e.g., '01 hrs, 05 mins, 30.1234 sec').
|
|
22
|
+
"""
|
|
23
|
+
hours = int(sec // 3600)
|
|
24
|
+
minutes = int((sec % 3600) // 60)
|
|
25
|
+
seconds = int(sec % 60)
|
|
26
|
+
decimals = int((sec % 1) * 10000)
|
|
27
|
+
|
|
28
|
+
if hours > 0:
|
|
29
|
+
return f"{hours:02} hrs, {minutes:02} mins, {seconds:02}.{decimals:04} sec"
|
|
30
|
+
elif minutes > 0:
|
|
31
|
+
return f"{minutes:02} mins, {seconds:02}.{decimals:04} sec"
|
|
32
|
+
else:
|
|
33
|
+
return f"{seconds:02}.{decimals:04} sec"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def print_time(sec: float, n_files: Optional[int] = None, space: bool = False, prefix: str = "", sufix: str = "", out_file: Optional[TextIO] = None) -> None:
|
|
37
|
+
"""Given a certain number of seconds, parse it to Formatted time string (e.g., '01 hrs, 05 mins, 30.1234 sec').
|
|
38
|
+
If not enough seconds for hours, just '05 mins, 30.1234 sec'.
|
|
39
|
+
If neither enogh seconds for minuts, just parse '30.1234 sec'.
|
|
40
|
+
Optionally you can add a 'number of files' to get avg metrics as well as extra config for better printing.
|
|
41
|
+
Optionally you can pass a file to print everyting there.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
sec (float): Number of seconds
|
|
45
|
+
n_files (Optional[int], optional): Number of files to add an avg. Defaults to None.
|
|
46
|
+
space (bool, optional): To add a space before the print. Defaults to False.
|
|
47
|
+
prefix (str, optional): To add a prefix before the print. Defaults to "".
|
|
48
|
+
sufix (str, optional): To add a sufix after the print. Defaults to "".
|
|
49
|
+
out_file (Optional[TextIO], optional): To print the line somewere that's not the std bash (keed None for bash). Defaults to None.
|
|
50
|
+
"""
|
|
51
|
+
if space:
|
|
52
|
+
print("")
|
|
53
|
+
|
|
54
|
+
if not prefix.endswith(" "):
|
|
55
|
+
prefix = f"{prefix} "
|
|
56
|
+
|
|
57
|
+
if n_files is not None:
|
|
58
|
+
message = f"{prefix} - {n_files:4} files in: {parse_seconds_to_minutes(sec)}{sufix}.\n"
|
|
59
|
+
message += f" - Per document: {parse_seconds_to_minutes(sec / n_files)}"
|
|
60
|
+
else:
|
|
61
|
+
message = f"{prefix}Time: {parse_seconds_to_minutes(sec)}{sufix}."
|
|
62
|
+
|
|
63
|
+
if out_file:
|
|
64
|
+
print(message, file=out_file)
|
|
65
|
+
else:
|
|
66
|
+
print(message)
|
|
67
|
+
|
|
68
|
+
# =================================================
|
|
69
|
+
# TIME TRACKER
|
|
70
|
+
# =================================================
|
|
71
|
+
|
|
72
|
+
class TimeTracker:
|
|
73
|
+
"""
|
|
74
|
+
Class for tracking the time of a process. It allows to track the time of different points in the process and save them in a json file.
|
|
75
|
+
It also allows to track the time of different laps in the process.
|
|
76
|
+
"""
|
|
77
|
+
def __init__(self, name: str, start_track_now: bool = False ):
|
|
78
|
+
self.name = name
|
|
79
|
+
self.hist: dict[str, Tuple[float, float]] = dict()
|
|
80
|
+
self.started: bool = False
|
|
81
|
+
self.last_time: float = -1.0
|
|
82
|
+
|
|
83
|
+
self.lap_hist = dict()
|
|
84
|
+
self.lap_runing: bool = False
|
|
85
|
+
self.lap_number: int = 0
|
|
86
|
+
|
|
87
|
+
if start_track_now:
|
|
88
|
+
self.start(verbose=False)
|
|
89
|
+
|
|
90
|
+
print_separator(f"β³ TIME TRACKER '{name}' INITIALIZED{'. STARTING NOW' if start_track_now else ''}! β³")
|
|
91
|
+
|
|
92
|
+
def start(self, verbose: bool = True, space: bool = True):
|
|
93
|
+
"""
|
|
94
|
+
Starts traking the time
|
|
95
|
+
"""
|
|
96
|
+
self.started = True
|
|
97
|
+
self.track("START", verbose=verbose, space=space)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def track(self, tag: str, verbose: bool = True, space: bool = False, mute_warning: bool = False) -> float:
|
|
101
|
+
"""
|
|
102
|
+
Track the time of a certain point and add it a tag. Return time since las track
|
|
103
|
+
"""
|
|
104
|
+
if not self.started and not mute_warning:
|
|
105
|
+
print_warn("WARNING: Traking without startng, will call start.")
|
|
106
|
+
self.start()
|
|
107
|
+
|
|
108
|
+
t = time.time()
|
|
109
|
+
diff = t - self.last_time if self.last_time > 0 else 0
|
|
110
|
+
|
|
111
|
+
if self.lap_runing:
|
|
112
|
+
if tag in self.lap_hist:
|
|
113
|
+
tag = f"{tag}_{self.lap_number}"
|
|
114
|
+
self.lap_hist[tag] = (t, diff)
|
|
115
|
+
else:
|
|
116
|
+
if tag in self.hist:
|
|
117
|
+
tag = f"{tag}_"
|
|
118
|
+
self.hist[tag] = (t, diff)
|
|
119
|
+
|
|
120
|
+
if verbose:
|
|
121
|
+
print_tag = tag if not self.lap_runing else f"{tag} lap {self.lap_number}"
|
|
122
|
+
print_time(diff, prefix=f"β³ {print_tag}", sufix=" β³", space=space)
|
|
123
|
+
|
|
124
|
+
self.last_time = t
|
|
125
|
+
return diff
|
|
126
|
+
|
|
127
|
+
# ============================================================================
|
|
128
|
+
# LAPS MANAGEMENT
|
|
129
|
+
# ============================================================================
|
|
130
|
+
def start_lap(self, N: int = None, verbose: bool = False, mute_warning: bool = False) -> int:
|
|
131
|
+
"""Starts a new lap with its oun metrics and returns the number of the current started lap
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
N (int, optional): Total number of potential laps. Defaults to None.
|
|
135
|
+
verbose (bool, optional): Print the number of the lap. Defaults to False.
|
|
136
|
+
mute_warning (bool, optional): Show or not warnings. Defaults to False.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
int: Number of the current started lap.
|
|
140
|
+
"""
|
|
141
|
+
self.lap_runing = True
|
|
142
|
+
self.lap_number += 1
|
|
143
|
+
if len(self.lap_hist) > 0 and not mute_warning:
|
|
144
|
+
print_warn("WARNING: Starting lap without finishing previous. The records will be overritten.")
|
|
145
|
+
|
|
146
|
+
t = time.time()
|
|
147
|
+
self.lap_hist["START_LAP"] = (t, 0)
|
|
148
|
+
self.last_time = t
|
|
149
|
+
|
|
150
|
+
if verbose:
|
|
151
|
+
print(f"β³ Starting lap num {self.lap_number}{f'/{N} ' if N is not None else ''}β³!")
|
|
152
|
+
|
|
153
|
+
return self.lap_number
|
|
154
|
+
|
|
155
|
+
def finish_lap(self):
|
|
156
|
+
"""Finish lap and add the point trak to the list
|
|
157
|
+
"""
|
|
158
|
+
self.lap_runing = False
|
|
159
|
+
|
|
160
|
+
t = time.time()
|
|
161
|
+
self.lap_hist["FINISH_LAP"] = (t, t-self.lap_hist["START_LAP"][0])
|
|
162
|
+
|
|
163
|
+
# Update possible previous times
|
|
164
|
+
for tag, (t, diff) in self.lap_hist.items():
|
|
165
|
+
if tag in self.hist:
|
|
166
|
+
_, prev_diff = self.hist[tag]
|
|
167
|
+
self.hist[tag] = (t, prev_diff + diff)
|
|
168
|
+
else:
|
|
169
|
+
self.hist[tag] = (t, diff)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
self.lap_hist = dict()
|
|
173
|
+
|
|
174
|
+
# ============================================================================
|
|
175
|
+
# STIMATE TIME
|
|
176
|
+
# ============================================================================
|
|
177
|
+
def stimate_lap_time(self, N: int, mute_warning: bool = False):
|
|
178
|
+
"""Stimate the time to finish N laps after all the alread finished laps
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
N (int): Total number of laps (including those already done)
|
|
182
|
+
mute_warning (bool, optional): Whether or not mute the waning about not running laps. Defaults to False.
|
|
183
|
+
"""
|
|
184
|
+
if not self.lap_runing and not mute_warning:
|
|
185
|
+
print_warn("WARNING: Stimating lap without starting it. Returning...")
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
t_f_end = time.time()
|
|
189
|
+
eta = (N - self.lap_number) * (t_f_end - self.hist["START"][0]) / self.lap_number
|
|
190
|
+
print_time(
|
|
191
|
+
t_f_end - self.lap_hist["START_LAP"][0],
|
|
192
|
+
prefix="Total ",
|
|
193
|
+
sufix=f". ETA: {parse_seconds_to_minutes(eta)}"
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# ============================================================================
|
|
197
|
+
# METRICS MANAGEMENT
|
|
198
|
+
# ============================================================================
|
|
199
|
+
|
|
200
|
+
def get_metrics(self, n: int = None, initial_tag: str = "START") -> dict:
|
|
201
|
+
"""
|
|
202
|
+
Return a dict with all the metrics with the form: tag: (time, diff)
|
|
203
|
+
Added Normalized if n of samples is passed with the form: tag: (time, diff, diff/n)
|
|
204
|
+
|
|
205
|
+
initial_tag change it in case it hasn't been set as 'START' for the first track
|
|
206
|
+
"""
|
|
207
|
+
t = time.time()
|
|
208
|
+
if len(self.hist) > 0:
|
|
209
|
+
if initial_tag not in self.hist:
|
|
210
|
+
print_warn(f"WARNING: Passed initial tag '{initial_tag}' not found in history. Setting to first.")
|
|
211
|
+
initial_tag = next(iter(self.hist)) # Getting the firts added tag
|
|
212
|
+
self.hist["TOTAL"] = (t, t - self.hist[initial_tag][0])
|
|
213
|
+
|
|
214
|
+
else:
|
|
215
|
+
print_warn("WARNING: Getting metrics with 0 tracked points. This will return an empty dict.")
|
|
216
|
+
|
|
217
|
+
if n is not None:
|
|
218
|
+
res_hist = {
|
|
219
|
+
tag: (time, diff, diff/n) for tag, (time, diff) in self.hist.items()
|
|
220
|
+
}
|
|
221
|
+
else:
|
|
222
|
+
res_hist = self.hist.copy()
|
|
223
|
+
|
|
224
|
+
if "START_LAP" in res_hist:
|
|
225
|
+
res_hist.pop("START_LAP")
|
|
226
|
+
return res_hist
|
|
227
|
+
|
|
228
|
+
def save_metric(self, save_path: str, n: int = None) -> dict:
|
|
229
|
+
"""Compute metrics, save them into a file and return them
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
save_path (str): Save path for the metrics
|
|
233
|
+
n (int, optional): 'Number of files' processed to get and avg. Defaults to None.
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
dict: Computed metrics
|
|
237
|
+
"""
|
|
238
|
+
metrics = self.get_metrics(n)
|
|
239
|
+
|
|
240
|
+
with open(save_path, "w") as f:
|
|
241
|
+
json.dump(metrics, f)
|
|
242
|
+
|
|
243
|
+
return metrics
|
|
244
|
+
|
|
245
|
+
def print_metrics(self, n: int = None, out_file: TextIO = None) -> dict:
|
|
246
|
+
"""Compute and print the metrics. Optionally into a file.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
n (int, optional): 'Number of files' processed to get and avg. Defaults to None.
|
|
250
|
+
out_file (TextIO, optional): File where printing should be done. Defaults to None.
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
dict: Computed metrics
|
|
254
|
+
"""
|
|
255
|
+
metrics = self.get_metrics(n)
|
|
256
|
+
metrics.pop('START', None)
|
|
257
|
+
print("")
|
|
258
|
+
if n is not None:
|
|
259
|
+
print(f"Processed {n} files in total\n", file=out_file)
|
|
260
|
+
|
|
261
|
+
for tag, records in metrics.items():
|
|
262
|
+
diff = records[1]
|
|
263
|
+
|
|
264
|
+
print_time(diff, n_files=n, prefix=tag, out_file=out_file)
|
|
265
|
+
|
|
266
|
+
return metrics
|
|
267
|
+
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: maikol-utils
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Python module with some utils for every day code that I've usefull lately while working. Print, print colors, print warnings, print errors, save files, load files, clear bash
|
|
5
|
+
Author: Miquel GΓ³mez
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
|
|
9
|
+
# Welcoome to maikol-utils π§°
|
|
10
|
+
maikol-utils is a lightweight and reusable Python utility package designed to speed up and clean up your everyday scripting and automation workflows.
|
|
11
|
+
|
|
12
|
+
Whether you're handling JSON files, managing the file system, cleaning up terminal outputs, or improving logging with colored messages and structured separators β this module brings a collection of practical tools ready to use.
|
|
13
|
+
# π§βπ» Author
|
|
14
|
+
Developed by [Miquel GΓ³mez](https://miquelgc.net) β Python developer.
|
|
15
|
+
Hand-picked utilities from real-world scripting and automation tasks.
|
|
16
|
+
|
|
17
|
+
# π Features
|
|
18
|
+
- πΉ Filesystem helpers: Easily check, load, save, and create files/directories.
|
|
19
|
+
- π¨οΈ Colorful terminal prints: Add colored, formatted, or warning messages to your logs.
|
|
20
|
+
- π Visual bash separators: Make your CLI outputs more structured and readable.
|
|
21
|
+
- π§Ή Terminal cleanup: Clear or update printed lines in-place for dynamic feedback.
|
|
22
|
+
|
|
23
|
+
# π¦ Installation
|
|
24
|
+
|
|
25
|
+
- `pip install maikol-utils`
|
|
26
|
+
- Clone it from [github](https://github.com/MiquelGomezCorral/maikol-utils).
|
|
27
|
+
|
|
28
|
+
### Examples of each function at the repo notebook
|
|
29
|
+
- This [notebook](https://github.com/MiquelGomezCorral/maikol-utils/blob/main/usage_examples.ipynb)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# π Usage
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from maikol_utils import save_json, load_json, print_separator, print_warn, clear_bash
|
|
36
|
+
|
|
37
|
+
# Save data to JSON
|
|
38
|
+
save_json("outputs/data.json", {"name": "maikol"})
|
|
39
|
+
|
|
40
|
+
# Load safely from JSON
|
|
41
|
+
data = load_json("outputs/data.json")
|
|
42
|
+
|
|
43
|
+
# Print a section title with visual separator
|
|
44
|
+
print_separator("Processing Data", sep_type="SUPER")
|
|
45
|
+
|
|
46
|
+
# Warn with color
|
|
47
|
+
print_warn("This file is missing some fields!")
|
|
48
|
+
|
|
49
|
+
# Clean last 2 terminal lines
|
|
50
|
+
clear_bash(2)
|
|
51
|
+
```
|
|
52
|
+
# π Functions Overview
|
|
53
|
+
### π File system
|
|
54
|
+
```python
|
|
55
|
+
save_json(path, content) β Save Python object to JSON.
|
|
56
|
+
|
|
57
|
+
load_json(path) β Load JSON or return empty dict if not found.
|
|
58
|
+
|
|
59
|
+
check_dirs_existance(paths) β Assert if dirs exist.
|
|
60
|
+
|
|
61
|
+
make_dirs(paths) β Create dirs if not present.
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
### π¨ Terminal Printing
|
|
66
|
+
```python
|
|
67
|
+
print_separator(text, sep_type) β Print formatted section headers.
|
|
68
|
+
|
|
69
|
+
print_color(text, color) β Print in red, green, blue, etc.
|
|
70
|
+
|
|
71
|
+
print_warn(text) β Print warnings wrapped in β οΈ emojis.
|
|
72
|
+
|
|
73
|
+
print_status(msg) β Overwrite previous line with dynamic status.
|
|
74
|
+
|
|
75
|
+
clear_bash(n) β Clear n lines above in terminal.
|
|
76
|
+
|
|
77
|
+
print_clear_bash(text, n) β Clear then print a new message.
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# π§ Roadmap
|
|
82
|
+
|
|
83
|
+
- Add async support for I/O.
|
|
84
|
+
- Option to customize color themes.
|
|
85
|
+
- CLI preview tool for bash formatting.
|
|
86
|
+
|
|
87
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
setup.py
|
|
4
|
+
maikol-utils/__init__.py
|
|
5
|
+
maikol-utils/file_utils.py
|
|
6
|
+
maikol-utils/print_utils.py
|
|
7
|
+
maikol-utils/time_tracker.py
|
|
8
|
+
maikol_utils.egg-info/PKG-INFO
|
|
9
|
+
maikol_utils.egg-info/SOURCES.txt
|
|
10
|
+
maikol_utils.egg-info/dependency_links.txt
|
|
11
|
+
maikol_utils.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
maikol-utils
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="maikol-utils",
|
|
5
|
+
version="0.0.1",
|
|
6
|
+
packages=find_packages(), #find_packages(where="src"),
|
|
7
|
+
author="Miquel GΓ³mez",
|
|
8
|
+
description="Python module with some utils for every day code that I've usefull lately while working. Print, print colors, print warnings, print errors, save files, load files, clear bash",
|
|
9
|
+
long_description=open("README.md").read(),
|
|
10
|
+
long_description_content_type="text/markdown",
|
|
11
|
+
install_requires=[
|
|
12
|
+
]
|
|
13
|
+
)
|