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
# Aktuální ročník MO
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)
app.jinja_env.globals.update(PartState=db.PartState)
app.jinja_env.globals.update(RoleType=db.RoleType)
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(JobState=db.JobState)
......
......@@ -23,6 +23,7 @@
{% if state == RoundState.running %}
{% if contest.ct_can_submit() %}
<h3>Odevzdat řešení</h3>
{% if task.type == TaskType.regular %}
{% 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 }}.
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 @@
nahradili (např. nahráli jste omylem řešení jiné úlohy).
{% endif %}
{{ 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 %}
<p>Již není možné odevzdat řešení, termín na odevzdávání vypršel.</p>
{% endif %}
......@@ -57,7 +74,7 @@
<p>Soutěž se nachází v neznámém stavu. To by se nemělo stát :)
{% 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>
{% if papers %}
......
from dataclasses import dataclass
from flask import render_template, jsonify, g, redirect, url_for, flash, request
from flask_wtf import FlaskForm
import flask_wtf.file
import hashlib
import hmac
from sqlalchemy import and_
from sqlalchemy.orm import joinedload
from typing import List, Tuple
from typing import List, Tuple, Optional
import werkzeug.exceptions
import wtforms
from wtforms.validators import Required
......@@ -342,6 +345,34 @@ class SubmitForm(FlaskForm):
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'))
def user_contest_task(contest_id: int, task_id: int):
contest = get_contest(contest_id)
......@@ -356,8 +387,13 @@ def user_contest_task(contest_id: int, task_id: int):
# stránku, abychom něco neprozradili jménem úlohy
raise werkzeug.exceptions.Forbidden()
form: Optional[SubmitForm] = None
if task.type == db.TaskType.regular:
form = SubmitForm()
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
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()
......@@ -391,6 +427,12 @@ def user_contest_task(contest_id: int, task_id: int):
flash('Řešení odevzdáno', 'success')
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()
papers = (sess.query(db.Paper)
......@@ -406,6 +448,7 @@ def user_contest_task(contest_id: int, task_id: int):
sol=sol,
papers=papers,
form=form,
cms_params=cms_params,
messages=messages,
)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment