atomicshop 2.11.47__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 (268) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
  3. atomicshop/a_mains/addons/process_list/compile.cmd +7 -0
  4. atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
  5. atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
  6. atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
  7. atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +8 -1
  8. atomicshop/a_mains/dns_gateway_setting.py +11 -0
  9. atomicshop/a_mains/get_local_tcp_ports.py +85 -0
  10. atomicshop/a_mains/github_wrapper.py +11 -0
  11. atomicshop/a_mains/install_ca_certificate.py +172 -0
  12. atomicshop/{addons/mains → a_mains}/msi_unpacker.py +3 -1
  13. atomicshop/a_mains/process_from_port.py +119 -0
  14. atomicshop/a_mains/set_default_dns_gateway.py +90 -0
  15. atomicshop/a_mains/update_config_toml.py +38 -0
  16. atomicshop/appointment_management.py +5 -3
  17. atomicshop/basics/ansi_escape_codes.py +3 -1
  18. atomicshop/basics/argparse_template.py +2 -0
  19. atomicshop/basics/booleans.py +27 -30
  20. atomicshop/basics/bytes_arrays.py +43 -0
  21. atomicshop/basics/classes.py +149 -1
  22. atomicshop/basics/dicts.py +12 -0
  23. atomicshop/basics/enums.py +2 -2
  24. atomicshop/basics/exceptions.py +5 -1
  25. atomicshop/basics/list_of_classes.py +29 -0
  26. atomicshop/basics/list_of_dicts.py +69 -5
  27. atomicshop/basics/lists.py +14 -0
  28. atomicshop/basics/multiprocesses.py +374 -50
  29. atomicshop/basics/package_module.py +10 -0
  30. atomicshop/basics/strings.py +160 -7
  31. atomicshop/basics/threads.py +14 -0
  32. atomicshop/basics/tracebacks.py +13 -4
  33. atomicshop/certificates.py +153 -52
  34. atomicshop/config_init.py +12 -7
  35. atomicshop/console_user_response.py +7 -14
  36. atomicshop/consoles.py +9 -0
  37. atomicshop/datetimes.py +98 -0
  38. atomicshop/diff_check.py +340 -40
  39. atomicshop/dns.py +128 -12
  40. atomicshop/etws/_pywintrace_fix.py +17 -0
  41. atomicshop/etws/const.py +38 -0
  42. atomicshop/etws/providers.py +21 -0
  43. atomicshop/etws/sessions.py +43 -0
  44. atomicshop/etws/trace.py +168 -0
  45. atomicshop/etws/traces/trace_dns.py +162 -0
  46. atomicshop/etws/traces/trace_sysmon_process_creation.py +126 -0
  47. atomicshop/etws/traces/trace_tcp.py +130 -0
  48. atomicshop/file_io/csvs.py +222 -24
  49. atomicshop/file_io/docxs.py +35 -18
  50. atomicshop/file_io/file_io.py +35 -19
  51. atomicshop/file_io/jsons.py +49 -0
  52. atomicshop/file_io/tomls.py +139 -0
  53. atomicshop/filesystem.py +864 -293
  54. atomicshop/get_process_list.py +133 -0
  55. atomicshop/{process_name_cmd.py → get_process_name_cmd_dll.py} +52 -19
  56. atomicshop/http_parse.py +149 -93
  57. atomicshop/ip_addresses.py +6 -1
  58. atomicshop/mitm/centered_settings.py +132 -0
  59. atomicshop/mitm/config_static.py +207 -0
  60. atomicshop/mitm/config_toml_editor.py +55 -0
  61. atomicshop/mitm/connection_thread_worker.py +875 -357
  62. atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
  63. atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
  64. atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
  65. atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
  66. atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
  67. atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
  68. atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
  69. atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
  70. atomicshop/mitm/engines/create_module_template.py +58 -14
  71. atomicshop/mitm/import_config.py +359 -139
  72. atomicshop/mitm/initialize_engines.py +160 -74
  73. atomicshop/mitm/message.py +64 -23
  74. atomicshop/mitm/mitm_main.py +892 -0
  75. atomicshop/mitm/recs_files.py +183 -0
  76. atomicshop/mitm/shared_functions.py +4 -10
  77. atomicshop/mitm/ssh_tester.py +82 -0
  78. atomicshop/mitm/statistic_analyzer.py +257 -166
  79. atomicshop/mitm/statistic_analyzer_helper/analyzer_helper.py +136 -0
  80. atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +525 -0
  81. atomicshop/monitor/change_monitor.py +96 -120
  82. atomicshop/monitor/checks/dns.py +139 -70
  83. atomicshop/monitor/checks/file.py +77 -0
  84. atomicshop/monitor/checks/network.py +81 -77
  85. atomicshop/monitor/checks/process_running.py +33 -34
  86. atomicshop/monitor/checks/url.py +94 -0
  87. atomicshop/networks.py +671 -0
  88. atomicshop/on_exit.py +205 -0
  89. atomicshop/package_mains_processor.py +84 -0
  90. atomicshop/permissions/permissions.py +22 -0
  91. atomicshop/permissions/ubuntu_permissions.py +239 -0
  92. atomicshop/permissions/win_permissions.py +33 -0
  93. atomicshop/print_api.py +24 -41
  94. atomicshop/process.py +63 -17
  95. atomicshop/process_poller/__init__.py +0 -0
  96. atomicshop/process_poller/pollers/__init__.py +0 -0
  97. atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py +95 -0
  98. atomicshop/process_poller/process_pool.py +207 -0
  99. atomicshop/process_poller/simple_process_pool.py +311 -0
  100. atomicshop/process_poller/tracer_base.py +45 -0
  101. atomicshop/process_poller/tracers/__init__.py +0 -0
  102. atomicshop/process_poller/tracers/event_log.py +46 -0
  103. atomicshop/process_poller/tracers/sysmon_etw.py +68 -0
  104. atomicshop/python_file_patcher.py +1 -1
  105. atomicshop/python_functions.py +27 -75
  106. atomicshop/question_answer_engine.py +2 -2
  107. atomicshop/scheduling.py +24 -5
  108. atomicshop/sound.py +4 -2
  109. atomicshop/speech_recognize.py +8 -0
  110. atomicshop/ssh_remote.py +158 -172
  111. atomicshop/startup/__init__.py +0 -0
  112. atomicshop/startup/win/__init__.py +0 -0
  113. atomicshop/startup/win/startup_folder.py +53 -0
  114. atomicshop/startup/win/task_scheduler.py +119 -0
  115. atomicshop/system_resource_monitor.py +61 -46
  116. atomicshop/system_resources.py +8 -8
  117. atomicshop/tempfiles.py +1 -2
  118. atomicshop/timer.py +30 -11
  119. atomicshop/urls.py +41 -0
  120. atomicshop/venvs.py +28 -0
  121. atomicshop/versioning.py +27 -0
  122. atomicshop/web.py +110 -25
  123. atomicshop/web_apis/__init__.py +0 -0
  124. atomicshop/web_apis/google_custom_search.py +44 -0
  125. atomicshop/web_apis/google_llm.py +188 -0
  126. atomicshop/websocket_parse.py +450 -0
  127. atomicshop/wrappers/certauthw/certauth.py +1 -0
  128. atomicshop/wrappers/cryptographyw.py +29 -8
  129. atomicshop/wrappers/ctyping/etw_winapi/__init__.py +0 -0
  130. atomicshop/wrappers/ctyping/etw_winapi/const.py +335 -0
  131. atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +393 -0
  132. atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
  133. atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
  134. atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +13 -9
  135. atomicshop/wrappers/ctyping/msi_windows_installer/tables.py +35 -0
  136. atomicshop/wrappers/ctyping/setup_device.py +466 -0
  137. atomicshop/wrappers/ctyping/win_console.py +39 -0
  138. atomicshop/wrappers/dockerw/dockerw.py +113 -2
  139. atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
  140. atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
  141. atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
  142. atomicshop/wrappers/factw/get_file_data.py +12 -5
  143. atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
  144. atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
  145. atomicshop/wrappers/factw/postgresql/firmware.py +4 -6
  146. atomicshop/wrappers/githubw.py +583 -51
  147. atomicshop/wrappers/loggingw/consts.py +49 -0
  148. atomicshop/wrappers/loggingw/filters.py +102 -0
  149. atomicshop/wrappers/loggingw/formatters.py +58 -71
  150. atomicshop/wrappers/loggingw/handlers.py +459 -40
  151. atomicshop/wrappers/loggingw/loggers.py +19 -0
  152. atomicshop/wrappers/loggingw/loggingw.py +1010 -178
  153. atomicshop/wrappers/loggingw/reading.py +344 -19
  154. atomicshop/wrappers/mongodbw/__init__.py +0 -0
  155. atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
  156. atomicshop/wrappers/mongodbw/mongodbw.py +1432 -0
  157. atomicshop/wrappers/netshw.py +271 -0
  158. atomicshop/wrappers/playwrightw/engine.py +34 -19
  159. atomicshop/wrappers/playwrightw/infra.py +5 -0
  160. atomicshop/wrappers/playwrightw/javascript.py +7 -3
  161. atomicshop/wrappers/playwrightw/keyboard.py +14 -0
  162. atomicshop/wrappers/playwrightw/scenarios.py +172 -5
  163. atomicshop/wrappers/playwrightw/waits.py +9 -7
  164. atomicshop/wrappers/powershell_networking.py +80 -0
  165. atomicshop/wrappers/psutilw/processes.py +81 -0
  166. atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
  167. atomicshop/wrappers/psutilw/psutilw.py +9 -0
  168. atomicshop/wrappers/pyopensslw.py +9 -2
  169. atomicshop/wrappers/pywin32w/__init__.py +0 -0
  170. atomicshop/wrappers/pywin32w/cert_store.py +116 -0
  171. atomicshop/wrappers/pywin32w/console.py +34 -0
  172. atomicshop/wrappers/pywin32w/win_event_log/__init__.py +0 -0
  173. atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
  174. atomicshop/wrappers/pywin32w/win_event_log/subscribe.py +212 -0
  175. atomicshop/wrappers/pywin32w/win_event_log/subscribes/__init__.py +0 -0
  176. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +57 -0
  177. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +49 -0
  178. atomicshop/wrappers/pywin32w/win_event_log/subscribes/schannel_logging.py +97 -0
  179. atomicshop/wrappers/pywin32w/winshell.py +19 -0
  180. atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
  181. atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
  182. atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
  183. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
  184. atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
  185. atomicshop/wrappers/socketw/accepter.py +21 -7
  186. atomicshop/wrappers/socketw/certificator.py +216 -150
  187. atomicshop/wrappers/socketw/creator.py +190 -50
  188. atomicshop/wrappers/socketw/dns_server.py +500 -173
  189. atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
  190. atomicshop/wrappers/socketw/process_getter.py +86 -0
  191. atomicshop/wrappers/socketw/receiver.py +144 -102
  192. atomicshop/wrappers/socketw/sender.py +65 -35
  193. atomicshop/wrappers/socketw/sni.py +334 -165
  194. atomicshop/wrappers/socketw/socket_base.py +134 -0
  195. atomicshop/wrappers/socketw/socket_client.py +137 -95
  196. atomicshop/wrappers/socketw/socket_server_tester.py +14 -9
  197. atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
  198. atomicshop/wrappers/socketw/ssl_base.py +15 -14
  199. atomicshop/wrappers/socketw/statistics_csv.py +148 -17
  200. atomicshop/wrappers/sysmonw.py +157 -0
  201. atomicshop/wrappers/ubuntu_terminal.py +65 -26
  202. atomicshop/wrappers/win_auditw.py +189 -0
  203. atomicshop/wrappers/winregw/__init__.py +0 -0
  204. atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
  205. atomicshop/wrappers/winregw/winreg_network.py +232 -0
  206. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -49
  207. atomicshop-3.10.5.dist-info/RECORD +306 -0
  208. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
  209. atomicshop/_basics_temp.py +0 -101
  210. atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
  211. atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
  212. atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
  213. atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
  214. atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
  215. atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
  216. atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
  217. atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
  218. atomicshop/addons/package_setup/Setup.cmd +0 -7
  219. atomicshop/addons/process_list/compile.cmd +0 -2
  220. atomicshop/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
  221. atomicshop/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
  222. atomicshop/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
  223. atomicshop/archiver/_search_in_zip.py +0 -189
  224. atomicshop/archiver/archiver.py +0 -34
  225. atomicshop/archiver/search_in_archive.py +0 -250
  226. atomicshop/archiver/sevenz_app_w.py +0 -86
  227. atomicshop/archiver/sevenzs.py +0 -44
  228. atomicshop/archiver/zips.py +0 -293
  229. atomicshop/etw/dns_trace.py +0 -118
  230. atomicshop/etw/etw.py +0 -61
  231. atomicshop/file_types.py +0 -24
  232. atomicshop/mitm/engines/create_module_template_example.py +0 -13
  233. atomicshop/mitm/initialize_mitm_server.py +0 -240
  234. atomicshop/monitor/checks/hash.py +0 -44
  235. atomicshop/monitor/checks/hash_checks/file.py +0 -55
  236. atomicshop/monitor/checks/hash_checks/url.py +0 -62
  237. atomicshop/pbtkmultifile_argparse.py +0 -88
  238. atomicshop/permissions.py +0 -110
  239. atomicshop/process_poller.py +0 -237
  240. atomicshop/script_as_string_processor.py +0 -38
  241. atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
  242. atomicshop/ssh_scripts/process_from_port.py +0 -27
  243. atomicshop/wrappers/_process_wrapper_curl.py +0 -27
  244. atomicshop/wrappers/_process_wrapper_tar.py +0 -21
  245. atomicshop/wrappers/dockerw/install_docker.py +0 -209
  246. atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
  247. atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
  248. atomicshop/wrappers/ffmpegw.py +0 -125
  249. atomicshop/wrappers/loggingw/checks.py +0 -20
  250. atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
  251. atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
  252. atomicshop/wrappers/socketw/base.py +0 -59
  253. atomicshop/wrappers/socketw/get_process.py +0 -107
  254. atomicshop/wrappers/wslw.py +0 -191
  255. atomicshop-2.11.47.dist-info/RECORD +0 -251
  256. /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
  257. /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
  258. /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
  259. /atomicshop/{addons/mains → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
  260. /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
  261. /atomicshop/{addons/mains → a_mains}/search_for_hyperlinks_in_docx.py +0 -0
  262. /atomicshop/{archiver → etws}/__init__.py +0 -0
  263. /atomicshop/{etw → etws/traces}/__init__.py +0 -0
  264. /atomicshop/{monitor/checks/hash_checks → mitm/statistic_analyzer_helper}/__init__.py +0 -0
  265. /atomicshop/{wrappers/nodejsw → permissions}/__init__.py +0 -0
  266. /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
  267. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
  268. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
@@ -1,33 +1,195 @@
1
1
  import os
2
- from typing import Literal
2
+ from typing import Literal, Union
3
+ from pathlib import Path
4
+ import datetime
5
+ import re
3
6
 
4
7
  from ... import filesystem, datetimes
8
+ from ...basics import booleans, list_of_classes
5
9
  from ...file_io import csvs
6
10
 
7
11
 
8
- def get_logs(
9
- path: str,
10
- pattern: str = '*.*',
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
23
+
24
+
25
+ def get_logs_paths(
26
+ log_file_path: str,
27
+ date_format: str = None,
28
+ latest_only: bool = False,
29
+ previous_day_only: bool = False,
30
+ yesterday_only: bool = False,
31
+ specific_date: str = None
32
+ ) -> list[filesystem.AtomicPath]:
33
+ """
34
+ This function gets the logs file paths from the directory. Supports rotating files to get the logs by time.
35
+
36
+ :param log_file_path: Path to the log file. If specified, the function will get the file and all the rotated logs
37
+ associated with this file. The 'file_name_pattern' will become the file name using the file name and extension.
38
+
39
+ Example:
40
+ log_file_path = 'C:/logs/test_log.csv'
41
+
42
+ # The function will get all the files that start with 'test_log' and have '.csv' extension:
43
+ file_name_pattern = 'test_log*.csv'
44
+
45
+ # The 'log_files_directory_path' will also be taken from the 'log_file_path':
46
+ log_files_directory_path = 'C:/logs'
47
+ :param date_format: date format string pattern to match the date in the log file name.
48
+ If specified, the function will get the log file by the date pattern.
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'
53
+ :param latest_only: Boolean, if True, only the latest log file path will be returned.
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.
62
+ """
63
+
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
+ )
73
+
74
+ if not date_format and specific_date:
75
+ raise ValueError('If "specific_date" is specified, "date_format" must be specified.')
76
+
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
81
+
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}')
85
+
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(
97
+ log_files_directory_path,
98
+ get_file=True,
99
+ file_name_check_pattern=file_name_pattern,
100
+ add_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]
142
+
143
+ if logs_files:
144
+ if latest_only:
145
+ logs_files = [logs_files[-1]]
146
+ elif previous_day_only:
147
+ if len(logs_files) == 1:
148
+ logs_files = []
149
+ else:
150
+ logs_files = [logs_files[-2]]
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]
160
+
161
+ return logs_files
162
+
163
+
164
+ def get_all_log_files_into_list(
165
+ log_file_path: str = None,
166
+ date_format: str = None,
11
167
  log_type: Literal['csv'] = 'csv',
12
168
  header_type_of_files: Literal['first', 'all'] = 'first',
13
169
  remove_logs: bool = False,
14
170
  move_to_path: str = None,
15
171
  print_kwargs: dict = None
16
- ):
172
+ ) -> list:
17
173
  """
18
- This function gets the logs from the log files. Supports rotating files to get the logs by time.
174
+ This function gets the logs contents from the log files. Supports rotating files to get the logs by time.
175
+ All the contents will be merged into one list.
19
176
 
20
- :param path: Path to the log files.
21
- :param pattern: Pattern to match the log files names.
22
- Default pattern will match all the files.
177
+ :param log_file_path: Path to the log file. Check the 'get_logs_paths' function for more details.
178
+ :param date_format: date format string pattern to match the date in the log file name.
179
+ If specified, the function will get the log file by the date pattern.
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'
23
184
  :param log_type: Type of log to get.
24
185
  :param header_type_of_files: Type of header to get from the files.
25
186
  'first' - Only the first file has a header for CSV. This header will be used for the rest of the files.
26
187
  'all' - Each CSV file has a header. Get the header from each file.
27
188
  :param remove_logs: Boolean, if True, the logs will be removed after getting them.
28
189
  :param move_to_path: Path to move the logs to.
29
-
30
190
  :param print_kwargs: Keyword arguments dict for 'print_api' function.
191
+
192
+ :return: List of dictionaries with the logs content.
31
193
  """
