"""Helpers for project-level INI organization and index lists."""
import configparser
import os
import platform
import psutil
import sys
from coseda.logging_utils import log_print, log_start, shoutout
from coseda.io import handle_input, is_parent_config_file, read_config, config_to_paths
[docs]
def find_configfiles(input_path):
"""Return child INIs (excluding parent configs) resolved from file/folder/list input."""
configfiles, _ = handle_input(input_path)
return configfiles
[docs]
def resume_processing(input_path):
"""Refresh stored configfile paths in INIs and log the environment header."""
configfiles, _ = handle_input(input_path)
for configfile in configfiles:
shoutout(configfile)
config = read_config(configfile)
config.set('Paths', 'configfile_path', configfile)
with open(configfile, 'w', encoding='utf-8') as cfgfile:
config.write(cfgfile)
_, _, _, logfile_path, _, _ = config_to_paths(configfile)
log_start(logfile_path, 'resuming processing, location of .ini file updated')
log_start(
logfile_path,
f'system: {platform.system()} {platform.architecture()}, version {platform.version()}'
)
log_start(
logfile_path,
f'hardware: {psutil.cpu_count(logical=False)} physical cores, '
f'{psutil.cpu_count(logical=True)} logical cores, '
f'{round(psutil.virtual_memory().total / (1024 ** 3))} GB total memory'
)
log_start(logfile_path, f'python version {sys.version}')
[docs]
def get_parent_ini_file(input_path):
"""
Accepts an input path which is either a parent INI file or a folder containing one parent INI file.
Returns the path to the parent INI file.
"""
if os.path.isfile(input_path):
# Input is a file
if input_path.endswith('.ini'):
if is_parent_config_file(input_path):
return input_path
raise ValueError(f"The file '{input_path}' is not a parent INI file.")
raise ValueError(f"The file '{input_path}' is not an INI file.")
if os.path.isdir(input_path):
# Input is a directory
ini_files = [os.path.join(input_path, f) for f in os.listdir(input_path) if f.endswith('.ini')]
parent_ini_files = [f for f in ini_files if is_parent_config_file(f)]
if len(parent_ini_files) == 1:
return parent_ini_files[0]
if len(parent_ini_files) == 0:
raise ValueError(f"No parent INI file found in the directory '{input_path}'.")
raise ValueError(
f"Multiple parent INI files found in the directory '{input_path}'. Please specify one."
)
raise ValueError(f"The input path '{input_path}' is neither a file nor a directory.")
[docs]
def write_index_file(input_path):
"""Build a CrystFEL-compatible .list file from all HDF5 paths in the inputs."""
configfiles, _ = handle_input(input_path)
log_print(input_path)
h5list = []
for configfile in configfiles:
shoutout(configfile)
config = read_config(configfile)
outputfolder, outputfolder_path, logfile, logfile_path, h5file, h5file_path = config_to_paths(configfile)
h5list.append(h5file_path)
outputpath = os.path.join(outputfolder_path, 'template.list')
with open(outputpath, 'w') as file:
for h5 in h5list:
file.write(h5 + '\n')
[docs]
def generate_parent_ini_file(folder_path, parent_ini_filename=None):
# Determine the default parent INI filename if not provided
if parent_ini_filename is None:
# Get the absolute path to handle cases where folder_path ends with '/'
folder_path = os.path.abspath(folder_path)
# Get the name of the parent folder
parent_folder_name = os.path.basename(folder_path)
# Construct the parent INI filename
parent_ini_filename = f"{parent_folder_name}.ini"
# Use the handle_input function to get all non-parent INI files
configfiles, _ = handle_input(folder_path)
if not configfiles:
message = 'No valid subset INI files found to include in the parent INI file.'
log_start(None, message)
return None
# Create the parent INI file
parent_config = configparser.ConfigParser()
# Add the General section with config_type = parent
parent_config['General'] = {'config_type': 'parent'}
# Add the Subsets section
parent_config['Subsets'] = {}
# Add each child INI file under the Subsets section
parent_ini_path = os.path.join(folder_path, parent_ini_filename)
parent_dir = os.path.dirname(parent_ini_path)
for idx, cfg in enumerate(configfiles, start=1):
# Get the relative path to the child INI file
relative_path = os.path.relpath(cfg, parent_dir)
key_name = f'subset{idx}'
parent_config['Subsets'][key_name] = relative_path
# Add the Paths section
parent_config['Paths'] = {}
# Set configfile_path to the absolute path of the parent INI file
parent_config['Paths']['configfile_path'] = parent_ini_path
# Set logfile to have the same name as the INI file but with .log extension
log_filename = os.path.splitext(parent_ini_filename)[0] + '.log'
parent_config['Paths']['logfile'] = log_filename
# Set framepath (assuming it's a fixed value)
parent_config['Paths']['framepath'] = 'entry/data/images'
# Write the parent INI file
with open(parent_ini_path, 'w') as parent_ini_file:
parent_config.write(parent_ini_file)
# Construct the log file path
log_file_path = os.path.join(folder_path, log_filename)
# Use log_start to create the log file and write the initial message
if not os.path.exists(log_file_path):
initial_message = f"Log file created for {parent_ini_filename}"
log_start(log_file_path, initial_message)
else:
message = f"Log file '{log_filename}' already exists."
log_start(log_file_path, message)
# Log the creation of the parent INI file
message = f"Parent INI file generated at: {parent_ini_path}"
log_start(log_file_path, message)
# Log the list of child INI files included
child_files = ', '.join([os.path.basename(cfg) for cfg in configfiles])
message = f"Subset INI files included: {child_files}"
log_start(log_file_path, message)
return parent_ini_path