class MissionReportVehicle(Base, ContentMixin, HiddenFromPublicExtension):

    __tablename__ = 'mission_report_vehicles'

    #: the public id of the vehicle
    id = Column(UUID, nullable=False, primary_key=True, default=uuid4)

    #: the short id of the vehicle
    name = Column(Text, nullable=False)

    #: the longer name of the vehicle
    description = Column(Text, nullable=False)

    #: symbol of the vehicle
    symbol = associated(MissionReportFile, 'symbol', 'one-to-one')

    #: a website describing the vehicle
    website = Column(Text, nullable=True)

    uses = relationship('MissionReportVehicleUse')

    @property
    def title(self):
        return f'{self.name} - {self.description}'

    @property
    def readable_website(self):
        if self.website:
            return self.website.replace('https://', '').replace('http://', '')
Example #2
0
class PayableManyTimes(PayableBase):
    """ Same as :class:`Payable`, but using a list of payments instead of
    a single one (proper many-to-many payments).

    """

    payments = associated(Payment, 'payments', 'many-to-many', uselist=True)
Example #3
0
class Payable(PayableBase):
    """ Links the parent model with 0 to n :class:`~onegov.pay.models.Payment`
    records through an automatically generated association table.

    """

    payment = associated(Payment, 'payment', 'many-to-many', uselist=False)
class MissionReport(Base, ContentMixin, HiddenFromPublicExtension):

    __tablename__ = 'mission_reports'

    #: the public id of the mission_report
    id = Column(UUID, nullable=False, primary_key=True, default=uuid4)

    #: the date of the report
    date = Column(UTCDateTime, nullable=False)

    #: how long the mission lasted, in hours
    duration = Column(Numeric(precision=6, scale=2), nullable=False)

    #: the nature of the mission
    nature = Column(Text, nullable=False)

    #: the location of the mission
    location = Column(Text, nullable=False)

    #: actually active personnel
    personnel = Column(Integer, nullable=False)

    #: backup personnel
    backup = Column(Integer, nullable=False)

    #: the Zivilschutz was involved
    civil_defence = Column(Boolean, nullable=False, default=False)

    #: the vehicle use of the mission report
    used_vehicles = relationship(
        'MissionReportVehicleUse',
        cascade="all, delete-orphan")

    #: pictures of the mission
    pictures = associated(MissionReportFile, 'pictures', 'one-to-many')

    @property
    def title(self):
        return self.nature

    @property
    def readable_duration(self):
        return str(self.duration).rstrip('.0') + 'h'

    @property
    def local_date(self):
        return to_timezone(self.date, 'Europe/Zurich')
Example #5
0
class ExtendedAgency(Agency, HiddenFromPublicExtension):
    """ An extended version of the standard agency from onegov.people. """

    __mapper_args__ = {'polymorphic_identity': 'extended'}

    es_type_name = 'extended_agency'

    @property
    def es_public(self):
        return not self.is_hidden_from_public

    #: Defines which fields of a membership and person should be exported to
    #: the PDF. The fields are expected to contain two parts seperated by a
    #: point. The first part is either `membership` or `person`, the second
    #: the name of the attribute (e.g. `membership.title`).
    export_fields = meta_property(default=list)

    #: The PDF for the agency and all its suborganizations.
    pdf = associated(AgencyPdf, 'pdf', 'one-to-one')

    trait = 'agency'

    @property
    def pdf_file(self):
        """ Returns the PDF content for the agency (and all its
        suborganizations).

        """

        if self.pdf:
            return self.pdf.reference.file

    @pdf_file.setter
    def pdf_file(self, value):
        """ Sets the PDF content for the agency (and all its
        suborganizations). Automatically sets a nice filename. Replaces only
        the reference, if possible.

        """

        filename = '{}.pdf'.format(normalize_for_url(self.title))
        if self.pdf:
            self.pdf.reference = as_fileintent(value, filename)
            self.pdf.name = filename
        else:
            pdf = AgencyPdf(id=random_token())
            pdf.reference = as_fileintent(value, filename)
            pdf.name = filename
            self.pdf = pdf

    @property
    def portrait_html(self):
        """ Returns the portrait as HTML. """

        return '<p>{}</p>'.format(linkify(self.portrait).replace('\n', '<br>'))

    def proxy(self):
        """ Returns a proxy object to this agency allowing alternative linking
        paths. """

        return AgencyProxy(self)

    def add_person(self, person_id, title, **kwargs):
        """ Appends a person to the agency with the given title. """

        order_within_agency = kwargs.pop('order_within_agency', 2**16)
        session = object_session(self)

        orders_for_person = session.query(
            ExtendedAgencyMembership.order_within_person).filter_by(
                person_id=person_id).all()

        orders_for_person = list(
            (o.order_within_person for o in orders_for_person))

        if orders_for_person:
            try:
                order_within_person = max(orders_for_person) + 1
            except ValueError:
                order_within_person = 0
            assert len(orders_for_person) == max(orders_for_person) + 1
        else:
            order_within_person = 0

        self.memberships.append(
            ExtendedAgencyMembership(person_id=person_id,
                                     title=title,
                                     order_within_agency=order_within_agency,
                                     order_within_person=order_within_person,
                                     **kwargs))

        for order, membership in enumerate(self.memberships):
            membership.order_within_agency = order
