Commit 0350caab authored by Robert Czechowski's avatar Robert Czechowski
Browse files

Show grades in contest overview

parent 874dfada
......@@ -28,6 +28,7 @@ pub trait MedalConnection {
fn submit_submission(&self, submission: Submission);
fn get_grade_by_submission(&self, submission_id: u32) -> Grade;
fn get_contest_groups_grades(&self, session_id: u32, contest_id: u32) -> (Vec<String>, Vec<(Group, Vec<(UserInfo, Vec<Grade>)>)>);
fn get_contest_user_grades(&self, session: String, contest_id: u32) -> Vec<Grade>;
fn get_contest_list(&self) -> Vec<Contest>;
fn get_contest_by_id(&self, contest_id: u32) -> Contest;
......
......@@ -389,43 +389,90 @@ impl MedalConnection for Connection {
if let Some(t/*Ok((grade, mut group, mut userinfo))*/) = gradeinfo_iter.next() {
let (grade, mut group, mut userinfo) = t.unwrap();
println!("yes");
let mut grades: Vec<Grade> = vec![Default::default(); n_tasks];
let mut users: Vec<(UserInfo, Vec<Grade>)> = Vec::new();
let mut groups: Vec<(Group, Vec<(UserInfo, Vec<Grade>)>)> = Vec::new();
let index = grade.taskgroup;
grades[taskindex[&index]] = grade;
// TODO: does
// https://stackoverflow.com/questions/29859892/mutating-an-item-inside-of-nested-loops
// help to spare all these clones?
for ggu in gradeinfo_iter {
if let Ok((g, gr, ui)) = ggu {
if gr.id != group.id {
users.push((userinfo.clone(), grades));
grades = vec![Default::default(); n_tasks];
groups.push((group.clone(), users));
users = Vec::new();
}
else if ui.id != userinfo.id {
users.push((userinfo.clone(), grades));
grades = vec![Default::default(); n_tasks];
let mut grades: Vec<Grade> = vec![Default::default(); n_tasks];
let mut users: Vec<(UserInfo, Vec<Grade>)> = Vec::new();
let mut groups: Vec<(Group, Vec<(UserInfo, Vec<Grade>)>)> = Vec::new();
let index = grade.taskgroup;
grades[taskindex[&index]] = grade;
// TODO: does
// https://stackoverflow.com/questions/29859892/mutating-an-item-inside-of-nested-loops
// help to spare all these clones?
for ggu in gradeinfo_iter {
if let Ok((g, gr, ui)) = ggu {
if gr.id != group.id {
users.push((userinfo.clone(), grades));
grades = vec![Default::default(); n_tasks];
groups.push((group.clone(), users));
users = Vec::new();
}
else if ui.id != userinfo.id {
users.push((userinfo.clone(), grades));
grades = vec![Default::default(); n_tasks];
}
let index = g.taskgroup;
grades[taskindex[&index]] = g;
}
let index = g.taskgroup;
grades[taskindex[&index]] = g;
}
}
users.push((userinfo, grades));
groups.push((group, users));
users.push((userinfo, grades));
groups.push((group, users));
(tasknames.iter().map(|(_, name)| name.clone()).collect(), groups)
}
else {
println!("no");
(Vec::new(), Vec::new())
(Vec::new(), Vec::new()) // should those be default filled?
}
}
fn get_contest_user_grades(&self, session_token: String, contest_id: u32) -> Vec<Grade> {
let mut stmt = self.prepare("SELECT id, name FROM taskgroup WHERE contest = ?1 ORDER BY id ASC").unwrap();
let mut tasknames_iter = stmt.query_map(&[&contest_id], |row| {
let x : (u32, String) = (row.get(0), row.get(1));
x
}).unwrap();
let tasknames : Vec<(u32, String)> = tasknames_iter.map(|x| x.unwrap()).collect();
let mut taskindex: ::std::collections::BTreeMap<u32, usize> = ::std::collections::BTreeMap::new();
let n_tasks = tasknames.len();
let mut index = 0;
for (i, _) in &tasknames {
taskindex.insert(*i, index);
index = index + 1
}
let mut stmt = self.prepare("SELECT grade.taskgroup, grade.user, grade.grade, grade.validated
FROM grade
JOIN taskgroup ON grade.taskgroup = taskgroup.id
JOIN session_user ON session_user.id = grade.user
WHERE session_user.session_token = ?1 AND taskgroup.contest = ?2
ORDER BY taskgroup.id ASC").unwrap();
let mut gradeinfo_iter = stmt.query_map(&[&session_token, &contest_id], |row| {
Grade {
taskgroup: row.get(0),
user: row.get(1),
grade: row.get(2),
validated: row.get(3),
}
}).unwrap();
let mut grades: Vec<Grade> = vec![Default::default(); n_tasks];
for g in gradeinfo_iter {
let g = g.unwrap();
let index = g.taskgroup;
grades[taskindex[&index]] = g;
}
grades
/*else {
println!("no");
Vec::new()
}*/
}
fn get_contest_list(&self) -> Vec<Contest> {
......@@ -522,7 +569,7 @@ impl MedalConnection for Connection {
SELECT ?1, id, ?2 FROM session_user WHERE session_token = ?3",
&[&contest_id, &now, &session]).unwrap();
Ok(self.get_participation(session, contest_id).unwrap())
Ok(self.get_participation(session, contest_id).unwrap()) // TODO: This errors if not logged in …
}
}
......
......@@ -101,12 +101,23 @@ pub fn show_contests<T: MedalConnection>(conn: &T) -> MedalValue {
pub fn show_contest<T: MedalConnection>(conn: &T, contest_id: u32, session_token: String) -> MedalValueResult {
let c = conn.get_contest_by_id_complete(contest_id);
let grades = conn.get_contest_user_grades(session_token.clone(), contest_id);
// TODO: Clean up star generation
let mut tasks = Vec::new();
for task in c.taskgroups {
for (task, grade) in c.taskgroups.into_iter().zip(grades) {
let mut not_print_yet = true;
let mut blackstars :usize = 0;
let mut stasks = Vec::new();
for st in task.tasks {
stasks.push(SubTaskInfo{id: st.id.unwrap(), linktext: str::repeat("☆", st.stars as usize)})
blackstars = 0;
if not_print_yet && st.stars >= grade.grade.unwrap_or(0) {
blackstars = grade.grade.unwrap_or(0) as usize;
not_print_yet = false;
}
stasks.push(SubTaskInfo{id: st.id.unwrap(), linktext: format!("{}{}", str::repeat("★", blackstars as usize),str::repeat("☆", st.stars as usize - blackstars as usize))})
}
let mut ti = TaskInfo {name: task.name,
subtasks: stasks};
......@@ -126,6 +137,7 @@ pub fn show_contest<T: MedalConnection>(conn: &T, contest_id: u32, session_token
let mut data = json_val::Map::new();
data.insert("contest".to_string(), to_json(&ci));
data.insert("logged_in".to_string(), to_json(&false));
if let Some(session) = conn.get_session(session_token.clone()) { // TODO: Work with string slices here
data.insert("logged_in".to_string(), to_json(&true));
data.insert("username".to_string(), to_json(&session.username));
......@@ -273,16 +285,16 @@ pub fn logout<T: MedalConnection>(conn: &T, session_token: Option<String>) -> ()
}
pub fn load_submission<T: MedalConnection>(conn: &T, task_id: u32, session_token: String) -> MedalResult<String> {
pub fn load_submission<T: MedalConnection>(conn: &T, task_id: u32, session_token: String, subtask: Option<String>) -> MedalResult<String> {
let session = conn.get_session(session_token).ok_or(MedalError::AccessDenied)?.ensure_alive().ok_or(MedalError::AccessDenied)?; // TODO SessionTimeout
match conn.load_submission(&session, task_id, None) {
match conn.load_submission(&session, task_id, subtask) {
Some(submission) => Ok(submission.value),
None => Ok("{}".to_string())
}
}
pub fn save_submission<T: MedalConnection>(conn: &T, task_id: u32, session_token: String, csrf_token: String, data: String, grade: u8) -> MedalResult<String> {
pub fn save_submission<T: MedalConnection>(conn: &T, task_id: u32, session_token: String, csrf_token: String, data: String, grade: u8, subtask: Option<String>) -> MedalResult<String> {
let session = conn.get_session(session_token).ok_or(MedalError::AccessDenied)?.ensure_alive().ok_or(MedalError::AccessDenied)?; // TODO SessionTimeout
if session.csrf_token != csrf_token {
......@@ -297,7 +309,7 @@ pub fn save_submission<T: MedalConnection>(conn: &T, task_id: u32, session_token
validated: false,
nonvalidated_grade: grade,
needs_validation: true,
subtask_identifier: None,
subtask_identifier: subtask,
value: data,
date: time::get_time()
};
......
......@@ -16,7 +16,7 @@ use staticfile::Static;
use iron_sessionstorage::SessionStorage;
use iron_sessionstorage::backends::SignedCookieBackend;
use rusqlite::Connection;
use urlencoded::{UrlEncodedBody};
use urlencoded::{UrlEncodedBody, UrlEncodedQuery};
use persistent::Write;
use handlebars_iron::{HandlebarsEngine,DirectorySource,Template};
......@@ -101,12 +101,12 @@ trait RequestSession {
impl<'a, 'b> RequestSession for Request<'a, 'b> {
fn get_session_token(self: &mut Self) -> Option<String> {
let session_token = SessionRequestExt::session(self).get::<SessionToken>().unwrap();
let session_token = self.session().get::<SessionToken>().unwrap();
(|st: Option<SessionToken>| -> Option<String> {Some(st?.token)}) (session_token)
}
fn require_session_token(self: &mut Self) -> IronResult<String> {
match SessionRequestExt::session(self).get::<SessionToken>().unwrap() {
match self.session().get::<SessionToken>().unwrap() {
Some(SessionToken { token: session }) => Ok(session),
_ => {
......@@ -121,7 +121,7 @@ impl<'a, 'b> RequestSession for Request<'a, 'b> {
}
fn expect_session_token(self: &mut Self) -> IronResult<String> {
match SessionRequestExt::session(self).get::<SessionToken>().unwrap() {
match self.session().get::<SessionToken>().unwrap() {
Some(SessionToken { token: session }) => Ok(session),
_ => {
Err(IronError { error: Box::new(SessionError { message: "No valid session found, access denied".to_string() }),
......@@ -413,13 +413,15 @@ fn logout(req: &mut Request) -> IronResult<Response> {
fn submission(req: &mut Request) -> IronResult<Response> {
let task_id = req.expect_int::<u32>("taskid")?;
let session_token = req.expect_session_token()?;
let subtask : Option<String> =
(|| -> Option<String> {req.get_ref::<UrlEncodedQuery>().ok()?.get("subtask")?.get(0).map(|x| x.to_owned())})();
println!("{}",task_id);
let result = {
let mutex = req.get::<Write<SharedDatabaseConnection>>().unwrap();
let conn = mutex.lock().unwrap_or_else(|e| e.into_inner());
functions::load_submission(&*conn, task_id, session_token)
functions::load_submission(&*conn, task_id, session_token, subtask)
};
match result {
......@@ -437,20 +439,22 @@ fn submission(req: &mut Request) -> IronResult<Response> {
fn submission_post(req: &mut Request) -> IronResult<Response> {
let task_id = req.expect_int::<u32>("taskid")?;
let session_token = req.expect_session_token()?;
let (csrf_token, data, grade) = {
let (csrf_token, data, grade, subtask) = {
let formdata = iexpect!(req.get_ref::<UrlEncodedBody>().ok());
(iexpect!(formdata.get("csrf"),(status::BadRequest, mime!(Text/Html), format!("400 Bad Request")))[0].to_owned(),
iexpect!(formdata.get("data"),(status::BadRequest, mime!(Text/Html), format!("400 Bad Request")))[0].to_owned(),
iexpect!(formdata.get("grade").unwrap_or(&vec!["0".to_owned()])[0].parse::<u8>().ok(),(status::BadRequest, mime!(Text/Html), format!("400 Bad Request"))),
formdata.get("subtask").map(|x| x[0].to_owned()),
)
};
println!("{}",data);
println!("{}",task_id);
println!("{}",grade);
let result = {
let mutex = req.get::<Write<SharedDatabaseConnection>>().unwrap();
let conn = mutex.lock().unwrap_or_else(|e| e.into_inner());
functions::save_submission(&*conn, task_id, session_token, csrf_token, data, grade)
functions::save_submission(&*conn, task_id, session_token, csrf_token, data, grade, subtask)
};
match result {
......@@ -859,6 +863,7 @@ pub fn start_server(conn: Connection, config: ::Config) -> iron::error::HttpResu
ch.link(Write::<SharedConfiguration>::both(config.clone()));
ch.link_around(SessionStorage::new(SignedCookieBackend::new(my_secret)));
ch.link_after(get_handlebars_engine());
ch.link_after(ErrorReporter);
......
......@@ -31,6 +31,7 @@
{{else}}
{{#if logged_in}}
<p>
<form action="" method="post">
<input type="hidden" name="csrftoken" value="{{csrftoken}}">
......@@ -39,6 +40,7 @@
</p>
<p>Der Wettbewerb kann nach dem starten nicht mehr pausiert werden.</p>
{{/if}}
{{/if}}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment