Skip to content

Equipment¤

Equipment dataclass ¤

Equipment(
    entered_by: str = "",
    checked_by: str = "",
    checked_date: date | None = None,
    alias: str = "",
    keywords: tuple[str, ...] = (),
    id: str = "",
    manufacturer: str = "",
    model: str = "",
    serial: str = "",
    description: str = "",
    specifications: Specifications = Specifications(),
    location: str = "",
    status: Status = Active,
    loggable: bool = False,
    traceable: bool = False,
    calibrations: tuple[Measurand, ...] = (),
    maintenance: Maintenance = Maintenance(),
    alterations: tuple[Alteration, ...] = (),
    firmware: tuple[Firmware, ...] = (),
    specified_requirements: SpecifiedRequirements = SpecifiedRequirements(),
    reference_materials: ReferenceMaterials = ReferenceMaterials(),
    quality_manual: QualityManual = QualityManual(),
    connection: Connection | None = None,
)

Represents the equipment element in an equipment register.

Parameters:

Name Type Description Default
entered_by str

The name of the person who initially entered the <equipment> element in the register.

''
checked_by str

The name of the person who checked the information in the <equipment> element.

''
checked_date date | None

The date that the information in the <equipment> element was last checked.

None
alias str

An alternative name to associate with the equipment.

''
keywords tuple[str, ...]

Keywords that describe the equipment.

()
id str

Identity in an equipment register.

''
manufacturer str

Name of manufacturer.

''
model str

Manufacturer's model number (or type identification).

''
serial str

Manufacturer's serial number (or other unique identification).

''
description str

A short description about the equipment.

''
specifications Specifications

Specifications provided by the manufacturer of the equipment.

Specifications()
location str

The usual location (laboratory) that the equipment is found in.

''
status Status

The status of the equipment is an indication of whether the equipment is active (in use) or inactive (not in use).

Active
loggable bool

Whether measurements from the equipment should be logged. Equipment that monitor (for example) pressure, temperature or humidity of a laboratory environment are considered as loggable.

False
traceable bool

Whether the equipment is used for a traceable measurement.

False
calibrations tuple[Measurand, ...]

The calibration history.

()
maintenance Maintenance

The maintenance history and maintenance plan.

Maintenance()
alterations tuple[Alteration, ...]

The alteration history.

()
firmware tuple[Firmware, ...]

The firmware version history.

()
specified_requirements SpecifiedRequirements

Verification that equipment conforms with specified requirements before being placed or returned into service.

SpecifiedRequirements()
reference_materials ReferenceMaterials

Documentation of reference materials, results, acceptance criteria, relevant dates and the period of validity.

ReferenceMaterials()
quality_manual QualityManual

Information that is specified in Section 4.3.6 of the MSL Quality Manual.

QualityManual()
connection Connection | None

The connection to the equipment for computer control.

None

alias class-attribute instance-attribute ¤

alias: str = ''

An alternative name to associate with the equipment.

alterations class-attribute instance-attribute ¤

alterations: tuple[Alteration, ...] = ()

The alteration history.

calibrations class-attribute instance-attribute ¤

calibrations: tuple[Measurand, ...] = ()

The calibration history.

checked_by class-attribute instance-attribute ¤

checked_by: str = ''

The name of the person who checked the information in the <equipment> element.

checked_date class-attribute instance-attribute ¤

checked_date: date | None = None

The date that the information in the <equipment> element was last checked.

connection class-attribute instance-attribute ¤

connection: Connection | None = None

The connection to use for computer control.

description class-attribute instance-attribute ¤

description: str = ''

A short description about the equipment.

entered_by class-attribute instance-attribute ¤

entered_by: str = ''

The name of the person who initially entered the <equipment> element in the register.

firmware class-attribute instance-attribute ¤

firmware: tuple[Firmware, ...] = ()

The firmware version history.

id class-attribute instance-attribute ¤

id: str = ''

Identity in an equipment register.

keywords class-attribute instance-attribute ¤

keywords: tuple[str, ...] = ()

Keywords that describe the equipment.

location class-attribute instance-attribute ¤

location: str = ''

The usual location (laboratory) that the equipment is found in.

loggable class-attribute instance-attribute ¤

loggable: bool = False

Whether measurements from the equipment should be logged.

maintenance class-attribute instance-attribute ¤

maintenance: Maintenance = field(default_factory=Maintenance)

The maintenance history and maintenance plan.

manufacturer class-attribute instance-attribute ¤

manufacturer: str = ''

Name of manufacturer.

model class-attribute instance-attribute ¤

model: str = ''

Manufacturer's model number (or type identification).

quality_manual class-attribute instance-attribute ¤

