atomicshop 2.2.9__py3-none-any.whl → 2.3.0__py3-none-any.whl
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.
Potentially problematic release.
This version of atomicshop might be problematic. Click here for more details.
- atomicshop/__init__.py +1 -1
- atomicshop/basics/bytes_arrays.py +34 -1
- atomicshop/basics/dicts.py +35 -0
- atomicshop/basics/dicts_nested.py +30 -0
- atomicshop/basics/enums.py +70 -0
- atomicshop/basics/strings.py +50 -0
- atomicshop/datetimes.py +36 -0
- atomicshop/domains.py +7 -2
- atomicshop/file_io/jsons.py +0 -1
- atomicshop/file_io/tomls.py +24 -0
- atomicshop/file_io/xlsxs.py +58 -0
- atomicshop/filesystem.py +77 -49
- atomicshop/mitm/initialize_mitm_server.py +2 -2
- atomicshop/mitm/statistic_analyzer.py +465 -0
- atomicshop/ssh_scripts/process_from_port.py +6 -1
- atomicshop/wrappers/factw/__init__.py +0 -0
- atomicshop/wrappers/factw/fact_config.py +2 -0
- atomicshop/wrappers/factw/upload_firmware.py +80 -0
- atomicshop/wrappers/loggingw/reading.py +10 -12
- atomicshop/wrappers/socketw/socket_server_tester.py +4 -4
- {atomicshop-2.2.9.dist-info → atomicshop-2.3.0.dist-info}/METADATA +3 -1
- {atomicshop-2.2.9.dist-info → atomicshop-2.3.0.dist-info}/RECORD +25 -18
- {atomicshop-2.2.9.dist-info → atomicshop-2.3.0.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.2.9.dist-info → atomicshop-2.3.0.dist-info}/WHEEL +0 -0
- {atomicshop-2.2.9.dist-info → atomicshop-2.3.0.dist-info}/top_level.txt +0 -0
atomicshop/__init__.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# v1.0.0
|
|
2
1
|
def get_single_byte_from_byte_string(byte_string, index: int):
|
|
3
2
|
"""
|
|
4
3
|
Function extracts single byte as byte from byte string object.
|
|
@@ -18,3 +17,37 @@ def get_single_byte_from_byte_string(byte_string, index: int):
|
|
|
18
17
|
"""
|
|
19
18
|
|
|
20
19
|
return byte_string[index:index+1]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def convert_sequence_of_bytes_to_sequence_of_strings(byte_sequence: bytes) -> list[str]:
|
|
23
|
+
"""
|
|
24
|
+
Convert sequence of bytes to sequence of strings.
|
|
25
|
+
:param byte_sequence: bytes, sequence of bytes.
|
|
26
|
+
:return: list of strings.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
result: list[str] = list()
|
|
30
|
+
for byte_index, single_byte in enumerate(byte_sequence):
|
|
31
|
+
# Convert byte to string character.
|
|
32
|
+
string_from_byte = chr(single_byte)
|
|
33
|
+
|
|
34
|
+
# Check if string is alphanumeric.
|
|
35
|
+
if string_from_byte.isalnum():
|
|
36
|
+
# Append string to result.
|
|
37
|
+
result.append(string_from_byte)
|
|
38
|
+
# If string is not alphanumeric, it means that it is something like '\x04'.
|
|
39
|
+
# So, we need to output '04'. We need to remove the '\x' from the string.
|
|
40
|
+
# The problem is that the string doesn't contain 4 characters, but 1.
|
|
41
|
+
# len(string_from_byte) returns 1.
|
|
42
|
+
# So, we can't remove '\x' from the string, since it is the character '\x04' in the table.
|
|
43
|
+
# We will convert it to printable string with repr() function.
|
|
44
|
+
# repr() function returns string with quotes, so we need to remove them with [1:-1].
|
|
45
|
+
else:
|
|
46
|
+
printable_string = repr(string_from_byte)[1:-1]
|
|
47
|
+
# Remove '\x' from the string.
|
|
48
|
+
printable_string = printable_string.replace('\\x', '')
|
|
49
|
+
result.append(printable_string)
|
|
50
|
+
|
|
51
|
+
# single_byte_string = byte_sequence[byte_index:byte_index+1]
|
|
52
|
+
|
|
53
|
+
return result
|
atomicshop/basics/dicts.py
CHANGED
|
@@ -118,3 +118,38 @@ def merge(dict1, dict2) -> dict:
|
|
|
118
118
|
def find_key_by_value(input_dict, value):
|
|
119
119
|
# This will return set of keys. If non found, will return empty set.
|
|
120
120
|
return {k for k, v in input_dict.items() if v == value}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def sort_by_values(input_dict: dict, reverse: bool = False) -> dict:
|
|
124
|
+
"""
|
|
125
|
+
The function will sort a dictionary by its values.
|
|
126
|
+
Example:
|
|
127
|
+
# Define dictionary.
|
|
128
|
+
test = {
|
|
129
|
+
'key1': '1',
|
|
130
|
+
'key2': '2',
|
|
131
|
+
'key3': '8',
|
|
132
|
+
'key4': '37',
|
|
133
|
+
'key5': '5',
|
|
134
|
+
'key6': '23'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
# Sort dictionary.
|
|
138
|
+
sorted_dict = sort_by_values(test, reverse=True)
|
|
139
|
+
|
|
140
|
+
# Result:
|
|
141
|
+
# {
|
|
142
|
+
# 'key4': '37',
|
|
143
|
+
# 'key6': '23',
|
|
144
|
+
# 'key3': '8',
|
|
145
|
+
# 'key5': '5',
|
|
146
|
+
# 'key2': '2',
|
|
147
|
+
# 'key1': '1'
|
|
148
|
+
# }
|
|
149
|
+
|
|
150
|
+
:param input_dict: dict, the dictionary to sort.
|
|
151
|
+
:param reverse: bool, if True, the dictionary will be sorted in reverse order.
|
|
152
|
+
:return: dict, the sorted dictionary.
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
return dict(sorted(input_dict.items(), key=lambda item: item[1], reverse=reverse))
|
|
@@ -70,3 +70,33 @@ def find_key_by_value(input_dict: dict, string1: str, string2: str = str(), key_
|
|
|
70
70
|
final_key_list.append(temp_list)
|
|
71
71
|
|
|
72
72
|
return final_key_list
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def find_key_of_last_nest(input_dict: dict) -> list:
|
|
76
|
+
"""
|
|
77
|
+
Returns list of keys of the last nest in the dictionary.
|
|
78
|
+
:param input_dict: dict, the dictionary to search.
|
|
79
|
+
:return: list of keys in order.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
for key, value in input_dict.items():
|
|
83
|
+
if isinstance(value, dict):
|
|
84
|
+
return [key] + find_key_of_last_nest(value)
|
|
85
|
+
elif isinstance(value, list):
|
|
86
|
+
return [key] + find_key_of_last_nest(value[0])
|
|
87
|
+
else:
|
|
88
|
+
return [key]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def find_key_of_last_dict(input_dict: dict) -> list:
|
|
92
|
+
"""
|
|
93
|
+
Returns list of keys of the last dictionary in the dict.
|
|
94
|
+
:param input_dict: dict, the dictionary to search.
|
|
95
|
+
:return: list of keys in order.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
for key, value in input_dict.items():
|
|
99
|
+
if isinstance(value, dict):
|
|
100
|
+
return [key] + find_key_of_last_dict(value)
|
|
101
|
+
else:
|
|
102
|
+
return []
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ComparisonOperator(Enum):
|
|
6
|
+
"""
|
|
7
|
+
Enum class that was created for 'scan_directory_and_return_list' function.
|
|
8
|
+
Specifically for 'file_name_check_tuple[1]' - second entry.
|
|
9
|
+
"""
|
|
10
|
+
EQ = '__eq__'
|
|
11
|
+
CONTAINS = '__contains__'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def __comparison_usage_example(
|
|
15
|
+
directory_fullpath: str,
|
|
16
|
+
file_name_check_tuple: tuple = tuple()):
|
|
17
|
+
"""
|
|
18
|
+
The function receives a filesystem directory as string, scans it recursively for files and returns list of
|
|
19
|
+
full paths to that file (including).
|
|
20
|
+
If 'file_name_check_tuple' specified, the function will return only list of files that answer to the input
|
|
21
|
+
of that tuple.
|
|
22
|
+
Recursive.
|
|
23
|
+
|
|
24
|
+
:param directory_fullpath: string to full path to directory on the filesystem to scan.
|
|
25
|
+
:param file_name_check_tuple: tuple that should contain 2 entries:
|
|
26
|
+
file_name_check_tuple[0]: string that will contain part of file name to check or full file name with extension.
|
|
27
|
+
file_name_check_tuple[1]: 'ComparisonOperator' object to test against found file name with extension.
|
|
28
|
+
'ComparisonOperator.EQ' will check for specific file name (eg: 'config_file.ini').
|
|
29
|
+
'ComparisonOperator.CONTAINS' will check if file name contains part of the string (eg: 'config')(eg: '.ini')
|
|
30
|
+
|
|
31
|
+
:return: list of all found filenames with full file paths, list with relative folders to file excluding the
|
|
32
|
+
main folder.
|
|
33
|
+
|
|
34
|
+
Usage:
|
|
35
|
+
from atomicshop.filesystem import get_file_paths_and_relative_directories, ComparisonOperator
|
|
36
|
+
|
|
37
|
+
# Get full paths of all the 'engine_config.ini' files.
|
|
38
|
+
engine_config_path_list = get_file_paths_and_relative_directories(
|
|
39
|
+
directory_fullpath=some_directory_path,
|
|
40
|
+
file_name_check_tuple=(config_file_name, ComparisonOperator.EQ))
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
# Type checking.
|
|
44
|
+
if file_name_check_tuple:
|
|
45
|
+
if not isinstance(file_name_check_tuple[1], ComparisonOperator):
|
|
46
|
+
raise TypeError(f'Second entry of tuple "file_name_check_tuple" is not of "ComparisonOperator" type.')
|
|
47
|
+
|
|
48
|
+
# "Walk" over all the directories and subdirectories - make list of full file paths inside the directory
|
|
49
|
+
# recursively.
|
|
50
|
+
for dirpath, subdirs, files in os.walk(directory_fullpath):
|
|
51
|
+
# Iterate through all the file names that were found in the folder.
|
|
52
|
+
for file in files:
|
|
53
|
+
# If 'file_name_check_tuple' was passed.
|
|
54
|
+
if file_name_check_tuple:
|
|
55
|
+
# Get separate variables from the tuple.
|
|
56
|
+
# 'check_string' is a string that will be checked against 'file' iteration, which also a string.
|
|
57
|
+
# 'comparison_operator' is 'ComparisonOperator' Enum object, that contains the string method
|
|
58
|
+
# operator that will be used against the 'check_string'.
|
|
59
|
+
check_string, comparison_operator = file_name_check_tuple
|
|
60
|
+
# 'getattr' adds the string comparison method to the 'file' string. Example:
|
|
61
|
+
# file.__eq__
|
|
62
|
+
# 'comparison_operator' is the Enum class representation and '.value' method is the string
|
|
63
|
+
# representation of '__eq__'.
|
|
64
|
+
# and after that comes the check string to check against:
|
|
65
|
+
# file.__eq__(check_string)
|
|
66
|
+
if getattr(file, comparison_operator.value)(check_string):
|
|
67
|
+
return
|
|
68
|
+
# If 'file_name_check_tuple' wasn't passed, then get all the files.
|
|
69
|
+
else:
|
|
70
|
+
return
|
atomicshop/basics/strings.py
CHANGED
|
@@ -159,11 +159,16 @@ def match_pattern_against_string(pattern: str, check_string: str, prefix_suffix:
|
|
|
159
159
|
# Replace the wildcard string '*' with regex wildcard string '.+'.
|
|
160
160
|
# In regex '.' is a wildcard, but only for 1 character, if you need more than 1 character you should add '+'.
|
|
161
161
|
pattern_re = pattern.replace(wildcard_str, wildcard_re)
|
|
162
|
+
pattern_no_wildcard = pattern.replace(wildcard_str, '')
|
|
162
163
|
|
|
163
164
|
# Search for pattern, if found, return 'True'.
|
|
164
165
|
if search_pattern(pattern_re):
|
|
165
166
|
return True
|
|
166
167
|
|
|
168
|
+
# 'regex' doesn't think that '.*' is a match for 'test', so we'll check for this case separately.
|
|
169
|
+
if search_pattern(pattern_no_wildcard):
|
|
170
|
+
return True
|
|
171
|
+
|
|
167
172
|
# If it wasn't found in previous check,
|
|
168
173
|
# then we'll continue checking without prefix and suffix if 'prefix_suffix' is 'True'.
|
|
169
174
|
if prefix_suffix:
|
|
@@ -268,6 +273,14 @@ def is_numeric_only(string: str) -> bool:
|
|
|
268
273
|
return string.isdigit()
|
|
269
274
|
|
|
270
275
|
|
|
276
|
+
def is_alphabetic_only(string: str) -> bool:
|
|
277
|
+
"""
|
|
278
|
+
Function to check if string contains only alphabetic characters.
|
|
279
|
+
"""
|
|
280
|
+
|
|
281
|
+
return string.isalpha()
|
|
282
|
+
|
|
283
|
+
|
|
271
284
|
def replace_words_with_values_from_dict(
|
|
272
285
|
sentence: str, dictionary: dict, contains: bool = False, case_insensitive: bool = False) -> str:
|
|
273
286
|
"""
|
|
@@ -305,3 +318,40 @@ def replace_words_with_values_from_dict(
|
|
|
305
318
|
joined_sentence: str = ' '.join(sentence_parts)
|
|
306
319
|
|
|
307
320
|
return joined_sentence
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def multiple_splits_by_delimiters(full_string: str, delimiters: list) -> list[str]:
|
|
324
|
+
"""
|
|
325
|
+
Function splits the string by multiple delimiters.
|
|
326
|
+
:param full_string: string, to split.
|
|
327
|
+
:param delimiters: list, of delimiters.
|
|
328
|
+
:return: list of strings.
|
|
329
|
+
"""
|
|
330
|
+
|
|
331
|
+
# Base case: no more delimiters, return the string itself in a list.
|
|
332
|
+
if not delimiters:
|
|
333
|
+
return [full_string]
|
|
334
|
+
|
|
335
|
+
# Take the first delimiter.
|
|
336
|
+
delimiter = delimiters[0]
|
|
337
|
+
# Split the string on the current delimiter.
|
|
338
|
+
split_strings = full_string.split(delimiter)
|
|
339
|
+
# Get the remaining delimiters.
|
|
340
|
+
remaining_delimiters = delimiters[1:]
|
|
341
|
+
|
|
342
|
+
result = []
|
|
343
|
+
for substring in split_strings:
|
|
344
|
+
# Recursively call the whole function with each substring and the remaining delimiters.
|
|
345
|
+
result.extend(multiple_splits_by_delimiters(substring, remaining_delimiters))
|
|
346
|
+
return result
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def check_if_suffix_is_in_string(string: str, suffix: str) -> bool:
|
|
350
|
+
"""
|
|
351
|
+
Function checks if 'suffix' is in the end of 'string'.
|
|
352
|
+
:param string: string, to check.
|
|
353
|
+
:param suffix: string, to check.
|
|
354
|
+
:return: boolean.
|
|
355
|
+
"""
|
|
356
|
+
|
|
357
|
+
return string.endswith(suffix)
|
atomicshop/datetimes.py
CHANGED
|
@@ -183,6 +183,42 @@ def create_date_range_for_year_month(
|
|
|
183
183
|
return create_date_range(first_date, last_date)
|
|
184
184
|
|
|
185
185
|
|
|
186
|
+
def get_difference_between_dates_in_days(first_datetime, reference_datetime) -> int:
|
|
187
|
+
"""
|
|
188
|
+
Get the difference between two dates in days.
|
|
189
|
+
The first day is '0'.
|
|
190
|
+
|
|
191
|
+
:param first_datetime: datetime.datetime object.
|
|
192
|
+
:param reference_datetime: datetime.datetime object.
|
|
193
|
+
|
|
194
|
+
:return: integer, the difference between the two dates in days.
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
# Get the difference between the two dates.
|
|
198
|
+
difference = reference_datetime - first_datetime
|
|
199
|
+
|
|
200
|
+
# Get the difference in days.
|
|
201
|
+
return difference.days
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def get_difference_between_dates_in_hours(first_datetime, reference_datetime) -> int:
|
|
205
|
+
"""
|
|
206
|
+
Get the difference between two dates in hours.
|
|
207
|
+
The first hour is '0'.
|
|
208
|
+
|
|
209
|
+
:param first_datetime: datetime.datetime object.
|
|
210
|
+
:param reference_datetime: datetime.datetime object.
|
|
211
|
+
|
|
212
|
+
:return: integer, the difference between the two dates in hours.
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
# Get the difference between the two dates.
|
|
216
|
+
difference = first_datetime - reference_datetime
|
|
217
|
+
|
|
218
|
+
# Get the difference in hours.
|
|
219
|
+
return difference.days * 24 + difference.seconds // (60 * 60)
|
|
220
|
+
|
|
221
|
+
|
|
186
222
|
def get_seconds_random(minimum_seconds, maximum_seconds):
|
|
187
223
|
return random.randint(minimum_seconds, maximum_seconds)
|
|
188
224
|
|
atomicshop/domains.py
CHANGED
|
@@ -56,11 +56,16 @@ def get_registered_domain(domain: str) -> str:
|
|
|
56
56
|
:return: string of main registered domain with tld only.
|
|
57
57
|
"""
|
|
58
58
|
# Extract all the parts from domain with 'tldextract'.
|
|
59
|
-
|
|
59
|
+
# By default, TLD Extract will use the online database, to use the offline database empty Sequence[str] must be
|
|
60
|
+
# passed to 'suffix_list_urls' option.
|
|
61
|
+
# Empty sequence in TLDExtract itself is defined as '()'.
|
|
62
|
+
# extracted_domain_parts = tldextract.TLDExtract(suffix_list_urls=str())(domain)
|
|
63
|
+
extracted_domain_parts = tldextract.TLDExtract(suffix_list_urls=())(domain)
|
|
60
64
|
|
|
61
65
|
# If 'suffix' is empty, it means that the tld is not in 'tldextract' offline database or there is no tld at all,
|
|
62
66
|
# for example: some-domain-without-tld
|
|
63
67
|
if not extracted_domain_parts.suffix:
|
|
64
68
|
return domain
|
|
65
69
|
else:
|
|
66
|
-
return f'{extracted_domain_parts.registered_domain}.{extracted_domain_parts.suffix}'
|
|
70
|
+
# return f'{extracted_domain_parts.registered_domain}.{extracted_domain_parts.suffix}'
|
|
71
|
+
return extracted_domain_parts.registered_domain
|
atomicshop/file_io/jsons.py
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import tomllib
|
|
2
|
+
|
|
3
|
+
from .file_io import read_file_decorator
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@read_file_decorator
|
|
7
|
+
def read_toml_file(file_path: str,
|
|
8
|
+
file_mode: str = 'rb',
|
|
9
|
+
encoding=None,
|
|
10
|
+
file_object=None,
|
|
11
|
+
**kwargs) -> dict:
|
|
12
|
+
"""
|
|
13
|
+
Read the toml file and return its content as dictionary.
|
|
14
|
+
|
|
15
|
+
:param file_path: String with full file path to file.
|
|
16
|
+
:param file_mode: string, file reading mode. Examples: 'r', 'rb'. Default is 'r'.
|
|
17
|
+
:param encoding: string, encoding of the file. Default is 'None'.
|
|
18
|
+
:param file_object: file object of the 'open()' function in the decorator. Decorator executes the 'with open()'
|
|
19
|
+
statement and passes to this function. That's why the default is 'None', since we get it from the decorator.
|
|
20
|
+
:return: dict.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
# Read the file to variable
|
|
24
|
+
return tomllib.load(file_object)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import pandas
|
|
2
|
+
|
|
3
|
+
from .file_io import write_file_decorator
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@write_file_decorator
|
|
7
|
+
def write_xlsx(
|
|
8
|
+
spread_sheets: dict,
|
|
9
|
+
file_path: str,
|
|
10
|
+
file_mode: str = 'w',
|
|
11
|
+
index: bool = False,
|
|
12
|
+
file_object=None,
|
|
13
|
+
**kwargs
|
|
14
|
+
) -> None:
|
|
15
|
+
"""
|
|
16
|
+
Write data to xlsx file. Including several spreadsheets if specified.
|
|
17
|
+
|
|
18
|
+
:param file_path: string, full path to the file to write.
|
|
19
|
+
:param spread_sheets: dict. Each dict is a spreadsheet. The keys are the names of the sheets and the
|
|
20
|
+
values are the dicts to write to the sheets.
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
spread_sheets = {
|
|
24
|
+
'sheet1': {
|
|
25
|
+
'col1': [1, 2, 3],
|
|
26
|
+
'col2': [4, 5, 6]
|
|
27
|
+
},
|
|
28
|
+
'sheet2': {
|
|
29
|
+
'col1': [7, 8, 9],
|
|
30
|
+
'col2': [10, 11, 12]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
:param file_mode: string, file writing mode. Examples: 'x', 'w', 'wb'.
|
|
34
|
+
:param index: boolean, if True, the index of the data frame will be written to the file on the left-most column.
|
|
35
|
+
The index meaning that each row will have a number, starting from 0.
|
|
36
|
+
:param file_object: file object of the 'open()' function in the decorator. Decorator executes the 'with open()'
|
|
37
|
+
statement and passes to this function. That's why the default is 'None', since we get it from the decorator.
|
|
38
|
+
:return:
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
# Create dict of data frames.
|
|
42
|
+
spread_sheets = {sheet_name: pandas.DataFrame(sheet_data) for sheet_name, sheet_data in spread_sheets.items()}
|
|
43
|
+
|
|
44
|
+
# Save the file.
|
|
45
|
+
with pandas.ExcelWriter(file_path, engine='openpyxl') as writer:
|
|
46
|
+
for sheet_name, sheet_data in spread_sheets.items():
|
|
47
|
+
sheet_data.to_excel(writer, sheet_name=sheet_name, index=index)
|
|
48
|
+
|
|
49
|
+
# # Create the writer object.
|
|
50
|
+
# writer = pandas.ExcelWriter(file_path, engine='xlsxwriter')
|
|
51
|
+
#
|
|
52
|
+
# # Iterate through the spreadsheets.
|
|
53
|
+
# for sheet_name, sheet_data in spread_sheets.items():
|
|
54
|
+
# # Write the sheet.
|
|
55
|
+
# sheet_data.to_excel(writer, sheet_name=sheet_name)
|
|
56
|
+
#
|
|
57
|
+
# # Save the file.
|
|
58
|
+
# writer.save()
|
atomicshop/filesystem.py
CHANGED
|
@@ -3,10 +3,9 @@ import pathlib
|
|
|
3
3
|
from pathlib import Path, PurePath, PureWindowsPath
|
|
4
4
|
import glob
|
|
5
5
|
import shutil
|
|
6
|
-
from enum import Enum
|
|
7
6
|
|
|
8
7
|
from .print_api import print_api
|
|
9
|
-
from .basics import strings
|
|
8
|
+
from .basics import strings, list_of_dicts
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
def get_working_directory() -> str:
|
|
@@ -258,33 +257,35 @@ def get_file_names_from_directory(directory_path: str) -> list:
|
|
|
258
257
|
return file_list
|
|
259
258
|
|
|
260
259
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
def get_file_paths_and_relative_directories(directory_fullpath: str,
|
|
271
|
-
file_name_check_tuple: tuple = tuple(),
|
|
272
|
-
relative_file_name_as_directory: bool = False):
|
|
260
|
+
def get_file_paths_and_relative_directories(
|
|
261
|
+
directory_fullpath: str,
|
|
262
|
+
recursive: bool = True,
|
|
263
|
+
file_name_check_pattern: str = '*',
|
|
264
|
+
relative_file_name_as_directory: bool = False,
|
|
265
|
+
add_last_modified_time: bool = False,
|
|
266
|
+
sort_by_last_modified_time: bool = False
|
|
267
|
+
):
|
|
273
268
|
"""
|
|
269
|
+
Recursive, by option.
|
|
274
270
|
The function receives a filesystem directory as string, scans it recursively for files and returns list of
|
|
275
271
|
full paths to that file (including).
|
|
276
272
|
If 'file_name_check_tuple' specified, the function will return only list of files that answer to the input
|
|
277
273
|
of that tuple.
|
|
278
|
-
Recursive.
|
|
279
274
|
|
|
280
275
|
:param directory_fullpath: string to full path to directory on the filesystem to scan.
|
|
281
|
-
:param
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
276
|
+
:param recursive: boolean.
|
|
277
|
+
'True', then the function will scan recursively in subdirectories.
|
|
278
|
+
'False', then the function will scan only in the directory that was passed.
|
|
279
|
+
:param file_name_check_pattern: string, if specified, the function will return only files that match the pattern.
|
|
280
|
+
The string can contain part of file name to check or full file name with extension.
|
|
281
|
+
Can contain wildcards.
|
|
286
282
|
:param relative_file_name_as_directory: boolean that will set if 'relative_directory_list' should contain
|
|
287
283
|
file name with extension for each entry.
|
|
284
|
+
:param add_last_modified_time: boolean, if 'True', then the function will add last modified time of the file
|
|
285
|
+
to the output list.
|
|
286
|
+
:param sort_by_last_modified_time: boolean, if 'True', then the function will sort the output list by last
|
|
287
|
+
modified time of the file.
|
|
288
|
+
|
|
288
289
|
:return: list of all found filenames with full file paths, list with relative folders to file excluding the
|
|
289
290
|
main folder.
|
|
290
291
|
"""
|
|
@@ -294,59 +295,57 @@ def get_file_paths_and_relative_directories(directory_fullpath: str,
|
|
|
294
295
|
Function gets the full file path, adds it to the found 'object_list' and gets the relative path to that
|
|
295
296
|
file, against the main path to directory that was passed to the parent function.
|
|
296
297
|
"""
|
|
298
|
+
|
|
299
|
+
file_result: dict = dict()
|
|
300
|
+
|
|
297
301
|
# Get full file path of the file.
|
|
298
|
-
|
|
299
|
-
object_list.append(file_path)
|
|
302
|
+
file_result['path'] = os.path.join(dirpath, file)
|
|
300
303
|
|
|
301
304
|
# if 'relative_file_name_as_directory' was passed.
|
|
302
305
|
if relative_file_name_as_directory:
|
|
303
306
|
# Output the path with filename.
|
|
304
|
-
|
|
307
|
+
file_result['relative_dir'] = _get_relative_output_path_from_input_path(directory_fullpath, dirpath, file)
|
|
305
308
|
# if 'relative_file_name_as_directory' wasn't passed.
|
|
306
309
|
else:
|
|
307
310
|
# Output the path without filename.
|
|
308
|
-
|
|
311
|
+
file_result['relative_dir'] = _get_relative_output_path_from_input_path(directory_fullpath, dirpath)
|
|
309
312
|
|
|
310
313
|
# Remove separator from the beginning if exists.
|
|
311
|
-
|
|
314
|
+
file_result['relative_dir'] = file_result['relative_dir'].removeprefix(os.sep)
|
|
315
|
+
|
|
316
|
+
# If 'add_last_modified_time' was passed.
|
|
317
|
+
if add_last_modified_time:
|
|
318
|
+
# Get last modified time of the file.
|
|
319
|
+
file_result['last_modified'] = get_file_modified_time(file_result['path'])
|
|
312
320
|
|
|
313
|
-
|
|
321
|
+
object_list.append(file_result)
|
|
314
322
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
raise TypeError(f'Second entry of tuple "file_name_check_tuple" is not of "ComparisonOperator" type.')
|
|
323
|
+
if sort_by_last_modified_time and not add_last_modified_time:
|
|
324
|
+
raise ValueError('Parameter "sort_by_last_modified_time" cannot be "True" if parameter '
|
|
325
|
+
'"add_last_modified_time" is not "True".')
|
|
319
326
|
|
|
320
327
|
# === Function main ================
|
|
321
328
|
# Define locals.
|
|
322
329
|
object_list: list = list()
|
|
323
|
-
relative_paths_list: list = list()
|
|
324
330
|
|
|
325
331
|
# "Walk" over all the directories and subdirectories - make list of full file paths inside the directory
|
|
326
332
|
# recursively.
|
|
327
333
|
for dirpath, subdirs, files in os.walk(directory_fullpath):
|
|
328
334
|
# Iterate through all the file names that were found in the folder.
|
|
329
335
|
for file in files:
|
|
330
|
-
# If '
|
|
331
|
-
if
|
|
332
|
-
# Get separate variables from the tuple.
|
|
333
|
-
# 'check_string' is a string that will be checked against 'file' iteration, which also a string.
|
|
334
|
-
# 'comparison_operator' is 'ComparisonOperator' Enum object, that contains the string method
|
|
335
|
-
# operator that will be used against the 'check_string'.
|
|
336
|
-
check_string, comparison_operator = file_name_check_tuple
|
|
337
|
-
# 'getattr' adds the string comparison method to the 'file' string. Example:
|
|
338
|
-
# file.__eq__
|
|
339
|
-
# 'comparison_operator' is the Enum class representation and '.value' method is the string
|
|
340
|
-
# representation of '__eq__'.
|
|
341
|
-
# and after that comes the check string to check against:
|
|
342
|
-
# file.__eq__(check_string)
|
|
343
|
-
if getattr(file, comparison_operator.value)(check_string):
|
|
344
|
-
get_file()
|
|
345
|
-
# If 'file_name_check_tuple' wasn't passed, then get all the files.
|
|
346
|
-
else:
|
|
336
|
+
# If 'file_name_check_pattern' was passed.
|
|
337
|
+
if strings.match_pattern_against_string(file_name_check_pattern, file):
|
|
347
338
|
get_file()
|
|
348
339
|
|
|
349
|
-
|
|
340
|
+
if not recursive:
|
|
341
|
+
break
|
|
342
|
+
|
|
343
|
+
# If 'sort_by_last_modified_time' was passed.
|
|
344
|
+
if sort_by_last_modified_time:
|
|
345
|
+
# Sort the list by last modified time.
|
|
346
|
+
object_list = list_of_dicts.sort_by_keys(object_list, key_list=['last_modified'])
|
|
347
|
+
|
|
348
|
+
return object_list
|
|
350
349
|
|
|
351
350
|
|
|
352
351
|
def _get_relative_output_path_from_input_path(main_directory: str, file_directory: str, file_name: str = str()):
|
|
@@ -434,3 +433,32 @@ def get_files_and_folders(directory_path: str, string_contains: str = str()):
|
|
|
434
433
|
"""
|
|
435
434
|
files_folders_list: list = glob.glob(f'{directory_path}{os.sep}*{string_contains}')
|
|
436
435
|
return files_folders_list
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def get_file_modified_time(file_path: str) -> float:
|
|
439
|
+
"""
|
|
440
|
+
The function returns the time of last modification of the file in seconds since the epoch.
|
|
441
|
+
|
|
442
|
+
:param file_path: string, full path to file.
|
|
443
|
+
:return: float, time of last modification of the file in seconds since the epoch.
|
|
444
|
+
"""
|
|
445
|
+
return os.path.getmtime(file_path)
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def change_last_modified_date_of_file(file_path: str, new_date: float) -> None:
|
|
449
|
+
"""
|
|
450
|
+
The function changes the last modified date of the file.
|
|
451
|
+
|
|
452
|
+
Example:
|
|
453
|
+
import os
|
|
454
|
+
import time
|
|
455
|
+
file_path = "C:\\Users\\file.txt"
|
|
456
|
+
new_timestamp = time.mktime(time.strptime('2023-10-03 12:00:00', '%Y-%m-%d %H:%M:%S'))
|
|
457
|
+
os.utime(file_path, (new_timestamp, new_timestamp))
|
|
458
|
+
|
|
459
|
+
:param file_path: string, full path to file.
|
|
460
|
+
:param new_date: float, time of last modification of the file in seconds since the epoch.
|
|
461
|
+
:return: None.
|
|
462
|
+
"""
|
|
463
|
+
|
|
464
|
+
os.utime(file_path, (new_date, new_date))
|
|
@@ -104,7 +104,7 @@ def initialize_mitm_server(config_static):
|
|
|
104
104
|
system_logger.info("Importing engine modules.")
|
|
105
105
|
|
|
106
106
|
# Get full paths of all the 'engine_config.ini' files.
|
|
107
|
-
engine_config_path_list
|
|
107
|
+
engine_config_path_list = get_file_paths_and_relative_directories(
|
|
108
108
|
directory_fullpath=config_static.ENGINES_DIRECTORY_PATH,
|
|
109
109
|
file_name_check_tuple=(config_static.ENGINE_CONFIG_FILE_NAME, ComparisonOperator.EQ))
|
|
110
110
|
|
|
@@ -114,7 +114,7 @@ def initialize_mitm_server(config_static):
|
|
|
114
114
|
for engine_config_path in engine_config_path_list:
|
|
115
115
|
# Initialize engine.
|
|
116
116
|
current_module = ModuleCategory(config_static.WORKING_DIRECTORY)
|
|
117
|
-
current_module.fill_engine_fields_from_config(engine_config_path)
|
|
117
|
+
current_module.fill_engine_fields_from_config(engine_config_path['path'])
|
|
118
118
|
current_module.initialize_engine(logs_path=config['log']['logs_path'],
|
|
119
119
|
logger=system_logger)
|
|
120
120
|
|