Source code for evalquiz_proto.shared.internal_lecture_material

from __future__ import annotations
from dataclasses import dataclass
import os
from pathlib import Path
from typing import Any, Optional

import jsonpickle
from evalquiz_proto.shared.generated import LectureMaterial
from blake3 import blake3
from evalquiz_proto.shared.exceptions import (
    MimetypeNotDetectedException,
)
from evalquiz_proto.shared.mimetype_resolver import MimetypeResolver


[docs]@dataclass(init=False) class InternalLectureMaterial(LectureMaterial): """A lecture material with an additional local path pointing to the file.""" local_path: Path = Path("") def __init__(self, local_path: Path, lecture_material: LectureMaterial): """Constructor of InternalLectureMaterial. Args: local_path (Path): The local path that the binary is located at. lecture_material (LectureMaterial): Metadata in form of the LectureMaterial datatype. """ self.local_path = local_path self.reference = lecture_material.reference self.url = lecture_material.url self.hash = lecture_material.hash self.file_type = lecture_material.file_type self.page_filter = lecture_material.page_filter self.update_mimetype() self.update_hash()
[docs] def update_hash(self, rename_file: bool = False) -> None: """Updates LectureMaterial hash with file contents. Args: rename_file (bool, optional): Filename of referenced file is changed to the updated hash, if set to True. Defaults to False. """ with open(self.local_path, "rb") as local_file: file_content = local_file.read() hash = blake3(file_content).hexdigest() if hash != self.hash: self.hash = hash self._update_mimetype() if rename_file: self._rename_file_to_hash(hash)
def _rename_file_to_hash(self, hash: str) -> None: """Renames a file to the newly calculated hash. Args: hash: The newly calculated hash and filename """ absolute_path = os.path.abspath(self.local_path) folder_path = os.path.dirname(absolute_path) renamed_path = Path(folder_path) / hash os.rename(self.local_path, renamed_path)
[docs] def verify_hash(self, other_hash: Optional[str] = None) -> bool: """Verifies that the file behind local_path is consistent with other_hash. Args: other_hash: An optional hash parameter to test consistency against. If other_hash is None, self.hash is used as other_hash. Returns: True, if other_hash matches calculated hash of file. """ if other_hash is None: other_hash = self.hash with open(self.local_path, "rb") as local_file: file_content = local_file.read() return other_hash == blake3(file_content).hexdigest()
[docs] def cast_to_lecture_material(self) -> LectureMaterial: """Casts self object to object of superclass: LectureMaterial. This method is required, as Python does not feature object type casting. Returns: LectureMaterial: Self object casted into LectureMaterial. """ return LectureMaterial( self.reference, self.url, self.hash, self.file_type, self.page_filter )
[docs] def update_mimetype(self) -> None: """Evaluates if given mimetype matches mimetype of file at local_path. Sets mimetype to new value, if mimetype does not match mimetype of file at local_path. Raises: MimetypeNotDetectedException """ type = MimetypeResolver.fixed_guess_type(self.local_path.suffix) if type is None: raise MimetypeNotDetectedException() if type != self.file_type: self.file_type = type
def _update_mimetype(self) -> None: """Updates mimetype to match the mimetype of file at local_path.""" type = MimetypeResolver.fixed_guess_type(self.local_path.suffix) if type is not None: self.file_type = type
[docs] def to_mongodb_document(self) -> dict[str, Any]: """Encodes self to a representation that can be inserted by pymongo. Returns: dict[str, Any]: Dictionary containing hash and serialized self. """ return {"_id": self.hash, "internal_lecture_material": jsonpickle.encode(self)}
[docs] @classmethod def from_mongodb_document(cls, document: dict[str, Any]) -> InternalLectureMaterial: """Constructor of self from pymongo representation. Args: document (dict[str, Any]): Dictionary containing hash and serialized self. """ return jsonpickle.decode(document["internal_lecture_material"])