def manage_logs(): """Show current instrument logs with options to manage them Returns ------- manage_logs.html: HTML document Page to manage all instrument logs """ if current_user.is_anonymous or current_user.authorizations not in [ "engineer" ]: abort(403) db_instrument_logs = db.session.query(InstrumentLog).order_by( InstrumentLog.time.desc()).all() instrument_logs = [ dict(status=status_code_to_text(log.status), author=log.author.name, time=log.time, instrument=log.instrument.site.name_short + "-" + log.instrument.name_short, contents=Markup(log.contents), id=log.id) for log in db_instrument_logs ] return render_template("manage_logs.html", instrument_logs=instrument_logs)
def show_radar_status(): """Show the status of all ARM Instruments Returns ------- radar_status.html: HTML document Returns an HTML document with arguments including a list of instruments, their status and their most recent log entries. """ # Get the most recent log for each instrument to determine its current status status = status_log_for_each_instrument() # Assume the instrument status is operational unless the status has changed, handled afterward db_instruments = db.session.query(Instrument).join(Instrument.site).all() instruments = [ dict(id=instrument.id, instrument_name=instrument.name_long, site_id=instrument.site_id, site=instrument.site.name_short, status=1, author="", contents="", time="") for instrument in db_instruments ] # For each instrument, if there is a corresponding status entry from the query above, # add the status and the last log's author. If not, status will stay default operational for instrument in instruments: if instrument['id'] in status: instrument['status'] = status[instrument['id']]["status_code"] instrument['author'] = status[instrument['id']]["author"] instrument['contents'] = status[instrument['id']]["contents"] instrument['log_time'] = status[instrument['id']]["time"] instrument['status'] = status_code_to_text(instrument['status']) # First, a list of site name/site id tuples for the sites that have instruments sites = [(inst['site'], inst['site_id']) for inst in instruments] # Remove duplicates by converting to a set and back to list sites_set = set(sites) sites_list = list(sites_set) # Then, the list of sites ordered by site name. sorted_sites = sorted(sites_list, key=lambda tup: tup[0]) # Finally, modify the order to have any sites with 'AMF' toward the end, and an 'OTHER' site at the end. other_sites = [site for site in sorted_sites if "OTHER" in site[0]] amf_sites = [site for site in sorted_sites if "AMF" in site[0]] regular_sites = [ site for site in sorted_sites if "OTHER" not in site[0] and "AMF" not in site[0] ] custom_ordered_sites = regular_sites + amf_sites + other_sites return render_template('radar_status.html', instruments=instruments, sites=custom_ordered_sites)
def instrument(instrument_id): """If method is "GET", get for the instrument specified by the instrument id the instrument information, recent log entries, the status of the instrument and a list of which data columns are available to plot. If the method is "DELETE", instead deletes the instrument specified by the instrument id and any table entries that reference that instrument. Parameters ---------- instrument_id: integer The database id of the instrument to be shown or deleted. Returns ------- show_instrument.html: HTML document If called with a "GET" method, returns an HTML document with arguments including instrument information, the 5 most recent log entries, the status of the instrument, and the list of columns for available data to plot on graphs. """ if request.method == "GET": db_instrument = db_select_instrument(instrument_id) db_links = db.session.query(InstrumentLink).filter(InstrumentLink.instrument_id == instrument_id).all() links = [dict(text=db_link.text, link=db_link.link) for db_link in db_links] recent_logs = db_recent_logs_by_instrument(instrument_id) # If there are any logs, the most recent log (the first of the list) has the current status if recent_logs: status = status_code_to_text(recent_logs[0]["status"]) # Change the status for each log from the enumerated code to the text name for log in recent_logs: log['status'] = status_code_to_text(log['status']) else: # If there are no recent logs, assume the instrument is operational status = "OPERATIONAL" # Proof of concept for gifs for instruments on the instrument show page. # Only keeping because the full implementation should be forthcoming soon. Also on 'show_instrument.html' # redint = redis_interface.RedisInterface() # kazr_tpi_gif_string = base64.b64encode(bytes(redint.get_gif(4))) column_list = valid_columns_for_instrument(instrument_id) return render_template('show_instrument.html', instrument=db_instrument, links=links, recent_logs=recent_logs, status=status, columns=sorted(column_list)) #, gif=kazr_tpi_gif_string) elif request.method == "DELETE": db_delete_instrument(instrument_id) return json.dumps({'id': instrument_id}), 200
def widget_status_plot(): """ Gets all instruments with site id matching 'site_id' (if less than 0 or not integer, assume all sites), the most recent log for each instrument, and sets the instrument's status as the status of the most recent log. Then groups the instruments by which site they are at, passing all the information back to the HTML template to render. Returns ------- 'widgets/status_plot.html': HTML document Returns an HTML document and passes in the instrument information and the widget id for the template generator. """ site_id = request.args.get('site_id') try: int(site_id) except ValueError: site_id = -1 # Get the most recent log for each instrument to determine its current status status = status_log_for_each_instrument() # Assume the instrument status is operational unless the status has changed, handled afterward if int(site_id) < 0: db_instruments = db.session.query(Instrument).join( Instrument.site).all() else: db_instruments = (db.session.query(Instrument).join( Instrument.site).filter(Instrument.site_id == int(site_id)).all()) instruments = [ dict(id=instrument.id, name=instrument.name_short, site_id=instrument.site.id, site=instrument.site.name_short, status=1) for instrument in db_instruments ] # For each instrument, if there is a corresponding status entry from the query above, # add the status and the last log's author. If not, status will stay default operational for instrument in instruments: if instrument['id'] in status: instrument['status'] = status[instrument['id']]["status_code"] instrument['status'] = status_code_to_text(instrument['status']) instrument_groups = {} for instrument in instruments: if instrument['site'] not in list(instrument_groups.keys()): instrument_groups[instrument['site']] = [instrument] else: instrument_groups[instrument['site']].append(instrument) return render_template('widgets/status_plot.html', instrument_groups=instrument_groups)
def widget_log_viewer(): """ Gets the most recent logs for the instrument whose id matches the request's 'instrument_id' argument. The returned list of instruments is limited by the request's 'max_logs' argument. Returns ------- 'widgets/log_viewer.html': HTML document Renders the log_viewer, passing in the logs' information. """ instrument_id = request.args.get('instrument_id') req_max_logs = request.args.get('max_logs') max_logs = 5 try: floor_max_logs = math.floor(float(req_max_logs)) if (floor_max_logs < 100) and (floor_max_logs > 0): max_logs = floor_max_logs except ValueError: max_logs = 5 if float(instrument_id) >= 0: db_logs = (db.session.query(InstrumentLog).filter( InstrumentLog.instrument_id == instrument_id).order_by( InstrumentLog.time.desc()).limit(max_logs).all()) else: db_logs = db.session.query(InstrumentLog).order_by( InstrumentLog.time.desc()).limit(max_logs).all() logs = [ dict(time=log.time, contents=Markup(log.contents), status=status_code_to_text(log.status), supporting_images=log.supporting_images, author=log.author.name, instrument_name="%s:%s" % (log.instrument.site.name_short, log.instrument.name_short)) for log in db_logs ] return render_template('widgets/log_viewer.html', logs=logs)
def new_log(): """Submit a new log entry to WARNO. Rather than having the normal 'Get' 'Post' format, this is designed to be available to more than just web users. If there are no optional arguments user_id, instrument_id, time, or status (or if one of those options is missing), the normal form to create a new log will render. If all of those options exist, the function will attempt a database insertion with the data. If the insertion fails, the form to create a new log will render with an error message. If the insertion is successful, the user will be redirected instead to the page of the instrument the log was submitted for. Also, upon success, if it is not the central facility, the log's information will be sent to the central facility's Event Manager. Parameters ---------- error: optional, integer Passed as an HTML parameter, an error message set if the latitude or longitude are not floating point numbers user-id: optional, integer Passed as an HTML parameter, the database id of the author of the new log instrument: optional, integer Passed as an HTML parameter, the database id of the instrument the log is for time: optional, string Passed as an HTML parameter, a string representing the date and time for the log status: optional, integer Passed as an HTML parameter, the status code of the instrument for the log contents: optional, string Passed as an HTML parameter, the message contents for the log create-another: optional, string Passed as an HTML parameter, 'on' indicates that the option to create a new log was selected, '' otherwise Returns ------- new_log.html: HTML document If the new log insertion was attempted but failed, or if no insertion was attempted, returns an HTML form to create a new site, with an optional argument 'error' if it was a failed database insertion. instrument: Flask redirect location If a new log insertion was attempted and succeeded, returns a Flask redirect location to the instrument function, redirecting the user to the page showing the instrument with the instrument_id matching the insertion. """ if current_user.is_anonymous or current_user.authorizations not in [ "engineer", "technician" ]: abort(403) # Default error message will not show on template when its the empty string error = "" if request.args.get('create-another') == "on": create_another = True else: create_another = False new_db_log = InstrumentLog() new_db_log.author_id = request.args.get('user-id') new_db_log.instrument_id = request.args.get('instrument') new_db_log.time = request.args.get('time') new_db_log.status = request.args.get('status') new_db_log.contents = request.args.get('contents') cfg = config.get_config_context() cert_verify = cfg['setup']['cert_verify'] # If there is valid data entered with the get request, insert and redirect to the instrument # that the log was placed for if new_db_log.author_id and new_db_log.instrument_id and new_db_log.status and new_db_log.time: # Attempt to insert an item into the database. Try/Except is necessary because # the timedate datatype the database expects has to be formatted correctly. if new_db_log.time == 'Invalid Date': error = "Invalid Date/Time Format" up_logger.error( "Invalid Date/Time format for new log entry. " "'Invalid Date' passed from JavaScript parser in template") else: try: db.session.add(new_db_log) db.session.commit() except psycopg2.DataError: # If the timedate object expected by the database was incorrectly formatted, error is set # for when the page is rendered again error = "Invalid Date/Time Format" up_logger.error( "Invalid Date/Time format for new log entry. Value: %s", new_db_log.time) else: # If it is not a central facility, pass the log to the central facility if not cfg['type']['central_facility']: packet = dict(event_code=5, data=dict( instrument_id=new_db_log.instrument_id, author_id=new_db_log.author_id, time=str(new_db_log.time), status=new_db_log.status, contents=new_db_log.contents, supporting_images=None)) payload = json.dumps(packet) requests.post(cfg['setup']['cf_url'], data=payload, headers={'Content-Type': 'application/json'}, verify=cert_verify) # If planning to create another, redirect back to this page. Prevents previous log information # from staying in the url bar, which would cause refreshes to submit new logs. If not creating another, # redirect to the instrument page that the log was submitted for if create_another: return redirect(url_for('logs.new_log')) else: return redirect( url_for('instruments.instrument', instrument_id=new_db_log.instrument_id)) # If there was no valid insert render normally but pass the indicative error # Format the instrument names to be more descriptive db_instruments = db.session.query(Instrument).all() instruments = [ dict(id=instrument.id, name=instrument.site.name_short + ":" + instrument.name_short) for instrument in db_instruments ] for instrument in instruments: recent_log = db.session.query(InstrumentLog).filter( InstrumentLog.instrument_id == instrument["id"]).order_by( InstrumentLog.time.desc()).first() if recent_log: instrument["status"] = status_code_to_text(recent_log.status) recent_log.contents = Markup(recent_log.contents) instrument["log"] = recent_log sorted_instruments = sorted(instruments, key=lambda x: x["name"]) return render_template('new_log.html', instruments=sorted_instruments, status=status_text, error=error)
def show_site(site_id): """Show an individual ARM Site's information, instruments at the site, and recent logs for those instruments. Parameters ---------- site_id: integer The database id of the site to be shown. Returns ------- show_site.html: HTML document Returns an HTML document with arguments including site information, the 5 most recent logs of all instruments at the site, and a list of the instruments at the site along with their information. """ db_site = db.session.query(Site).filter(Site.id == site_id).first() site = dict(abbv=db_site.name_short, name=db_site.name_long, latitude=db_site.latitude, longitude=db_site.longitude, facility=db_site.facility, mobile=db_site.mobile, location_name=db_site.location_name, id=db_site.id) # Get the 5 most recent logs from all instruments at the site to display db_logs = db.session.query(InstrumentLog).join(InstrumentLog.instrument).join(Instrument.site).\ filter(Instrument.site_id == site_id).order_by(desc(InstrumentLog.time)).limit(5).all() recent_logs = [ dict(time=log.time, contents=Markup(log.contents), status=status_code_to_text(log.status), supporting_images=log.supporting_images, author=log.author.name, instrument=log.instrument.name_short) for log in db_logs ] # Get the most recent log for each instrument to determine its current status il_alias_1 = aliased(InstrumentLog, name='il_alias_1') il_alias_2 = aliased(InstrumentLog, name='il_alias_2') logs = db.session.query(il_alias_1).join(il_alias_1.instrument).join(il_alias_1.author).join(Instrument.site).\ outerjoin(il_alias_2, and_(Instrument.id == il_alias_2.instrument_id, or_(il_alias_1.time < il_alias_2.time, and_(il_alias_1.time == il_alias_2.time, il_alias_1.instrument_id < il_alias_2.instrument_id)))).\ filter(il_alias_2.id == None).filter(Instrument.site_id == site_id).all() status = { log.instrument.id: dict(last_author=log.author.name, status_code=log.status) for log in logs } # Assume the instrument status is operational unless the status has changed, handled afterward db_instruments = db.session.query(Instrument).filter( Instrument.site_id == site_id).all() instruments = [ dict(abbv=instrument.name_short, name=instrument.name_long, type=instrument.type, vendor=instrument.vendor, description=instrument.description, status=1, last_author="", id=instrument.id, latitude=instrument.latitude, longitude=instrument.longitude, effective_radius=instrument.effective_radius) for instrument in db_instruments ] # For each instrument, if there is a corresponding status entry from the query above, # add the status and the last log's author. If not, status will stay default operational for instrument in instruments: if instrument['id'] in status: instrument['status'] = status[instrument['id']]["status_code"] instrument['last_author'] = status[instrument['id']]["last_author"] instrument['status'] = status_code_to_text(instrument['status']) return render_template('show_site.html', site=site, instruments=instruments, recent_logs=recent_logs)