32
194
 
33
195
  if not print_kwargs:
@@ -36,9 +198,13 @@ def get_logs(
36
198
  if remove_logs and move_to_path:
37
199
  raise ValueError('Both "remove_logs" and "move_to_path" cannot be True/specified at the same time.')
38
200
 
39
- logs_files: list = filesystem.get_file_paths_from_directory(
40
- path, file_name_check_pattern=pattern,
41
- add_last_modified_time=True, sort_by_last_modified_time=True)
201
+ if header_type_of_files not in ['first', 'all']:
202
+ raise ValueError('Only "first" and "all" header types are supported.')
203
+
204
+ # Get all the log file paths by the file_name_pattern.
205
+ logs_files: list = get_logs_paths(
206
+ log_file_path=log_file_path,
207
+ date_format=date_format)
42
208
 
43
209
  # Read all the logs.
44
210
  logs_content: list = list()
@@ -46,12 +212,13 @@ def get_logs(
46
212
  for single_file in logs_files:
47
213
  if log_type == 'csv':
48
214
  if header_type_of_files == 'all':
49
- csv_content, _ = csvs.read_csv_to_list(single_file['file_path'], **print_kwargs)
215
+ csv_content, _ = csvs.read_csv_to_list_of_dicts_by_header(single_file.path, **print_kwargs)
50
216
  logs_content.extend(csv_content)
51
217
  elif header_type_of_files == 'first':
52
218
  # The function gets empty header to read it from the CSV file, the returns the header that it read.
53
219
  # Then each time the header is fed once again to the function.
54
- csv_content, header = csvs.read_csv_to_list(single_file['file_path'], header=header, **print_kwargs)
220
+ csv_content, header = csvs.read_csv_to_list_of_dicts_by_header(
221
+ single_file.path, header=header, **print_kwargs)
55
222
  # Any way the first file will be read with header.
56
223
  logs_content.extend(csv_content)
57
224
 
@@ -62,7 +229,7 @@ def get_logs(
62
229
  if remove_logs:
63
230
  # Remove the statistics files.
64
231
  for single_file in logs_files:
65
- filesystem.remove_file(single_file['file_path'])
232
+ filesystem.remove_file(single_file.path)
66
233
 
67
234
  if move_to_path:
68
235
  # Get formatted time stamp for file name.
@@ -75,8 +242,166 @@ def get_logs(
75
242
  filesystem.create_directory(move_to_path_with_timestamp)
76
243
  # Move the statistics files.
77
244
  for single_file in logs_files:
78
- single_file_name = filesystem.get_file_name(single_file['file_path'])
79
- move_to_path_with_file = f'{move_to_path_with_timestamp}{os.sep}{single_file_name}'
80
- filesystem.move_file(single_file['file_path'], move_to_path_with_file)
245
+ filesystem.move_file(single_file.path, move_to_path_with_timestamp)
81
246
 
82
247
  return logs_content
248
+
249
+
250
+ class LogReader:
251
+ """
252
+ This class gets the latest lines from the log file.
253
+
254
+ return: List of new lines.
255
+
256
+ Usage:
257
+ from typing import Union
258
+
259
+
260
+ # The header of the log file will be read from the first iteration of the log file.
261
+ # When the file is rotated, this header will be used to not read the header again.
262
+ header: Union[list, None] = None
263
+ log_reader = reading.LogReader(
264
+ log_file_path='/path/to/log.csv',
265
+ log_type='csv',
266
+ date_pattern='%Y_%m_%d',
267
+ get_previous_file=True,
268
+ header=header
269
+ )
270
+ while True:
271
+ latest_lines, previous_day_24h_lines, header = log_reader.get_latest_lines(header=header)
272
+
273
+ if latest_lines:
274
+ # Do something with the new lines.
275
+
276
+ if previous_day_24h_lines:
277
+ # Do something with the last 24 hours lines. Reminder, this will happen once a day on log rotation.
278
+
279
+ time.sleep(1)
280
+ """
281
+
282
+ def __init__(
283
+ self,
284
+ log_file_path: str,
285
+ date_format: str = None,
286
+ log_type: Literal['csv'] = 'csv',
287
+ get_previous_file: bool = False,
288
+ header: list = None
289
+ ):
290
+ """
291
+ :param log_file_path: Path to the log file.
292
+ :param date_format: date format string pattern to match the date in the log file name.
293
+ If specified, the function will get the log file by the date pattern.
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'
298
+ :param log_type: Type of log to get.
299
+ :param get_previous_file: Boolean, if True, the function will get the previous log file.
300
+ For example, your log is set to rotate every Midnight.
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.
304
+ :param header: List of strings that will be the header of the CSV file. Default is 'None'.
305
+ None: the header from the CSV file will be used. The first row of the CSV file will be the header.
306
+ Meaning, that the first line will be skipped and the second line will be the first row of the content.
307
+ List: the list will be used as header.
308
+ All the lines of the CSV file will be considered as content.
309
+ """
310
+
311
+ self.log_file_path: str = log_file_path
312
+ self.date_format: str = date_format
313
+ self.log_type: Literal['csv'] = log_type
314
+ self.get_previous_file: bool = get_previous_file
315
+ self.header: list = header
316
+
317
+ self._reading_existing_lines: list = []
318
+ self._existing_logs_file_count: int = 0
319
+
320
+ def _extract_new_lines_only(self, content_lines: list):
321
+ new_lines: list = []
322
+ for row in content_lines:
323
+ # If the row is not in the existing lines, then add it to the new lines.
324
+ if row not in self._reading_existing_lines:
325
+ new_lines.append(row)
326
+
327
+ if new_lines:
328
+ self._reading_existing_lines.extend(new_lines)
329
+
330
+ return new_lines
331
+
332
+ def get_latest_lines(self, header: list = None) -> tuple:
333
+ if header:
334
+ self.header = header
335
+
336
+ # If the existing logs file count is 0, it means that this is the first check. We need to get the current count.
337
+ if self._existing_logs_file_count == 0:
338
+ self._existing_logs_file_count = len(get_logs_paths(
339
+ log_file_path=self.log_file_path
340
+ ))
341
+
342
+ # If the count is still 0, then there are no logs to read.
343
+ if self._existing_logs_file_count == 0:
344
+ return [], [], self.header
345
+
346
+ if self.log_type != 'csv':
347
+ raise ValueError('Only "csv" log type is supported.')
348
+
349
+ previous_file_lines: list = []
350
+
351
+ # Get the latest statistics file path.
352
+ latest_statistics_file_path_object = get_logs_paths(
353
+ log_file_path=self.log_file_path,
354
+ date_format=self.date_format,
355
+ latest_only=True
356
+ )
357
+
358
+ # # If there are no logs to read, return empty lists.
359
+ # if not latest_statistics_file_path_object:
360
+ # return [], [], self.header
361
+
362
+ latest_statistics_file_path: str = latest_statistics_file_path_object[0].path
363
+
364
+ # Get the previous day statistics file path.
365
+ previous_day_statistics_file_path: Union[str, None] = None
366
+ try:
367
+ previous_day_statistics_file_path = get_logs_paths(
368
+ log_file_path=self.log_file_path,
369
+ date_format=self.date_format,
370
+ previous_day_only=True
371
+ )[0].path
372
+ # If you get IndexError, it means that there are no previous day logs to read.
373
+ except IndexError:
374
+ pass
375
+
376
+ # Count all the rotated files.
377
+ current_log_files_count: int = len(get_logs_paths(
378
+ log_file_path=self.log_file_path
379
+ ))
380
+
381
+ # If the count of the log files is greater than the existing logs file count, it means that the rotation
382
+ # happened. We will read the previous day statistics file.
383
+ new_lines_from_previous_file: list = []
384
+ if current_log_files_count > self._existing_logs_file_count:
385
+ current_lines, self.header = csvs.read_csv_to_list_of_dicts_by_header(
386
+ previous_day_statistics_file_path, header=self.header, stdout=False)
387
+
388
+ if self.get_previous_file:
389
+ previous_file_lines = current_lines
390
+
391
+ self._existing_logs_file_count = current_log_files_count
392
+
393
+ new_lines_from_previous_file = self._extract_new_lines_only(current_lines)
394
+
395
+ # empty the previous file lines, since the file is rotated.
396
+ self._reading_existing_lines.clear()
397
+
398
+ current_lines, self.header = csvs.read_csv_to_list_of_dicts_by_header(
399
+ latest_statistics_file_path, header=self.header, stdout=False)
400
+
401
+ new_lines = self._extract_new_lines_only(current_lines)
402
+
403
+ # If we have new lines from the previous file, we will add the new lines from the latest file.
404
+ if new_lines_from_previous_file:
405
+ new_lines = new_lines_from_previous_file + new_lines
406
+
407
+ return new_lines, previous_file_lines, self.header
File without changes
@@ -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