calculator.py 22.1 KB
Newer Older
sarrvesh's avatar
sarrvesh committed
1 2
"""Main file for LUCI"""

sarrvesh's avatar
sarrvesh committed
3
__author__ = "Sarrvesh S. Sridhar"
sarrvesh's avatar
sarrvesh committed
4
__email__ = "sarrvesh@astron.nl"
sarrvesh's avatar
sarrvesh committed
5

sarrvesh's avatar
sarrvesh committed
6 7
from random import randint
import os
sarrvesh's avatar
sarrvesh committed
8 9
import dash
import dash_bootstrap_components as dbc
sarrvesh's avatar
sarrvesh committed
10
from dash.dependencies import Input, Output, State
sarrvesh's avatar
sarrvesh committed
11
import flask
12
from gui import layout
sarrvesh's avatar
sarrvesh committed
13
import backend as bk
14
import targetvis as tv
15
import generatepdf as g
sarrvesh's avatar
sarrvesh committed
16 17

# Initialize the dash app
sarrvesh's avatar
sarrvesh committed
18 19
server = flask.Flask(__name__)
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.LUMEN], \
20 21 22
                server=server, url_base_pathname='/luci/')
app.css.config.serve_locally = True
app.scripts.config.serve_locally = True
sarrvesh's avatar
sarrvesh committed
23 24 25 26

#######################################
# Setup the layout of the web interface
#######################################
27
app.layout = layout
sarrvesh's avatar
sarrvesh committed
28
app.title = 'LUCI - LOFAR Unified Calculator for Imaging'
sarrvesh's avatar
sarrvesh committed
29

30 31 32 33 34
##############################################
# TODO: Move all callbacks to a separate file
# See https://community.plot.ly/t/dash-callback-in-a-separate-file/14122/16
##############################################

35
##############################################
36
# Show observational setup fields based on
37 38 39 40 41 42 43
# obsMode dropdown value
##############################################
@app.callback(
    [Output('tabModeForm', 'style'),
     Output('tabModeRowL', 'style'),
     Output('tabModeRow', 'style'),
     Output('tabModeRow', 'value'),
44

45 46 47
     Output('stokesForm', 'style'),
     Output('stokesRowL', 'style'),
     Output('stokesRow', 'style'),
48

49 50 51
     Output('nRingsForm', 'style'),
     Output('nRingsRow', 'style'),
     Output('nRingsRowL', 'style'),
52

53 54 55 56 57
     Output('pipeTypeRow', 'options'),
     Output('pipeTypeRow', 'value')
    ],
    [Input('obsModeRow', 'value')]
)
58
def toggle_obs_mode(obs_value):
59 60 61 62 63 64
    """Function to show relevant observational setup fields
       depending on the user's choice"""
    all_pipelines = {
        'Interferometric': ['none', 'preprocessing'],
        'Beamformed': ['none', 'pulp']
    }
65 66 67
    valid_pipes = [{'label':i, 'value':i} for i in all_pipelines[obs_value]]
    if obs_value == 'Interferometric':
        return {'display':'none'}, {'display':'none'}, {'display':'none'}, 'Incoherent', \
68 69 70 71
               {'display':'none'}, {'display':'none'}, {'display':'none'}, \
               {'display':'none'}, {'display':'none'}, {'display':'none'}, \
               valid_pipes, 'none'
    else:
72
        return {}, {'display':'block'}, {'display':'block'}, 'Incoherent', \
73 74 75 76 77 78 79 80
               {}, {'display':'block'}, {'display':'block'}, \
               {}, {'display':'block'}, {'display':'block'}, \
               valid_pipes, 'none'

################################################
# Show TAb stokes fields based on dropdown value
################################################
@app.callback(
81
    [Output('stokesRow', 'options'),
82
     Output('stokesRow', 'value'),
83

84 85 86
     Output('nRemoteForm', 'style'),
     Output('nRemoteRow', 'style'),
     Output('nRemoteRowL', 'style'),
87

88 89 90
     Output('nIntForm', 'style'),
     Output('nIntRow', 'style'),
     Output('nIntRowL', 'style'),
91 92 93 94 95 96 97 98 99 100 101 102 103
    ],
    [Input('tabModeRow', 'value')]
)
def toggle_stokes(value):
    """Function to show relevant Stokes products depending
       on the user's TAB choice"""
    if value == '':
        value = 'Coherent'
    all_stokes = {
        'Coherent': ['I', 'IQUV', 'XXYY'],
        'Incoherent': ['I', 'IQUV']
    }
    valid_stokes = [{'label':i, 'value':i} for i in all_stokes[value]]
104 105 106 107 108 109 110
    if value == 'Incoherent':
        return valid_stokes, 'I', \
               {}, {'display':'block'}, {'display':'block'}, \
               {}, {'display':'block'}, {'display':'block'}
    else:
        return valid_stokes, 'I', \
               {'display':'none'}, {'display':'none'}, {'display':'none'}, \
111
               {'display':'none'}, {'display':'none'}, {'display':'none'},
112 113


114 115 116 117
##############################################
# Show pipeline fields based on dropdown value
##############################################
@app.callback(
sarrvesh's avatar
sarrvesh committed
118 119 120 121 122 123 124 125 126 127
    [Output('tAvgRowL', 'style'),
     Output('tAvgRow', 'style'),
     Output('fAvgRowL', 'style'),
     Output('fAvgRow', 'style'),
     Output('dyCompressRowL', 'style'),
     Output('dyCompressRow', 'style'),
     Output('pipeSizeRowL', 'style'),
     Output('pipeSizeRow', 'style'),
     Output('pipeProcTimeRow', 'style'),
     Output('pipeProcTimeRowL', 'style')
128
    ],
sarrvesh's avatar
sarrvesh committed
129
    [Input('pipeTypeRow', 'value')]
130 131 132 133 134 135
)
def toggle_pipeline(value):
    """Function to show relevant pipeline fields depending on
       the user's pipeline choice."""
    if value == 'none':
        return {'display':'none'}, {'display':'none'}, \
136 137
               {'display':'none'}, {'display':'none'}, \
               {'display':'none'}, {'display':'none'}, \
138 139
               {'display':'none'}, {'display':'none'}, \
               {'display':'none'}, {'display':'none'}
140
    elif value == 'preprocessing':
141
        return {'display':'block'}, {'display':'block'}, \
142 143
               {'display':'block'}, {'display':'block'}, \
               {'display':'block'}, {'display':'block'}, \
144 145 146
               {'display':'block'}, {'display':'block'}, \
               {'display':'block'}, {'display':'block'}

147 148 149 150
#######################################
# Validate time averaging factor
#######################################
@app.callback(
sarrvesh's avatar
sarrvesh committed
151 152 153 154 155 156 157
    Output('msgboxTAvg', 'is_open'),
    [Input('tAvgRow', 'n_blur'),
     Input('mbtAvgClose', 'n_clicks')
    ],
    [State('tAvgRow', 'value'),
     State('msgboxTAvg', 'is_open')
    ]
158
)
sarrvesh's avatar
sarrvesh committed
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
def validate_t_avg(n_blur, n_clicks, value, is_open):
    """Validate time averaging factor and display error message if needed"""
    if is_open is True and n_clicks is not None:
        # The message box is open and the user has clicked the close
        # button. Close the alert message
        return False
    if n_blur is None:
        # The page is loading. Do not validate anything
        return False
    else:
        # Text box has lost focus.
        # Go ahead and validate the text in it.
        try:
            int(str(value))
        except ValueError:
            return True
        return False
176 177 178 179 180

#######################################
# Validate freq averaging factor
#######################################
@app.callback(
sarrvesh's avatar
sarrvesh committed
181
    Output('msgboxFAvg', 'is_open'),
182
    [Input('fAvgRow', 'value'),
sarrvesh's avatar
sarrvesh committed
183 184
     Input('mbfAvgClose', 'n_clicks')
    ],
185 186
    [State('msgboxFAvg', 'is_open'),
     State('nChanRow','value')
sarrvesh's avatar
sarrvesh committed
187
    ]
188
)
189
def validate_f_avg(value, n_clicks, is_open, channels_per_subband):
sarrvesh's avatar
sarrvesh committed
190 191 192 193 194
    """Validate frequency averaging factor and display error message if needed"""
    if is_open is True and n_clicks is not None:
        # The message box is open and the user has clicked the close
        # button. Close the alert message
        return False
195
    #if n_blur is None:
sarrvesh's avatar
sarrvesh committed
196
        # The page is loading. Do not validate anything
197
    #    return False
sarrvesh's avatar
sarrvesh committed
198 199 200 201 202 203 204
    else:
        # Text box has lost focus.
        # Go ahead and validate the text in it.
        try:
            int(str(value))
        except ValueError:
            return True
