From a034a2c9085c0025fbf599fd18c9db1c22c2596d Mon Sep 17 00:00:00 2001 From: Rene Reimann Date: Mon, 2 Jun 2025 16:26:30 +0200 Subject: [PATCH 01/16] starting a flask web-app that should serve as a regular checklist for the MATS setup --- Dockerfile | 5 +++++ dragonfly/hello.py | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 dragonfly/hello.py diff --git a/Dockerfile b/Dockerfile index 2287f91b..c7fc426d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,11 +2,16 @@ ARG img_user=ghcr.io/driplineorg ARG img_repo=dripline-python ARG img_tag=develop-dev +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 . +RUN pip install Flask WORKDIR / + diff --git a/dragonfly/hello.py b/dragonfly/hello.py new file mode 100644 index 00000000..7f477a89 --- /dev/null +++ b/dragonfly/hello.py @@ -0,0 +1,20 @@ +"""test Flask with this""" + +from flask import Flask +app = Flask(__name__) + +@app.route('/') +def hello(): + content = """ + Hello World! + +
+ +
+ +
+ + +
+ """ + return content From 6276b41580a357d5f2c6b4c0bcf98e0f6d5fdd41 Mon Sep 17 00:00:00 2001 From: Rene Reimann Date: Tue, 15 Jul 2025 20:13:55 +0200 Subject: [PATCH 02/16] install py_elog in dockerfile, add a utility file that contains a function to send messages to elog --- Dockerfile | 1 + dragonfly/__init__.py | 2 +- dragonfly/utility.py | 8 ++++++++ 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 dragonfly/utility.py diff --git a/Dockerfile b/Dockerfile index c7fc426d..3b84354f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,7 @@ COPY . /usr/local/src_dragonfly WORKDIR /usr/local/src_dragonfly RUN pip install . RUN pip install Flask +RUN pip install ./py_elog WORKDIR / diff --git a/dragonfly/__init__.py b/dragonfly/__init__.py index ae9daaf1..7c36140a 100644 --- a/dragonfly/__init__.py +++ b/dragonfly/__init__.py @@ -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 diff --git a/dragonfly/utility.py b/dragonfly/utility.py new file mode 100644 index 00000000..28a3dbd6 --- /dev/null +++ b/dragonfly/utility.py @@ -0,0 +1,8 @@ +import elog +from getpass import getpass + + +def send_to_elog(message, subject="Checklist", author="Rene Reimann", category="Slow Controls"): + password = getpass() + logbook = elog.open("https://maxwell.npl.washington.edu/elog/project8/P8+Mainz+lab", user="rreimann", password=password) + return logbook.post(message, subject=subject, Author=author, category=category, suppress_email_notification=True) From afa53fdf043ee27f8f1399a28e736e61f8114549 Mon Sep 17 00:00:00 2001 From: Rene Reimann Date: Tue, 15 Jul 2025 20:15:03 +0200 Subject: [PATCH 03/16] we have the py_elog cloned in here but since that is not part of the repository but its own we ignore it in git --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 68bc17f9..5908fb0c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +py_elog/ + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] From 2466df925fc17ef568582d7e68430ceec6216dfe Mon Sep 17 00:00:00 2001 From: Rene Reimann Date: Tue, 15 Jul 2025 22:12:09 +0200 Subject: [PATCH 04/16] adding the git clone to Dockerfile --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 3b84354f..dfd5d96e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,8 @@ COPY . /usr/local/src_dragonfly WORKDIR /usr/local/src_dragonfly RUN pip install . RUN pip install Flask -RUN pip install ./py_elog +RUN git clone https://github.com/paulscherrerinstitute/py_elog.git /usr/local/py_elog +RUN pip install /usr/local/py_elog WORKDIR / From bc5ea6c39ab01ced01c805dcc7f02e7c7f8823bb Mon Sep 17 00:00:00 2001 From: Rene Reimann Date: Wed, 16 Jul 2025 11:30:16 +0200 Subject: [PATCH 05/16] add that we can overwrite an elog entry, we will use that for testing to not swarm the elog --- dragonfly/utility.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dragonfly/utility.py b/dragonfly/utility.py index 28a3dbd6..880e2fb0 100644 --- a/dragonfly/utility.py +++ b/dragonfly/utility.py @@ -2,7 +2,10 @@ from getpass import getpass -def send_to_elog(message, subject="Checklist", author="Rene Reimann", category="Slow Controls"): +def send_to_elog(message, subject="Checklist", author="Rene Reimann", category="Slow Controls", msg_id=None): password = getpass() logbook = elog.open("https://maxwell.npl.washington.edu/elog/project8/P8+Mainz+lab", user="rreimann", password=password) - return logbook.post(message, subject=subject, Author=author, category=category, suppress_email_notification=True) + 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) From 141ca06e913dc97882ec8205330acd2c7570889c Mon Sep 17 00:00:00 2001 From: Rene Reimann Date: Wed, 16 Jul 2025 11:32:07 +0200 Subject: [PATCH 06/16] make a submit button for the form and make flask handle the form result. --- dragonfly/hello.py | 61 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/dragonfly/hello.py b/dragonfly/hello.py index 7f477a89..a2ddd0cd 100644 --- a/dragonfly/hello.py +++ b/dragonfly/hello.py @@ -1,20 +1,57 @@ """test Flask with this""" -from flask import Flask +from flask import Flask, redirect, request app = Flask(__name__) +Slowdash_Pressures_URL = ('http://astro-wake.physik.uni-mainz.de:18881/slowplot.html?config=slowplot-Pressures.json') + @app.route('/') def hello(): + linkBakeout = 'Baking out? Click me!
' + linkExp = 'Running an experiment? Click me!' + content = """ - Hello World! - -
- -
- -
- - -
+

