"""Stdlib-only storage records for OPTIMADE ``files`` entries.
``FileRecord`` stores file metadata and a flat ``sha256`` digest. The URL is
part of identity deliberately: two paths to the same bytes are two entries.
The mapping-valued ``checksums`` field is skipped because SQL storage cannot
persist mappings; use ``sha256`` for the storable digest.
"""
import datetime
from collections.abc import Mapping
from dataclasses import dataclass, field
from typing import Annotated, Any, ClassVar, Self, cast
from .entry_types import File
from .storage import IdentitySkip, Indexed, Skip, StorageInfo, StoredPropertyProjection, Unique
[docs]
FILES_DEFINITION_ID = "https://schemas.optimade.org/defs/v1.2/entrytypes/optimade/files"
@dataclass(frozen=True)
[docs]
class FileRecord(File):
"""Store one OPTIMADE ``files`` entry with content identity metadata.
URL and name remain required positional fields. The URL is included in
identity so separate paths to identical bytes remain separate entries.
The human-readable and immutable identifiers, metadata timestamps, and
other metadata are excluded from content identity.
``checksums`` is skipped because mapping fields are not SQL-storable;
store the flat ``sha256`` value when a storable digest is needed.
:param url: The URL to get the contents of the file.
:param name: The base name of the file.
:param id: The human-readable entry id shared by all revisions; minted by the store when None.
:param immutable_id: The per-revision immutable id; minted by the store when None.
:param last_modified: The optional timezone-aware metadata timestamp.
:param url_stable_until: The optional URL stability deadline.
:param size: The file size in bytes, if known.
:param media_type: The file MIME type, if known.
:param version: The file version, if known.
:param modification_timestamp: The optional content modification timestamp.
:param description: An optional free-form file description.
:param checksums: Optional checksums, kept out of SQL storage.
:param atime: The optional POSIX access timestamp.
:param ctime: The optional POSIX status-change timestamp.
:param mtime: The optional POSIX modification timestamp.
:param sha256: The optional flat SHA-256 digest.
"""
__httk_storage__: ClassVar[StorageInfo] = StorageInfo(
storage_name="core_file",
identity_name="core_file",
indexes=(("url",), ("name",), ("sha256",)),
)
[docs]
id: Annotated[str | None, IdentitySkip(), Indexed()] = field(default=None, compare=False)
[docs]
immutable_id: Annotated[str | None, IdentitySkip(), Unique()] = field(default=None, compare=False)
[docs]
last_modified: Annotated[datetime.datetime | None, IdentitySkip()] = field(default=None, compare=False)
[docs]
url_stable_until: Annotated[datetime.datetime | None, IdentitySkip()] = field(default=None, compare=False)
[docs]
size: int | None = None
[docs]
version: str | None = None
[docs]
modification_timestamp: Annotated[datetime.datetime | None, IdentitySkip()] = field(default=None, compare=False)
[docs]
description: str | None = None
[docs]
checksums: Annotated[Mapping[str, str] | None, Skip()] = None
[docs]
atime: Annotated[datetime.datetime | None, IdentitySkip()] = field(default=None, compare=False)
[docs]
ctime: Annotated[datetime.datetime | None, IdentitySkip()] = field(default=None, compare=False)
[docs]
mtime: Annotated[datetime.datetime | None, IdentitySkip()] = field(default=None, compare=False)
[docs]
sha256: str | None = None
@property
[docs]
def type(self) -> str:
"""Return the served entry type name."""
return "files"
[docs]
class FileEntry:
"""Logical entry family for served :class:`FileRecord` records.
This family is not itself storable; store a ``FileRecord`` directly.
"""
[docs]
definition_id = FILES_DEFINITION_ID
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
raise TypeError("FileEntry is a logical entry family; store a FileRecord directly")
def _served_file_value(field_name: str) -> StoredPropertyProjection:
"""Return a response-only projection reading one served ``FileRecord`` field.
The served ``files`` properties map directly onto stored columns, so the
projection reads the field verbatim; filtering and sorting are intentionally
omitted (files are reached by id through their producing run's edges, not
filtered by metadata).
:param field_name: The ``FileRecord`` field read for the served value.
:return: A projection whose response reads ``field_name`` directly.
"""
return StoredPropertyProjection(response=lambda record: getattr(record, field_name))
# Default projections so ``StoredEntrySource(store, FileEntry, ...)`` resolves: a
# stored source rejects a family whose non-nullable served properties (``url``,
# ``name``) have no response mapping. ``size`` and ``media_type`` are the useful
# nullable served properties; ``sha256`` is deliberately absent (it is a stored
# field but not an OPTIMADE ``files`` property, so projecting it would be rejected
# as unknown). Keys are the served (wire) ``files`` property names.
cast(Any, FileRecord).__httk_stored_properties__ = {
"url": _served_file_value("url"),
"name": _served_file_value("name"),
"size": _served_file_value("size"),
"media_type": _served_file_value("media_type"),
}
__all__ = ["FILES_DEFINITION_ID", "FileEntry", "FileRecord"]