Skip to content

Commit

Permalink
Merge pull request #3 from ac-rad/prepare-release
Browse files Browse the repository at this point in the history
Prepare for release
  • Loading branch information
SebA-R committed Jul 17, 2023
2 parents ff8224e + d981544 commit 47492b5
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 105 deletions.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# CLAIRify Web Interface
This directory contains the source code for CLAIRify Web interface

## Dependencies
- [OpenAI Python Library](https://pypi.org/project/openai/) 0.27.8
- [Flask](https://flask.palletsprojects.com/) 2.2.2
- [Flask SocketIO](https://flask-socketio.readthedocs.io/) 5.3.4

You can install dependencies by

```bash
pip install -r requirements.txt
```

## Getting Started
1. Set `OPENAI_API_KEY` environment variable. (cf. [Best Practices for API Key Safety](https://help.openai.com/en/articles/5112595-best-practices-for-api-key-safety))
2. Start the server
```bash
python main.py
```
3. Open `http://127.0.0.1:3000/` in your favorite browser.

## Usage
![](/images/screenshot.png)

1. Type your experiment in the left column.
2. Click "Translate" button.
3. You will see an execution log while translation
4. Final output XDL will be automatically shown after translation is done.
Binary file added images/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 20 additions & 13 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import json
import os
from threading import Lock, Thread

import openai
from flask import Flask, render_template, request
from flask_socketio import SocketIO, emit
import os
from threading import Thread, Lock

from verify import verify_xdl
import json
import openai


def prompt(instructions, description, max_tokens):
Expand All @@ -20,16 +22,17 @@ def prompt(instructions, description, max_tokens):
)
return response["choices"][0]["text"]


def translate(input_xdl):
"""Function that translates the input XDL"""

# Get API key
with open("static/config.json", "r") as f:
# Get API key
with open("config.json", "r") as f:
openai.api_key = json.load(f)["OPENAI_API_KEY"]

# Get XDL description
with open("XDL_description.txt", "r") as f:
XDL_description = f.read()
XDL_description = f.read()

correct_syntax = False
errors = {}
Expand All @@ -41,7 +44,8 @@ def translate(input_xdl):
try:
gpt3_output = prompt(input_xdl, XDL_description, 1000)
except:
socketio.emit("message", "Error. Too many tokens required or invalid API key.")
socketio.emit(
"message", "Error. Too many tokens required or invalid API key.")
break

socketio.emit("message", "gpt3_output:::")
Expand All @@ -58,15 +62,15 @@ def translate(input_xdl):
if not compile_correct:
correct_syntax = True
break
else:
else:
error_list = set()
for item in compile_correct:
for error in item["errors"]:
error_list.add(error)
error_message = f"\n{gpt3_output}\nThis XDL was not correct. These were the errors\n{os.linesep.join(list(error_list))}\nPlease fix the errors."
input_xdl = f"{prev_input_xdl} {error_message}"

else:
else:
error_message = f"\n{gpt3_output}\nThis XDL was not correct. XDL should start with <XDL>. Please fix the errors."
input_xdl = f"{prev_input_xdl} {error_message}"

Expand All @@ -75,7 +79,7 @@ def translate(input_xdl):
xdl = gpt3_output
else:
xdl = "The correct XDL could not be generated."

except Exception as e:
socketio.emit("message", f"Error: {e}")

Expand All @@ -95,21 +99,24 @@ def translate(input_xdl):
thread_lock = Lock()
input_xdl = ""


def run_translation(input_xdl):
"""Function that runs the translation in a separate thread."""
global thread
with thread_lock:
thread = Thread(target=translate, args=(input_xdl,))
thread.start()


@app.route("/", methods=["GET", "POST"])
def index():
"""Function that renders the index page."""
global input_xdl
if request.method == "POST":
if request.method == "POST":
input_xdl = request.form["input_field"]
run_translation(input_xdl)

return render_template("index.html", input_xdl=input_xdl)

socketio.run(app, debug=True, host="0.0.0.0", port=3000)

socketio.run(app, port=3000)
22 changes: 0 additions & 22 deletions static/interact.js

This file was deleted.

17 changes: 1 addition & 16 deletions static/myscript.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,3 @@
async function getConfig() {
try {
const response = await fetch('static/config.json');
const data = await response.json();
const openai_api_key = data["OPENAI_API_KEY"];
if (openai_api_key == "") {
alert("Set up your OpenAI API key in config.json");
}
} catch (error) {
console.error('Error with config.json:', error);
}
}

getConfig();

const button = document.getElementById("submit_button");
const tab2 = document.getElementById("secondaryOpen");
if (button.value == "Translate") {
Expand Down Expand Up @@ -40,4 +25,4 @@ if (window.innerWidth < 750) {
tabContainer.insertBefore(formContainer, tabContainer.children[1]);
} else {
formContainer.className = "";
}
}
27 changes: 27 additions & 0 deletions static/prism/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## LICENSE

This directory contains a part of [prism](https://github.com/PrismJS/prism) released under the following license.

```
MIT LICENSE
Copyright (c) 2012 Lea Verou
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
```
4 changes: 1 addition & 3 deletions static/socketio.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ convertHTMLString = (msg) => {
}

const createMessage = (msg) => {
console.log(msg)

const content = `
<div class="text">
<span style="font-size: 0.7rem;">
Expand Down Expand Up @@ -63,4 +61,4 @@ socketio.on("correct_xdl", (msg) => {
button.value = "Translate";
button.disabled = false;
}
});
});
19 changes: 0 additions & 19 deletions static/tabs.js

This file was deleted.

12 changes: 0 additions & 12 deletions templates/device.html

This file was deleted.

2 changes: 1 addition & 1 deletion templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,6 @@ <h1>CLAIRify</h1>
<script src="static/myscript.js" type="text/javascript" charset="utf-8"></script>
<script src="static/tab.js" type="text/javascript" charset="utf-8"></script>
<script src="static/socketio.js" type="text/javascript" charset="utf-8"></script>
<script src="static\speechtotext.js" type="text/javascript" charset="utf-8"></script>
<script src="static/speechtotext.js" type="text/javascript" charset="utf-8"></script>
</body>
</html>
51 changes: 32 additions & 19 deletions verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,16 @@
'RunColumn': ['from_vessel', 'to_vessel', 'column'],
}

reagent_properties = ["name", "inchi", "cas", "role", "preserve", "use_for_cleaning", "clean_with", "stir", "temp", "atmosphere", "purity"]
reagent_properties = ["name", "inchi", "cas", "role", "preserve",
"use_for_cleaning", "clean_with", "stir", "temp", "atmosphere", "purity"]


def parse_hardware(root, error_list, available_hardware):
hardware_list = []
tag_lst = list(root.iter('Hardware'))
tags = []
strs=[]
error=""
strs = []
error = ""
for item in tag_lst:
tags += [elem.tag for elem in item.iter()]
strs += [ET.tostring(item, encoding='unicode', method='xml').strip()]
Expand All @@ -83,8 +84,10 @@ def parse_hardware(root, error_list, available_hardware):
if component.attrib['id'] not in available_hardware:
wrong_hardware = component.attrib['id']
error_str = f"{wrong_hardware} is not defined in the given Hardware list. The available Hardware is: {', '.join(available_hardware)[:-2]}."
step_str = ET.tostring(component, encoding='unicode', method='xml').strip()
error_list.append({"step": "Hardware definition", "errors": [error_str]})
step_str = ET.tostring(
component, encoding='unicode', method='xml').strip()
error_list.append(
{"step": "Hardware definition", "errors": [error_str]})
hardware_list.append(component.attrib['id'])
return hardware_list, error_list, (error, strs)

Expand All @@ -97,20 +100,24 @@ def parse_reagents(root, error_list, available_reagents):
if reagent.attrib['name'] not in available_reagents:
wrong_reagent = reagent.attrib['name']
error_str = f"{wrong_reagent} is not defined in the given Reagents list. The available reagents are: {', '.join(available_reagents)[:-2]}."
error_list.append({"step": "Reagents definition", "errors": [error_str]})
error_list.append(
{"step": "Reagents definition", "errors": [error_str]})
errors = []
if 'name' not in reagent.attrib:
errors.append(f"You must have 'name' property in Reagent")
else:
reagent_list.append(reagent.attrib['name'])
for attr in reagent.attrib:
if attr not in reagent_properties:
errors.append(f"The {attr} property in Reagent is not allowed")
errors.append(
f"The {attr} property in Reagent is not allowed")
if errors:
step_str = ET.tostring(reagent, encoding='unicode', method='xml').strip()
step_str = ET.tostring(
reagent, encoding='unicode', method='xml').strip()
error_list.append({"step": step_str, "errors": errors})
return reagent_list


def verify_procedure(root, hardware, reagents, error_list):
for procedure in root.iter('Procedure'):
for step in procedure:
Expand All @@ -126,11 +133,12 @@ def verify_procedure(root, hardware, reagents, error_list):
f"You must have '{prop}' property when doing '{step.tag}'")
for attr in step.attrib:
if attr not in optional_properties[action]:
allowed_actions = list(set(optional_properties[action] + mandatory_properties[action]))
allowed_actions = list(
set(optional_properties[action] + mandatory_properties[action]))
errors.append(
f"The {attr} property in the {action} procedure is not allowed. The allowed properties are: {', '.join(allowed_actions)}.")
f"The {attr} property in the {action} procedure is not allowed. The allowed properties are: {', '.join(allowed_actions)}.")
# Check vessels are defined in Hardware
#print(error_list)
# print(error_list)
if len(error_list) == 0 or "Hardware" not in error_list[0]["step"].lower():
for attr in ['vessel', 'from_vessel', 'to_vessel']:
if attr in step.attrib and step.attrib[attr] not in hardware:
Expand All @@ -144,33 +152,38 @@ def verify_procedure(root, hardware, reagents, error_list):
# Check if there is any text content between tags
for elem in step.iter():
if elem.text and elem.text.strip() and elem != step:
errors.append("There should be no text content between tags.")
errors.append(
"There should be no text content between tags.")

if errors:
step_str = ET.tostring(step, encoding='unicode', method='xml').strip()
step_str = ET.tostring(
step, encoding='unicode', method='xml').strip()
step_str = ' '.join(step_str.split())
error_list.append({"step": step_str, "errors": errors})
return error_list



def verify_synthesis(root, available_hardware, available_reagents):
error_list = []
for element in root.iter():
if element.text and element.text.strip():
errors = [f"Tags should not have text content: '{element.text.strip()}'"]
step_str = ET.tostring(element, encoding='unicode', method='xml').strip()
errors = [
f"Tags should not have text content: '{element.text.strip()}'"]
step_str = ET.tostring(
element, encoding='unicode', method='xml').strip()
error_list.append({"step": step_str, "errors": errors})

hardware, hardware_list_error_list, (errors, strs) = parse_hardware(root, error_list, available_hardware)
hardware, hardware_list_error_list, (errors, strs) = parse_hardware(
root, error_list, available_hardware)
if errors != "":
error_list.append({"step": "Hardware definition", "errors": [errors]})

#return error_list
#return [{"step": "Hardware definition", "errors": errors}]
# return error_list
# return [{"step": "Hardware definition", "errors": errors}]
reagents = parse_reagents(root, error_list, available_reagents)
return verify_procedure(root, hardware, reagents, error_list)


def verify_xdl(xdl, available_hardware=None, available_reagents=None):
"""
Verify XDL and return errors
Expand Down

0 comments on commit 47492b5

Please sign in to comment.