Hello there!

+

You are in a regular day (no Baking out, no experimenting) or else, click on the links at the end of the page

+

    + +Go on this page: CoolingLoop Sensor
    + +
    + Go on this page: BC Thermocouples check all the temperatures
    + +
    + Go on this page: MainzAtomicTestStandPage
    + + +
    + +
    + + + + +

""" - return content + return content + linkBakeout +linkExp + + +@app.route('/handle_data', methods=['POST']) +def handle_data(): + print(request.form, flush=True) + return "

Thanks for filling out the check list. You are done for today.

" + + +@app.route('/Bakeout') #sends you on another one of the flask app page named Bakeout +def foobar(): + return '

Baking out? No problem!

' + +@app.route('/test') +def test2(): + return {'url': 'http://astro-wake.physik.uni-mainz.de:18881/slowplot.html?config=slowplot-Pressures.json'} + +@app.route('/Experimenting') +def foobar2(): + return '

Running an experiment? No problem!

' + + From 8f43c27e9bbe1bc6b8837b1cdf380135aa9d3c70 Mon Sep 17 00:00:00 2001 From: Rene Reimann Date: Wed, 16 Jul 2025 12:31:13 +0200 Subject: [PATCH 07/16] some nice formating and adding comments for explanations --- dragonfly/hello.py | 50 +++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/dragonfly/hello.py b/dragonfly/hello.py index a2ddd0cd..e178fec9 100644 --- a/dragonfly/hello.py +++ b/dragonfly/hello.py @@ -11,34 +11,42 @@ def hello(): linkExp = 'Running an experiment? Click me!' content = """ -

Hello there!

-

You are in a regular day (no Baking out, no experimenting) or else, click on the links at the end of the page

-

    - -Go on this page: CoolingLoop Sensor
    - -
    - Go on this page: BC Thermocouples check all the temperatures
    - -
    - Go on this page: MainzAtomicTestStandPage
    - - -
    - -
    - - - - -

+

Hello there!

+

You are in a regular day (no Baking out, no experimenting) or else, click on the links at the end of the page

+

    +

    + Go on this page: CoolingLoop Sensor
    + +
    + Go on this page: Brainbox Thermocouples + check all the temperatures
    + +
    + Go on this page: MainzAtomicTestStandPage
    + +
    + + Go on this page: Pressures
    + +
    + +
    + +
    +

