diff --git a/doc/data/filter_examples/fritz_filter_clu.json b/doc/data/filter_examples/fritz_filter_clu.json index 9192a030..b8228898 100644 --- a/doc/data/filter_examples/fritz_filter_clu.json +++ b/doc/data/filter_examples/fritz_filter_clu.json @@ -556,104 +556,196 @@ { "$project": { "objectId": 1, - "cross_matches_CLU": 1, - "annotations.FWHM": "$fwhm", - "annotations.drb": "$drb", + "annotations.FWHM": { + "$round": [ + "$fwhm", 3 + ] + }, + "annotations.drb": { + "$round": [ + "$drb", 5 + ] + }, "annotations.host g-r": { - "$subtract": [ - "$sgmag", "$srmag" + "$round": [ + { + "$subtract": [ + "$sgmag", "$srmag" + ] + }, 3 ] }, "annotations.host r-i": { - "$subtract": [ - "$srmag", "$simag" + "$round": [ + { + "$subtract": [ + "$srmag", "$simag" + ] + }, 3 + ] + }, + "annotations.mag at max": { + "$round": [ + "$m_max", 3 ] }, - "annotations.mag at max": "$m_max", "annotations.time at max": "$t_max", - "annotations.min-mag": "$m_min", + "annotations.min-mag": { + "$round": [ + "$m_min", 3 + ] + }, "annotations.min-time": "$t_min", "annotations.time difference": { - "$subtract": [ - "$t_max", "$t_min" + "$round": [ + { + "$subtract": [ + "$t_max", "$t_min" + ] + }, 5 ] }, "annotations.mag diff": { - "$subtract": [ - "$m_min", "$m_max" + "$round": [ + { + "$subtract": [ + "$m_min", "$m_max" + ] + }, 3 ] }, "annotations.rise rate": { - "$cond": { - "if": { - "$gt": [ - { - "$subtract": [ - "$t_max", "$t_min" - ] - }, 0 - ] - }, - "then": { - "$divide": [ - { - "$subtract": [ - "$m_min", "$m_max" + "$round": [ + { + "$cond": { + "if": { + "$gt": [ + { + "$subtract": [ + "$t_max", "$t_min" + ] + }, 0 ] - }, { - "$subtract": [ - "$t_max", "$t_min" + }, + "then": { + "$divide": [ + { + "$subtract": [ + "$m_min", "$m_max" + ] + }, { + "$subtract": [ + "$t_max", "$t_min" + ] + } ] - } - ] - }, - "else": null - } + }, + "else": null + } + }, 5 + ] }, "annotations.decay rate": { - "$cond": { - "if": { - "$lt": [ - { - "$subtract": [ - "$t_max", "$t_min" - ] - }, 0 - ] - }, - "then": { - "$divide": [ - { - "$subtract": [ - "$m_max", "$m_min" + "$round": [ + { + "$cond": { + "if": { + "$lt": [ + { + "$subtract": [ + "$t_max", "$t_min" + ] + }, 0 ] - }, { - "$subtract": [ - "$t_max", "$t_min" + }, + "then": { + "$divide": [ + { + "$subtract": [ + "$m_max", "$m_min" + ] + }, { + "$subtract": [ + "$t_max", "$t_min" + ] + } ] - } - ] - }, - "else": null - } + }, + "else": null + } + }, 5 + ] + }, + "annotations.host ZTF ref PSF r-mag": { + "$round": [ + "$magnr", 3 + ] + }, + "annotations.PS1 psf r-mag": { + "$round": [ + "$srmag", 3 + ] + }, + "annotations.rb score": { + "$round": [ + "$rbscore", 5 + ] + }, + "annotations.sgscore1": { + "$round": [ + "$sgscore", 5 + ] + }, + "annotations.ZOGI scorr": { + "$round": [ + "$scorr", 3 + ] + }, + "annotations.distpsnr1": { + "$round": [ + "$distpsnr1", 3 + ] + }, + "annotations.distpsnr2": { + "$round": [ + "$distpsnr2", 3 + ] + }, + "annotations.distpsnr3": { + "$round": [ + "$distpsnr3", 3 + ] + }, + "annotations.magpsf": { + "$round": [ + "$m_now", 3 + ] + }, + "annotations.elongation": { + "$round": [ + "$elong", 3 + ] + }, + "annotations.magap_min_magpsf": { + "$round": [ + "$psfminap", 3 + ] + }, + "annotations.gal_lat": { + "$round": [ + "$gal_lat", 5 + ] }, - "annotations.host ZTF ref PSF r-mag": "$magnr", - "annotations.PS1 psf r-mag": "$srmag", - "annotations.rb score": "$rbscore", - "annotations.sgscore1": "$sgscore", - "annotations.ZOGI scorr": "$scorr", - "annotations.distpsnr1": "$distpsnr1", - "annotations.distpsnr2": "$distpsnr2", - "annotations.distpsnr3": "$distpsnr3", - "annotations.magpsf": "$m_now", - "annotations.elongation": "$elong", - "annotations.magap_min_magpsf": "$psfminap", - "annotations.gal_lat": "$gal_lat", "annotations.deltajd": { - "$subtract": [ - "$jdendhist", "$jdstarthist" + "$round": [ + { + "$subtract": [ + "$jdendhist", "$jdstarthist" + ] + }, 5 ] - } + }, + "annotations.cross_matches_CLU": "$cross_matches_CLU" } } ] diff --git a/docker-compose.traefik.defaults.yaml b/docker-compose.traefik.defaults.yaml index 8214109e..817c5dab 100644 --- a/docker-compose.traefik.defaults.yaml +++ b/docker-compose.traefik.defaults.yaml @@ -12,6 +12,8 @@ services: command: # fixme: comment out if do not want the traefik dashboard on port 8090 - "--api.insecure=true" + - "--metrics=true" + - "--metrics.prometheus=true" - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--providers.docker.network=proxy" diff --git a/extensions/skyportal/data/db_fritz.yaml b/extensions/skyportal/data/db_fritz.yaml index 82538adb..33d5e8f3 100644 --- a/extensions/skyportal/data/db_fritz.yaml +++ b/extensions/skyportal/data/db_fritz.yaml @@ -2,9 +2,12 @@ user: - username: fritz.astro.marshal@gmail.com roles: - Super admin + =id: fritz + oauth_uid: fritz.astro.marshal@gmail.com - username: kowalski@caltech.edu roles: - Group admin + =id: kowalski streams: - name: ZTF Public @@ -48,6 +51,20 @@ groups: =id: superluminous_sne - name: X-ray Counterparts =id: xray_counterparts + - name: Outbursting Stars + =id: outbursting_stars + +streams/=ztf_public/users: + - user_id: =fritz + - user_id: =kowalski + +streams/=ztf_partnership/users: + - user_id: =fritz + - user_id: =kowalski + +streams/=ztf_caltech/users: + - user_id: =fritz + - user_id: =kowalski groups/=ztf_science_validation/streams: - stream_id: =ztf_public @@ -83,6 +100,9 @@ groups/=superluminous_sne/streams: groups/=xray_counterparts/streams: - stream_id: =ztf_partnership +groups/=outbursting_stars/streams: + - stream_id: =ztf_partnership + filters: - name: Public Transients group_id: =ztf_science_validation @@ -124,6 +144,10 @@ filters: group_id: =xray_counterparts stream_id: =ztf_partnership + - name: Outbursting Stars + group_id: =outbursting_stars + stream_id: =ztf_partnership + telescope: file: telescopes_fritz.yaml @@ -131,16 +155,93 @@ instrument: file: instruments_fritz.yaml taxonomy: - file: taxonomy_sitewide.yaml + file: taxonomy_fritz.yaml allocation: - pi: none, test allocation only, targets will not be observed proposal_id: SEDM-001 - start_date: "2020-09-01T00:00:00" - end_date: "2020-09-14T00:00:00" + start_date: "2020-09-15T00:00:00" + end_date: "2020-10-15T00:00:00" hours_allocated: 100 group_id: =public_group_id instrument_id: =SEDM + - pi: none, test allocation only, targets will not be observed + proposal_id: SEDM-001 + start_date: "2020-09-15T00:00:00" + end_date: "2020-10-15T00:00:00" + hours_allocated: 100 + group_id: =ztf_science_validation + instrument_id: =SEDM + - pi: none, test allocation only, targets will not be observed + proposal_id: SEDM-001 + start_date: "2020-09-15T00:00:00" + end_date: "2020-10-15T00:00:00" + hours_allocated: 100 + group_id: =rcf + instrument_id: =SEDM + - pi: none, test allocation only, targets will not be observed + proposal_id: SEDM-001 + start_date: "2020-09-15T00:00:00" + end_date: "2020-10-15T00:00:00" + hours_allocated: 100 + group_id: =clu + instrument_id: =SEDM + - pi: none, test allocation only, targets will not be observed + proposal_id: SEDM-001 + start_date: "2020-09-15T00:00:00" + end_date: "2020-10-15T00:00:00" + hours_allocated: 100 + group_id: =em_gw + instrument_id: =SEDM + - pi: none, test allocation only, targets will not be observed + proposal_id: SEDM-001 + start_date: "2020-09-15T00:00:00" + end_date: "2020-10-15T00:00:00" + hours_allocated: 100 + group_id: =short_grb + instrument_id: =SEDM + - pi: none, test allocation only, targets will not be observed + proposal_id: SEDM-001 + start_date: "2020-09-15T00:00:00" + end_date: "2020-10-15T00:00:00" + hours_allocated: 100 + group_id: =infant_sne + instrument_id: =SEDM + - pi: none, test allocation only, targets will not be observed + proposal_id: SEDM-001 + start_date: "2020-09-15T00:00:00" + end_date: "2020-10-15T00:00:00" + hours_allocated: 100 + group_id: =fast_transients + instrument_id: =SEDM + - pi: none, test allocation only, targets will not be observed + proposal_id: SEDM-001 + start_date: "2020-09-15T00:00:00" + end_date: "2020-10-15T00:00:00" + hours_allocated: 100 + group_id: =red_transients + instrument_id: =SEDM + - pi: none, test allocation only, targets will not be observed + proposal_id: SEDM-001 + start_date: "2020-09-15T00:00:00" + end_date: "2020-10-15T00:00:00" + hours_allocated: 100 + group_id: =superluminous_sne + instrument_id: =SEDM + - pi: none, test allocation only, targets will not be observed + proposal_id: SEDM-001 + start_date: "2020-09-15T00:00:00" + end_date: "2020-10-15T00:00:00" + hours_allocated: 100 + group_id: =xray_counterparts + instrument_id: =SEDM + - pi: none, test allocation only, targets will not be observed + proposal_id: SEDM-001 + start_date: "2020-09-15T00:00:00" + end_date: "2020-10-15T00:00:00" + hours_allocated: 100 + group_id: =outbursting_stars + instrument_id: =SEDM observing_run: - pi: Mansi Kasliwal diff --git a/extensions/skyportal/data/instruments_fritz.yaml b/extensions/skyportal/data/instruments_fritz.yaml index b180638b..ef2513eb 100644 --- a/extensions/skyportal/data/instruments_fritz.yaml +++ b/extensions/skyportal/data/instruments_fritz.yaml @@ -17,6 +17,7 @@ name: SEDM telescope_id: =P60 type: imaging spectrograph + api_classname: SEDMAPI - =id: DBSP band: optical name: DBSP diff --git a/extensions/skyportal/data/taxonomy_fritz.yaml b/extensions/skyportal/data/taxonomy_fritz.yaml new file mode 100644 index 00000000..46ee7729 --- /dev/null +++ b/extensions/skyportal/data/taxonomy_fritz.yaml @@ -0,0 +1,806 @@ +- group_ids: + - =public_group_id + - =ztf_science_validation + - =rcf + - =clu + - =em_gw + - =short_grb + - =infant_sne + - =fast_transients + - =red_transients + - =superluminous_sne + - =xray_counterparts + hierarchy: + class: Time-domain Source + subclasses: + - class: Nonstellar + subclasses: + - class: DM annihilation + other names: + - dark matter annihilation + - class: Solar System Object + other names: + - SSO + - class: Galactic Nuclei + subclasses: + - class: Tidal Disruption Event + other names: + - TDE + - class: AGN + other names: + - Active Galactic Nuclei + subclasses: + - class: Seyfert + other names: + - Sy + - class: QSO + other names: + - Quasistellar Object + - class: Blazar + subclasses: + - class: OVV + other names: + - optically violent variable + - class: BL Lac + - class: gravitational lensing + other names: + - lensing + tags: + - time delay + tags: + - extragalactic + - black hole + - class: Stellar variable + subclasses: + - class: Cataclysmic + subclasses: + - class: compact merger + other names: + - gravitational wave event + subclasses: + - class: DNS + other names: + - double neutron star + - NS-NS + subclasses: + - class: kilonova + other names: + - KN + tags: + - neutron star + - short GRB + - class: NS-BH + other names: + - NSBH + subclasses: + - class: kilonova + other names: + - KN + tags: + - black hole + - neutron star + - short GRB + - class: GRB + other names: + - Gamma-ray bursts + - GRBs + subclasses: + - class: short GRB + other names: + - SHB + - sGRB + tags: + - gravitational waves + - compact object + - class: long GRB + other names: + - LSB + - lGRB + - long-soft GRB + - class: Supernova + other names: + - SN + - sn + - sne + - supernova + - supernovae + subclasses: + - class: Type I + other names: + - SN Type I + subclasses: + - class: Ia + other names: + - Ia-p + - SN Ia + - SNIa + - supernovae Ia + - SNe Ia + subclasses: + - class: Ia-pec + other names: + - SN Ia-pec + - SNIa-pec + - Ia-p + tags: + - peculiar + tags: + - white dwarf + - cosmology + - thermonuclear + - class: Ib + other names: + - SN Ib + - SNIb + - supernovae Ib + - SNe Ib + subclasses: + - class: Ib-p + other names: + - SN Ib-p + tags: + - core collapse + - helium rich + - class: Ic + other names: + - SN Ic + - SNIc + - supernovae Ic + - SNe Ic + subclasses: + - class: Ic-BL + other names: + - SN IcBL + - SN Ic-BL + tags: + - broad lines + - high-velocity + - massive stars + - class: SLSN I + other names: + - SLSN Type I + tags: + - bright + - super-luminous + - class: Ic-p + other names: + - SN Ic-p + tags: + - peculiar + tags: + - core collapse + - no helium + - class: Ib/c + other names: + - SN Ib/c + - SNIb/c + - SN Ibc + tags: + - core collapse + - unclear which subclass + tags: + - hydrogen poor + - class: Type II + other names: + - SN Type II + subclasses: + - class: IIn + other names: + - SN IIn + - SNe IIn + sublasses: + - class: SNLN II + other names: + - SLSN Type II + tags: + - bright + - super-luminous + tags: + - narrow line + - class: IIP + other names: + - SNIIP + - SN II-P + - SN Type IIP + tags: + - plateau + - class: IIL + other names: + - SNIIP + - SN II-L + - SN Type IIL + tags: + - linear + - class: IIb + other names: + - SNIIb + - SN II-b + - SN Type IIb + tags: + - helium rich + - class: IIpec + comments: SN 1987a + other names: + - SNIIp + - SN II-p + - SN Type IIp + - IIpec + - II-p + tags: + - peculiar + tags: hydrogen rich + tags: + - explosive + - transient + - class: Novae + subclasses: + - class: Classical Nova + other names: [] + - class: Nova-like + other names: [] + subclasses: + - class: SW Sex + other names: [] + - class: VY Scl + other names: [] + - class: UX UMa + other names: [] + - class: AM CVn + other names: [] + - class: Polars + subclasses: + - class: AM Her + other names: [] + - class: DQ Her + other names: [] + - class: Recurrent Nova + other names: [] + - class: U Gem + subclasses: + - class: SS Cyg + other names: [] + - class: Z Cam + other names: [] + - class: SU UMaj + other names: [] + subclasses: + - class: ER U Maj + other names: [] + - class: WZ Sge + other names: [] + tags: + - binary + - class: Eclipsing + subclasses: + - class: Beta Lyrae + other names: + - EB + - B Lyr + tags: + - types A/B + - ellipsoidal + - giant/supergiant + - accretion disk + - class: X-Ray Burster + other names: + - XB + - XRay Burster + tags: + - neutron star + - main sequence companion + - standard candle + - class: System with Planet(s) + other names: + - EP + - System with planet + - Binary star system w/ planet + - Binary star system with planet + - Planetary transit + tags: + - exoplanet + - class: RS CVn + other names: + - RS Canum Venaticorum + - RS Can Venat + - RS + - radio + - X-ray + - Ca II + tags: + - active chromosphere + - eruptive + - class: W Ursae Maj + other names: + - EW + - W Ursae Majoris + - W UMa + subclasses: + - class: EWa + other names: + - A-type W UMa + - W Uma a + tags: + - types A/F + - class: EWw + other names: + - W-type W UMa + - W UMa w + tags: + - types G/K + tags: + - contact binary + - ellipsoidal + - class: Algol + other names: + - EA + - Algol-type + - Beta Persei-type + - Beta Per + tags: + - main sequence star + - less massive companion + - semidetached + - class: Symbiotic Var + other names: + - Symbiotic Variable + - ZAND + tags: + - cataclysmic + - class: Rotating + subclasses: + - class: SX Ari + other names: + - SXA + - SXARI + - SX Arietus + - SX Ari + - helium variable + tags: + - type BOp-B9p + - main sequence + - strong He I/Si III + - class: Alpha2 CVn + other names: + - Alpha2 Canum Venaticorum + - ACV + subclasses: + - class: Rapidly Oscillating + other names: + - ACVO + - roAp + tags: + - class Ap + - instability strip + tags: + - type B8p-A7p + - main sequence + - chemically peculiar + - class: FK Comae Bern + other names: + - FK Comae Berenices + - FKCom + - FKCOM + tags: + - class G/K + - binary merger + - ellipsoidal + - starspots + - high-velocity + - class: Pulsar + other names: + - PSR + tags: + - neutron star + - regular period + - class: BY Dra + other names: + - BY + - BYDraconis + tags: + - class G/K/M + - dwarf + - starspots + - quasiperiodic + - class: Eruptive + subclasses: + - class: UV Ceti + other names: + - UV + - Flare star + - UV Cet-type + tags: + - types M/K + - dwarf + - magnetic reconnection + - class: S Doradus + other names: + - S Dor + - SDor + - SDOR + - Luminous blue variable + - LBV + tags: + - type B + - supergiant/hypergiant + - S Doradus instability strip + - high luminosity + - class: Wolf-Rayet + other names: + - WR + tags: + - broad lines + - class: Gamma Cas + other names: + - Gamma Cassiopeiae + - GCas + - GCAS + subclasses: + - class: Be + other names: [] + - class: Shell + other names: [] + tags: + - Be star + - accretion disk + - class: FU Ori + other names: + - FU + - FUor + - FU Ori + - FU Orionis + tags: + - pre-main sequence + - accretion disk + - T Tauri evolution + - possible Orion subtype + - class: Orion + other names: + - IN + subclasses: + - class: w/Abs + other names: + - IN(YY) + - class: T Tauri + other names: + - TTS + - TT + - INT + - IT + subclasses: + - class: TTc + other names: + - Classical TTS + - Classical T Tauri + - class: TTw + other names: + - Weak-line TTS + - Weak-line T Tauri + tags: + - Weak emission lines + tags: + - types F/G/K/M + - pre-main sequence + - strong chromospheric lines + - Hayashi track + tags: + - pre-main sequence + - diffuse nebulae + - class: R Cor Borealis + other names: + - R Coronae Borealis + - RCB + - R CrB + - R Cor Bor + subclasses: + - class: DY Per + links: + - https://en.wikipedia.org/wiki/DY_Persei_variable + other names: + - DY Persei + tags: + - AGB + - carbon-rich + tags: + - types F/G + - supergiant + - class: Herbig AE/BE + other names: + - HAeBe + tags: + - types A/B + - pre-main sequence + - class: FS CMa + other names: + - FS Canis Majoris + tags: + - irregular + tags: + - chromospheric process + - corona process + - class: Pulsating + subclasses: + - class: Gamma Doradus + other names: + - GDOR + - GDor + tags: + - main sequence + - class: SX Phe + other names: + - SX Phoenicis + - SXPHE + tags: + - type A2-F5 + - subdwarf + - instability strip + - possible Delta Scuti subtype + - class: RR Lyrae + other names: + - RR Lyr + - RR variable + - RR + - cluster variable + subclasses: + - class: RRcl + other names: + - RRCL + - class: RRab + other names: + - RRAB + - class: RRc + other names: + - RRC + - class: RRd + other names: + - RRD + - class: RRe + other names: + - RRE + tags: + - type A/F + - instability strip + - standard candle + - class: Alpha Cygni + other names: + - Alpha Cyg + - A Cyg + - ACYG + tags: + - types A/B + - supergiant + - class: Mira + other names: + - M + - Long period variable + tags: + - red giant + - long period + - class: Pulsating WD + other names: + - Pulsating white dwarf + subclasses: + - class: GW VIr + other names: + - DOV + - Pulsating PG 1159 + tags: + - pre-degenerate + - class: ZZ Ceti + other names: + - DAV + - ZZ Cet + subclasses: + - class: H abs only + other names: + - DAV + - H-WD + tags: + - type DA + - class: He abs only + other names: + - DBV + - DB white dwarf + - V777 Her + - He-WD + tags: + - type DB + - class: Showing HeII + other names: [] + tags: + - ZZ + tags: + - white dwarf + - class: Sun analogue + other names: + - Sun analog + - Solar analogue + - Solar analog + - Solar-like + tags: + - main sequence + - class: Delta Scuti + other names: + - Delta Sct + - DSct + - dwarf cepheid + - DST + - DSCT + subclasses: + - class: Low-Amp + other names: + - LADS + - Low-amplitude delta Scuti + - class: High-Amp + other names: + - giant/main sequence + - HADS + - High-amplitude delta Scuti + - Al Velorum + - class: PMS Delta Scuti + other names: + - Pre-main sequence Delta Scuti + tags: + - pre-main sequence + tags: + - instability strip + - standard candle + - class: PV Telescopii + other names: + - PVTEL + - PVTel + - PV Tel + tags: + - type Bp + - chemically peculiar + - supergiant + - class: Beta Cephei + other names: + - BCep + - B Cep + - Beta Canis Majoris + - "\u03B2 Cephei" + - BCEP + tags: + - type B + - main sequence + - Z bump + - class: Semiregular + other names: + - SR + - SRPV + - semi-regular + subclasses: + - class: SRa/Z-Aquarii + other names: + - SRA + - class: SRb + other names: + - SRB + - class: SRc + other names: + - SRC + - class: SRd + other names: + - SRD + tags: + - giant/supergiant + - long period + - class: RV Tau + other names: + - RV Tauri + - RV + subclasses: + - class: Const Mean Mag + other names: + - Constant mean magnitude + - class: Var Mean Mag + other names: + - Variable mean magnitude + tags: + - post-AGB + - possible Population II Cepheid + - instability strip + - long period + - class: Population II Cepheid + other names: + - Population II Ceph + - Type II Ceph + - Type II Cepheid + - Type-II Ceph + - Type-II Cepheid + - CW + subclasses: + - class: BL Her + other names: + - BL Herculis + - CWB + tags: + - pre-AGB + - class: W Vir + other names: + - W Virginis + - CWA + tags: + - types F6-K2 + - blue loop + tags: [] + - class: Cepheid + other names: + - Cep + - CEP + subclasses: + - class: Anomolous + other names: + - Anomolous Cepheid + - BLBOO + - class: Mult-mode + other names: + - Double-mode Cepheid + - Multi-mode Cepheid + - CEP(B) + - class: Classical + other names: + - Population I Cepheid + - Type I Cepheid + - DCEP + - Delta Cepheid + - Classical Cepheid + subclasses: + - class: Symmetrical + other names: + - DCEPS + - Delta Cep-type Symmetrical + tags: [] + tags: + - giant/supergiant + - instability strip + - standard candle + - class: SPB + other names: + - 53 Persei + - Slowly Pulsating B-type + - Slowly Pulsating B + tags: + - type B2-B9 + - main sequence + - class: BLAP + other names: + - Blue Large-Amplitude Pulsator + tags: [] + tags: + - expanding/contracting outer layers + - class: microlensing + subclasses: + - class: binary lens + subclass: + - class: lens with planet + - class: triple-system lens + - class: binary source + tags: + - stellar lensing + - gravitational lensing + name: Sitewide Taxonomy + provenance: https://github.com/profjsb/timedomain-taxonomy + version: 0.1.1 +- group_ids: + - =public_group_id + - =ztf_science_validation + - =rcf + - =clu + - =em_gw + - =short_grb + - =infant_sne + - =fast_transients + - =red_transients + - =superluminous_sne + - =xray_counterparts + name: Basic Taxonomy + hierarchy: + class: Time-domain Source + subclasses: + - class: roid + - class: varstar + - class: transient + - class: AGN + - class: bogus + version: 0.1.0 diff --git a/extensions/skyportal/docker-compose.skyportal.yaml b/extensions/skyportal/docker-compose.skyportal.yaml index 2a536cec..4d0fc815 100644 --- a/extensions/skyportal/docker-compose.skyportal.yaml +++ b/extensions/skyportal/docker-compose.skyportal.yaml @@ -10,19 +10,11 @@ services: image: skyportal/web expose: - "5000" -# ports: -# - "9000:5000" volumes: # fritz takes care of making a docker-appropriate config.yaml with Fritz-specific additions - ${PWD}/skyportal/config.yaml:/skyportal/config.yaml - ${PWD}/skyportal/data/db_fritz.yaml:/skyportal/data/db_seed.yaml - thumbnails:/skyportal/static/thumbnails - command: > - bash -c "source /skyportal_env/bin/activate - && cat js-requirements.fritz.txt | xargs npm install - && pip install -r requirements.fritz.txt - && (make log &) && make run" - # && (make log &) && make run_production" labels: # # Explicitly tell Traefik to expose this container - "traefik.enable=true" diff --git a/extensions/skyportal/js-requirements.fritz.txt b/extensions/skyportal/js-requirements.fritz.txt deleted file mode 100644 index 6533df4e..00000000 --- a/extensions/skyportal/js-requirements.fritz.txt +++ /dev/null @@ -1 +0,0 @@ -react-diff-viewer diff --git a/extensions/skyportal/package.fritz.json b/extensions/skyportal/package.fritz.json new file mode 100644 index 00000000..11630a7a --- /dev/null +++ b/extensions/skyportal/package.fritz.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "react-diff-viewer": "^3.1.1", + "react-json-view": "^1.19.1" + } +} diff --git a/extensions/skyportal/skyportal/handlers/api/alert.py b/extensions/skyportal/skyportal/handlers/api/alert.py index 642f712d..ea5ad538 100644 --- a/extensions/skyportal/skyportal/handlers/api/alert.py +++ b/extensions/skyportal/skyportal/handlers/api/alert.py @@ -12,12 +12,31 @@ from baselayer.app.access import auth_or_token from ..base import BaseHandler +from ...models import ( + DBSession, + Stream, + StreamUser, +) s = requests.Session() class ZTFAlertHandler(BaseHandler): + def get_user_streams(self): + + streams = ( + DBSession() + .query(Stream) + .join(StreamUser) + .filter(StreamUser.user_id == self.current_user.id) + .all() + ) + if streams is None: + streams = [] + + return streams + @auth_or_token def get(self, objectId: str = None): """ @@ -56,6 +75,17 @@ def get(self, objectId: str = None): application/json: schema: Error """ + streams = self.get_user_streams() + + # allow access to public data only by default + selector = {1} + + for stream in streams: + if "ztf" in stream.name.lower(): + selector.update(set(stream.altdata.get("selector", []))) + + selector = list(selector) + try: query = { "query_type": "aggregate", @@ -65,7 +95,7 @@ def get(self, objectId: str = None): { "$match": { "objectId": objectId, - "candidate.programid": {"$in": [1, 2, 3]} # fixme: ACLs plug in here! + "candidate.programid": {"$in": selector} } }, { @@ -112,7 +142,7 @@ def get(self, objectId: str = None): return self.error(f'failure: {_err}') -class ZTFAlertAuxHandler(BaseHandler): +class ZTFAlertAuxHandler(ZTFAlertHandler): @auth_or_token def get(self, objectId: str = None): """ @@ -146,6 +176,16 @@ def get(self, objectId: str = None): application/json: schema: Error """ + streams = self.get_user_streams() + + # allow access to public data only by default + selector = {1} + + for stream in streams: + if "ztf" in stream.name.lower(): + selector.update(set(stream.altdata.get("selector", []))) + + selector = list(selector) try: query = { @@ -169,9 +209,7 @@ def get(self, objectId: str = None): "cond": { "$in": [ "$$item.programid", - [ - 1, 2, 3 # fixme: ACLs plug in here! - ] + selector ] } } @@ -221,7 +259,8 @@ def get(self, objectId: str = None): "pipeline": [ { "$match": { - "objectId": objectId + "objectId": objectId, + "candidate.programid": {"$in": selector} } }, { @@ -264,7 +303,9 @@ def get(self, objectId: str = None): return self.error(f"Failed to fetch data for {objectId} from Kowalski") if len(latest_alert_data) > 0: - alert_data['prv_candidates'].append(latest_alert_data['candidate']) + candids = {a.get('candid', None) for a in alert_data['prv_candidates']} + if latest_alert_data['candidate']["candid"] not in candids: + alert_data['prv_candidates'].append(latest_alert_data['candidate']) return self.success(data=alert_data) @@ -273,7 +314,7 @@ def get(self, objectId: str = None): return self.error(f'failure: {_err}') -class ZTFAlertCutoutHandler(BaseHandler): +class ZTFAlertCutoutHandler(ZTFAlertHandler): @auth_or_token def get(self, objectId: str = None): """ @@ -323,6 +364,17 @@ def get(self, objectId: str = None): application/json: schema: Error """ + streams = self.get_user_streams() + + # allow access to public data only by default + selector = {1} + + for stream in streams: + if "ztf" in stream.name.lower(): + selector.update(set(stream.altdata.get("selector", []))) + + selector = list(selector) + try: candid = int(self.get_argument('candid')) cutout = self.get_argument('cutout').capitalize() @@ -344,7 +396,7 @@ def get(self, objectId: str = None): "filter": { "candid": candid, "candidate.programid": { - "$in": [1, 2, 3] # fixme: ACLs + "$in": selector } }, "projection": { diff --git a/extensions/skyportal/static/js/components/Alerts.jsx b/extensions/skyportal/static/js/components/Alerts.jsx index 4c61ccb6..f9757e7a 100644 --- a/extensions/skyportal/static/js/components/Alerts.jsx +++ b/extensions/skyportal/static/js/components/Alerts.jsx @@ -1,6 +1,11 @@ -import React, { useState } from "react"; +import React from "react"; import { useHistory } from "react-router-dom"; +import Typography from "@material-ui/core/Typography"; +import Card from "@material-ui/core/Card"; +import CardActions from "@material-ui/core/CardActions"; +import CardContent from "@material-ui/core/CardContent"; + import TextField from "@material-ui/core/TextField"; import InputLabel from "@material-ui/core/InputLabel"; import MenuItem from "@material-ui/core/MenuItem"; @@ -12,18 +17,15 @@ import Grid from "@material-ui/core/Grid"; import Button from "@material-ui/core/Button"; import { makeStyles } from "@material-ui/core/styles"; +import { useForm, Controller } from "react-hook-form"; const useStyles = makeStyles((theme) => ({ root: { width: "100%", "& > *": { margin: theme.spacing(1), - width: "25ch", }, }, - container: { - maxHeight: 440, - }, whitish: { color: "#f0f0f0", }, @@ -39,7 +41,6 @@ const useStyles = makeStyles((theme) => ({ width: 1, }, search_button: { - margin: theme.spacing(1), color: "#f0f0f0 !important", }, margin_bottom: { @@ -59,84 +60,81 @@ const useStyles = makeStyles((theme) => ({ color: theme.palette.text.secondary, }, formControl: { - margin: theme.spacing(1), - minWidth: 120, + width: "100%", }, selectEmpty: { - marginTop: theme.spacing(2), + width: "100%", + }, + header: { + paddingBottom: "0.625rem", }, })); const Alerts = () => { const classes = useStyles(); - const [stream, setStream] = useState("ztf"); - const [objectId, setObjectId] = useState(""); const history = useHistory(); - const handleStreamChange = (event) => { - setStream(event.target.value); - }; - - const handleObjectIdChange = (event) => { - setObjectId(event.target.value); - }; + const { register, handleSubmit, control } = useForm(); - const submitForm = () => { - if (objectId.length > 0) { - const path = `/alerts/${stream}/${objectId}`; - history.push(path); - } + const submitForm = (data) => { + const path = `/alerts/${data.instrument}/${data.object_id}`; + history.push(path); }; return (
-

Search objects from alert streams

+ + Search objects from alert streams + - - - - Alert stream - - - Required - - + + +
+ + + + Instrument + + + ZTF + + Required + - - - - + +
+ + + + +
- -
); }; diff --git a/extensions/skyportal/static/js/components/Group.jsx b/extensions/skyportal/static/js/components/Group.jsx deleted file mode 100644 index ea441dd6..00000000 --- a/extensions/skyportal/static/js/components/Group.jsx +++ /dev/null @@ -1,598 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { useHistory, useParams } from "react-router-dom"; -import PropTypes from "prop-types"; - -import { useDispatch, useSelector } from "react-redux"; -import { makeStyles, useTheme } from "@material-ui/core/styles"; -import Button from "@material-ui/core/Button"; -import DeleteIcon from "@material-ui/icons/Delete"; -import List from "@material-ui/core/List"; -import ListItem from "@material-ui/core/ListItem"; -import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction"; -import ListItemText from "@material-ui/core/ListItemText"; -import IconButton from "@material-ui/core/IconButton"; -import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; - -import Accordion from "@material-ui/core/Accordion"; -import AccordionSummary from "@material-ui/core/AccordionSummary"; -import AccordionDetails from "@material-ui/core/AccordionDetails"; -import Typography from "@material-ui/core/Typography"; - -import Dialog from "@material-ui/core/Dialog"; -import DialogActions from "@material-ui/core/DialogActions"; -import DialogContent from "@material-ui/core/DialogContent"; -import DialogContentText from "@material-ui/core/DialogContentText"; -import DialogTitle from "@material-ui/core/DialogTitle"; -import useMediaQuery from "@material-ui/core/useMediaQuery"; -import TextField from "@material-ui/core/TextField"; -import OpenInNewIcon from "@material-ui/icons/OpenInNew"; - -import InputLabel from "@material-ui/core/InputLabel"; -import MenuItem from "@material-ui/core/MenuItem"; -import FormHelperText from "@material-ui/core/FormHelperText"; -import FormControl from "@material-ui/core/FormControl"; -import Select from "@material-ui/core/Select"; -import Divider from "@material-ui/core/Divider"; -import Chip from "@material-ui/core/Chip"; -import CircularProgress from "@material-ui/core/CircularProgress"; - -import { useForm, Controller } from "react-hook-form"; -import { showNotification } from "baselayer/components/Notifications"; - -import * as groupActions from "../ducks/group"; -import * as groupsActions from "../ducks/groups"; -import * as streamsActions from "../ducks/streams"; -import * as filterActions from "../ducks/filter"; -import NewGroupUserForm from "./NewGroupUserForm"; - -const useStyles = makeStyles((theme) => ({ - padding_bottom: { - paddingBottom: "2em", - }, - paper: { - width: "100%", - padding: theme.spacing(1), - textAlign: "left", - color: theme.palette.text.primary, - }, - nested: { - paddingLeft: theme.spacing(2), - }, - heading: { - fontSize: "1.0625rem", - fontWeight: 500, - }, - accordion_details: { - flexDirection: "column", - }, - button_add: { - maxWidth: "8.75rem", - }, - selectEmpty: { - width: "100%", - marginTop: theme.spacing(2), - }, -})); - -const Group = () => { - const classes = useStyles(); - const dispatch = useDispatch(); - const theme = useTheme(); - const history = useHistory(); - - const { register, handleSubmit, control } = useForm(); - - const [groupLoadError, setGroupLoadError] = useState(""); - - const [panelMembersExpanded, setPanelMembersExpanded] = React.useState( - "panel-members" - ); - const [panelStreamsExpanded, setPanelStreamsExpanded] = React.useState( - "panel-streams" - ); - const [dialogOpen, setDialogOpen] = React.useState(false); - const fullScreen = !useMediaQuery(theme.breakpoints.up("md")); - - const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false); - - const [addStreamOpen, setAddStreamOpen] = useState(false); - - const handleDialogClose = () => { - setDialogOpen(false); - }; - - const handleClickDialogOpen = () => { - setDialogOpen(true); - }; - - const handleConfirmDeleteDialogClose = () => { - setConfirmDeleteOpen(false); - }; - - const handleAddStreamOpen = () => { - setAddStreamOpen(true); - }; - - const handleAddStreamClose = () => { - setAddStreamOpen(false); - }; - - const handlePanelMembersChange = (panel) => (event, isExpanded) => { - setPanelMembersExpanded(isExpanded ? panel : false); - }; - const handlePanelStreamsChange = (panel) => (event, isExpanded) => { - setPanelStreamsExpanded(isExpanded ? panel : false); - }; - - const { id } = useParams(); - const loadedId = useSelector((state) => state.group.id); - - useEffect(() => { - const fetchGroup = async () => { - const data = await dispatch(groupActions.fetchGroup(id)); - if (data.status === "error") { - setGroupLoadError(data.message); - } - }; - fetchGroup(); - }, [id, loadedId, dispatch]); - - const group = useSelector((state) => state.group); - const currentUser = useSelector((state) => state.profile); - - // fetch streams: - const streams = useSelector((state) => state.streams); - - useEffect(() => { - const fetchStreams = async () => { - const data = await dispatch(streamsActions.fetchStreams()); - if (data.status === "error") { - setGroupLoadError(data.message); - } - }; - fetchStreams(); - }, [currentUser, dispatch]); - - // forms - // add stream to group - const onSubmitAddStream = async (data) => { - const result = await dispatch( - streamsActions.addGroupStream({ - group_id: group.id, - stream_id: data.stream_id, - }) - ); - if (result.status === "success") { - dispatch(showNotification("Added stream to group")); - } - setAddStreamOpen(false); - }; - // add filter to group - const onSubmitAddFilter = async (data) => { - const result = await dispatch( - filterActions.addGroupFilter({ - name: data.filter_name, - group_id: group.id, - stream_id: data.filter_stream_id, - }) - ); - if (result.status === "success") { - dispatch(showNotification("Added filter to group")); - } - handleDialogClose(); - dispatch(groupActions.fetchGroup(loadedId)); - }; - - if (groupLoadError) { - return
{groupLoadError}
; - } - - // renders - if (!group) { - return ( -
- -
- ); - } - - // currentUser may not have the "Group admin" role, but can still be the group admin? - const currentGroupUser = group?.users?.filter( - (group_user) => group_user.username === currentUser.username - )[0]; - - const isAdmin = (aUser, aGroup) => - aUser && - aGroup.group_users && - aGroup.group_users.filter( - (group_user) => group_user.user_id === aUser.id - )[0].admin; - - let numAdmins = 0; - group?.group_users?.forEach((groupUser) => { - if (groupUser?.admin) { - numAdmins += 1; - } - }); - - const groupStreamIds = group?.streams?.map((stream) => stream.id); - - const isStreamIdInStreams = (sid) => - streams?.map((stream) => stream.id).includes(sid); - - return ( -
- - Group:  {group.name} - - - } - aria-controls="panel-members-content" - id="panel-members-header" - style={{ borderBottom: "1px solid rgba(0, 0, 0, .125)" }} - > - Members - - - - {group?.users?.map((user) => ( - - - {isAdmin(user, group) && ( -
- -    -
- )} - {(currentUser.roles.includes("Super admin") || - (currentUser.roles.includes("Group admin") && - isAdmin(currentGroupUser, group))) && - isAdmin(user, group) && - numAdmins > 1 && ( - - - dispatch( - groupsActions.deleteGroupUser({ - username: user.username, - group_id: group.id, - }) - ) - } - > - - - - )} - {(currentUser.roles.includes("Super admin") || - (currentUser.roles.includes("Group admin") && - isAdmin(currentGroupUser, group))) && - !isAdmin(user, group) && ( - - - dispatch( - groupsActions.deleteGroupUser({ - username: user.username, - group_id: group.id, - }) - ) - } - > - - - - )} -
- ))} -
- -
- {/*eslint-disable */} - {(currentUser.roles.includes("Super admin") || - (currentUser.roles.includes("Group admin") && - isAdmin(currentGroupUser, group))) && ( - - )} - {/* eslint-enable */} -
-
-
- {streams?.length > 0 && ( - - } - aria-controls="panel-streams-content" - id="panel-streams-header" - style={{ borderBottom: "1px solid rgba(0, 0, 0, .125)" }} - > - - Alert streams and filters - - - - - {group.streams?.map((stream) => ( -
- - - - - {group.filters?.map((filter) => - filter.stream_id === stream.id ? ( - - - {/*eslint-disable */} - {(currentUser.roles.includes("Super admin") || - (currentUser.roles.includes("Group admin") && - isAdmin(currentGroupUser, group))) && ( - - { - const result = await dispatch( - filterActions.deleteGroupFilter({ - filter_id: filter.id, - }) - ); - if (result.status === "success") { - dispatch( - showNotification( - "Deleted filter from group" - ) - ); - } - dispatch(groupActions.fetchGroup(loadedId)); - }} - > - - - - )} - {/* eslint-enable */} - - ) : ( - "" - ) - )} - -
- ))} -
- -
- {/* only Super admins can add streams to groups */} - {currentUser.roles.includes("Super admin") && - streams?.length > 0 && - group?.streams?.length < streams?.length && ( - - )} - - {(currentUser.roles.includes("Super admin") || - (currentUser.roles.includes("Group admin") && - isAdmin(currentGroupUser, group))) && - group?.streams?.length > 0 && ( - - )} -
-
-
- )} -
- {/*eslint-disable */} - {(currentUser.roles.includes("Super admin") || - (currentUser.roles.includes("Group admin") && - isAdmin(currentGroupUser, group))) && ( - - )} - {/* eslint-enable */} - -
- - Add alert stream to group - - - - - Alert stream - - - {streams?.map( - (stream) => - // display only streams that are not yet added - !groupStreamIds?.includes(stream.id) && ( - {stream.name} - ) - )} - - Required - - - - - - -
-
- -
- - Create a new alert stream filter - - - - Please refer to the   - - docs - -   for an extensive guide on Alert filters in Fritz. - - - - - Alert stream - - - {group.streams?.map((stream) => ( - - {stream.name} - - ))} - - Required - - - - - - -
-
- - Delete Group? - - - Are you sure you want to delete this Group? -
- Warning! This will delete the group and all of its filters. All - source data will be transferred to the Site-wide group. -
-
- - - - - -
-
- ); -}; - -Group.propTypes = { - route: PropTypes.shape({ - id: PropTypes.string, - }).isRequired, -}; - -export default Group; diff --git a/extensions/skyportal/static/js/components/VegaPlotZTFAlert.jsx b/extensions/skyportal/static/js/components/VegaPlotZTFAlert.jsx index 19fd0b8f..b8bc8fc6 100644 --- a/extensions/skyportal/static/js/components/VegaPlotZTFAlert.jsx +++ b/extensions/skyportal/static/js/components/VegaPlotZTFAlert.jsx @@ -6,7 +6,6 @@ import embed from 'vega-embed'; const spec = (url, jd) => ({ $schema: "https://vega.github.io/schema/vega-lite/v4.json", width: 500, - // width: 400, // width: "container", height: 250, data: { @@ -16,6 +15,11 @@ const spec = (url, jd) => ({ property: "data.prv_candidates" // where in the JSON does the data live } }, + autosize: { + type: "fit", + resize: true, + // contains: "padding" + }, background: "transparent", layer: [ { diff --git a/extensions/skyportal/static/js/components/ZTFAlert.jsx b/extensions/skyportal/static/js/components/ZTFAlert.jsx index 8a7d4739..13248c22 100644 --- a/extensions/skyportal/static/js/components/ZTFAlert.jsx +++ b/extensions/skyportal/static/js/components/ZTFAlert.jsx @@ -4,7 +4,7 @@ import { useDispatch, useSelector } from "react-redux"; import Button from "@material-ui/core/Button"; import SaveIcon from "@material-ui/icons/Save"; import PropTypes from "prop-types"; -import { withStyles, makeStyles } from "@material-ui/core/styles"; +import { withStyles, makeStyles, useTheme } from "@material-ui/core/styles"; import Table from "@material-ui/core/Table"; import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; @@ -15,23 +15,23 @@ import TableRow from "@material-ui/core/TableRow"; import TableSortLabel from "@material-ui/core/TableSortLabel"; import Paper from "@material-ui/core/Paper"; import Grid from "@material-ui/core/Grid"; +import Accordion from "@material-ui/core/Accordion"; +import AccordionSummary from "@material-ui/core/AccordionSummary"; +import AccordionDetails from "@material-ui/core/AccordionDetails"; +import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; +import Typography from "@material-ui/core/Typography"; +import CircularProgress from "@material-ui/core/CircularProgress"; -import Responsive from "./Responsive"; -import FoldBox from "./FoldBox"; +import ThumbnailList from "./ThumbnailList"; +import ReactJson from "react-json-view"; import * as Actions from "../ducks/alert"; const VegaPlot = React.lazy(() => import("./VegaPlotZTFAlert")); const StyledTableCell = withStyles(() => ({ - head: { - // backgroundColor: theme.palette.common.black, - // backgroundColor: "#111", - // color: theme.palette.common.white, - // color: "#f0f0f0", - }, body: { - fontSize: 14, + fontSize: "0.875rem", }, }))(TableCell); @@ -72,9 +72,14 @@ const useStyles = makeStyles((theme) => ({ textAlign: "center", color: theme.palette.text.secondary, }, - // table: { - // minWidth: 650, - // }, + heading: { + fontSize: "1.0625rem", + fontWeight: 500, + }, + header: { + paddingBottom: "0.625rem", + color: theme.palette.text.primary, + }, })); function createRows( @@ -109,14 +114,12 @@ const columns = [ label: "candid", numeric: false, disablePadding: false, - // minWidth: 170 }, { id: "jd", numeric: true, disablePadding: false, label: "JD", - // minWidth: 170, align: "left", format: (value) => value.toFixed(5), }, @@ -132,7 +135,6 @@ const columns = [ numeric: true, disablePadding: false, label: "mag", - // minWidth: 170, align: "left", format: (value) => value.toFixed(3), }, @@ -141,7 +143,6 @@ const columns = [ numeric: true, disablePadding: false, label: "e_mag", - // minWidth: 170, align: "left", format: (value) => value.toFixed(3), }, @@ -150,7 +151,6 @@ const columns = [ numeric: true, disablePadding: false, label: "rb", - // minWidth: 170, align: "left", format: (value) => value.toFixed(5), }, @@ -159,7 +159,6 @@ const columns = [ numeric: true, disablePadding: false, label: "drb", - // minWidth: 170, align: "left", format: (value) => value.toFixed(5), }, @@ -168,7 +167,6 @@ const columns = [ numeric: false, disablePadding: false, label: "isdiffpos", - // minWidth: 170, align: "left", }, { @@ -176,7 +174,6 @@ const columns = [ numeric: true, disablePadding: false, label: "programid", - // minWidth: 170, align: "left", }, { @@ -185,8 +182,6 @@ const columns = [ disablePadding: false, label: "actions", align: "right", - // render: ({ row }) => lolol, - // render: ({ row }) => ({row.label}) }, ]; @@ -268,6 +263,27 @@ const ZTFAlert = ({ route }) => { const objectId = route.id; const dispatch = useDispatch(); + const theme = useTheme(); + const darkTheme = theme.palette.type === "dark"; + + const [ + panelPhotometryThumbnailsExpanded, + setPanelPhotometryThumbnailsExpanded, + ] = useState(true); + + const handlePanelPhotometryThumbnailsChange = (panel) => ( + event, + isExpanded + ) => { + setPanelPhotometryThumbnailsExpanded(isExpanded ? panel : false); + }; + + const [panelXMatchExpanded, setPanelXMatchExpanded] = useState(true); + + const handlePanelXMatchChange = (panel) => (event, isExpanded) => { + setPanelXMatchExpanded(isExpanded ? panel : false); + }; + const [candid, setCandid] = useState(0); const [jd, setJd] = useState(0); @@ -359,105 +375,103 @@ const ZTFAlert = ({ route }) => { setPage(0); }; + const thumbnails = [ + { + type: "new", + id: 0, + public_url: `/api/alerts/ztf/${objectId}/cutout?candid=${candid}&cutout=science&file_format=png`, + }, + { + type: "ref", + id: 1, + public_url: `/api/alerts/ztf/${objectId}/cutout?candid=${candid}&cutout=template&file_format=png`, + }, + { + type: "sub", + id: 2, + public_url: `/api/alerts/ztf/${objectId}/cutout?candid=${candid}&cutout=difference&file_format=png`, + }, + ]; + if (alert_data === null) { - return
Loading...
; - } if (isString(alert_data) || isString(alert_aux_data)) { + return
; + } + if (isString(alert_data) || isString(alert_aux_data)) { return
Failed to fetch alert data, please try again later.
; - } if (alert_data.length === 0) { + } + if (alert_data.length === 0) { return (
-

{objectId} not found

+ + {objectId} not found +
); - } if (alert_data.length > 0) { + } + if (alert_data.length > 0) { return (
-
-

- {objectId} - -

-
-
- + {objectId} + + + + + } + aria-controls="panel-content" + id="panel-header" > + + Photometry and cutouts + + + - + Loading plot...
}> - {/*
*/} - {/*
*/} - - science 0 - ? `/api/alerts/ztf/${objectId}/cutout?candid=${candid}&cutout=science&file_format=png` - : null - } + {candid > 0 && ( + -
- Science -
- - reference 0 - ? `/api/alerts/ztf/${objectId}/cutout?candid=${candid}&cutout=template&file_format=png` - : null - } - /> -
- Reference -
- - difference 0 - ? `/api/alerts/ztf/${objectId}/cutout?candid=${candid}&cutout=difference&file_format=png` - : null - } - /> -
- Difference -
- - Cross-matches: -
- {/* todo: plot interleaved on PS1 cutout? */} - {JSON.stringify(cross_matches, null, 2)} -
+ )}
- -
+ + @@ -506,11 +520,26 @@ const ZTFAlert = ({ route }) => { onChangeRowsPerPage={handleChangeRowsPerPage} /> + + + } + aria-controls="panel-content" + id="panel-header" + > + Cross-matches + + + + + ); } - return
Error rendering page...
; - + return
Error rendering page...
; }; ZTFAlert.propTypes = { diff --git a/fritz b/fritz index 9cb672c4..15cc42f3 100755 --- a/fritz +++ b/fritz @@ -2,6 +2,7 @@ import argparse import datetime from distutils.dir_util import copy_tree +import json import os from pathlib import Path import subprocess @@ -101,23 +102,24 @@ def check_config(cfg='fritz.defaults.yaml', yes=False): yaml.dump(config_skyportal, cyaml) -def run(args): +def build(args): """ - Launch Fritz + Build Fritz """ env = os.environ.copy() env.update({"FLAGS": "--config=../fritz.yaml"}) - # initialize/update fritz's submodules kowalski and skyportal - if args.init: - # pull skyportal and kowalski - p = subprocess.run(['git', 'submodule', 'update', '--init', '--recursive']) + if not args.dev: + if args.init: + # initialize/update fritz's submodules kowalski and skyportal + # pull skyportal and kowalski + p = subprocess.run(['git', 'submodule', 'update', '--init', '--recursive']) + if p.returncode != 0: + raise RuntimeError("Failed to initialize fritz's submodules") + # auto stash + p = subprocess.run(['git', 'stash'], cwd="skyportal") if p.returncode != 0: - raise RuntimeError("Failed to initialize fritz's submodules") - p = subprocess.run(['git', 'submodule', 'update', '--recursive']) - if p.returncode != 0: - raise RuntimeError("Failed to update fritz's submodules") - else: + raise RuntimeError("SkyPortal autostash failed") p = subprocess.run(['git', 'submodule', 'update', '--recursive']) if p.returncode != 0: raise RuntimeError("Failed to update fritz's submodules") @@ -127,63 +129,84 @@ def run(args): if not env_ok: raise RuntimeError("Halting because of unsatisfied system dependencies") - # create common docker network (if it does not exist yet) - p = subprocess.run( - ["docker", "network", 'create', "fritz_net"], - capture_output=True, - universal_newlines=True - ) - if (p.returncode != 0) and ("already exists" not in p.stderr): - raise RuntimeError("Failed to create network fritz_net") + # check config + check_config(cfg='fritz.defaults.yaml', yes=args.yes) + + # load config + with open("fritz.yaml") as fyaml: + fritz_config = yaml.load(fyaml, Loader=yaml.FullLoader) + + # add Fritz-specific K and SP extensions + # copy_tree('extensions/kowalski/', 'kowalski/') + copy_tree('extensions/skyportal/', 'skyportal/') + + # add Fritz-specific dependencies for SP + # js + with open("extensions/skyportal/package.fritz.json", "r") as fpjf, open("skyportal/package.json", "r") as fpj: + pjf = json.load(fpjf) + pj = json.load(fpj) + pj["dependencies"] = {**pj["dependencies"], **pjf["dependencies"]} + with open("skyportal/package.json", "w") as fpj: + json.dump(pj, fpj, indent=2) + # python + with open(".requirements/ext.txt", "r") as frf, open("skyportal/requirements.txt", "r") as fr: + rf = frf.readlines() + r = fr.readlines() + with open("skyportal/requirements.txt", "w") as fr: + for line in rf + r: + fr.write(line) + + # adjust F-specific docker-compose.yaml for SP + with open("skyportal/docker-compose.skyportal.yaml") as dcyaml: + dc = yaml.load(dcyaml, Loader=yaml.FullLoader) + # fix absolute paths in docker-compose.skyportal.yaml + for vi, volume in enumerate(dc["services"]["web"]["volumes"]): + dc["services"]["web"]["volumes"][vi] = volume.replace( + "${PWD}", + str(Path(__file__).parent.absolute()) + ) + if args.traefik: + # fix host for Traefik + dc["services"]["web"]["labels"][2] = dc["services"]["web"]["labels"][2].replace( + "", + fritz_config["skyportal"]["server"]["host"] + ) + else: + # not running behind Traefik? then publish port 5000 on host + port = fritz_config["skyportal"]["server"].get("port", 5000) + if port is None: + port = 5000 + dc["services"]["web"]["ports"] = [f"{port}:{port}"] + pass + with open("skyportal/docker-compose.skyportal.yaml", 'w') as dcyaml: + yaml.dump(dc, dcyaml) + + # Build skyportal's images + p = subprocess.run(["make", "docker-local"], cwd="skyportal") + if p.returncode != 0: + raise RuntimeError("Failed to build skyportal's docker images") + # when initializing, must start SP to generate token for K if args.init: - # check config - check_config(cfg='fritz.defaults.yaml', yes=args.yes) - - # load config - with open("fritz.yaml") as fyaml: - fritz_config = yaml.load(fyaml, Loader=yaml.FullLoader) - - # add Fritz-specific K and SP extensions: - copy_tree('extensions/skyportal/', 'skyportal/') - - # adjust docker-compose.skyportal.yaml - with open("skyportal/docker-compose.skyportal.yaml") as dcyaml: - dc = yaml.load(dcyaml, Loader=yaml.FullLoader) - # fix absolute paths in docker-compose.skyportal.yaml - for vi, volume in enumerate(dc["services"]["web"]["volumes"]): - dc["services"]["web"]["volumes"][vi] = volume.replace( - "${PWD}", - str(Path(__file__).parent.absolute()) - ) - if args.traefik: - # fix host for traefik - dc["services"]["web"]["labels"][2] = dc["services"]["web"]["labels"][2].replace( - "", - fritz_config["skyportal"]["server"]["host"] - ) - with open("skyportal/docker-compose.skyportal.yaml", 'w') as dcyaml: - yaml.dump(dc, dcyaml) - - # copy over the additional requirements: - subprocess.run(["cp", ".requirements/ext.txt", f"skyportal/requirements.fritz.txt"], check=True) + # create common docker network (if it does not exist yet) + p = subprocess.run( + ["docker", "network", 'create', "fritz_net"], + capture_output=True, + universal_newlines=True + ) + if (p.returncode != 0) and ("already exists" not in p.stderr): + raise RuntimeError("Failed to create network fritz_net") - # Build skyportal's images - p = subprocess.run(["make", "docker-local"], cwd="skyportal") + # start up skyportal + # docker-compose.skyportal.yaml bind-mounts the fritz-specific config.yaml and db_seed.yaml + p = subprocess.run( + ["docker-compose", "-f", 'docker-compose.skyportal.yaml', "up", "-d"], + cwd="skyportal", + check=True + ) if p.returncode != 0: - raise RuntimeError("Failed to build skyportal's docker images") + raise RuntimeError("Failed to start SkyPortal") - # start up skyportal - # docker-compose.skyportal.yaml bind-mounts the fritz-specific config.yaml and db_seed.yaml - p = subprocess.run( - ["docker-compose", "-f", 'docker-compose.skyportal.yaml', "up", "-d"], - cwd="skyportal", - check=True - ) - if p.returncode != 0: - raise RuntimeError("Failed to start SkyPortal") - - if args.init: # init skyportal and load seed data mi, max_retires = 1, 3 while mi <= max_retires: @@ -213,16 +236,53 @@ def run(args): with open('kowalski/config.yaml', 'w') as cyaml: yaml.dump(config, cyaml) - # install Kowalski's deps: - c = ["pip", "install", "-r", "requirements.txt"] - subprocess.run(c, cwd="kowalski", check=True) - # Build kowalski's images - c = ["python", "kowalski.py", "build"] - if args.yes: - c.append("--yes") - p = subprocess.run(c, cwd="kowalski") - if p.returncode != 0: - raise RuntimeError("Failed to build Kowalski's docker images") + # install Kowalski's deps: + c = ["pip", "install", "-r", "requirements.txt"] + subprocess.run(c, cwd="kowalski", check=True) + # Build kowalski's images + c = ["python", "kowalski.py", "build"] + if args.yes: + c.append("--yes") + p = subprocess.run(c, cwd="kowalski") + if p.returncode != 0: + raise RuntimeError("Failed to build Kowalski's docker images") + + if args.init: + # stop SkyPortal + subprocess.run(["docker-compose", "-f", "docker-compose.skyportal.yaml", "down"], cwd="skyportal") + + # remove common network + subprocess.run(["docker", "network", "remove", "fritz_net"]) + + +def run(args): + """ + Launch Fritz + """ + env = os.environ.copy() + env.update({"FLAGS": "--config=../fritz.yaml"}) + + if args.init: + build(args=args) + + # create common docker network (if it does not exist yet) + p = subprocess.run( + ["docker", "network", 'create', "fritz_net"], + capture_output=True, + universal_newlines=True + ) + if (p.returncode != 0) and ("already exists" not in p.stderr): + raise RuntimeError("Failed to create network fritz_net") + + # start up skyportal + # docker-compose.skyportal.yaml bind-mounts the fritz-specific config.yaml and db_seed.yaml + p = subprocess.run( + ["docker-compose", "-f", 'docker-compose.skyportal.yaml', "up", "-d"], + cwd="skyportal", + check=True + ) + if p.returncode != 0: + raise RuntimeError("Failed to start SkyPortal") # start up kowalski c = ["python", "kowalski.py", "up"] @@ -272,7 +332,14 @@ def log(args): """ Show colorized logs while the marshal is running """ - p = subprocess.run(["make", "log"], cwd="skyportal") + # p = subprocess.run(["make", "log"], cwd="skyportal") + p = subprocess.run( + [ + "docker", "exec", "-it", "skyportal_web_1", "/bin/bash", "-c", + "source /skyportal_env/bin/activate; make log" + ], + cwd="skyportal" + ) if p.returncode != 0: raise RuntimeError("Failed to display fritz's logs") @@ -338,8 +405,6 @@ def prune(args): def test(args): print("Launching tests...") - # load_demo_data is run as part of ./fritz run --init since it is needed to create a token for kowalski - print("Testing Kowalski...") subprocess.run(["python", "kowalski.py", "test"], cwd="kowalski") @@ -380,6 +445,7 @@ if __name__ == "__main__": ("run", "🚀 Launch Fritz"), ("stop", "✋ Shut Fritz down"), ("test", "Run the test suite"), + ("build", "Build Fritz"), ("develop", "Install tools for developing Fritz"), ("lint", "Lint the full code base"), ("prune", "☠️ Wipe out containers, volumes, and submodules"), @@ -394,11 +460,23 @@ if __name__ == "__main__": parsers["run"].add_argument( "--init", action="store_true", help="Initialize Fritz" ) - + parsers["run"].add_argument( + "--dev", action="store_true", help="Run Fritz in dev mode" + ) parsers["run"].add_argument( "--traefik", action="store_true", help="Run Fritz behind Traefik" ) + parsers["build"].add_argument( + "--init", action="store_true", help="Initialize before building Fritz" + ) + parsers["build"].add_argument( + "--dev", action="store_true", help="Build Fritz for running in dev mode" + ) + parsers["build"].add_argument( + "--traefik", action="store_true", help="Build Fritz to run behind Traefik" + ) + parsers["doc"].add_argument( "--upload", action="store_true", help="Upload documentation to GitHub" ) diff --git a/fritz.defaults.yaml b/fritz.defaults.yaml index f34d8722..6c253d58 100644 --- a/fritz.defaults.yaml +++ b/fritz.defaults.yaml @@ -39,12 +39,18 @@ skyportal: component: About - path: "/share_data/:id" component: ShareDataForm + - path: "/filter/:fid" + component: Filter + - path: "/runs" + component: ObservingRunList + - path: "/run/:id" + component: RunSummary + - path: "/user_management" + component: UserManagement - path: "/alerts" component: Alerts - path: "/alerts/ztf/:id" component: ZTFAlert - - path: "/filter/:fid" - component: Filter kowalski: protocol: "http" @@ -75,10 +81,92 @@ skyportal: icon: GroupWork url: /groups + - name: Observing Runs + icon: LocalCafe + url: /runs + - name: About icon: Info url: /about + homepage_widgets: + # This section describes the specific widgets shown on the Home Page and how + # they are laid out by default on the grid of the page. + # + # The name of section should be the same as the widget's React component. + # + # The props property should be a set of properties to be passed on to the + # underlying React component for the widget. You may run into cases in which + # you must pass a more complex, dyamic property (perhaps fetched from the + # application redux store). Since you can not know that in the time of the + # configuration writing, such properties should be directly coded into the + # HomePage.jsx.template file (see the GroupList widget for an example) + # + # By default, any widget listed here is shown on the Home Page. However, you + # can give a widget the property "show: false" to turn off rendering of the + # widget. + # + # The resizable property determines whether the user is able to resize the + # widget after it has been rendered based on default layouts. + # + # Finally, the layouts property provides an array of default sizes/locations + # for each screen width breakpoint for the given widget. Layout arrays are + # given in the order [x, y, width, height], in units of grid columns/rows. + # For example, a layout array of [1, 2, 3, 4] will render a widget 3 grid + # columns in width, 4 grid rows in height, and have its upper-left corner at + # the column 1 (zero-indexed) and row 2. Note that each row is by default + # 150px in height. The row height can be altered in the homepage_grid + # section above (as well as other grid characteristics). + + SourceCounts: + props: + sinceDaysAgo: 7 + resizeable: false + layouts: + xlg: [14, 0, 2, 1] + lg: [10, 0, 2, 1] + md: [8, 0, 2, 1] + sm: [4.5, 0, 1.5, 1] + xs: [0, 0, 4, 1] + + RecentSources: + resizeable: false + layouts: + xlg: [0, 0, 5, 3] + lg: [0, 0, 4, 3] + md: [0, 3, 5, 3] + sm: [0, 3, 3, 3] + xs: [0, 4, 4, 3] + + NewsFeed: + resizeable: false + layouts: + xlg: [10, 0, 4, 3] + lg: [7, 0, 3, 3] + md: [0, 0, 8, 3] + sm: [0, 0, 4.5, 3] + xs: [0, 1, 4, 3] + + TopSources: + resizeable: false + layouts: + xlg: [5, 0, 5, 3] + lg: [4, 3, 3, 3] + md: [5, 3, 5, 3] + sm: [3, 3, 3, 3] + xs: [0, 7, 4, 3] + + GroupList: + props: + title: My Groups + resizeable: true + layouts: + xlg: [14, 1, 2, 2] + lg: [10, 1, 2, 2] + md: [8, 1, 2, 2] + sm: [4.5, 1, 1.5, 2] + xs: [0, 10, 4, 2] + database: database: skyportal host: localhost @@ -96,6 +184,10 @@ skyportal: # You need to have Google+ API enabled; it takes a few minutes to activate. host: + port: + + ssl: False + processes: 1 auth: debug_login: True @@ -114,6 +206,17 @@ skyportal: script: jobs/delete_unsaved_candidates.py limit: ["01:00", "02:00"] + invitations: + enabled: False # If debug_login=True above, invite tokens won't be used during auth + days_until_expiry: 3 + email_subject: "You've been invited to collaborate on Fritz" + email_body_preamble: | # This can include HTML tags + Welcome to Fritz! +
+ Dark star crashes, pouring its light into ashes. + from_email: # This needs to be set to a valid, Sendgrid-registered address in config.yaml + sendgrid_api_key: # This needs to be obtained via Sendgrid setup on their site + kowalski: server: @@ -146,7 +249,7 @@ kowalski: kafka: default.topic.config: auto.offset.reset: "earliest" - group: "kowalski" + group: "fritz" bootstrap.servers: "192.168.0.64:9092,192.168.0.65:9092,192.168.0.66:9092" zookeeper: "192.168.0.64:2181" @@ -433,7 +536,7 @@ kowalski: serverurl: unix:///dev/shm/supervisor.sock "program:gunicorn": - # fixme: adjust number of workers -w for your system (e.g. -w 20 for kowalski.caltech.edu) + # fixme: adjust number of workers -w for your system command: > /usr/local/bin/gunicorn -w 10 @@ -483,7 +586,7 @@ kowalski: stdout_logfile: /data/logs/alert_watcher_ztf_stdout.log stdout_logfile_maxbytes: 30MB stderr_logfile: /data/logs/alert_watcher_ztf_stderr.log - redirect_stderr: True + redirect_stderr: true environment: "PRODUCTION=1" "program:ops-watcher-ztf": @@ -495,5 +598,5 @@ kowalski: stdout_logfile: /data/logs/ops_watcher_ztf_stdout.log stdout_logfile_maxbytes: 30MB stderr_logfile: /data/logs/ops_watcher_ztf_stderr.log - redirect_stderr: True + redirect_stderr: true environment: "PRODUCTION=1" diff --git a/kowalski b/kowalski index 2d1c670d..882a7fa7 160000 --- a/kowalski +++ b/kowalski @@ -1 +1 @@ -Subproject commit 2d1c670d58402449de53730f4f8b8146d302b31e +Subproject commit 882a7fa7e292676dd4864212efa696fb99668b4c diff --git a/skyportal b/skyportal index afc04117..6f63c8d4 160000 --- a/skyportal +++ b/skyportal @@ -1 +1 @@ -Subproject commit afc04117ac610a7dad986c1208c9bfb247745e4d +Subproject commit 6f63c8d4b9397ad05eff6f0c7776175c8840a9ef