quality_manual: QualityManual = field(default_factory=QualityManual)

Information that is specified in Section 4.3.6 of the MSL Quality Manual.

reference_materials class-attribute instance-attribute ¤

reference_materials: ReferenceMaterials = field(
    default_factory=ReferenceMaterials
)

Documentation of reference materials, results, acceptance criteria, relevant dates and the period of validity.

serial class-attribute instance-attribute ¤

serial: str = ''

Manufacturer's serial number (or other unique identification).

specifications class-attribute instance-attribute ¤

specifications: Specifications = field(default_factory=Specifications)

Specifications provided by the manufacturer of the equipment.

specified_requirements class-attribute instance-attribute ¤

specified_requirements: SpecifiedRequirements = field(
    default_factory=SpecifiedRequirements
)

Verification that equipment conforms with specified requirements before being placed or returned into service.

status class-attribute instance-attribute ¤

status: Status = Active

The status of the equipment is an indication of whether the equipment is active (in use) or inactive (not in use).

traceable class-attribute instance-attribute ¤

traceable: bool = False

Whether the equipment is used for a traceable measurement.

connect ¤

connect() -> Any

Connect to the equipment for computer control.

The following sequence is used to decide how the connection is established.

  1. If a Connection has a specified backend, that is not MSL, that Backend is used.
  2. If the Equipment has the appropriate manufacturer and model values for one of the Resources, that Resource is used.
  3. If the Connection has an address that is supported by one of the Interfaces, that Interface is used.
Source code in src/msl/equipment/schema.py
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
def connect(self) -> _Any:  # noqa: ANN401
    """Connect to the equipment for computer control.

    The following sequence is used to decide how the connection is established.

    1. If a [Connection][] has a specified [backend][msl.equipment.schema.Connection.backend],
       that is not `MSL`, that [Backend][connections-backend] is used.
    2. If the [Equipment][] has the appropriate [manufacturer][msl.equipment.schema.Equipment.manufacturer]
       and [model][msl.equipment.schema.Equipment.model] values for one of the [Resources][],
       that Resource is used.
    3. If the [Connection][] has an [address][msl.equipment.schema.Connection.address]
       that is supported by one of the [Interfaces][connections-interfaces], that Interface is used.
    """
    if self.connection is None:
        # Cannot simply call super(). Must specify (type, object) since the dataclass uses slots=True
        super(Equipment, self).__setattr__("connection", connections[self.id])  # noqa: UP008

    return _find_interface_class(self)(self)

from_xml classmethod ¤

from_xml(element: Element[str]) -> Equipment

Convert an XML element into an Equipment instance.

Parameters:

Name Type Description Default
element Element[str]

An equipment XML element from an equipment register.

required

Returns:

Type Description
Equipment

The Equipment instance.

Source code in src/msl/equipment/schema.py
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
@classmethod
def from_xml(cls, element: Element[str]) -> Equipment:
    """Convert an XML element into an [Equipment][msl.equipment.schema.Equipment] instance.

    Args:
        element: An [equipment][type_equipment] XML element from an equipment register.

    Returns:
        The [Equipment][msl.equipment.schema.Equipment] instance.
    """
    # Schema forces order
    a = element.attrib
    return cls(
        entered_by=a["enteredBy"],
        checked_by=a.get("checkedBy", ""),
        checked_date=None if not a.get("checkedDate") else _date.fromisoformat(a["checkedDate"]),
        alias=a.get("alias", ""),
        keywords=tuple(a.get("keywords", "").split()),
        id=element[0].text or "",
        manufacturer=element[1].text or "",
        model=element[2].text or "",
        serial=element[3].text or "",
        description=element[4].text or "",
        specifications=Specifications.from_xml(element[5]),
        location=element[6].text or "",
        status=Status(element[7].text),
        loggable=element[8].text in {"1", "true"},
        traceable=element[9].text in {"1", "true"},
        calibrations=tuple(Measurand.from_xml(e) for e in element[10]),
        maintenance=Maintenance.from_xml(element[11]),
        alterations=tuple(Alteration.from_xml(e) for e in element[12]),
        firmware=tuple(Firmware.from_xml(e) for e in element[13]),
        specified_requirements=SpecifiedRequirements.from_xml(element[14]),
        reference_materials=ReferenceMaterials.from_xml(element[15]),
        quality_manual=QualityManual.from_xml(element[16]),
    )

latest_performance_check ¤

latest_performance_check(
    *, quantity: str = "", name: str = ""
) -> LatestPerformanceCheck | None

Returns the latest performance check.

Parameters:

Name Type Description Default
quantity str

The measurand quantity.

''
name str

