Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pulling Base CSV Checking Feature #8

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 0 additions & 14 deletions doorstop/cli/tests/files/exported-map.csv
Original file line number Diff line number Diff line change
@@ -1,14 +0,0 @@
uid,mylevel,text,ref,links,active,derived,normative
REQ001,1.2.3,"Hello, world!
",,"SYS001,
SYS002",True,False,True
REQ003,1.4,"Hello, world!
",REF123,REQ001,True,False,True
REQ004,1.6,"Hello, world!
",,,True,False,True
REQ002,2.1,"Hello, world!
",,,True,False,True
invalid,2.1,"Hello, world!
",,REQ001,True,False,True
REQ2-001,2.1,"Hello, world!
",,REQ001,True,False,True
14 changes: 0 additions & 14 deletions doorstop/cli/tests/files/exported.csv
Original file line number Diff line number Diff line change
@@ -1,14 +0,0 @@
uid,level,text,ref,links,active,derived,normative
REQ001,1.2.3,"Hello, world!
",,"SYS001,
SYS002",True,False,True
REQ003,1.4,"Hello, world!
",REF123,REQ001,True,False,True
REQ004,1.6,"Hello, world!
",,,True,False,True
REQ002,2.1,"Hello, world!
",,,True,False,True
invalid,2.1,"Hello, world!
",,REQ001,True,False,True
REQ2-001,2.1,"Hello, world!
",,REQ001,True,False,True
12 changes: 0 additions & 12 deletions doorstop/cli/tests/files/exported.tsv
Original file line number Diff line number Diff line change
@@ -1,12 +0,0 @@
uid level text ref links active derived normative
REQ001 1.2.3 "Hello, world!
" "SYS001,
SYS002" True False True
REQ003 1.4 "Hello, world!
" REF123 REQ001 True False True
REQ004 1.6 "Hello, world!
" True False True
REQ002 2.1 "Hello, world!
" True False True
REQ2-001 2.1 "Hello, world!
" REQ001 True False True
Binary file modified doorstop/cli/tests/files/exported.xlsx
Binary file not shown.
3 changes: 2 additions & 1 deletion doorstop/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ def _trace(self, message, *args, **kws):

logging.addLevelName(logging.DEBUG - 1, "TRACE")
logging.Logger.trace = _trace # type: ignore

logging.basicConfig(filename="warning_log.txt", level=logging.WARNING)
logging.captureWarnings(True)
logger = logging.getLogger
log = logger(__name__)

Expand Down
1 change: 1 addition & 0 deletions doorstop/core/files/templates/latex/doorstop.cls
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
\RequirePackage{color}
\RequirePackage{listings}
\RequirePackage{tikz}
\RequirePackage{csvsimple}
\RequirePackage{xstring}
\RequirePackage[compact]{titlesec}
\RequirePackage{appendix}
Expand Down
1 change: 1 addition & 0 deletions doorstop/core/files/templates/latex/doorstop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ usepackage:
gitinfo2:
parskip:
silence:
csvsimple:
hyperref:
# Options are defined as sub-lists.
- unicode
Expand Down
23 changes: 19 additions & 4 deletions doorstop/core/importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import openpyxl

from doorstop import common, settings
from doorstop.common import DoorstopError
from doorstop.common import DoorstopError, DoorstopWarning
from doorstop.core.builder import _get_tree
from doorstop.core.document import Document
from doorstop.core.item import Item
Expand All @@ -24,6 +24,7 @@
log = common.logger(__name__)



def import_file(path, document, ext=None, mapping=None, **kwargs):
"""Import items from an exported file.

Expand Down Expand Up @@ -150,8 +151,10 @@ def _file_csv(path, document, delimiter=",", mapping=None):
row = []
value: Any
for value in _row:
if _row[0].startswith("#") or _row[0].startswith(" "):
value = False
# convert string booleans
if isinstance(value, str):
elif isinstance(value, str):
if value.lower() == "true":
value = True
elif value.lower() == "false":
Expand Down Expand Up @@ -227,12 +230,16 @@ def _itemize(header, data, document, mapping=None):
"""
log.info("converting rows to items...")
log.debug("header: {}".format(header))

prev_uid = []

for row in data:
log.debug("row: {}".format(row))

# Parse item attributes
attrs = {}
uid = None

