Beispiel #1
0
    def process_args(self, event):
        notice_type = get_notice_type(event.element)

        # Just cast to enum for prettier log messages
        try:
            notice_type = NoticeType(notice_type)
        except ValueError:
            pass

        return notice_type, (event.raw_bytes, ), {}
Beispiel #2
0
def handle(payload, root):
    notice_type = gcn.get_notice_type(root)
    if notice_type in notice_types:
        user_id = 1
        with DBSession() as session:

            default_observation_plans = session.query(
                DefaultObservationPlanRequest).all()
            gcn_observation_plans = []
            for plan in default_observation_plans:
                allocation = (session.query(Allocation).filter(
                    Allocation.id == plan.allocation_id).first())

                gcn_observation_plan = {}
                gcn_observation_plan[
                    'allocation-proposal_id'] = allocation.proposal_id
                gcn_observation_plan['payload'] = plan.payload
                gcn_observation_plans.append(gcn_observation_plan)
            gcn_observation_plans = gcn_observation_plans + config_gcn_observation_plans

            event_id = post_gcnevent_from_xml(payload, user_id, session)
            event = session.query(GcnEvent).get(event_id)

            start_date = str(datetime.utcnow()).replace("T", "")
            end_date = str(datetime.utcnow() + timedelta(days=1)).replace(
                "T", "")
            for ii, gcn_observation_plan in enumerate(gcn_observation_plans):
                proposal_id = gcn_observation_plan['allocation-proposal_id']
                allocation = (session.query(Allocation).filter(
                    Allocation.proposal_id == proposal_id).first())

                if allocation is not None:
                    payload = {
                        **gcn_observation_plan['payload'],
                        'start_date':
                        start_date,
                        'end_date':
                        end_date,
                        'queue_name':
                        f'{allocation.instrument.name}-{start_date}-{ii}',
                    }
                    plan = {
                        'payload': payload,
                        'allocation_id': allocation.id,
                        'gcnevent_id': event.id,
                        'localization_id': event.localizations[-1].id,
                    }

                    post_observation_plan(plan, user_id, session)
                else:
                    log(f'No allocation with proposal_id {proposal_id}')
Beispiel #3
0
def handle(payload, root):
    with app.app_context():
        dateobs = get_dateobs(root)

        event = models.db.session.merge(models.Event(dateobs=dateobs))
        old_tags = set(event.tags)
        tags = [
            models.Tag(dateobs=event.dateobs, text=_) for _ in get_tags(root)
        ]

        gcn_notice = models.GcnNotice(
            content=payload,
            ivorn=root.attrib['ivorn'],
            notice_type=gcn.get_notice_type(root),
            stream=urlparse(root.attrib['ivorn']).path.lstrip('/'),
            date=root.find('./Who/Date').text,
            dateobs=event.dateobs)

        for tag in tags:
            models.db.session.merge(tag)
        models.db.session.merge(gcn_notice)
        models.db.session.commit()
        new_tags = set(event.tags)

        skymap = get_skymap(gcn_notice, root)
        if skymap is not None:
            (skymap | group(
                *(tasks.tiles.tile.s(dateobs, tele.telescope, **
                                     tele.default_plan_args)
                  for tele in models.Telescope.query),
                tasks.skymaps.contour.s(dateobs))).delay()

        old_alertable = bool((DESIRABLE_TAGS & old_tags)
                             and not (UNDESIRABLE_TAGS & old_tags))
        new_alertable = bool((DESIRABLE_TAGS & new_tags)
                             and not (UNDESIRABLE_TAGS & new_tags))
        if old_alertable != new_alertable:
            tasks.twilio.call_everyone.delay('event_new_voice',
                                             dateobs=dateobs)
            tasks.twilio.text_everyone.delay(
                render_template('event_new_text.txt', event=event))
            tasks.email.email_everyone.delay(dateobs)
            tasks.slack.slack_everyone.delay(dateobs)
Beispiel #4
0
def get_tags(root):
    """Get source classification tag strings from GCN notice."""
    # Get event stream.
    mission = urlparse(root.attrib['ivorn']).path.lstrip('/')
    yield mission

    # What type of burst is this: GRB or GW?
    try:
        value = root.find("./Why/Inference/Concept").text
    except AttributeError:
        pass
    else:
        if value == 'process.variation.burst;em.gamma':
            yield 'GRB'
        elif value == 'process.variation.trans;em.gamma':
            yield 'transient'

    # LIGO/Virgo alerts don't provide the Why/Inference/Concept tag,
    # so let's just identify it as a GW event based on the notice type.
    notice_type = gcn.get_notice_type(root)
    if notice_type in {
            gcn.NoticeType.LVC_PRELIMINARY, gcn.NoticeType.LVC_INITIAL,
            gcn.NoticeType.LVC_UPDATE, gcn.NoticeType.LVC_RETRACTION
    }:
        yield 'GW'

    # Is this a retracted LIGO/Virgo event?
    if notice_type == gcn.NoticeType.LVC_RETRACTION:
        yield 'retracted'

    # Is this a short GRB, or a long GRB?
    try:
        value = root.find(".//Param[@name='Long_short']").attrib['value']
    except AttributeError:
        pass
    else:
        if value != 'unknown':
            yield value.lower()

    # Gaaaaaah! Alerts of type FERMI_GBM_SUBTHRESH store the
    # classification in a different property!
    try:
        value = root.find(
            ".//Param[@name='Duration_class']").attrib['value'].title()
    except AttributeError:
        pass
    else:
        if value != 'unknown':
            yield value.lower()

    # Get LIGO/Virgo source classification, if present.
    classifications = [
        (float(elem.attrib['value']), elem.attrib['name'])
        for elem in root.iterfind("./What/Group[@type='Classification']/Param")
    ]
    if classifications:
        _, classification = max(classifications)
        yield classification

    search = root.find("./What/Param[@name='Search']")
    if search is not None:
        yield search.attrib['value']
