atomicshop 2.15.11__py3-none-any.whl → 3.10.5__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.
Files changed (221) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
  3. atomicshop/a_mains/dns_gateway_setting.py +11 -0
  4. atomicshop/a_mains/get_local_tcp_ports.py +85 -0
  5. atomicshop/a_mains/github_wrapper.py +11 -0
  6. atomicshop/a_mains/install_ca_certificate.py +172 -0
  7. atomicshop/a_mains/process_from_port.py +119 -0
  8. atomicshop/a_mains/set_default_dns_gateway.py +90 -0
  9. atomicshop/a_mains/update_config_toml.py +38 -0
  10. atomicshop/basics/ansi_escape_codes.py +3 -1
  11. atomicshop/basics/argparse_template.py +2 -0
  12. atomicshop/basics/booleans.py +27 -30
  13. atomicshop/basics/bytes_arrays.py +43 -0
  14. atomicshop/basics/classes.py +149 -1
  15. atomicshop/basics/enums.py +2 -2
  16. atomicshop/basics/exceptions.py +5 -1
  17. atomicshop/basics/list_of_classes.py +29 -0
  18. atomicshop/basics/multiprocesses.py +374 -50
  19. atomicshop/basics/strings.py +72 -3
  20. atomicshop/basics/threads.py +14 -0
  21. atomicshop/basics/tracebacks.py +13 -3
  22. atomicshop/certificates.py +153 -52
  23. atomicshop/config_init.py +11 -6
  24. atomicshop/console_user_response.py +7 -14
  25. atomicshop/consoles.py +9 -0
  26. atomicshop/datetimes.py +1 -1
  27. atomicshop/diff_check.py +3 -3
  28. atomicshop/dns.py +128 -3
  29. atomicshop/etws/_pywintrace_fix.py +17 -0
  30. atomicshop/etws/trace.py +40 -42
  31. atomicshop/etws/traces/trace_dns.py +56 -44
  32. atomicshop/etws/traces/trace_tcp.py +130 -0
  33. atomicshop/file_io/csvs.py +27 -5
  34. atomicshop/file_io/docxs.py +34 -17
  35. atomicshop/file_io/file_io.py +31 -17
  36. atomicshop/file_io/jsons.py +49 -0
  37. atomicshop/file_io/tomls.py +139 -0
  38. atomicshop/filesystem.py +616 -291
  39. atomicshop/get_process_list.py +3 -3
  40. atomicshop/http_parse.py +149 -93
  41. atomicshop/ip_addresses.py +6 -1
  42. atomicshop/mitm/centered_settings.py +132 -0
  43. atomicshop/mitm/config_static.py +207 -0
  44. atomicshop/mitm/config_toml_editor.py +55 -0
  45. atomicshop/mitm/connection_thread_worker.py +875 -357
  46. atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
  47. atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
  48. atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
  49. atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
  50. atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
  51. atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
  52. atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
  53. atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
  54. atomicshop/mitm/engines/create_module_template.py +58 -14
  55. atomicshop/mitm/import_config.py +359 -139
  56. atomicshop/mitm/initialize_engines.py +160 -80
  57. atomicshop/mitm/message.py +64 -23
  58. atomicshop/mitm/mitm_main.py +892 -0
  59. atomicshop/mitm/recs_files.py +183 -0
  60. atomicshop/mitm/shared_functions.py +4 -10
  61. atomicshop/mitm/ssh_tester.py +82 -0
  62. atomicshop/mitm/statistic_analyzer.py +136 -40
  63. atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +265 -83
  64. atomicshop/monitor/checks/dns.py +1 -1
  65. atomicshop/networks.py +671 -0
  66. atomicshop/on_exit.py +39 -9
  67. atomicshop/package_mains_processor.py +84 -0
  68. atomicshop/permissions/permissions.py +22 -0
  69. atomicshop/permissions/ubuntu_permissions.py +239 -0
  70. atomicshop/permissions/win_permissions.py +33 -0
  71. atomicshop/print_api.py +24 -42
  72. atomicshop/process.py +24 -6
  73. atomicshop/process_poller/process_pool.py +0 -1
  74. atomicshop/process_poller/simple_process_pool.py +204 -5
  75. atomicshop/python_file_patcher.py +1 -1
  76. atomicshop/python_functions.py +27 -75
  77. atomicshop/speech_recognize.py +8 -0
  78. atomicshop/ssh_remote.py +158 -172
  79. atomicshop/system_resource_monitor.py +61 -47
  80. atomicshop/system_resources.py +8 -8
  81. atomicshop/tempfiles.py +1 -2
  82. atomicshop/urls.py +6 -0
  83. atomicshop/venvs.py +28 -0
  84. atomicshop/versioning.py +27 -0
  85. atomicshop/web.py +98 -27
  86. atomicshop/web_apis/google_custom_search.py +44 -0
  87. atomicshop/web_apis/google_llm.py +188 -0
  88. atomicshop/websocket_parse.py +450 -0
  89. atomicshop/wrappers/certauthw/certauth.py +1 -0
  90. atomicshop/wrappers/cryptographyw.py +29 -8
  91. atomicshop/wrappers/ctyping/etw_winapi/const.py +97 -47
  92. atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +178 -49
  93. atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
  94. atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
  95. atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -2
  96. atomicshop/wrappers/ctyping/setup_device.py +466 -0
  97. atomicshop/wrappers/ctyping/win_console.py +39 -0
  98. atomicshop/wrappers/dockerw/dockerw.py +113 -2
  99. atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
  100. atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
  101. atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
  102. atomicshop/wrappers/factw/get_file_data.py +12 -5
  103. atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
  104. atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
  105. atomicshop/wrappers/githubw.py +537 -54
  106. atomicshop/wrappers/loggingw/consts.py +1 -1
  107. atomicshop/wrappers/loggingw/filters.py +23 -0
  108. atomicshop/wrappers/loggingw/formatters.py +12 -0
  109. atomicshop/wrappers/loggingw/handlers.py +214 -107
  110. atomicshop/wrappers/loggingw/loggers.py +19 -0
  111. atomicshop/wrappers/loggingw/loggingw.py +860 -22
  112. atomicshop/wrappers/loggingw/reading.py +134 -112
  113. atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
  114. atomicshop/wrappers/mongodbw/mongodbw.py +1324 -36
  115. atomicshop/wrappers/netshw.py +271 -0
  116. atomicshop/wrappers/playwrightw/engine.py +34 -19
  117. atomicshop/wrappers/playwrightw/infra.py +5 -0
  118. atomicshop/wrappers/playwrightw/javascript.py +7 -3
  119. atomicshop/wrappers/playwrightw/keyboard.py +14 -0
  120. atomicshop/wrappers/playwrightw/scenarios.py +172 -5
  121. atomicshop/wrappers/playwrightw/waits.py +9 -7
  122. atomicshop/wrappers/powershell_networking.py +80 -0
  123. atomicshop/wrappers/psutilw/processes.py +37 -1
  124. atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
  125. atomicshop/wrappers/pyopensslw.py +9 -2
  126. atomicshop/wrappers/pywin32w/cert_store.py +116 -0
  127. atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
  128. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
  129. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
  130. atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
  131. atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
  132. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
  133. atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
  134. atomicshop/wrappers/socketw/accepter.py +21 -7
  135. atomicshop/wrappers/socketw/certificator.py +216 -150
  136. atomicshop/wrappers/socketw/creator.py +190 -50
  137. atomicshop/wrappers/socketw/dns_server.py +491 -182
  138. atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
  139. atomicshop/wrappers/socketw/process_getter.py +86 -0
  140. atomicshop/wrappers/socketw/receiver.py +144 -102
  141. atomicshop/wrappers/socketw/sender.py +65 -35
  142. atomicshop/wrappers/socketw/sni.py +334 -165
  143. atomicshop/wrappers/socketw/socket_base.py +134 -0
  144. atomicshop/wrappers/socketw/socket_client.py +137 -95
  145. atomicshop/wrappers/socketw/socket_server_tester.py +11 -7
  146. atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
  147. atomicshop/wrappers/socketw/ssl_base.py +15 -14
  148. atomicshop/wrappers/socketw/statistics_csv.py +148 -17
  149. atomicshop/wrappers/sysmonw.py +1 -1
  150. atomicshop/wrappers/ubuntu_terminal.py +65 -26
  151. atomicshop/wrappers/win_auditw.py +189 -0
  152. atomicshop/wrappers/winregw/__init__.py +0 -0
  153. atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
  154. atomicshop/wrappers/winregw/winreg_network.py +232 -0
  155. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -51
  156. atomicshop-3.10.5.dist-info/RECORD +306 -0
  157. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
  158. atomicshop/_basics_temp.py +0 -101
  159. atomicshop/a_installs/win/fibratus.py +0 -9
  160. atomicshop/a_installs/win/mongodb.py +0 -9
  161. atomicshop/a_installs/win/pycharm.py +0 -9
  162. atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
  163. atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
  164. atomicshop/addons/mains/__pycache__/install_fibratus_windows.cpython-312.pyc +0 -0
  165. atomicshop/addons/mains/__pycache__/msi_unpacker.cpython-312.pyc +0 -0
  166. atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
  167. atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
  168. atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
  169. atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
  170. atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
  171. atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
  172. atomicshop/addons/package_setup/Setup.cmd +0 -7
  173. atomicshop/archiver/_search_in_zip.py +0 -189
  174. atomicshop/archiver/archiver.py +0 -34
  175. atomicshop/archiver/search_in_archive.py +0 -250
  176. atomicshop/archiver/sevenz_app_w.py +0 -86
  177. atomicshop/archiver/sevenzs.py +0 -44
  178. atomicshop/archiver/zips.py +0 -293
  179. atomicshop/file_types.py +0 -24
  180. atomicshop/mitm/config_editor.py +0 -37
  181. atomicshop/mitm/engines/create_module_template_example.py +0 -13
  182. atomicshop/mitm/initialize_mitm_server.py +0 -268
  183. atomicshop/pbtkmultifile_argparse.py +0 -88
  184. atomicshop/permissions.py +0 -151
  185. atomicshop/script_as_string_processor.py +0 -38
  186. atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
  187. atomicshop/ssh_scripts/process_from_port.py +0 -27
  188. atomicshop/wrappers/_process_wrapper_curl.py +0 -27
  189. atomicshop/wrappers/_process_wrapper_tar.py +0 -21
  190. atomicshop/wrappers/dockerw/install_docker.py +0 -209
  191. atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
  192. atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
  193. atomicshop/wrappers/ffmpegw.py +0 -125
  194. atomicshop/wrappers/fibratusw/install.py +0 -81
  195. atomicshop/wrappers/mongodbw/infrastructure.py +0 -53
  196. atomicshop/wrappers/mongodbw/install_mongodb.py +0 -190
  197. atomicshop/wrappers/msiw.py +0 -149
  198. atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
  199. atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
  200. atomicshop/wrappers/psutilw/networks.py +0 -45
  201. atomicshop/wrappers/pycharmw.py +0 -81
  202. atomicshop/wrappers/socketw/base.py +0 -59
  203. atomicshop/wrappers/socketw/get_process.py +0 -107
  204. atomicshop/wrappers/wslw.py +0 -191
  205. atomicshop-2.15.11.dist-info/RECORD +0 -302
  206. /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
  207. /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
  208. /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
  209. /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
  210. /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
  211. /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
  212. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
  213. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
  214. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
  215. /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
  216. /atomicshop/{archiver → permissions}/__init__.py +0 -0
  217. /atomicshop/{wrappers/fibratusw → web_apis}/__init__.py +0 -0
  218. /atomicshop/wrappers/{nodejsw → pywin32w/wmis}/__init__.py +0 -0
  219. /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
  220. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
  221. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
