Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Standard runs #161

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a5d7bf7
WIP get started on standard run interface
marzece May 26, 2018
e7d20eb
get overview page for standard runs working okay
marzece May 26, 2018
53bd774
add detail view for standard run
marzece May 27, 2018
a9ac6aa
implement updating standard runs
marzece May 28, 2018
51f12c4
allow for trailing slash in some urls
marzece May 28, 2018
a885cbc
redo form layout and add meta-info inputs
marzece May 28, 2018
ab6bf43
change comment
marzece May 28, 2018
b56a205
add back button if standard run detail view
marzece May 28, 2018
017dc07
minor changes
marzece May 28, 2018
4696ffa
add link for standard runs to nav bar
marzece May 28, 2018
b61d71c
add template for SR overview (which I forgot to add to git)
marzece May 28, 2018
4c48d46
Change page title
marzece May 28, 2018
4e0e5d4
create new documents rather then new versions of existing ones
marzece May 30, 2018
7d5d4e8
update timestamp for new documents
marzece May 31, 2018
236555e
redirect to new standard run upon creation of one
marzece May 31, 2018
25a0fe1
Add error checking for couchdb interactions
marzece May 31, 2018
b7e01d7
add some basic validation
marzece May 31, 2018
90fc799
coerce types for new values
marzece May 31, 2018
d290e21
Only display standard runs matching orca's standard run version
marzece Jun 1, 2018
b8522de
remove dictionary list comprehesion for python 2.6
marzece Jun 1, 2018
f8eacba
move standard run password validation to couch DB
marzece Jun 2, 2018
79e41e4
reimplement UI using wtforms and add validators
marzece Jun 7, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions minard/standard_runs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import couchdb
from .views import app
from itertools import groupby
from time import time
from uuid import uuid4
from wtforms import Form, IntegerField, StringField, TextAreaField, PasswordField,\
DecimalField, BooleanField, validators

def get_standard_runs():
# This number should match the version used by ORCA
COUCH_DOC_VERSION = app.config["ORCA_STANDARD_RUN_VERSION"]
url = "https://%s:%s@%s" % ("snoplus", app.config["COUCHDB_PASSWORD"], app.config["COUCHDB_HOSTNAME"])
couch = couchdb.Server(url)
orca_db = couch['orca']
sr_view = orca_db.view("standardRuns/getStandardRunsWithVersion")
# standard run keys are [doc-version, run name, run version, timestamp]
# I want to groupby by run name then within that group all run versions sorted
# by timestamp
rows = filter(lambda x: x.key[0] == COUCH_DOC_VERSION, sr_view.rows)
rows = sorted(rows, key=lambda x: x.key[1] + x.key[2])
groups = groupby(rows, lambda x: x.key[1] + x.key[2])
groups = [(x, list(y)) for x, y in groups]
runs = [max(group, key=lambda x: x.key[3]) for _, group in groups]
runs = sorted(runs, key=lambda x: x.key[1])
runs = [(x, list(y)) for x,y in groupby(runs, lambda x: x.key[1])]
return runs

def get_standard_run(uuid):
url = "https://%s:%s@%s" % ("snoplus", app.config["COUCHDB_PASSWORD"], app.config["COUCHDB_HOSTNAME"])
couch = couchdb.Server(url)
orca_db = couch['orca']
return orca_db.get(uuid)

def update_standard_run(uuid, new_values):
try:
password = new_values.pop("password", None)
except KeyError:
raise RuntimeError("no password given")
url = "https://%s:%s@%s" % (app.config["COUCH_DETECTOR_EXPERT_NAME"],
password,
app.config["COUCHDB_HOSTNAME"])
couch = couchdb.Server(url)
try:
orca_db = couch['orca']
except couchdb.http.Unauthorized:
raise RuntimeError("Incorrect password given")

doc = dict(orca_db.get(uuid))
for k, v in new_values.iteritems():
doc[k] = v
doc["_id"] = uuid4().hex
doc["time_stamp"] = time()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the same format that's used by the other standard runs?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, the couch view that orca uses sorts by doc.time_stamp so this is the field that should be updated

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah ok. For some reason I thought the timestamps in couch were stored as text.


