Skip to content

Commit

Permalink
Merge branch 'main' into bdnyc-photometry
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-rodriguez committed Aug 18, 2020
2 parents e0b3f52 + d0a42fa commit 278f97d
Show file tree
Hide file tree
Showing 16 changed files with 1,649 additions and 2 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
name: Database Integration Tests

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch: # manual execution
Expand Down
1 change: 1 addition & 0 deletions scripts/tutorials/generate_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
db.load_database(DB_PATH, verbose=False)

print('New db file generated.')
print(os.listdir())

# Close all connections
db.session.close()
Expand Down
62 changes: 62 additions & 0 deletions website/gen_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/usr/bin/env python
"""
Prints the info about the given object in this div
Methods
-------
output
Converts inventory into JSON string
main
Loads simple database, queries for a target and pulls out inventory
"""
# inbuilt libs
import json

# local libs
from web_base import load_db, target_exists, target_argparse


def output(inventory: dict):
"""
Converts object inventory to json string
Parameters
----------
inventory
The dictionary of data about given object
Returns
-------
inventory
The json string of data about given object
"""
if 'Photometry' in inventory.keys():
del inventory['Photometry'] # will not print photometry data raw to page
inventory = json.dumps(inventory, indent=4) # convert to json string and pretty print
return inventory


def main(target: str):
"""
Main module, loading simple database, querying and outputting object inventory as json string
Parameters
----------
target
The name of the object to query database by
Returns
-------
inventory
The json string of the inventory for that object
"""
db = load_db() # load database
inventory = target_exists(db, target) # query database for given object
inventory = output(inventory) # converts inventory of object to string
return inventory


if __name__ == '__main__' or 'bokeh' in __name__:
_tgt = target_argparse() # if running from command line
main(_tgt)
70 changes: 70 additions & 0 deletions website/gen_photo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env python
"""
Creates photometry table
Attributes
----------
KNOWN_MAGS
Dictionary of photometric bands against central wavelength in microns
Methods
-------
photometry_exists
Grabs photometry from object inventory on query
main
Queries database for given object and finds photometry if it exists
"""
# local files
from web_base import load_db, target_exists, target_argparse

# global vars
# TODO: expand known mags or use different method of initialising tables depending on if plotting photometry
KNOWN_MAGS = {'WISE_W1': 3.368, 'WISE_W2': 4.618, 'WISE_W3': 12.082, 'WISE_W4': 22.194} # central wave in microns


def photometry_exists(inventory: dict):
"""
Unpacks the photometry for an object
Parameters
----------
inventory
Dictionary of the database output for that object
"""
bands, mags, errs = [], [], []
try:
if 'Photometry' not in inventory.keys():
raise NotImplementedError('No "Photometry" key yet in db')
elif type(inventory['Photometry']) != list:
raise TypeError('Photometry entry needs to produce list')
except (NotImplementedError, TypeError):
bands = [*KNOWN_MAGS.keys(), ]
mags, errs = [None, ] * len(bands), [None, ] * len(bands)
return bands, mags, errs
else:
photo = inventory['Photometry']
for band in photo:
bands.append(band['band']) # photometric band name
mags.append(band['magnitude']) # magnitude for object
errs.append(band['magnitude_error']) # magnitude error for object
return bands, mags, errs


def main(target: str):
"""
Main module
Parameters
----------
target
Name of the target to be searched for, given as script argument
"""
db = load_db() # load simple database
inventory = target_exists(db, target) # query database for given object
bands, mags, errs = photometry_exists(inventory) # get lists for photometry
return bands, mags, errs


if __name__ == '__main__' or 'bokeh' in __name__:
_tgt = target_argparse() # pull target name from system arguments
main(_tgt)
56 changes: 56 additions & 0 deletions website/gen_plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env python
"""
Generates the script and div for the bokeh plot of spectra
Methods
-------
spectra_exists
Takes spectra from database for given object
main
Queries database for given object and finds spectra
"""
# local files
from web_base import load_db, target_exists, target_argparse


def spectra_exists(inventory: dict):
"""
Unpacks the spectra from the object inventory
Parameters
----------
inventory
Dictionary of the database output for that object
Returns
-------
_
The spectra
"""
try:
if 'Spectra' not in inventory.keys():
raise NotImplementedError('No "Spectra" key yet in db')
except NotImplementedError:
pass # for now
# TODO: when implemented add the unpacking and handling of spectra here (astropy specutils?)
# TODO: including multiple spectra per object
return


def main(target: str):
"""
Main module
Parameters
----------
target
Name of the target to be searched for, given as script argument
"""
db = load_db() # load simple database
inventory = target_exists(db, target) # query database for given object
spectra = spectra_exists(inventory) # pull spectra from database query
return spectra


if __name__ == '__main__' or 'bokeh' in __name__:
_tgt = target_argparse() # pull target name from system arguments
main(_tgt)
162 changes: 162 additions & 0 deletions website/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
"""
Main bokeh serve host
Methods
-------
info_box
The <pre> box to be filled with the object inventory
photo_table
The empty photometric table to be populated with object photometry
spectra_fig
The spectra for an object which will be populated with all object spectra
main
Generating initial elements on current document and assigns callback to fill them on selection
"""
# 3rd party imports
from bokeh.models import ColumnDataSource, PreText, DataTable, TableColumn
from bokeh.plotting import curdoc, figure
import numpy as np
# local imports
import splash_table as st
import gen_info as geninfo
import gen_plot as genspec
import gen_photo as genphot