""" return content + linkBakeout +linkExp @app.route('/handle_data', methods=['POST']) def handle_data(): + # 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 print(request.form, flush=True) + + # Tell the user that checklist is done. return "

Thanks for filling out the check list. You are done for today.

" From fa28cf8a877b53d8ef7787a324293d3ab1e77555 Mon Sep 17 00:00:00 2001 From: Rene Reimann Date: Fri, 15 Aug 2025 19:34:49 +0200 Subject: [PATCH 08/16] now checkboxes are either evaluated as true or false and result is send with the form. The form result is analyzed to create an elog entry --- dragonfly/hello.py | 93 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 70 insertions(+), 23 deletions(-) diff --git a/dragonfly/hello.py b/dragonfly/hello.py index e178fec9..3e1a60d5 100644 --- a/dragonfly/hello.py +++ b/dragonfly/hello.py @@ -1,50 +1,97 @@ """test Flask with this""" -from flask import Flask, redirect, request +from flask import Flask, redirect, request, render_template +from datetime import datetime app = Flask(__name__) Slowdash_Pressures_URL = ('http://astro-wake.physik.uni-mainz.de:18881/slowplot.html?config=slowplot-Pressures.json') + + + + + +listOfChecks = ["check1", "check2","check3","check4","check5"]#number of checks +Description = [' Go on this page: CoolingLoop Sensor
',' Go on this page: Brainbox Thermocouples check all the temperatures
','Go on this page: MainzAtomicTestStandPage
','Go on this page: Pressures
','' ] #what is displayed in the checklist's web page before each checkbox, such as a link for example +Labels = ['Is the Cooling Loop Sensor Ok? ie is there any flow',' Are the thermocouples temperature between 18C and 25C?',' Check if there is no red attention sign.','Check all the pressures','Go in the lab. Check if there is no weird sound '] # what is displayed in front of the checkbox +ResponseInElog = ["Cooling Loop Sensor","Brainbox thermocouples temperatures", "Red attention signs in Mainz Atomic Test Stand Page","Pressure", "Lab sounds"] #what will be written in the created eLog + @app.route('/') def hello(): linkBakeout = 'Baking out? Click me!
' linkExp = 'Running an experiment? Click me!' + now = datetime.now() # current date and time + date_time = now.strftime("%d/%m/%Y, %H:%M:%S") + + content = """

Hello there!

-

You are in a regular day (no Baking out, no experimenting) or else, click on the links at the end of the page

+

You are in a regular day (no Baking, no experimenting). If you are not, please click on the links at the end of the page.

+

    -

    - Go on this page: CoolingLoop Sensor
    - -
    - Go on this page: Brainbox Thermocouples - check all the temperatures
    - -
    - Go on this page: MainzAtomicTestStandPage
    - -
    - - Go on this page: Pressures
    - -
    - -
    - + +
    + + """ + + for check,text, label in zip(listOfChecks, Description, Labels): + content += text + content += f""" + + +
    + """ + + + content += """ +

+ + #in order to give the checkox a value if it was not checked + + """ + + return date_time +content + linkBakeout +linkExp -@app.route('/handle_data', methods=['POST']) +@app.route('/handle_data', methods=["GET",'POST']) def handle_data(): # 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 + + now = datetime.now() # current date and time + date_time = now.strftime("%d/%m/%Y, %H:%M:%S") + + print("Date and time is :"+ date_time, flush=True) print(request.form, flush=True) + print("The one writing is : ",request.form["UserName"],flush=True) + + try : + for check,response in zip(listOfChecks,ResponseInElog): + if request.form.get(check+"_hidden")== 'True' : + print("Checked", response ,"[x]",flush=True) + else: + print("Checked",response, "[ ]", flush=True) + + except Exception as e: + print(e) + # print(dir(request.form),flush=True) # Tell the user that checklist is done. return "

Thanks for filling out the check list. You are done for today.

