Skip to content
Snippets Groups Projects
Commit 6c86c3d4 authored by Martin Mareš's avatar Martin Mareš
Browse files

Propojení s CMS

U praktických úloh nabízíme místo formuláře na odevzdání přechod do CMS
přes nově dodělaný single sign-on.
parent 324e2b6a
No related branches found
No related tags found
1 merge request!98Praktické úlohy a propojení s CMS
...@@ -56,3 +56,8 @@ REG_TOKEN_VALIDITY = 10 ...@@ -56,3 +56,8 @@ REG_TOKEN_VALIDITY = 10
# Aktuální ročník MO # Aktuální ročník MO
CURRENT_YEAR = 71 CURRENT_YEAR = 71
# Instance CMS, ve které žijí praktické programovací úlohy, a její SSO secret.
# Pokud se neuvede nebo je None, praktické úlohy nejde odevzdávat.
# CMS_ROOT = 'https://contest.kam.mff.cuni.cz/cms/'
# CMS_SSO_SECRET = 'BrumBrum'
...@@ -42,6 +42,7 @@ app.jinja_env.globals.update(LogType=db.LogType) ...@@ -42,6 +42,7 @@ app.jinja_env.globals.update(LogType=db.LogType)
app.jinja_env.globals.update(PartState=db.PartState) app.jinja_env.globals.update(PartState=db.PartState)
app.jinja_env.globals.update(RoleType=db.RoleType) app.jinja_env.globals.update(RoleType=db.RoleType)
app.jinja_env.globals.update(PaperType=db.PaperType) app.jinja_env.globals.update(PaperType=db.PaperType)
app.jinja_env.globals.update(TaskType=db.TaskType)
app.jinja_env.globals.update(JobType=db.JobType) app.jinja_env.globals.update(JobType=db.JobType)
app.jinja_env.globals.update(JobState=db.JobState) app.jinja_env.globals.update(JobState=db.JobState)
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
{% if state == RoundState.running %} {% if state == RoundState.running %}
{% if contest.ct_can_submit() %} {% if contest.ct_can_submit() %}
<h3>Odevzdat řešení</h3> <h3>Odevzdat řešení</h3>
{% if task.type == TaskType.regular %}
{% if round.ct_submit_end and g.now > round.ct_submit_end %} {% if round.ct_submit_end and g.now > round.ct_submit_end %}
<p class="alert alert-danger">Pozor, odevzdáváte po termínu, uplynul {{ round.ct_submit_end|time_and_timedelta }}. <p class="alert alert-danger">Pozor, odevzdáváte po termínu, uplynul {{ round.ct_submit_end|time_and_timedelta }}.
Vaše řešení nemusí být hodnoceno. Doporučujeme využít políčko pro poznámku a vysvětlit situaci. Vaše řešení nemusí být hodnoceno. Doporučujeme využít políčko pro poznámku a vysvětlit situaci.
...@@ -32,6 +33,22 @@ ...@@ -32,6 +33,22 @@
nahradili (např. nahráli jste omylem řešení jiné úlohy). nahradili (např. nahráli jste omylem řešení jiné úlohy).
{% endif %} {% endif %}
{{ wtf.quick_form(form, form_type='basic', button_map={'submit': 'primary'}) }} {{ wtf.quick_form(form, form_type='basic', button_map={'submit': 'primary'}) }}
{% elif task.type == TaskType.cms and cms_params %}
<p>Tato úloha je praktická a odevzdává se do systému CMS. Odevzdaná řešení
se zde objeví až s koncem soutěže.
<form action='{{ cms_params.url }}' method=POST>
<input type=hidden name=username value="{{ cms_params.username }}">
<input type=hidden name=first_name value="{{ cms_params.first_name }}">
<input type=hidden name=last_name value="{{ cms_params.last_name }}">
<input type=hidden name=timestamp value="{{ cms_params.timestamp }}">
<input type=hidden name=signature value="{{ cms_params.signature }}">
<input type=hidden name=back_url value="{{ cms_params.back_url }}">
<input type=submit class='btn btn-primary' value="Přejít do CMS">
</form>
{% else %}
<p>Úloha s neznámým režimem odevzdávání.
{% endif %}
{% else %} {% else %}
<p>Již není možné odevzdat řešení, termín na odevzdávání vypršel.</p> <p>Již není možné odevzdat řešení, termín na odevzdávání vypršel.</p>
{% endif %} {% endif %}
...@@ -57,7 +74,7 @@ ...@@ -57,7 +74,7 @@
<p>Soutěž se nachází v neznámém stavu. To by se nemělo stát :) <p>Soutěž se nachází v neznámém stavu. To by se nemělo stát :)
{% endif %} {% endif %}
{% if sol or state == RoundState.running %} {% if sol or state == RoundState.running and task.type == TaskType.regular %}
<h3>Historie vašich řešení</h3> <h3>Historie vašich řešení</h3>
{% if papers %} {% if papers %}
......
from dataclasses import dataclass
from flask import render_template, jsonify, g, redirect, url_for, flash, request from flask import render_template, jsonify, g, redirect, url_for, flash, request
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
import flask_wtf.file import flask_wtf.file
import hashlib
import hmac
from sqlalchemy import and_ from sqlalchemy import and_
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
from typing import List, Tuple from typing import List, Tuple, Optional
import werkzeug.exceptions import werkzeug.exceptions
import wtforms import wtforms
from wtforms.validators import Required from wtforms.validators import Required
...@@ -342,6 +345,34 @@ class SubmitForm(FlaskForm): ...@@ -342,6 +345,34 @@ class SubmitForm(FlaskForm):
submit = wtforms.SubmitField('Odevzdat') submit = wtforms.SubmitField('Odevzdat')
@dataclass
class CMSParams:
url: str
username: str
first_name: str
last_name: str
timestamp: str
signature: str = ""
back_url: str = ""
def get_cms_params() -> Optional[CMSParams]:
if not (hasattr(config, 'CMS_ROOT') and hasattr(config, 'CMS_SSO_SECRET')):
return None
p = CMSParams(
url=config.CMS_ROOT + 'sso-login',
username=f'osmo{g.user.user_id}',
first_name=g.user.first_name,
last_name=g.user.last_name,
timestamp=str(int(mo.now.timestamp())),
)
msg = ":".join((p.username, p.first_name, p.last_name, p.timestamp)).encode('utf-8')
key = config.CMS_SSO_SECRET.encode('us-ascii')
p.signature = hmac.HMAC(key, msg, digestmod=hashlib.sha256).hexdigest()
return p
@app.route('/user/contest/<int:contest_id>/task/<int:task_id>/', methods=('GET', 'POST')) @app.route('/user/contest/<int:contest_id>/task/<int:task_id>/', methods=('GET', 'POST'))
def user_contest_task(contest_id: int, task_id: int): def user_contest_task(contest_id: int, task_id: int):
contest = get_contest(contest_id) contest = get_contest(contest_id)
...@@ -356,8 +387,13 @@ def user_contest_task(contest_id: int, task_id: int): ...@@ -356,8 +387,13 @@ def user_contest_task(contest_id: int, task_id: int):
# stránku, abychom něco neprozradili jménem úlohy # stránku, abychom něco neprozradili jménem úlohy
raise werkzeug.exceptions.Forbidden() raise werkzeug.exceptions.Forbidden()
form: Optional[SubmitForm] = None
if task.type == db.TaskType.regular:
form = SubmitForm() form = SubmitForm()
if contest.ct_can_submit() and form.validate_on_submit(): if contest.ct_can_submit() and form.validate_on_submit():
if task.type != db.TaskType.regular:
raise werkzeug.exceptions.Forbidden()
file = form.file.data.stream file = form.file.data.stream
paper = db.Paper(task=task, for_user_obj=g.user, uploaded_by_obj=g.user, type=db.PaperType.solution, note=form.note.data) paper = db.Paper(task=task, for_user_obj=g.user, uploaded_by_obj=g.user, type=db.PaperType.solution, note=form.note.data)
submitter = mo.submit.Submitter() submitter = mo.submit.Submitter()
...@@ -391,6 +427,12 @@ def user_contest_task(contest_id: int, task_id: int): ...@@ -391,6 +427,12 @@ def user_contest_task(contest_id: int, task_id: int):
flash('Řešení odevzdáno', 'success') flash('Řešení odevzdáno', 'success')
return redirect(url_for('user_contest', id=contest_id)) return redirect(url_for('user_contest', id=contest_id))
cms_params: Optional[CMSParams] = None
if task.type == db.TaskType.cms:
cms_params = get_cms_params()
if cms_params:
cms_params.back_url = url_for('user_contest_task', contest_id=contest_id, task_id=task_id)
sol = sess.query(db.Solution).filter_by(task=task, user=g.user).one_or_none() sol = sess.query(db.Solution).filter_by(task=task, user=g.user).one_or_none()
papers = (sess.query(db.Paper) papers = (sess.query(db.Paper)
...@@ -406,6 +448,7 @@ def user_contest_task(contest_id: int, task_id: int): ...@@ -406,6 +448,7 @@ def user_contest_task(contest_id: int, task_id: int):
sol=sol, sol=sol,
papers=papers, papers=papers,
form=form, form=form,
cms_params=cms_params,
messages=messages, messages=messages,
) )
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment