atomicshop 2.15.5__py3-none-any.whl → 2.15.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of atomicshop might be problematic. Click here for more details.

atomicshop/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  """Atomic Basic functions and classes to make developer life easier"""
2
2
 
3
3
  __author__ = "Den Kras"
4
- __version__ = '2.15.5'
4
+ __version__ = '2.15.7'
@@ -108,7 +108,7 @@ def is_any_string_from_list_in_string(string_list: list, check_string: str) -> b
108
108
  return any(test_string in check_string for test_string in string_list)
109
109
 
110
110
 
111
- def match_pattern_against_string(
111
+ def _match_pattern_against_string(
112
112
  pattern: str,
113
113
  check_string: str,
114
114
  case_insensitive: bool = False,
@@ -214,6 +214,73 @@ def match_pattern_against_string(
214
214
  return False
215
215
 
216
216
 
217
+ def match_pattern_against_string(
218
+ pattern: str,
219
+ check_string: str,
220
+ case_insensitive: bool = False,
221
+ prefix_suffix: bool = False
222
+ ) -> bool:
223
+ """
224
+ Function checks the 'pattern' against 'check_string' and returns 'True' if pattern matches and 'False' if not.
225
+
226
+ Example:
227
+ pattern_string = "*ffmpeg*full_build.zip"
228
+ check_string = "https://github.com/GyanD/codexffmpeg/releases/download/5.1.2/ffmpeg-5.1.2-full_build.zip"
229
+ match_pattern_against_string(pattern_string, check_string)
230
+ Result:
231
+ True
232
+
233
+ :param pattern: string, can include wildcards as '*'.
234
+ :param check_string: string, to check the pattern against.
235
+ :param case_insensitive: boolean, if 'True' will treat the 'pattern' and 'check_string' as case-insensitive.
236
+ :param prefix_suffix: boolean, that sets if the function should return 'True' also for all the cases that wildcard
237
+ in the beginning of the pattern and in the end of the pattern, since the default behavior of regex to return
238
+ 'False' on these cases.
239
+
240
+ Example:
241
+ pattern: *test
242
+ check_string: testblabla
243
+ Default regex behavior will return 'False' with 'prefix_suffix' switch set to 'True',
244
+ this case will return 'True'. Same will go for:
245
+ pattern: test*
246
+ check_string: blablatest
247
+
248
+ Why this is good?
249
+ Let's say you have a python script 'example.py' and you want to find all the executed command lines,
250
+ and you want to make sure that this was executed by 'python'. Your python is installed
251
+ in 'c:\\Python310\\python.exe', and you want to match all the possible patterns of 'python' and 'example.py'.
252
+ Pattern:
253
+ *python*example.py
254
+ You want to match 'True' for the next cases:
255
+ python example.py
256
+ c:\\Python310\\python.exe example.py
257
+
258
+ Default regex behavior is to return 'False' on 'python example.py'.
259
+
260
+ :return: boolean.
261
+ """
262
+ # Determine the regex flags based on case_insensitive.
263
+ flags = re.IGNORECASE if case_insensitive else 0
264
+
265
+ # Escape the pattern for regex, then replace '*' with '.*' to match any characters.
266
+ escaped_pattern = re.escape(pattern).replace(r'\*', '.*')
267
+
268
+ # Adjust the pattern to match from the start and/or end based on prefix_suffix.
269
+ if prefix_suffix:
270
+ if not pattern.startswith('*'):
271
+ escaped_pattern = '.*' + escaped_pattern
272
+ if not pattern.endswith('*'):
273
+ escaped_pattern = escaped_pattern + '.*'
274
+ else:
275
+ escaped_pattern = '^' + escaped_pattern + '$'
276
+
277
+ # Compile the regex pattern with the appropriate flags.
278
+ regex_pattern = re.compile(escaped_pattern, flags)
279
+
280
+ # Perform the search and return the result.
281
+ return bool(regex_pattern.search(check_string))
282
+
283
+
217
284
  def match_list_of_patterns_against_string(
218
285
  patterns: list,
219
286
  check_string: str,
@@ -435,6 +435,7 @@ def deviation_calculator_by_moving_average_main(
435
435
  'message': deviation['message'],
436
436
  'value': deviation.get('value', None),
437
437
  'ma_value': deviation.get('ma_value', None),
438
+ 'deviation_percentage': deviation.get('deviation_percentage', None),
438
439
  'total_entries_averaged': total_entries_averaged
439
440
  })
440
441
 
@@ -273,11 +273,17 @@ def find_deviation_from_moving_average(
273
273
  check_type_moving_below = host_moving_average_by_type - check_type_moving_by_percent
274
274
 
275
275
  deviation_type = None
276
+ deviation_percentage = None
276
277
  if day_statistics_content_dict[check_type] > check_type_moving_above:
277
278
  deviation_type = 'above'
279
+ deviation_percentage = (
280
+ (day_statistics_content_dict[check_type] - host_moving_average_by_type) /
281
+ host_moving_average_by_type)
278
282
  elif day_statistics_content_dict[check_type] < check_type_moving_below:
279
283
  deviation_type = 'below'
280
-
284
+ deviation_percentage = (
285
+ (host_moving_average_by_type - day_statistics_content_dict[check_type]) /
286
+ host_moving_average_by_type)
281
287
  if deviation_type:
282
288
  message = f'[{check_type}] is [{deviation_type}] the moving average.'
283
289
  deviation_list.append({
@@ -289,6 +295,7 @@ def find_deviation_from_moving_average(
289
295
  'check_type': check_type,
290
296
  'percentage': top_bottom_deviation_percentage,
291
297
  'ma_value_checked': check_type_moving_above,
298
+ 'deviation_percentage': deviation_percentage,
292
299
  'deviation_type': deviation_type,
293
300
  'data': day_statistics_content_dict,
294
301
  'ma_data': moving_averages_dict[host]
atomicshop/process.py CHANGED
@@ -9,11 +9,16 @@ from .print_api import print_api
9
9
  from .inspect_wrapper import get_target_function_default_args_and_combine_with_current
10
10
  from .basics import strings
11
11
  from .wrappers import ubuntu_terminal
12
+ from .wrappers.psutilw import processes
12
13
 
13
14
  if os.name == 'nt':
14
15
  from . import get_process_list
15
16
 
16
17
 
18
+ class MultipleProcessesFound(Exception):
19
+ pass
20
+
21
+
17
22
  def is_command_exists(cmd: str) -> bool:
18
23
  """
19
24
  The function checks if the command exists in the system.
@@ -247,25 +252,28 @@ def safe_terminate(popen_process: subprocess.Popen):
247
252
  popen_process.wait()
248
253
 
249
254
 
250
- def match_pattern_against_running_processes_cmdlines(
255
+ def get_running_processes_by_cmdline_pattern(
251
256
  pattern: str,
252
- process_name_case_insensitive: bool = False,
257
+ cmdline_case_insensitive: bool = False,
253
258
  first: bool = False,
254
259
  prefix_suffix: bool = False
255
- ):
260
+ ) -> list:
256
261
  """
257
262
  The function matches specified string pattern including wildcards against all the currently running processes'
258
263
  command lines.
259
264
 
260
265
  :param pattern: string, the pattern that we will search in the command line list of currently running processes.
261
- :param process_name_case_insensitive: boolean, if True, the process name will be matched case insensitive.
266
+ :param cmdline_case_insensitive: boolean,
267
+ True, the pattern and the command line will be matched case-insensitive.
262
268
  :param first: boolean, that will set if first pattern match found the iteration will stop, or we will return
263
269
  the list of all command lines that contain the pattern.
264
270
  :param prefix_suffix: boolean. Check the description in 'match_pattern_against_string' function.
271
+
272
+ :return: list, of command lines that contain the pattern.
265
273
  """
266
274
 
267
275
  # Get the list of all the currently running processes.
268
- get_process_list_instance = get_process_list.GetProcessList(get_method='pywin32', connect_on_init=True)
276
+ get_process_list_instance = get_process_list.GetProcessList(get_method='psutil', connect_on_init=True)
269
277
  processes = get_process_list_instance.get_processes(as_dict=False)
270
278
 
271
279
  # Iterate through all the current process, while fetching executable file 'name' and the command line.
@@ -273,18 +281,30 @@ def match_pattern_against_running_processes_cmdlines(
273
281
  matched_cmdlines: list = list()
274
282
  for process in processes:
275
283
  # Check if command line isn't empty and that string pattern is matched against command line.
276
- if process['cmdline'] and \
277
- strings.match_pattern_against_string(
278
- pattern, process['cmdline'], case_insensitive=process_name_case_insensitive,
279
- prefix_suffix=prefix_suffix):
280
- matched_cmdlines.append(process['cmdline'])
281
- # If 'first' was set to 'True' we will stop, since we found the first match.
282
- if first:
283
- break
284
+ if process['cmdline']:
285
+ is_pattern_matched = strings.match_pattern_against_string(
286
+ pattern, process['cmdline'], case_insensitive=cmdline_case_insensitive, prefix_suffix=prefix_suffix)
287
+ if is_pattern_matched:
288
+ matched_cmdlines.append(process)
289
+ # If 'first' was set to 'True' we will stop, since we found the first match.
290
+ if first:
291
+ break
284
292
 
285
293
  return matched_cmdlines
286
294
 
287
295
 
296
+ def kill_process_by_filename_pattern(pattern: str):
297
+ running_processes = get_running_processes_by_cmdline_pattern(
298
+ pattern, first=False, prefix_suffix=True, cmdline_case_insensitive=True)
299
+
300
+ processes_found: int = len(running_processes)
301
+
302
+ if processes_found > 1:
303
+ raise MultipleProcessesFound(f"[{processes_found}] processes found with pattern '{pattern}'.")
304
+ elif processes_found == 1:
305
+ processes.kill_process_by_pid(running_processes[0]['pid'])
306
+
307
+
288
308
  def run_powershell_command(command):
289
309
  try:
290
310
  result = subprocess.run(["powershell", "-Command", command], capture_output=True, text=True, check=True)
@@ -1,6 +1,8 @@
1
1
  import psutil
2
2
  import time
3
3
 
4
+ from ...print_api import print_api
5
+
4
6
 
5
7
  def wait_for_process(pid: int):
6
8
  """
@@ -24,3 +26,20 @@ def wait_for_process(pid: int):
24
26
  print(f"No process found with PID {pid}")
25
27
  except psutil.AccessDenied:
26
28
  print(f"Access denied to process with PID {pid}")
29
+
30
+
31
+ def kill_process_by_pid(pid: int, print_kwargs: dict = None):
32
+ try:
33
+ print_api(f"Terminating process: {pid}.", **(print_kwargs or {}))
34
+ proc = psutil.Process(pid)
35
+ proc.terminate() # or proc.kill() if you want to forcefully kill it
36
+ proc.wait(timeout=5) # Wait up to 5 seconds for the process to terminate
37
+ except psutil.NoSuchProcess:
38
+ # print(f"No process found with PID {pid}.")
39
+ pass
40
+ except psutil.AccessDenied:
41
+ # print(f"Access denied to terminate process with PID {pid}.")
42
+ pass
43
+ except psutil.TimeoutExpired:
44
+ # print(f"Process {pid} did not terminate in time.")
45
+ pass
@@ -48,7 +48,7 @@ def is_sysmon_running():
48
48
  :return: boolean, True if Sysmon is running, False otherwise.
49
49
  """
50
50
 
51
- process_list: list = process.match_pattern_against_running_processes_cmdlines(
51
+ process_list: list = process.get_running_processes_by_cmdline_pattern(
52
52
  pattern=SYSMON_FILE_NAME, first=True, process_name_case_insensitive=True)
53
53
 
54
54
  if process_list:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.15.5
3
+ Version: 2.15.7
4
4
  Summary: Atomic functions and classes to make developer life easier
5
5
  Author: Denis Kras
6
6
  License: MIT License
@@ -1,4 +1,4 @@
1
- atomicshop/__init__.py,sha256=wv9rbZlxTZ5q_oeV6eNTIAmbZBcHrjaJyAJaNjKcU78,123
1
+ atomicshop/__init__.py,sha256=qrVt_gSFBiv7h190pVz0_ywto9IuWrKhtjX_WOTD6pM,123
2
2
  atomicshop/_basics_temp.py,sha256=6cu2dd6r2dLrd1BRNcVDKTHlsHs_26Gpw8QS6v32lQ0,3699
3
3
  atomicshop/_create_pdf_demo.py,sha256=Yi-PGZuMg0RKvQmLqVeLIZYadqEZwUm-4A9JxBl_vYA,3713
4
4
  atomicshop/_patch_import.py,sha256=ENp55sKVJ0e6-4lBvZnpz9PQCt3Otbur7F6aXDlyje4,6334
@@ -27,7 +27,7 @@ atomicshop/on_exit.py,sha256=Wf1iy2e0b0Zu7oRxrct3VkLdQ_x9B32-z_JerKTt9Z0,5493
27
27
  atomicshop/pbtkmultifile_argparse.py,sha256=aEk8nhvoQVu-xyfZosK3ma17CwIgOjzO1erXXdjwtS4,4574
28
28
  atomicshop/permissions.py,sha256=P6tiUKV-Gw-c3ePEVsst9bqWaHJbB4ZlJB4xbDYVpEs,4436
29
29
  atomicshop/print_api.py,sha256=j0bZ9b2rFKCcr0TVx1ARraVKeEs6JaaSgIlBdndy1nI,11600
30
- atomicshop/process.py,sha256=U2gyRl0bw2138y-rOMABMVptRvAL81ZfX1JyfxJI_Oo,15973
30
+ atomicshop/process.py,sha256=PeLvyixXaCfftdUF3oMbohI1L4MdLtvQVDx2V1Tz_Rk,16662
31
31
  atomicshop/python_file_patcher.py,sha256=kd3rBWvTcosLEk-7TycNdfKW9fZbe161iVwmH4niUo0,5515
32
32
  atomicshop/python_functions.py,sha256=zJg4ogUwECxrDD7xdDN5JikIUctITM5lsyabr_ZNsRw,4435
33
33
  atomicshop/question_answer_engine.py,sha256=7nM6kGDSFjQNi87b87-kP9lYM0vTjBHn1rEQGNAfdGA,825
@@ -100,7 +100,7 @@ atomicshop/basics/multiprocesses.py,sha256=nSskxJSlEdalPM_Uf8cc9kAYYlVwYM1GonBLA
100
100
  atomicshop/basics/numbers.py,sha256=ESX0z_7o_ok3sOmCKAUBoZinATklgMy2v-4RndqXlVM,1837
101
101
  atomicshop/basics/package_module.py,sha256=fBd0uVgFce25ZCVtLq83iyowRlbwdWYFj_t4Ml7LU14,391
102
102
  atomicshop/basics/randoms.py,sha256=DmYLtnIhDK29tAQrGP1Nt-A-v8WC7WIEB8Edi-nk3N4,282
103
- atomicshop/basics/strings.py,sha256=T4MpEpwxqsiOSnXcwYkqMKB5okHiJfvUCO7t5kcRtBg,18316
103
+ atomicshop/basics/strings.py,sha256=YIE0q5mdpwPtGXsRgvn-HSvR3R6sU1EyBCWHQq3IlKs,21216
104
104
  atomicshop/basics/threads.py,sha256=xvgdDJdmgN0wmmARoZ-H7Kvl1GOcEbvgaeGL4M3Hcx8,2819
105
105
  atomicshop/basics/timeit_template.py,sha256=fYLrk-X_dhdVtnPU22tarrhhvlggeW6FdKCXM8zkX68,405
106
106
  atomicshop/basics/tracebacks.py,sha256=cNfh_oAwF55kSIdqtv3boHZQIoQI8TajxkTnwJwpweI,535
@@ -128,7 +128,7 @@ atomicshop/mitm/initialize_engines.py,sha256=YnXPK1UKrmULnfL4zLo2LOpKWq-aGKzc9p3
128
128
  atomicshop/mitm/initialize_mitm_server.py,sha256=j1yMUbHsnFh9l5rFiUgBQA0mRZqREOKviP0frRzYikM,14611
129
129
  atomicshop/mitm/message.py,sha256=u2U2f2SOHdBNU-6r1Ik2W14ai2EOwxUV4wVfGZA098k,1732
130
130
  atomicshop/mitm/shared_functions.py,sha256=PaK_sbnEA5zo9k2ktEOKLmvo-6wRUunxzSNRr41uXIQ,1924
131
- atomicshop/mitm/statistic_analyzer.py,sha256=4uCpibb5tLhHv2Sv4jqL8kCldjdRgI5W10Ia9D_LwMk,23750
131
+ atomicshop/mitm/statistic_analyzer.py,sha256=F7RjQXE9S8iZe-DF7_hLx6dl5fHUKi0wf10bFxhTL9M,23840
132
132
  atomicshop/mitm/engines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
133
133
  atomicshop/mitm/engines/create_module_template.py,sha256=tRjVSm1sD6FzML71Qbuwvita0qsusdFGm8NZLsZ-XMs,4853
134
134
  atomicshop/mitm/engines/create_module_template_example.py,sha256=X5xhvbV6-g9jU_bQVhf_crZmaH50LRWz3bS-faQ18ds,489
@@ -142,7 +142,7 @@ atomicshop/mitm/engines/__reference_general/recorder___reference_general.py,sha2
142
142
  atomicshop/mitm/engines/__reference_general/responder___reference_general.py,sha256=1AM49UaFTKA0AHw-k3SV3uH3QbG-o6ux0c-GoWkKNU0,6993
143
143
  atomicshop/mitm/statistic_analyzer_helper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
144
144
  atomicshop/mitm/statistic_analyzer_helper/analyzer_helper.py,sha256=pk6L1t1ea1kvlBoR9QEJptOmaX-mumhwLsP2GCKukbk,5920
145
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py,sha256=yi3-8xYZEpCIQiKYHrw4wt0T00CeCXwypY32wlXigLg,13542
145
+ atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py,sha256=bSjsrDhlwhDKTzK5QWPZPpCoqPE26Hv_DlKj65TperI,14006
146
146
  atomicshop/monitor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
147
147
  atomicshop/monitor/change_monitor.py,sha256=K5NlVp99XIDDPnQQMdru4BDmua_DtcDIhVAzkTOvD5s,7673
148
148
  atomicshop/monitor/checks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -181,7 +181,7 @@ atomicshop/wrappers/pipw.py,sha256=mu4jnHkSaYNfpBiLZKMZxEX_E2LqW5BVthMZkblPB_c,1
181
181
  atomicshop/wrappers/process_wrapper_pbtk.py,sha256=ycPmBRnv627RWks6N8OhxJQe8Gu3h3Vwj-4HswPOw0k,599
182
182
  atomicshop/wrappers/pycharmw.py,sha256=OHcaVlyhIqhgRioPhkeS5krDZ_NezZjpBCvyRiLjWwI,2723
183
183
  atomicshop/wrappers/pyopensslw.py,sha256=OBWxA6EJ2vU_Qlf4M8m6ilcG3hyYB4yB0EsXUf7NhEU,6804
184
- atomicshop/wrappers/sysmonw.py,sha256=impveU8MhbD2oizBoGLuj00wuyB8l_EiRRGIESdFESs,5359
184
+ atomicshop/wrappers/sysmonw.py,sha256=MFF8ts0gHbXn2_QeH196UncOUtm4MnM2cQBzTOnfrnk,5351
185
185
  atomicshop/wrappers/ubuntu_terminal.py,sha256=BBZD3EH6KSDORd5IZBZM-ti4U6Qh1sZwftx42s7hqB4,10917
186
186
  atomicshop/wrappers/wslw.py,sha256=AKphiHLSddL7ErevUowr3f9Y1AgGz_R3KZ3NssW07h8,6959
187
187
  atomicshop/wrappers/certauthw/certauth.py,sha256=hKedW0DOWlEigSNm8wu4SqHkCQsGJ1tJfH7s4nr3Bk0,12223
@@ -265,7 +265,7 @@ atomicshop/wrappers/psutilw/cpus.py,sha256=w6LPBMINqS-T_X8vzdYkLS2Wzuve28Ydp_Gaf
265
265
  atomicshop/wrappers/psutilw/disks.py,sha256=3ZSVoommKH1TWo37j_83frB-NqXF4Nf5q5mBCX8G4jE,9221
266
266
  atomicshop/wrappers/psutilw/memories.py,sha256=_S0aL8iaoIHebd1vOFrY_T9aROM5Jx2D5CvDh_4j0Vc,528
267
267
  atomicshop/wrappers/psutilw/networks.py,sha256=jC53QXKdZQPCLdy_iNWXeq-CwpW7H6va6bFPRmI_e7A,1507
268
- atomicshop/wrappers/psutilw/processes.py,sha256=XVqbr7btnT1aJioCFOBnudKGZ5-L9sTpBFWb4DHn2Rw,847
268
+ atomicshop/wrappers/psutilw/processes.py,sha256=ZOkqEEF36NFeQg7G-baUUdTaV8DQe_jpkJg42ASUuc0,1566
269
269
  atomicshop/wrappers/psutilw/psutilw.py,sha256=q3EwgprqyrR4zLCjl4l5DHFOQoukEvQMIPjNB504oQ0,21262
270
270
  atomicshop/wrappers/psycopgw/psycopgw.py,sha256=XJvVf0oAUjCHkrYfKeFuGCpfn0Oxj3u4SbKMKA1508E,7118
271
271
  atomicshop/wrappers/pywin32w/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -294,8 +294,8 @@ atomicshop/wrappers/socketw/socket_server_tester.py,sha256=AhpurHJmP2kgzHaUbq5ey
294
294
  atomicshop/wrappers/socketw/socket_wrapper.py,sha256=aXBwlEIJhFT0-c4i8iNlFx2It9VpCEpsv--5Oqcpxao,11624
295
295
  atomicshop/wrappers/socketw/ssl_base.py,sha256=k4V3gwkbq10MvOH4btU4onLX2GNOsSfUAdcHmL1rpVE,2274
296
296
  atomicshop/wrappers/socketw/statistics_csv.py,sha256=t3dtDEfN47CfYVi0CW6Kc2QHTEeZVyYhc57IYYh5nmA,826
297
- atomicshop-2.15.5.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
298
- atomicshop-2.15.5.dist-info/METADATA,sha256=4AbSJ0FMia7X1hepHPSyzyT8uzUPfZd96LkVe1br2Y4,10502
299
- atomicshop-2.15.5.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
300
- atomicshop-2.15.5.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
301
- atomicshop-2.15.5.dist-info/RECORD,,
297
+ atomicshop-2.15.7.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
298
+ atomicshop-2.15.7.dist-info/METADATA,sha256=kJydJd_Ttl27wkR6H-Nbc-BiTtOTgn9aK5Fc2TDwwW8,10502
299
+ atomicshop-2.15.7.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
300
+ atomicshop-2.15.7.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
301
+ atomicshop-2.15.7.dist-info/RECORD,,