" From 59539f8620377c795cf161172f6e5faaeda5c4c2 Mon Sep 17 00:00:00 2001 From: Rene Reimann Date: Mon, 6 Oct 2025 15:09:17 +0200 Subject: [PATCH 09/16] update to recent dripline version --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index dfd5d96e..c96ad44e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ ARG img_user=ghcr.io/driplineorg ARG img_repo=dripline-python -ARG img_tag=develop-dev +ARG img_tag=v5.1.0 ARG UID=5001 ARG GID=5001 From f3ea95ce890820b2f8b68069a496e1dd033c5f59 Mon Sep 17 00:00:00 2001 From: Rene Reimann Date: Fri, 23 Jan 2026 09:21:19 +0100 Subject: [PATCH 10/16] adding lists for individual checks --- dragonfly/hello.py | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/dragonfly/hello.py b/dragonfly/hello.py index 3e1a60d5..be7a15eb 100644 --- a/dragonfly/hello.py +++ b/dragonfly/hello.py @@ -10,11 +10,13 @@ - listOfChecks = ["check1", "check2","check3","check4","check5"]#number of checks -Description = [' Go on this page: CoolingLoop Sensor
',' Go on this page: Brainbox Thermocouples check all the temperatures
','Go on this page: MainzAtomicTestStandPage
','Go on this page: Pressures
','' ] #what is displayed in the checklist's web page before each checkbox, such as a link for example -Labels = ['Is the Cooling Loop Sensor Ok? ie is there any flow',' Are the thermocouples temperature between 18C and 25C?',' Check if there is no red attention sign.','Check all the pressures','Go in the lab. Check if there is no weird sound '] # what is displayed in front of the checkbox -ResponseInElog = ["Cooling Loop Sensor","Brainbox thermocouples temperatures", "Red attention signs in Mainz Atomic Test Stand Page","Pressure", "Lab sounds"] #what will be written in the created eLog +Description = [' Go on this page: CoolingLoop Sensor
',' Go on this page: Brainbox Thermocouples check all the temperatures
','Go on this page: MainzAtomicTestStandPage
','' ,'Go on this page: Slowdash Pressures
'] #what is displayed in the checklist's web page before each checkbox, such as a link for example +Labels = ['Is the Cooling Loop Sensor Ok? ie is there any flow',' Are the thermocouples temperature between 18C and 25C?',' Check if there is no red attention sign.','Go in the lab. Check if there is no weird sound ','Check all the pressures'] # what is displayed in front of the checkbox +ResponseInElog = ["Cooling Loop Sensor","Brainbox thermocouples temperatures", "Red attention signs in Mainz Atomic Test Stand Page", "Lab sounds","Pressures"] #what will be written in the created eLog + +PressureGauges = ["PG80", "PG90"]#name of the Pressure Gauge +ValueOfPG = ["Should be between 1e-10 hPa and 2e-9 hPa", "Should be between 1e-10hPa and 2e-9 hPa"]# range of the value the corresponding PG should have @app.route('/') def hello(): @@ -31,8 +33,8 @@ def hello():

    -
    - + Who is filling the Checklist : +

    """ for check,text, label in zip(listOfChecks, Description, Labels): @@ -40,16 +42,22 @@ def hello(): content += f""" -
    +

    """ + for PG,labelOfPG in zip(PressureGauges,ValueOfPG,) : + content += PG + """ :""" + content += f""" + +
    + + """ content += """

- - #in order to give the checkox a value if it was not checked + + """ + 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'' + if "label_checklist_post" in question.keys(): + questions += f'' + elif question["type"] == "checkbox": + label = f"{i}" + questions += f' ' + if "label_checklist" in question.keys(): + questions += f'' + 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'Check on this page.' + questions += '

' + return content.format(title=title, checklist_name=checklist_name, questions=questions, checkbox_script=checkbox_script) + + +@app.route('/submit/', 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 start page' + + + 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 '

Checklist successfully submitted. You can go back to the start page .

