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

REFACT: StructuralRequirment -> Requirement #30

Merged
merged 4 commits into from
May 8, 2024
Merged
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
2 changes: 1 addition & 1 deletion eark_validator/ipxml/schematron.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def __init__(self, sch_path: str=None):
try:
self._schematron = Schematron(file=self._path, store_schematron=True, store_report=True)
except (ET.SchematronParseError, KeyError) as ex:
ex_mess = ex.__doc__ if isinstance(ex, KeyError) else ex.error_log.last_error.message
ex_mess = ex.__doc__ if isinstance(ex, KeyError) else ex.error_log.last_error.message # pylint: disable=E1101
subject = 'Schematron' if isinstance(ex, ET.SchematronParseError) else 'XML'
raise ValueError(f'Rules file is not valid {subject}: {sch_path}. {ex_mess}') from ex

Expand Down
2 changes: 1 addition & 1 deletion eark_validator/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
#
"""
E-ARK : Information Package Validation
Information Package API model types
Information Package model types and constants.
"""
# import models into model package
from .checksum import Checksum, ChecksumAlg
Expand Down
4 changes: 2 additions & 2 deletions eark_validator/model/checksum.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class ChecksumAlg(str, Enum):
SHA512 = 'SHA-512'

@classmethod
def from_string(cls, value: str) -> Optional['ChecksumAlg']:
def from_string(cls, value: str) -> 'ChecksumAlg':
"""
Obtain a ChecksumAlg from a string identifier.

Expand All @@ -62,7 +62,7 @@ def from_string(cls, value: str) -> Optional['ChecksumAlg']:
for algorithm in ChecksumAlg:
if search_value in [ algorithm.name, algorithm.value ]:
return algorithm
return None
raise ValueError(f'No ChecksumAlg with id: {value}')

@classmethod
def get_implementation(cls, algorithm: 'ChecksumAlg' = SHA1):
Expand Down
42 changes: 42 additions & 0 deletions eark_validator/model/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# flake8: noqa
#
# E-ARK Validation
# Copyright (C) 2019
# All rights reserved.
#
# Licensed to the E-ARK project under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The E-ARK project licenses
# this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
"""
E-ARK : Information Package Validation Model constants
Constant values for the model package
"""
METS = 'mets'
METS_FILE = 'METS.xml'
MIME_DEFAULT = 'application/octet-stream'
MAY = 'MAY'
SHOULD = 'SHOULD'
MUST = 'MUST'
UNKNOWN = 'Unknown'
INFORMATION = 'Information'
WARNING = 'Warning'
ERROR = 'Error'
NOTWELLFORMED = 'Not Well Formed'
WELLFORMED = 'Well Formed'
PACKAGE = 'Package'
7 changes: 4 additions & 3 deletions eark_validator/model/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from pydantic import BaseModel

from .checksum import Checksum
from .constants import METS, UNKNOWN, PACKAGE # pylint: disable=W0611

class ManifestEntry(BaseModel):
path : Path | str
Expand All @@ -43,11 +44,11 @@ class ManifestSummary(BaseModel):
@unique
class SourceType(str, Enum):
"""Enum covering information package validation statuses."""
UNKNOWN = 'UNKNOWN'
UNKNOWN = UNKNOWN.upper()
# Information level, possibly not best practise
METS = 'METS'
METS = METS.upper()
# Non-fatal issue that should be corrected
PACKAGE = 'PACKAGE'
PACKAGE = PACKAGE.upper()

class Manifest(BaseModel):
source: SourceType = SourceType.UNKNOWN
Expand Down
3 changes: 2 additions & 1 deletion eark_validator/model/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from pydantic import BaseModel, StringConstraints

from .checksum import Checksum
from .constants import MIME_DEFAULT

class EntryType(str, Enum):
FILE = 'file'
Expand All @@ -40,7 +41,7 @@ class FileEntry(BaseModel):
type: EntryType = EntryType.FILE
size : int = 0
checksum : Checksum
mimetype : Annotated[ str, StringConstraints(to_lower=True) ] = 'application/octet-stream'
mimetype : Annotated[ str, StringConstraints(to_lower=True) ] = MIME_DEFAULT

class MetsRoot(BaseModel):
namespaces: dict[str, str] = {}
Expand Down
19 changes: 6 additions & 13 deletions eark_validator/model/specifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,16 @@

from pydantic import BaseModel, computed_field

from .constants import MAY, SHOULD, MUST

@unique
class Level(str, Enum):
"""Enum covering information package validation statuses."""
MAY = 'MAY'
MAY = MAY
# Package has basic parse / structure problems and can't be validated
SHOULD = 'SHOULD'
SHOULD = SHOULD
# Package structure is OK
MUST = 'MUST'
MUST = MUST

@staticmethod
def from_string(level: str) -> 'Level':
Expand All @@ -50,27 +51,19 @@ def from_string(level: str) -> 'Level':
return item
raise ValueError(f'No Level with value {level}')

class StructuralRequirement(BaseModel):
"""Encapsulates a structural requirement."""
id: str
level: Level = Level.MUST
message: Optional[str] = None

class Requirement(BaseModel):
"""Encapsulates a requirement."""
id: str
name: str
level: Level = Level.MUST
xpath: Optional[str] = None
cardinality: Optional[str] = None
message: Optional[str] = None

class Specification(BaseModel):
"""Stores the vital facts and figures an IP specification."""
title: str
url: Optional[str] = None
version: str
date: str
structural_requirements: List[StructuralRequirement] = []
structural_requirements: List[Requirement] = []
requirements: Dict[str, List[Requirement]] = {}

@computed_field
Expand Down
20 changes: 11 additions & 9 deletions eark_validator/model/validation_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,21 @@

from pydantic import BaseModel

from eark_validator.model.package_details import InformationPackage
from eark_validator.model.specifications import Level
from .package_details import InformationPackage
from .specifications import Level
from .constants import (
UNKNOWN, INFORMATION, WARNING, ERROR, WELLFORMED, NOTWELLFORMED)

@unique
class Severity(str, Enum):
"""Enum covering information package validation statuses."""
UNKNOWN = 'Unknown'
UNKNOWN = UNKNOWN
# Information level, possibly not best practise
INFORMATION = 'Information'
INFORMATION = INFORMATION
# Non-fatal issue that should be corrected
WARNING = 'Warning'
WARNING = WARNING
# Error level message means invalid package
ERROR = 'Error'
ERROR = ERROR

@classmethod
def from_id(cls, severity_id: str) -> Optional['Severity']:
Expand Down Expand Up @@ -90,11 +92,11 @@ class Result(BaseModel):
@unique
class StructureStatus(str, Enum):
"""Enum covering information package validation statuses."""
UNKNOWN = 'Unknown'
UNKNOWN = UNKNOWN
# Package has basic parse / structure problems and can't be validated
NOTWELLFORMED = 'Not Well Formed'
NOTWELLFORMED = NOTWELLFORMED
# Package structure is OK
WELLFORMED = 'Well Formed'
WELLFORMED = WELLFORMED

class StructResults(BaseModel):
status: StructureStatus = StructureStatus.UNKNOWN
Expand Down
13 changes: 6 additions & 7 deletions eark_validator/specifications/specification.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from eark_validator.ipxml.namespaces import Namespaces
from eark_validator.ipxml.resources import profiles
from eark_validator.ipxml.schema import METS_PROF_SCHEMA
from eark_validator.model.specifications import Requirement, Specification, StructuralRequirement
from eark_validator.model.specifications import Requirement, Specification
from eark_validator.specifications.struct_reqs import REQUIREMENTS
from eark_validator.specifications.struct_reqs import Level

Expand Down Expand Up @@ -126,30 +126,29 @@ def from_element(req_ele: ET.Element) -> Requirement:

class StructuralRequirements():
@staticmethod
def from_rule_no(rule_no: int) -> StructuralRequirement:
def from_rule_no(rule_no: int) -> Requirement:
"""Create an StructuralRequirement from a numerical rule id and a sub_message."""
item = REQUIREMENTS.get(rule_no)
if not item:
raise ValueError(f'No rule with number {rule_no}')
return StructuralRequirements.from_dictionary(item)

@staticmethod
def from_dictionary(item: dict[str, str]) -> StructuralRequirement:
def from_dictionary(item: dict[str, str]) -> Requirement:
"""Create an StructuralRequirement from dictionary item and a sub_message."""
return StructuralRequirement.model_validate({
return Requirement.model_validate({
'id': item.get('id'),
'level': item.get('level'),
'message': item.get('message')
})

@staticmethod
def get_requirements() -> list[StructuralRequirement]:
def get_requirements() -> list[Requirement]:
reqs = []
for req in REQUIREMENTS.values():
reqs.append(StructuralRequirement.model_validate(req))
reqs.append(Requirement.model_validate(req))
return reqs


@unique
class EarkSpecifications(str, Enum):
"""Enumeration of E-ARK specifications."""
Expand Down
2 changes: 1 addition & 1 deletion eark_validator/structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ def get_test_results(self) -> StructResults:

def get_representations(self) -> List[Representation]:
reps: List[Representation] = []
for rep in self.representations.keys():
for rep in self.representations: # pylint: disable=C0201
reps.append(Representation.model_validate({ 'name': rep }))
return reps

Expand Down
6 changes: 3 additions & 3 deletions tests/manifests_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ def test_alg_items(self):
self.assertIsNotNone(alg)

def test_missing_alg(self):
"""Test that a missing algorithm returns None."""
alg = ChecksumAlg.from_string('NOT_AN_ALGORITHM')
self.assertIsNone(alg)
"""Test that a missing algorithm raises a ValueError."""
with self.assertRaises(ValueError):
ChecksumAlg.from_string('NOT_AN_ALGORITHM')

def test_missing_implementation(self):
"""Test that a missing algorithm returns None."""
Expand Down
9 changes: 6 additions & 3 deletions tests/specification_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,12 @@
from lxml import etree as ET

from importlib_resources import files
from eark_validator.model.specifications import Specification, StructuralRequirement
from eark_validator.model.specifications import Specification, Requirement

from eark_validator.specifications.specification import EarkSpecifications, Specifications, StructuralRequirements
from eark_validator.specifications.specification import (
EarkSpecifications,
Specifications,
StructuralRequirements)
import tests.resources.xml as XML
from eark_validator.ipxml.resources import profiles

Expand Down Expand Up @@ -68,7 +71,7 @@ def test_from_rule_no_str(self):
StructuralRequirements.from_rule_no('1')

def test_from_rule_no(self):
req: StructuralRequirement = StructuralRequirements.from_rule_no(1)
req: Requirement = StructuralRequirements.from_rule_no(1)
self.assertEqual(req.id, 'CSIPSTR1')

class EarkSpecificationsTest(unittest.TestCase):
Expand Down
Loading