for index, value in enumerate(row):
# Key lookup
key = str(header[index]).lower().strip() if header[index] else ""
Expand All @@ -249,7 +256,15 @@ def _itemize(header, data, document, mapping=None):

# Convert values for particular keys
if key in ("uid", "id"): # 'id' for backwards compatibility
uid = value
if value is not False:
uid = value
prev_uid.append(uid)
# Checks for duplicate UID's that were already entered from the same csv file.
if len(prev_uid) != len(set(prev_uid)):
log.warning(" Duplicate UID in csv found: '" + str(uid) + "'.")
print("DOORSTOP WARNING: Duplicate UID in csv found: '" + str(uid) + "'.")
prev_uid = list(set(prev_uid))

elif key == "links":
# split links into a list
attrs[key] = _split_list(value)
Expand Down Expand Up @@ -286,9 +301,9 @@ def _itemize(header, data, document, mapping=None):

# Convert the row to an item
if uid and uid != settings.PLACEHOLDER:
# Delete the old item
try:
item = document.find_item(uid)
print("Updating entry for ("+ uid + ")...")
except DoorstopError:
log.debug("not yet an item: {}".format(uid))
else:
Expand Down
94 changes: 92 additions & 2 deletions doorstop/core/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ class Item(BaseFileObject): # pylint: disable=R0902
DEFAULT_REF = ""
DEFAULT_HEADER = Text()
DEFAULT_ITEMFORMAT = "yaml"
DEFAULT_TEST_METHODS = Text()
DEFAULT_TIER = Text()
DEFAULT_STATUS = Text()
DEFAULT_ARTIFACT = Text()
DEFAULT_TESTING_NOTES = Text()

def __init__(self, document, path, root=os.getcwd(), **kwargs):
"""Initialize an item from an existing file.
Expand Down Expand Up @@ -150,6 +155,11 @@ def __init__(self, document, path, root=os.getcwd(), **kwargs):
self._data["text"] = Item.DEFAULT_TEXT
self._data["notes"] = Item.DEFAULT_NOTES
self._data["ref"] = Item.DEFAULT_REF
self._data["test method(s)"] = Item.DEFAULT_TEST_METHODS # type: ignore
self._data["tier"] = Item.DEFAULT_TIER # type: ignore
self._data["status"] = Item.DEFAULT_STATUS # type: ignore
self._data["artifact"] = Item.DEFAULT_ARTIFACT # type: ignore
self._data["testing notes"] = Item.DEFAULT_TESTING_NOTES # type:ignore
self._data["references"] = None # type: ignore
self._data["links"] = set() # type: ignore
if settings.ENABLE_HEADERS:
Expand Down Expand Up @@ -259,6 +269,16 @@ def _set_attributes(self, attributes):
value = Text(value)
elif key == "notes":
value = Text(value)
elif key == "status":
value = Text(value)
elif key == "tier":
value = Text(value)
elif key == "artifact":
value = Text(value)
elif key == "test method(s)":
value = Text(value)
elif key == "testing notes":
value = Text(value)
elif key == "ref":
value = value.strip()
elif key == "references":
Expand Down Expand Up @@ -296,7 +316,7 @@ def load(self, reload=False):
# Parse YAML data from text
data = common.load_yaml(text, self.path)
else:
msg = "unknwon item format detected during load: {}({})".format(
msg = "unknown item format detected during load: {}({})".format(
self.uid, self.itemformat
)
raise DoorstopError(msg) from None
Expand All @@ -323,7 +343,7 @@ def save(self):
# Dump the data to YAML
text = self._dump(data)
else:
msg = "unknwon item format detected during save: {}({})".format(
msg = "unknown item format detected during save: {}({})".format(
self.uid, self.itemformat
)
raise DoorstopError(msg) from None
Expand Down Expand Up @@ -382,6 +402,16 @@ def _yaml_data(self, textattributekeys=None):
value = value.yaml # type: ignore
elif key == "notes":
value = value.yaml # type: ignore
elif key == "status":
value = value.yaml # type: ignore
elif key == "tier":
value = value.yaml # type: ignore
elif key == "artifact":
value = value.yaml # type: ignore
elif key == "test method(s)":
value = _convert_to_yaml(0, len(key) + 2, value) # type: ignore
elif key == "testing notes":
value = _convert_to_yaml(0, len(key) + 2, value) # type: ignore
else:
value = _convert_to_yaml(0, len(key) + 2, value)
data[key] = value
Expand Down Expand Up @@ -559,6 +589,66 @@ def notes(self, value):
"""Set the item's text."""
self._data["notes"] = Text(value)

