Exemple #1
0
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)
Exemple #2
0
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)
Exemple #3
0
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
Exemple #4
0
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)
Exemple #5
0
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)
Exemple #6
0
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)
Exemple #7
0
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)