if not doc.has_key("run_version") or not doc["run_version"]:
raise RuntimeError("run_version must be present in new document")

if not doc.has_key("run_type") or not doc["run_type"]:
raise RuntimeError("run_type must be present in new document")

doc["run_type"] = doc["run_type"].upper()
doc["run_version"] = doc["run_version"].upper()

# Remove revision field since we want to post a new document, not a
# revision of an existing one.
try:
del doc["_rev"]
except KeyError:
pass

new_uuid, _ = orca_db.save(doc)
return new_uuid

expected_fields = [
IntegerField("CAEN_acquisitionMode", [validators.NumberRange(min=0, max=0b111)]),
IntegerField("CAEN_channelConfigMask"),
IntegerField("CAEN_coincidenceLevel"),
BooleanField("CAEN_countAllTriggers"),
IntegerField("CAEN_customSize"),
IntegerField("CAEN_dac_0", [validators.NumberRange(min=0, max=0xFFFF)]),
IntegerField("CAEN_dac_1", [validators.NumberRange(min=0, max=0xFFFF)]),
IntegerField("CAEN_dac_2", [validators.NumberRange(min=0, max=0xFFFF)]),
IntegerField("CAEN_dac_3", [validators.NumberRange(min=0, max=0xFFFF)]),
IntegerField("CAEN_dac_4", [validators.NumberRange(min=0, max=0xFFFF)]),
IntegerField("CAEN_dac_5", [validators.NumberRange(min=0, max=0xFFFF)]),
IntegerField("CAEN_dac_6", [validators.NumberRange(min=0, max=0xFFFF)]),
IntegerField("CAEN_dac_7", [validators.NumberRange(min=0, max=0xFFFF)]),
IntegerField("CAEN_enabledMask", [validators.NumberRange(min=0, max=0xFF)]),
IntegerField("CAEN_eventSize"),
IntegerField("CAEN_frontPanelControlMask"),
BooleanField("CAEN_isCustomSize"),
IntegerField("CAEN_postTriggerSetting"),
IntegerField("CAEN_triggerOutMask"),
IntegerField("CAEN_triggerSourceMask"),
IntegerField("MTC_ESUMH_Threshold", [validators.NumberRange(min=0,max=4095)]),
IntegerField("MTC_ESUML_Threshold", [validators.NumberRange(min=0,max=4095)]),
IntegerField("MTC_GTMask", [validators.NumberRange(min=0, max=0x3FFFFFF)]),
IntegerField("MTC_LockoutWidth", [validators.NumberRange(min=20, max=5000)]),
IntegerField("MTC_N100H_Threshold", [validators.NumberRange(min=0, max=4095)]),
IntegerField("MTC_N100L_Threshold", [validators.NumberRange(min=0, max=4095)]),
IntegerField("MTC_N100M_Threshold", [validators.NumberRange(min=0, max=4095)]),
IntegerField("MTC_N20LB_Threshold", [validators.NumberRange(min=0, max=4095)]),
IntegerField("MTC_N20_Threshold", [validators.NumberRange(min=0, max=4095)]),
IntegerField("MTC_OWLEH_Threshold", [validators.NumberRange(min=0, max=4095)]),
IntegerField("MTC_OWLEL_Threshold", [validators.NumberRange(min=0, max=4095)]),
IntegerField("MTC_OWLN_Threshold", [validators.NumberRange(min=0, max=4095)]),
IntegerField("MTC_PrescaleValue", [validators.NumberRange(min=2, max=65536)]),
BooleanField("MTC_PulserEnabled"),
BooleanField("MTC_PulserMode"),
DecimalField("MTC_PulserRate", [validators.NumberRange(min=0.04, max=390000)]),
IntegerField("TUBii_CaenChannelMask", [validators.NumberRange(min=0, max=255)]),
IntegerField("TUBii_CaenGainMask", [validators.NumberRange(min=0, max=255)]),
IntegerField("TUBii_DGT_Bits", [validators.NumberRange(min=0, max=255)]),
IntegerField("TUBii_LO_Bits", [validators.NumberRange(min=0, max=255)]),
IntegerField("TUBii_MTCAMimic1_ThresholdInBits", [validators.NumberRange(min=0, max=4095)]),
IntegerField("TUBii_TUBiiPGT_Rate", [validators.NumberRange(min=0)]),
IntegerField("TUBii_asyncTrigMask"),
IntegerField('TUBii_controlReg', [validators.NumberRange(min=0,max=255)]),
IntegerField('TUBii_counterMask', [validators.NumberRange(min=0)]),
IntegerField('TUBii_speakerMask', [validators.NumberRange(min=0)]),
IntegerField('TUBii_syncTrigMask', [validators.NumberRange(min=0)]),
IntegerField("run_type_word", [validators.NumberRange(min=0x0, max=0xFFFFFFF)]),
StringField("run_version", [validators.DataRequired()]),
StringField("type", [validators.DataRequired()])
]
expected_fields = dict([(x.args[0], x) for x in expected_fields])