@@ -2,28 +2,37 @@ import os
2
2
  from typing import Literal, Union
3
3
  from pathlib import Path
4
4
  import datetime
5
+ import re
5
6
 
6
7
  from ... import filesystem, datetimes
8
+ from ...basics import booleans, list_of_classes
7
9
  from ...file_io import csvs
8
10
 
9
11
 
10
- class LogReaderTimeCouldntBeFoundInFileNameError(Exception):
11
- pass
12
+ def is_string_ends_with_ymd(s: str) -> bool:
13
+ date_tail_re = re.compile(r'(\d{4}-\d{2}-\d{2})$')
14
+
15
+ m = date_tail_re.search(s)
16
+ if not m:
17
+ return False
18
+ try:
19
+ datetime.datetime.strptime(m.group(1), "%Y-%m-%d")
20
+ return True
21
+ except ValueError:
22
+ return False
12
23
 
13
24
 
14
25
  def get_logs_paths(
15
- log_files_directory_path: str = None,
16
- log_file_path: str = None,
17
- file_name_pattern: str = '*.*',
18
- date_pattern: str = None,
26
+ log_file_path: str,
27
+ date_format: str = None,
19
28
  latest_only: bool = False,
20
- previous_day_only: bool = False
21
- ):
29
+ previous_day_only: bool = False,
30
+ yesterday_only: bool = False,
31
+ specific_date: str = None
32
+ ) -> list[filesystem.AtomicPath]:
22
33
  """
23
34
  This function gets the logs file paths from the directory. Supports rotating files to get the logs by time.
24
35
 
25
- :param log_files_directory_path: Path to the log files. If specified, the function will get all the files from the
26
- directory by the 'file_name_pattern'.
27
36
  :param log_file_path: Path to the log file. If specified, the function will get the file and all the rotated logs
28
37
  associated with this file. The 'file_name_pattern' will become the file name using the file name and extension.
29
38
 
@@ -35,112 +44,126 @@ def get_logs_paths(
35
44
 
36
45
  # The 'log_files_directory_path' will also be taken from the 'log_file_path':
37
46
  log_files_directory_path = 'C:/logs'
38
- :param file_name_pattern: Pattern to match the log files names.
39
- Default file_name_pattern will match all the files.
40
- :param date_pattern: Pattern to match the date in the log file name.
47
+ :param date_format: date format string pattern to match the date in the log file name.
41
48
  If specified, the function will get the log file by the date pattern.
42
49
  If not specified, the function will get the file date by file last modified time.
50
+
51
+ Example:
52
+ date_format = '%Y-%m-%d'
43
53
  :param latest_only: Boolean, if True, only the latest log file path will be returned.
44
54
  :param previous_day_only: Boolean, if True, only the log file path from the previous day will be returned.
55
+ :param yesterday_only: Boolean, if True, only the log file path from yesterday will be returned.
56
+ There's a difference between 'previous_day_only' and 'yesterday_only'.
57
+ 'previous_day_only' will get the log file from the previous day in the list of files that were found.
58
+ Since that doesn't guarantee that the log file from the previous day is yesterday, we have 'yesterday_only'.
59
+ :param specific_date: Specific date to get the log file path.
60
+ If specified, the function will get the log file by the specific date.
61
+ Meaning that 'date_format' must be specified.
45
62
  """
46
63
 
47
- if not log_files_directory_path and not log_file_path:
48
- raise ValueError('Either "log_files_directory_path" or "log_file_path" must be specified.')
49
- elif log_files_directory_path and log_file_path:
50
- raise ValueError('Both "log_files_directory_path" and "log_file_path" cannot be specified at the same time.')
64
+ booleans.is_only_1_true_in_list(
65
+ booleans_list_of_tuples=[
66
+ (latest_only, 'latest_only'),
67
+ (previous_day_only, 'previous_day_only'),
68
+ (yesterday_only, 'yesterday_only'),
69
+ (specific_date, 'specific_date'),
70
+ ],
71
+ raise_if_all_false=False
72
+ )
51
73
 
52
- if latest_only and previous_day_only:
53
- raise ValueError('Both "latest_only" and "previous_day_only" cannot be True at the same time.')
74
+ if not date_format and specific_date:
75
+ raise ValueError('If "specific_date" is specified, "date_format" must be specified.')
54
76
 
55
- # If log file path is specified, get the file_name_pattern from the file name.
56
- if log_file_path:
57
- # Build the file_name_pattern.
58
- log_file_name: str = Path(log_file_path).stem
59
- log_file_extension: str = Path(log_file_path).suffix
60
- file_name_pattern = f'{log_file_name}*{log_file_extension}'
77
+ # Get the file_name_pattern from the file name. Build the file_name_pattern.
78
+ # For some reason if the file name will be '.zip', then the file stem will be '.zip' and the extension will be ''.
79
+ log_file_name: str = Path(log_file_path).stem
80
+ log_file_extension: str = Path(log_file_path).suffix
61
81
 
