#!/bin/python3 import sys import os # magic values NO = 0 AUTO = 1 YES = 2 def convert_generic_v1(data): split_raw_lines = [i.strip().split("|") for i in data.split("\n") if i] return generate_table(split_raw_lines) def convert_usecase_v1(filename, data): split_raw_lines_from_file = [i.split(" | ") for i in data.split("\n") if i] if '/' in filename: use_case_name = filename.rsplit("/", 1)[1].upper() elif '\\' in filename: use_case_name = filename.rsplit("\\", 1)[1].upper() else: use_case_name = filename.upper() split_raw_lines = [["ID", use_case_name]] + split_raw_lines_from_file merged_lines = [] for i in split_raw_lines: if i[0].replace(" ", "") == "": merged_lines[-1][1] += "
" + i[1] else: merged_lines.append(i) return generate_table(merged_lines) def convert_activity_v1(filename, data): split_raw_lines_from_file = [i.split(" | ") for i in data.split("\n") if i] if '/' in filename: use_case_name = filename.rsplit("/", 1)[1].upper() elif '\\' in filename: use_case_name = filename.rsplit("\\", 1)[1].upper() else: use_case_name = filename.upper() split_raw_lines = [["ID", use_case_name]] + split_raw_lines_from_file merged_lines = [] for i in split_raw_lines: if i[0].replace(" ", "") == "": merged_lines[-1][1] += "
" + i[1] else: merged_lines.append(i) return generate_table_with_activity_diagram(merged_lines) def generate_table(raw_table_data): table_lines = raw_table_data transposed_table = list(zip(*table_lines)) field_sizes = [max([max([len(k)+2 for k in j.split("\n")]) for j in i]) for i in transposed_table] formatted_table_lines = [] l = "|" for i, field in enumerate(table_lines[0]): l += field.center(field_sizes[i]) l += "|" formatted_table_lines.append(l) formatted_table_lines.append(f"|{'|'.join([':'+'-'*(i-2)+':' for i in field_sizes])}|") for line in table_lines[1:]: l = "|" for i, field in enumerate(line): l += field.center(field_sizes[i]) l += "|" formatted_table_lines.append(l) return "\n".join(formatted_table_lines) def convert_line_to_activity_diagram(line): split_line = line[1].split("
") result = "@startuml\n %PLACEHOLDER%\nstop\n@enduml" last_swimline_name = "" initiator_name = "" for i in split_line: clear_line = i.lstrip("1234567890. ") swimline_label, combined_action = clear_line.split(" ", 1) action_and_exceptions = combined_action.split(" (") if len(action_and_exceptions) > 1: action = action_and_exceptions[0].strip().capitalize() exceptions = action_and_exceptions[1].strip(") ").capitalize() else: action = action_and_exceptions[0].strip().capitalize() exceptions = "" # запам'ятовуємо назву користувача системи # (він завжди починає взаємодію, а, отже, # перша дія завжди належить йому) if not initiator_name: initiator_name = swimline_label # змінюємо swinline, якщо керування перейшло до іншого актора if last_swimline_name != swimline_label: result = result.replace("%PLACEHOLDER%", f"|{swimline_label}|\n %PLACEHOLDER%") # якщо це перша дія, то вказуємо start if not last_swimline_name: result = result.replace("%PLACEHOLDER%", f"start\n %PLACEHOLDER%") # зберігаємо нове ім'я актора last_swimline_name = swimline_label # прописуємо поточну дію актора result = result.replace("%PLACEHOLDER%", f": {action};\n %PLACEHOLDER%") # якщо є виключні ситуації, додаємо інформацію про них if exceptions: result = result.replace("%PLACEHOLDER%", f"note right #lightpink\n {exceptions}\n end note\n %PLACEHOLDER%") # впенюємося, що взаємодія закінчується на swinline користувача if last_swimline_name != initiator_name: result = result.replace("%PLACEHOLDER%", f"|{initiator_name}|\n %PLACEHOLDER%") # видаляємо мітку %PLACEHOLDER% result = result.replace("%PLACEHOLDER%\n", "") return result def generate_table_with_activity_diagram(raw_table_data): table_lines = raw_table_data[:-1] activity_line = raw_table_data[-1] transposed_table = list(zip(*table_lines)) field_sizes = [max([max([len(k)+2 for k in j.split("\n")]) for j in i]) for i in transposed_table] formatted_table_lines = [] l = "|" for i, field in enumerate(table_lines[0]): l += field.center(field_sizes[i]) l += "|" formatted_table_lines.append(l) formatted_table_lines.append(f"|{'|'.join([':'+'-'*(i-2)+':' for i in field_sizes])}|") for line in table_lines[1:]: l = "|" for i, field in enumerate(line): l += field.center(field_sizes[i]) l += "|" formatted_table_lines.append(l) activity_diagram = convert_line_to_activity_diagram(activity_line) return "\n".join(formatted_table_lines) + "\n" + activity_diagram if __name__=="__main__": # parse args files = [] write_to_file = AUTO write_to_stdout = AUTO usecase_formatting = AUTO verbose = AUTO process_table_files = AUTO convert_to_activity_diagram = AUTO file_output_path = None # 1 pass (argument harvest) for n, i in enumerate(sys.argv[1:]): if i.startswith("-"): # записувати таблицю в файл if i in ["-f", "--file"]: write_to_file = YES elif i in ["-nf", "--no-file"]: write_to_file = NO # виводити таблицю у стандартний вивід elif i in ["-o", "--stdout"]: write_to_stdout = YES elif i in ["-no", "--no-stdout"]: write_to_stdout = NO # форматувати таблицю як use-case elif i in ["-u", "--usecase", "--use-case"]: usecase_formatting = YES elif i in ["-nu", "--no-usecase", "--no-use-case"]: usecase_formatting = NO # виводити на екран додаткову інформацію elif i in ["-v", "--verbose"]: verbose = YES elif i in ["-nv", "--no-verbose"]: verbose = NO # дозволити обробку файлів .table elif i in ["-t", "--process-table"]: process_table_files = YES elif i in ["-nt", "--no-process-table"]: process_table_files = NO # перетворює останню клітинку таблиці в діаграму активностей if i in ["-a", "--convert-to-activity-diagram"]: convert_to_activity_diagram = YES elif i in ["-na", "--no-convert-to-activity-diagram"]: convert_to_activity_diagram = NO # задає папку, в яку необхідно зберігати конвертовані таблиці elif i in ["-d", "--destination"]: file_output_path = sys.argv[n+2] sys.argv.remove(sys.argv[n+2]) # 2 pass (filename harvest) for i in sys.argv[1:]: if not i.startswith("-"): if i.endswith(".table") and process_table_files <= AUTO: print(f"[Warning]: Excluding {i} to prevent processing of an already processed file (pass --process-table to override this behaviour)") else: files.append(i) if len(files) < 1: print("You need to pass at least one file as CLI argument", file=sys.stderr) print("Exiting...", file=sys.stderr) exit(1) if len(files) == 1: name = files[0] data = open(name, encoding = "utf-8").read() if usecase_formatting == YES: if verbose == YES: print(f"Force-formatting {name} as a use-case\n") formatted_table_data = convert_usecase_v1(name.rsplit(".", 1)[0], data) elif (name.endswith(".usecase") or name.endswith(".uc")) and usecase_formatting >= AUTO: if verbose == YES: print(f"Auto-detected use-case in file {name}\n") if convert_to_activity_diagram == YES: if verbose == YES: print(f"Converting file {name} to activity diagram\n") formatted_table_data = convert_activity_v1(name.rsplit(".", 1)[0], data) else: formatted_table_data = convert_usecase_v1(name.rsplit(".", 1)[0], data) else: formatted_table_data = convert_generic_v1(data) if write_to_stdout >= AUTO: print(formatted_table_data) if write_to_file > AUTO: if file_output_path: open(os.path.join(file_output_path, os.path.basename(name)) \ + ".table", 'w', encoding = "utf-8") \ .write(formatted_table_data+"\n") else: open(name + ".table", 'w', encoding = "utf-8").write(formatted_table_data+"\n") exit(0) for no, name in enumerate(files): print(f"Converting {no+1:02d}/{len(files)}: {name}") data = open(name, encoding = "utf-8").read() if usecase_formatting == YES: if verbose >= AUTO: print(f"Force-formatting {name} as a use-case") formatted_table_data = convert_usecase_v1(name.rsplit(".", 1)[0], data) elif (name.endswith(".usecase") or name.endswith(".uc")) and usecase_formatting >= AUTO: if verbose >= AUTO: print(f"Auto-detected use-case in file {name}") if convert_to_activity_diagram == YES: if verbose == YES: print(f"Converting file {name} to activity diagram\n") formatted_table_data = convert_activity_v1(name.rsplit(".", 1)[0], data) else: formatted_table_data = convert_usecase_v1(name.rsplit(".", 1)[0], data) else: formatted_table_data = convert_generic_v1(data) if write_to_stdout > AUTO: print(formatted_table_data) if write_to_file >= AUTO: if file_output_path: open(os.path.join(file_output_path, os.path.basename(name)) \ + ".table", 'w', encoding = "utf-8") \ .write(formatted_table_data+"\n") else: open(name + ".table", 'w', encoding = "utf-8").write(formatted_table_data+"\n")