The component name.

''

Returns:

Type Description
LatestPerformanceCheck | None

The LatestPerformanceCheck for the specified quantity and name. If the equipment has only one measurand and only one component then you do not need to specify a value for the quantity and name. Returns None if there are no performance checks that match the quantity and name criteria or if the equipment does not have performance checks entered in the register.

Source code in src/msl/equipment/schema.py
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
def latest_performance_check(self, *, quantity: str = "", name: str = "") -> LatestPerformanceCheck | None:
    """Returns the latest performance check.

    Args:
        quantity: The measurand [quantity][msl.equipment.schema.Measurand.quantity].
        name: The component [name][msl.equipment.schema.Component.name].

    Returns:
        The [LatestPerformanceCheck][msl.equipment.schema.LatestPerformanceCheck] for the specified
            `quantity` and `name`. If the equipment has only one _measurand_ and only one _component_
            then you do not need to specify a value for the `quantity` and `name`. Returns `None` if
            there are no performance checks that match the `quantity` and `name` criteria or if
            the equipment does not have performance checks entered in the register.
    """
    return _latest(items=list(self.latest_performance_checks()), quantity=quantity, name=name)

latest_performance_checks ¤

latest_performance_checks() -> Iterator[LatestPerformanceCheck]

Yields the latest performance check for every measurand and component.

Yields:

Type Description
LatestPerformanceCheck

The latest performance check.

Source code in src/msl/equipment/schema.py
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
def latest_performance_checks(self) -> Iterator[LatestPerformanceCheck]:
    """Yields the latest performance check for every _measurand_ and _component_.

    Yields:
        The latest performance check.
    """
    default = _date(1875, 5, 20)
    for m in self.calibrations:
        for c in m.components:
            latest = default
            check: PerformanceCheck | None = None
            for pc in c.performance_checks:
                if pc.completed_date > latest:
                    check = pc
                    latest = pc.completed_date

            if check is not None:
                yield LatestPerformanceCheck(
                    calibration_interval=m.calibration_interval,
                    name=c.name,
                    next_calibration_date=_future_date(latest, m.calibration_interval),
                    quantity=m.quantity,
                    completed_date=check.completed_date,
                    competency=check.competency,
                    entered_by=check.entered_by,
                    checked_by=check.checked_by,
                    checked_date=check.checked_date,
                    conditions=check.conditions,
                    cvd_equations=check.cvd_equations,
                    deserialisers=check.deserialisers,
                    equations=check.equations,
                    files=check.files,
                    tables=check.tables,
                )

latest_report ¤

latest_report(
    *,
    quantity: str = "",
    name: str = "",
    date: Literal["issue", "start", "stop"] = "stop"
) -> LatestReport | None

Returns the latest calibration report.

Parameters:

Name Type Description Default
quantity str

The measurand quantity.

''
name str

The component name.

''
date Literal['issue', 'start', 'stop']

Which date in a report to use to determine what latest refers to:

  • issue: Report issue date
  • start: Measurement start date
  • stop: Measurement stop date
'stop'

Returns:

Type Description
LatestReport | None

The LatestReport for the specified quantity and name. If the equipment has only one measurand and only one component then you do not need to specify a value for the quantity and name. Returns None if there are no calibration reports that match the quantity and name criteria or if the equipment does not have calibration reports entered in the register.

Source code in src/msl/equipment/schema.py
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
def latest_report(
    self, *, quantity: str = "", name: str = "", date: Literal["issue", "start", "stop"] = "stop"
) -> LatestReport | None:
    """Returns the latest calibration report.

    Args:
        quantity: The measurand [quantity][msl.equipment.schema.Measurand.quantity].
        name: The component [name][msl.equipment.schema.Component.name].
        date: Which date in a report to use to determine what _latest_ refers to:

            * `issue`: Report issue date
            * `start`: Measurement start date
            * `stop`: Measurement stop date

    Returns:
        The [LatestReport][msl.equipment.schema.LatestReport] for the specified `quantity` and `name`.
            If the equipment has only one _measurand_ and only one _component_ then you do not need
            to specify a value for the `quantity` and `name`. Returns `None` if there are no calibration
            reports that match the `quantity` and `name` criteria or if the equipment does not have
            calibration reports entered in the register.
    """
    return _latest(items=list(self.latest_reports(date=date)), quantity=quantity, name=name)

latest_reports ¤

latest_reports(
    date: Literal["issue", "start", "stop"] = "stop",
) -> Iterator[LatestReport]

Yields the latest calibration report for every measurand and component.

Parameters:

Name Type Description Default
date Literal['issue', 'start', 'stop']

