Skip to content

Commit

Permalink
Re-factor collections to work for classifications
Browse files Browse the repository at this point in the history
  • Loading branch information
aeslaughter authored and jain651 committed Apr 19, 2021
1 parent 740f7ce commit 468421b
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 381 deletions.
246 changes: 97 additions & 149 deletions python/MooseDocs/extensions/sqa.py

Large diffs are not rendered by default.

26 changes: 2 additions & 24 deletions python/MooseDocs/test/content/extensions/sqa.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,12 @@

## Collections

### All Collections

!sqa collections category=demo

### Specific Collection

!sqa collections category=demo items=Andrew
!sqa requirements collections=Andrew category=demo

## Types

### All Types

!sqa types category=demo

### Specific Type

!sqa types category=demo items=Andrew

## Custom Matrix

!sqa requirements category=demo prefix=C

## Collections

### Collection List

!sqa collections-list

### Requirement Collection

!sqa collections category=demo
!sqa requirements category=demo types=TestType
196 changes: 50 additions & 146 deletions python/MooseDocs/test/extensions/test_sqa.py

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions python/MooseDocs/test/moosedocs.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@
# run from any location. CIVET runs 'modules/doc/moosedocs.py check' from the root directory.
os.chdir(os.path.abspath(os.path.dirname(__file__)))

import moosesqa
moosesqa.MOOSESQA_CLASSIFICATION['CUSTOM'] = ('C', "This is a custom classification")

from MooseDocs import main
if __name__ == '__main__':
sys.exit(main.run())
2 changes: 2 additions & 0 deletions python/MooseDocs/test/tree/demo
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
classification = custom
[r0]
requirement = "Tree One"
type = TestType
[]
[r1]
requirement = "Tree Two"
Expand All @@ -13,6 +14,7 @@
requirement = "Tree Three"
design = "bibtex.md"
prereq = r1
type = TestType
[]
[r3]
requirement = "Tree Four"
Expand Down
11 changes: 1 addition & 10 deletions python/moosesqa/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,4 @@
from .Requirement import Requirement, Detail, TestSpecification
from .LogHelper import LogHelper

MOOSESQA_COLLECTIONS = dict()
MOOSESQA_COLLECTIONS['FAILURE_ANALYSIS'] = "Requirements that perform check for simulation " \
"integrity such as error handling and convergence " \
"failures."