def info_box():
"""
Generates a <pre> box to be filled with the object inventory
Returns
-------
infopre
Bokeh PreText object with empty text
"""
# TODO: fix the object info to be 2 column, fitting to surrounding div not relying on overflow-y
infopre = PreText(text='', sizing_mode='stretch_both', name='objectinfo')
return infopre


def photo_table():
"""
Creates an empty DataTable to be filled with photometric data
Returns
-------
photdt
Bokeh DataTable object containing columns linked to cds
cds
Bokeh ColumnDataSource containing null data
"""
bands = [*genphot.KNOWN_MAGS.keys(), ] # the photometric bands to start with
# TODO: change what bands are shown initially, do not need to be this restrictive list
data = dict(
bands=bands, # photometric bands
vals=[None, ] * len(bands), # null list for empty cells
valserr=[None, ] * len(bands) # null list for empty cells
)
cds = ColumnDataSource(data) # pass to bokeh columndatasource object
cols = [
TableColumn(field="bands", title="Band", width=100), # the column for photometric bands
TableColumn(field="vals", title="Magnitude", width=100), # the column for magnitudes
TableColumn(field="valserr", title="Error", width=100) # the column for magnitude errors
]
photdt = DataTable(source=cds, columns=cols, name='photodt', # the cds to link to, columns and element id
fit_columns=False, index_position=None, # for making small width table and ignore index column
width=300, height=150) # set exact size (pixels)
return photdt, cds


def spectra_fig():
"""
Creates the empty figure and relevant ColumnDataSource
Returns
-------
specfig
Bokeh figure object for showing spectra
cds
Bokeh ColumnDataSource object relating to the spectral data
"""
# TODO: think of a better way of doing multiple spectra on one plot, maybe call for each spectra in total number
# TODO: could add photometry to this plot, requires knowing mags, their central wavelengths and zero points
# TODO: decide what we want to see in splash view
# TODO: decide if we want it normalised or not
data = dict(
wave=np.linspace(0.1, 10, 100), # initial view
flux=np.random.randn(100) * 0.5 + 1, # some random flux data
)
cds = ColumnDataSource(data) # pass to bokeh object
tooltips = [('Wavelength', '@wave'), ('Flux', '@flux')] # tooltips for hovering
specfig = figure(tools='pan,box_zoom,wheel_zoom,reset,save,hover', tooltips=tooltips, # bokeh tools
sizing_mode='stretch_width', name='spectraplot', height=495, # sizing and ids w height in pixels
title='Empty Spectra', x_axis_label='Wavelength (um)', y_axis_label='Flux (erg/s/s/um)') # format
specfig.line(source=cds, x='wave', y='flux') # create line plot for spectra
# font sizing
specfig.title.text_font_size = '18pt'
specfig.xaxis.major_label_text_font_size = '14pt'
specfig.xaxis.axis_label_text_font_size = '16pt'
specfig.yaxis.major_label_text_font_size = '14pt'
specfig.yaxis.axis_label_text_font_size = '16pt'
return specfig, cds


def main():
"""
Generates initial elements in curdoc() and holds callback function
Methods
-------
update
The callback running on bokeh serve triggered on selection of row
"""
def update(attrname, old, new):
"""
Python callback function running in current document on selection of row.
The parameters shown are not used in shown script but are required in the bokeh callback manager
Parameters
----------
attrname
The changing attribute (required under hood)
old
The old object (required under hood)
new
The new object (required under hood)
"""
selected_index = dtcds.selected.indices[0] # the index selected from datatable on page
data = dtcds.data # the data in table
target = data['sname'][selected_index] # pulling out object name

band, mags, magserr = genphot.main(target) # grab photometric data fro given object
photcds.data['bands'] = band # update photometric table with known bands
photcds.data['vals'] = mags # update photometric table with corresponding magnitudes
photcds.data['valserr'] = magserr # update photometric table with corresponding magnitude errors

inventory = geninfo.main(target) # pull out full inventory (minus photometry) for given object
infopre.text = inventory # update the object info text box

specfig.title.text = target # change title on spectra to object name
# TODO: add spectra in here
return
# document title
curdoc().template_variables['title'] = 'Simple Browser'
curdoc().title = 'Simple Browser'

photdt, photcds = photo_table() # create empty photometric table and relating cds object
curdoc().add_root(photdt) # embed photometric table in page

infopre = info_box() # create empty <pre> box for object info
curdoc().add_root(infopre) # embed object info in page

specfig, specfigcds = spectra_fig() # create empty bokeh figure and relating cds object
curdoc().add_root(specfig) # embed spectra plot in page

dtcds, dt = st.main() # the populated table of all sources, ra and dec
dtcds.selected.on_change('indices', update) # assign python callback to be on row selection
curdoc().add_root(dt) # embed full table in page
return


if __name__ == '__main__' or 'bokeh' in __name__: # nb. the latter condition is true when running in bokeh serve
main()
Loading

0 comments on commit 278f97d

Please sign in to comment.