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,143 +1,18 @@
1
+ import os
1
2
  import datetime
3
+ import json
4
+ from typing import Union, Literal
5
+ import argparse
2
6
 
7
+ from .statistic_analyzer_helper import analyzer_helper, moving_average_helper
3
8
  from .. import filesystem, domains, datetimes, urls
4
9
  from ..basics import dicts
5
- from ..file_io import tomls, xlsxs
10
+ from ..file_io import tomls, xlsxs, jsons, csvs
6
11
  from ..wrappers.loggingw import reading
7
12
  from ..print_api import print_api
8
13
 
9
14
 
10
- def get_the_last_day_number(statistics_content: list, stop_after_lines: int = None) -> int:
11
- """
12
- This function gets the last day number from the statistics content.
13
-
14
- :param statistics_content: list, of lines in the statistics content.
15
- :param stop_after_lines: integer, if specified, the function will stop after the specified number of lines.
16
- :return: integer, the last day number.
17
- """
18
-
19
- last_day_number = None
20
- start_time_temp = None
21
- for line_index, line in enumerate(statistics_content):
22
- try:
23
- request_time = datetime.datetime.strptime(line['request_time_sent'], '%Y-%m-%d %H:%M:%S.%f')
24
- except ValueError:
25
- continue
26
-
27
- if not start_time_temp:
28
- start_time_temp = request_time
29
-
30
- if stop_after_lines:
31
- if line_index == stop_after_lines:
32
- break
33
-
34
- last_day_number = datetimes.get_difference_between_dates_in_days(start_time_temp, request_time)
35
- return last_day_number
36
-
37
-
38
- def create_empty_features_dict() -> dict:
39
- """
40
- This function creates an empty dictionary for the daily stats. This should be initiated for each 'host_type' of:
41
- 'domain', 'subdomain', 'url_no_parameters'.
42
- :return: dict
43
- """
44
-
45
- return {
46
- 'total_count': {}, 'normal_count': {}, 'error_count': {},
47
- 'request_0_byte_count': {}, 'response_0_byte_count': {},
48
- 'request_sizes_list': {}, 'response_sizes_list': {},
49
- 'request_sizes_no_0_bytes_list': {}, 'response_sizes_no_0_bytes_list': {},
50
- 'average_request_size': {}, 'average_response_size': {},
51
- 'average_request_size_no_0_bytes': {}, 'average_response_size_no_0_bytes': {}}
52
-
53
-
54
- def add_to_count_to_daily_stats(
55
- daily_stats: dict, current_day: int, last_day: int, host_type: str, feature: str, host_name: str) -> None:
56
- """
57
- This function adds 1 to the 'count' feature of the current day in the daily stats.
58
-
59
- :param daily_stats: dict, the daily statistics dict.
60
- :param current_day: integer, the current day number.
61
- :param last_day: integer, the last day number.
62
- :param host_type: string, the type of the host. Can be: 'domain', 'subdomain', 'url_no_parameters'.
63
- :param feature: string, the feature to add the count to. Can be: 'total_count', 'normal_count', 'error_count',
64
- 'request_0_byte_count', 'response_0_byte_count'.
65
- :param host_name: string, the name of the host.
66
-
67
- :return: None.
68
- """
69
-
70
- # Aggregate daily domain hits.
71
- if host_name not in daily_stats[host_type][feature].keys():
72
- daily_stats[host_type][feature][host_name] = {}
73
- # Iterate from first day to the last day.
74
- for day in range(0, last_day + 1):
75
- daily_stats[host_type][feature][host_name][day] = 0
76
-
77
- # Add count to current day.
78
- daily_stats[host_type][feature][host_name][current_day] += 1
79
-
80
-
81
- def add_to_list_to_daily_stats(
82
- daily_stats: dict, current_day: int, last_day: int, host_type: str, feature: str, host_name: str,
83
- size: float) -> None:
84
- """
85
- This function adds the 'size' to the 'feature' list of the current day in the daily stats.
86
-
87
- :param daily_stats: dict, the daily statistics dict.
88
- :param current_day: integer, the current day number.
89
- :param last_day: integer, the last day number.
90
- :param host_type: string, the type of the host. Can be: 'domain', 'subdomain', 'url_no_parameters'.
91
- :param feature: string, the feature to add the count to. Can be: 'request_sizes_list', 'response_sizes_list',
92
- 'request_sizes_no_0_bytes_list', 'response_sizes_no_0_bytes_list'.
93
- :param host_name: string, the name of the host.
94
- :param size: float, the size in bytes to add to the list.
95
-
96
- :return: None.
97
- """
98
-
99
- # Aggregate daily domain hits.
100
- if host_name not in daily_stats[host_type][feature].keys():
101
- daily_stats[host_type][feature][host_name] = {}
102
- # Iterate from first day to the last day.
103
- for day in range(0, last_day + 1):
104
- daily_stats[host_type][feature][host_name][day] = []
105
-
106
- # Add count to current day.
107
- daily_stats[host_type][feature][host_name][current_day].append(size)
108
-
109
-
110
- def add_to_average_to_daily_stats(
111
- daily_stats: dict, current_day: int, last_day: int, host_type: str, feature: str, host_name: str,
112
- list_of_sizes: list) -> None:
113
- """
114
- This function adds the average size in bytes calculated from the 'list_of_sizes' to the 'feature' of the current
115
- day in the daily stats.
116
-
117
- :param daily_stats: dict, the daily statistics dict.
118
- :param current_day: integer, the current day number.
119
- :param last_day: integer, the last day number.
120
- :param host_type: string, the type of the host. Can be: 'domain', 'subdomain', 'url_no_parameters'.
121
- :param feature: string, the feature to add the count to. Can be: 'average_request_size', 'average_response_size',
122
- 'average_request_size_no_0_bytes', 'average_response_size_no_0_bytes'.
123
- :param host_name: string, the name of the host.
124
- :param list_of_sizes: list, the list of sizes to calculate the average from.
125
-
126
- :return: None.
127
- """
128
-
129
- # Aggregate daily domain hits.
130
- if host_name not in daily_stats[host_type][feature].keys():
131
- daily_stats[host_type][feature][host_name] = {}
132
- # Iterate from first day to the last day.
133
- for day in range(0, last_day + 1):
134
- daily_stats[host_type][feature][host_name][day] = 0
135
-
136
- # If the list of size is empty, add 0 to the average, since we cannot divide by 0.
137
- if len(list_of_sizes) == 0:
138
- daily_stats[host_type][feature][host_name][current_day] = 0
139
- else:
140
- daily_stats[host_type][feature][host_name][current_day] = sum(list_of_sizes) / len(list_of_sizes)
15
+ STATISTICS_FILE_NAME: str = 'statistics.csv'
141
16
 
