Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
# Class parser_v7 - Carries over functionality from previous parser iteration
# with several differences:
# 1. Parser converted to class object
# 2. Parsing and HLR/Truth table components segregated
# 3. Additional robustness added to parser
# Conversion by Austin Deschenes
# Uses library document to create list of keywords/phrases. Takes separate document
# for analysis. Document is scanned for keywords in both text and tables - partial
# (up to MATCH_THRESHOLD) and exact matches are searched for. Matches are added to
# log which is printed to console. Additionally, a document identical to the one
# being analyzed is created with exact matches highlighted in green and partial
# matches highlighted in red
# Other stuff (astha):
# added hlr verification code
# new regex to handle different HLR formats
# cleaner xlsx formatting
(4/29)
# major bug fixes!
# can handle interpolating [<>=] conditions
# won't break on newline in HLR
# handles single conditions
# added parts lib handling!!
# Imports #
from __future__ import (
absolute_import, division, print_function, unicode_literals
)
from docx import Document
from docx.document import Document as _Document
from docx.oxml.text.paragraph import CT_P
from docx.oxml.table import CT_Tbl
from docx.table import _Cell, Table
from docx.text.paragraph import Paragraph
from docx.shared import RGBColor
from difflib import SequenceMatcher
import re
import enchant
import os
import string
import shlex
from Tkinter import *
from openpyxl import load_workbook
from openpyxl.workbook import Workbook
from openpyxl.worksheet import Worksheet
from openpyxl.styles import Color, PatternFill, Font, Border
from openpyxl.styles import colors
from openpyxl.cell import Cell
# End of Imports #
MATCH_THRESHOLD = 0.9
PARTIAL_MATCH = 'Partial Match'
EXACT_MATCH = 'Exact Match'
BAD_SPELL = 'Bad Spell'
NEWLINE = '\n'
COMP_OPS = {'>': '<=', '<': '>=', '<=': '>', '>=': '<', '=': '!=', '!=': '='}
BOOL_VALS = {'TRUE': '1', 'FALSE': '0'}
BOOL_OPS = {'TRUE': '0', 'FALSE': '1'}
ASCII_CHARS = list(string.digits) + list(string.letters) + [' ', '"', '_', '\t']
# ADD NEW CATEGORIES HERE
PARTS_LIBRARY_TYPES = ['Boolean Hold', 'Timer']
class Parser:
"""Provides functionality for SRS parsing, HLR grabbing/verification, and truth table creation"""
def __init__(self, gui, doc_path, rev_path, hlr_path):
# Set up class instance vars #
self.gui = gui
self.doc_path = doc_path
#self.doc_path = 'C:\\Users\\vitaFrui\\Downloads\\SRS-sample_v3.docx'
self.rev_path = rev_path + '\\' + doc_path.split('\\')[-1].split('.')[0] + '_revision.docx'
#self.rev_path = 'C:\\Users\\Public\\Desktop\\test.docx'
self.hlr_path = hlr_path
self.doc = Document(self.doc_path)
self.dict = enchant.Dict("en_US")
self.doc_text = None
self.doc_hlr = None
self.doc_hlr_parsed = None
# self.doc_library = []
self.doc_library = None
self.hlr_keywords = ['any', 'all', 'otherwise', 'met', 'greater', 'less', 'equal', 'TRUE', 'True', 'true', 'FALSE', 'False', 'false']
# dictionary of Parts Lib category : [keywords]
self.parts_lib_keywords = {
'Boolean Hold' : ['SET', 'RESET', 'OUT'],
'Timer' : ['OUT', 'SET', 'LATCH_TIME'],
}
def parse_doc(self):
"""
Parses the document given to the parser at creation time - builds variable library if one does not exist,
checks for variable matches in text and highlights them accordingly; also locates likely spelling errors
:return:
"""
if self.doc_library is None:
self.library_parse()
self.doc_text = ''
for block in self.iter_block_items():
if isinstance(block, Paragraph):
self.doc_text = self.doc_text + block.text + '\n'
self.parse_paragraph(block)
elif isinstance(block, Table):
for row in block.rows:
for cell in row.cells:
for paragraph in cell.paragraphs:
self.doc_text = self.doc_text + paragraph.text + '\n'
self.parse_paragraph(paragraph)
self.doc.save(self.rev_path)
self.gui_print("SRS verified. Please revise and correct any marked errors:\n\n", '')
def parse_paragraph(self, my_paragraph):
"""
Parses an xml paragraph element from parent .docx file to highlight vars and spelling errors
:param my_paragraph: xml paragraph element to parse for variables and textual errors
:return: nothing
"""
my_text = my_paragraph.text
my_text_tokens = self.get_tokens(my_text)
my_paragraph.clear()
match_found = False
exact_match_found = False
good_spell = True
for token in my_text_tokens:
# check spelling (only considered if no match found) of normal words (just letter, not all caps)
if token.isupper() == False and bool(re.search(r'\d', token)) == False:
good_spell = self.dict.check(token)
for keyword in self.doc_library.keys():
# if keyword == token:
if keyword.strip().lower() == token.strip().lower(): # handle whitespace and weird sizes
# handle exact match
self.log_match(EXACT_MATCH, token, keyword)
match_found = True
exact_match_found = True
elif SequenceMatcher(None, token, keyword).ratio() > MATCH_THRESHOLD:
# handle partial match
self.log_match(PARTIAL_MATCH, token, keyword)
match_found = True
if match_found or good_spell is False:
# split text on current keyword
# this code needs further development for more keyword/text cases
my_text_pieces = my_text.split(token, 1)
if len(my_text_pieces) > 1:
# keyword + text before/after
text_before_keyword = my_text_pieces[0]
my_text = my_text_pieces[1]
self.doc_add_run(my_paragraph, text_before_keyword, None)
else:
# only keyword or only keyword and text before OR after
my_text = my_text_pieces[0]
if exact_match_found:
self.doc_add_run(my_paragraph, token, EXACT_MATCH)
# gui_print("%s \n" % token, 'good')
elif match_found:
self.doc_add_run(my_paragraph, token, PARTIAL_MATCH)
#self.gui_print("%s \n" % token, 'bad_match')
else:
self.doc_add_run(my_paragraph, token, BAD_SPELL)
# gui_print("%s \n Try: " % token, 'error_spell')
# suggestions = DICT.suggest(token)
# for s in suggestions:
# gui_print("%s, " % s, '-')
# gui_print ("\n", '-')
# reset booleans for next iteration
match_found = False
exact_match_found = False
good_spell = True
# add any leftover text
self.doc_add_run(my_paragraph, my_text, None)
@staticmethod
def doc_add_run(my_paragraph, my_run_text, my_match_flag):
# used to add text to paragraph being parsed
if my_run_text == '':
# eliminate "empty" runs
return
if my_match_flag is PARTIAL_MATCH:
# handle partial match (COLOR = BLUE)
my_run = my_paragraph.add_run(my_run_text + ' ')
my_run_font = my_run.font
my_run_font.bold = True
my_run_font.color.rgb = RGBColor(0x00, 0x00, 0xFF)
elif my_match_flag is EXACT_MATCH:
# handle exact match (COLOR = GREEN)
my_run = my_paragraph.add_run(my_run_text + ' ')
my_run_font = my_run.font
my_run_font.bold = True
my_run_font.color.rgb = RGBColor(0x32, 0xCD, 0x32)
elif my_match_flag is BAD_SPELL:
# handle bad spelling (COLOR = PURPLE)
my_run = my_paragraph.add_run(my_run_text + ' ')
my_run_font = my_run.font
my_run_font.bold = True
my_run_font.color.rgb = RGBColor(0x66, 0x00, 0xFF)
else:
# add runs w/o matches
my_paragraph.add_run(my_run_text)
@staticmethod
def get_tokens(my_text):
# remove non-alphanumeric chars, except underscore, and tokenize
pattern = re.compile('[\W]+', re.UNICODE)
tokens_ascii = shlex.split(filter(lambda x: x in ASCII_CHARS, my_text))
tokens = [pattern.sub(" ", elem) for elem in tokens_ascii]
return tokens
@staticmethod
def log_match(my_match_type, my_matching_token, my_matching_keyword):
# simple method to log matches
my_log = ''
my_log += my_match_type + ': ' + \
my_matching_token + ' / ' + my_matching_keyword + NEWLINE
return my_log
def library_parse(self):
# f = open('library1.txt', 'w')
self.doc_library = {}
for table in self.doc.tables:
# selects each table from the document which has "Signal Name" in the leftmost
if table.cell(0, 0).text == 'Signal Name':
# remove the header cell
s = (table.column_cells(0))
s.pop(0)
v = (table.column_cells(1))
v.pop(0)
for cell,cell2 in zip(s, v):
var_name = cell.text.strip()
var_type = 0
if 'bool' in cell2.text.lower() :
var_type = 1
vt = self.doc_library.get(var_name, -1)
# check to make sure var was not already identified
if vt != -1:
self.gui_print("Variable {} defined more than once.\n".format(var_name), '')
if vt != var_type:
self.gui_print("New definition for {} contradicts an old one.\n".format(var_name), '')
self.doc_library[var_name] = var_type
for k in self.doc_library.keys():
print ("%s --> %d" % (k, self.doc_library[k]))
def get_all_hlr(self):
# attempts to parse out all HLR related text from doc based on pattern matching
if self.doc_text is None:
self.parse_doc()
my_text_lines = self.doc_text.split('\n')
# HLR start pattern
hlr_match_start = []
hlr_match_end = []
in_hlr = False
hlr_start_pattern = re.compile('^The[^.]*shall (?!provide)', re.IGNORECASE)
hlr_cont_pattern = re.compile(r"<<[\w]+>>|func|\b%s\b" % r"\b|\b".join(self.hlr_keywords), re.IGNORECASE)
for i in range(0, len(my_text_lines)):
if not in_hlr and hlr_start_pattern.search(my_text_lines[i]):
hlr_match_start.append(i - 1)
in_hlr = True
continue
if in_hlr:
if (my_text_lines[i].strip() == ''):
continue
if hlr_start_pattern.search(my_text_lines[i]):
hlr_match_start.append(i)
hlr_match_end.append(i)
in_hlr = False
elif not hlr_cont_pattern.search(my_text_lines[i]):
hlr_match_end.append(i)
in_hlr = False
self.doc_hlr = []
for i in range(0, len(hlr_match_start)):
start = hlr_match_start[i]
end = hlr_match_end[i]
print (my_text_lines[start] + " : " + my_text_lines[end])
self.doc_hlr.append("\n".join(my_text_lines[start:end]))
return self.doc_hlr
def parse_all_hlr(self):
# get info from HLR - (tries) to get rid of all non functional HLRs
if self.doc_hlr is None:
self.get_all_hlr()
# print (self.doc_hlr)
# for h in self.doc_hlr:
# print ("\nbf: ", h[0:18], h[-28:])
all_func_hlr = [hlr for hlr in self.doc_hlr if 'FUNC' in hlr.upper()]
# for h in all_func_hlr:
# print ("\nfunc: ", h[0:18])
self.doc_hlr_parsed = [self.parse_single_hlr(func_hlr) for func_hlr in all_func_hlr]
return self.doc_hlr_parsed
def parse_single_hlr(self, my_hlr):
# hlr parsing tool that can be called to grab info
# built pattern and use it to grab all keywords
# Ex format: For HLR Object 1_1 in Table of HLRs the following is provided:
# [['<<CC>>', 'TRUE'], ['and', ['<<A>>', 'TRUE'], ['<<B>>', 'TRUE']]]
uses_PL = 0 # parts library indicator
# keywords, expand/add as needed - but must update _parse_single_hlr_helper as well
hlr_pattern = re.compile(r"<<[\w]+>>|\b%s\b" % r"\b|\b".join(self.hlr_keywords))
# HERE: deal with variations of parts libraries
## HERE IS WHERE YOU HANDLE PARTS LIB HLRS DIFFERENTLY
if 'parts lib' in my_hlr.lower():
uses_PL = 1
parts_lib_type = ''
for plt in PARTS_LIBRARY_TYPES:
if plt.lower() in my_hlr.lower():
parts_lib_type = plt # mark the parts library type
# print ("$$$$$" + my_hlr + '---------------' + parts_lib_type)
hlr_pattern = re.compile(r"<<[\w]+>>|\b%s\b" % r"\b|\b".join( self.parts_lib_keywords[parts_lib_type] ))
hlr_tokens = re.findall(hlr_pattern, my_hlr)
# print ("=====================\n" + my_hlr[1:8] + "\n MATCHES \n" + ", ".join(hlr_tokens) + "\n")
parsed_hlr = hlr_tokens
# DEBUGGING HERE
# print "Tokens:"
# print hlr_tokens
# print "Length = " + str(len(hlr_tokens))
# exit()
if not uses_PL:
parsed_hlr = [hlr_tokens[0], hlr_tokens[1]]
next_part = self.hlr_token_handler(hlr_tokens[2:])
if len(next_part) == 1:
next_part = next_part[0]
parsed_hlr = [parsed_hlr, next_part]
print (parsed_hlr)
return [my_hlr.splitlines()[0].strip(), parsed_hlr, uses_PL]
# def parse_parts_lib_hlr(self, pl_hlr):
# parts_lib_type = ''
# for plt in PARTS_LIBRARY_TYPES:
# if plt.lower() in pl_hlr.lower():
# parts_lib_type = plt # mark the parts library type
# ## define the parts library formats here
# BoolHold_keywords = ['SET', 'RESET', 'OUT']
# Timer_keywords = ['OUT', 'SET', 'LATCH_TIME']
# if parts_lib_type == 'Boolean Hold':
# hlr_pattern = re.compile(r"<<[\w]+>>|\b%s\b" % r"\b|\b".join(BoolHold_keywords))
# hlr_tokens = re.findall(hlr_pattern, pl_hlr)
# print ("=====================\n" + pl_hlr + "\n ~~MATCHES~~ \n" + ", ".join(hlr_tokens) + "\n")
def hlr_token_handler(self, my_hlr_tokens, index=0, met_flag=-1):
if index < len(my_hlr_tokens):
hlr_token = my_hlr_tokens[index]
else:
return []
parsed_hlr = []
if hlr_token == 'any':
parsed_hlr = ['or']
next_hlr_token = my_hlr_tokens[index + 1]
if next_hlr_token == 'TRUE' or next_hlr_token == 'FALSE':
parsed_hlr.extend(self.hlr_token_handler(my_hlr_tokens, index + 2, (0 if next_hlr_token == 'FALSE' else 1)))
else:
parsed_hlr.extend(self.hlr_token_handler(my_hlr_tokens, index + 1, -1))
elif hlr_token == 'all':
parsed_hlr = ['and']
next_hlr_token = my_hlr_tokens[index + 1]
if next_hlr_token == 'TRUE' or next_hlr_token == 'FALSE':
parsed_hlr.extend(self.hlr_token_handler(my_hlr_tokens, index + 2, (0 if next_hlr_token == 'FALSE' else 1)))
else:
parsed_hlr.extend(self.hlr_token_handler(my_hlr_tokens, index + 1, -1))
elif hlr_token == 'greater':
# parsed_hlr = ['>', my_hlr_tokens[index - 1], my_hlr_tokens[index + 1]]
parsed_hlr.append(['>', my_hlr_tokens[index - 1], my_hlr_tokens[index + 1]])
parsed_hlr.extend(self.hlr_token_handler(my_hlr_tokens, index + 2, met_flag))
elif hlr_token == 'less':
# parsed_hlr = ['<', my_hlr_tokens[index - 1], my_hlr_tokens[index + 1]]
parsed_hlr.append(['<', my_hlr_tokens[index - 1], my_hlr_tokens[index + 1]])
parsed_hlr.extend(self.hlr_token_handler(my_hlr_tokens, index + 2, met_flag))
elif hlr_token == 'equal':
# parsed_hlr = ['=', my_hlr_tokens[index - 1], my_hlr_tokens[index + 1]]
parsed_hlr.append(['=', my_hlr_tokens[index - 1], my_hlr_tokens[index + 1]])
parsed_hlr.extend(self.hlr_token_handler(my_hlr_tokens, index + 2, met_flag))
elif hlr_token == 'TRUE' and met_flag == -1 or hlr_token == 'FALSE' and met_flag == -1:
parsed_hlr.append([my_hlr_tokens[index - 1], hlr_token])
parsed_hlr.extend(self.hlr_token_handler(my_hlr_tokens, index + 1, met_flag))
elif "<<" in hlr_token and met_flag != -1:
parsed_hlr.append([my_hlr_tokens[index], 'FALSE' if met_flag == 0 else 'TRUE'])
parsed_hlr.extend(self.hlr_token_handler(my_hlr_tokens, index + 1, met_flag))
else:
parsed_hlr = self.hlr_token_handler(my_hlr_tokens, index + 1)
return parsed_hlr
def verify_all_hlr(self):
# Double checks all HLRs in document to make sure they follow HLR rules
# DOES NOT VERIFY PARTS LIBRARY HLRs - we don't have clear enough info on that yet
if self.doc_hlr is None:
self.get_all_hlr()
titles = set()
for hlr in self.doc_hlr:
# print ("------------------------------\n", hlr, "------------------------------\n")
if hlr == '':
continue
lines = hlr.splitlines()
title = lines[0].strip()
# verify title only used once
if title in titles:
self.gui_print("HLR title %s is used more than once.\n" % title, '')
titles.add(title)
# verify 'shall' only used once
if hlr.lower().split().count('shall') != 1:
self.gui_print("%s contains # of 'shall'.\n" % title, '')
self.doc_hlr.remove(hlr)
continue
# prelim detection of parts lib hlr
if 'parts lib' in hlr.lower():
parts_lib_type = ''
for plt in PARTS_LIBRARY_TYPES:
if plt.lower() in hlr.lower():
parts_lib_type = plt # mark the parts library type
if parts_lib_type == '':
self.gui_print("Skipping %s, uses unknown Parts Lib.\n" % title, '')
self.doc_hlr.remove(hlr)
else:
self.gui_print("%s uses %s Parts Lib.\n" % (title, parts_lib_type), '')
# self.doc_hlr.remove(hlr)
continue
# look through parse array for inconsistencies
if self.doc_hlr_parsed is None:
self.parse_all_hlr()
parsed_list = self.doc_hlr_parsed
var_pattern = re.compile('^<<[^ ]*>>')
arr_index = 0
while arr_index < len(parsed_list):
arr = parsed_list[arr_index]
# for a in ['', '1', '1']:
# arr = [ hlr name, [ ['<<CC>>', 'TRUE'], ['and', ['<<A>>', 'TRUE'], ['<', '<<B>>', '<<X1>>'] ] ], parts lib indicaton ]
print ("verif: ", arr)
title = arr[0] # hlr name
main_hlr = arr[1] # [ ['<<CC>>', 'TRUE'], ['and', ['<<A>>', 'TRUE'], ['<', '<<B>>', '<<X1>>'] ] ]
if len(main_hlr) < 2: # invalid HLR array
parsed_list.remove(arr)
continue
if arr[2] == 1: # skip any parts library hlrs
arr_index += 1
continue
test_case = main_hlr[0] # ['<<CC>>', 'TRUE']
conditions = main_hlr[1] # ['and', ['<<A>>', 'TRUE'], ['<', '<<B>>', '<<X1>>'] ]
print (" arr inde: ", arr_index)
print (" title: ", title)
print (" main_hlr: ", main_hlr)
print (" test_cas: ", test_case)
print (" conditio: ", conditions, "len : ", len(conditions))
# continue
# check for test case to contain variable
if not var_pattern.match(test_case[0]):
self.gui_print("%s may be missing a <<variable>> declaration.\n" % title, '')
parsed_list.remove(arr)
continue
elif not self.doc_library.has_key(test_case[0][2:-2]):
self.gui_print("Variable {} in {} is not declared in library.\n".format(test_case[0], title ), '')
elif (self.doc_library[test_case[0][2:-2]] == 1 and test_case[1] not in BOOL_VALS.keys()) or (self.doc_library[test_case[0][2:-2]] == 0 and test_case[1] in BOOL_VALS.keys()): # if a boolean var is not working with boolean val...
self.gui_print("Variable %s in %s is of inconsistent type.\n" % (test_case[0], title), '')
elif len(conditions) >= 3 and str(conditions[0]) != 'and' and str(conditions[0]) != 'or' and str(conditions[0]) not in COMP_OPS.keys() :
self.gui_print("%s has poor syntax and will be skipped.\n" % title, '')
parsed_list.remove(arr)
continue
elif conditions[0] == 'and' or conditions[0] == 'or':
# conditions.pop(0)
for c_sub in conditions[1:]:
if c_sub[0] in COMP_OPS.keys(): # clip the '>' signs from the front
c_sub = c_sub[1:]
for sub_var in c_sub:
if sub_var != 'TRUE' and sub_var != 'FALSE' and not var_pattern.match(sub_var):
# now would be the time to deal with data variables
# if sub_var is data variable:
# continue
# else:
self.gui_print("%s may be missing a <<variable>> declaration.\n" % title, '')
parsed_list.remove(arr)
continue
if not self.doc_library.has_key(c_sub[0][2:-2]):
self.gui_print("Variable {} in {} is not declared in library.\n".format(c_sub[0], title ), '')
elif (self.doc_library[c_sub[0][2:-2]] == 1 and c_sub[1] not in BOOL_VALS.keys()) or (self.doc_library[c_sub[0][2:-2]] == 0 and c_sub[1] in BOOL_VALS.keys()): # if a boolean var is not working with boolean val...
self.gui_print("Variable %s in %s is of inconsistent type.\n" % (c_sub[0], title), '')
elif conditions[0] in COMP_OPS.keys():
# conditions[0].pop(0)
for sub_var in conditions[0][1:]:
if not var_pattern.match(sub_var):
# now would be the time to deal with data variables
# if sub_var is data variable:
# continue
# else:
self.gui_print("%s may be missing a <<variable>> declaration.\n" % title, '')
parsed_list.remove(arr)
continue
elif var_pattern.match(str(conditions[0])): # make sure 1st var is a variable
if not self.doc_library.has_key(str(conditions[0])[2:-2]):
self.gui_print("Variable {} in {} is not declared in library.\n".format(str(conditions[0]), title ), '')
elif (self.doc_library[str(conditions[0])[2:-2]] == 1 and str(conditions[1]) not in BOOL_VALS.keys()) or (self.doc_library[str(conditions[0])[2:-2]] == 0 and str(conditions[1]) in BOOL_VALS.keys()): # if a boolean var is not working with boolean val...
self.gui_print("Variable %s in %s is of inconsistent type.\n" % (str(conditions[0]), title), '')
else:
self.gui_print("%s has bad syntax.\n" % title, '')
parsed_list.remove(arr)
continue
arr_index+=1
self.doc_hlr_parsed = parsed_list
def make_all_truth_tables(self):
if self.doc_hlr_parsed is None:
self.parse_all_hlr()
for ph in self.doc_hlr_parsed:
self.make_truth_table(ph)
def make_truth_table(self, given_hlr):
# [ hlr name, [['<<CC>>', 'TRUE'], ['and', ['<<A>>', 'TRUE'], ['<<B>>', 'TRUE']]] , uses parts library ]
# [['<<CC>>', 'TRUE'], ['>', '<<A>>', '<<NV>>']]
hlr_title = given_hlr[0] ## name
p_hlr = given_hlr[1] ## [whole thing]
uses_PL = given_hlr[2] ## parts lib indicator
if uses_PL == 1:
## HANDLE PULLING PARTS LIB TEMPLATES (maybe use .csv or .xlsx?)
return [-1]
test_var = p_hlr[0] ## [<<c>> t]
test_name = test_var[0] ## []
test_res = '0->1'
test_res_op = '1->0'
indexes_to_switch_res = [] # array of result matrix indices where result must be set to opposite (<>= operators)
# hlr_column_names = ['Sub test case / ID Variables'] # array of col names for each variable comparison
if test_var[1] == 'FALSE': # if CC is set to a boolean, flip resulting strings
trt = test_res
test_res = test_res_op
test_res_op = test_res
elif test_var[1] != 'TRUE': # if CC is set to some value, edit the name to become the value
test_name = test_var[0] + ' = ' + test_var[1]
conditions = p_hlr[1]
res_arr = []
if len(conditions) < 1: # [] WEIRD CASE
return [-1]
gate = conditions[0]
if gate == 'or' or gate == 'and':
num_conds = len(conditions)-1
# print ("# conditions", num_conds, "conditions: ", conditions)
# now handle condition options
i = 1
while i <= len(conditions)-1:
# print ("i: ", i)
cond_type = conditions[i][0]
if len(conditions[i]) < 2: # ['or'] ['TRUE'] WEIRD CASES
return [-1]
if cond_type == '>' or cond_type == '<' or cond_type == '=':
# first, add new default column for old rows (since these ops require an extra 'condition' row)
ri = 0
while ri < len(res_arr):
if 'becomes' in res_arr[ri][0]:
if gate == 'and':
res_arr[ri].append('1')
res_arr[ri+1].append('0')
else:
res_arr[ri].append('0')
res_arr[ri+1].append('1')
ri += 1
else:
char = res_arr[ri][1]
if gate == 'and':
res_arr[ri].append(char[-1])
else:
res_arr[ri].append(char[0])
ri += 1
# now create the two rows for the conditions
var = conditions[i][1]
comp_var = conditions[i][2]
c1 = var + " becomes " + cond_type + " " + comp_var
c2 = var + " becomes " + COMP_OPS[cond_type] + " " + comp_var
crow1 = [c1]
crow2 = [c2]
j = 1
while j < num_conds + 2:
if j == i: # changing tested condition
crow1.append('0->1')
crow2.append('0')
elif j == i + 1: # changing opposite condition
crow1.append('0')
crow2.append('0->1')
else:
if gate == 'and':
crow1.append('1')
crow2.append('0') # basic 'and' condition
else:
crow1.append('0')
crow2.append('1') # basic 'or' condition
j += 1
res_arr.append(crow1)
res_arr.append(crow2)
indexes_to_switch_res.append(i+1) # record the switch of opposite conditions
i += 1
num_conds += 1
else:
cond_bool = conditions[i][1]
crow = []
crow.append(cond_type) # first elem is variable
j = 1
while j < num_conds+1:
if j == i: # changing condition
crow.append( BOOL_OPS[cond_bool] + '->' + BOOL_VALS[cond_bool] )
else:
if gate == 'and':
crow.append('' + BOOL_VALS[cond_bool]) # basic 'and' condition
else:
crow.append('' + BOOL_OPS[cond_bool]) # basic 'or' condition
j += 1
res_arr.append(crow)
i += 1
elif conditions[0] == '>' or conditions[0] == '<' or conditions[0] == '=':
var = conditions[1]
comp_var = conditions[2]
c1 = var + " becomes " + conditions[0] + " " + comp_var
c2 = var + " becomes " + COMP_OPS[conditions[0]] + " " + comp_var
crow1 = [c1, '0->1', '0']
crow2 = [c2, '0', '0->1']
res_arr.append(crow1)
res_arr.append(crow2)
indexes_to_switch_res.append(2)
elif str(conditions[0])[0:2] == '<<':
conditions = ['and', conditions]
return self.make_truth_table( [hlr_title, [test_var,conditions], uses_PL] )
else:
return [-1]
test_arr = []
test_arr.append(test_name) # first elem is variable
j = 1
if len(res_arr) < 1:
return [-1]
while j < len(res_arr[0]): # handle every conditional column
if j in indexes_to_switch_res: # changing condition
test_arr.append( test_res_op )
else:
test_arr.append( test_res )
j += 1
res_arr.append(test_arr)
self.gui_print(" - " + hlr_title + "\n", '')
# for RR in res_arr:
# print (RR)
# print ("\n\n")
return res_arr
def make_test_cases(self):
if self.doc_hlr_parsed is None:
self.verify_all_hlr()
wb = Workbook()
ws = wb.active
grayFill = PatternFill(start_color='D9D9D9', end_color='D9D9D9', fill_type='solid')
if os.path.isfile(self.hlr_path) and self.hlr_path.split('\\')[-1].split('.')[1] == 'xlsx':
wb = load_workbook(self.hlr_path)
ws = wb.create_sheet()
ws.title = "Truth Tables"
ws_row = 2
ws_col = 'C'
self.gui_print("\nThe following HLRs were turned into test cases:\n", '')
for arr in self.doc_hlr_parsed:
print (arr)
print ("\n ------------ \n")
test_case = self.make_truth_table(arr)
# skip empty results
if test_case == [-1]:
continue
tc_size = len(test_case[0])
# print title of TT and any PL reqs
ws[ws_col + str(ws_row) ] = arr[0]
ws_row += 1
ws[ws_col + str(ws_row)] = 'PL requirements?'
ws_row += 2
# print corner cell
ws.cell(ws_col + str(ws_row)).style.alignment.wrap_text = True
ws[ws_col + str(ws_row)] = "Sub test case / ID Variables"
ws[ws_col + str(ws_row)].fill = grayFill
ws[ws_col + str(ws_row)].font = Font(bold=True)
# add column names
for i in range(1, tc_size):
col_let = chr(ord(ws_col) + i)
ws[col_let + str(ws_row)] = "TCX_" + chr(ord(col_let) + 10)
ws[col_let + str(ws_row)].fill = grayFill
ws[col_let + str(ws_row)].font = Font(bold=True)
ws_row += 1
# make actual test case
for tc_row in test_case:
i = 0
for tc_element in tc_row:
ws[chr(ord(ws_col) + i) + str(ws_row)] = tc_element
if i == 0:
ws[chr(ord(ws_col) + i) + str(ws_row)].fill = grayFill
ws[chr(ord(ws_col) + i) + str(ws_row)].font = Font(bold=True)
i += 1
ws_row += 1
ws_row += 2
print ("\n _______________________________________________ \n")
# _make_all_truth_tables(parsed_arrays)
ws.column_dimensions[ws_col].width = 28
wb.save(self.hlr_path.split('.')[0] + '.xlsx')
def iter_block_items(self):
"""
Generate a reference to each paragraph and table child within *parent*,
in document order. Each returned value is an instance of either Table or
Paragraph. *parent* would most commonly be a reference to a main
Document object, but also works for a _Cell object, which itself can
contain paragraphs and tables.
"""
if isinstance(self.doc, _Document):
parent_elm = self.doc.element.body
# print(parent_elm.xml)
elif isinstance(self.doc, _Cell):
parent_elm = self.doc._tc
else:
raise ValueError("something's not right")
for child in parent_elm.iterchildren():
if isinstance(child, CT_P):
yield Paragraph(child, self.doc)
elif isinstance(child, CT_Tbl):
yield Table(child, self.doc)
def gui_print(self, text, code):
self.gui.insert('end', text, code)
# define colors of tags 'error' and 'good' and any others
self.gui.tag_configure('error_spell', foreground='red')
self.gui.tag_configure('good', foreground='green')
self.gui.tag_configure('bad_match', foreground='blue')
self.gui.tag_configure('', foreground='black')