Which date in a report to use to determine what latest refers to:

  • issue: Report issue date
  • start: Measurement start date
  • stop: Measurement stop date
'stop'

Yields:

Type Description
LatestReport

The latest calibration report.

Source code in src/msl/equipment/schema.py
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
def latest_reports(self, date: Literal["issue", "start", "stop"] = "stop") -> Iterator[LatestReport]:
    """Yields the latest calibration report for every _measurand_ and _component_.

    Args:
        date: Which date in a report to use to determine what _latest_ refers to:

            * `issue`: Report issue date
            * `start`: Measurement start date
            * `stop`: Measurement stop date

    Yields:
        The latest calibration report.
    """
    default = _date(1875, 5, 20)
    for m in self.calibrations:
        for c in m.components:
            latest = default
            report: Report | None = None
            for r in c.reports:
                if date == "stop":
                    if r.measurement_stop_date > latest:
                        report = r
                        latest = r.measurement_stop_date
                elif date == "start":
                    if r.measurement_start_date > latest:
                        report = r
                        latest = r.measurement_start_date
                elif r.report_issue_date > latest:
                    report = r
                    latest = r.report_issue_date

            if report is not None:
                yield LatestReport(
                    calibration_interval=m.calibration_interval,
                    name=c.name,
                    next_calibration_date=_future_date(latest, m.calibration_interval),
                    quantity=m.quantity,
                    id=report.id,
                    report_issue_date=report.report_issue_date,
                    measurement_start_date=report.measurement_start_date,
                    measurement_stop_date=report.measurement_stop_date,
                    issuing_laboratory=report.issuing_laboratory,
                    technical_procedure=report.technical_procedure,
                    entered_by=report.entered_by,
                    checked_by=report.checked_by,
                    checked_date=report.checked_date,
                    conditions=report.conditions,
                    acceptance_criteria=report.acceptance_criteria,
                    cvd_equations=report.cvd_equations,
                    deserialisers=report.deserialisers,
                    equations=report.equations,
                    files=report.files,
                    tables=report.tables,
                )

to_xml ¤

to_xml() -> Element[str]

Convert the Equipment class into an XML element.

The connection attribute is not included as an XML element.

Returns:

Type Description
Element[str]

The Equipment as an XML element.

Source code in src/msl/equipment/schema.py
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
def to_xml(self) -> Element[str]:
    """Convert the [Equipment][msl.equipment.schema.Equipment] class into an XML element.

    The [connection][msl.equipment.schema.Equipment.connection] attribute is not included
    as an XML element.

    Returns:
        The [Equipment][msl.equipment.schema.Equipment] as an XML element.
    """
    a = {"enteredBy": self.entered_by}
    if self.checked_by:
        a["checkedBy"] = self.checked_by
    if self.checked_date is not None:
        a["checkedDate"] = self.checked_date.isoformat()
    if self.alias:
        a["alias"] = self.alias
    if self.keywords:
        a["keywords"] = " ".join(self.keywords)

    e = Element("equipment", attrib=a)

    _id = SubElement(e, "id")
    _id.text = self.id

    manufacturer = SubElement(e, "manufacturer")
    manufacturer.text = self.manufacturer

    model = SubElement(e, "model")
    model.text = self.model

    serial = SubElement(e, "serial")
    serial.text = self.serial

    description = SubElement(e, "description")
    description.text = self.description

    e.append(self.specifications)

    location = SubElement(e, "location")
    location.text = self.location

    status = SubElement(e, "status")
    status.text = self.status.value

    loggable = SubElement(e, "loggable")
    loggable.text = "true" if self.loggable else "false"

    traceable = SubElement(e, "traceable")
    traceable.text = "true" if self.traceable else "false"

    calibrations = SubElement(e, "calibrations")
    calibrations.extend(c.to_xml() for c in self.calibrations)

    e.append(self.maintenance.to_xml())

    alterations = SubElement(e, "alterations")
    alterations.extend(a.to_xml() for a in self.alterations)

    firmware = SubElement(e, "firmware")
    firmware.extend(f.to_xml() for f in self.firmware)

    e.append(self.specified_requirements)
    e.append(self.reference_materials)
    e.append(self.quality_manual.to_xml())
    return e

Latest dataclass ¤

Latest(
    calibration_interval: float = 0.0,
    name: str = "",
    next_calibration_date: date = date(1875, 5, 20),
    quantity: str = "",
)

Base class for LatestReport and LatestPerformanceCheck.

calibration_interval class-attribute instance-attribute ¤

calibration_interval: float = 0.0

The number of years that may pass between a calibration or a performance check.

For equipment that do not have a required and periodic interval, but are calibrated on demand, the value is 0.