@property # type: ignore
@auto_load
def test_methods(self):
"""Get the item's text."""
return self._data["test method(s)"]

@test_methods.setter # type: ignore
@auto_save
def test_methods(self, value):
"""Set the item's text."""
self._data["test method(s)"] = Text(value)

@property # type: ignore
@auto_load
def tier(self):
"""Get the item's text."""
return self._data["tier"]

@tier.setter # type: ignore
@auto_save
def tier(self, value):
"""Set the item's text."""
self._data["tier"] = Text(value)

@property # type: ignore
@auto_load
def status(self):
"""Get the item's text."""
return self._data["status"]

@status.setter # type: ignore
@auto_save
def status(self, value):
"""Set the item's text."""
self._data["status"] = Text(value)

@property # type: ignore
@auto_load
def artifact(self):
"""Get the item's text."""
return self._data["artifact"]

@artifact.setter # type: ignore
@auto_save
def artifact(self, value):
"""Set the item's text."""
self._data["artifact"] = Text(value)

@property # type: ignore
@auto_load
def testing_notes(self):
"""Get the item's text."""
return self._data["testing notes"]

@testing_notes.setter # type: ignore
@auto_save
def testing_notes(self, value):
"""Set the item's text."""
self._data["testing notes"] = Text(value)

@property # type: ignore
@auto_load
def header(self):
Expand Down
12 changes: 6 additions & 6 deletions doorstop/core/publishers/latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,23 +360,23 @@ def _format_href_text(self, text):
split_line_text = line.split("[")
# Text before the link
text = str(split_line_text[0])
text = text.replace("_", "\_")
text = text.replace("_", "\\_")
# Rest of line / unformatted with no text part
remainder = str(split_line_text[1])
split_link_prefix = remainder.split("](")
split_link = str(split_link_prefix[1]).split(")")
# Markdown URL prefix
url_prefix = "{" + str(split_link_prefix[0]) + "}"
link = "{" + str(split_link[0]) + "}"
url_prefix = url_prefix.replace("_", "\_")
rest_text = str(split_link[1]).replace("_", "\_")
output_line = text + "\href" + link + url_prefix + rest_text
url_prefix = url_prefix.replace("_", "\\_")
rest_text = str(split_link[1]).replace("_", "\\_")
output_line = text + "\\href" + link + url_prefix + rest_text
yield output_line
elif "<br>" in line:
output_line = line.replace("<br> <br>", "\\par ").replace("<br><br>", "\\par ").replace("<br>", "\\par")
yield output_line.replace("^", "\^").replace("_", "\_")
yield output_line.replace("^", "\\^").replace("_", "\\_")
else:
output_line = line.replace("^", "\^").replace("_", "\_")
output_line = line.replace("^", "\\^").replace("_", "\\_")
yield output_line


Expand Down
19 changes: 18 additions & 1 deletion doorstop/core/publishers/markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,24 @@ def create_matrix(self, directory):
markdown = "\n".join(lines)
common.write_text(markdown, path)

# def lines_rvm(self, obj, **kwargs):
# """Requirements Verification Matrix Lines"""
# # title
# title = '# Requirements Verification Matrix'
# yield title
# # header
# table_format = "| --------- |"
# table_adjustments = "---------- |"
# count = 0
# linkify = kwargs.get("linkify", False)
# to_html = kwargs.get("to_html", False)
#
# # header data / table start
# for item in iter_items(obj):
# if item.



def lines_matrix(self, **kwargs):
"""Traceability table for markdown output."""
# title
Expand Down Expand Up @@ -326,7 +344,6 @@ def _lines_markdown(self, obj, **kwargs):
if item.ref:
yield "" # break before reference
yield self.format_ref(item)

# Reference
if item.references:
yield "" # break before reference
Expand Down
5 changes: 0 additions & 5 deletions doorstop/core/tests/docs/.doorstop.yml

This file was deleted.

11 changes: 0 additions & 11 deletions doorstop/core/tests/docs/LLT001.yml

This file was deleted.

11 changes: 0 additions & 11 deletions doorstop/core/tests/docs/LLT002.yml

This file was deleted.

11 changes: 0 additions & 11 deletions doorstop/core/tests/docs/LLT003.yml

This file was deleted.

Loading