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.
- atomicshop/__init__.py +1 -1
- atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
- atomicshop/a_mains/addons/process_list/compile.cmd +7 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +8 -1
- atomicshop/a_mains/dns_gateway_setting.py +11 -0
- atomicshop/a_mains/get_local_tcp_ports.py +85 -0
- atomicshop/a_mains/github_wrapper.py +11 -0
- atomicshop/a_mains/install_ca_certificate.py +172 -0
- atomicshop/{addons/mains → a_mains}/msi_unpacker.py +3 -1
- atomicshop/a_mains/process_from_port.py +119 -0
- atomicshop/a_mains/set_default_dns_gateway.py +90 -0
- atomicshop/a_mains/update_config_toml.py +38 -0
- atomicshop/appointment_management.py +5 -3
- atomicshop/basics/ansi_escape_codes.py +3 -1
- atomicshop/basics/argparse_template.py +2 -0
- atomicshop/basics/booleans.py +27 -30
- atomicshop/basics/bytes_arrays.py +43 -0
- atomicshop/basics/classes.py +149 -1
- atomicshop/basics/dicts.py +12 -0
- atomicshop/basics/enums.py +2 -2
- atomicshop/basics/exceptions.py +5 -1
- atomicshop/basics/list_of_classes.py +29 -0
- atomicshop/basics/list_of_dicts.py +69 -5
- atomicshop/basics/lists.py +14 -0
- atomicshop/basics/multiprocesses.py +374 -50
- atomicshop/basics/package_module.py +10 -0
- atomicshop/basics/strings.py +160 -7
- atomicshop/basics/threads.py +14 -0
- atomicshop/basics/tracebacks.py +13 -4
- atomicshop/certificates.py +153 -52
- atomicshop/config_init.py +12 -7
- atomicshop/console_user_response.py +7 -14
- atomicshop/consoles.py +9 -0
- atomicshop/datetimes.py +98 -0
- atomicshop/diff_check.py +340 -40
- atomicshop/dns.py +128 -12
- atomicshop/etws/_pywintrace_fix.py +17 -0
- atomicshop/etws/const.py +38 -0
- atomicshop/etws/providers.py +21 -0
- atomicshop/etws/sessions.py +43 -0
- atomicshop/etws/trace.py +168 -0
- atomicshop/etws/traces/trace_dns.py +162 -0
- atomicshop/etws/traces/trace_sysmon_process_creation.py +126 -0
- atomicshop/etws/traces/trace_tcp.py +130 -0
- atomicshop/file_io/csvs.py +222 -24
- atomicshop/file_io/docxs.py +35 -18
- atomicshop/file_io/file_io.py +35 -19
- atomicshop/file_io/jsons.py +49 -0
- atomicshop/file_io/tomls.py +139 -0
- atomicshop/filesystem.py +864 -293
- atomicshop/get_process_list.py +133 -0
- atomicshop/{process_name_cmd.py → get_process_name_cmd_dll.py} +52 -19
- atomicshop/http_parse.py +149 -93
- atomicshop/ip_addresses.py +6 -1
- atomicshop/mitm/centered_settings.py +132 -0
- atomicshop/mitm/config_static.py +207 -0
- atomicshop/mitm/config_toml_editor.py +55 -0
- atomicshop/mitm/connection_thread_worker.py +875 -357
- atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
- atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
- atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
- atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
- atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
- atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
- atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
- atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
- atomicshop/mitm/engines/create_module_template.py +58 -14
- atomicshop/mitm/import_config.py +359 -139
- atomicshop/mitm/initialize_engines.py +160 -74
- atomicshop/mitm/message.py +64 -23
- atomicshop/mitm/mitm_main.py +892 -0
- atomicshop/mitm/recs_files.py +183 -0
- atomicshop/mitm/shared_functions.py +4 -10
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/mitm/statistic_analyzer.py +257 -166
- atomicshop/mitm/statistic_analyzer_helper/analyzer_helper.py +136 -0
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +525 -0
- atomicshop/monitor/change_monitor.py +96 -120
- atomicshop/monitor/checks/dns.py +139 -70
- atomicshop/monitor/checks/file.py +77 -0
- atomicshop/monitor/checks/network.py +81 -77
- atomicshop/monitor/checks/process_running.py +33 -34
- atomicshop/monitor/checks/url.py +94 -0
- atomicshop/networks.py +671 -0
- atomicshop/on_exit.py +205 -0
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/permissions.py +22 -0
- atomicshop/permissions/ubuntu_permissions.py +239 -0
- atomicshop/permissions/win_permissions.py +33 -0
- atomicshop/print_api.py +24 -41
- atomicshop/process.py +63 -17
- atomicshop/process_poller/__init__.py +0 -0
- atomicshop/process_poller/pollers/__init__.py +0 -0
- atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py +95 -0
- atomicshop/process_poller/process_pool.py +207 -0
- atomicshop/process_poller/simple_process_pool.py +311 -0
- atomicshop/process_poller/tracer_base.py +45 -0
- atomicshop/process_poller/tracers/__init__.py +0 -0
- atomicshop/process_poller/tracers/event_log.py +46 -0
- atomicshop/process_poller/tracers/sysmon_etw.py +68 -0
- atomicshop/python_file_patcher.py +1 -1
- atomicshop/python_functions.py +27 -75
- atomicshop/question_answer_engine.py +2 -2
- atomicshop/scheduling.py +24 -5
- atomicshop/sound.py +4 -2
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +158 -172
- atomicshop/startup/__init__.py +0 -0
- atomicshop/startup/win/__init__.py +0 -0
- atomicshop/startup/win/startup_folder.py +53 -0
- atomicshop/startup/win/task_scheduler.py +119 -0
- atomicshop/system_resource_monitor.py +61 -46
- atomicshop/system_resources.py +8 -8
- atomicshop/tempfiles.py +1 -2
- atomicshop/timer.py +30 -11
- atomicshop/urls.py +41 -0
- atomicshop/venvs.py +28 -0
- atomicshop/versioning.py +27 -0
- atomicshop/web.py +110 -25
- atomicshop/web_apis/__init__.py +0 -0
- atomicshop/web_apis/google_custom_search.py +44 -0
- atomicshop/web_apis/google_llm.py +188 -0
- atomicshop/websocket_parse.py +450 -0
- atomicshop/wrappers/certauthw/certauth.py +1 -0
- atomicshop/wrappers/cryptographyw.py +29 -8
- atomicshop/wrappers/ctyping/etw_winapi/__init__.py +0 -0
- atomicshop/wrappers/ctyping/etw_winapi/const.py +335 -0
- atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +393 -0
- atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
- atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
- atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +13 -9
- atomicshop/wrappers/ctyping/msi_windows_installer/tables.py +35 -0
- atomicshop/wrappers/ctyping/setup_device.py +466 -0
- atomicshop/wrappers/ctyping/win_console.py +39 -0
- atomicshop/wrappers/dockerw/dockerw.py +113 -2
- atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
- atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
- atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
- atomicshop/wrappers/factw/get_file_data.py +12 -5
- atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
- atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
- atomicshop/wrappers/factw/postgresql/firmware.py +4 -6
- atomicshop/wrappers/githubw.py +583 -51
- atomicshop/wrappers/loggingw/consts.py +49 -0
- atomicshop/wrappers/loggingw/filters.py +102 -0
- atomicshop/wrappers/loggingw/formatters.py +58 -71
- atomicshop/wrappers/loggingw/handlers.py +459 -40
- atomicshop/wrappers/loggingw/loggers.py +19 -0
- atomicshop/wrappers/loggingw/loggingw.py +1010 -178
- atomicshop/wrappers/loggingw/reading.py +344 -19
- atomicshop/wrappers/mongodbw/__init__.py +0 -0
- atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
- atomicshop/wrappers/mongodbw/mongodbw.py +1432 -0
- atomicshop/wrappers/netshw.py +271 -0
- atomicshop/wrappers/playwrightw/engine.py +34 -19
- atomicshop/wrappers/playwrightw/infra.py +5 -0
- atomicshop/wrappers/playwrightw/javascript.py +7 -3
- atomicshop/wrappers/playwrightw/keyboard.py +14 -0
- atomicshop/wrappers/playwrightw/scenarios.py +172 -5
- atomicshop/wrappers/playwrightw/waits.py +9 -7
- atomicshop/wrappers/powershell_networking.py +80 -0
- atomicshop/wrappers/psutilw/processes.py +81 -0
- atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
- atomicshop/wrappers/psutilw/psutilw.py +9 -0
- atomicshop/wrappers/pyopensslw.py +9 -2
- atomicshop/wrappers/pywin32w/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/cert_store.py +116 -0
- atomicshop/wrappers/pywin32w/console.py +34 -0
- atomicshop/wrappers/pywin32w/win_event_log/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribe.py +212 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +57 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +49 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/schannel_logging.py +97 -0
- atomicshop/wrappers/pywin32w/winshell.py +19 -0
- atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
- atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
- atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
- atomicshop/wrappers/socketw/accepter.py +21 -7
- atomicshop/wrappers/socketw/certificator.py +216 -150
- atomicshop/wrappers/socketw/creator.py +190 -50
- atomicshop/wrappers/socketw/dns_server.py +500 -173
- atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
- atomicshop/wrappers/socketw/process_getter.py +86 -0
- atomicshop/wrappers/socketw/receiver.py +144 -102
- atomicshop/wrappers/socketw/sender.py +65 -35
- atomicshop/wrappers/socketw/sni.py +334 -165
- atomicshop/wrappers/socketw/socket_base.py +134 -0
- atomicshop/wrappers/socketw/socket_client.py +137 -95
- atomicshop/wrappers/socketw/socket_server_tester.py +14 -9
- atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
- atomicshop/wrappers/socketw/ssl_base.py +15 -14
- atomicshop/wrappers/socketw/statistics_csv.py +148 -17
- atomicshop/wrappers/sysmonw.py +157 -0
- atomicshop/wrappers/ubuntu_terminal.py +65 -26
- atomicshop/wrappers/win_auditw.py +189 -0
- atomicshop/wrappers/winregw/__init__.py +0 -0
- atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
- atomicshop/wrappers/winregw/winreg_network.py +232 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -49
- atomicshop-3.10.5.dist-info/RECORD +306 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
- atomicshop/_basics_temp.py +0 -101
- atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
- atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
- atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
- atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
- atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
- atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
- atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
- atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
- atomicshop/addons/package_setup/Setup.cmd +0 -7
- atomicshop/addons/process_list/compile.cmd +0 -2
- atomicshop/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/archiver/_search_in_zip.py +0 -189
- atomicshop/archiver/archiver.py +0 -34
- atomicshop/archiver/search_in_archive.py +0 -250
- atomicshop/archiver/sevenz_app_w.py +0 -86
- atomicshop/archiver/sevenzs.py +0 -44
- atomicshop/archiver/zips.py +0 -293
- atomicshop/etw/dns_trace.py +0 -118
- atomicshop/etw/etw.py +0 -61
- atomicshop/file_types.py +0 -24
- atomicshop/mitm/engines/create_module_template_example.py +0 -13
- atomicshop/mitm/initialize_mitm_server.py +0 -240
- atomicshop/monitor/checks/hash.py +0 -44
- atomicshop/monitor/checks/hash_checks/file.py +0 -55
- atomicshop/monitor/checks/hash_checks/url.py +0 -62
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/permissions.py +0 -110
- atomicshop/process_poller.py +0 -237
- atomicshop/script_as_string_processor.py +0 -38
- atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
- atomicshop/ssh_scripts/process_from_port.py +0 -27
- atomicshop/wrappers/_process_wrapper_curl.py +0 -27
- atomicshop/wrappers/_process_wrapper_tar.py +0 -21
- atomicshop/wrappers/dockerw/install_docker.py +0 -209
- atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
- atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
- atomicshop/wrappers/ffmpegw.py +0 -125
- atomicshop/wrappers/loggingw/checks.py +0 -20
- atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/socketw/base.py +0 -59
- atomicshop/wrappers/socketw/get_process.py +0 -107
- atomicshop/wrappers/wslw.py +0 -191
- atomicshop-2.11.47.dist-info/RECORD +0 -251
- /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
- /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
- /atomicshop/{addons/mains → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
- /atomicshop/{addons/mains → a_mains}/search_for_hyperlinks_in_docx.py +0 -0
- /atomicshop/{archiver → etws}/__init__.py +0 -0
- /atomicshop/{etw → etws/traces}/__init__.py +0 -0
- /atomicshop/{monitor/checks/hash_checks → mitm/statistic_analyzer_helper}/__init__.py +0 -0
- /atomicshop/{wrappers/nodejsw → permissions}/__init__.py +0 -0
- /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
- {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
|
|
9
|
-
|
|
10
|
-
|
|
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
|
|
21
|
-
:param
|
|
22
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|