# Valispace Script for Custom Actions # Purpose: Fetch all Requirements Texts from a project and compile them into a Report in the Analysis Module # Developed by Paul Grey # Date: 26/11/2023 #IMPORTANT: Provide your valispace url in the Configuration Section below from typing import Any, Dict from valispace import API from datetime import datetime # --------------------------------------------- # Configuration Section VALISPACE = { "domain": "https://YOUR_DEPLOYMENT_NAME.valispace.com", # Base URL for the Valispace instance "warn_https": False, # Disable HTTPS warnings if set to False } # --------------------------------------------- def initialize_api(temporary_access_token) -> API: """ Initialize the Valispace API. Returns: API: Initialized Valispace API object. """ return API( url=VALISPACE["domain"], session_token=temporary_access_token, warn_https=VALISPACE.get("warn_https", False) ) def fetch_specification_name(spec_id, api): """ Fetch the name of a specification given its ID. Args: spec_id (int): Specification ID. api (API): Initialized Valispace API object. Returns: str: Name of the specification. """ spec = api.request('GET', f'requirements/specifications/{spec_id}/') return spec['name'] def generate_analysis_procedure_folder(api: API, project_id): folder_name = 'ValiAssistant Reports' folders = api.request('GET', f'documents/folders/?project={project_id}') for folder in folders: if folder['name'] == folder_name: return folder['id'] else: continue response = api.request('POST', 'documents/folders/', {"project": project_id, "name": "ValiAssistant Reports"}) return response['id'] def create_analysis_report(api, project_id, report_text, specs, custum_action_objects_ids, folder_id) -> Dict[str, Any]: """ Create a new Analysis in the Analysis Module and add a text block with the report text. Args: api (API): Initialized Valispace API object. project_id (int): Project ID. report_text (str): Text to be included in the report. Returns: Dict[str, Any]: Response from the API after creating the report. """ #Get current Date and Time current_datetime = datetime.now().strftime("%Y-%m-%d %H:%M:%S") # Create a new Analysis analysis = api.request('POST', 'analyses/', { "project": project_id, "folder": folder_id, "name": "ValiAssistant Inconsistency Report " + current_datetime }) # Fetch Specification Names spec_names = [fetch_specification_name(spec_id, api) for spec_id in specs] report_title = "Inconsistencies from Specification(s): " + ", ".join(spec_names) # Create a Text Block for the Report Title title_column = api.request('POST', 'analyses/columns/', { "block_type": 10, "block_data": {"style": "h1", "target_analysis_id": analysis["id"]}, "position": 5, # Adjust position as needed "new_row": {"position": 20, "analysis": analysis["id"]} # Adjust position as needed }) # Fetch the Title Block ID title_block_id = api.request('GET', f'analyses/blocks/{title_column["id"]}/')['content_object']['id'] # Update the Title Text Field api.request('PATCH', f'analyses/blocks/text/{title_block_id}/', { "text": report_title }) # Create a Text Block for the Report Title title_column = api.request('POST', 'analyses/columns/', { "block_type": 10, "block_data": {"style": "h2", "target_analysis_id": analysis["id"]}, "position": 10, # Adjust position as needed "new_row": {"position": 40, "analysis": analysis["id"]} # Adjust position as needed }) # Fetch the Title Block ID title_block_id = api.request('GET', f'analyses/blocks/{title_column["id"]}/')['content_object']['id'] # Update the Title Text Field api.request('PATCH', f'analyses/blocks/text/{title_block_id}/', { "text": 'Analyzed Requirements' }) # Create a new Text Block with Req Identifiers column = api.request('POST', 'analyses/columns/', { "block_type": 10, "block_data": {"style": "p", "target_analysis_id": analysis["id"]}, "position": 15, "new_row": {"position": 60, "analysis": analysis["id"]} }) # Fetch the Block ID block_id = api.request('GET', f'analyses/blocks/{column["id"]}/')['content_object']['id'] # Update the Text Field analyzed_reqs = "" for custum_action_objects_id in custum_action_objects_ids: identifier, text = fetch_requirement_details(custum_action_objects_id, api) analyzed_reqs += identifier + ', ' update_response = api.request('PATCH', f'analyses/blocks/text/{block_id}/', { "text": analyzed_reqs }) # Create a new Text Block column = api.request('POST', 'analyses/columns/', { "block_type": 10, "block_data": {"style": "p", "target_analysis_id": analysis["id"]}, "position": 20, "new_row": {"position": 80, "analysis": analysis["id"]} }) # Fetch the Block ID block_id = api.request('GET', f'analyses/blocks/{column["id"]}/')['content_object']['id'] # Update the Text Field update_response = api.request('PATCH', f'analyses/blocks/text/{block_id}/', { "text": report_text }) return update_response def fetch_requirement_details(req_id, api): req = api.request('GET', f'requirements/{req_id}/') identifier = req['identifier'] text = req['text'] return identifier, text def convert_json_to_html_table(find_inconsistency_result, api) -> str: """ Convert JSON data from find_inconsistency_result into an HTML table. Args: find_inconsistency_result (dict): JSON data with inconsistency results. Returns: str: HTML table string. """ html_table = "" # Add table headers html_table += ("" "" "" "") # Process each result and add to the table for result in find_inconsistency_result["results"]: # Process each requirement ID req_details = [] for req_id in result.get("affected_ids", []): identifier, text = fetch_requirement_details(req_id, api) req_details.append(f"{identifier} - {text}") # Join multiple requirements with a newline req_details_str = "
".join(req_details) observation = result.get("observation", "") probability = result.get("probability", "") type_ = result.get("type", "") html_table += ("" "" "" "".format( req_details_str, observation, probability, type_)) html_table += "

Requirement Identifier and Text

Observation

Probability

Type

{}

{}

{}

{}

" return html_table def main(**kwargs) -> Dict[str, Any]: """ Main function to execute the script. Args: **kwargs: Additional arguments passed to the script. Returns: Dict[str, Any]: Result data to be sent back to Valispace. """ api = initialize_api(kwargs['temporary_access_token']) # Get the Inconsistencies custum_action_objects_ids = kwargs.get("objects_ids", []) objects_ids = {"requirements": custum_action_objects_ids,"specifications":[],"sections":[],"service_type":1} # Define the project ID based on selected Requirements Location project_id = api.request('GET', f'requirements/{custum_action_objects_ids[0]}')['project'] if not objects_ids: return {"error": "No object IDs provided."} print(objects_ids) print("Find Inconsistencies Starts") find_inconsistencies_response = api.request('PUT', 'vali-assistant/inconsistencies/', objects_ids) print(find_inconsistencies_response) # Format JSON into HTML table print("JSON to HTML conversion starts") html_table = convert_json_to_html_table(find_inconsistencies_response, api) # Extract specifications from requirements spec_ids = set() for req_id in custum_action_objects_ids: req = api.request('GET', f'requirements/{req_id}/') spec_ids.add(req['specification']) # Get Folder id or Create Folder for ValiAssistant Reports folder_id = generate_analysis_procedure_folder(api, project_id) # Create a report in the Analysis Module response = create_analysis_report(api, project_id, html_table, list(spec_ids), custum_action_objects_ids, folder_id) return { "result": "Report created successfully.", "response": response } if __name__=='__main__': main()