From 9375d5536c060eaa6c132f7533f8486abfd04074 Mon Sep 17 00:00:00 2001
From: Lan Hui <1348141770@qq.com>
Date: Wed, 14 Jul 2021 15:05:47 +0800
Subject: Upload Jin Xiongrong's work -- https://gitee.com/dragondove/storode;
fix UnicodeDecodeError
---
src/collision.py | 54 ++++++++
src/storode.py | 380 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 434 insertions(+)
create mode 100644 src/collision.py
create mode 100644 src/storode.py
(limited to 'src')
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', '
')
+
+
+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('')
+ 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: ' + '' + code_out_file + ':' + str(
+ i) + '' + ' 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: ' + '' + code_out_file + ':' + str(
+ i) + '' + ' 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: ' + '' + code_out_file + ':' + str(
+ i) + '' + ' requirement identifier: [' + requirement_identifier + '] not found')
+ if i == superlink_line:
+ insertion_line = '' + insertion_line + ""
+ line_number = str(i)
+ while len(line_number) < 4:
+ line_number = ' ' + line_number
+ insertion_line = '' + \
+ line_number + '' + '' + \
+ insertion_line + ''
+ out_file.write(insertion_line)
+
+ # generate the page for requirements
+ with open(requirement_out_file, 'w') as out_file:
+ out_file.write(
+ '
Identifier | Front Card | Back Card | Date | Links |
---|---|---|---|---|
' + requirement.identifier + ' | ' + + fronts + ' | ' + + backs + ' | ' + + transfer_str_to_html(requirement.create_info) + + transfer_str_to_html(requirement.update_info) + + ' | ' + link_str + ' |
')
+ for problem in problems:
+ out_file.write(problem + '
')
+
+ out_file.write('