Beispiel #5
0
    def post(self):
        """
        ---
        description: Ingest GCN xml file
        tags:
          - gcnevents
          - gcntags
          - gcnnotices
          - localizations
        requestBody:
          content:
            application/json:
              schema: GcnHandlerPut
        responses:
          200:
            content:
              application/json:
                schema: Success
          400:
            content:
              application/json:
                schema: Error
        """
        data = self.get_json()
        payload = data['xml']

        schema = f'{os.path.dirname(__file__)}/../../utils/schema/VOEvent-v2.0.xsd'
        voevent_schema = xmlschema.XMLSchema(schema)
        if voevent_schema.is_valid(payload):
            root = lxml.etree.fromstring(payload.encode('ascii'))
        else:
            raise Exception("xml file is not valid VOEvent")

        dateobs = get_dateobs(root)

        try:
            event = GcnEvent.query.filter_by(dateobs=dateobs).one()

            if not event.is_accessible_by(self.current_user, mode="update"):
                return self.error(
                    "Insufficient permissions: GCN event can only be updated by original poster"
                )

        except NoResultFound:
            event = GcnEvent(dateobs=dateobs,
                             sent_by_id=self.associated_user_object.id)
            DBSession().add(event)

        tags = [
            GcnTag(
                dateobs=event.dateobs,
                text=text,
                sent_by_id=self.associated_user_object.id,
            ) for text in get_tags(root)
        ]

        gcn_notice = GcnNotice(
            content=payload.encode('ascii'),
            ivorn=root.attrib['ivorn'],
            notice_type=gcn.get_notice_type(root),
            stream=urlparse(root.attrib['ivorn']).path.lstrip('/'),
            date=root.find('./Who/Date').text,
            dateobs=event.dateobs,
            sent_by_id=self.associated_user_object.id,
        )

        for tag in tags:
            DBSession().add(tag)
        DBSession().add(gcn_notice)

        skymap = get_skymap(root, gcn_notice)
        skymap["dateobs"] = event.dateobs
        skymap["sent_by_id"] = self.associated_user_object.id

        try:
            localization = (Localization.query_records_accessible_by(
                self.current_user, ).filter(
                    Localization.dateobs == dateobs,
                    Localization.localization_name ==
                    skymap["localization_name"],
                ).one())
        except NoResultFound:
            localization = Localization(**skymap)
            localization = get_contour(localization)
            DBSession().add(localization)

        self.verify_and_commit()

        return self.success()
Beispiel #6
0
    def post(self):
        """
        ---
        description: Ingest GCN xml file
        tags:
          - gcnevents
          - gcntags
          - gcnnotices
          - localizations
        requestBody:
          content:
            application/json:
              schema: GcnHandlerPut
        responses:
          200:
            content:
              application/json:
                schema: Success
                properties:
                  data:
                    type: object
                    properties:
                      gcnevent_id:
                        type: integer
                        description: New GcnEvent ID
          400:
            content:
              application/json:
                schema: Error
        """
        data = self.get_json()
        if 'xml' not in data:
            return self.error("xml must be present in data to parse GcnEvent")

        payload = data['xml']

        schema = f'{os.path.dirname(__file__)}/../../utils/schema/VOEvent-v2.0.xsd'
        voevent_schema = xmlschema.XMLSchema(schema)
        if voevent_schema.is_valid(payload):
            root = lxml.etree.fromstring(payload.encode('ascii'))
        else:
            self.error("xml file is not valid VOEvent")

        dateobs = get_dateobs(root)

        try:
            event = GcnEvent.query.filter_by(dateobs=dateobs).one()

            if not event.is_accessible_by(self.current_user, mode="update"):
                return self.error(
                    "Insufficient permissions: GCN event can only be updated by original poster"
                )

        except NoResultFound:
            event = GcnEvent(dateobs=dateobs,
                             sent_by_id=self.associated_user_object.id)
            DBSession().add(event)

        tags = [
            GcnTag(
                dateobs=event.dateobs,
                text=text,
                sent_by_id=self.associated_user_object.id,
            ) for text in get_tags(root)
        ]

        gcn_notice = GcnNotice(
            content=payload.encode('ascii'),
            ivorn=root.attrib['ivorn'],
            notice_type=gcn.get_notice_type(root),
            stream=urlparse(root.attrib['ivorn']).path.lstrip('/'),
            date=root.find('./Who/Date').text,
            dateobs=event.dateobs,
            sent_by_id=self.associated_user_object.id,
        )

        for tag in tags:
            DBSession().add(tag)
        DBSession().add(gcn_notice)

        skymap = get_skymap(root, gcn_notice)
        if skymap is None:
            return self.success(
                f"Event {event.dateobs} does not have skymap. Returning.")

        skymap["dateobs"] = event.dateobs
        skymap["sent_by_id"] = self.associated_user_object.id

        try:
            localization = (Localization.query_records_accessible_by(
                self.current_user, ).filter_by(
                    dateobs=dateobs,
                    localization_name=skymap["localization_name"],
                ).one())
        except NoResultFound:
            localization = Localization(**skymap)
            DBSession().add(localization)
            DBSession().commit()

            log(f"Generating tiles/contours for localization {localization.id}"
                )

            IOLoop.current().run_in_executor(
                None, lambda: add_tiles(localization.id))
            IOLoop.current().run_in_executor(
                None, lambda: add_contour(localization.id))

        return self.success(data={'gcnevent_id': event.id})