MOOSESQA_CLASSIFICATION = dict()
MOOSESQA_CLASSIFICATION['FUNCTIONAL'] = ('F', "Requirements that define uses cases and are correct, unambiguous, complete, consistent, verifiable, and traceable.")
MOOSESQA_CLASSIFICATION['USABILITY'] = ('U', "Requirements for the system that include measurable effectiveness, efficiency, and satisfaction criteria in specific contexts of use.")
MOOSESQA_CLASSIFICATION['PERFORMANCE'] = ('P', "Requirements to define the critical performance conditions and associated capabilities.")
MOOSESQA_CLASSIFICATION['SYSTEM'] = ('S', "Requirements for interfaces among system elements and with external entities.")
MOOSESQA_COLLECTIONS = {'FUNCTIONAL', 'USABILITY', 'PERFORMANCE', 'SYSTEM', 'FAILURE_ANALYSIS'}
20 changes: 3 additions & 17 deletions python/moosesqa/check_requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ def check_requirements(requirements, file_list=None, color_text=True, allowed_co
kwargs.setdefault('log_extra_issues', log_default)
kwargs.setdefault('log_extra_collections', log_default)
kwargs.setdefault('log_invalid_collection', log_default)
kwargs.setdefault('log_invalid_classification', log_default)
kwargs.setdefault('log_issue_format', log_default)
kwargs.setdefault('log_design_files', log_default)
kwargs.setdefault('log_validation_files', log_default)
Expand All @@ -89,20 +88,14 @@ def check_requirements(requirements, file_list=None, color_text=True, allowed_co

# Setup allowed collections
if allowed_collections is None:
allowed_collections = set(moosesqa.MOOSESQA_COLLECTIONS.keys())

# Setup allowed
if allowed_classifications is None:
allowed_classifications = set(k.upper() for k in moosesqa.MOOSESQA_CLASSIFICATION.keys())
else:
allowed_classifications = set(c.upper() for c in allowed_classifications)
allowed_collections = set(moosesqa.MOOSESQA_COLLECTIONS)

# Storage container for duplicate detection
requirement_dict = collections.defaultdict(set)

# Check each Requirement object for deficiencies
for req in requirements:
_check_requirement(req, logger, file_list, allowed_collections, allowed_classifications)
_check_requirement(req, logger, file_list, allowed_collections)
if req.requirement is not None:
key = [req.requirement]
for detail in req.details:
Expand All @@ -123,7 +116,7 @@ def check_requirements(requirements, file_list=None, color_text=True, allowed_co

return logger

def _check_requirement(req, logger, file_list, allowed_collections, allowed_classifications):
def _check_requirement(req, logger, file_list, allowed_collections):
"""Opens tests specification and extracts requirement items."""

# Test for 'deprecated' with other parameters
Expand Down Expand Up @@ -264,13 +257,6 @@ def _check_requirement(req, logger, file_list, allowed_collections, allowed_clas
msg = 'Invalid collection names found: {}'.format(' '.join(wrong))
LogHelper.log(logger, 'log_invalid_collection', msg)

# Test for invalid 'classification'
if (allowed_classifications is not None) and (req.classification is not None):
if req.classification.upper() not in allowed_classifications:
msg = 'Invalid classification found: {}'.format(req.classification)
LogHelper.log(logger, 'log_invalid_classification', msg)


def _has_file(filename, file_list):
"""Test if the filename is located in the list"""
for f in file_list:
Expand Down
23 changes: 6 additions & 17 deletions python/moosesqa/get_requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,24 @@ def get_requirements_from_tests(directories, specs):
out[group] = get_requirements_from_file(filename)
return out

def number_requirements(requirement_dict, prefix, category):
def number_requirements(requirement_dict, category):
"""
Apply a number label to the requirements.
Input:
requirement_dict[dict]: Container of Requirement objects, as returned from get_requirement_from_tests.
prefix[str]: Number prefix (e.g., 'F')
requirement_dict[dict]: Container of Requirement objects, as returned from
get_requirement_from_tests.
category[int]: Category index to apply to label.
The format of the number is <prefix><category>.<group>.<number>, e.g., F3.2.1. The group
The format of the number is <category>.<group>.<number>, e.g., 3.2.1. The group
is the indexed according to the supplied dict keys.
IMPORTANT: These numbers are not designed to be referenced in any manner, they are simply applied
for organizational purposes.
"""
for i, requirements in enumerate(requirement_dict.values()):
for j, req in enumerate(requirements):
req.label = "{}{}.{}.{}".format(prefix, category, i+1, j+1)
req.label = "{}.{}.{}".format(category, i+1, j+1)

def get_requirements_from_file(filename):
"""
Expand Down Expand Up @@ -77,15 +77,12 @@ def get_requirements_from_file(filename):
deprecated_line = root.children[0].line('deprecated', None)
collections = root.children[0].get('collections', None)
collections_line = root.children[0].line('collections', None)
classification = root.children[0].get('classification', None)
classification_line = root.children[0].line('classification', None)

for child in root.children[0]:
req = _create_requirement(child, filename,
design, design_line,
issues, issues_line,
collections, collections_line,
classification, classification_line,
deprecated, deprecated_line)
requirements.append(req)

Expand Down Expand Up @@ -169,8 +166,7 @@ def _create_specification(child, name, filename):
return spec

def _create_requirement(child, filename, design, design_line, issues, issues_line,
collections, collections_line, classification, classification_line,
deprecated, deprecated_line):
collections, collections_line, deprecated, deprecated_line):

# Create the Requirement object
req = Requirement(name=child.name,
Expand Down Expand Up @@ -200,11 +196,6 @@ def _create_requirement(child, filename, design, design_line, issues, issues_lin
req.collections = set(collections.strip().split()) if (collections is not None) else None
req.collections_line = child.line('collections', collections_line)

# "classification" parameter
classification = child.get('classification', classification if (classification is not None) else None)
req.classification = classification.strip().upper() if (classification is not None) else None
req.classification_line = child.line('classification', classification_line)

# V&V document
verification = child.get('verification', None)
req.verification = verification.strip().split() if (verification is not None) else None
Expand Down Expand Up @@ -238,6 +229,4 @@ def _create_detail(child, filename):
req.deprecated_line = child.line('deprecated', None)
req.collections = child.get('collections', None)
req.collections_line = child.line('collections', None)
req.classification = child.get('classification', None)
req.classification_line = child.line('classification', None)
return req
11 changes: 0 additions & 11 deletions python/moosesqa/test/test_check_requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,16 +247,5 @@ def testCollections(self):
moosesqa.check_requirements([req0, req1], allowed_collections={'test'})
self.assertIn("Invalid collection names found: test2", cm.output[0])

def testClassification(self):
req0 = moosesqa.Requirement(name='req0',
requirement='requirement', design=['Diffusion.md'], issues=['#1234'],
specification=moosesqa.TestSpecification(), classification='test')
req1 = moosesqa.Requirement(name='req1',
requirement='requirement2', design=['Diffusion.md'], issues=['#1234'],
specification=moosesqa.TestSpecification(), classification='test2')
with self.assertLogs(level='ERROR') as cm:
moosesqa.check_requirements([req0, req1], allowed_classifications={'test'})
self.assertIn("Invalid classification found: test2", cm.output[0])

if __name__ == '__main__':
unittest.main(verbosity=2)
15 changes: 11 additions & 4 deletions python/moosesqa/test/test_get_requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ def testBase(self):
r2 = Requirement()
req = collections.OrderedDict(a=[r0, r1], b=[r2])

number_requirements(req, 'R', 1980)
self.assertEqual(r0.label, 'R1980.1.1')
self.assertEqual(r1.label, 'R1980.1.2')
self.assertEqual(r2.label, 'R1980.2.1')
number_requirements(req, 1980)
self.assertEqual(r0.label, '1980.1.1')
self.assertEqual(r1.label, '1980.1.2')
self.assertEqual(r2.label, '1980.2.1')

class TestFindFile(unittest.TestCase):
def testExact(self):
Expand Down Expand Up @@ -319,5 +319,12 @@ def testRequirementWithDetails(self):
r = req['test_get_requirements_spec0'][18]
self.assertEqual(r.prerequisites, {'prereq_first', 'prereq_group/a'})

class TestRequirementCollections(unittest.TestCase):
def testAvailable(self):
# Help avoid this being changed without docs being updated
import moosesqa
self.assertEqual(moosesqa.MOOSESQA_COLLECTIONS, {'FUNCTIONAL', 'USABILITY', 'PERFORMANCE', 'SYSTEM', 'FAILURE_ANALYSIS'},
"If you are adding to moosesqa.MOOSESQA_COLLECTIONS make sure you update associated documentation.")

if __name__ == '__main__':
unittest.main(verbosity=2)

0 comments on commit 468421b

Please sign in to comment.