142
17
 
143
18
  def analyze(main_file_path: str):
@@ -154,8 +29,10 @@ def analyze(main_file_path: str):
154
29
  summary_path: str = filesystem.check_absolute_path___add_full(config['report_file_path'], script_directory)
155
30
 
156
31
  # Get the content from statistics files.
157
- statistics_content: list = reading.get_logs(
158
- config['statistic_files_path'], pattern='statistics*.csv', log_type='csv',
32
+ log_file_path_pattern: str = f"{config['statistic_files_path']}{os.sep}statistics.csv"
33
+ statistics_content: list = reading.get_all_log_files_into_list(
34
+ log_file_path=log_file_path_pattern,
35
+ log_type='csv'
159
36
  )
160
37
 
161
38
  # Initialize loop.
@@ -167,9 +44,9 @@ def analyze(main_file_path: str):
167
44
  'subdomain': {'total_count': {}, 'normal_count': {}, 'error_count': {}}
168
45
  }
169
46
  daily_stats: dict = {
170
- 'domain': create_empty_features_dict(),
171
- 'subdomain': create_empty_features_dict(),
172
- 'url_no_parameters': create_empty_features_dict()
47
+ 'domain': analyzer_helper.create_empty_features_dict(),
48
+ 'subdomain': analyzer_helper.create_empty_features_dict(),
49
+ 'url_no_parameters': analyzer_helper.create_empty_features_dict()
173
50
  }
174
51
 
175
52
  # Start the main loop.
@@ -190,7 +67,7 @@ def analyze(main_file_path: str):
190
67
 
191
68
  # Find the last day number. If 'break_after_lines' is specified, the loop will stop after the specified line.