def create_form(fields):
class SRSettingsForm(Form):
name = StringField('Name', [validators.DataRequired()])
info = TextAreaField('Info', [validators.DataRequired()])
password = PasswordField('Password', [validators.DataRequired()])

# First create a form for the various fields in the couchDB doc
for key, value in fields.iteritems():
# skip any fields that are already in the form
if key.lower() in ["name", "info", "password"]:
continue
if expected_fields.has_key(key):
field = expected_fields[key]
setattr(SRSettingsForm, key, field)
else:
setattr(SRSettingsForm, key, StringField(key))
return SRSettingsForm
1 change: 1 addition & 0 deletions minard/templates/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
{{ nav_link('ecal_state_diff', 'ECAL State Diff') }}
{{ nav_link('check_rates', 'Check Rates') }}
{{ nav_link('update_mtca_crate_mapping', 'Update MTCA+ Mapping') }}
{{ nav_link('standard_runs', 'Standard Runs') }}
{{ nav_link('alarms', 'Alarms') }}
<li><a href="logs">DAQ Logs</a></li>
{{ nav_link('cavity_temp', 'Cavity Temperature') }}
Expand Down
82 changes: 82 additions & 0 deletions minard/templates/standard_run.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{% extends "layout.html" %}
{% block title %}Standard Run{% endblock %}
{% block head %}
{{ super() }}
{% endblock %}
{% block body %}
{{ super() }}
<div class="container">
{% if form %}
{% if form.errors %}
<ul class="errors">
{% for field_name, field_errors in form.errors|dictsort if field_errors %}
{% for error in field_errors %}
<li>{{ form[field_name].label }}: {{ error }}</li>
{% endfor %}
{% endfor %}
</ul>
{% endif %}
<div class = "row">
<h2>
<a href="{{url_for('standard_runs')}}">
<span class="glyphicon glyphicon-chevron-left"> </span>
</a>
{{ form.run_type.data }} - {{ form.run_version.data }}
</h2>
</div>
{% if updater %}
<div class = "row">
<h4>
Last Updated By: {{ updater }}
</h4>
</div>
{% endif %}
{% if comments %}
<div class="row">
<p> Notes: {{ comments }} </p>
</div>
{% endif %}
<hr>
<div class="row">
<div>
<form action="{{url_for('standard_runs',uuid=uuid)}}" method="POST">
<table class="table table-striped">
<thead>
<th> Setting </th>
<th> Current Value </th>
<th> New Value </th>
</thead>
{% for field in form %}
{% if field.label.text.lower() not in ['name', 'password', 'info'] %}
<tr>
<td> {{ field.label }} </td>
<td> {{ field.data }} </td>
<td> {{ field()}} </td>
</tr>
{% endif %}
{% endfor %}
</table>
<div class="form-group">
{{ form.name.label }}
{{ form.name(class="form-control",style="width:500px") }}
</div>
<div class="form-group">
{{ form.info.label }}
{{ form.info(class="form-control",style="width:500px") }}
</div>
<div class="form-group">
{{ form.password.label }}
{{ form.password(class="form-control",style="width:500px") }}
</div>
<div class="row">
<button type="submit" class="btn btn-primary"> Submit </button>
</div>
</form>
</div>
</div>
{% else %}
<p> No form provided...not sure how this happened. </p>
{% endif %}
</div>
<br>
{% endblock %}
25 changes: 25 additions & 0 deletions minard/templates/standard_runs.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{% extends "layout.html" %}
{% block title %}Standard Runs{% endblock %}
{% block head %}
{{ super() }}
{% endblock %}
{% block body %}
{{ super() }}
<div class="container">
{% if values %}
<h1> Standard Runs </h1>
<ul class="list-group">
{% for k, v in values %}
<li class="list-group-item">
<h4>{{ k }}</h4>
<div class="list-group">
{% for x in v %}
<a class="list-group-item" href="{{url_for('standard_runs') }}/{{x.id}}" >{{ x.key[2] }} </a>
{% endfor %}
</div>
</li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endblock %}
68 changes: 68 additions & 0 deletions minard/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import dropout
import pmtnoisedb
import gain_monitor
import standard_runs as sr
from run_list import golden_run_list
from .polling import polling_runs, polling_info, polling_info_card, polling_check, polling_history, polling_summary
from .channeldb import ChannelStatusForm, upload_channel_status, get_channels, get_channel_status, get_channel_status_form, get_channel_history, get_pmt_info, get_nominal_settings, get_most_recent_polling_info, get_discriminator_threshold, get_all_thresholds, get_maxed_thresholds, get_gtvalid_lengths, get_pmt_types, pmt_type_description, get_fec_db_history
Expand Down Expand Up @@ -191,6 +192,7 @@ def calculate_resistors():
return render_template('calculate_resistors.html', crate=crate, slot=slot, resistors=resistors)