62
- # Get the directory path from the file path.
63
- log_files_directory_path = Path(log_file_path).parent
82
+ if is_string_ends_with_ymd(log_file_name):
83
+ raise ValueError(
84
+ f'The log file name cannot end with a date in the format YYYY-MM-DD: {log_file_name}')
64
85
 
65
- # Get all the log file paths by the file_name_pattern.
66
- logs_files: list = filesystem.get_file_paths_from_directory(
86
+ if not log_file_extension and '.' in log_file_name:
87
+ log_file_name, log_file_extension = log_file_name.rsplit('.')
88
+ log_file_extension = f'.{log_file_extension}'
89
+
90
+ file_name_pattern: str = f'{log_file_name}*{log_file_extension}'
91
+
92
+ # Get the directory path from the file path.
93
+ log_files_directory_path: str = str(Path(log_file_path).parent)
94
+
95
+ # Get all the log file paths by the file_name_pattern and the date_format string.
96
+ logs_files: list[filesystem.AtomicPath] = filesystem.get_paths_from_directory(
67
97
  log_files_directory_path,
98
+ get_file=True,
68
99
  file_name_check_pattern=file_name_pattern,
69
100
  add_last_modified_time=True,
70
- sort_by_last_modified_time=True)
101
+ sort_by_last_modified_time=True,
102
+ datetime_format=date_format
103
+ )
104
+
105
+ # The above will not include the latest log file if it is not rotated yet.
106
+ # noinspection PyTypeChecker
107
+ last_log_file_atomic_path: filesystem.AtomicPath = None
108
+ if os.path.isfile(log_file_path):
109
+ last_log_file_atomic_path = filesystem.AtomicPath(log_file_path)
110
+ last_log_file_atomic_path.update(update_last_modified=True)
111
+
112
+ if logs_files and last_log_file_atomic_path and date_format:
113
+ # The problem here is the file name that doesn't contain the date string in the name.
114
+ # If it is regular log rotation, then there will be one file that doesn't have the date string in the name.
115
+ # If the function used to get the previous day log, then there will be no file that doesn't have the date
116
+ # string.
117
+
118
+ # Get the latest timestamp from the files with dates.
119
+ latest_datetime_float: float = 0
120
+ for file_index, single_file in enumerate(logs_files):
121
+ if single_file.datetime_float > latest_datetime_float:
122
+ latest_datetime_float = single_file.datetime_float
123
+
124
+ # We will add one day to the latest date that we found and assign to the latest file in rotation
125
+ # which is without the datetime string.
126
+ latest_datetime_float += 86400
127
+ last_log_file_atomic_path.datetime_float = latest_datetime_float
128
+ last_log_file_atomic_path.datetime_datetime = datetime.datetime.fromtimestamp(latest_datetime_float)
129
+ last_log_file_atomic_path.datetime_string = (
130
+ last_log_file_atomic_path.datetime_datetime.strftime(date_format))
131
+ last_log_file_atomic_path.datetime_format = date_format
132
+
133
+ # Add the last log file to the list.
134
+ logs_files.append(last_log_file_atomic_path)
135
+
136
+ # Sort the files by the last modified time.
137
+ logs_files = list_of_classes.sort_by_attributes(logs_files, ['datetime_float'])
138
+ elif last_log_file_atomic_path and logs_files and not date_format:
139
+ logs_files.append(last_log_file_atomic_path)
140
+ elif last_log_file_atomic_path and not logs_files:
141
+ logs_files = [last_log_file_atomic_path]
71
142
 
