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.

Source code in src/msl/equipment/schema.py
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
def connect(self) -> _Any:  # noqa: ANN401
    """Connect to the equipment."""
    if self.connection is None:
        super().__setattr__("connection", connections[self.id])
        assert self.connection is not None  # noqa: S101

    for backend in backends:
        if backend.handles(self.connection):
            return backend.cls(self)

    for resource in resources:
        if resource.handles(self):
            return resource.cls(self)

    address = self.connection.address
    for interface in interfaces:
        if interface.handles(address):
            return interface.cls(self)

    msg = f"Cannot determine the interface from the address {address!r}"
    raise ValueError(msg)

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
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
@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
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
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
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
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),
                    performance_check=check,
                    quantity=m.quantity,
                )

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
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
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
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
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),
                    report=report,
                    quantity=m.quantity,
                )

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
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
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,
    name: str,
    next_calibration_date: date,
    quantity: str,
)

Base class for LatestReport and LatestPerformanceCheck.

calibration_interval instance-attribute ¤

calibration_interval: float

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 instance-attribute ¤

name: str

The Component name.

next_calibration_date instance-attribute ¤

next_calibration_date: date

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 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
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
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 dataclass ¤

LatestPerformanceCheck(
    calibration_interval: float,
    name: str,
    next_calibration_date: date,
    quantity: str,
    performance_check: PerformanceCheck,
)

Bases: Latest

Latest performance check.

calibration_interval instance-attribute ¤

calibration_interval: float

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 instance-attribute ¤

name: str

The Component name.

next_calibration_date instance-attribute ¤

next_calibration_date: date

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.

performance_check instance-attribute ¤

performance_check: PerformanceCheck

Latest performance check.

quantity 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
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
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

LatestReport dataclass ¤

LatestReport(
    calibration_interval: float,
    name: str,
    next_calibration_date: date,
    quantity: str,
    report: Report,
)

Bases: Latest

Latest calibration report.

calibration_interval instance-attribute ¤

calibration_interval: float

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 instance-attribute ¤

name: str

The Component name.

next_calibration_date instance-attribute ¤

next_calibration_date: date

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 instance-attribute ¤

quantity: str

The Measurand quantity.

report instance-attribute ¤

report: Report

Latest calibration report.

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
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
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