name class-attribute instance-attribute ¤

name: str = ''

The Component name.

next_calibration_date class-attribute instance-attribute ¤

next_calibration_date: date = date(1875, 5, 20)

The date that the equipment is due for a re-calibration.

If the calibration_interval is 0, i.e., the equipment is calibrated on demand, this date is equal to the date that the equipment was last calibrated.

quantity class-attribute instance-attribute ¤

quantity: str = ''

The Measurand quantity.

is_calibration_due ¤

is_calibration_due(months: int = 0) -> bool

Determine if the equipment needs to be re-calibrated.

Parameters:

Name Type Description Default
months int

The number of months to add to today's date to determine if the equipment needs to be re-calibrated.

0

Returns:

Type Description
bool

Whether a calibration is due within the specified number of months.

Source code in src/msl/equipment/schema.py
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
def is_calibration_due(self, months: int = 0) -> bool:
    """Determine if the equipment needs to be re-calibrated.

    Args:
        months: The number of months to add to today's date to determine if
            the equipment needs to be re-calibrated.

    Returns:
        Whether a calibration is due within the specified number of `months`.
    """
    if self.calibration_interval <= 0:
        return False  # calibrate on-demand

    ask_date = _future_date(_date.today(), max(0.0, months / 12.0))  # noqa: DTZ011
    return ask_date >= self.next_calibration_date

LatestPerformanceCheck (Latest, PerformanceCheck) dataclass ¤

LatestPerformanceCheck(
    completed_date: date,
    competency: Competency,
    entered_by: str = "",
    checked_by: str = "",
    checked_date: date | None = None,
    conditions: Conditions = Conditions(),
    cvd_equations: tuple[CVDEquation, ...] = (),
    deserialisers: tuple[Deserialised, ...] = (),
    equations: tuple[Equation, ...] = (),
    files: tuple[File, ...] = (),
    tables: tuple[Table, ...] = (),
    calibration_interval: float = 0.0,
    name: str = "",
    next_calibration_date: date = date(1875, 5, 20),
    quantity: str = "",
)

Latest performance check.

calibration_interval class-attribute instance-attribute ¤

calibration_interval: float = 0.0

The number of years that may pass between a calibration or a performance check.

For equipment that do not have a required and periodic interval, but are calibrated on demand, the value is 0.

checked_by class-attribute instance-attribute ¤

checked_by: str = ''

The name of the person who checked the information in the <performanceCheck> element.

checked_date class-attribute instance-attribute ¤

checked_date: date | None = None

The date that the information in the <performanceCheck> element was last checked.

competency instance-attribute ¤

competency: Competency

The competent people who accomplished the performance check and the technical procedure that was executed.

completed_date instance-attribute ¤

completed_date: date

The date that the performance check was completed.

conditions class-attribute instance-attribute ¤

conditions: Conditions = field(default_factory=Conditions)

The conditions under which the performance check is valid.

cvd_equation property ¤

cvd_equation: CVDEquation

Returns the first item in the cvd_equations tuple.

Raises IndexError if the performance check does not contain Callendar-Van Dusen equations.

cvd_equations class-attribute instance-attribute ¤

cvd_equations: tuple[CVDEquation, ...] = ()

Performance-check data is expressed as Callendar-Van Dusen equations.

deserialised property ¤

deserialised: Deserialised

Returns the first item in the deserialisers tuple.

Raises IndexError if the performance check does not contain serialised data.

deserialisers class-attribute instance-attribute ¤

deserialisers: tuple[Deserialised, ...] = ()

Performance-check data is stored in serialised formats and deserialised.

entered_by class-attribute instance-attribute ¤

entered_by: str = ''

The name of the person who initially entered the <performanceCheck> element in the register.

equation property ¤

equation: Equation

Returns the first item in the equations tuple.

Raises IndexError if the performance check does not contain equations.

equations class-attribute instance-attribute ¤

equations: tuple[Equation, ...] = ()

Performance-check data is expressed as equations.

file property ¤

file: File

Returns the first item in the files tuple.

Raises IndexError if the performance check does not contain files.

files class-attribute instance-attribute ¤

files: tuple[File, ...] = ()

Performance-check data is stored in other files (not in the equipment register).

name class-attribute instance-attribute ¤

name: str = ''

The Component name.

next_calibration_date class-attribute instance-attribute ¤

next_calibration_date: date = date(1875, 5, 20)

The date that the equipment is due for a re-calibration.

If the calibration_interval is 0, i.e., the equipment is calibrated on demand, this date is equal to the date that the equipment was last calibrated.

quantity class-attribute instance-attribute ¤

quantity: str = ''

The Measurand quantity.

