diff --git a/src/app.py b/src/app.py index 4a0cf79..67c4ef5 100644 --- a/src/app.py +++ b/src/app.py @@ -65,10 +65,11 @@ def enforce_https_in_production(): answer_logger = Logger() PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) -BENCHMARK_FILES = { - "near": os.path.join(PROJECT_ROOT, "semantic_benchmark_near_eng.csv"), - "far": os.path.join(PROJECT_ROOT, "semantic_benchmark_far_eng.csv"), -} +BENCHMARK_FILES = { + "near": os.path.join(PROJECT_ROOT, "semantic_benchmark_near_eng.csv"), + "far": os.path.join(PROJECT_ROOT, "semantic_benchmark_far_eng.csv"), +} +SUGGESTED_FORMULAS_PATH = os.path.join(os.path.dirname(__file__), "static", "ltl_formula_suggestions.json") def _load_benchmark_rows(): @@ -99,7 +100,30 @@ def _load_benchmark_rows(): return data -BENCHMARK_ROWS = _load_benchmark_rows() +BENCHMARK_ROWS = _load_benchmark_rows() + + +def _load_suggested_formulas(): + if not os.path.exists(SUGGESTED_FORMULAS_PATH): + return [] + + try: + with open(SUGGESTED_FORMULAS_PATH, "r", encoding="utf-8") as handle: + data = json.load(handle) + return [ + { + "formula": entry.get("formula", ""), + "description": entry.get("description", ""), + "source": entry.get("source", ""), + } + for entry in data + if entry.get("formula") + ] + except (json.JSONDecodeError, OSError): + return [] + + +SUGGESTED_FORMULAS = _load_suggested_formulas() def _highlight_differences(text_a, text_b): @@ -380,9 +404,9 @@ def ltltoeng_rating(): @app.route('/ltltoeng/ui', methods=['GET', 'POST']) @login_required -def ltl_to_english_ui(): - """Simple HTML interface to translate an LTL formula to English.""" - translation = None +def ltl_to_english_ui(): + """Simple HTML interface to translate an LTL formula to English.""" + translation = None error = None input_formula = "" @@ -400,10 +424,30 @@ def ltl_to_english_ui(): return render_template( 'ltltoengui.html', uid=getUserName(), - translation=translation, - error=error, - input_formula=input_formula, - ) + translation=translation, + error=error, + input_formula=input_formula, + ) + + +@app.route('/ltltoeng/suggestion', methods=['GET']) +@login_required +def ltl_suggestion(): + if not SUGGESTED_FORMULAS: + return jsonify({"error": "No suggestions available."}), 503 + + user_id = getUserName() + rated_formulas = answer_logger.getRatedEnglishFormulas(user_id) + available_suggestions = [ + s for s in SUGGESTED_FORMULAS + if s.get("formula") and s.get("formula") not in rated_formulas + ] + + if not available_suggestions: + return jsonify({"error": "No new suggestions available."}), 404 + + suggestion = random.choice(available_suggestions) + return jsonify(suggestion) @app.route('/authorquestion/', methods=['POST']) @login_required diff --git a/src/logger.py b/src/logger.py index 7098f6f..389e3a0 100644 --- a/src/logger.py +++ b/src/logger.py @@ -114,11 +114,22 @@ def __init__(self): if SENTENCE_PAIR_RATING_TABLE not in self.inspector.get_table_names(): Base.metadata.tables[SENTENCE_PAIR_RATING_TABLE].create(self.engine) - def record(self, log): - with self.Session() as session: - print("Recording log") - session.add(log) - session.commit() + def record(self, log): + with self.Session() as session: + print("Recording log") + session.add(log) + session.commit() + + def getRatedEnglishFormulas(self, user_id): + """Return a set of LTL formulas this user has already rated.""" + if not isinstance(user_id, str): + raise ValueError("user_id should be a string") + + with self.Session() as session: + rows = session.query(EnglishLTLRating.ltl).filter( + EnglishLTLRating.user_id == user_id + ).all() + return {row.ltl for row in rows if row.ltl} def logStudentResponse(self, userId, misconceptions, question_text, question_options, correct_answer, questiontype, mp_class, exercise, course): diff --git a/src/static/css/common.css b/src/static/css/common.css index 48ccb68..b77f61f 100644 --- a/src/static/css/common.css +++ b/src/static/css/common.css @@ -67,4 +67,28 @@ position: absolute; top: 0; right: 0; /* Align to the right side of the container */ +} + +.suggestion-btn { + background: linear-gradient(120deg, #1b7fbd, #0fb08a); + color: #fff; + border: none; + padding-left: 1rem; + padding-right: 1rem; + letter-spacing: 0.01em; + transition: transform 0.12s ease-in-out, box-shadow 0.12s ease-in-out; +} + +.suggestion-btn:hover, +.suggestion-btn:focus { + color: #fff; + transform: translateY(-1px); + box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); +} + +.suggestion-btn:disabled { + background: #9bbccd; + color: #f8f9fa; + transform: none; + box-shadow: none; } \ No newline at end of file diff --git a/src/static/ltl_formula_suggestions.json b/src/static/ltl_formula_suggestions.json new file mode 100644 index 0000000..21405c7 --- /dev/null +++ b/src/static/ltl_formula_suggestions.json @@ -0,0 +1,42 @@ +[ + { + "formula": "G(p -> F q)", + "description": "Response pattern: whenever p holds, q eventually follows", + "source": "Dwyer patterns" + }, + { + "formula": "G((p) -> X(q))", + "description": "Immediate response pattern: if p is true then q must hold in the next state", + "source": "Dwyer patterns" + }, + { + "formula": "F G p", + "description": "Stability: p will eventually hold forever", + "source": "VLTL Bench" + }, + { + "formula": "G F p", + "description": "Recurrence: p happens infinitely often", + "source": "VLTL Bench" + }, + { + "formula": "G(a -> F(b & X c))", + "description": "If a occurs, eventually b occurs and is immediately followed by c", + "source": "Dwyer patterns" + }, + { + "formula": "G((req) -> (F grant & (!grant) U grant))", + "description": "A request must eventually be granted and grant stays false until then", + "source": "VLTL Bench" + }, + { + "formula": "G(!(critical1 & critical2))", + "description": "Mutual exclusion between two critical sections", + "source": "VLTL Bench" + }, + { + "formula": "G(trigger -> (trigger U response))", + "description": "Response within until: response eventually follows trigger and holds until then", + "source": "Dwyer patterns" + } +] diff --git a/src/templates/ltltoengui.html b/src/templates/ltltoengui.html index 815afdc..bae44a9 100644 --- a/src/templates/ltltoengui.html +++ b/src/templates/ltltoengui.html @@ -18,8 +18,12 @@