192
69
  if not last_day_number:
193
- last_day_number = get_the_last_day_number(statistics_content, break_after_lines)
70
+ last_day_number = analyzer_helper.get_the_last_day_number(statistics_content, break_after_lines)
194
71
 
195
72
  if break_after_lines:
196
73
  if line_index == break_after_lines:
@@ -290,87 +167,87 @@ def analyze(main_file_path: str):
290
167
  day_number = datetimes.get_difference_between_dates_in_days(start_time, request_time)
291
168
 
292
169
  # Add 1 to the total count of the current day.
293
- add_to_count_to_daily_stats(
170
+ analyzer_helper.add_to_count_to_daily_stats(
294
171
  daily_stats, day_number, last_day_number, 'domain', 'total_count', main_domain)
295
- add_to_count_to_daily_stats(
172
+ analyzer_helper.add_to_count_to_daily_stats(
296
173
  daily_stats, day_number, last_day_number, 'subdomain', 'total_count', subdomain)
297
- add_to_count_to_daily_stats(
174
+ analyzer_helper.add_to_count_to_daily_stats(
298
175
  daily_stats, day_number, last_day_number, 'url_no_parameters', 'total_count', url_no_parameters)
299
176
 
300
177
  # Handle line if it has error.
301
178
  if line['error'] != '':
302
- add_to_count_to_daily_stats(
179
+ analyzer_helper.add_to_count_to_daily_stats(
303
180
  daily_stats, day_number, last_day_number, 'domain', 'error_count', main_domain)
304
- add_to_count_to_daily_stats(
181
+ analyzer_helper.add_to_count_to_daily_stats(
305
182
  daily_stats, day_number, last_day_number, 'subdomain', 'error_count', subdomain)
306
- add_to_count_to_daily_stats(
183
+ analyzer_helper.add_to_count_to_daily_stats(
307
184
  daily_stats, day_number, last_day_number, 'url_no_parameters', 'error_count', url_no_parameters)
308
185
  else:
309
- add_to_count_to_daily_stats(
186
+ analyzer_helper.add_to_count_to_daily_stats(
310
187
  daily_stats, day_number, last_day_number, 'domain', 'normal_count', main_domain)
311
- add_to_count_to_daily_stats(
188
+ analyzer_helper.add_to_count_to_daily_stats(
312
189
  daily_stats, day_number, last_day_number, 'subdomain', 'normal_count', subdomain)
313
- add_to_count_to_daily_stats(
190
+ analyzer_helper.add_to_count_to_daily_stats(
314
191
  daily_stats, day_number, last_day_number, 'url_no_parameters', 'normal_count', url_no_parameters)
315
192
 
316
193
  if request_size == 0:
317
- add_to_count_to_daily_stats(
194
+ analyzer_helper.add_to_count_to_daily_stats(
318
195
  daily_stats, day_number, last_day_number, 'domain', 'request_0_byte_count',
319
196
  main_domain)
320
- add_to_count_to_daily_stats(
197
+ analyzer_helper.add_to_count_to_daily_stats(
321
198
  daily_stats, day_number, last_day_number, 'subdomain', 'request_0_byte_count',
322
199
  subdomain)
323
- add_to_count_to_daily_stats(
200
+ analyzer_helper.add_to_count_to_daily_stats(
324
201
  daily_stats, day_number, last_day_number, 'url_no_parameters', 'request_0_byte_count',
325
202
  url_no_parameters)
326
203
 
327
204
  if response_size == 0:
328
- add_to_count_to_daily_stats(
205
+ analyzer_helper.add_to_count_to_daily_stats(
329
206
  daily_stats, day_number, last_day_number, 'domain', 'response_0_byte_count',
330
207
  main_domain)
331
- add_to_count_to_daily_stats(
208
+ analyzer_helper.add_to_count_to_daily_stats(
332
209
  daily_stats, day_number, last_day_number, 'subdomain', 'response_0_byte_count',
333
210
  subdomain)
334
- add_to_count_to_daily_stats(
211
+ analyzer_helper.add_to_count_to_daily_stats(
335
212
  daily_stats, day_number, last_day_number, 'url_no_parameters', 'response_0_byte_count',
336
213
  url_no_parameters)
337
214
 
338
215
  if request_size is not None and response_size is not None:
339
- add_to_list_to_daily_stats(
216
+ analyzer_helper.add_to_list_to_daily_stats(
340
217
  daily_stats, day_number, last_day_number, 'domain', 'request_sizes_list', main_domain, request_size)
341
- add_to_list_to_daily_stats(
218
+ analyzer_helper.add_to_list_to_daily_stats(
342
219
  daily_stats, day_number, last_day_number, 'subdomain', 'request_sizes_list', subdomain, request_size)
343
- add_to_list_to_daily_stats(
220
+ analyzer_helper.add_to_list_to_daily_stats(
344
221
  daily_stats, day_number, last_day_number, 'url_no_parameters', 'request_sizes_list', url_no_parameters,
345
222
  request_size)
346
223
 
347
- add_to_list_to_daily_stats(
224
+ analyzer_helper.add_to_list_to_daily_stats(
348
225
  daily_stats, day_number, last_day_number, 'domain', 'response_sizes_list', main_domain, response_size)
349
- add_to_list_to_daily_stats(
226
+ analyzer_helper.add_to_list_to_daily_stats(
350
227
  daily_stats, day_number, last_day_number, 'subdomain', 'response_sizes_list', subdomain, response_size)
351
- add_to_list_to_daily_stats(
228
+ analyzer_helper.add_to_list_to_daily_stats(
352
229
  daily_stats, day_number, last_day_number, 'url_no_parameters', 'response_sizes_list', url_no_parameters,
353
230
  response_size)
354
231
 
355
232
  if request_size != 0 and request_size is not None:
356
- add_to_list_to_daily_stats(
233
+ analyzer_helper.add_to_list_to_daily_stats(
357
234
  daily_stats, day_number, last_day_number, 'domain', 'request_sizes_no_0_bytes_list',
358
235
  main_domain, request_size)
359
- add_to_list_to_daily_stats(
236
+ analyzer_helper.add_to_list_to_daily_stats(
360
237
  daily_stats, day_number, last_day_number, 'subdomain', 'request_sizes_no_0_bytes_list',
361
238
  subdomain, request_size)
362
- add_to_list_to_daily_stats(
239
+ analyzer_helper.add_to_list_to_daily_stats(
363
240
  daily_stats, day_number, last_day_number, 'url_no_parameters', 'request_sizes_no_0_bytes_list',
364
241
  url_no_parameters, request_size)
365
242
 
366
243
  if response_size != 0 and response_size is not None:
367
- add_to_list_to_daily_stats(
244
+ analyzer_helper.add_to_list_to_daily_stats(
368
245
  daily_stats, day_number, last_day_number, 'domain', 'response_sizes_no_0_bytes_list',
369
246
  main_domain, response_size)
370
- add_to_list_to_daily_stats(
247
+ analyzer_helper.add_to_list_to_daily_stats(
371
248
  daily_stats, day_number, last_day_number, 'subdomain', 'response_sizes_no_0_bytes_list',
372
249
  subdomain, response_size)
373
- add_to_list_to_daily_stats(
250
+ analyzer_helper.add_to_list_to_daily_stats(
374
251
  daily_stats, day_number, last_day_number, 'url_no_parameters', 'response_sizes_no_0_bytes_list',
375
252
  url_no_parameters, response_size)
376
253
 
@@ -392,7 +269,7 @@ def analyze(main_file_path: str):
392
269
 
393
270
  for host_name, days in hosts.items():
394
271
  for day, sizes in days.items():
395
- add_to_average_to_daily_stats(
272
+ analyzer_helper.add_to_average_to_daily_stats(
396
273
  daily_stats, day, last_day_number, host_type, feature_name, host_name, sizes)
397
274
 
398
275
  # Sorting overall stats.
@@ -463,3 +340,217 @@ def analyze(main_file_path: str):
463
340
  xlsxs.write_xlsx(combined_sorted_stats, file_path=summary_path)
464
341
 
465
342
  return
343
+
344
+
345
+ # ======================================================================================================================
346
+
347
+
348
+ def deviation_calculator_by_moving_average(
349
+ statistics_file_directory: str = None,
350
+ statistics_content: dict = None,
351
+ by_type: Literal['host', 'url'] = 'url',
352
+ moving_average_window_days: int = 5,
353
+ top_bottom_deviation_percentage: float = 0.25,
354
+ get_deviation_for_last_day_only: bool = False,
355
+ get_deviation_for_date: str = None,
356
+ summary: bool = False,
357
+ output_file_path: str = None,
358
+ output_file_type: Literal['json', 'csv'] = 'json',
359
+ convert_sizes_lists_and_ma_data_to_string: bool = False,
360
+ skip_total_count_less_than: int = None
361
+ ) -> Union[list, None]:
362
+ """
363
+ This function is the main function for the moving average calculator.
364
+
365
+ :param statistics_file_directory: string, can be either providing the directory where 'statistics.csv' file resides
366
+ or None. If None, the 'statistics_content' must be provided.
367
+ The directory where 'statistics.csv' file resides.
368
+ Also, all the rotated files like: statistics_2021-01-01.csv, statistics_2021-01-02.csv, etc.
369
+ These will be analyzed in the order of the date in the file name.
370
+ :param statistics_content: dict, if specified, this will be used instead of reading the files from the directory.
371
+ The dict should be a result of the 'atomicshop.mitm.statistic_analyzer_helper.moving_average_helper.get_all_files_content'.
372
+ :param by_type: string, 'host' or 'url'. The type of the deviation calculation.
373
+ 'host' will calculate the deviation by the host name. Example: maps.google.com, yahoo.com, etc.
374
+ 'url' will calculate the deviation by the URL. Example: maps.google.com/maps, yahoo.com/news, etc.
375
+ :param moving_average_window_days: integer, the moving average window days.
376
+ :param top_bottom_deviation_percentage: float, the top bottom deviation percentage. Example: 0.1 for 10%.
377
+ :param get_deviation_for_last_day_only: bool, if True, only the last day will be analyzed.
378
+ Example: With 'moving_average_window_days=5', the last 6 days will be analyzed.
379
+ 5 days for moving average and the last day for deviation.
380
+ File names example:
381
+ statistics_2021-01-01.csv
382
+ statistics_2021-01-02.csv
383
+ statistics_2021-01-03.csv
384
+ statistics_2021-01-04.csv
385
+ statistics_2021-01-05.csv
386
+ statistics_2021-01-06.csv
387
+ Files 01 to 05 will be used for moving average and the file 06 for deviation.
388
+ Meaning the average calculated for 2021-01-06 will be compared to the values moving average of 2021-01-01
389
+ to 2021-01-05.
390
+ :param get_deviation_for_date: string, if specified, only the specified date will be analyzed.
391
+ The date must be in the format of 'YYYY-MM-DD'. Example: '2021-01-06'.
392
+ :param summary: bool, if True, Only the summary will be generated without all the numbers that were used
393
+ to calculate the averages and the moving average data.
394
+ :param output_file_path: string, if None, no file will be written.
395
+ :param output_file_type: string, the type of the output file. 'json' or 'csv'.
396
+ :param convert_sizes_lists_and_ma_data_to_string: bool, if True, the 'request_sizes', 'response_sizes' and 'ma_data'
397
+ will be converted to string. This is useful when writing to files, so the view will be more readable.
398
+ :param skip_total_count_less_than: integer, if specified, the deviation calculation will be skipped
399
+ if the total count is less than this number.
400
+ This means that if we have moving average window of 7 days, on the 8th day, the deviation will be calculated
401
+ only if the total count of the checked type
402
+ (request average size, response average, request count, response count)
403
+ is greater or equal to this number.
404
+ -----------------------------
405
+ :return: the deviation list of dicts.
406
+
407
+ Example:
408
+ import sys
409
+ from atomicshop.mitm import statistic_analyzer
410
+
411
+
412
+ def main():
413
+ return statistic_analyzer.moving_average_calculator_main(
414
+ statistics_file_path='statistics.csv',
415
+ moving_average_window_days=7,
416
+ top_bottom_deviation_percentage=0.1,
417
+ output_json_file='C:\\output\\deviation_list.json'
418
+ )
419
+
420
+
421
+ if __name__ == '__main__':
422
+ sys.exit(main())
423
+ """
424
+
425
+ def convert_data_value_to_string(value_key: str, list_index: int) -> None:
426
+ deviation_list[list_index]['data'][value_key] = json.dumps(deviation_list[list_index]['data'][value_key])
427
+
428
+ def convert_value_to_string(value_key: str, list_index: int) -> None:
429
+ if value_key in deviation_list[list_index]:
430
+ deviation_list[list_index][value_key] = json.dumps(deviation_list[list_index][value_key])
431
+
432
+ if not statistics_file_directory and not statistics_content:
433
+ raise ValueError('Either [statistics_file_directory] or [statistics_content] must be provided.')
434
+ if statistics_file_directory and statistics_content:
435
+ raise ValueError('Either [statistics_file_directory] or [statistics_content] must be provided, not both.')
436
+
437
+ if output_file_type not in ['json', 'csv']:
438
+ raise ValueError(f'output_file_type must be "json" or "csv", not [{output_file_type}]')
439
+
440
+ if by_type not in ['host', 'url']:
441
+ raise ValueError(f'by_type must be "host" or "url", not [{by_type}]')
442
+
443
+ if get_deviation_for_last_day_only and get_deviation_for_date:
444
+ raise ValueError('Either [get_deviation_for_last_day_only] or [get_deviation_for_date] can be provided, not both.')
445
+
446
+ if statistics_file_directory:
447
+ statistics_file_path: str | None = f'{statistics_file_directory}{os.sep}{STATISTICS_FILE_NAME}'
448
+ else:
449
+ statistics_file_path: str | None = None
450
+
451
+ deviation_list = moving_average_helper.calculate_moving_average(
452
+ statistics_file_path,
453
+ statistics_content,
454
+ by_type,
455
+ moving_average_window_days,
456
+ top_bottom_deviation_percentage,
457
+ get_deviation_for_last_day_only,
458
+ get_deviation_for_date,
459
+ skip_total_count_less_than
460
+ )
461
+
462
+ if deviation_list:
463
+ if convert_sizes_lists_and_ma_data_to_string:
464
+ for deviation_list_index, deviation in enumerate(deviation_list):
465
+ convert_data_value_to_string('request_sizes', deviation_list_index)
466
+ convert_data_value_to_string('response_sizes', deviation_list_index)
467
+ convert_value_to_string('ma_data', deviation_list_index)
468
+
469
+ if summary:
470
+ for deviation in deviation_list:
471
+ _ = deviation.pop('check_type')
472
+ _ = deviation.pop('percentage')
473
+ _ = deviation.pop('ma_value_checked')
474
+ _ = deviation.pop('deviation_type')
475
+ _ = deviation.pop('data')
476
+ _ = deviation.pop('ma_data')
477
+
478
+ if output_file_path:
479
+ print_api(f'Deviation Found, saving to file: {output_file_path}', color='blue')
480
+
481
+ if output_file_type == 'csv':
482
+ csvs.write_list_to_csv(deviation_list, output_file_path)
483
+ elif output_file_type == 'json':
484
+ jsons.write_json_file(deviation_list, output_file_path, use_default_indent=True)
485
+
486
+ return deviation_list
487
+
488
+ return []
489
+
490
+
491
+ def deviation_calculator_by_moving_average_main():
492
+ def parse_args():
493
+ description_full: str = (
494
+ f'Description: Calculate deviation by moving average.')
495
+
496
+ parser = argparse.ArgumentParser(
497
+ description=description_full)
498
+
499
+ parser.add_argument(
500
+ '-d', '--directory', type=str, required=True,
501
+ help='Provide full path to directory with statistics.csv files.')
502
+ parser.add_argument(
503
+ '-of', '--output_file', type=str, required=True, help='Provide full path to output file.')
504
+ parser.add_argument(
505
+ '-ot', '--output_type', type=str, required=True, help='Provide output type: [json] or [csv].')
506
+ parser.add_argument(
507
+ '-by', '--by_type', type=str, required=True, help='Calculate by [host] or [url].')
508
+ parser.add_argument(
509
+ '-f', '--full_details', action='store_true', required=False,
510
+ help='(OPTIONAL) Output full processing details instead of summary.')
511
+ parser.add_argument(
512
+ '-w', '--window', type=int, required=True, help='Moving average window in days.')
513
+ parser.add_argument(
514
+ '-p', '--percentage', type=float, required=True,
515
+ help='Percentage of deviation from moving average. Example: 0.1 for 10%%.')
516
+ parser.add_argument(
517
+ '-slt', '--skip_total_count_less_than', type=int, required=False,
518
+ help='An integer to skip the deviation calculation if the total count is less than this number.')
519
+
520
+ return parser.parse_args()
521
+
522
+ args = parse_args()
523
+
524
+ if args.output_type == 'csv' and args.full_details:
525
+ print_api("Full details output is not supported for csv files.", color='red')
526
+ return 1
527
+
528
+ if not os.path.isdir(args.directory):
529
+ print_api(f"Directory does not exist: {args.directory}", color='red')
530
+ return 1
531
+
532
+ if not filesystem.get_paths_from_directory(
533
+ directory_path=args.directory, get_file=True, simple_list=True, file_name_check_pattern='*statistics*.csv'):
534
+ print_api(f"No statistics files found in: {args.directory}", color='red')
535
+ return 1
536
+
537
+ if args.full_details:
538
+ summary = False
539
+ convert_sizes_lists_and_ma_data_to_string = True
540
+ else:
541
+ summary = True
542
+ convert_sizes_lists_and_ma_data_to_string = False
543
+
544
+ _ = deviation_calculator_by_moving_average(
545
+ statistics_file_directory=args.directory,
546
+ by_type=args.by_type,
547
+ moving_average_window_days=args.window,
548
+ top_bottom_deviation_percentage=args.percentage,
549
+ summary=summary,
550
+ output_file_path=args.output_file,
551
+ output_file_type=args.output_type,
552
+ convert_sizes_lists_and_ma_data_to_string=convert_sizes_lists_and_ma_data_to_string,
553
+ skip_total_count_less_than=args.skip_total_count_less_than
554
+ )
555
+
556
+ return 0
@@ -0,0 +1,136 @@
1
+ import datetime
2
+
3
+ from ... import datetimes
4
+
5
+
6
+ def get_the_last_day_number(statistics_content: list, stop_after_lines: int = None) -> int:
7
+ """
8
+ This function gets the last day number from the statistics content.
9
+
10
+ :param statistics_content: list, of lines in the statistics content.
11
+ :param stop_after_lines: integer, if specified, the function will stop after the specified number of lines.
12
+ :return: integer, the last day number.
13
+ """
14
+
15
+ last_day_number = None
16
+ start_time_temp = None
17
+ for line_index, line in enumerate(statistics_content):
18
+ try:
19
+ request_time = datetime.datetime.strptime(line['request_time_sent'], '%Y-%m-%d %H:%M:%S.%f')
20
+ except ValueError:
21
+ continue
22
+
23
+ if not start_time_temp:
24
+ start_time_temp = request_time
25
+
26
+ if stop_after_lines:
27
+ if line_index == stop_after_lines:
28
+ break
29
+
30
+ last_day_number = datetimes.get_difference_between_dates_in_days(start_time_temp, request_time)
31
+ return last_day_number
32
+
33
+
34
+ def create_empty_features_dict() -> dict:
35
+ """
36
+ This function creates an empty dictionary for the daily stats. This should be initiated for each 'host_type' of:
37
+ 'domain', 'subdomain', 'url_no_parameters'.
38
+ :return: dict
39
+ """
40
+
41
+ return {
42
+ 'total_count': {}, 'normal_count': {}, 'error_count': {},
43
+ 'request_0_byte_count': {}, 'response_0_byte_count': {},
44
+ 'request_sizes_list': {}, 'response_sizes_list': {},
45
+ 'request_sizes_no_0_bytes_list': {}, 'response_sizes_no_0_bytes_list': {},
46
+ 'average_request_size': {}, 'average_response_size': {},
47
+ 'average_request_size_no_0_bytes': {}, 'average_response_size_no_0_bytes': {}}
48
+
49
+
50
+ def add_to_count_to_daily_stats(
51
+ daily_stats: dict, current_day: int, last_day: int, host_type: str, feature: str, host_name: str) -> None:
52
+ """
53
+ This function adds 1 to the 'count' feature of the current day in the daily stats.
54
+
55
+ :param daily_stats: dict, the daily statistics dict.
56
+ :param current_day: integer, the current day number.
57
+ :param last_day: integer, the last day number.
58
+ :param host_type: string, the type of the host. Can be: 'domain', 'subdomain', 'url_no_parameters'.
59
+ :param feature: string, the feature to add the count to. Can be: 'total_count', 'normal_count', 'error_count',
60
+ 'request_0_byte_count', 'response_0_byte_count'.
61
+ :param host_name: string, the name of the host.
62
+
63
+ :return: None.
64
+ """
65
+
66
+ # Aggregate daily domain hits.
67
+ if host_name not in daily_stats[host_type][feature].keys():
68
+ daily_stats[host_type][feature][host_name] = {}
69
+ # Iterate from first day to the last day.
70
+ for day in range(0, last_day + 1):
71
+ daily_stats[host_type][feature][host_name][day] = 0
72
+
73
+ # Add count to current day.
74
+ daily_stats[host_type][feature][host_name][current_day] += 1
75
+
76
+
77
+ def add_to_list_to_daily_stats(
78
+ daily_stats: dict, current_day: int, last_day: int, host_type: str, feature: str, host_name: str,
79
+ size: float) -> None:
80
+ """
81
+ This function adds the 'size' to the 'feature' list of the current day in the daily stats.
82
+
83
+ :param daily_stats: dict, the daily statistics dict.
84
+ :param current_day: integer, the current day number.
85
+ :param last_day: integer, the last day number.
86
+ :param host_type: string, the type of the host. Can be: 'domain', 'subdomain', 'url_no_parameters'.
87
+ :param feature: string, the feature to add the count to. Can be: 'request_sizes_list', 'response_sizes_list',
88
+ 'request_sizes_no_0_bytes_list', 'response_sizes_no_0_bytes_list'.
89
+ :param host_name: string, the name of the host.
90
+ :param size: float, the size in bytes to add to the list.
91
+
92
+ :return: None.
93
+ """
94
+
95
+ # Aggregate daily domain hits.
96
+ if host_name not in daily_stats[host_type][feature].keys():
97
+ daily_stats[host_type][feature][host_name] = {}
98
+ # Iterate from first day to the last day.
99
+ for day in range(0, last_day + 1):
100
+ daily_stats[host_type][feature][host_name][day] = []
101
+
102
+ # Add count to current day.
103
+ daily_stats[host_type][feature][host_name][current_day].append(size)
104
+
105
+
106
+ def add_to_average_to_daily_stats(
107
+ daily_stats: dict, current_day: int, last_day: int, host_type: str, feature: str, host_name: str,
108
+ list_of_sizes: list) -> None:
109
+ """
110
+ This function adds the average size in bytes calculated from the 'list_of_sizes' to the 'feature' of the current
111
+ day in the daily stats.
112
+
113
+ :param daily_stats: dict, the daily statistics dict.
114
+ :param current_day: integer, the current day number.
115
+ :param last_day: integer, the last day number.
116
+ :param host_type: string, the type of the host. Can be: 'domain', 'subdomain', 'url_no_parameters'.
117
+ :param feature: string, the feature to add the count to. Can be: 'average_request_size', 'average_response_size',
118
+ 'average_request_size_no_0_bytes', 'average_response_size_no_0_bytes'.
119
+ :param host_name: string, the name of the host.
120
+ :param list_of_sizes: list, the list of sizes to calculate the average from.
121
+
122
+ :return: None.
123
+ """
124
+
125
+ # Aggregate daily domain hits.
126
+ if host_name not in daily_stats[host_type][feature].keys():
127
+ daily_stats[host_type][feature][host_name] = {}
128
+ # Iterate from first day to the last day.
129
+ for day in range(0, last_day + 1):
130
+ daily_stats[host_type][feature][host_name][day] = 0
131
+
132
+ # If the list of size is empty, add 0 to the average, since we cannot divide by 0.
133
+ if len(list_of_sizes) == 0:
134
+ daily_stats[host_type][feature][host_name][current_day] = 0
135
+ else:
136
+ daily_stats[host_type][feature][host_name][current_day] = sum(list_of_sizes) / len(list_of_sizes)