table property ¤

table: Table

Returns the first item in the tables tuple.

Raises IndexError if the performance check does not contain tables.

tables class-attribute instance-attribute ¤

tables: tuple[Table, ...] = ()

Performance-check data is stored as Comma Separated Values (CSV) tables.

from_xml classmethod ¤

from_xml(element: Element[str]) -> PerformanceCheck

Convert an XML element into a PerformanceCheck instance.

Parameters:

Name Type Description Default
element Element[str]

A performanceCheck XML element from an equipment register.

required

Returns:

Type Description
PerformanceCheck

The PerformanceCheck instance.

Source code in src/msl/equipment/schema.py
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
@classmethod
def from_xml(cls, element: Element[str]) -> PerformanceCheck:
    """Convert an XML element into a [PerformanceCheck][msl.equipment.schema.PerformanceCheck] instance.

    Args:
        element: A [performanceCheck][type_performanceCheck] XML element from an
            equipment register.

    Returns:
        The [PerformanceCheck][msl.equipment.schema.PerformanceCheck] instance.
    """
    # Schema forces order for `competency` and `conditions` but uses xsd:choice,
    # which allows sub-elements to appear (or not appear) in any order, for the data elements.
    # Using str.endswith() allows for ignoring XML namespaces that may be associated with each tag
    cvd_equations: list[CVDEquation] = []
    equations: list[Equation] = []
    files: list[File] = []
    deserialisers: list[Deserialised] = []
    tables: list[Table] = []
    for child in element[2:]:
        tag = child.tag
        if tag.endswith("equation"):
            equations.append(Equation.from_xml(child))
        elif tag.endswith("table"):
            tables.append(Table.from_xml(child))
        elif tag.endswith("cvdCoefficients"):
            cvd_equations.append(CVDEquation.from_xml(child))
        elif tag.endswith("file"):
            files.append(File.from_xml(child))
        else:
            deserialisers.append(Deserialised.from_xml(child))

    a = element.attrib
    return cls(
        completed_date=_date.fromisoformat(a["completedDate"] or ""),
        entered_by=a["enteredBy"] or "",
        checked_by=a.get("checkedBy", ""),
        checked_date=None if not a.get("checkedDate") else _date.fromisoformat(a["checkedDate"]),
        competency=Competency.from_xml(element[0]),
        conditions=Conditions.from_xml(element[1]),
        cvd_equations=tuple(cvd_equations),
        deserialisers=tuple(deserialisers),
        equations=tuple(equations),
        files=tuple(files),
        tables=tuple(tables),
    )

is_calibration_due ¤

is_calibration_due(months: int = 0) -> bool

Determine if the equipment needs to be re-calibrated.

Parameters:

Name Type Description Default
months int

The number of months to add to today's date to determine if the equipment needs to be re-calibrated.

0

Returns:

Type Description
bool

Whether a calibration is due within the specified number of months.

Source code in src/msl/equipment/schema.py
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
def is_calibration_due(self, months: int = 0) -> bool:
    """Determine if the equipment needs to be re-calibrated.

    Args:
        months: The number of months to add to today's date to determine if
            the equipment needs to be re-calibrated.

    Returns:
        Whether a calibration is due within the specified number of `months`.
    """
    if self.calibration_interval <= 0:
        return False  # calibrate on-demand

    ask_date = _future_date(_date.today(), max(0.0, months / 12.0))  # noqa: DTZ011
    return ask_date >= self.next_calibration_date

to_xml ¤

to_xml() -> Element[str]

Convert the PerformanceCheck class into an XML element.

Returns:

Type Description
Element[str]

The PerformanceCheck as an XML element.

Source code in src/msl/equipment/schema.py
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
def to_xml(self) -> Element[str]:
    """Convert the [PerformanceCheck][msl.equipment.schema.PerformanceCheck] class into an XML element.

    Returns:
        The [PerformanceCheck][msl.equipment.schema.PerformanceCheck] as an XML element.
    """
    a = {"completedDate": self.completed_date.isoformat(), "enteredBy": self.entered_by}
    if self.checked_by:
        a["checkedBy"] = self.checked_by
    if self.checked_date is not None:
        a["checkedDate"] = self.checked_date.isoformat()

    e = Element("performanceCheck", attrib=a)
    e.append(self.competency.to_xml())
    e.append(self.conditions)
    e.extend(equation.to_xml() for equation in self.equations)
    e.extend(table.to_xml() for table in self.tables)
    e.extend(cvd.to_xml() for cvd in self.cvd_equations)
    e.extend(file.to_xml() for file in self.files)
    e.extend(deserialised.to_xml() for deserialised in self.deserialisers)
    return e

