Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/local/bin/python
2# encoding: utf-8
3"""
4*The context tab for the PESSTO Object tickets*
6:Author:
7 David Young
8"""
9import sys
10import os
11import datetime
12import re
13import khufu
14from marshall_webapp.templates.commonelements.tickets.single_ticket import ticket_building_blocks
15from marshall_webapp.templates.commonelements import commonutils as cu
18def context_tab(
19 log,
20 request,
21 discoveryDataDictionary,
22 objectAkas,
23 atelData,
24 lightcurveData,
25 transientCrossmatches):
26 """context tab
28 **Key Arguments**
30 - ``log`` -- logger
31 - ``request`` -- the pyramid request
32 - ``discoveryDataDictionary`` -- the unique discoveryData dictionary of the object in the pessto marshall database (from view_object_contextual_data)
33 - ``objectAkas`` -- object akas
34 - ``lightcurveData`` -- the lightcurve data for the objects displayed on the webpage
35 - ``atelData`` -- the atel matches for the objects displayed on the webpage
36 - ``transientCrossmatches`` -- catalogue crossmatches (from sherlock)
39 **Return**
41 - ``context_tab`` -- the lightcurve/context tab for a single ticket on the transient listings page
43 """
44 from marshall_webapp.templates.commonelements.tickets import single_ticket
46 log.debug('starting the ``context_tab`` function')
48 crossmatches = _crossmatch_info_block(
49 log=log,
50 request=request,
51 discoveryDataDictionary=discoveryDataDictionary,
52 transientCrossmatches=transientCrossmatches
53 )
55 crossmatches = khufu.grid_row(
56 responsive=True,
57 columns=crossmatches,
58 htmlId=False,
59 htmlClass=False
60 )
62 aladin = _aladin_block(
63 log=log,
64 request=request,
65 discoveryDataDictionary=discoveryDataDictionary,
66 transientCrossmatches=transientCrossmatches
67 )
69 aladin = khufu.grid_row(
70 responsive=True,
71 columns=aladin,
72 htmlId=False,
73 htmlClass=False
74 )
76 sherlockForm = _sherlock_development_form(
77 log=log,
78 request=request,
79 discoveryDataDictionary=discoveryDataDictionary,
80 transientCrossmatches=transientCrossmatches
81 )
83 context_block = """%(aladin)s %(crossmatches)s %(sherlockForm)s """ % locals(
84 )
85 # context_block = """%(host_stamp)s%(crossmatches)s""" % locals(
86 # )
88 footer = context_footer_bar(
89 log,
90 request=request
91 )
93 context_tab = single_ticket._ticket_tab_template(
94 log,
95 request=request,
96 tabHeader=False,
97 blockList=[context_block],
98 tabFooter=footer,
99 htmlId="contexttab"
100 )
102 log.debug('completed the ``context_tab`` function')
103 return "%(context_tab)s" % locals()
106def context_footer_bar(
107 log,
108 request):
109 """get ticket footer bar
111 **Key Arguments**
113 - ``log`` -- logger
114 - ``discoveryData`` -- the discoveryData for the object
115 - ``lightcurveData`` -- the lightcurve data for the object
118 **Return**
120 - ``context_footer_bar`` -- the ticket footer bar for the pesssto object
122 """
123 lsqExists = False
124 log.debug('starting the ``context_footer_bar`` function')
125 ## VARIABLES ##
127 footerColumn = khufu.grid_column(
128 span=2, # 1-12
129 offset=0, # 1-12
130 content="<BR>" % locals(
131 )
132 )
134 context_footer_bar = khufu.grid_row(
135 responsive=True,
136 columns=footerColumn,
137 htmlId=False,
138 htmlClass="ticketFooter"
139 )
141 return context_footer_bar
144def _host_info_block(
145 log,
146 request,
147 discoveryDataDictionary):
148 """get ticket host info block
150 **Key Arguments**
152 - ``log`` -- logger
153 - ``request`` -- the pyramid request
154 - ``discoveryDataDictionary`` -- a dictionary of the discovery data for this transient.
157 **Return**
159 - ``host_info_block`` -- the ticket identity block for the pesssto object
161 """
162 log.debug('starting the ``host_info_block`` function')
164 title = cu.block_title(
165 log,
166 title="host stamp"
167 )
169 masterName = discoveryDataDictionary["masterName"]
170 sherlockClassification = discoveryDataDictionary["sherlockClassification"]
172 nearestObjectUrl = ""
173 exactLocationUrl = ""
174 ogleStamp = ""
175 ra = discoveryDataDictionary["raDeg"]
176 dec = discoveryDataDictionary["decDeg"]
177 transientBucketId = discoveryDataDictionary["transientBucketId"]
178 if discoveryDataDictionary["sdss_coverage"] == 1:
179 nearestObjectUrl = "http://skyserver.sdss3.org/public/en/tools/explore/obj.aspx?ra=%(ra)s&dec=%(dec)s" % locals(
180 )
181 exactLocationUrl = """http://skyserver.sdss3.org/public/en/tools/chart/image.aspx?ra=%(ra)s&dec=%(dec)s&scale=0.25&opt=GS&width=512&height=512""" % locals(
182 )
183 downloadContextStamp = "caches/transients/%s/sdss_stamp.jpeg" % (
184 discoveryDataDictionary["transientBucketId"],)
185 contextStamp = request.static_url(f'marshall_webapp:{downloadContextStamp}')
186 stampName = "%(masterName)s_sdss_context_image" % locals()
187 elif discoveryDataDictionary["ogle_color_context_stamp"] == 1:
188 downloadContextStamp = "caches/transients/%(transientBucketId)s/ogle_color_context_stamp.png" % locals(
189 )
190 contextStamp = request.static_url(f'marshall_webapp:{downloadContextStamp}')
191 ogleStamp = "OGLE context stamp"
192 stampName = "%(masterName)s_ogle_context_image" % locals()
193 elif discoveryDataDictionary["sdss_coverage"] == 0:
194 contextStamp = 'holder.js/500x500/auto/industrial/text:not in sdss footprint'
195 downloadContextStamp = contextStamp
196 stampName = False
197 else:
198 contextStamp = 'holder.js/500x500/auto/industrial/text:sdss stamp not ready yet'
199 downloadContextStamp = contextStamp
200 stampName = False
201 sdssUrl = """http://skyserver.sdss3.org/public/en/tools/chart/image.aspx?ra=%(ra)s&dec=%(dec)s&scale=0.25&opt=GSP&width=512&height=512&query=G""" % locals(
202 )
204 sdssLinkRow = ""
205 if len(nearestObjectUrl):
206 nearestObjectUrl = khufu.a(
207 content='sdss nearest object',
208 href=nearestObjectUrl,
209 openInNewTab=True
210 )
211 nearestObjectUrl = khufu.coloredText(
212 text=nearestObjectUrl,
213 color="cyan",
214 size=False, # 1-10
215 pull=False, # "left" | "right"
216 )
217 exactLocationUrl = khufu.a(
218 content='exact sdss location',
219 href=exactLocationUrl,
220 openInNewTab=True
221 )
222 exactLocationUrl = khufu.coloredText(
223 text=exactLocationUrl,
224 color="blue",
225 size=False, # 1-10
226 pull=False, # "left" | "right"
227 )
228 sdssLinkRow = khufu.grid_row(
229 responsive=True,
230 columns="    %(ogleStamp)s%(exactLocationUrl)s<br>    %(nearestObjectUrl)s" % locals(
231 ),
232 htmlId=False,
233 htmlClass=False,
234 onPhone=True,
235 onTablet=True,
236 onDesktop=True
237 )
239 if len(ogleStamp):
240 ogleStamp = khufu.coloredText(
241 text="      %(ogleStamp)s" % locals(),
242 color="blue"
243 )
245 sdssLink = khufu.a(
246 content='sdss dr12 location',
247 href=sdssUrl,
248 )
250 if stampName:
251 href = request.route_path(
252 'download', _query={'url': downloadContextStamp, "webapp": "marshall_webapp", "filename": stampName})
253 else:
254 href = False
256 imageModal = khufu.imagingModal(
257 log=log,
258 imagePath=contextStamp,
259 display="rounded", # [ rounded | circle | polaroid | False ]
260 modalHeaderContent="Context Stamp for %(masterName)s" % locals(),
261 modalFooterContent=sdssLink,
262 stampWidth=400,
263 modalImageWidth=400,
264 downloadLink=href)
265 imageModal = imageModal.get()
267 return "%(title)s %(imageModal)s %(sdssLinkRow)s" % locals()
270def _crossmatch_info_block(
271 log,
272 request,
273 discoveryDataDictionary,
274 transientCrossmatches):
275 """get ticket host info block
277 **Key Arguments**
279 - ``log`` -- logger
280 - ``request`` -- the pyramid request
281 - ``discoveryDataDictionary`` -- a dictionary of the discovery data for this transient.
284 **Return**
286 - ``_crossmatch_info_block`` -- the crossmatch info from sherlock for the pesssto object
288 """
289 log.debug('starting the ``_crossmatch_info_block`` function')
291 title = cu.block_title(
292 log,
293 title="crossmatched sources"
294 )
296 # FIND THIS TRANSIENT'S CROSSMATCHES
297 transientBucketId = discoveryDataDictionary["transientBucketId"]
298 cms = []
299 for row in transientCrossmatches:
300 if row["transientBucketId"] == transientBucketId:
301 cms.append(row)
303 masterName = discoveryDataDictionary["masterName"]
304 sherlockClassification = discoveryDataDictionary["sherlockClassification"]
306 sourceIcons = {
307 "galaxy": "spinner5",
308 "agn": "target3",
309 "qso": "target3",
310 "star": "star3",
311 "cv": "sun6",
312 "cb": "sun6",
313 "other": "help2",
314 "unclear": "help2"
315 }
316 transColors = {
317 "sn": "blue",
318 "nt": "magenta",
319 "cv": "green",
320 "agn": "yellow",
321 "variablestar": "orange",
322 "vs": "orange",
323 "bs": "brown",
324 "?": "violet",
325 "unclear": "violet",
326 "kepler": "#dc322f"
327 }
329 table = ""
330 if len(cms) > 0:
332 hs = ["Rank", "Catalogue", "Catalogue ID", "Catalogue Type (& Subtype)", "Classification",
333 "Angular Separation from Transient", "Physical Separation from Transient", "Transient Peak M", "Source Distance", "Source Redshift", "Source Mag"]
334 tableHead = ""
335 for h in hs:
336 th = khufu.th(
337 content=h,
338 color=False
339 )
340 tableHead = """%(tableHead)s%(th)s""" % locals()
341 # xkhufu-th
342 tableHead = khufu.thead(
343 trContent=tableHead
344 )
346 tableBody = []
348 rs = ["rank", "catalogue_table_name", "catalogue_object_id", "catalogue_object_type", "sherlockClassification",
349 "separationArcsec", "physical_separation_kpc", "transientAbsMag", "distance", "z", "best_mag"]
350 for c in cms:
352 # generate object links
353 if c["object_link"]:
354 c["catalogue_object_id"] = khufu.a(
355 content=c["catalogue_object_id"],
356 href=c["object_link"],
357 openInNewTab=True
358 )
360 if c["catalogue_table_name"]:
362 popover = khufu.popover(
363 tooltip=True,
364 placement="top", # [ top | bottom | left | right ]
365 # [ False | click | hover | focus | manual ]
366 trigger="hover",
367 title=c["search_name"],
368 content=False,
369 delay=200
370 )
372 # add text color
373 c["catalogue_table_name"] = khufu.a(
374 content=c["catalogue_table_name"],
375 href="#",
376 popover=popover
377 )
379 # SOURCE MAGNITUDE
380 if c["best_mag"]:
381 m = c["best_mag"]
382 e = c["best_mag_error"]
383 f = c["best_mag_filter"]
384 if e:
385 e = " ± %(e)0.2f" % locals()
386 else:
387 e = ""
388 if isinstance(m, float):
389 c["best_mag"] = "%(f)s = %(m)0.2f%(e)s" % locals()
390 else:
391 c["best_mag"] = m
393 # CLASSIFICATION
394 if c["classificationReliability"] == 1:
395 cText = "transient is <em>synonymous</em> with this source"
396 cLevel = "success"
397 elif c["classificationReliability"] == 2:
398 cText = "transient may be <em>associated</em> with this source"
399 cLevel = "warning"
400 else:
401 cText = "<em>annotation</em> - not to be relied on for predicting transient classification"
402 cLevel = "important"
404 b = khufu.badge(
405 text=c["classificationReliability"],
406 level=cLevel
407 )
408 popover = khufu.popover(
409 tooltip=False,
410 placement="right",
411 trigger="hover",
412 title="<b>Classification Reliability</b>",
413 content=cText,
414 delay=200
415 )
416 b = khufu.a(
417 content=b,
418 href=False,
419 popover=popover
420 )
422 # DISTANCE CURATION
423 if c["z"]:
424 c["distance_method"] = "spec-z distance"
425 col = "success"
426 zbadge = "s"
427 elif c["photoZ"]:
428 c["distance_method"] = "photo-z distance"
429 col = "important"
430 zbadge = "p"
431 if c["direct_distance"]:
432 col = "warning"
433 c["distance"] = c["direct_distance"]
434 c["distance_method"] = "redshift-independent distance"
435 zbadge = "r"
437 # REDSHIFT CURATION
438 if not c["z"]:
439 if c["photoZ"]:
440 pz = c["photoZ"]
441 if c["photoZErr"]:
442 pze = c["photoZErr"]
443 pz = "%(pz)0.3f ± %(pze)0.3f" % locals()
444 b = khufu.badge(
445 text='p',
446 level='important'
447 )
448 popover = khufu.popover(
449 tooltip=True,
450 placement="right",
451 trigger="hover",
452 title="photo-z",
453 content=False,
454 delay=200
455 )
456 b = khufu.a(
457 content=b,
458 href=False,
459 popover=popover
460 )
461 c["z"] = pz + b
463 else:
464 b = khufu.badge(
465 text='s',
466 level='success'
467 )
468 popover = khufu.popover(
469 tooltip=True,
470 placement="right",
471 trigger="hover",
472 title="spec-z",
473 content=False,
474 delay=200
475 )
476 b = khufu.a(
477 content=b,
478 href=False,
479 popover=popover
480 )
481 cz = c["z"]
482 if isinstance(cz, float):
483 c["z"] = "%(cz)0.3f %(b)s" % locals()
485 tableRow = []
487 for r in rs:
488 if c[r] == None:
489 c[r] = "-"
490 if r == "catalogue_object_type":
491 try:
492 icon = sourceIcons[c[r]]
493 except:
494 icon = "help2"
495 if c["catalogue_object_type"].lower() == "other":
496 c["catalogue_object_type"] = c[
497 "catalogue_object_subtype"]
498 thisType = c[r]
500 if c["catalogue_object_subtype"]:
501 thisType += " (" + c["catalogue_object_subtype"] + ")"
503 # add text color
504 c[r] = """<i class="icon-%(icon)s" color="#268bd2"></i> %(thisType)s""" % locals()
506 if isinstance(c[r], float):
507 this = c[r]
508 c[r] = """%(this)0.2f""" % locals()
510 # ADD UNITS
511 if c[r] != "-":
512 if r in ["original_search_radius_arcsec", "major_axis_arcsec"] and c[r] != "-":
513 c[r] = c[r] + '"'
514 if r in ["distance"]:
516 b = khufu.badge(
517 text=zbadge,
518 level=col
519 )
520 popover = khufu.popover(
521 tooltip=True,
522 # [ top | bottom | left | right ]
523 placement="right",
524 # [ False | click | hover | focus | manual ]
525 trigger="hover",
526 title=c["distance_method"],
527 content=False,
528 delay=200
529 )
530 b = khufu.a(
531 content=b,
532 href=False,
533 popover=popover
534 )
536 c[r] = c[r] + ' Mpc ' + b
538 if r in ["physical_separation_kpc"]:
539 c[r] = c[r] + ' Kpc'
540 if r in ["separationArcsec"]:
541 n = c["northSeparationArcsec"]
542 e = c["eastSeparationArcsec"]
543 c[r] = c[r] + '" (%(n)0.2fN, %(e)0.2fE)' % locals()
545 content = khufu.coloredText(
546 text=c[r],
547 color=transColors[c["association_type"].lower()],
548 size=False, # 1-10
549 pull=False, # "left" | "right",
550 addBackgroundColor=False
551 )
552 td = khufu.td(
553 content=content,
554 color=False
555 )
556 tableRow.append(td)
557 # xkhufu-table-cell
558 tr = khufu.tr(
559 cellContent=tableRow,
560 color=transColors[c["association_type"].lower()]
561 )
562 tableBody.append(tr)
564 tableBody = khufu.tbody(
565 trContent=tableBody
566 )
567 table = khufu.table(
568 caption=False,
569 thead=tableHead,
570 tbody=tableBody,
571 striped=False,
572 bordered=False,
573 hover=True,
574 condensed=True
575 )
577 return "%(table)s" % locals()
580def _aladin_block(
581 log,
582 request,
583 discoveryDataDictionary,
584 transientCrossmatches):
585 """get aladin lite instance for transient
587 **Key Arguments**
589 - ``log`` -- logger
590 - ``request`` -- the pyramid request
591 - ``discoveryDataDictionary`` -- a dictionary of the discovery data for this transient.
594 **Return**
596 - ``_aladin_block`` -- the crossmatch info from sherlock for the pesssto object
598 """
599 log.debug('starting the ``_aladin_block`` function')
601 title = cu.block_title(
602 log,
603 title="crossmatch map"
604 )
606 # ADD TEXT COLOR
607 masterName = discoveryDataDictionary["masterName"]
608 text = khufu.coloredText(
609 text="Contextual classification for <em>%(masterName)s</em>" % locals(
610 ),
611 color="grey",
612 size=3, # 1-10
613 pull="right", # "left" | "right",
614 addBackgroundColor=False
615 )
617 masterClassification = discoveryDataDictionary["classification"]
619 # ADD TEXT COLOR
620 masterClassification = khufu.coloredText(
621 text=masterClassification,
622 color="violet",
623 size=6, # 1-10
624 pull="right", # "left" | "right",
625 addBackgroundColor=False
626 )
627 masterClassification = masterClassification + "</br>" + text + "</br>"
629 text = khufu.p(
630 content='',
631 lead=False,
632 textAlign=False, # [ left | center | right ]
633 color=False, # [ muted | warning | info | error | success ]
634 navBar=False,
635 onPhone=True,
636 onTablet=True,
637 onDesktop=True
638 )
640 coords = "%s %s" % (
641 discoveryDataDictionary["raDeg"], discoveryDataDictionary["decDeg"],)
643 name = discoveryDataDictionary["masterName"]
645 # FIND THIS TRANSIENT'S CROSSMATCHES
646 transientBucketId = discoveryDataDictionary["transientBucketId"]
647 cms = []
648 for row in transientCrossmatches:
649 if row["transient_object_id"] == transientBucketId:
650 cms.append(row)
652 masterName = discoveryDataDictionary["masterName"]
653 sherlockClassification = discoveryDataDictionary["sherlockClassification"]
654 if discoveryDataDictionary["ps1_map"] == 1:
655 survey = "P/PanSTARRS/DR1/color/z/zg/g"
656 elif discoveryDataDictionary["sdss_coverage"] == 1:
657 survey = "P/SDSS9/color"
658 else:
659 survey = "P/DSS2/color"
661 aladin = '<div class="aladin-hide" coords="%(coords)s" survey="%(survey)s" data="/marshall/transients/%(transientBucketId)s/context" transient="%(name)s" style="font-family:dryx_icon_font"></div>' % locals(
662 )
664 return "%(masterClassification)s %(aladin)s" % locals()
667def _sherlock_development_form(
668 log,
669 request,
670 discoveryDataDictionary,
671 transientCrossmatches):
672 """*a form for adding comments and confirming/reporting incorrect matches from sherlock trasient classifier*
674 **Key Arguments**
676 - ``log`` -- logger
677 - ``request`` -- the pyramid request
678 - ``discoveryDataDictionary`` -- a dictionary of the discovery data for this transient.):
680 """
682 transientBucketId = discoveryDataDictionary["transientBucketId"]
684 if discoveryDataDictionary["mismatchComment"]:
685 # add text color
686 commentDate = discoveryDataDictionary["commentDate"]
687 if commentDate:
688 commentDate = commentDate.strftime("%Y-%m-%d")
689 else:
690 commentDate = "no comment date"
691 if not discoveryDataDictionary["user"]:
692 discoveryDataDictionary["user"] = "anon"
693 text = khufu.coloredText(
694 text="Mismatch reported by " + discoveryDataDictionary["user"].replace(".", " ").title(
695 ) + ", " + commentDate + ": ",
696 color="red",
697 size=4, # 1-10
698 pull=False, # "left" | "right",
699 addBackgroundColor=False
700 )
702 currentComment = khufu.coloredText(
703 text=text + discoveryDataDictionary["mismatchComment"],
704 color="grey",
705 size=3, # 1-10
706 pull=False, # "left" | "right",
707 addBackgroundColor=False
708 )
709 else:
710 currentComment = ""
712 if len(currentComment) == 0:
714 button = khufu.button(
715 buttonText='mismatch',
716 # [ default | primary | info | success | warning | danger | inverse | link ]
717 buttonStyle='success',
718 buttonSize='default', # [ large | default | small | mini ]
719 htmlId=False,
720 href=False,
721 pull=False, # right, left, center
722 submit=True,
723 block=False,
724 disable=False,
725 postInBackground=False,
726 dataToggle=False, # [ modal ]
727 popover=False
728 )
730 correctMatchInput = khufu.formInput(
731 # [ text | password | datetime | datetime-local | date | month | time | week | number | float | email | url | search | tel | color ]
732 ttype='text',
733 placeholder='comment ...',
734 span=10,
735 htmlId="sherlockMatchComment",
736 searchBar=False,
737 pull=False,
738 prepend=False,
739 append=False,
740 prependDropdown=False,
741 appendDropdown=False,
742 inlineHelpText=False,
743 blockHelpText=False,
744 focusedInputText=False,
745 required=True,
746 disabled=False
747 )
748 correctMatchInput = khufu.controlRow(
749 inputList=[correctMatchInput, button]
750 )
751 correctMatchLabel = khufu.horizontalFormControlLabel(
752 labelText='If you think the first ranked transient-host match is obviously incorrect please report the mismatch to help improve the contextual classifier',
753 forId="sherlockMatchComment"
754 )
756 correctMatchCG = khufu.horizontalFormControlGroup(
757 content=correctMatchLabel + correctMatchInput,
758 validationLevel=False
759 )
760 # xkhufu-tmpx-form-control-groue
762 sherlockForm = khufu.form(
763 content=correctMatchCG, # list of control groups
764 # [ "inline" | "horizontal" | "search" | "navbar-form" | "navbar-search" ]
765 formType='inline',
766 navBarPull=False, # [ false | right | left ],
767 postToScript="/marshall/transients/%(transientBucketId)s/context" % locals(),
768 htmlId="sherlockDevelopment",
769 postInBackground=False,
770 htmlClass=False,
771 redirectUrl="/marshall/transients/%(transientBucketId)s" % locals(),
772 span=10,
773 offset=1
774 )
776 currentComment = khufu.grid_column(
777 span=6, # 1-12
778 offset=1, # 1-12
779 content=currentComment,
780 pull=False, # ["right", "left", "center"]
781 htmlId=False,
782 htmlClass=False,
783 onPhone=True,
784 onTablet=True,
785 onDesktop=True
786 )
787 else:
788 sherlockForm = ""
790 currentComment = khufu.grid_column(
791 span=6, # 1-12
792 offset=0, # 1-12
793 content=currentComment,
794 pull=False, # ["right", "left", "center"]
795 htmlId=False,
796 htmlClass=False,
797 onPhone=True,
798 onTablet=True,
799 onDesktop=True
800 )
802 return currentComment + sherlockForm
804 # xt-class-method