205 206 207 208
        try:
            assert int(str(channels_per_subband))>=int(str(value))
        except:
            return True
sarrvesh's avatar
sarrvesh committed
209
        return False
210

211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270

#######################################
# Limit freq averaging factor
#######################################
@app.callback(
    Output('fAvgRow', 'options'),
    [Input('nChanRow', 'value'),
     Input('mbfAvgClose', 'n_clicks')
    ],
    [State('msgboxFAvg', 'is_open'),
    ]
)
def validate_f_avg(value, n_clicks, is_open):
    """Validate frequency averaging factor and display error message if needed"""
    if is_open is True and n_clicks is not None:
        # The message box is open and the user has clicked the close
        # button. Close the alert message
        return False
    #if n_blur is None:
        # The page is loading. Do not validate anything
    #    return False
    else:
        # Text box has lost focus.
        # Go ahead and validate the text in it.
        if int(str(value))==64:
            return [
                        {'label':'1', 'value':1},
                        {'label':'2', 'value':2},
                        {'label':'4', 'value':4},
                        {'label':'8', 'value':8},
                        {'label':'16', 'value':16},
                        {'label':'32', 'value':32},
                        {'label':'64', 'value':64},
                    ]
        elif  int(str(value))==128:
            return [
                        {'label':'1', 'value':1},
                        {'label':'2', 'value':2},
                        {'label':'4', 'value':4},
                        {'label':'8', 'value':8},
                        {'label':'16', 'value':16},
                        {'label':'32', 'value':32},
                        {'label':'64', 'value':64},
                        {'label':'128', 'value':128},
                    ]
        # else, return default, max 256 averaging factor
        return [
                        {'label':'1', 'value':1},
                        {'label':'2', 'value':2},
                        {'label':'4', 'value':4},
                        {'label':'8', 'value':8},
                        {'label':'16', 'value':16},
                        {'label':'32', 'value':32},
                        {'label':'64', 'value':64},
                        {'label':'128', 'value':128},
                        {'label':'256', 'value':256}
                    ]



271 272 273 274
#######################################
# What should the resolve button do?
#######################################
@app.callback(
sarrvesh's avatar
sarrvesh committed
275 276 277 278 279 280 281 282 283
    [Output('coordRow', 'value'),
     Output('msgboxResolve', 'is_open')
    ],
    [Input('resolve', 'n_clicks'),
     Input('mbResolveClose', 'n_clicks')
    ],
    [State('targetNameRow', 'value'),
     State('msgboxResolve', 'is_open')
    ]
284
)
sarrvesh's avatar
sarrvesh committed
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
def on_resolve_click(n, close_msg_box, target_name, is_open):
    """Function defines what to do when the resolve button is clicked"""
    if is_open is True and close_msg_box is not None:
        # The message box is open and the user has clicked the close
        # button. Close the alert message
        return '', False
    if n is None:
        # The page has just loaded.
        return '', False
    else:
        # Resole button has been clicked
        coord_str = tv.resolve_source(target_name)
        if coord_str is None:
            # Display error message.
            return '', True
        else:
            return coord_str, False
302

sarrvesh's avatar
sarrvesh committed
303 304 305 306
#######################################
# What should the export button do?
#######################################
@app.callback(
sarrvesh's avatar
sarrvesh committed
307 308 309 310 311 312 313 314
    [Output('download-link', 'style'),
     Output('download-link', 'href'),
     Output('msgboxGenPdf', 'is_open')
    ],
    [Input('genpdf', 'n_clicks'),
     Input('mbGenPdfClose', 'n_clicks')
    ],
    [State('obsTimeRow', 'value'),
315 316
     State('calTimeRow', 'value'),
     State('nCalRow', 'value'),
sarrvesh's avatar
sarrvesh committed
317 318 319 320 321 322 323
     State('nCoreRow', 'value'),
     State('nRemoteRow', 'value'),
     State('nIntRow', 'value'),
     State('nChanRow', 'value'),
     State('nSbRow', 'value'),
     State('intTimeRow', 'value'),
     State('hbaDualRow', 'value'),
324
     State('coordRow', 'value'),
sarrvesh's avatar
sarrvesh committed
325 326 327 328 329 330 331 332 333 334 335 336 337 338

     State('pipeTypeRow', 'value'),
     State('tAvgRow', 'value'),
     State('fAvgRow', 'value'),
     State('dyCompressRow', 'value'),

     State('imNoiseRow', 'value'),
     State('rawSizeRow', 'value'),
     State('pipeSizeRow', 'value'),
     State('pipeProcTimeRow', 'value'),

     State('msgboxGenPdf', 'is_open'),

     State('elevation-plot', 'figure'),
sarrvesh's avatar
sarrvesh committed
339
     State('distance-table', 'figure'),
sarrvesh's avatar
sarrvesh committed
340

sarrvesh's avatar
sarrvesh committed
341
     State('dateRow', 'date')
sarrvesh's avatar
sarrvesh committed
342
    ]
sarrvesh's avatar
sarrvesh committed
343
)
344 345
def on_genpdf_click(n_clicks, close_msg_box, obs_t, cal_t, n_cal, n_core, n_remote, n_int, n_chan,
                    n_sb, integ_t, ant_set, coord, pipe_type, t_avg, f_avg, is_dysco,
sarrvesh's avatar
sarrvesh committed
346
                    im_noise_val, raw_size, proc_size, pipe_time, is_msg_box_open,
sarrvesh's avatar
sarrvesh committed
347
                    elevation_fig, distance_table, obs_date):