@app.route('/detector-state-check')
@app.route('/detector-state-check/')
@app.route('/detector-state-check/<int:run>')
def detector_state_check(run=None):
if run is None:
Expand Down Expand Up @@ -433,6 +435,7 @@ def detector_state_diff():
detector_state2=detector_state2)

@app.route('/state')
@app.route('/state/')
@app.route('/state/<int:run>')
def state(run=None):
try:
Expand Down Expand Up @@ -1576,3 +1579,68 @@ def _dropout_detail_n100(run_number):
@app.route("/dropout/_dropout_detail/N20/<int:run_number>")
def _dropout_detail_n20(run_number):
return dropout.get_details(run_number, 2)

@app.route('/standard_runs/<uuid>', methods=['GET', 'POST'])
@app.route("/standard_runs/")
@app.route("/standard_runs")
def standard_runs(uuid=None):
if uuid is None:
values = None
try:
values = sr.get_standard_runs()
except sr.couchdb.http.socket.error:
flash("Error connecting to database, could not retrieve standard runs", "danger")
return render_template('standard_runs.html', values=values)

if request.method =='POST':
Form = sr.create_form(request.form)
form = Form(request.form)
if form.validate():
# Change form to a dict
new_values = {}
for field in form:
new_values[field.name] = field.data

try:
new_uuid = sr.update_standard_run(uuid, new_values)
flash("Updated standard run", "success")
uuid = new_uuid
except sr.couchdb.http.socket.error:
flash("Error connecting to database, standard run not updated", "danger")
except RuntimeError as e:
flash("Did not update standard run: %s" % str(e), "danger")
return redirect(url_for("standard_runs",uuid=uuid))

else:
flash("Did not update standard run, new settings are not valid", "danger")
return render_template("standard_run.html", form=form, uuid=uuid)

error_string = "Requested standard run does not exist"
try:
sr_info = sr.get_standard_run(uuid)
except sr.couchdb.http.socket.error:
sr_info = None
error_string = "Error connecting to database"

if sr_info is None:
flash(error_string, "danger")
return render_template("standard_run.html")

# Remove values that shouldn't be changed or are just noise
sr_info.pop("version", None)
sr_info.pop("timestamp", None)
sr_info.pop("time_stamp", None)
sr_info.pop("_rev", None)
sr_info.pop("_id", None)
sr_info.pop("type", None)
sr_info.pop("Comments", None)
comments = sr_info.pop("info", None)
updater = sr_info.pop("name", None)


Form = sr.create_form(sr_info)
form = Form()
for k, v in sr_info.iteritems():
form[k].data = v
return render_template("standard_run.html", form=form, uuid=uuid,
comments=comments, updater=updater)