diff options
| author | Lan Hui <1348141770@qq.com> | 2021-07-14 15:05:47 +0800 | 
|---|---|---|
| committer | Lan Hui <1348141770@qq.com> | 2021-07-14 15:05:47 +0800 | 
| commit | 9375d5536c060eaa6c132f7533f8486abfd04074 (patch) | |
| tree | 6d88340bb70f9adec6f5a469686609e5777f8160 /src | |
Upload Jin Xiongrong's work -- https://gitee.com/dragondove/storode; fix UnicodeDecodeError
Diffstat (limited to 'src')
| -rw-r--r-- | src/collision.py | 54 | ||||
| -rw-r--r-- | src/storode.py | 380 | 
2 files changed, 434 insertions, 0 deletions
| diff --git a/src/collision.py b/src/collision.py new file mode 100644 index 0000000..5d87cf7 --- /dev/null +++ b/src/collision.py @@ -0,0 +1,54 @@ +import hashlib +import random + +# collision tests for my identifier solution + + +def get_identifier(s): +    m2 = hashlib.md5() +    m2.update(s.encode('utf-8')) +    digest = m2.hexdigest()[-4:] +    return digest + + +def pick_random_text(lines, num): +    length = len(text_lines) +    s = '' +    for i in range(num): +        s += lines[random.randint(0, num)] + +    return s + + +def get_text_lines_from_file(file_path): +    lines = [] +    with open(file_path, encoding='utf8') as f: +        for line in f.readlines(): +            if (line.strip() != ''): +                lines.append(line) + +    return lines + + +if __name__ == "__main__": +    text_lines = get_text_lines_from_file('./sample/Wonderland.txt') +    total_sample_count = 100 +    sample_size = 8 +    test_times = 100 +    collision_counts = [] +    for i in range(test_times): +        collision_count = 0 +        digest_set = set() +        for j in range(total_sample_count): +            digest = get_identifier(pick_random_text(text_lines, sample_size)) +            if digest in digest_set: +                collision_count += 1 +            else: +                digest_set.add(digest) + +        collision_counts.append(collision_count) + +    avg_collision_count = sum(collision_counts) / test_times +    print('average collision happened count: ' + str(avg_collision_count)) +    print('collision rate: ' + +          str(100 * (avg_collision_count / total_sample_count)) + '%') diff --git a/src/storode.py b/src/storode.py new file mode 100644 index 0000000..54145b2 --- /dev/null +++ b/src/storode.py @@ -0,0 +1,380 @@ +import sys +import os +import hashlib + +from pathlib import Path +from enum import Enum + + +# function for calculating relative paths of path a to path b +def calculate_relative_path(a, b): +    file_name = b[b.rindex('/') + 1:] +    a, b = a[:a.rindex('/')], b[:b.rindex('/')] +    a, b = a.split('/'), b.split('/') + +    intersection = 0 + +    for index in range(min(len(a), len(b))): +        m, n = a[index], b[index] +        if m != n: +            intersection = index +            break + +    def backward(): +        return (len(a) - intersection - 1) * '../' + +    def forward(): +        return '/'.join(b[intersection:]) + +    out = backward() + forward() + '/' + file_name +    return out + + +# transfer whitespaces in string to html string +def transfer_str_to_html(s): +    return s.replace( +        ' ', ' ').replace('\t', '  ').replace('\n', '<br />') + + +class ReadState(Enum): +    FRONT_OF_CARD = 0 +    BACK_OF_CARD = 1 +    CREATE = 2 +    UPDATE = 3 + + +class Requirement: +    def __init__(self): +        self.state = ReadState.FRONT_OF_CARD +        self.identifier = '' +        self.front_of_card = '' +        self.front_lines = 0 +        self.back_lines = 0 +        self.back_of_card = '' +        self.create_info = '' +        self.update_info = '' +        self.code_links = {} + +    def generate_identifier(self, exclusion=[]): +        m2 = hashlib.md5() +        m2.update((self.front_of_card + self.back_of_card).encode('utf-8')) +        digest = m2.hexdigest()[-4:] +        loop = 0 +        while digest in exclusion: +            last = digest[-1] +            if loop > 16: +                raise Exception('Too many collision') +            loop += 1 +            if last > 'f': +                last = '0' +            else: +                digest = digest[:-1] + chr(ord(digest[-1]) + 1) +        self.identifier = digest +        collision.append(digest) + +    def text_append(self, line): +        if self.state == ReadState.FRONT_OF_CARD: +            if line.strip() != '': +                self.front_lines += 1 +            self.front_of_card += line +        elif self.state == ReadState.BACK_OF_CARD: +            if line.strip() != '': +                self.back_lines += 1 +            self.back_of_card += line +        elif self.state == ReadState.CREATE: +            self.create_info += line +        elif self.state == ReadState.UPDATE: +            self.update_info += line + +    def generate_line_number(self): +        fronts = self.front_of_card.split('\n') +        backs = self.back_of_card.split('\n') + +        def get_line_number(strings): +            line_number = 1 +            ret = '' +            for string in strings: +                if string.strip() == '': +                    ret += string + '\n' +                else: +                    ret += str(line_number) + ' ' + string + '\n' +                    line_number += 1 +            return ret + +        self.front_of_card = get_line_number(fronts) +        self.back_of_card = get_line_number(backs) + +    def process_str(self, line): +        if line == '\n': +            return +        if 'back of card' in line.lower(): +            self.line_number = 1 +            self.state = ReadState.BACK_OF_CARD +        elif 'created by' in line.lower(): +            self.state = ReadState.CREATE +            self.text_append(line) +        elif 'updated by' in line.lower(): +            self.state = ReadState.UPDATE +            self.text_append(line) +        else: +            self.text_append(line) + + +class CodeFile: +    def __init__(self): +        self.filename = '' +        self.filepath = '' +        self.lines = [] + +    def find_next_code_signature(self, from_line): +        length = len(self.lines) +        index = from_line +        while index < length: +            line = self.lines[index].strip() +            if line == '' or line[0] == '#': +                index += 1 +                continue +            if line.startswith('def ') or line.startswith('class '): +                return index, line.split()[1][:-1] +            # delete end comments +            if '#' in line: +                line = line[:line.index('#')].strip() +            if '=' in line and '==' not in line: +                return index, line[:line.index('=')].strip() +            if line.startswith('if '): +                return index, line[3:-1] +            return index, line +        return None, None + + +if __name__ == "__main__": +    argc = len(sys.argv) +    if argc != 2 and argc != 3: +        print('Usage:') +        print('"python storode.py ./srs.txt" to get a srs file with id') +        print('"python storode.py ./srs_id.txt ./src" to generate web pages for crossing reference') +        exit() + +    need_identifier = True if argc == 2 else False + +    # Read requirements +    requirements = {} + +    problems = [] + +    code_files = [] + +    # root dir for out pages +    out_dir = './doc' + +    srs_file = sys.argv[1] +    if not need_identifier: +        src_path = sys.argv[2] + +    # Read requirements from file +    with open(srs_file) as srs: +        last_line = '' +        requirement = None +        lines = srs.readlines() +        collision = [] +        for i in range(len(lines)): +            line = lines[i] +            if 'front of card' in line.lower(): +                if need_identifier: +                    if requirement != None: +                        requirement.generate_identifier(collision) +                        requirements[requirement.identifier] = requirement +                    requirement = Requirement() +                else: +                    requirement = Requirement() +                    requirement.identifier = last_line.replace('\n', '') +                    requirements[requirement.identifier] = requirement +            elif requirement != None: +                requirement.process_str(line) +            last_line = line +            if i == len(lines) - 1: +                if need_identifier: +                    if requirement != None: +                        requirement.generate_identifier(collision) +                        requirements[requirement.identifier] = requirement + +    # Write srs with id file to disk +    if need_identifier: +        with open(srs_file[:srs_file.rindex('.')] + '_with_id.txt', 'w') as srs: +            for _, requirement in requirements.items(): +                requirement.generate_line_number() +                srs.write(requirement.identifier + '\n') +                srs.write('FRONT OF CARD\n') +                srs.write(requirement.front_of_card) +                srs.write('BACK OF CARD\n') +                srs.write(requirement.back_of_card) +                srs.write(requirement.create_info) +                srs.write(requirement.update_info) +                srs.write('\n\n') +        exit(0) + +    src_path = Path(src_path) + +    requirement_out_file = out_dir + '/' + \ +        srs_file[srs_file.rindex('/') + 1:srs_file.rindex('.')] + '.html' + +    # Read source code +    python_files = [] + +    # Add python paths recursively +    def recursion_view_path(path): +        if path.is_dir(): +            for single_path in path.iterdir(): +                recursion_view_path(single_path) +        else: +            if path.match('*.py'): +                python_files.append(path) + +    recursion_view_path(src_path) + +    for python_file in python_files: +        code_file = CodeFile() +        code_files.append(code_file) +        code_file.filename = python_file.name +        code_file.filepath = str(python_file.parent).replace('\\', '/') +        with open(python_file, encoding='UTF-8') as file: +            for line in file.readlines(): +                code_file.lines.append(line) + +    # generate pages for code +    for code_file in code_files: +        out_path = out_dir + '/' + code_file.filepath +        code_out_file = out_path + '/' + code_file.filename + '.html' +        relative_requirement_file = calculate_relative_path( +            code_out_file, requirement_out_file) +        relative_code_file = calculate_relative_path( +            requirement_out_file, code_out_file) +        if not os.path.exists(out_path): +            os.makedirs(out_path) +        with open(code_out_file, 'w', encoding='UTF-8') as out_file: +            out_file.write('<style>:target{background-color:#ffff00;}</style>') +            len_of_code = len(code_file.lines) +            former_superlink = None +            superlink_line = None +            infect_lines = None +            for i in range(len_of_code): +                line = code_file.lines[i] +                insertion_line = transfer_str_to_html(line) +                if '#' in line: +                    code = line[:line.index('#')] + '\n' +                    comment = line[line.index('#'):] +                    # make sure this commet is for linking requirements +                    if '@req ' in comment: +                        requirement_identifier = comment[comment.index( +                            '@req') + 5:].strip() +                        requirement_split = requirement_identifier.split() +                        requirement_identifier = requirement_split[ +                            0] +                        if len(requirement_split) > 1: +                            infect_lines = requirement_split[1] +                        else: +                            infect_lines = None + +                        card_line = None +                        is_front = True +                        requirement_link = requirement_identifier[:] +                        if ':' in requirement_identifier: +                            requirement_identifier, card_line = requirement_identifier.split( +                                ':') +                            if card_line.lower().startswith('front'): +                                card_line = card_line[5:] +                            elif card_line.lower().startswith('back'): +                                is_front = False +                                card_line = card_line[4:] + +                        if i + 1 < len_of_code and requirements.__contains__(requirement_identifier): +                            requirement = requirements[requirement_identifier] +                            if card_line != None: +                                card_line = int(card_line) +                                if is_front: +                                    if requirement.front_lines < card_line: +                                        problems.append('WARNING: ' + '<a href="' + relative_code_file + '#line' + str(i) + '">' + code_out_file + ':' + str( +                                            i) + '</a>' + ' requirement identifier: [' + requirement_link + '] not found') +                                    else: +                                        superlink_line, signature = code_file.find_next_code_signature( +                                            i) +                                        former_superlink = relative_requirement_file + '#' + requirement_link +                                        requirement.code_links[signature] = relative_code_file + \ +                                            '#line' + str(superlink_line) +                                        insertion_line = transfer_str_to_html( +                                            code) +                                else: +                                    if requirement.back_lines < card_line: +                                        problems.append('WARNING: ' + '<a href="' + relative_code_file + '#line' + str(i) + '">' + code_out_file + ':' + str( +                                            i) + '</a>' + ' requirement identifier: [' + requirement_link + '] not found') +                                    else: +                                        superlink_line, signature = code_file.find_next_code_signature( +                                            i) +                                        former_superlink = relative_requirement_file + '#' + requirement_link +                                        requirement.code_links[signature] = relative_code_file + \ +                                            '#line' + str(superlink_line) +                                        insertion_line = transfer_str_to_html( +                                            code) +                            else: +                                superlink_line, signature = code_file.find_next_code_signature( +                                    i) +                                former_superlink = relative_requirement_file + '#' + requirement_link +                                requirement.code_links[signature] = relative_code_file + \ +                                    '#line' + str(superlink_line) +                                insertion_line = transfer_str_to_html(code) +                        else: +                            problems.append('WARNING: ' + '<a href="' + relative_code_file + '#line' + str(i) + '">' + code_out_file + ':' + str( +                                i) + '</a>' + ' requirement identifier: [' + requirement_identifier + '] not found') +                if i == superlink_line: +                    insertion_line = '<a title="' + ('+' + infect_lines if infect_lines != None else '') + '" href="' + \ +                        former_superlink + '">' + insertion_line + "</a>" +                line_number = str(i) +                while len(line_number) < 4: +                    line_number = ' ' + line_number +                insertion_line = '<span id="line' + str(i) + '" style="user-select:none;font-family:serif;font-weight: bold;white-space: pre;">' + \ +                    line_number + '</span>' + '<span style="padding-left: 12px;">' + \ +                    insertion_line + '</span>' +                out_file.write(insertion_line) + +    # generate the page for requirements +    with open(requirement_out_file, 'w') as out_file: +        out_file.write( +            '<style>:target{background-color:#ffff00;}th{overflow: hidden;text-overflow:ellipsis;}td{overflow:hidden;text-overflow:ellipsis;}</style><table border="1" width="94%" style="word-break:break-all;margin:0 auto;"><tr><th>Identifier</th><th>Front Card</th><th>Back Card</th><th>Date</th><th>Links</th></tr><tbody>') +        total_req_count = len(requirements) +        covered_count = 0 +        for _, requirement in requirements.items(): +            link_str = '' +            for key, value in requirement.code_links.items(): +                link_str += '<a href="' + value + '">' + key + '</a><br />' + +            if link_str != '': +                covered_count += 1 + +            fronts = '' +            for card_line in requirement.front_of_card.split('\n'): +                if card_line.strip() != '': +                    fronts += '<span id="' + requirement.identifier + ':FRONT' + \ +                        card_line.split()[0] + '">' + \ +                        transfer_str_to_html(card_line) + '</span><br />' + +            backs = '' +            for card_line in requirement.back_of_card.split('\n'): +                if card_line.strip() != '': +                    backs += '<span id="' + requirement.identifier + ':BACK' + \ +                        card_line.split()[0] + '">' + \ +                        transfer_str_to_html(card_line) + '</span><br />' + +            out_file.write('<tr><td id="' + requirement.identifier + '">' + requirement.identifier + '</td><td>' +                           + fronts + '</td><td>' +                           + backs + '</td><td>' +                           + transfer_str_to_html(requirement.create_info) + +                           transfer_str_to_html(requirement.update_info) +                           + '</td><td>' + link_str + '</td></tr>') +        out_file.write('</tbody></table><br />') +        problems.append('Conclusion: Total Requirements Count: ' + str(total_req_count) + ', Covered Requirements: ' + +                        str(covered_count) + ', Coverage Rate: ' + str(100 * round(covered_count / total_req_count, 4)) + '%.') +        out_file.write('<p style="margin-left: 3%; margin-top: -2px;">') +        for problem in problems: +            out_file.write(problem + '<br />') + +        out_file.write('</p>') | 