sarrvesh's avatar
sarrvesh committed
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
    """Function defines what to do when the generate pdf button is clicked"""
    if is_msg_box_open is True and close_msg_box is not None:
        # The message box is open and the user has clicked the close
        # button. Close the alert message.
        return {'display':'none'}, '', False
    if n_clicks is None:
        # Generate button has not been clicked. Hide the download link
        return {'display':'none'}, '', False
    else:
        if im_noise_val is '':
            # User has clicked generate PDF button before calculate
            return {'display':'none'}, '', True
        else:
            # Generate a random number so that this user's pdf can be stored here
            randnum = '{:05d}'.format(randint(0, 10000))
            rel_path = 'static/'
            # Generate a relative and absolute filenames to the pdf file
            rel_path = os.path.join(rel_path, 'summary_{}.pdf'.format(randnum))
            abs_path = os.path.join(os.getcwd(), rel_path)
367 368
            g.generate_pdf(rel_path, obs_t, cal_t, n_cal, n_core, n_remote, n_int, n_chan,
                           n_sb, integ_t, ant_set, coord, pipe_type, t_avg, f_avg,
sarrvesh's avatar
sarrvesh committed
369
                           is_dysco, im_noise_val, raw_size, proc_size, pipe_time,
sarrvesh's avatar
sarrvesh committed
370
                           elevation_fig, distance_table, obs_date)
sarrvesh's avatar
sarrvesh committed
371
            return {'display':'block'}, '/luci/{}'.format(rel_path), False
sarrvesh's avatar
sarrvesh committed
372

sarrvesh's avatar
sarrvesh committed
373 374
@app.server.route('/luci/static/<resource>')
def serve_static(resource):
sarrvesh's avatar
sarrvesh committed
375
    path = os.path.join(os.getcwd(), 'static')
sarrvesh's avatar
sarrvesh committed
376
    return flask.send_from_directory(path, resource)
sarrvesh's avatar
sarrvesh committed
377

sarrvesh's avatar
sarrvesh committed
378 379 380 381
#######################################
# What should the submit button do?
#######################################
@app.callback(
sarrvesh's avatar
sarrvesh committed
382 383 384 385 386 387 388 389 390 391 392 393
    [Output('imNoiseRow', 'value'),
     Output('rawSizeRow', 'value'),
     Output('pipeSizeRow', 'value'),
     Output('pipeProcTimeRow', 'value'),
     Output('msgBoxBody', 'children'),
     Output('msgbox', 'is_open'),
     Output('elevation-plot', 'style'),
     Output('elevation-plot', 'figure'),
     Output('beam-plot', 'style'),
     Output('beam-plot', 'figure'),
     Output('distance-table', 'style'),
     Output('distance-table', 'figure')
394
    ],
sarrvesh's avatar
sarrvesh committed
395 396
    [Input('calculate', 'n_clicks'),
     Input('msgBoxClose', 'n_clicks'),
sarrvesh's avatar
sarrvesh committed
397
    ],
sarrvesh's avatar
sarrvesh committed
398
    [State('obsTimeRow', 'value'),
399 400
     State('calTimeRow', 'value'),
     State('nCalRow', 'value'),
sarrvesh's avatar
sarrvesh committed
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
     State('nCoreRow', 'value'),
     State('nRemoteRow', 'value'),
     State('nIntRow', 'value'),
     State('nChanRow', 'value'),
     State('nSbRow', 'value'),
     State('intTimeRow', 'value'),
     State('hbaDualRow', 'value'),
     State('pipeTypeRow', 'value'),
     State('tAvgRow', 'value'),
     State('fAvgRow', 'value'),
     State('dyCompressRow', 'value'),
     State('msgbox', 'is_open'),
     State('targetNameRow', 'value'),
     State('coordRow', 'value'),
     State('dateRow', 'date'),
     State('calListRow', 'value'),
     State('demixListRow', 'value')
sarrvesh's avatar
sarrvesh committed
418 419
    ]
)
420
def on_calculate_click(n, n_clicks, obs_t, cal_t, n_cal, n_core, n_remote, n_int, n_chan, n_sb,
sarrvesh's avatar
sarrvesh committed
421 422 423
                       integ_t, hba_mode, pipe_type, t_avg, f_avg, dy_compress,
                       is_open, src_name, coord, obs_date, calib_names,
                       ateam_names):
