Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a034a2c
starting a flask web-app that should serve as a regular checklist for…
renereimann Jun 2, 2025
6276b41
install py_elog in dockerfile, add a utility file that contains a fun…
renereimann Jul 15, 2025
afa53fd
we have the py_elog cloned in here but since that is not part of the …
renereimann Jul 15, 2025
2466df9
adding the git clone to Dockerfile
renereimann Jul 15, 2025
bc5ea6c
add that we can overwrite an elog entry, we will use that for testing…
renereimann Jul 16, 2025
141ca06
make a submit button for the form and make flask handle the form result.
renereimann Jul 16, 2025
8f43c27
some nice formating and adding comments for explanations
renereimann Jul 16, 2025
fa28cf8
now checkboxes are either evaluated as true or false and result is se…
renereimann Aug 15, 2025
59539f8
update to recent dripline version
renereimann Oct 6, 2025
f3ea95c
adding lists for individual checks
renereimann Jan 23, 2026
1e25baf
Merged <develop> to <feature/flask>
renereimann Jan 23, 2026
d8eaf14
Merging <develop> in <feature/flask_app>
renereimann Jan 26, 2026
248d66d
make password an optional parameter
renereimann Feb 3, 2026
3d64191
now sending report to elog. Therefor we import the send_to_elog funct…
renereimann Feb 3, 2026
cdb521d
fixing user authentication, had hardcoded name before, now use from c…
renereimann Feb 26, 2026
f92e627
changing name of script to make it more meaningful
renereimann Feb 26, 2026
ea20c47
checklist are now generated from config file
renereimann Mar 2, 2026
3fad0b6
remove all the hard coded checklist functions that are now handled by…
renereimann Mar 2, 2026
6044e55
Merge branch 'develop' into feature/flask_app
renereimann Mar 5, 2026
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
py_elog/

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
7 changes: 7 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@ ARG img_user=ghcr.io/driplineorg
ARG img_repo=dripline-python
ARG img_tag=v5.1.5

ARG UID=5001
ARG GID=5001

FROM ${img_user}/${img_repo}:${img_tag}

COPY . /usr/local/src_dragonfly

WORKDIR /usr/local/src_dragonfly
RUN pip install docker pymodbus
RUN pip install .
RUN pip install Flask
RUN git clone https://github.com/paulscherrerinstitute/py_elog.git /usr/local/py_elog
RUN pip install /usr/local/py_elog

WORKDIR /

2 changes: 1 addition & 1 deletion dragonfly/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def __get_version():
version.parse(pkg_resources.get_distribution('dragonfly').version)
version.package = 'project8/dragonfly'
version.commit = 'na'
dragonfly.core.add_version('dragonfly', version)
#dragonfly.core.add_version('dragonfly', version)
return version
version = __get_version()
__version__ = version.version
Expand Down
103 changes: 103 additions & 0 deletions dragonfly/checklist_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
from flask import Flask, redirect, request, render_template
from datetime import datetime
from dragonfly.utility import send_to_elog
import yaml

with open("/root/checklist.yaml", "r") as open_file:
config = yaml.safe_load( open_file.read() )

app = Flask(__name__)

@app.route('/')
def select_list():
content = ""
content += "<h1> Please select a Checklist </h1>"
for checklist in config["checklists"]:
label = checklist["name"]
url_name = label.replace(" ", "_")
content += f'<a href="checklist/{url_name}"> {label} </a> <br>'
return content

@app.route('/checklist/<checklist_name>')
def show_checklist(checklist_name):
checklist = None
for c in config["checklists"]:
if checklist_name == c["name"].replace(" ", "_"):
checklist = c
if checklist is None:
return f'Checklist for {checklist_name} not found. Go back to <a href="/"> start page</a>'

content = """
<h1> {title} </h1>
<p> <form id="checklist" action="/submit/{checklist_name}" method="post">
<input type="hidden" id="checklist_name" name="checklist_name" value="{title}" />
User: <input type="text" id="user" name="user"> <br> <br>
{questions}
<button type="submit" id="submit_button"> Submit Checklist </button>
</form> </p>

<script>
const form = document.getElementById('checklist');
const button = document.getElementById('submit_button');
{checkbox_script}
</script>
"""
title = checklist['name']
questions = ""
checkbox_script = ""

for i, question in enumerate(checklist["questions"]):
if question["type"] == "text":
if "label_checklist_pre" in question.keys():
questions += f'{question["label_checklist_pre"]}: '
questions += f'<input type"text" id="question_{i}" name="question_{i}">'
if "label_checklist_post" in question.keys():
questions += f'<label for"question_{i}"> {question["label_checklist_post"]} </label>'
elif question["type"] == "checkbox":
label = f"{i}"
questions += f'<input type="hidden" id="hidden_{i}" name="hidden_{i}"> <input type="checkbox" id="question_{i}"> '
if "label_checklist" in question.keys():
questions += f'<label for="question_{i}"> {question["label_checklist"]} </label>'
checkbox_script += f"""const checkbox_{i} = document.getElementById("question_{i}");
const hidden_{i} = document.getElementById("hidden_{i}"); """
checkbox_script += "button.addEventListener('click', () => { hidden_%d.value = checkbox_%d.checked ? 'True' : 'False'; }); "%(i,i)
if "link_page" in question.keys():
questions += f'<a target="_blank" rel="noopener noreferrer" href="{question["link_page"]}">Check on this page.</a>'
questions += '<br><br>'
return content.format(title=title, checklist_name=checklist_name, questions=questions, checkbox_script=checkbox_script)


@app.route('/submit/<checklist_name>', methods=["GET",'POST'])
def submit_checklist(checklist_name):
# This function is called with the result of the check list.
# Use the request.form content to generate a nice ELOG message
# That elog message will be posted to the elog

checklist = None
for c in config["checklists"]:
if checklist_name == c["name"].replace(" ", "_"):
checklist = c
if checklist is None:
return f'Checklist for {checklist_name} not found. Go back to <a href="/"> start page</a>'


report = ""
try:
report += f"Timestamp: %s\n"%(datetime.now().strftime("%d/%m/%Y %H:%M:%S"))
report += f"User: {request.form['user']}\n"
for i, question in enumerate(checklist["questions"]):
if question["type"] == "text":
report = report + question["label_elog"] + ": " + request.form.get(f"question_{i}") + "\n"
elif question["type"] == "checkbox":
if request.form.get(f"hidden_{i}") == 'True':
report += f"[x] {question['label_elog']}\n"
else:
report += f"[ ] {question['label_elog']}\n"
except Exception as e:
print(e, flush=True)

print(report, flush=True)
send_to_elog(report, subject=checklist_name, author=config["username"], category="Slow controls", msg_id=None, password=config["password"])

# Tell the user that checklist is done.
return '<h1> Checklist successfully submitted. You can go back to the <a href="/"> start page </a>. </h1>'
12 changes: 12 additions & 0 deletions dragonfly/utility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import elog
from getpass import getpass


def send_to_elog(message, subject="Checklist", author="Rene Reimann", category="Slow Controls", msg_id=None, password=None):
if password is None:
password = getpass()
logbook = elog.open("https://maxwell.npl.washington.edu/elog/project8/P8+Mainz+lab", user=author, password=password)
if msg_id is None:
return logbook.post(message, subject=subject, Author=author, category=category, suppress_email_notification=True)
else:
return logbook.post(message, msg_id=msg_id, subject=subject, Author=author, category=category, suppress_email_notification=True)