docs_control_writer
trestle.core.docs_control_writer
¤
Handle writing of controls to markdown for docs purposes.
logger
¤
Classes¤
DocsControlWriter (ControlWriter)
¤
Class to write controls as markdown for docs purposes.
Source code in trestle/core/docs_control_writer.py
class DocsControlWriter(ControlWriter):
"""Class to write controls as markdown for docs purposes."""
def write_control_with_sections(
self,
control: cat.Control,
profile: prof.Profile,
group_title: str,
sections: List[str],
sections_dict: Optional[Dict[str, str]] = None,
label_column: bool = True,
add_group_to_title: bool = False
) -> str:
"""Write the control into markdown file with specified sections."""
self._md_file = MDWriter(None)
self._sections_dict = sections_dict if sections_dict else {}
tag_pattern = '{: #[.]}' # noqa: FS003 - not f string but tag
if not isinstance(group_title, str):
raise TrestleError(f'Group title must be provided and be a string, instead received: {group_title}')
for section in sections:
if 'statement' == section:
self._add_control_statement(control, group_title, add_group_to_title, tag_pattern=tag_pattern)
elif const.OBJECTIVE_PART == section:
self._add_control_objective(control, tag_pattern=tag_pattern)
elif 'table_of_parameters' == section:
self.get_param_table(
control, label_column, section_dict=sections_dict, tag_pattern=tag_pattern, md_file=self._md_file
)
else:
self._add_one_section(control, profile, section, tag_pattern=tag_pattern)
return '\n'.join(self._md_file._lines)
def get_control_statement_ssp(self, control: cat.Control) -> List[str]:
"""Get the control statement as formatted markdown from a control formatted for SSP."""
self._md_file = MDWriter(None)
self._add_control_statement_ssp(control)
return self._md_file.get_lines()
def get_param_table(
self,
control: cat.Control,
label_column: bool = False,
section_dict: Optional[Dict[str, str]] = None,
tag_pattern: str = None,
md_file: MDWriter = None
) -> List[str]:
"""Get parameters of a control as a markdown table for ssp_io, with optional third label column."""
def _get_displayname_if_exists(param_id: str) -> str:
for param in as_filtered_list(control.params, lambda p: p.id == param_id):
for prop in as_filtered_list(param.props, lambda p: p.name == const.DISPLAY_NAME):
return prop.value
return param_id
param_dict = ControlInterface.get_control_param_dict(control, False)
if param_dict:
if md_file:
self._md_file = md_file
else:
self._md_file = MDWriter(None)
header_title = 'Table of Parameters'
if section_dict:
header_title = section_dict.get(const.TABLE_OF_PARAMS_PART, 'Table of Parameters')
self._md_file.new_paragraph()
self._md_file.new_header(level=2, title=header_title, add_new_line_after_header=not tag_pattern)
if tag_pattern:
self._md_file.new_line(tag_pattern.replace('[.]', header_title.replace(' ', '-').lower()))
self._md_file.new_paragraph()
self._md_file.set_indent_level(-1)
if label_column:
self._md_file.new_table(
[
[
_get_displayname_if_exists(key),
ControlInterface.param_to_str(param_dict[key], ParameterRep.VALUE_OR_EMPTY_STRING),
ControlInterface.param_to_str(param_dict[key], ParameterRep.LABEL_OR_CHOICES, True),
] for key in param_dict.keys()
], ['Parameter ID', 'Values', 'Label or Choices']
)
else:
self._md_file.new_table(
[
[
_get_displayname_if_exists(key),
ControlInterface.param_to_str(param_dict[key], ParameterRep.VALUE_OR_LABEL_OR_CHOICES)
] for key in param_dict.keys()
], ['Parameter ID', 'Values']
)
self._md_file.set_indent_level(-1)
if tag_pattern:
bottom_tag_pattern = '{: #\"Parameters for [.]\" caption-side=\"top\"}' # noqa: FS003 - not f string
control_id = self._get_pretty_control_id_if_exists(control)
self._md_file.new_line(bottom_tag_pattern.replace('[.]', control_id))
self._md_file.new_paragraph()
return self._md_file.get_lines()
return []
def _add_control_statement(
self, control: cat.Control, group_title: str, print_group_title: bool = True, tag_pattern: str = None
) -> None:
"""Add the control statement and items to the md file."""
self._md_file.new_paragraph()
group_name = ''
control_title = control.title
if print_group_title:
group_name = ' \[' + group_title + '\]'
control_id = self._get_pretty_control_id_if_exists(control)
title = f'{control_id} -{group_name} {control_title}'
header_title = self._sections_dict.get(const.STATEMENT, 'Control Statement')
self._md_file.new_header(level=1, title=title, add_new_line_after_header=not tag_pattern)
if tag_pattern:
self._md_file.new_line(tag_pattern.replace('[.]', control.id))
self._md_file.new_paragraph()
self._md_file.new_header(level=2, title=header_title, add_new_line_after_header=not tag_pattern)
if tag_pattern:
self._md_file.new_line(tag_pattern.replace('[.]', header_title.replace(' ', '-').lower()))
self._md_file.new_paragraph()
self._md_file.set_indent_level(-1)
self._add_part_and_its_items(control, const.STATEMENT, const.ITEM)
self._md_file.set_indent_level(-1)
def _add_control_objective(self, control: cat.Control, tag_pattern: str = None) -> None:
if control.parts:
for part in control.parts:
if part.name == const.OBJECTIVE_PART:
self._md_file.new_paragraph()
heading_title = self._sections_dict.get(const.OBJECTIVE_PART, 'Control Objective')
self._md_file.new_header(level=2, title=heading_title, add_new_line_after_header=not tag_pattern)
if tag_pattern:
self._md_file.new_line(tag_pattern.replace('[.]', heading_title.replace(' ', '-').lower()))
self._md_file.new_paragraph()
self._md_file.set_indent_level(-1)
self._add_part_and_its_items(control, const.OBJECTIVE_PART, const.OBJECTIVE_PART)
self._md_file.set_indent_level(-1)
return
def _add_one_section(
self, control: cat.Control, profile: prof.Profile, section: str, tag_pattern: Optional[str] = None
) -> None:
"""Add specific control section."""
prose = ControlInterface.get_control_section_prose(control, section)
if prose:
section_title = self._sections_dict.get(section, section)
heading_title = f'{section_title}'
self._md_file.new_header(level=2, title=heading_title, add_new_line_after_header=not tag_pattern)
if tag_pattern:
self._md_file.new_line(tag_pattern.replace('[.]', heading_title.replace(' ', '-').lower()))
self._md_file.new_paragraph()
self._md_file.new_line(prose)
self._md_file.new_paragraph()
else:
# write parts and subparts if exist
part_infos = ControlInterface.get_all_add_info(control.id, profile)
for part_info in part_infos:
if part_info.name == section:
self._write_part_info(part_info, section, tag_pattern)
break
def _write_part_info(
self,
part_info: PartInfo,
section_name: str,
tag_pattern: Optional[str] = None,
section_prefix: str = '',
heading_level: int = 2
) -> None:
section_title = self._sections_dict.get(section_name, part_info.name)
heading_title = f'{section_title}'
tag_section_name = section_prefix + f'{section_title}'
tag_section_name = re.sub(const.MATCH_ALL_EXCEPT_LETTERS_UNDERSCORE_SPACE_REGEX, '', tag_section_name)
tag_section_name = tag_section_name.replace(' ', '-').replace('_', '-').lower()
self._md_file.new_header(level=heading_level, title=heading_title, add_new_line_after_header=not tag_pattern)
if tag_pattern:
self._md_file.new_line(tag_pattern.replace('[.]', tag_section_name))
self._md_file.new_paragraph()
prose = '' if part_info.prose is None else part_info.prose
self._md_file.new_line(prose)
self._md_file.new_paragraph()
for subpart_info in as_list(part_info.parts):
self._write_part_info(
subpart_info, subpart_info.name, tag_pattern, tag_section_name + '-', heading_level + 1
)
def _get_pretty_control_id_if_exists(self, control: cat.Control) -> str:
control_id = control.id.upper()
if control.props:
# Take control id from the properties
for prop in control.props:
if prop.name == 'label':
control_id = prop.value
break
return control_id
def _add_control_statement_ssp(self, control: cat.Control) -> None:
"""Add the control statement and items to the markdown SSP."""
self._md_file.new_paragraph()
label = ControlInterface.get_label(control)
label = label if label else control.id.upper()
title = f'{label} - {control.title}'
self._md_file.new_header(level=1, title=title)
self._md_file.new_header(level=2, title='Control Statement')
self._md_file.set_indent_level(-1)
self._add_part_and_its_items(control, const.STATEMENT, const.ITEM)
self._md_file.set_indent_level(-1)
Methods¤
get_control_statement_ssp(self, control)
¤
Get the control statement as formatted markdown from a control formatted for SSP.
Source code in trestle/core/docs_control_writer.py
def get_control_statement_ssp(self, control: cat.Control) -> List[str]:
"""Get the control statement as formatted markdown from a control formatted for SSP."""
self._md_file = MDWriter(None)
self._add_control_statement_ssp(control)
return self._md_file.get_lines()
get_param_table(self, control, label_column=False, section_dict=None, tag_pattern=None, md_file=None)
¤
Get parameters of a control as a markdown table for ssp_io, with optional third label column.
Source code in trestle/core/docs_control_writer.py
def get_param_table(
self,
control: cat.Control,
label_column: bool = False,
section_dict: Optional[Dict[str, str]] = None,
tag_pattern: str = None,
md_file: MDWriter = None
) -> List[str]:
"""Get parameters of a control as a markdown table for ssp_io, with optional third label column."""
def _get_displayname_if_exists(param_id: str) -> str:
for param in as_filtered_list(control.params, lambda p: p.id == param_id):
for prop in as_filtered_list(param.props, lambda p: p.name == const.DISPLAY_NAME):
return prop.value
return param_id
param_dict = ControlInterface.get_control_param_dict(control, False)
if param_dict:
if md_file:
self._md_file = md_file
else:
self._md_file = MDWriter(None)
header_title = 'Table of Parameters'
if section_dict:
header_title = section_dict.get(const.TABLE_OF_PARAMS_PART, 'Table of Parameters')
self._md_file.new_paragraph()
self._md_file.new_header(level=2, title=header_title, add_new_line_after_header=not tag_pattern)
if tag_pattern:
self._md_file.new_line(tag_pattern.replace('[.]', header_title.replace(' ', '-').lower()))
self._md_file.new_paragraph()
self._md_file.set_indent_level(-1)
if label_column:
self._md_file.new_table(
[
[
_get_displayname_if_exists(key),
ControlInterface.param_to_str(param_dict[key], ParameterRep.VALUE_OR_EMPTY_STRING),
ControlInterface.param_to_str(param_dict[key], ParameterRep.LABEL_OR_CHOICES, True),
] for key in param_dict.keys()
], ['Parameter ID', 'Values', 'Label or Choices']
)
else:
self._md_file.new_table(
[
[
_get_displayname_if_exists(key),
ControlInterface.param_to_str(param_dict[key], ParameterRep.VALUE_OR_LABEL_OR_CHOICES)
] for key in param_dict.keys()
], ['Parameter ID', 'Values']
)
self._md_file.set_indent_level(-1)
if tag_pattern:
bottom_tag_pattern = '{: #\"Parameters for [.]\" caption-side=\"top\"}' # noqa: FS003 - not f string
control_id = self._get_pretty_control_id_if_exists(control)
self._md_file.new_line(bottom_tag_pattern.replace('[.]', control_id))
self._md_file.new_paragraph()
return self._md_file.get_lines()
return []
write_control_with_sections(self, control, profile, group_title, sections, sections_dict=None, label_column=True, add_group_to_title=False)
¤
Write the control into markdown file with specified sections.
Source code in trestle/core/docs_control_writer.py
def write_control_with_sections(
self,
control: cat.Control,
profile: prof.Profile,
group_title: str,
sections: List[str],
sections_dict: Optional[Dict[str, str]] = None,
label_column: bool = True,
add_group_to_title: bool = False
) -> str:
"""Write the control into markdown file with specified sections."""
self._md_file = MDWriter(None)
self._sections_dict = sections_dict if sections_dict else {}
tag_pattern = '{: #[.]}' # noqa: FS003 - not f string but tag
if not isinstance(group_title, str):
raise TrestleError(f'Group title must be provided and be a string, instead received: {group_title}')
for section in sections:
if 'statement' == section:
self._add_control_statement(control, group_title, add_group_to_title, tag_pattern=tag_pattern)
elif const.OBJECTIVE_PART == section:
self._add_control_objective(control, tag_pattern=tag_pattern)
elif 'table_of_parameters' == section:
self.get_param_table(
control, label_column, section_dict=sections_dict, tag_pattern=tag_pattern, md_file=self._md_file
)
else:
self._add_one_section(control, profile, section, tag_pattern=tag_pattern)
return '\n'.join(self._md_file._lines)
handler: python