' + +@app.route('/hello') def hello(): linkBakeout = 'Baking out? Click me!
' linkExp = 'Running an experiment? Click me!' From 3fad0b623e8990148baf31df43c1d8a5c08e1a48 Mon Sep 17 00:00:00 2001 From: Rene Reimann Date: Mon, 2 Mar 2026 22:09:41 +0100 Subject: [PATCH 16/16] remove all the hard coded checklist functions that are now handled by the config files generated ones --- dragonfly/checklist_server.py | 110 ---------------------------------- 1 file changed, 110 deletions(-) diff --git a/dragonfly/checklist_server.py b/dragonfly/checklist_server.py index f8399ff6..ae0d8751 100644 --- a/dragonfly/checklist_server.py +++ b/dragonfly/checklist_server.py @@ -8,15 +8,6 @@ app = Flask(__name__) -Slowdash_Pressures_URL = ('http://astro-wake.physik.uni-mainz.de:18881/slowplot.html?config=slowplot-Pressures.json') -listOfChecks = ["check1", "check2","check3","check4","check5"] -Description = [' Go on this page: CoolingLoop Sensor
',' Go on this page: Brainbox Thermocouples check all the temperatures
','Go on this page: MainzAtomicTestStandPage
','' ,'Go on this page: Slowdash Pressures
'] #what is displayed in the checklist's web page before each checkbox, such as a link for example -Labels = ['Is the Cooling Loop Sensor Ok? ie is there any flow',' Are the thermocouples temperature between 18C and 25C?',' Check if there is no red attention sign.','Go in the lab. Check if there is no weird sound ','Check all the pressures'] # what is displayed in front of the checkbox -ResponseInElog = ["Cooling Loop Sensor","Brainbox thermocouples temperatures", "Red attention signs in Mainz Atomic Test Stand Page", "Lab sounds","Pressures"] #what will be written in the created eLog - -PressureGauges = ["PG80", "PG90"]#name of the Pressure Gauge -ValueOfPG = ["Should be between 1e-10 hPa and 2e-9 hPa", "Should be between 1e-10hPa and 2e-9 hPa"]# range of the value the corresponding PG should have - @app.route('/') def select_list(): content = "" @@ -110,104 +101,3 @@ def submit_checklist(checklist_name): # Tell the user that checklist is done. return '

Checklist successfully submitted. You can go back to the start page .

' - -@app.route('/hello') -def hello(): - linkBakeout = 'Baking out? Click me!
' - linkExp = 'Running an experiment? Click me!' - - now = datetime.now() # current date and time - date_time = now.strftime("%d/%m/%Y, %H:%M:%S") - - - content = """ -

Hello there!

-

You are in a regular day (no Baking, no experimenting). If you are not, please click on the links at the end of the page.

- -

    -

    - Who is filling the Checklist : -

    - """ - - for check,text, label in zip(listOfChecks, Description, Labels): - content += text - content += f""" - - -

    - """ - - for PG,labelOfPG in zip(PressureGauges,ValueOfPG,) : - content += PG + """ :""" - content += f""" - -
    - - """ - - content += """ - -
    -

- - - """ - - return date_time +content + linkBakeout +linkExp - - -@app.route('/handle_data', methods=["GET",'POST']) -def handle_data(): - # 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 - - now = datetime.now() # current date and time - date_time = now.strftime("%d/%m/%Y, %H:%M:%S") - - report = "" - report += f"Date and time is: {date_time}\n" - report += f"The one writing is: {request.form['UserName']}\n" - try: - for check, response in zip(listOfChecks, ResponseInElog): - if request.form.get(f"{check}_hidden") == 'True': - report += f"Checked {response} [x]\n" - else: - report += f"Checked {response} [ ]\n" - - for namePG in PressureGauges: - report += f"The value of %s is: %s\n"%(namePG, request.form[f"{namePG}"]) - - except Exception as e: - print(e) - - send_to_elog(report, subject="Checklist", author=config["username"], category="Slow controls", msg_id=None, password=config["password"]) - - # Tell the user that checklist is done. - return "

Thanks for filling out the check list. You are done for today.

" - - -@app.route('/Bakeout') #sends you on another one of the flask app page named Bakeout -def foobar(): - return '

Baking out? No problem!

' - - -@app.route('/Experimenting') -def foobar2(): - return '

Running an experiment? No problem!

' - -