Source code for libres.db.models.reservation

import sedate

from collections import namedtuple
from datetime import timedelta

from sqlalchemy import types
from sqlalchemy.orm import object_session, deferred
from sqlalchemy.schema import Column
from sqlalchemy.schema import Index

from libres.db.models import ORMBase
from libres.db.models.types import UUID, UTCDateTime, JSON
from libres.db.models.other import OtherModels
from libres.db.models.timestamp import TimestampMixin


Timespan = namedtuple(
    'Timespan', ('start', 'end')
)


[docs]class Reservation(TimestampMixin, ORMBase, OtherModels): """Describes a pending or approved reservation. """ __tablename__ = 'reservations' id = Column( types.Integer(), primary_key=True, autoincrement=True ) token = Column( UUID(), nullable=False, ) target = Column( UUID(), nullable=False, ) target_type = Column( types.Enum(u'group', u'allocation', name='reservation_target_type'), nullable=False ) type = Column( types.Text(), nullable=True ) resource = Column( UUID(), nullable=False ) start = Column( UTCDateTime(timezone=False), nullable=True ) end = Column( UTCDateTime(timezone=False), nullable=True ) timezone = Column( types.String(), nullable=True ) status = Column( types.Enum(u'pending', u'approved', name="reservation_status"), nullable=False ) data = deferred( Column( JSON(), nullable=True ) ) email = Column( types.Unicode(254), nullable=False ) session_id = Column( UUID() ) quota = Column( types.Integer(), nullable=False ) __table_args__ = ( Index('target_status_ix', 'status', 'target', 'id'), ) __mapper_args__ = { 'polymorphic_identity': None, 'polymorphic_on': type } def _target_allocations(self): """ Returns the allocations this reservation is targeting. This should NOT be confused with db.allocations_by_reservation. The method in the db module returns the actual allocations belonging to an approved reservation. This method only returns the master allocations to get information about timespans and other properties. If you don't know exactly what you're doing you do not want to use this method as misuse might be dangerous. """ Allocation = self.models.Allocation query = object_session(self).query(Allocation) query = query.filter(Allocation.group == self.target) # master allocations only query = query.filter(Allocation.resource == Allocation.mirror_of) # order by date query = query.order_by(Allocation._start) return query
[docs] def display_start(self, timezone=None): """Does nothing but to form a nice pair to display_end.""" return sedate.to_timezone(self.start, timezone or self.timezone)
[docs] def display_end(self, timezone=None): """Returns the end plus one microsecond (nicer display).""" end = self.end + timedelta(microseconds=1) return sedate.to_timezone(end, timezone or self.timezone)
[docs] def timespans(self): """ Returns the timespans targeted by this reservation. The result is a list of :class:`~libres.db.models.reservation.Timespan` timespans. The start and end are the start and end dates of the referenced allocations. The timespans are ordered by start. """ if self.target_type == u'allocation': # we don't need to hit the database in this case return [ Timespan(self.start, self.end + timedelta(microseconds=1)) ] elif self.target_type == u'group': return [ Timespan(a.start, a.end) for a in self._target_allocations() ] else: raise NotImplementedError
@property def title(self): return self.email @property def autoapprovable(self): query = self._target_allocations() query = query.filter(self.models.Allocation.approve_manually == True) # A reservation is deemed autoapprovable if no allocation # requires explicit approval return query.first() is None