LatestReport (Latest, Report) dataclass ¤

LatestReport(
    id: str,
    report_issue_date: date,
    measurement_start_date: date,
    measurement_stop_date: date,
    issuing_laboratory: IssuingLaboratory = IssuingLaboratory(),
    technical_procedure: str = "",
    entered_by: str = "",
    checked_by: str = "",
    checked_date: date | None = None,
    conditions: Conditions = Conditions(),
    acceptance_criteria: AcceptanceCriteria = AcceptanceCriteria(),
    cvd_equations: tuple[CVDEquation, ...] = (),
    deserialisers: tuple[Deserialised, ...] = (),
    equations: tuple[Equation, ...] = (),
    files: tuple[File, ...] = (),
    tables: tuple[Table, ...] = (),
    calibration_interval: float = 0.0,
    name: str = "",
    next_calibration_date: date = date(1875, 5, 20),
    quantity: str = "",
)

Latest calibration report.

acceptance_criteria class-attribute instance-attribute ¤

acceptance_criteria: AcceptanceCriteria = field(
    default_factory=AcceptanceCriteria
)

Acceptance criteria for the calibration report.

calibration_interval class-attribute instance-attribute ¤

calibration_interval: float = 0.0

The number of years that may pass between a calibration or a performance check.

For equipment that do not have a required and periodic interval, but are calibrated on demand, the value is 0.

checked_by class-attribute instance-attribute ¤

checked_by: str = ''

The name of the person who checked the information in the <report> element.

checked_date class-attribute instance-attribute ¤

checked_date: date | None = None

The date that the information in the <report> element was last checked.

conditions class-attribute instance-attribute ¤

conditions: Conditions = field(default_factory=Conditions)

The conditions under which the report is valid.

cvd_equation property ¤

cvd_equation: CVDEquation

Returns the first item in the cvd_equations tuple.

Raises IndexError if the report does not contain Callendar-Van Dusen equations.

cvd_equations class-attribute instance-attribute ¤

cvd_equations: tuple[CVDEquation, ...] = ()

Calibration data is expressed as Callendar-Van Dusen equations.

deserialised property ¤

deserialised: Deserialised

Returns the first item in the deserialisers tuple.

Raises IndexError if the report does not contain serialised data.

deserialisers class-attribute instance-attribute ¤

deserialisers: tuple[Deserialised, ...] = ()

Calibration data is stored in serialised formats and deserialised.

entered_by class-attribute instance-attribute ¤

entered_by: str = ''

The name of the person who initially entered the <report> element in the register.

equation property ¤

equation: Equation

Returns the first item in the equations tuple.

Raises IndexError if the report does not contain equations.

equations class-attribute instance-attribute ¤

equations: tuple[Equation, ...] = ()

Calibration data is expressed as equations.

file property ¤

file: File

Returns the first item in the files tuple.

Raises IndexError if the report does not contain files.

files class-attribute instance-attribute ¤

files: tuple[File, ...] = ()

Calibration data is stored in other files (not in the equipment register).

id instance-attribute ¤

id: str

The report identification number.

issuing_laboratory class-attribute instance-attribute ¤

issuing_laboratory: IssuingLaboratory = field(default_factory=IssuingLaboratory)

Information about the laboratory that issued the calibration report.

measurement_start_date instance-attribute ¤

measurement_start_date: date

The date that the calibration measurement started.

measurement_stop_date instance-attribute ¤

measurement_stop_date: date

The date that the calibration measurement stopped.

name class-attribute instance-attribute ¤

name: str = ''

The Component name.

next_calibration_date class-attribute instance-attribute ¤

next_calibration_date: date = date(1875, 5, 20)

The date that the equipment is due for a re-calibration.

If the calibration_interval is 0, i.e., the equipment is calibrated on demand, this date is equal to the date that the equipment was last calibrated.

quantity class-attribute instance-attribute ¤

quantity: str = ''

The Measurand quantity.

report_issue_date instance-attribute ¤

report_issue_date: date

The date that the report was issued.

table property ¤

table: Table

Returns the first item in the tables tuple.

Raises IndexError if the report does not contain tables.

tables class-attribute instance-attribute ¤

tables: tuple[Table, ...] = ()

Calibration data is stored as Comma Separated Values (CSV) tables.

technical_procedure class-attribute instance-attribute ¤

technical_procedure: str = ''

The technical procedure(s) that was(were) followed to perform the calibration.

from_xml classmethod ¤

from_xml(element: Element[str]) -> Report

Convert an XML element into a Report instance.

Parameters:

Name Type Description Default
element Element[str]

A report XML element from an equipment register.

required

Returns:

Type Description
Report

