From f13e1e2ee22cd3517e2a17b17eefebf3945c6b73 Mon Sep 17 00:00:00 2001 From: Nick Touran Date: Mon, 29 Dec 2025 12:11:38 -0500 Subject: [PATCH] Validate doc type data during build --- documents/_data/doc-types.yaml | 135 ++++++++++++++++++ documents/_data/it-systems.yaml | 4 +- documents/conf.py | 8 +- .../procedures/administration/doc-types.yaml | 16 --- src/nrsk/documents/__init__.py | 0 src/nrsk/documents/validate.py | 19 +++ src/nrsk/models.py | 33 ++++- 7 files changed, 195 insertions(+), 20 deletions(-) create mode 100644 documents/_data/doc-types.yaml delete mode 100644 documents/procedures/administration/doc-types.yaml create mode 100644 src/nrsk/documents/__init__.py create mode 100644 src/nrsk/documents/validate.py diff --git a/documents/_data/doc-types.yaml b/documents/_data/doc-types.yaml new file mode 100644 index 0000000..93ccd98 --- /dev/null +++ b/documents/_data/doc-types.yaml @@ -0,0 +1,135 @@ +# Many of these came from cloverDocumentControlRecords2010 originally +# Other sources include IEC 631355 https://en.wikipedia.org/wiki/IEC_61355 +# and ANSI-N45-2-9-74 https://jetsquality.com/wp-content/uploads/2019/01/ANSI-N45-2-9-74.pdf +# has like >100 record types nicely sorted into use case categories (Design, Procurement, Manufacture, ...) +# I like these the best, but maybe they can be compressed with considering USAGE. + +- name: Calculation + abbrev: CALC + use_cases: Documenting an analysis + record: True + retention: +- name: Design Report + abbrev: DREP + use_cases: Documenting a design + record: True + retention: +- name: Design Review Report + abbrev: DREV + use_cases: Documenting the review of a design + record: True + retention: +- name: System Design Description + abbrev: SDD + use_cases: Describing a system + record: True + retention: +- name: Correspondence + abbrev: CSP + use_cases: Communications + record: False + retention: +- name: Drawing + abbrev: DRW + use_cases: Describing SSCs, includes many engineering deliverables + record: True + retention: +- name: Engineering Change Package + abbrev: ECP + use_cases: Describing a formal change to the system configuration + record: True + retention: +- name: Equipment Data Sheets + abbrev: EDS + use_cases: Define technical requirements and operating boundaries + record: True + retention: +- name: Environmental Qualification Package + abbrev: EQP + use_cases: > + Documents describing environmental qualifications of equipment such as + lab reports, thermal aging analyses, radiation resistance data supporting + 10 CFR 50.49 + record: True + retention: +- name: Form + abbrev: FORM + use_cases: A reusable starting point for other Documents/Records, or for collecting data + record: False + retention: Lifetime + notes: Forms are blank documents. +- name: Instructions + abbrev: INSTR + use_cases: Explanations of how to use systems or equipment + record: True + retention: +- name: Native File + abbrev: NTV + use_cases: A native file i.e. from a proprietary authoring software + record: False + notes: > + Native files are kept for ease of revision. They may also be kept as + additional file attachment alongside the document/record. +- name: Policy + abbrev: POL + use_cases: A policy + record: True +- name: Business Practice/Desk Guide + abbrev: BPDG + record: False +- name: Procedure + abbrev: PROC + use_cases: Defining and dictating how work is done + record: True + retention: Lifetime +- name: Procurement + abbrev: PCMT + use_cases: Related to purchases + record: True + retention: Lifetime +- name: Program Manual/Plan + abbrev: PMAN + use_cases: > + High-level governance documents that describes how the plant will manage + a specific program area (e.g., Radiation Protection, In-Service + Inspection, or Fire Protection). + record: True + retention: Lifetime +- name: Quality Classification List + abbrev: QLST + use_cases: Categorizes every SSC based on importance to safety + record: True + retention: +- name: Radiation Protection Survey + abbrev: RPS + record: True + retention: +- name: Records Transmittal Instructions/Indexing Guide + abbrev: RTI + record: True + retention: +- name: Regulatory Documents + abbrev: REG + use_cases: Safety Analysis Report, Technical Specifications, etc. + record: True + retention: +- name: Setpoints + abbrev: SET + record: True + retention: +- name: Specifications + abbrev: SPEC + record: True + retention: +- name: Training + abbrev: TRN + record: True + retention: +- name: Vendor Drawings + abbrev: VDRW + record: True + retention: +- name: Vendor Information + abbrev: VNFO + record: True + retention: diff --git a/documents/_data/it-systems.yaml b/documents/_data/it-systems.yaml index eed5cf5..c3d6881 100644 --- a/documents/_data/it-systems.yaml +++ b/documents/_data/it-systems.yaml @@ -4,11 +4,11 @@ RMDC: - name: NukeVault description: Specialized commercial records management system - use-cases: Storing Documents and Records generated during design of Project X + use_cases: Storing Documents and Records generated during design of Project X location: https://nukevault.opennucleonics.org - name: Supplier Portal description: A place where our suppliers can get documents - use-cases: External suppliers send documents/records to us + use_cases: External suppliers send documents/records to us location: Online Data Management: - name: Data Dictionary diff --git a/documents/conf.py b/documents/conf.py index 35d320d..28b41f2 100644 --- a/documents/conf.py +++ b/documents/conf.py @@ -10,12 +10,14 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # +import datetime import os import sys SRC = os.path.abspath("../src") sys.path.insert(0, SRC) -import datetime + +from nrsk.documents.validate import validate_doc_types # -- Project information ----------------------------------------------------- company_name = "Applied Maritime Sciences, LLC" @@ -198,3 +200,7 @@ intersphinx_mapping = { "pydantic": ("https://docs.pydantic.dev/latest", None), "python": ("https://docs.python.org/3", None), } + + +def setup(app): + app.connect("builder-inited", validate_doc_types) diff --git a/documents/procedures/administration/doc-types.yaml b/documents/procedures/administration/doc-types.yaml deleted file mode 100644 index 4314e58..0000000 --- a/documents/procedures/administration/doc-types.yaml +++ /dev/null @@ -1,16 +0,0 @@ -- name: Calculation - abbrev: CALC - use-cases: Documenting an analysis - record: False - retention: Varies -- name: Procedure - abbrev: PROC - use-cases: Defining and dictating how work is done - record: False - retention: Lifetime -- name: Form - abbrev: FORM - use-cases: Providing evidence of tasks that were done - record: True - retention: Lifetime - diff --git a/src/nrsk/documents/__init__.py b/src/nrsk/documents/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/nrsk/documents/validate.py b/src/nrsk/documents/validate.py new file mode 100644 index 0000000..40f1a50 --- /dev/null +++ b/src/nrsk/documents/validate.py @@ -0,0 +1,19 @@ +""" +Validate document data during build. + +In particular, check doc types. +""" + +import pathlib + +import yaml + +from nrsk.models import InformationTypes + + +def validate_doc_types(app): + """Ensure doc type data is valid.""" + fpath = pathlib.Path(app.srcdir) / "_data" / "doc-types.yaml" + with open(fpath) as f: + data = yaml.safe_load(f) + data = InformationTypes.validate_python(data) diff --git a/src/nrsk/models.py b/src/nrsk/models.py index 6f4ca81..1404800 100644 --- a/src/nrsk/models.py +++ b/src/nrsk/models.py @@ -48,6 +48,7 @@ from typing import Annotated, Any from pydantic import ( AnyUrl, BaseModel, + ConfigDict, EmailStr, Field, PositiveInt, @@ -209,6 +210,26 @@ class ITSystem(BaseModel): quality_related: bool +class InformationType(BaseModel): + """A type/kind/class of Information, Document, or Record.""" + + model_config = ConfigDict(extra="forbid") + + name: str + abbrev: str + examples: list[str] | None = None + description: str = "" + retention: str | None = "" + record: bool = True + use_cases: str = "" + notes: str = "" + parent: InformationType | None = None + + +InformationTypes = TypeAdapter(list[InformationType]) +"""A list of document types.""" + + class Document(BaseModel): """ Data dictionary entry for Documents and Records. @@ -363,6 +384,9 @@ class Document(BaseModel): Retention plans define how long the document or record is to be kept before it is destroyed. + + .. note:: May want this to actually be a timedelta + """ LIFETIME = "LIFETIME" @@ -411,6 +435,10 @@ class Document(BaseModel): description="Filenames of files attached to this Document. Main file should be the first.", default=[], ) + file_notes: list[str] = Field( + description="Short description of each file represented in filenames.", + default=[], + ) checksums: list[str] = Field( description="SHA-256 checksum of each file for data integrity", default=[] ) @@ -420,9 +448,12 @@ class Document(BaseModel): easier periodic re-verification of large data libraries.""" physical_location: str | None = Field( - description="Location of a media when not stored as an electronic file.", + description="Location of a media (only valid when not stored as an electronic file).", default=None, ) + notes: str = Field( + description="Additional information about the Document/Record", default="" + ) @field_validator("type", mode="after") @classmethod