72
- # Get the datetime object from the first file name by the date pattern.
73
- first_date_string = None
74
143
  if logs_files:
75
- first_file_name: str = Path(logs_files[0]['file_path']).name
76
- first_datetime_object, first_date_string, first_timestamp_float = (
77
- datetimes.get_datetime_from_complex_string_by_pattern(first_file_name, date_pattern))
78
-
79
- # The problem here is the file name that doesn't contain the date string in the name.
80
- # If it is regular log rotation, then there will be one file that doesn't have the date string in the name.
81
- # If the function used to get the previous day log, then there will be no file that doesn't have the date string.
82
- if len(logs_files) > 1 or (len(logs_files) == 1 and first_date_string):
83
- if date_pattern:
84
- latest_timestamp: float = 0
85
- for file_index, single_file in enumerate(logs_files):
86
- # Get file name from current loop file path.
87
- current_file_name: str = Path(single_file['file_path']).name
88
- logs_files[file_index]['file_name'] = current_file_name
89
-
90
- # Get the datetime object from the file name by the date pattern.
91
- datetime_object, date_string, timestamp_float = (
92
- datetimes.get_datetime_from_complex_string_by_pattern(current_file_name, date_pattern))
93
-
94
- # Update the last modified time to the dictionary.
95
- logs_files[file_index]['last_modified'] = timestamp_float
96
- logs_files[file_index]['datetime'] = datetime_object
97
- logs_files[file_index]['date_string'] = date_string
98
-
99
- if timestamp_float and timestamp_float > latest_timestamp:
100
- latest_timestamp = timestamp_float
101
-
102
- # Check timestamps, if more than 1 file is None, then the function that gets the date from the file name
103
- # didn't work properly, probably because of the string datetime format or the filenames.
104
- none_timestamps = [single_file['last_modified'] for single_file in logs_files].count(None)
105
- if none_timestamps > 1:
106
- raise LogReaderTimeCouldntBeFoundInFileNameError(
107
- 'The date pattern could not be found in the file name. Check the date pattern and the file names.')
108
-
109
- # Now, there should be a file that doesn't have the string date pattern in the file name.
110
- # We will add one day to the latest date that we found and assign to that file path.
111
- for file_index, single_file in enumerate(logs_files):
112
- if single_file['last_modified'] is None:
113
- latest_timestamp += 86400
114
- logs_files[file_index]['last_modified'] = latest_timestamp
115
- logs_files[file_index]['datetime'] = datetime.datetime.fromtimestamp(latest_timestamp)
116
- logs_files[file_index]['date_string'] = logs_files[file_index]['datetime'].strftime(date_pattern)
117
- break
118
-
119
- # Sort the files by the last modified time.
120
- logs_files = sorted(logs_files, key=lambda x: x['last_modified'], reverse=False)
121
-
122
144
  if latest_only:
123
145
  logs_files = [logs_files[-1]]
124
-
125
- if previous_day_only:
126
- # Check if there is a previous day log file.
146
+ elif previous_day_only:
127
147
  if len(logs_files) == 1:
128
148
  logs_files = []
129
149
  else:
130
150
  logs_files = [logs_files[-2]]
131
- # If there is only one file, meaning it is the current day log.
132
- # If the 'previous_day_only' is True, then there are no previous day logs to output.
133
- elif len(logs_files) == 1 and previous_day_only:
134
- logs_files = []
151
+ elif yesterday_only:
152
+ # Get yesterday's date.
153
+ yesterday_date_string = (datetime.datetime.now() - datetime.timedelta(days=1)).strftime(date_format)
154
+ # Check if there is a yesterday log file.
155
+ logs_files = [single_file
156
+ for single_file in logs_files if single_file.datetime_string == yesterday_date_string]
157
+ elif specific_date:
158
+ # Check if there is a specific date log file.
159
+ logs_files = [single_file for single_file in logs_files if single_file.datetime_string == specific_date]
135
160
 
136
161
  return logs_files
137
162
 
138
163
 
139
164
  def get_all_log_files_into_list(
140
- log_files_directory_path: str = None,
141
165
  log_file_path: str = None,
142
- file_name_pattern: str = '*.*',
143
- date_pattern: str = None,
166
+ date_format: str = None,
144
167
  log_type: Literal['csv'] = 'csv',
145
168
  header_type_of_files: Literal['first', 'all'] = 'first',
146
169
  remove_logs: bool = False,
@@ -151,13 +174,13 @@ def get_all_log_files_into_list(
151
174
  This function gets the logs contents from the log files. Supports rotating files to get the logs by time.
152
175
  All the contents will be merged into one list.
153
176
 
154
- :param log_files_directory_path: Path to the log files. Check the 'get_logs_paths' function for more details.
155
177
  :param log_file_path: Path to the log file. Check the 'get_logs_paths' function for more details.
156
- :param file_name_pattern: Pattern to match the log files names.
157
- Default file_name_pattern will match all the files.
158
- :param date_pattern: Pattern to match the date in the log file name.
178
+ :param date_format: date format string pattern to match the date in the log file name.
159
179
  If specified, the function will get the log file by the date pattern.
160
180
  If not specified, the function will get the file date by file last modified time.
181
+
182
+ Example:
183
+ date_format = '%Y-%m-%d'
161
184
  :param log_type: Type of log to get.
162
185
  :param header_type_of_files: Type of header to get from the files.
163
186
  'first' - Only the first file has a header for CSV. This header will be used for the rest of the files.
@@ -180,10 +203,8 @@ def get_all_log_files_into_list(
180
203
 
181
204
  # Get all the log file paths by the file_name_pattern.
182
205
  logs_files: list = get_logs_paths(
183
- log_files_directory_path=log_files_directory_path,
184
206
  log_file_path=log_file_path,
185
- file_name_pattern=file_name_pattern,
186
- date_pattern=date_pattern)
207
+ date_format=date_format)
187
208
 
188
209
  # Read all the logs.
189
210
  logs_content: list = list()
@@ -191,13 +212,13 @@ def get_all_log_files_into_list(
191
212
  for single_file in logs_files:
192
213
  if log_type == 'csv':
193
214
  if header_type_of_files == 'all':
194
- csv_content, _ = csvs.read_csv_to_list_of_dicts_by_header(single_file['file_path'], **print_kwargs)
215
+ csv_content, _ = csvs.read_csv_to_list_of_dicts_by_header(single_file.path, **print_kwargs)
195
216
  logs_content.extend(csv_content)
196
217
  elif header_type_of_files == 'first':
197
218
  # The function gets empty header to read it from the CSV file, the returns the header that it read.
198
219
  # Then each time the header is fed once again to the function.
199
220
  csv_content, header = csvs.read_csv_to_list_of_dicts_by_header(
200
- single_file['file_path'], header=header, **print_kwargs)
221
+ single_file.path, header=header, **print_kwargs)
201
222
  # Any way the first file will be read with header.
202
223
  logs_content.extend(csv_content)
203
224
 
@@ -208,7 +229,7 @@ def get_all_log_files_into_list(
208
229
  if remove_logs:
209
230
  # Remove the statistics files.
210
231
  for single_file in logs_files:
211
- filesystem.remove_file(single_file['file_path'])
232
+ filesystem.remove_file(single_file.path)
212
233
 
213
234
  if move_to_path:
214
235
  # Get formatted time stamp for file name.
@@ -221,9 +242,7 @@ def get_all_log_files_into_list(
221
242
  filesystem.create_directory(move_to_path_with_timestamp)
222
243
  # Move the statistics files.
223
244
  for single_file in logs_files:
224
- single_file_name = filesystem.get_file_name(single_file['file_path'])
225
- move_to_path_with_file = f'{move_to_path_with_timestamp}{os.sep}{single_file_name}'
226
- filesystem.move_file(single_file['file_path'], move_to_path_with_file)
245
+ filesystem.move_file(single_file.path, move_to_path_with_timestamp)
227
246
 
228
247
  return logs_content
229
248
 
@@ -263,22 +282,25 @@ class LogReader:
263
282
  def __init__(
264
283
  self,
265
284
  log_file_path: str,
266
- date_pattern: str = None,
285
+ date_format: str = None,
267
286
  log_type: Literal['csv'] = 'csv',
268
287
  get_previous_file: bool = False,
269
288
  header: list = None
270
289
  ):
271
290
  """
272
291
  :param log_file_path: Path to the log file.
273
- :param date_pattern: Pattern to match the date in the log file name.
292
+ :param date_format: date format string pattern to match the date in the log file name.
274
293
  If specified, the function will get the log file by the date pattern.
275
294
  If not specified, the function will get the file date by file last modified time.
295
+
296
+ Example:
297
+ date_format = '%Y-%m-%d'
276
298
  :param log_type: Type of log to get.
277
299
  :param get_previous_file: Boolean, if True, the function will get the previous log file.
278
300
  For example, your log is set to rotate every Midnight.
279
- Meaning, once the day will change, the function will get the log file from the previous day in the third entry
280
- of the return tuple. This happens only once each 24 hours. Not from the time the function was called, but from
281
- the time the day changed.
301
+ Meaning, once the day will change, the function will get the log file from the previous day in the
302
+ third entry of the return tuple. This happens only once each 24 hours. Not from the time
303
+ the function was called, but from the time the day changed.
282
304
  :param header: List of strings that will be the header of the CSV file. Default is 'None'.
283
305
  None: the header from the CSV file will be used. The first row of the CSV file will be the header.
284
306
  Meaning, that the first line will be skipped and the second line will be the first row of the content.
@@ -287,7 +309,7 @@ class LogReader:
287
309
  """
288
310
 
289
311
  self.log_file_path: str = log_file_path
290
- self.date_pattern: str = date_pattern
312
+ self.date_format: str = date_format
291
313
  self.log_type: Literal['csv'] = log_type
292
314
  self.get_previous_file: bool = get_previous_file
293
315
  self.header: list = header
@@ -329,7 +351,7 @@ class LogReader:
329
351
  # Get the latest statistics file path.
330
352
  latest_statistics_file_path_object = get_logs_paths(
331
353
  log_file_path=self.log_file_path,
332
- date_pattern=self.date_pattern,
354
+ date_format=self.date_format,
333
355
  latest_only=True
334
356
  )
335
357
 
@@ -337,16 +359,16 @@ class LogReader:
337
359
  # if not latest_statistics_file_path_object:
338
360
  # return [], [], self.header
339
361
 
340
- latest_statistics_file_path: str = latest_statistics_file_path_object[0]['file_path']
362
+ latest_statistics_file_path: str = latest_statistics_file_path_object[0].path
341
363
 
342
364
  # Get the previous day statistics file path.
343
365
  previous_day_statistics_file_path: Union[str, None] = None
344
366
  try:
345
367
  previous_day_statistics_file_path = get_logs_paths(
346
368
  log_file_path=self.log_file_path,
347
- date_pattern=self.date_pattern,
369
+ date_format=self.date_format,
348
370
  previous_day_only=True
349
- )[0]['file_path']
371
+ )[0].path
350
372
  # If you get IndexError, it means that there are no previous day logs to read.
351
373
  except IndexError:
352
374
  pass
@@ -0,0 +1,31 @@
1
+ from pymongo import MongoClient
2
+ import pymongo.errors
3
+
4
+
5
+ MONGODB_DEFAULT_URI: str = 'mongodb://localhost:27017/'
6
+
7
+
8
+ class MongoDBNoConnectionError(Exception):
9
+ pass
10
+
11
+
12
+ def test_connection(
13
+ uri: str = MONGODB_DEFAULT_URI,
14
+ raise_exception: bool = False
15
+ ) -> bool:
16
+ """
17
+ Test the connection to the MongoDB server.
18
+
19
+ :param uri: str, URI to the MongoDB server.
20
+ :param raise_exception: bool, True to raise an exception if the connection fails, False otherwise (just return
21
+ False).
22
+ :return: bool, True if the connection is successful, False otherwise.
23
+ """
24
+ try:
25
+ client = MongoClient(uri)
26
+ client.admin.command('ping')
27
+ return True
28
+ except pymongo.errors.ServerSelectionTimeoutError:
29
+ if raise_exception:
30
+ raise MongoDBNoConnectionError(f"Could not connect to the MongoDB server on: ")
31
+ return False