#!/usr/bin/env python3 """ Generate bicorder.txt from bicorder.json """ import json import argparse import sys def center_text(text, width): """Center text within a given width""" return text.center(width) def format_gradient_bar(value): """ Format the gradient bar based on value. If value is None, show all bars: [|||||||||] If value is 0-8, replace that position with #: [|#||||||||] """ if value is None: return "[|||||||||]" # Ensure value is in valid range if not isinstance(value, int) or value < 0 or value > 8: return "[|||||||||]" bars = list("|||||||||") bars[value] = "#" return "[" + "".join(bars) + "]" def format_gradient_line(term_left, term_right, value, left_width, right_width, center_width): """ Format a gradient line with proper spacing. Example: " explicit < [|||||||||] > implicit " """ bar = format_gradient_bar(value) # Right-align the left term, add the bar, then left-align the right term line = f"{term_left.rjust(left_width)} < {bar} > {term_right.ljust(right_width)}" return center_text(line, center_width) def format_metadata_field(field_value, field_name): """ Format metadata field - show value if provided, otherwise show field name in brackets """ if field_value is None or field_value == "": return f"[{field_name}]" return str(field_value) def generate_bicorder_text(json_data): """Generate the formatted bicorder text from JSON data""" lines = [] # First pass: calculate maximum widths for left and right terms max_left_width = 0 max_right_width = 0 # Check diagnostic gradients for diagnostic_set in json_data.get("diagnostic", []): for gradient in diagnostic_set.get("gradients", []): term_left = gradient.get("term_left", "") term_right = gradient.get("term_right", "") max_left_width = max(max_left_width, len(term_left)) max_right_width = max(max_right_width, len(term_right)) # Check analysis items for analysis_item in json_data.get("analysis", []): term_left = analysis_item.get("term_left", "") term_right = analysis_item.get("term_right", "") max_left_width = max(max_left_width, len(term_left)) max_right_width = max(max_right_width, len(term_right)) # Calculate the width needed for centering # Gradient line format: "{left_term} < [|||||||||] > {right_term}" # That's: max_left_width + 3 + 11 + 3 + max_right_width gradient_line_width = max_left_width + max_right_width + 17 # Also check metadata and headers metadata = json_data.get("metadata", {}) max_text_width = max( len("Protocol"), len("BICORDER"), len(format_metadata_field(metadata.get("protocol"), "Protocol")), len(format_metadata_field(metadata.get("analyst"), "Analyst")), len(format_metadata_field(metadata.get("standpoint"), "Standpoint")), len(format_metadata_field(metadata.get("timestamp"), "Timestamp")), len("ANALYSIS") ) # Check diagnostic set names for diagnostic_set in json_data.get("diagnostic", []): set_name = diagnostic_set.get("set_name", "").upper() max_text_width = max(max_text_width, len(set_name)) # Use the maximum of gradient line width and text width center_width = max(gradient_line_width, max_text_width) # Header lines.append(center_text("Protocol", center_width)) lines.append(center_text("BICORDER", center_width)) lines.append("") # Metadata section lines.append(center_text(format_metadata_field(metadata.get("protocol"), "Protocol"), center_width)) lines.append(center_text(format_metadata_field(metadata.get("analyst"), "Analyst"), center_width)) lines.append(center_text(format_metadata_field(metadata.get("standpoint"), "Standpoint"), center_width)) lines.append(center_text(format_metadata_field(metadata.get("timestamp"), "Timestamp"), center_width)) lines.append("") # Diagnostic sections for diagnostic_set in json_data.get("diagnostic", []): set_name = diagnostic_set.get("set_name", "").upper() lines.append(center_text(set_name, center_width)) for gradient in diagnostic_set.get("gradients", []): term_left = gradient.get("term_left", "") term_right = gradient.get("term_right", "") value = gradient.get("value") lines.append(format_gradient_line(term_left, term_right, value, max_left_width, max_right_width, center_width)) lines.append("") # Analysis section lines.append(center_text("ANALYSIS", center_width)) for analysis_item in json_data.get("analysis", []): term_left = analysis_item.get("term_left", "") term_right = analysis_item.get("term_right", "") value = analysis_item.get("value") lines.append(format_gradient_line(term_left, term_right, value, max_left_width, max_right_width, center_width)) lines.append("") return "\n".join(lines) def main(): """Main function to read JSON and generate text output""" # Set up argument parser parser = argparse.ArgumentParser( description="Generate formatted bicorder text from JSON input" ) parser.add_argument( "input_json", help="Path to input JSON file" ) parser.add_argument( "output_txt", help="Path to output TXT file" ) args = parser.parse_args() # Read the JSON file try: with open(args.input_json, "r") as f: data = json.load(f) except FileNotFoundError: print(f"Error: Input file '{args.input_json}' not found.", file=sys.stderr) sys.exit(1) except json.JSONDecodeError as e: print(f"Error: Invalid JSON in '{args.input_json}': {e}", file=sys.stderr) sys.exit(1) # Generate the formatted text output = generate_bicorder_text(data) # Write to output file try: with open(args.output_txt, "w") as f: f.write(output) print(f"Successfully generated '{args.output_txt}'") except IOError as e: print(f"Error: Could not write to '{args.output_txt}': {e}", file=sys.stderr) sys.exit(1) if __name__ == "__main__": main()