Source code for topchef.models.service

"""
Contains an implementation of the ``Service`` interface that pulls all the
required data from a SQLAlchemy model class.
"""
import json
from typing import Type, Callable
from uuid import UUID
from datetime import datetime, timedelta
from sqlalchemy.orm import Session
from sqlalchemy.ext.declarative import declarative_base
from .interfaces import Service as ServiceInterface
from .interfaces import JobList as JobListInterface
from .abstract_classes import JobListFromQuery
from .job import Job
from ..database.models import Job as DatabaseJob
from ..database.models import Service as DatabaseService
from ..json_type import JSON_TYPE as JSON


[docs]class Service(ServiceInterface): """ Provides a model class that gets all its data from an underlying database service already in the API database """ def __init__( self, database_service: DatabaseService, session_getter_for_model: Callable[[declarative_base()], Session]=Session.object_session ) -> None: self.db_model = database_service self._session_getter_for_model = session_getter_for_model @property def id(self) -> UUID: """ :return: The service ID """ return self.db_model.id @property def name(self) -> str: """ :return: The service name """ return self.db_model.name @name.setter def name(self, new_name: str) -> None: """ :param new_name: The new name to be set in the API """ self.db_model.name = new_name @property def description(self) -> str: """ :return: A human-readable description for the service """ return self.db_model.description @description.setter def description(self, new_description: str) -> None: """ :param new_description: The desired new description """ self.db_model.description = new_description @property def job_registration_schema(self) -> JSON: """ :return: The JSON schema that must be satisfied in order to create a new job """ return self.db_model.job_registration_schema @property def job_result_schema(self) -> JSON: """ :return: The JSON schema that must be satisfied in order to post a result to the job """ return self.db_model.job_result_schema @property def is_service_available(self) -> bool: """ :return: A flag that indicates whether the service is ready to accept jobs """ return self.db_model.is_service_available @is_service_available.setter def is_service_available(self, service_available: bool) -> None: """ :param service_available: The desired value for the flag """ self.db_model.is_service_available = service_available @property def has_timed_out(self) -> bool: """ :return: Whether the service has timed out or not """ return ( datetime.utcnow() - self.db_model.last_checked_in ) > self.timeout @property def timeout(self) -> timedelta: return timedelta(seconds=self.db_model.timeout) @timeout.setter def timeout(self, new_timeout: timedelta) -> None: if not new_timeout.total_seconds() > 0: raise ValueError( 'The timeout must be a time longer than 0 seconds' ) self.db_model.timeout = new_timeout.total_seconds() def check_in(self) -> None: self.db_model.last_checked_in = datetime.utcnow() @classmethod def new(cls, name: str, description: str, registration_schema: JSON, result_schema: JSON, database_session: Session) -> 'Service': db_model = DatabaseService.new( name, description, registration_schema, result_schema ) cls._write_database_model(db_model, database_session) return cls(db_model) def new_job( self, parameters: JSON, database_job_constructor: Type[DatabaseJob]=DatabaseJob.new ) -> Job: db_job = database_job_constructor(self.db_model, parameters) session = self._session_getter_for_model(self.db_model) session.add(db_job) return Job(db_job) @property def jobs(self) -> JobListInterface: return self._ListOfJobsForService( self, self._session_getter_for_model(self.db_model) ) @staticmethod def _assert_json(json_to_set): try: json.loads(json_to_set) except json.JSONDecodeError as error: raise ValueError( 'The input is not JSON', error ) @staticmethod def _write_database_model( model: DatabaseService, session: Session ) -> None: session.add(model) def __hash__(self) -> int: return hash((self.__class__.__name__, self.db_model.id)) def __repr__(self) -> str: return '%s(database_service=%s, session_getter_for_model=%s)' % ( self.__class__.__name__, self.db_model, self._session_getter_for_model ) class _ListOfJobsForService(JobListFromQuery): def __init__(self, service: ServiceInterface, db_session: Session): super(self.__class__, self).__init__(db_session) self.service_id = service.id @property def root_job_query(self): return self.session.query(DatabaseJob).filter_by( service_id=self.service_id )