Ejemplo n.º 1
0
class GcnTag(Base):
    """Store qualitative tags for events."""

    update = delete = AccessibleIfUserMatches('sent_by')

    sent_by_id = sa.Column(
        sa.ForeignKey('users.id', ondelete='CASCADE'),
        nullable=False,
        index=True,
        doc="The ID of the User who created this GcnTag.",
    )

    sent_by = relationship(
        "User",
        foreign_keys=sent_by_id,
        back_populates="gcntags",
        doc="The user that saved this GcnTag",
    )

    dateobs = sa.Column(
        sa.ForeignKey('gcnevents.dateobs', ondelete="CASCADE"),
        nullable=False,
        index=True,
    )

    text = sa.Column(sa.Unicode, nullable=False)
Ejemplo n.º 2
0
class UserNotification(Base):

    read = update = delete = AccessibleIfUserMatches('user')

    user_id = sa.Column(
        sa.ForeignKey("users.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
        doc="ID of the associated User",
    )
    user = relationship(
        "User",
        back_populates="notifications",
        doc="The associated User",
    )
    text = sa.Column(
        sa.String(),
        nullable=False,
        doc="The notification text to display",
    )

    viewed = sa.Column(
        sa.Boolean,
        nullable=False,
        default=False,
        index=True,
        doc="Boolean indicating whether notification has been viewed.",
    )

    url = sa.Column(
        sa.String(),
        nullable=True,
        doc="URL to which to direct upon click, if relevant",
    )
Ejemplo n.º 3
0
class ObjAnalysis(Base, AnalysisMixin, WebhookMixin):
    """Analysis on an Obj with a set of results as JSON"""

    __tablename__ = 'obj_analyses'

    create = AccessibleIfRelatedRowsAreAccessible(obj='read')
    read = accessible_by_groups_members & AccessibleIfRelatedRowsAreAccessible(
        obj='read'
    )
    update = delete = AccessibleIfUserMatches('author')

    @declared_attr
    def obj_id(cls):
        return sa.Column(
            sa.ForeignKey('objs.id', ondelete='CASCADE'),
            nullable=False,
            index=True,
            doc="ID of the ObjAnalysis's Obj.",
        )

    @declared_attr
    def obj(cls):
        return relationship(
            'Obj',
            back_populates=cls.backref_name(),
            doc="The ObjAnalysis's Obj.",
        )
Ejemplo n.º 4
0
class AnnotationOnSpectrum(Base, AnnotationMixin):

    __tablename__ = 'annotations_on_spectra'

    create = AccessibleIfRelatedRowsAreAccessible(obj='read', spectrum='read')

    read = accessible_by_groups_members & AccessibleIfRelatedRowsAreAccessible(
        obj='read',
        spectrum='read',
    )

    update = delete = AccessibleIfUserMatches('author')

    spectrum_id = sa.Column(
        sa.ForeignKey('spectra.id', ondelete='CASCADE'),
        nullable=False,
        index=True,
        doc="ID of the Annotation's Spectrum.",
    )
    spectrum = relationship(
        'Spectrum',
        back_populates='annotations',
        doc="The Spectrum referred to by this annotation.",
    )

    __table_args__ = (UniqueConstraint('spectrum_id', 'origin'), )
Ejemplo n.º 5
0
class Listing(Base):
    create = read = update = delete = AccessibleIfUserMatches("user")

    user_id = sa.Column(
        sa.ForeignKey('users.id', ondelete='CASCADE'),
        nullable=False,
        index=True,
        doc="The ID of the User who created this Listing.",
    )

    user = relationship(
        "User",
        foreign_keys=user_id,
        back_populates="listings",
        doc="The user that saved this object/listing",
    )

    obj_id = sa.Column(
        sa.ForeignKey('objs.id', ondelete='CASCADE'),
        nullable=False,
        index=True,
        doc="The ID of the object that is on this Listing",
    )

    obj = relationship(
        "Obj",
        doc="The object referenced by this listing",
    )

    list_name = sa.Column(
        sa.String,
        index=True,
        nullable=False,
        doc="Name of the list, e.g., 'favorites'. ",
    )
Ejemplo n.º 6
0
class SourceNotification(Base):

    create = read = AccessibleIfRelatedRowsAreAccessible(source='read')
    update = delete = AccessibleIfUserMatches('sent_by')

    groups = relationship(
        "Group",
        secondary="group_notifications",
        cascade="save-update, merge, refresh-expire, expunge",
        passive_deletes=True,
    )
    sent_by_id = sa.Column(
        sa.ForeignKey("users.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
        doc="The ID of the User who sent this notification.",
    )
    sent_by = relationship(
        "User",
        back_populates="source_notifications",
        foreign_keys=[sent_by_id],
        doc="The User who sent this notification.",
    )
    source_id = sa.Column(
        sa.ForeignKey("objs.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
        doc="ID of the target Obj.",
    )
    source = relationship('Obj',
                          back_populates='obj_notifications',
                          doc='The target Obj.')

    additional_notes = sa.Column(sa.String(), nullable=True)
    level = sa.Column(sa.String(), nullable=False)
Ejemplo n.º 7
0
class Classification(Base):
    """Classification of an Obj."""

    create = ok_if_tax_and_obj_readable
    read = accessible_by_groups_members & ok_if_tax_and_obj_readable
    update = delete = AccessibleIfUserMatches('author')

    classification = sa.Column(sa.String,
                               nullable=False,
                               index=True,
                               doc="The assigned class.")
    taxonomy_id = sa.Column(
        sa.ForeignKey('taxonomies.id', ondelete='CASCADE'),
        nullable=False,
        index=True,
        doc="ID of the Taxonomy in which this Classification was made.",
    )
    taxonomy = relationship(
        'Taxonomy',
        back_populates='classifications',
        doc="Taxonomy in which this Classification was made.",
    )
    probability = sa.Column(
        sa.Float,
        doc='User-assigned probability of belonging to this class',
        nullable=True,
        index=True,
    )

    author_id = sa.Column(
        sa.ForeignKey('users.id', ondelete='CASCADE'),
        nullable=False,
        index=True,
        doc="ID of the User that made this Classification",
    )
    author = relationship('User',
                          doc="The User that made this classification.")
    author_name = sa.Column(
        sa.String,
        nullable=False,
        doc="User.username or Token.id "
        "of the Classification's author.",
    )
    obj_id = sa.Column(
        sa.ForeignKey('objs.id', ondelete='CASCADE'),
        nullable=False,
        index=True,
        doc="ID of the Classification's Obj.",
    )
    obj = relationship('Obj',
                       back_populates='classifications',
                       doc="The Classification's Obj.")
    groups = relationship(
        "Group",
        secondary="group_classifications",
        cascade="save-update, merge, refresh-expire, expunge",
        passive_deletes=True,
        doc="Groups that can access this Classification.",
    )
Ejemplo n.º 8
0
class DefaultObservationPlanRequest(Base):
    """A default request for an EventObservationPlan."""

    # TODO: Make read-accessible via target groups
    create = read = AccessibleIfRelatedRowsAreAccessible(allocation="read")
    update = delete = (
        (AccessibleIfUserMatches('allocation.group.users')
         | AccessibleIfUserMatches('requester'))
        & read) | CustomUserAccessControl(updatable_by_token_with_listener_acl)

    requester_id = sa.Column(
        sa.ForeignKey('users.id', ondelete='SET NULL'),
        nullable=True,
        index=True,
        doc=
        "ID of the User who requested the default observation plan request.",
    )

    requester = relationship(
        User,
        back_populates='default_observationplan_requests',
        doc="The User who requested the default requests.",
        foreign_keys=[requester_id],
    )

    payload = sa.Column(
        psql.JSONB,
        nullable=False,
        doc="Content of the default observation plan request.",
    )

    allocation_id = sa.Column(sa.ForeignKey('allocations.id',
                                            ondelete='CASCADE'),
                              nullable=False,
                              index=True)
    allocation = relationship('Allocation',
                              back_populates='default_observation_plans')

    target_groups = relationship(
        'Group',
        secondary='default_observationplan_groups',
        passive_deletes=True,
        doc=
        'Groups to share the resulting data from this default request with.',
    )
Ejemplo n.º 9
0
class Comment(Base, CommentMixin):
    """A comment made by a User or a Robot (via the API) on a Source."""

    create = AccessibleIfRelatedRowsAreAccessible(obj='read')

    read = accessible_by_groups_members & AccessibleIfRelatedRowsAreAccessible(
        obj='read')

    update = delete = AccessibleIfUserMatches('author')
Ejemplo n.º 10
0
class GroupAdmissionRequest(Base):
    """Table tracking requests from users to join groups."""

    read = AccessibleIfUserMatches('user') | accessible_by_group_admins
    create = delete = AccessibleIfUserMatches('user')
    update = accessible_by_group_admins

    user_id = sa.Column(
        sa.ForeignKey("users.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
        doc="ID of the User requesting to join the group",
    )
    user = relationship(
        "User",
        foreign_keys=[user_id],
        back_populates="group_admission_requests",
        doc="The User requesting to join a group",
    )
    group_id = sa.Column(
        sa.ForeignKey("groups.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
        doc="ID of the Group to which admission is requested",
    )
    group = relationship(
        "Group",
        foreign_keys=[group_id],
        back_populates="admission_requests",
        doc="The Group to which admission is requested",
    )
    status = sa.Column(
        sa.Enum(
            "pending",
            "accepted",
            "declined",
            name="admission_request_status",
            validate_strings=True,
        ),
        nullable=False,
        default="pending",
        doc=("Admission request status. Can be one of either 'pending', "
             "'accepted', or 'declined'."),
    )
Ejemplo n.º 11
0
class Annotation(Base, AnnotationMixin):
    """A sortable/searchable Annotation on a source, made by a filter or other robot,
    with a set of data as JSON"""

    create = AccessibleIfRelatedRowsAreAccessible(obj='read')
    read = accessible_by_groups_members & AccessibleIfRelatedRowsAreAccessible(
        obj='read')
    update = delete = AccessibleIfUserMatches('author')

    __table_args__ = (UniqueConstraint('obj_id', 'origin'), )
Ejemplo n.º 12
0
class Stream(Base):
    """A data stream producing alerts that can be programmatically filtered
    using a Filter."""

    read = AccessibleIfUserMatches('users')
    create = update = delete = restricted

    name = sa.Column(sa.String,
                     unique=True,
                     nullable=False,
                     doc="Stream name.")
    altdata = sa.Column(
        JSONB,
        nullable=True,
        doc="Misc. metadata stored in JSON format, e.g. "
        "`{'collection': 'ZTF_alerts', selector: [1, 2]}`",
    )

    groups = relationship(
        'Group',
        secondary='group_streams',
        back_populates='streams',
        passive_deletes=True,
        doc="The Groups with access to this Stream.",
    )
    users = relationship(
        'User',
        secondary='stream_users',
        back_populates='streams',
        passive_deletes=True,
        doc="The users with access to this stream.",
    )
    filters = relationship(
        'Filter',
        back_populates='stream',
        passive_deletes=True,
        doc="The filters with access to this stream.",
    )
    photometry = relationship(
        "Photometry",
        secondary="stream_photometry",
        back_populates="streams",
        cascade="save-update, merge, refresh-expire, expunge",
        passive_deletes=True,
        doc='The photometry associated with this stream.',
    )

    photometric_series = relationship(
        "PhotometricSeries",
        secondary="stream_photometric_series",
        back_populates="streams",
        cascade="save-update, merge, refresh-expire, expunge",
        passive_deletes=True,
        doc='Photometric series associated with this stream.',
    )
Ejemplo n.º 13
0
class Candidate(Base):
    "An Obj that passed a Filter, becoming scannable on the Filter's scanning page."
    create = read = update = delete = AccessibleIfUserMatches(
        'filter.group.group_users.user')

    obj_id = sa.Column(
        sa.ForeignKey("objs.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
        doc="ID of the Obj",
    )
    obj = relationship(
        "Obj",
        foreign_keys=[obj_id],
        back_populates="candidates",
        doc="The Obj that passed a filter",
    )
    filter_id = sa.Column(
        sa.ForeignKey("filters.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
        doc="ID of the filter the candidate passed",
    )
    filter = relationship(
        "Filter",
        foreign_keys=[filter_id],
        back_populates="candidates",
        doc="The filter that the Candidate passed",
    )
    passed_at = sa.Column(
        sa.DateTime,
        nullable=False,
        index=True,
        doc="ISO UTC time when the Candidate passed the Filter.",
    )
    passing_alert_id = sa.Column(
        sa.BigInteger,
        index=True,
        doc="ID of the latest Stream alert that passed the Filter.",
    )
    uploader_id = sa.Column(
        sa.ForeignKey("users.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
        doc="ID of the user that posted the candidate",
    )
Ejemplo n.º 14
0
class Comment(Base, CommentMixin):
    """A comment made by a User or a Robot (via the API) on a Source."""

    create = AccessibleIfRelatedRowsAreAccessible(obj='read')

    read = accessible_by_groups_members & AccessibleIfRelatedRowsAreAccessible(
        obj='read')

    update = delete = AccessibleIfUserMatches('author')

    obj_id = sa.Column(
        sa.ForeignKey('objs.id', ondelete='CASCADE'),
        nullable=False,
        index=True,
        doc="ID of the Comment's Obj.",
    )

    obj = relationship(
        'Obj',
        back_populates='comments',
        doc="The Comment's Obj.",
    )
Ejemplo n.º 15
0
class Invitation(Base):

    read = update = delete = AccessibleIfUserMatches('invited_by')

    token = sa.Column(sa.String(), nullable=False, unique=True)
    role_id = sa.Column(
        sa.ForeignKey('roles.id'),
        nullable=False,
    )
    role = relationship(
        "Role",
        cascade="save-update, merge, refresh-expire, expunge",
        passive_deletes=True,
        uselist=False,
    )
    groups = relationship(
        "Group",
        secondary="group_invitations",
        cascade="save-update, merge, refresh-expire, expunge",
        passive_deletes=True,
    )
    streams = relationship(
        "Stream",
        secondary="stream_invitations",
        cascade="save-update, merge, refresh-expire, expunge",
        passive_deletes=True,
    )
    admin_for_groups = sa.Column(psql.ARRAY(sa.Boolean), nullable=False)
    can_save_to_groups = sa.Column(psql.ARRAY(sa.Boolean), nullable=False)
    user_email = sa.Column(EmailType(), nullable=True)
    invited_by = relationship(
        "User",
        secondary="user_invitations",
        cascade="save-update, merge, refresh-expire, expunge",
        passive_deletes=True,
        uselist=False,
    )
    used = sa.Column(sa.Boolean, nullable=False, default=False)
    user_expiration_date = sa.Column(sa.DateTime, nullable=True)
Ejemplo n.º 16
0
class CommentOnShift(Base, CommentMixin):

    __tablename__ = 'comments_on_shifts'

    create = AccessibleIfRelatedRowsAreAccessible(shift='read')

    read = accessible_by_groups_members & AccessibleIfRelatedRowsAreAccessible(
        shift='read', )

    update = delete = AccessibleIfUserMatches('author')

    shift_id = sa.Column(
        sa.ForeignKey('shifts.id', ondelete='CASCADE'),
        nullable=False,
        index=True,
        doc="ID of the Comment's Shift.",
    )
    shift = relationship(
        'Shift',
        back_populates='comments',
        doc="The Shift referred to by this comment.",
    )
Ejemplo n.º 17
0
class CommentOnGCN(Base, CommentMixin):

    __tablename__ = 'comments_on_gcns'

    create = AccessibleIfRelatedRowsAreAccessible(gcn='read')

    read = accessible_by_groups_members & AccessibleIfRelatedRowsAreAccessible(
        spectrum='read', )

    update = delete = AccessibleIfUserMatches('author')

    gcn_id = sa.Column(
        sa.ForeignKey('gcnevents.id', ondelete='CASCADE'),
        nullable=False,
        index=True,
        doc="ID of the Comment's GCN.",
    )
    gcn = relationship(
        'GcnEvent',
        back_populates='comments',
        doc="The GcnEvent referred to by this comment.",
    )
Ejemplo n.º 18
0
class CommentOnSpectrum(Base, CommentMixin):

    __tablename__ = 'comments_on_spectra'

    create = AccessibleIfRelatedRowsAreAccessible(obj='read', spectrum='read')

    read = accessible_by_groups_members & AccessibleIfRelatedRowsAreAccessible(
        obj='read',
        spectrum='read',
    )

    update = delete = AccessibleIfUserMatches('author')

    obj_id = sa.Column(
        sa.ForeignKey('objs.id', ondelete='CASCADE'),
        nullable=False,
        index=True,
        doc="ID of the Comment's Obj.",
    )

    obj = relationship(
        'Obj',
        back_populates='comments_on_spectra',
        doc="The Comment's Obj.",
    )

    spectrum_id = sa.Column(
        sa.ForeignKey('spectra.id', ondelete='CASCADE'),
        nullable=False,
        index=True,
        doc="ID of the Comment's Spectrum.",
    )
    spectrum = relationship(
        'Spectrum',
        back_populates='comments',
        doc="The Spectrum referred to by this comment.",
    )
Ejemplo n.º 19
0
    target_groups = relationship(
        'Group',
        secondary='default_observationplan_groups',
        passive_deletes=True,
        doc=
        'Groups to share the resulting data from this default request with.',
    )


DefaultObservationPlanRequestTargetGroup = join_model(
    'default_observationplan_groups', DefaultObservationPlanRequest, Group)
DefaultObservationPlanRequestTargetGroup.create = (
    DefaultObservationPlanRequestTargetGroup.update
) = DefaultObservationPlanRequestTargetGroup.delete = (
    AccessibleIfUserMatches('defaultobservationplanrequest.requester')
    & DefaultObservationPlanRequestTargetGroup.read)


class ObservationPlanRequest(Base):
    """A request for an EventObservationPlan."""

    # TODO: Make read-accessible via target groups
    create = read = AccessibleIfRelatedRowsAreAccessible(gcnevent="read",
                                                         allocation="read")
    update = delete = (
        (AccessibleIfUserMatches('allocation.group.users')
         | AccessibleIfUserMatches('requester'))
        & read) | CustomUserAccessControl(updatable_by_token_with_listener_acl)

    requester_id = sa.Column(
Ejemplo n.º 20
0
class ObservationPlanRequest(Base):
    """A request for an EventObservationPlan."""

    # TODO: Make read-accessible via target groups
    create = read = AccessibleIfRelatedRowsAreAccessible(gcnevent="read",
                                                         allocation="read")
    update = delete = (
        (AccessibleIfUserMatches('allocation.group.users')
         | AccessibleIfUserMatches('requester'))
        & read) | CustomUserAccessControl(updatable_by_token_with_listener_acl)

    requester_id = sa.Column(
        sa.ForeignKey('users.id', ondelete='SET NULL'),
        nullable=True,
        index=True,
        doc="ID of the User who requested the follow-up.",
    )

    requester = relationship(
        User,
        back_populates='observationplan_requests',
        doc="The User who requested the follow-up.",
        foreign_keys=[requester_id],
    )

    last_modified_by_id = sa.Column(
        sa.ForeignKey('users.id', ondelete='SET NULL'),
        nullable=True,
        doc="The ID of the User who last modified the request.",
    )

    last_modified_by = relationship(
        User,
        doc="The user who last modified the request.",
        foreign_keys=[last_modified_by_id],
    )

    gcnevent = relationship(
        'GcnEvent',
        back_populates='observationplan_requests',
        doc="The target GcnEvent.",
    )
    gcnevent_id = sa.Column(
        sa.ForeignKey('gcnevents.id', ondelete='CASCADE'),
        nullable=False,
        index=True,
        doc="ID of the target GcnEvent.",
    )

    localization = relationship(
        'Localization',
        back_populates='observationplan_requests',
        doc="The target Localization.",
    )
    localization_id = sa.Column(
        sa.ForeignKey('localizations.id', ondelete='CASCADE'),
        nullable=False,
        index=True,
        doc="ID of the target Localization.",
    )

    payload = sa.Column(psql.JSONB,
                        nullable=False,
                        doc="Content of the observation plan request.")

    status = sa.Column(
        sa.String(),
        nullable=False,
        default="pending submission",
        index=True,
        doc="The status of the request.",
    )

    allocation_id = sa.Column(sa.ForeignKey('allocations.id',
                                            ondelete='CASCADE'),
                              nullable=False,
                              index=True)
    allocation = relationship('Allocation', back_populates='observation_plans')

    observation_plans = relationship(
        'EventObservationPlan',
        passive_deletes=True,
        doc='Observation plans associated with this request.',
    )

    target_groups = relationship(
        'Group',
        secondary='observationplan_groups',
        passive_deletes=True,
        doc='Groups to share the resulting data from this request with.',
    )

    transactions = relationship(
        'FacilityTransaction',
        back_populates='observation_plan_request',
        passive_deletes=True,
        order_by="FacilityTransaction.created_at.desc()",
    )

    @property
    def instrument(self):
        return self.allocation.instrument
Ejemplo n.º 21
0
class GcnEvent(Base):
    """Event information, including an event ID, mission, and time of the event."""

    update = delete = AccessibleIfUserMatches('sent_by')

    sent_by_id = sa.Column(
        sa.ForeignKey('users.id', ondelete='CASCADE'),
        nullable=False,
        index=True,
        doc="The ID of the User who created this GcnEvent.",
    )

    sent_by = relationship(
        "User",
        foreign_keys=sent_by_id,
        back_populates="gcnevents",
        doc="The user that saved this GcnEvent",
    )

    dateobs = sa.Column(sa.DateTime,
                        doc='Event time',
                        unique=True,
                        nullable=False)

    gcn_notices = relationship("GcnNotice", order_by=GcnNotice.date)

    _tags = relationship(
        "GcnTag",
        order_by=(
            sa.func.lower(GcnTag.text).notin_(
                {'fermi', 'swift', 'amon', 'lvc'}),
            sa.func.lower(GcnTag.text).notin_({'long', 'short'}),
            sa.func.lower(GcnTag.text).notin_({'grb', 'gw', 'transient'}),
        ),
    )

    localizations = relationship("Localization")

    observationplan_requests = relationship(
        'ObservationPlanRequest',
        back_populates='gcnevent',
        cascade='delete',
        passive_deletes=True,
        doc="Observation plan requests of the event.",
    )

    comments = relationship(
        'CommentOnGCN',
        back_populates='gcn',
        cascade='save-update, merge, refresh-expire, expunge, delete',
        passive_deletes=True,
        order_by="CommentOnGCN.created_at",
        doc="Comments posted about this GCN event.",
    )

    @hybrid_property
    def tags(self):
        """List of tags."""
        return [tag.text for tag in self._tags]

    @tags.expression
    def tags(cls):
        """List of tags."""
        return (DBSession().query(
            GcnTag.text).filter(GcnTag.dateobs == cls.dateobs).subquery())

    @hybrid_property
    def retracted(self):
        """Check if event is retracted."""
        return 'retracted' in self.tags

    @retracted.expression
    def retracted(cls):
        """Check if event is retracted."""
        return sa.literal('retracted').in_(cls.tags)

    @property
    def lightcurve(self):
        """GRB lightcurve URL."""
        try:
            notice = self.gcn_notices[0]
        except IndexError:
            return None
        root = lxml.etree.fromstring(notice.content)
        elem = root.find(".//Param[@name='LightCurve_URL']")
        if elem is None:
            return None
        else:
            try:
                return elem.attrib.get('value',
                                       '').replace('http://', 'https://')
            except Exception:
                return None

    @property
    def gracesa(self):
        """Event page URL."""
        try:
            notice = self.gcn_notices[0]
        except IndexError:
            return None
        root = lxml.etree.fromstring(notice.content)
        elem = root.find(".//Param[@name='EventPage']")
        if elem is None:
            return None
        else:
            try:
                return elem.attrib.get('value', '')
            except Exception:
                return None

    @property
    def graceid(self):
        try:
            notice = self.gcn_notices[0]
        except IndexError:
            return None
        root = lxml.etree.fromstring(notice.content)
        elem = root.find(".//Param[@name='GraceID']")
        if elem is None:
            return None
        else:
            return elem.attrib.get('value', '')

    @property
    def ned_gwf(self):
        """NED URL."""
        return "https://ned.ipac.caltech.edu/gwf/events"

    @property
    def HasNS(self):
        """Checking if GW event contains NS."""
        notice = self.gcn_notices[0]
        root = lxml.etree.fromstring(notice.content)
        elem = root.find(".//Param[@name='HasNS']")
        if elem is None:
            return None
        else:
            try:
                return 'HasNS: ' + elem.attrib.get('value', '')
            except Exception:
                return None

    @property
    def HasRemnant(self):
        """Checking if GW event has remnant matter."""
        notice = self.gcn_notices[0]
        root = lxml.etree.fromstring(notice.content)
        elem = root.find(".//Param[@name='HasRemnant']")
        if elem is None:
            return None
        else:
            try:
                return 'HasRemnant: ' + elem.attrib.get('value', '')
            except Exception:
                return None

    @property
    def FAR(self):
        """Returning event false alarm rate."""
        notice = self.gcn_notices[0]
        root = lxml.etree.fromstring(notice.content)
        elem = root.find(".//Param[@name='FAR']")
        if elem is None:
            return None
        else:
            try:
                return 'FAR: ' + elem.attrib.get('value', '')
            except Exception:
                return None
Ejemplo n.º 22
0
        return cls(
            obj_id=obj_id,
            instrument_id=instrument_id,
            type=type,
            label=label,
            observed_at=observed_at,
            altdata=header,
            **spec_data,
        )


SpectrumReducer = join_model("spectrum_reducers", Spectrum, User)
SpectrumObserver = join_model("spectrum_observers", Spectrum, User)
SpectrumReducer.create = (
    SpectrumReducer.delete
) = SpectrumReducer.update = AccessibleIfUserMatches('spectrum.owner')
SpectrumObserver.create = (
    SpectrumObserver.delete
) = SpectrumObserver.update = AccessibleIfUserMatches('spectrum.owner')

# should be accessible only by spectrumowner ^^

SpectrumReducer.external_reducer = sa.Column(
    sa.String,
    nullable=True,
    doc="The actual reducer for the spectrum, provided as free text if the "
    "reducer is not a user in the database. Separate from the point-of-contact "
    "user designated as reducer",
)
SpectrumObserver.external_observer = sa.Column(
    sa.String,
Ejemplo n.º 23
0
class FollowupRequest(Base):
    """A request for follow-up data (spectroscopy, photometry, or both) using a
    robotic instrument."""

    # TODO: Make read-accessible via target groups
    create = read = AccessibleIfRelatedRowsAreAccessible(obj="read",
                                                         allocation="read")
    update = delete = (
        (AccessibleIfUserMatches('allocation.group.users')
         | AccessibleIfUserMatches('requester'))
        & read) | CustomUserAccessControl(updatable_by_token_with_listener_acl)

    requester_id = sa.Column(
        sa.ForeignKey('users.id', ondelete='SET NULL'),
        nullable=True,
        index=True,
        doc="ID of the User who requested the follow-up.",
    )

    requester = relationship(
        User,
        back_populates='followup_requests',
        doc="The User who requested the follow-up.",
        foreign_keys=[requester_id],
    )

    last_modified_by_id = sa.Column(
        sa.ForeignKey('users.id', ondelete='SET NULL'),
        nullable=True,
        doc="The ID of the User who last modified the request.",
    )

    last_modified_by = relationship(
        User,
        doc="The user who last modified the request.",
        foreign_keys=[last_modified_by_id],
    )

    obj = relationship('Obj',
                       back_populates='followup_requests',
                       doc="The target Obj.")
    obj_id = sa.Column(
        sa.ForeignKey('objs.id', ondelete='CASCADE'),
        nullable=False,
        index=True,
        doc="ID of the target Obj.",
    )

    payload = sa.Column(psql.JSONB,
                        nullable=False,
                        doc="Content of the followup request.")

    status = sa.Column(
        sa.String(),
        nullable=False,
        default="pending submission",
        index=True,
        doc="The status of the request.",
    )

    allocation_id = sa.Column(sa.ForeignKey('allocations.id',
                                            ondelete='CASCADE'),
                              nullable=False,
                              index=True)
    allocation = relationship('Allocation', back_populates='requests')

    transactions = relationship(
        'FacilityTransaction',
        back_populates='followup_request',
        passive_deletes=True,
        order_by="FacilityTransaction.created_at.desc()",
    )

    target_groups = relationship(
        'Group',
        secondary='request_groups',
        passive_deletes=True,
        doc='Groups to share the resulting data from this request with.',
    )

    photometry = relationship('Photometry', back_populates='followup_request')
    spectra = relationship('Spectrum', back_populates='followup_request')

    @property
    def instrument(self):
        return self.allocation.instrument
Ejemplo n.º 24
0
    allocation = relationship('Allocation', back_populates='requests')

    transactions = relationship(
        'FacilityTransaction',
        back_populates='followup_request',
        passive_deletes=True,
        order_by="FacilityTransaction.created_at.desc()",
    )

    target_groups = relationship(
        'Group',
        secondary='request_groups',
        passive_deletes=True,
        doc='Groups to share the resulting data from this request with.',
    )

    photometry = relationship('Photometry', back_populates='followup_request')
    spectra = relationship('Spectrum', back_populates='followup_request')

    @property
    def instrument(self):
        return self.allocation.instrument


FollowupRequestTargetGroup = join_model('request_groups', FollowupRequest,
                                        Group)
FollowupRequestTargetGroup.create = (
    FollowupRequestTargetGroup.update) = FollowupRequestTargetGroup.delete = (
        AccessibleIfUserMatches('followuprequest.requester')
        & FollowupRequestTargetGroup.read)
Ejemplo n.º 25
0
class GcnNotice(Base):
    """Records of ingested GCN notices"""

    update = delete = AccessibleIfUserMatches('sent_by')

    sent_by_id = sa.Column(
        sa.ForeignKey('users.id', ondelete='CASCADE'),
        nullable=False,
        index=True,
        doc="The ID of the User who created this GcnNotice.",
    )

    sent_by = relationship(
        "User",
        foreign_keys=sent_by_id,
        back_populates="gcnnotices",
        doc="The user that saved this GcnNotice",
    )

    ivorn = sa.Column(sa.String,
                      unique=True,
                      index=True,
                      doc='Unique identifier of VOEvent')

    notice_type = sa.Column(
        sa.Enum(gcn.NoticeType),
        nullable=False,
        doc='GCN Notice type',
    )

    stream = sa.Column(sa.String,
                       nullable=False,
                       doc='Event stream or mission (i.e., "Fermi")')

    date = sa.Column(sa.DateTime, nullable=False, doc='UTC message timestamp')

    dateobs = sa.Column(
        sa.ForeignKey('gcnevents.dateobs', ondelete="CASCADE"),
        nullable=False,
        doc='UTC event timestamp',
    )

    content = deferred(
        sa.Column(sa.LargeBinary, nullable=False, doc='Raw VOEvent content'))

    def _get_property(self, property_name, value=None):
        root = lxml.etree.fromstring(self.content)
        path = f".//Param[@name='{property_name}']"
        elem = root.find(path)
        value = float(elem.attrib.get('value', '')) * 100
        return value

    @property
    def has_ns(self):
        return self._get_property(property_name="HasNS")

    @property
    def has_remnant(self):
        return self._get_property(property_name="HasRemnant")

    @property
    def far(self):
        return self._get_property(property_name="FAR")

    @property
    def bns(self):
        return self._get_property(property_name="BNS")

    @property
    def nsbh(self):
        return self._get_property(property_name="NSBH")

    @property
    def bbh(self):
        return self._get_property(property_name="BBH")

    @property
    def mass_gap(self):
        return self._get_property(property_name="MassGap")

    @property
    def noise(self):
        return self._get_property(property_name="Terrestrial")
Ejemplo n.º 26
0
                GroupUser.admin.is_(True)).subquery())
            query = query.join(
                group_user_subq,
                sa.and_(
                    Group.id == group_user_subq.c.group_id,
                    User.id == group_user_subq.c.user_id,
                ),
            )
        return query


accessible_by_group_admins = AccessibleIfGroupUserIsAdminAndUserMatches(
    'group.group_users.user')
accessible_by_admins = AccessibleIfGroupUserIsAdminAndUserMatches(
    'group_users.user')
accessible_by_members = AccessibleIfUserMatches('users')
accessible_by_stream_members = AccessibleIfUserMatches('stream.users')
accessible_by_streams_members = AccessibleIfUserMatches('streams.users')
accessible_by_groups_members = AccessibleIfGroupUserMatches('groups.users')
accessible_by_group_members = AccessibleIfGroupUserMatches('group.users')


def delete_group_access_logic(cls, user_or_token):
    """User can delete a group that is not the sitewide public group, is not
    a single user group, and that they are an admin member of."""
    user_id = UserAccessControl.user_id_from_user_or_token(user_or_token)
    query = (DBSession().query(cls).join(GroupUser).filter(
        cls.name != cfg['misc']['public_group_name']).filter(
            cls.single_user_group.is_(False)))
    if not user_or_token.is_system_admin:
        query = query.filter(GroupUser.user_id == user_id,
Ejemplo n.º 27
0
class Localization(Base):
    """Localization information, including the localization ID, event ID, right
    ascension, declination, error radius (if applicable), and the healpix
    map. The healpix map is a multi-order healpix skymap, and this
    representation of the skymap has many tiles (in the
    LocalizationTile table). Healpix decomposes the sky into a set of equal
    area tiles each with a unique index, convenient for decomposing
    the sphere into subdivisions."""

    update = delete = AccessibleIfUserMatches('sent_by')

    sent_by_id = sa.Column(
        sa.ForeignKey('users.id', ondelete='CASCADE'),
        nullable=False,
        index=True,
        doc="The ID of the User who created this Localization.",
    )

    sent_by = relationship(
        "User",
        foreign_keys=sent_by_id,
        back_populates="localizations",
        doc="The user that saved this Localization",
    )

    nside = 512
    # HEALPix resolution used for flat (non-multiresolution) operations.

    dateobs = sa.Column(
        sa.ForeignKey('gcnevents.dateobs', ondelete="CASCADE"),
        nullable=False,
        index=True,
        doc='UTC event timestamp',
    )

    localization_name = sa.Column(sa.String,
                                  doc='Localization name',
                                  index=True)

    uniq = deferred(
        sa.Column(
            sa.ARRAY(sa.BigInteger),
            nullable=False,
            doc='Multiresolution HEALPix UNIQ pixel index array',
        ))

    probdensity = deferred(
        sa.Column(
            sa.ARRAY(sa.Float),
            nullable=False,
            doc='Multiresolution HEALPix probability density array',
        ))

    distmu = deferred(
        sa.Column(sa.ARRAY(sa.Float),
                  doc='Multiresolution HEALPix distance mu array'))

    distsigma = deferred(
        sa.Column(sa.ARRAY(sa.Float),
                  doc='Multiresolution HEALPix distance sigma array'))

    distnorm = deferred(
        sa.Column(
            sa.ARRAY(sa.Float),
            doc='Multiresolution HEALPix distance normalization array',
        ))

    contour = deferred(sa.Column(JSONB, doc='GeoJSON contours'))

    observationplan_requests = relationship(
        'ObservationPlanRequest',
        back_populates='localization',
        cascade='delete',
        passive_deletes=True,
        doc="Observation plan requests of the localization.",
    )

    @hybrid_property
    def is_3d(self):
        return (self.distmu is not None and self.distsigma is not None
                and self.distnorm is not None)

    @is_3d.expression
    def is_3d(cls):
        return sa.and_(
            cls.distmu.isnot(None),
            cls.distsigma.isnot(None),
            cls.distnorm.isnot(None),
        )

    @property
    def table_2d(self):
        """Get multiresolution HEALPix dataset, probability density only."""
        return Table(
            [np.asarray(self.uniq, dtype=np.int64), self.probdensity],
            names=['UNIQ', 'PROBDENSITY'],
        )

    @property
    def table(self):
        """Get multiresolution HEALPix dataset, probability density and
        distance."""
        if self.is_3d:
            return Table(
                [
                    np.asarray(self.uniq, dtype=np.int64),
                    self.probdensity,
                    self.distmu,
                    self.distsigma,
                    self.distnorm,
                ],
                names=[
                    'UNIQ', 'PROBDENSITY', 'DISTMU', 'DISTSIGMA', 'DISTNORM'
                ],
            )
        else:
            return self.table_2d

    @property
    def flat_2d(self):
        """Get flat resolution HEALPix dataset, probability density only."""
        order = healpy.nside2order(Localization.nside)
        result = ligo_bayestar.rasterize(self.table_2d, order)['PROB']
        return healpy.reorder(result, 'NESTED', 'RING')

    @property
    def flat(self):
        """Get flat resolution HEALPix dataset, probability density and
        distance."""
        if self.is_3d:
            order = healpy.nside2order(Localization.nside)
            t = ligo_bayestar.rasterize(self.table, order)
            result = t['PROB'], t['DISTMU'], t['DISTSIGMA'], t['DISTNORM']
            return healpy.reorder(result, 'NESTED', 'RING')
        else:
            return (self.flat_2d, )
Ejemplo n.º 28
0
GroupCommentOnShift = join_model("group_comments_on_shifts", Group,
                                 CommentOnShift)
GroupCommentOnShift.__doc__ = "Join table mapping Groups to CommentOnShift."
GroupCommentOnShift.delete = GroupCommentOnShift.update = (
    accessible_by_group_admins & GroupCommentOnShift.read)

GroupInvitation = join_model('group_invitations', Group, Invitation)

GroupSourceNotification = join_model('group_notifications', Group,
                                     SourceNotification)
GroupSourceNotification.create = (
    GroupSourceNotification.read) = accessible_by_group_members
GroupSourceNotification.update = (
    GroupSourceNotification.delete
) = accessible_by_group_admins | AccessibleIfUserMatches(
    'sourcenotification.sent_by')

GroupStream = join_model('group_streams', Group, Stream)
GroupStream.__doc__ = "Join table mapping Groups to Streams."
GroupStream.update = restricted
GroupStream.delete = (
    # only admins can delete streams from groups
    accessible_by_group_admins
    & GroupStream.read
) & CustomUserAccessControl(
    # Can only delete a stream from the group if none of the group's filters
    # are operating on the stream.
    lambda cls, user_or_token: DBSession().query(cls).outerjoin(Stream).
    outerjoin(
        Filter,
        sa.and_(Filter.stream_id == Stream.id, Filter.group_id == cls.group_id