sarrvesh's avatar
sarrvesh committed
424
    """Function defines what to do when the calculate button is clicked"""
425
    if is_open is True:
sarrvesh's avatar
sarrvesh committed
426 427 428 429
        # User has closed the error message box
        return '', '', '', '', '', False, \
               {'display':'none'}, {}, {'display':'none'}, \
               {}, {'display':'none'}, {}
sarrvesh's avatar
sarrvesh committed
430 431 432
    if n is None:
        # Calculate button has not been clicked yet
        # So, do nothing and set default values to results field
sarrvesh's avatar
sarrvesh committed
433
        return '', '', '', '', '', False, \
sarrvesh's avatar
sarrvesh committed
434 435
               {'display':'none'}, {}, {'display':'none'}, {}, \
               {'display':'none'}, {}
sarrvesh's avatar
sarrvesh committed
436 437 438
    else:
        # Calculate button has been clicked.
        # First, validate all command line inputs
439

sarrvesh's avatar
sarrvesh committed
440 441
        # If the user sets n_core, n_remote, or n_int to 0, dash return None.
        # Why is this?
442
        # Correct this manually, for now.
sarrvesh's avatar
sarrvesh committed
443 444 445 446 447 448
        if n_core is None:
            n_core = '0'
        if n_remote is None:
            n_remote = '0'
        if n_int is None:
            n_int = '0'
449
        status, msg = bk.validate_inputs(obs_t, cal_t, int(n_cal), int(n_core), int(n_remote), \
sarrvesh's avatar
sarrvesh committed
450
                                         int(n_int), n_sb, integ_t, t_avg, f_avg, \
451 452
                                         src_name, coord, hba_mode, pipe_type, \
                                         ateam_names)
sarrvesh's avatar
sarrvesh committed
453
        if status is False:
sarrvesh's avatar
sarrvesh committed
454 455 456
            return '', '', '', '', msg, True, \
                   {'display':'none'}, {}, {'display':'none'}, {}, \
                   {'display':'none'}, {}
sarrvesh's avatar
sarrvesh committed
457
        else:
sarrvesh's avatar
sarrvesh committed
458
            # Estimate the raw data size
459 460 461 462 463 464 465
            if coord is not '':
                coord_list = coord.split(',')
                coord_input_list = coord.split(',')
                n_sap=len(coord_list)
            else:
                n_sap=1

sarrvesh's avatar
sarrvesh committed
466 467 468 469 470
            n_baselines = bk.compute_baselines(int(n_core), int(n_remote),
                                               int(n_int), hba_mode)
            im_noise = bk.calculate_im_noise(int(n_core), int(n_remote),
                                             int(n_int), hba_mode, float(obs_t),
                                             int(n_sb))
471 472 473 474
            raw_size = bk.calculate_raw_size(float(obs_t), float(cal_t), int(n_cal), float(integ_t),
                                             n_baselines, int(n_chan), int(n_sb), n_sap)
            avg_size = bk.calculate_proc_size(float(obs_t), float(cal_t), int(n_cal), float(integ_t),
                                              n_baselines, int(n_chan), int(n_sb), n_sap,
sarrvesh's avatar
sarrvesh committed
475 476
                                              pipe_type, int(t_avg), int(f_avg),
                                              dy_compress)
sarrvesh's avatar
sarrvesh committed
477 478 479 480
            if pipe_type == 'none':
                # No pipeline
                pipe_time = None
            else:
481
                pipe_time = bk.calculate_pipe_time(float(obs_t), float(cal_t), int(n_cal), int(n_sb), n_sap,
sarrvesh's avatar
sarrvesh committed
482 483
                                                   hba_mode, ateam_names,
                                                   pipe_type)
sarrvesh's avatar
sarrvesh committed
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499

            # It is useful to have coord as a list from now on

            # Add calibrator names to the target list so that they can be
            # plotted together. Before doing that, make a copy of the input
            # target list and its coordinates
            src_name_input = src_name
            coord_input = coord
            if calib_names is not None:
                for i in range(len(calib_names)):
                    if i == 0 and src_name is None:
                        src_name = '{}'.format(calib_names[i])
                        coord_list = [tv.CALIB_COORDINATES[calib_names[i]]]
                    else:
                        src_name += ', {}'.format(calib_names[i])
                        coord_list.append(tv.CALIB_COORDINATES[calib_names[i]])
sarrvesh's avatar
sarrvesh committed
500

sarrvesh's avatar
sarrvesh committed
501 502 503 504 505 506 507 508 509 510
            # Add A-team names to the target list so that they can be
            # plotted together
            if ateam_names is not None:
                for i in range(len(ateam_names)):
                    if i == 0 and src_name is None:
                        src_name = '{}'.format(ateam_names[i])
                        coord_list = [tv.ATEAM_COORDINATES[ateam_names[i]]]
                    else:
                        src_name += ', {}'.format(ateam_names[i])
                        coord_list.append(tv.ATEAM_COORDINATES[ateam_names[i]])
sarrvesh's avatar
sarrvesh committed
511

sarrvesh's avatar
sarrvesh committed
512 513 514 515 516 517 518 519 520 521 522 523
            if coord is '':
                # No source is specified under Target setup
                display_fig = {'display':'none'}
                elevation_fig = {}
                beam_fig = {}
                display_tab = {'display':'none'}
                distance_tab = {}
            else:
                # User has specified a coordinate and it has passed validation
                # in the validate_inputs function.
                # Check if the number of SAPs is less than 488
                n_point = len(coord_input_list)
524 525 526
                n_beamlet = n_point * int(n_sb)
                max_beamlet = 488
                if n_beamlet > max_beamlet:
sarrvesh's avatar
sarrvesh committed
527
                    msg = 'Number of targets times number of subbands cannot ' + \
528
                          'be greater than {}.'.format(max_beamlet)
sarrvesh's avatar
sarrvesh committed
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558
                    return '', '', '', '', msg, True, \
                           {'display':'none'}, {}, {'display':'none'}, {}, \
                           {'display':'none'}, {}
                # Find target elevation across a 24-hour period
                data = tv.find_target_elevation(src_name, coord_list,
                                                obs_date, int(n_int))
                display_fig = {'display':'block', 'height':600}
                elevation_fig = {'data':data,
                                 'layout':{
                                     'xaxis':{'title':'Time (UTC)'},
                                     'yaxis':{'title':'Elevation'},
                                     'title':'Target visibility plot',
                                     'shapes':[]
                                 }
                                }
                elevation_fig = tv.add_sun_rise_and_set_times(obs_date,
                                                              int(n_int),
                                                              elevation_fig)
                # Find the position of the station and tile beam
                beam_fig = tv.find_beam_layout(src_name_input, coord_input, \
                                   int(n_core), int(n_remote), int(n_int), hba_mode)
                # Calculate distance between all the targets and offending sources
                display_tab = {'display':'block'}
                table_data = [tv.make_distance_table(src_name_input,
                                                     coord_input, obs_date)]
                table_title = 'Angular distances in degrees between specified ' +\
                             'targets and other bright sources'
                distance_tab = {'data':table_data,
                                'layout':{'title':table_title, 'autosize':True}
                               }
sarrvesh's avatar
sarrvesh committed
559

sarrvesh's avatar
sarrvesh committed
560
            return im_noise, raw_size, avg_size, pipe_time, '', \
sarrvesh's avatar
sarrvesh committed
561 562
                   False, display_fig, elevation_fig, display_fig, beam_fig, \
                   display_tab, distance_tab
sarrvesh's avatar
sarrvesh committed
563 564

if __name__ == '__main__':
565 566 567
    app.run_server(debug=True, host='0.0.0.0', port=8051)
    #app.run_server(debug=False, host='0.0.0.0', port=8051, \
    #              dev_tools_ui=False, dev_tools_props_check=False)