Example #6
0
class Message(Base):
    """ A single chat message bound to channel. """

    __tablename__ = 'messages'

    #: the public id of the message - uses ulid for absolute ordering
    id = Column(Text, primary_key=True, default=ulid)

    #: channel to which this message belongs -> this might one day be
    #: linked to an actual channel record - for now it's just a string that
    #: binds all messages with the same string together
    channel_id = Column(Text, index=True, nullable=False)

    #: optional owner of the message -> this is just an identifier, it isn't
    #: necessarily linked to the user table
    owner = Column(Text, nullable=True)

    #: the polymorphic type of the message
    type = Column(Text, nullable=True)

    #: meta information specific to this message and maybe its type -> we
    #: don't use the meta/content mixin yet as we might not need the content
    #: property
    meta = Column(JSON, nullable=False, default=dict)

    #: the text of the message, maybe None for certain use cases (say if the
    # content of the message is generated from the meta property)
    text = Column(Text, nullable=True)

    #: the time this message was created - not taken from the timestamp mixin
    #: because here we don't want it to be deferred
    created = Column(UTCDateTime, default=sedate.utcnow)

    #: the time this message was modified - not taken from the timestamp mixin
    #: because here we don't want it to be deferred
    modified = Column(UTCDateTime, onupdate=sedate.utcnow)

    #: a single optional file associated with this message
    file = associated(File, 'file', 'one-to-one')

    __mapper_args__ = {
        'order_by': id
    }

    # we need to override __hash__ and __eq__ to establish the equivalence of
    # polymorphic subclasses that differ - we need to compare the base class
    # with subclasses to work around a limitation of the association proxy
    # (see backref in onegov.core.orm.abstract.associable.associated)
    def __hash__(self):
        return super().__hash__()

    def __eq__(self, other):
        if isinstance(other, self.__class__) \
                and self.id == other.id\
                and self.channel_id == other.channel_id:

            return True

        return super().__eq__(other)

    @property
    def subtype(self):
        """ An optional subtype for this message used for separating messages
        of a type further (currently for UI).

        Should be made unique, but there's no guarantee.

        """
        return None

    def get(self, request):
        """ Code rendering a message should call this method to get the
        actual text of the message. It might be rendered from meta or it
        might be returned directly from the text column.

        How this is done is up to the polymorphic Message.

        """
        return self.text

    @hybrid_property
    def edited(self):
        # use != instead of "is not" as we want this translated into SQL
        return self.modified != None

    @classmethod
    def bound_messages(cls, session):
        """ A message collection bound to the polymorphic identity of this
        message.

        """
        from onegov.chat import MessageCollection  # XXX circular import

        return MessageCollection(
            session=session,
            type=cls.__mapper_args__['polymorphic_identity']
        )