Spaces:
Running
Running
Marjor refactoring of the backend.
Browse files- main.py +138 -81
- storage.py +1 -0
main.py
CHANGED
@@ -32,7 +32,6 @@ HF_DATASET_AUTH_TOKEN = os.environ.get('HF_DATASET_AUTH_TOKEN', "(none)")
|
|
32 |
LOCAL_STORAGE_PATH = ""
|
33 |
|
34 |
datetime_now = datetime.isoformat(datetime.utcnow())
|
35 |
-
|
36 |
FILE_EVENTS_NAME = f"events-{datetime_now}-{uuid4()}.jsonl"
|
37 |
FILE_SUBMITTED_NAME = f"submitted-{datetime_now}-{uuid4()}.jsonl"
|
38 |
|
@@ -45,7 +44,7 @@ else:
|
|
45 |
if IS_LOCALHOST:
|
46 |
DATABASE_NAME = "data/sessions_meta.db"
|
47 |
LOCAL_STORAGE_PATH = Path("data/persistent/")
|
48 |
-
pathlib.Path(DATABASE_NAME).unlink(missing_ok=True)
|
49 |
print(f"Database {DATABASE_NAME=} exists? {pathlib.Path(DATABASE_NAME).exists()}")
|
50 |
else:
|
51 |
DATABASE_NAME = "/tmp/cache/sessions_meta.db"
|
@@ -96,21 +95,20 @@ if session_state_table not in global_database_tables:
|
|
96 |
# foreign_keys=[("current_qeval", question_evaluation_table, "id")])
|
97 |
Session_State_cls = session_state_table.dataclass()
|
98 |
|
99 |
-
|
100 |
-
|
101 |
EVAL_STATE_NEW=0
|
102 |
EVAL_STATE_QUERY=1
|
103 |
EVAL_STATE_TIMEDOUT=2
|
104 |
EVAL_STATE_ANSWER=3
|
105 |
EVAL_STATE_ERROR=4
|
106 |
|
|
|
107 |
# Constants to name the various HTML ids in the code
|
108 |
HTML_SUBMIT_CODE_AREA = "submit_code_area"
|
109 |
HTML_RESULTS_AREA = "prompt_response"
|
110 |
HTML_CLEAR_FORM = "clear_the_form"
|
111 |
-
HTML_SUBMIT_FEEDBACK = "submit_feedback"
|
112 |
HTML_USER_DATA = "login_user_data"
|
113 |
-
|
|
|
114 |
|
115 |
def get_openid_configuration():
|
116 |
config_url = OPENID_PROVIDER_URL + "/.well-known/openid-configuration"
|
@@ -142,7 +140,8 @@ else:
|
|
142 |
|
143 |
|
144 |
hdrs = (
|
145 |
-
HighlightJS(langs=['python', '
|
|
|
146 |
)
|
147 |
|
148 |
if IS_LOCALHOST:
|
@@ -150,9 +149,11 @@ if IS_LOCALHOST:
|
|
150 |
print("Localhost detected in SPACE_HOST. App started in debug+live mode!")
|
151 |
app, rt = fast_app(debug=True, live=True, hdrs=hdrs)
|
152 |
REFRESH_TIME = 0.1
|
|
|
153 |
else:
|
154 |
app, rt = fast_app(debug=False, live=False, hdrs=hdrs)
|
155 |
REFRESH_TIME = 1
|
|
|
156 |
|
157 |
################# STORAGE
|
158 |
|
@@ -205,7 +206,7 @@ def validate_and_get_question_evaluation_objectid(session, qe_id:int):
|
|
205 |
return False, None, None
|
206 |
qe_obj = qa_obj_row[0]
|
207 |
if qe_id != qe_obj.id:
|
208 |
-
print("validate_and_get_question_evaluation_objectid QE {qe_id} does not belong to {qe_obj.id}")
|
209 |
return False, None, None
|
210 |
return True, qe_obj, state_rows[0]
|
211 |
|
@@ -227,26 +228,26 @@ def html_create_feedback_updown_button(qe_id, ans_id, selected=0, disabled=False
|
|
227 |
if selected == 1: up_col = colors[1]
|
228 |
if selected == -1: down_col = colors[1]
|
229 |
toggle_url = f"/toggle_up_down/{qe_id}/{ans_id}"
|
230 |
-
up = Button("👍", hx_get=f"{toggle_url}/1",
|
231 |
-
|
232 |
-
down = Button("👎", hx_get=f"{toggle_url}/-1", disabled=disabled,
|
233 |
-
|
234 |
-
target_id=f"{html_target_id}", style=f"background-color:{down_col}")
|
235 |
button_row = Div(up, down, id=html_target_id)
|
236 |
return button_row
|
237 |
|
238 |
|
239 |
-
def html_augment_evaluation_text_with_feedback(eval_html,
|
240 |
"""
|
241 |
Will plot the + / - buttons for feedback.
|
242 |
|
243 |
:param eval_html:
|
244 |
-
:param
|
245 |
:param ans_id:
|
246 |
:return:
|
247 |
"""
|
248 |
-
|
249 |
-
buttons = html_create_feedback_updown_button(
|
|
|
250 |
final_div = Div(eval_html, buttons, style=" background-color: #f0f0f0;")
|
251 |
return final_div
|
252 |
|
@@ -262,11 +263,11 @@ def get(session, qe_id:int, ans_id:int, which:int):
|
|
262 |
:param which:
|
263 |
:return:
|
264 |
"""
|
265 |
-
print(qe_id, ans_id, which)
|
266 |
if which not in {-1, 1}:
|
267 |
print(f"The {which=} is bad")
|
268 |
return None
|
269 |
-
print(f"{qe_id=} {ans_id=} {which=}")
|
270 |
|
271 |
is_ok, qe_obj, session_obj = validate_and_get_question_evaluation_objectid(session, qe_id)
|
272 |
if not is_ok:
|
@@ -275,7 +276,7 @@ def get(session, qe_id:int, ans_id:int, which:int):
|
|
275 |
|
276 |
save_to_storage(
|
277 |
storage.NavigationEvent(event_type="/toggle_up_down", event_session_id=session_obj.session_id,
|
278 |
-
event_params={"
|
279 |
)
|
280 |
|
281 |
answer_eval_js = json.loads(qe_obj.answer_eval_text)
|
@@ -293,17 +294,18 @@ def get(session, qe_id:int, ans_id:int, which:int):
|
|
293 |
return buttons
|
294 |
|
295 |
|
296 |
-
def html_get_textual_feedback_form(qe_obj,
|
297 |
-
if
|
298 |
ph = "Thank you!"
|
299 |
else:
|
300 |
ph = "Write your general feedback here"
|
301 |
form = Form(Input(name="freeform_feedback", placeholder=ph),
|
302 |
Button("Submit", disabled=(qe_obj.submitted == 1)), hx_post=f"/submit_feedback/{qe_obj.id}",
|
303 |
-
target_id=
|
304 |
-
div = Div(P("Give us a general feedback for the evaluation (optional)"), form
|
305 |
return div
|
306 |
|
|
|
307 |
@rt("/submit_feedback/{qe_id}")
|
308 |
def post(session, qe_id:int, freeform_feedback:str):
|
309 |
is_ok, qe_obj, session_obj = validate_and_get_question_evaluation_objectid(session, qe_id)
|
@@ -315,7 +317,7 @@ def post(session, qe_id:int, freeform_feedback:str):
|
|
315 |
session_id = session.get("session_id", "Not set")
|
316 |
save_to_storage(
|
317 |
storage.NavigationEvent(event_type="/submit_feedback", event_session_id=session_id,
|
318 |
-
event_params={"
|
319 |
)
|
320 |
|
321 |
answer_eval_js = json.loads(qe_obj.answer_eval_text)
|
@@ -323,21 +325,22 @@ def post(session, qe_id:int, freeform_feedback:str):
|
|
323 |
qe_obj.submitted = True
|
324 |
qe_obj.answer_eval_text = json.dumps(answer_eval_js)
|
325 |
question_evaluation_table.upsert(qe_obj)
|
326 |
-
|
327 |
save_to_storage(
|
328 |
storage.CodeSubmittedEvent(event_session_id=session["session_id"], db_question_evaluation_id=qe_obj.id,
|
329 |
submitted_date=session_obj.submitted_date,
|
330 |
received_date=session_obj.evaluated_date,
|
331 |
code_to_eval=qe_obj.code_text, evaluation_response=qe_obj.answer_eval_text,
|
332 |
-
has_feedback=True, feedback_date=datetime.isoformat(datetime.utcnow())
|
|
|
333 |
)
|
334 |
|
335 |
-
return html_get_textual_feedback_form(qe_obj, thank=True)
|
336 |
|
337 |
|
338 |
####### EVALUATE CODE
|
339 |
|
340 |
-
def html_format_code_review_form(qe_obj
|
341 |
"""
|
342 |
Formats the code review, adding fields for feedback if it is required.
|
343 |
|
@@ -357,58 +360,96 @@ def html_format_code_review_form(qe_obj, html_id=""):
|
|
357 |
for k, eval_line in enumerate(enhanced_answer):
|
358 |
if caug_code == eval_line["criteria"]:
|
359 |
eval_txt = P(eval_line["explanation"])
|
360 |
-
eval_txt_fb = html_augment_evaluation_text_with_feedback(eval_txt, qe_obj
|
361 |
list_of_citerias.append(eval_txt_fb)
|
362 |
textual_feedback = html_get_textual_feedback_form(qe_obj)
|
363 |
-
return
|
|
|
364 |
|
365 |
-
def html_default_results(html_id):
|
366 |
-
return Div(P("This is where criterias will show up once the code is evaluated"), id=html_id)
|
367 |
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
374 |
|
375 |
def get_latest_eval_request_status(session_id):
|
376 |
state_rows = session_state_table(limit=1, where=f"session_id == '{session_id}'", order_by="id DESC")
|
377 |
if len(state_rows) <= 0:
|
378 |
return EVAL_STATE_NEW, None
|
379 |
state_obj = state_rows[0]
|
380 |
-
if state_obj.state in {EVAL_STATE_NEW, EVAL_STATE_QUERY, EVAL_STATE_ANSWER}:
|
381 |
-
return
|
382 |
return EVAL_STATE_ERROR, state_obj
|
383 |
|
384 |
|
385 |
-
def html_error_results(message
|
386 |
-
|
387 |
-
return
|
388 |
|
389 |
|
390 |
def html_render_code_output(code):
|
391 |
-
txtarea = Pre(Code(code))
|
392 |
return txtarea
|
393 |
|
394 |
|
395 |
-
def html_render_answer_from_db(session_id
|
396 |
eval_request_status, state_obj = get_latest_eval_request_status(session_id)
|
397 |
# state_rows = session_state_table(limit=1, where=f"session_id == '{session_id}'", order_by="id DESC")
|
398 |
# print(eval_request_status, state_obj)
|
399 |
if eval_request_status == EVAL_STATE_NEW:
|
400 |
-
return html_default_results(
|
401 |
if eval_request_status == EVAL_STATE_ANSWER:
|
402 |
qe_obj_lst = question_evaluation_table(limit=1, where=f"id == {state_obj.current_qeval}")
|
403 |
if len(qe_obj_lst) < 1:
|
404 |
print(f"Object id {state_obj.current_qeval} can't be found in question_evaluation_table")
|
405 |
return (None,)
|
406 |
qe_obj = qe_obj_lst[0]
|
407 |
-
return (html_format_code_review_form(qe_obj
|
408 |
-
html_render_inputbox(target_html_id=HTML_RESULTS_AREA, region_html_id=HTML_SUBMIT_CODE_AREA))
|
|
|
|
|
409 |
if eval_request_status == EVAL_STATE_QUERY:
|
410 |
-
|
411 |
-
|
|
|
|
|
412 |
|
413 |
|
414 |
# How can I timeout? Well ... TBD.
|
@@ -468,6 +509,18 @@ def call_gpt_and_store_result(session_obj_id, code_to_check):
|
|
468 |
import traceback
|
469 |
traceback.print_exc()
|
470 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
471 |
######## CODE INPUT FORM
|
472 |
|
473 |
|
@@ -481,6 +534,7 @@ def html_render_inputbox(target_html_id, region_html_id):
|
|
481 |
return Div(form, id=region_html_id, hx_swap_oob='true')
|
482 |
|
483 |
|
|
|
484 |
########## CLEAR FORM
|
485 |
|
486 |
def html_render_clear_area_button(html_id):
|
@@ -555,8 +609,8 @@ function checkPopupClosed() {
|
|
555 |
else:
|
556 |
auth_callback_url = f"https://{SPACE_HOST}/auth_callback/"
|
557 |
|
558 |
-
secret = secrets.token_urlsafe(32)
|
559 |
-
session[
|
560 |
encoded_scopes = html.escape(OAUTH_SCOPES)
|
561 |
redirect_link = (f"https://huggingface.co/oauth/authorize?redirect_uri={auth_callback_url}&scope={encoded_scopes}"
|
562 |
f"&client_id={OAUTH_CLIENT_ID}&state={secret}&response_type=code&prompt=consent")
|
@@ -598,6 +652,7 @@ def get(session, code:str=None, state:str=None, error:str=None, error_descriptio
|
|
598 |
:param error_description:
|
599 |
:return:
|
600 |
"""
|
|
|
601 |
close_script = Script("""
|
602 |
localStorage.setItem('login_success', 'true');
|
603 |
setTimeout(function() {
|
@@ -613,17 +668,18 @@ def get(session, code:str=None, state:str=None, error:str=None, error_descriptio
|
|
613 |
P("Please close this page"))
|
614 |
return_answer.append(ans)
|
615 |
return Div(*return_answer)
|
616 |
-
print(f"OAuth returned a code")
|
617 |
|
618 |
# validating the secret
|
619 |
-
sess_secret = session.get(
|
620 |
if sess_secret is None:
|
621 |
print("No session secret")
|
622 |
return_answer.append(P("access denied"))
|
623 |
return Div(*return_answer)
|
624 |
|
625 |
if sess_secret != state:
|
626 |
-
|
|
|
|
|
627 |
return Div(*return_answer)
|
628 |
|
629 |
# Moving on and get the token
|
@@ -660,10 +716,9 @@ def get(session, code:str=None, state:str=None, error:str=None, error_descriptio
|
|
660 |
user_data = response_userinfo.json()
|
661 |
# Set the user data in DB
|
662 |
# TODO Is it mildly safe to store user data in session?
|
663 |
-
session[
|
664 |
-
session[
|
665 |
-
print(user_data)
|
666 |
-
|
667 |
else:
|
668 |
print(f"Error while taking the user data: {response_userinfo.text}" )
|
669 |
return_answer.append(P("Error logging in"))
|
@@ -675,13 +730,21 @@ def get(session, code:str=None, state:str=None, error:str=None, error_descriptio
|
|
675 |
|
676 |
# print(session)
|
677 |
return_answer.append(P("Succes! Close this page."))
|
678 |
-
|
|
|
|
|
|
|
|
|
679 |
return Div(*return_answer)
|
680 |
|
681 |
|
682 |
@rt("/logout")
|
683 |
def get(session):
|
684 |
-
session[
|
|
|
|
|
|
|
|
|
685 |
session["user_data"] = None
|
686 |
return RedirectResponse(url="/")
|
687 |
|
@@ -690,7 +753,6 @@ def get(session):
|
|
690 |
|
691 |
@rt("/")
|
692 |
def get(session):
|
693 |
-
print(session)
|
694 |
if 'session_id' not in session:
|
695 |
session['session_id'] = str(uuid.uuid4())
|
696 |
session_id = session["session_id"]
|
@@ -700,7 +762,7 @@ def get(session):
|
|
700 |
save_to_storage(
|
701 |
storage.NavigationEvent(event_type="/", event_session_id=session_id)
|
702 |
)
|
703 |
-
user_data = session.get(
|
704 |
if user_data is not None:
|
705 |
auth_area = html_render_welcome_user(user_data)
|
706 |
else:
|
@@ -715,21 +777,15 @@ def get(session):
|
|
715 |
]
|
716 |
|
717 |
input_area = html_render_inputbox(target_html_id=HTML_RESULTS_AREA, region_html_id=HTML_SUBMIT_CODE_AREA)
|
718 |
-
|
719 |
clear_area = render_clear_area(session_id, HTML_CLEAR_FORM)
|
720 |
-
|
721 |
-
|
722 |
-
@rt("/render_answer")
|
723 |
-
def get(session):
|
724 |
-
if 'session_id' not in session: return "render_answer No session ID"
|
725 |
-
session_id = session["session_id"]
|
726 |
-
answer_area = html_render_answer_from_db(session_id, HTML_RESULTS_AREA)
|
727 |
-
return answer_area
|
728 |
|
729 |
|
730 |
@rt("/submit_to_eval", methods="post")
|
731 |
def post(session, ccodetoeval:str):
|
732 |
-
print(session)
|
733 |
if 'session_id' not in session:
|
734 |
return P("submit_to_eval. Bad call. No session ID")
|
735 |
session_id = session["session_id"]
|
@@ -738,13 +794,15 @@ def post(session, ccodetoeval:str):
|
|
738 |
state=EVAL_STATE_QUERY,
|
739 |
submitted_date=datetime.isoformat(datetime.utcnow()),
|
740 |
)
|
|
|
|
|
|
|
|
|
741 |
# we insert and we get the new primary key
|
742 |
session_obj = session_state_table.insert(session_obj)
|
743 |
# will be executed in another thread with magic @threaded
|
744 |
call_gpt_and_store_result(session_obj.id, ccodetoeval)
|
745 |
-
return (
|
746 |
-
render_clear_area(session_id, HTML_CLEAR_FORM)
|
747 |
-
)
|
748 |
|
749 |
|
750 |
@rt("/clear_area", methods="get")
|
@@ -763,11 +821,10 @@ def get(session):
|
|
763 |
submitted_date=datetime.isoformat(datetime.utcnow()),
|
764 |
)
|
765 |
session_state_table.insert(session_obj)
|
766 |
-
# re-issue
|
767 |
input_area = html_render_inputbox(target_html_id=HTML_RESULTS_AREA, region_html_id=HTML_SUBMIT_CODE_AREA)
|
768 |
-
results_area =
|
769 |
clear_area = render_clear_area(session_id, HTML_CLEAR_FORM)
|
770 |
-
|
771 |
-
return *results_area, input_area, clear_area
|
772 |
|
773 |
serve()
|
|
|
32 |
LOCAL_STORAGE_PATH = ""
|
33 |
|
34 |
datetime_now = datetime.isoformat(datetime.utcnow())
|
|
|
35 |
FILE_EVENTS_NAME = f"events-{datetime_now}-{uuid4()}.jsonl"
|
36 |
FILE_SUBMITTED_NAME = f"submitted-{datetime_now}-{uuid4()}.jsonl"
|
37 |
|
|
|
44 |
if IS_LOCALHOST:
|
45 |
DATABASE_NAME = "data/sessions_meta.db"
|
46 |
LOCAL_STORAGE_PATH = Path("data/persistent/")
|
47 |
+
# pathlib.Path(DATABASE_NAME).unlink(missing_ok=True)
|
48 |
print(f"Database {DATABASE_NAME=} exists? {pathlib.Path(DATABASE_NAME).exists()}")
|
49 |
else:
|
50 |
DATABASE_NAME = "/tmp/cache/sessions_meta.db"
|
|
|
95 |
# foreign_keys=[("current_qeval", question_evaluation_table, "id")])
|
96 |
Session_State_cls = session_state_table.dataclass()
|
97 |
|
|
|
|
|
98 |
EVAL_STATE_NEW=0
|
99 |
EVAL_STATE_QUERY=1
|
100 |
EVAL_STATE_TIMEDOUT=2
|
101 |
EVAL_STATE_ANSWER=3
|
102 |
EVAL_STATE_ERROR=4
|
103 |
|
104 |
+
|
105 |
# Constants to name the various HTML ids in the code
|
106 |
HTML_SUBMIT_CODE_AREA = "submit_code_area"
|
107 |
HTML_RESULTS_AREA = "prompt_response"
|
108 |
HTML_CLEAR_FORM = "clear_the_form"
|
|
|
109 |
HTML_USER_DATA = "login_user_data"
|
110 |
+
OAUTH_SECRET_SESSION_NAME = "oauth_secret"
|
111 |
+
USER_DATA_SESSION_NAME = "user_data"
|
112 |
|
113 |
def get_openid_configuration():
|
114 |
config_url = OPENID_PROVIDER_URL + "/.well-known/openid-configuration"
|
|
|
140 |
|
141 |
|
142 |
hdrs = (
|
143 |
+
HighlightJS(langs=['python', 'c', 'c++']),
|
144 |
+
# Meta(name="htmx-config", content='{"defaultSwapStyle":"outerHTML"}')
|
145 |
)
|
146 |
|
147 |
if IS_LOCALHOST:
|
|
|
149 |
print("Localhost detected in SPACE_HOST. App started in debug+live mode!")
|
150 |
app, rt = fast_app(debug=True, live=True, hdrs=hdrs)
|
151 |
REFRESH_TIME = 0.1
|
152 |
+
EVAL_TIMEOUT_SECONDS = 2
|
153 |
else:
|
154 |
app, rt = fast_app(debug=False, live=False, hdrs=hdrs)
|
155 |
REFRESH_TIME = 1
|
156 |
+
EVAL_TIMEOUT_SECONDS = 15
|
157 |
|
158 |
################# STORAGE
|
159 |
|
|
|
206 |
return False, None, None
|
207 |
qe_obj = qa_obj_row[0]
|
208 |
if qe_id != qe_obj.id:
|
209 |
+
print(f"validate_and_get_question_evaluation_objectid QE {qe_id} does not belong to {qe_obj.id}")
|
210 |
return False, None, None
|
211 |
return True, qe_obj, state_rows[0]
|
212 |
|
|
|
228 |
if selected == 1: up_col = colors[1]
|
229 |
if selected == -1: down_col = colors[1]
|
230 |
toggle_url = f"/toggle_up_down/{qe_id}/{ans_id}"
|
231 |
+
up = Button("👍", hx_get=f"{toggle_url}/1", disabled=disabled, target_id=html_target_id,
|
232 |
+
hx_swap="outerHTML", style=f"background-color:{up_col}")
|
233 |
+
down = Button("👎", hx_get=f"{toggle_url}/-1", disabled=disabled, target_id=html_target_id,
|
234 |
+
hx_swap="outerHTML", style=f"background-color:{down_col}")
|
|
|
235 |
button_row = Div(up, down, id=html_target_id)
|
236 |
return button_row
|
237 |
|
238 |
|
239 |
+
def html_augment_evaluation_text_with_feedback(eval_html, qe_obj, ans_id):
|
240 |
"""
|
241 |
Will plot the + / - buttons for feedback.
|
242 |
|
243 |
:param eval_html:
|
244 |
+
:param qe_obj:
|
245 |
:param ans_id:
|
246 |
:return:
|
247 |
"""
|
248 |
+
answer_eval_js = json.loads(qe_obj.answer_eval_text)
|
249 |
+
buttons = html_create_feedback_updown_button(qe_obj.id, ans_id, answer_eval_js[ans_id]["EVAL"],
|
250 |
+
disabled=(qe_obj.submitted == 1))
|
251 |
final_div = Div(eval_html, buttons, style=" background-color: #f0f0f0;")
|
252 |
return final_div
|
253 |
|
|
|
263 |
:param which:
|
264 |
:return:
|
265 |
"""
|
266 |
+
# print(qe_id, ans_id, which)
|
267 |
if which not in {-1, 1}:
|
268 |
print(f"The {which=} is bad")
|
269 |
return None
|
270 |
+
# print(f"{qe_id=} {ans_id=} {which=}")
|
271 |
|
272 |
is_ok, qe_obj, session_obj = validate_and_get_question_evaluation_objectid(session, qe_id)
|
273 |
if not is_ok:
|
|
|
276 |
|
277 |
save_to_storage(
|
278 |
storage.NavigationEvent(event_type="/toggle_up_down", event_session_id=session_obj.session_id,
|
279 |
+
event_params={"question_evaluation_id":qe_id, "answer_id":ans_id, "which":which}),
|
280 |
)
|
281 |
|
282 |
answer_eval_js = json.loads(qe_obj.answer_eval_text)
|
|
|
294 |
return buttons
|
295 |
|
296 |
|
297 |
+
def html_get_textual_feedback_form(qe_obj,):
|
298 |
+
if qe_obj.submitted == 1:
|
299 |
ph = "Thank you!"
|
300 |
else:
|
301 |
ph = "Write your general feedback here"
|
302 |
form = Form(Input(name="freeform_feedback", placeholder=ph),
|
303 |
Button("Submit", disabled=(qe_obj.submitted == 1)), hx_post=f"/submit_feedback/{qe_obj.id}",
|
304 |
+
target_id=HTML_RESULTS_AREA, hx_swap="outerHTML",)
|
305 |
+
div = Div(P("Give us a general feedback for the evaluation (optional)"), form)
|
306 |
return div
|
307 |
|
308 |
+
|
309 |
@rt("/submit_feedback/{qe_id}")
|
310 |
def post(session, qe_id:int, freeform_feedback:str):
|
311 |
is_ok, qe_obj, session_obj = validate_and_get_question_evaluation_objectid(session, qe_id)
|
|
|
317 |
session_id = session.get("session_id", "Not set")
|
318 |
save_to_storage(
|
319 |
storage.NavigationEvent(event_type="/submit_feedback", event_session_id=session_id,
|
320 |
+
event_params={"question_evaluation_id":qe_id})
|
321 |
)
|
322 |
|
323 |
answer_eval_js = json.loads(qe_obj.answer_eval_text)
|
|
|
325 |
qe_obj.submitted = True
|
326 |
qe_obj.answer_eval_text = json.dumps(answer_eval_js)
|
327 |
question_evaluation_table.upsert(qe_obj)
|
328 |
+
user_data = session.get(USER_DATA_SESSION_NAME, {})
|
329 |
save_to_storage(
|
330 |
storage.CodeSubmittedEvent(event_session_id=session["session_id"], db_question_evaluation_id=qe_obj.id,
|
331 |
submitted_date=session_obj.submitted_date,
|
332 |
received_date=session_obj.evaluated_date,
|
333 |
code_to_eval=qe_obj.code_text, evaluation_response=qe_obj.answer_eval_text,
|
334 |
+
has_feedback=True, feedback_date=datetime.isoformat(datetime.utcnow()),
|
335 |
+
feedback_userdata=user_data)
|
336 |
)
|
337 |
|
338 |
+
return tl_html_results_and_feedback_area(session_id) #html_render_answer_from_db(session_id, html_id=HTML_RESULTS_AREA) #html_get_textual_feedback_form(qe_obj, thank=True), html_format_code_review_form(qe_obj, HTML_RESULTS_AREA)
|
339 |
|
340 |
|
341 |
####### EVALUATE CODE
|
342 |
|
343 |
+
def html_format_code_review_form(qe_obj):
|
344 |
"""
|
345 |
Formats the code review, adding fields for feedback if it is required.
|
346 |
|
|
|
360 |
for k, eval_line in enumerate(enhanced_answer):
|
361 |
if caug_code == eval_line["criteria"]:
|
362 |
eval_txt = P(eval_line["explanation"])
|
363 |
+
eval_txt_fb = html_augment_evaluation_text_with_feedback(eval_txt, qe_obj, k)
|
364 |
list_of_citerias.append(eval_txt_fb)
|
365 |
textual_feedback = html_get_textual_feedback_form(qe_obj)
|
366 |
+
return html_render_code_output(c_code), *list_of_citerias, textual_feedback
|
367 |
+
|
368 |
|
|
|
|
|
369 |
|
370 |
+
|
371 |
+
def check_if_query_should_timeout(session_obj):
|
372 |
+
"""
|
373 |
+
Checks if the evaluation request is timed out. Will update the database to ERROR
|
374 |
+
|
375 |
+
:param eval_request_status:
|
376 |
+
:return:
|
377 |
+
"""
|
378 |
+
submitted_dt = datetime.fromisoformat(session_obj.submitted_date)
|
379 |
+
crt_time = datetime.utcnow()
|
380 |
+
time_difference = crt_time - submitted_dt
|
381 |
+
difference_in_seconds = time_difference.total_seconds()
|
382 |
+
if difference_in_seconds > EVAL_TIMEOUT_SECONDS:
|
383 |
+
session_obj.state = EVAL_STATE_TIMEDOUT
|
384 |
+
session_state_table.upsert(session_obj)
|
385 |
+
|
386 |
+
|
387 |
+
def html_default_results():
|
388 |
+
return P("Submit a piece of C code to get a Clean-Code evaluation.")
|
389 |
+
|
390 |
+
def html_waiting_for_results():
|
391 |
+
return Div(P("Working . . ."), hx_get=f"/render_answer",
|
392 |
+
hx_trigger = f"every {REFRESH_TIME}s", hx_swap="outerHTML", target_id=HTML_RESULTS_AREA,)
|
393 |
+
|
394 |
+
def html_eval_request_timed_out():
|
395 |
+
return P("Timed out. Retry in few minutes pls!")
|
396 |
+
|
397 |
+
|
398 |
+
@rt("/render_answer")
|
399 |
+
def get(session):
|
400 |
+
"""
|
401 |
+
Endpoint to render the evaluation answer. Pooled by frontend.
|
402 |
+
|
403 |
+
:param session:
|
404 |
+
:return:
|
405 |
+
"""
|
406 |
+
if 'session_id' not in session: return "render_answer No session ID"
|
407 |
+
session_id = session["session_id"]
|
408 |
+
answer_area = tl_html_results_and_feedback_area(session_id)
|
409 |
+
return answer_area
|
410 |
+
|
411 |
|
412 |
def get_latest_eval_request_status(session_id):
|
413 |
state_rows = session_state_table(limit=1, where=f"session_id == '{session_id}'", order_by="id DESC")
|
414 |
if len(state_rows) <= 0:
|
415 |
return EVAL_STATE_NEW, None
|
416 |
state_obj = state_rows[0]
|
417 |
+
if state_obj.state in {EVAL_STATE_NEW, EVAL_STATE_QUERY, EVAL_STATE_TIMEDOUT, EVAL_STATE_ANSWER}:
|
418 |
+
return state_obj.state, state_obj
|
419 |
return EVAL_STATE_ERROR, state_obj
|
420 |
|
421 |
|
422 |
+
def html_error_results(message):
|
423 |
+
ans = P("There was an error:", P(message))
|
424 |
+
return ans
|
425 |
|
426 |
|
427 |
def html_render_code_output(code):
|
428 |
+
txtarea = Pre(Code(code, _class="language-c"))
|
429 |
return txtarea
|
430 |
|
431 |
|
432 |
+
def html_render_answer_from_db(session_id):
|
433 |
eval_request_status, state_obj = get_latest_eval_request_status(session_id)
|
434 |
# state_rows = session_state_table(limit=1, where=f"session_id == '{session_id}'", order_by="id DESC")
|
435 |
# print(eval_request_status, state_obj)
|
436 |
if eval_request_status == EVAL_STATE_NEW:
|
437 |
+
return html_default_results(),
|
438 |
if eval_request_status == EVAL_STATE_ANSWER:
|
439 |
qe_obj_lst = question_evaluation_table(limit=1, where=f"id == {state_obj.current_qeval}")
|
440 |
if len(qe_obj_lst) < 1:
|
441 |
print(f"Object id {state_obj.current_qeval} can't be found in question_evaluation_table")
|
442 |
return (None,)
|
443 |
qe_obj = qe_obj_lst[0]
|
444 |
+
return (html_format_code_review_form(qe_obj),
|
445 |
+
html_render_inputbox(target_html_id=HTML_RESULTS_AREA, region_html_id=HTML_SUBMIT_CODE_AREA))
|
446 |
+
if eval_request_status == EVAL_STATE_TIMEDOUT:
|
447 |
+
return html_eval_request_timed_out(),
|
448 |
if eval_request_status == EVAL_STATE_QUERY:
|
449 |
+
check_if_query_should_timeout(state_obj)
|
450 |
+
return html_waiting_for_results(),
|
451 |
+
print(f"Unknown state of the code evalation request {state_obj.state}:")
|
452 |
+
return html_error_results("Some error occured."),
|
453 |
|
454 |
|
455 |
# How can I timeout? Well ... TBD.
|
|
|
509 |
import traceback
|
510 |
traceback.print_exc()
|
511 |
|
512 |
+
|
513 |
+
def tl_html_results_and_feedback_area(session_id):
|
514 |
+
"""
|
515 |
+
Top level component that will render the code evaluation overlapped with feedback submission form.
|
516 |
+
|
517 |
+
:param session_id:
|
518 |
+
:return:
|
519 |
+
"""
|
520 |
+
results_feedback_area = html_render_answer_from_db(session_id)
|
521 |
+
return Div(*results_feedback_area, id=HTML_RESULTS_AREA)
|
522 |
+
|
523 |
+
|
524 |
######## CODE INPUT FORM
|
525 |
|
526 |
|
|
|
534 |
return Div(form, id=region_html_id, hx_swap_oob='true')
|
535 |
|
536 |
|
537 |
+
|
538 |
########## CLEAR FORM
|
539 |
|
540 |
def html_render_clear_area_button(html_id):
|
|
|
609 |
else:
|
610 |
auth_callback_url = f"https://{SPACE_HOST}/auth_callback/"
|
611 |
|
612 |
+
secret = datetime.isoformat(datetime.utcnow()) + "-" + secrets.token_urlsafe(32)
|
613 |
+
session[OAUTH_SECRET_SESSION_NAME] = secret
|
614 |
encoded_scopes = html.escape(OAUTH_SCOPES)
|
615 |
redirect_link = (f"https://huggingface.co/oauth/authorize?redirect_uri={auth_callback_url}&scope={encoded_scopes}"
|
616 |
f"&client_id={OAUTH_CLIENT_ID}&state={secret}&response_type=code&prompt=consent")
|
|
|
652 |
:param error_description:
|
653 |
:return:
|
654 |
"""
|
655 |
+
# print(session)
|
656 |
close_script = Script("""
|
657 |
localStorage.setItem('login_success', 'true');
|
658 |
setTimeout(function() {
|
|
|
668 |
P("Please close this page"))
|
669 |
return_answer.append(ans)
|
670 |
return Div(*return_answer)
|
|
|
671 |
|
672 |
# validating the secret
|
673 |
+
sess_secret = session.get(OAUTH_SECRET_SESSION_NAME, None)
|
674 |
if sess_secret is None:
|
675 |
print("No session secret")
|
676 |
return_answer.append(P("access denied"))
|
677 |
return Div(*return_answer)
|
678 |
|
679 |
if sess_secret != state:
|
680 |
+
msg = f"Mismatch session secret and HF secret: {sess_secret=} {state=}"
|
681 |
+
print(msg)
|
682 |
+
return_answer.append(P("Mismatch session secret and HF secret"))
|
683 |
return Div(*return_answer)
|
684 |
|
685 |
# Moving on and get the token
|
|
|
716 |
user_data = response_userinfo.json()
|
717 |
# Set the user data in DB
|
718 |
# TODO Is it mildly safe to store user data in session?
|
719 |
+
session[USER_DATA_SESSION_NAME] = user_data
|
720 |
+
session[OAUTH_SECRET_SESSION_NAME] = None
|
721 |
+
# print(user_data)
|
|
|
722 |
else:
|
723 |
print(f"Error while taking the user data: {response_userinfo.text}" )
|
724 |
return_answer.append(P("Error logging in"))
|
|
|
730 |
|
731 |
# print(session)
|
732 |
return_answer.append(P("Succes! Close this page."))
|
733 |
+
save_to_storage(
|
734 |
+
storage.NavigationEvent(event_type="/auth_callback", event_session_id=session["session_id"],
|
735 |
+
event_params={"user_data":user_data})
|
736 |
+
)
|
737 |
+
|
738 |
return Div(*return_answer)
|
739 |
|
740 |
|
741 |
@rt("/logout")
|
742 |
def get(session):
|
743 |
+
session[OAUTH_SECRET_SESSION_NAME] = None
|
744 |
+
save_to_storage(
|
745 |
+
storage.NavigationEvent(event_type="/logout", event_session_id=session["session_id"],
|
746 |
+
event_params={"user_data":session["user_data"]})
|
747 |
+
)
|
748 |
session["user_data"] = None
|
749 |
return RedirectResponse(url="/")
|
750 |
|
|
|
753 |
|
754 |
@rt("/")
|
755 |
def get(session):
|
|
|
756 |
if 'session_id' not in session:
|
757 |
session['session_id'] = str(uuid.uuid4())
|
758 |
session_id = session["session_id"]
|
|
|
762 |
save_to_storage(
|
763 |
storage.NavigationEvent(event_type="/", event_session_id=session_id)
|
764 |
)
|
765 |
+
user_data = session.get(USER_DATA_SESSION_NAME, None)
|
766 |
if user_data is not None:
|
767 |
auth_area = html_render_welcome_user(user_data)
|
768 |
else:
|
|
|
777 |
]
|
778 |
|
779 |
input_area = html_render_inputbox(target_html_id=HTML_RESULTS_AREA, region_html_id=HTML_SUBMIT_CODE_AREA)
|
780 |
+
results_feedback_area = tl_html_results_and_feedback_area(session_id)
|
781 |
clear_area = render_clear_area(session_id, HTML_CLEAR_FORM)
|
782 |
+
# print(session)
|
783 |
+
return title, Main(*preamble, input_area, results_feedback_area, clear_area)
|
|
|
|
|
|
|
|
|
|
|
|
|
784 |
|
785 |
|
786 |
@rt("/submit_to_eval", methods="post")
|
787 |
def post(session, ccodetoeval:str):
|
788 |
+
# print(session)
|
789 |
if 'session_id' not in session:
|
790 |
return P("submit_to_eval. Bad call. No session ID")
|
791 |
session_id = session["session_id"]
|
|
|
794 |
state=EVAL_STATE_QUERY,
|
795 |
submitted_date=datetime.isoformat(datetime.utcnow()),
|
796 |
)
|
797 |
+
save_to_storage(
|
798 |
+
storage.NavigationEvent(event_type="/submit_to_eval", event_session_id=session_id, event_params={"ccodetoeval":ccodetoeval})
|
799 |
+
)
|
800 |
+
|
801 |
# we insert and we get the new primary key
|
802 |
session_obj = session_state_table.insert(session_obj)
|
803 |
# will be executed in another thread with magic @threaded
|
804 |
call_gpt_and_store_result(session_obj.id, ccodetoeval)
|
805 |
+
return tl_html_results_and_feedback_area(session_id), render_clear_area(session_id, HTML_CLEAR_FORM)
|
|
|
|
|
806 |
|
807 |
|
808 |
@rt("/clear_area", methods="get")
|
|
|
821 |
submitted_date=datetime.isoformat(datetime.utcnow()),
|
822 |
)
|
823 |
session_state_table.insert(session_obj)
|
824 |
+
# re-issue the page, basically.
|
825 |
input_area = html_render_inputbox(target_html_id=HTML_RESULTS_AREA, region_html_id=HTML_SUBMIT_CODE_AREA)
|
826 |
+
results_area = tl_html_results_and_feedback_area(session_id)
|
827 |
clear_area = render_clear_area(session_id, HTML_CLEAR_FORM)
|
828 |
+
return results_area, input_area, clear_area
|
|
|
829 |
|
830 |
serve()
|
storage.py
CHANGED
@@ -23,6 +23,7 @@ class CodeSubmittedEvent():
|
|
23 |
evaluation_response:str = ""
|
24 |
has_feedback:bool = False
|
25 |
feedback_date:str = ""
|
|
|
26 |
|
27 |
|
28 |
|
|
|
23 |
evaluation_response:str = ""
|
24 |
has_feedback:bool = False
|
25 |
feedback_date:str = ""
|
26 |
+
feedback_userdata:dict=dataclasses.field(default_factory=dict)
|
27 |
|
28 |
|
29 |
|