The Report instance.

Source code in src/msl/equipment/schema.py
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
@classmethod
def from_xml(cls, element: Element[str]) -> Report:
    """Convert an XML element into a [Report][msl.equipment.schema.Report] instance.

    Args:
        element: A [report][type_report] XML element from an equipment register.

    Returns:
        The [Report][msl.equipment.schema.Report] instance.
    """
    # Schema forces order until `acceptanceCriteria` and then uses xsd:choice, which
    # allows sub-elements to appear (or not appear) in any order, for the data elements.
    # Using str.endswith() allows for ignoring XML namespaces that may be associated with each tag
    cvd_equations: list[CVDEquation] = []
    deserialisers: list[Deserialised] = []
    equations: list[Equation] = []
    files: list[File] = []
    tables: list[Table] = []
    for child in element[7:]:
        tag = child.tag
        if tag.endswith("equation"):
            equations.append(Equation.from_xml(child))
        elif tag.endswith("table"):
            tables.append(Table.from_xml(child))
        elif tag.endswith("cvdCoefficients"):
            cvd_equations.append(CVDEquation.from_xml(child))
        elif tag.endswith("file"):
            files.append(File.from_xml(child))
        else:
            deserialisers.append(Deserialised.from_xml(child))

    a = element.attrib
    return cls(
        id=a["id"] or "",
        entered_by=a["enteredBy"] or "",
        checked_by=a.get("checkedBy", ""),
        checked_date=None if not a.get("checkedDate") else _date.fromisoformat(a["checkedDate"]),
        report_issue_date=_date.fromisoformat(element[0].text or ""),
        measurement_start_date=_date.fromisoformat(element[1].text or ""),
        measurement_stop_date=_date.fromisoformat(element[2].text or ""),
        issuing_laboratory=IssuingLaboratory.from_xml(element[3]),
        technical_procedure=element[4].text or "",
        conditions=Conditions.from_xml(element[5]),
        acceptance_criteria=AcceptanceCriteria.from_xml(element[6]),
        cvd_equations=tuple(cvd_equations),
        deserialisers=tuple(deserialisers),
        equations=tuple(equations),
        files=tuple(files),
        tables=tuple(tables),
    )

is_calibration_due ¤

is_calibration_due(months: int = 0) -> bool

Determine if the equipment needs to be re-calibrated.

Parameters:

Name Type Description Default
months int

The number of months to add to today's date to determine if the equipment needs to be re-calibrated.

0

Returns:

Type Description
bool

Whether a calibration is due within the specified number of months.

Source code in src/msl/equipment/schema.py
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
def is_calibration_due(self, months: int = 0) -> bool:
    """Determine if the equipment needs to be re-calibrated.

    Args:
        months: The number of months to add to today's date to determine if
            the equipment needs to be re-calibrated.

    Returns:
        Whether a calibration is due within the specified number of `months`.
    """
    if self.calibration_interval <= 0:
        return False  # calibrate on-demand

    ask_date = _future_date(_date.today(), max(0.0, months / 12.0))  # noqa: DTZ011
    return ask_date >= self.next_calibration_date

to_xml ¤

to_xml() -> Element[str]

Convert the Report class into an XML element.

Returns:

Type Description
Element[str]

The Report as an XML element.

Source code in src/msl/equipment/schema.py
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
def to_xml(self) -> Element[str]:
    """Convert the [Report][msl.equipment.schema.Report] class into an XML element.

    Returns:
        The [Report][msl.equipment.schema.Report] as an XML element.
    """
    a = {"id": self.id, "enteredBy": self.entered_by}
    if self.checked_by:
        a["checkedBy"] = self.checked_by
    if self.checked_date is not None:
        a["checkedDate"] = self.checked_date.isoformat()

    e = Element("report", attrib=a)

    rid = SubElement(e, "reportIssueDate")
    rid.text = self.report_issue_date.isoformat()

    start = SubElement(e, "measurementStartDate")
    start.text = self.measurement_start_date.isoformat()

    stop = SubElement(e, "measurementStopDate")
    stop.text = self.measurement_stop_date.isoformat()

    e.append(self.issuing_laboratory.to_xml())

    tp = SubElement(e, "technicalProcedure")
    tp.text = self.technical_procedure

    e.append(self.conditions)
    e.append(self.acceptance_criteria)
    e.extend(equation.to_xml() for equation in self.equations)
    e.extend(table.to_xml() for table in self.tables)
    e.extend(cvd.to_xml() for cvd in self.cvd_equations)
    e.extend(file.to_xml() for file in self.files)
    e.extend(deserialised.to_xml() for deserialised in self.deserialisers)
    return e