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, ), {}
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}')
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)
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']
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()
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})