Commit 46f94141 authored by Robert Czechowski's avatar Robert Czechowski

Add cleanup function that deletes unreferenced participation data (Closes #131)

parent 7e85d3fe
...@@ -1598,6 +1598,28 @@ pub fn admin_do_cleanup<T: MedalConnection>(conn: &T, session_token: &str, csrf_ ...@@ -1598,6 +1598,28 @@ pub fn admin_do_cleanup<T: MedalConnection>(conn: &T, session_token: &str, csrf_
} }
} }
pub fn admin_do_soft_cleanup<T: MedalConnection>(conn: &T, session_token: &str, csrf_token: &str) -> MedalValueResult {
let session = conn.get_session(&session_token)
.ensure_logged_in()
.ok_or(MedalError::NotLoggedIn)?
.ensure_admin()
.ok_or(MedalError::AccessDenied)?;
if session.csrf_token != csrf_token {
return Err(MedalError::CsrfCheckFailed);
}
let result = conn.remove_unreferenced_participation_data();
let mut data = json_val::Map::new();
if let Ok(()) = result {
Ok(("delete_ok".to_string(), data))
} else {
data.insert("reason".to_string(), to_json(&"Fehler."));
Ok(("delete_fail".to_string(), data))
}
}
#[derive(PartialEq, Clone, Copy)] #[derive(PartialEq, Clone, Copy)]
pub enum UserType { pub enum UserType {
User, User,
......
...@@ -1620,6 +1620,36 @@ impl MedalConnection for Connection { ...@@ -1620,6 +1620,36 @@ impl MedalConnection for Connection {
Ok((n_users, n_groups, n_teachers, n_other)) Ok((n_users, n_groups, n_teachers, n_other))
} }
fn remove_unreferenced_participation_data(&self) -> Result<(), ()> {
// We use
// DELETE FROM submission WHERE session NOT IN (SELECT id FROM session);
// which works on Postgres as well es on Sqlite
// However, Postgres also allows
// DELETE FROM submission WHERE NOT EXISTS (SELECT FROM session WHERE session.id = submission.session);
// and the latter might be faster on Postgres, so if we ever feel the need to speed up this function,
// it should be split up in database specific code
let query = "DELETE
FROM submission
WHERE session NOT IN (SELECT id
FROM session)";
self.execute(query, &[]).unwrap();
let query = "DELETE
FROM participation
WHERE session NOT IN (SELECT id
FROM session)";
self.execute(query, &[]).unwrap();
let query = "DELETE
FROM grade
WHERE session NOT IN (SELECT id
FROM session)";
self.execute(query, &[]).unwrap();
Ok(())
}
fn get_debug_information(&self) -> String { fn get_debug_information(&self) -> String {
let now = time::get_time(); let now = time::get_time();
let cache_key = "dbstatus"; let cache_key = "dbstatus";
......
...@@ -149,6 +149,7 @@ pub trait MedalConnection { ...@@ -149,6 +149,7 @@ pub trait MedalConnection {
fn remove_old_users_and_groups(&self, maxstudentage: time::Timespec, maxteacherage: Option<time::Timespec>, fn remove_old_users_and_groups(&self, maxstudentage: time::Timespec, maxteacherage: Option<time::Timespec>,
maxage: Option<time::Timespec>) maxage: Option<time::Timespec>)
-> Result<(i32, i32, i32, i32), ()>; -> Result<(i32, i32, i32, i32), ()>;
fn remove_unreferenced_participation_data(&self) -> Result<(), ()>;
fn get_search_users( fn get_search_users(
&self, _: (Option<i32>, Option<String>, Option<String>, Option<String>, Option<String>, Option<String>)) &self, _: (Option<i32>, Option<String>, Option<String>, Option<String>, Option<String>, Option<String>))
......
...@@ -1739,6 +1739,36 @@ impl MedalConnection for Connection { ...@@ -1739,6 +1739,36 @@ impl MedalConnection for Connection {
Ok((n_users, n_groups, n_teachers, n_other)) Ok((n_users, n_groups, n_teachers, n_other))
} }
fn remove_unreferenced_participation_data(&self) -> Result<(), ()> {
// We use
// DELETE FROM submission WHERE session NOT IN (SELECT id FROM session);
// which works on Postgres as well es on Sqlite
// However, Postgres also allows
// DELETE FROM submission WHERE NOT EXISTS (SELECT FROM session WHERE session.id = submission.session);
// and the latter might be faster on Postgres, so if we ever feel the need to speed up this function,
// it should be split up in database specific code
let query = "DELETE
FROM submission
WHERE session NOT IN (SELECT id
FROM session)";
self.execute(query, &[]).unwrap();
let query = "DELETE
FROM participation
WHERE session NOT IN (SELECT id
FROM session)";
self.execute(query, &[]).unwrap();
let query = "DELETE
FROM grade
WHERE session NOT IN (SELECT id
FROM session)";
self.execute(query, &[]).unwrap();
Ok(())
}
fn get_debug_information(&self) -> String { fn get_debug_information(&self) -> String {
let now = time::get_time(); let now = time::get_time();
let cache_key = "dbstatus"; let cache_key = "dbstatus";
......
...@@ -1739,6 +1739,36 @@ impl MedalConnection for Connection { ...@@ -1739,6 +1739,36 @@ impl MedalConnection for Connection {
Ok((n_users, n_groups, n_teachers, n_other)) Ok((n_users, n_groups, n_teachers, n_other))
} }
fn remove_unreferenced_participation_data(&self) -> Result<(), ()> {
// We use
// DELETE FROM submission WHERE session NOT IN (SELECT id FROM session);
// which works on Postgres as well es on Sqlite
// However, Postgres also allows
// DELETE FROM submission WHERE NOT EXISTS (SELECT FROM session WHERE session.id = submission.session);
// and the latter might be faster on Postgres, so if we ever feel the need to speed up this function,
// it should be split up in database specific code
let query = "DELETE
FROM submission
WHERE session NOT IN (SELECT id
FROM session)";
self.execute(query, &[]).unwrap();
let query = "DELETE
FROM participation
WHERE session NOT IN (SELECT id
FROM session)";
self.execute(query, &[]).unwrap();
let query = "DELETE
FROM grade
WHERE session NOT IN (SELECT id
FROM session)";
self.execute(query, &[]).unwrap();
Ok(())
}
fn get_debug_information(&self) -> String { fn get_debug_information(&self) -> String {
let now = time::get_time(); let now = time::get_time();
let cache_key = "dbstatus"; let cache_key = "dbstatus";
......
...@@ -1125,7 +1125,12 @@ fn admin_cleanup<C>(req: &mut Request) -> IronResult<Response> ...@@ -1125,7 +1125,12 @@ fn admin_cleanup<C>(req: &mut Request) -> IronResult<Response>
}; };
let (template, data) = if let Some(csrf_token) = csrf_token { let (template, data) = if let Some(csrf_token) = csrf_token {
with_conn![core::admin_do_cleanup, C, req, &session_token, &csrf_token].aug(req)? let cleanup_type = req.get_str("type");
match cleanup_type.as_deref() {
Some("soft") => with_conn![core::admin_do_soft_cleanup, C, req, &session_token, &csrf_token].aug(req)?,
_ => with_conn![core::admin_do_cleanup, C, req, &session_token, &csrf_token].aug(req)?,
}
} else { } else {
with_conn![core::admin_show_cleanup, C, req, &session_token].aug(req)? with_conn![core::admin_show_cleanup, C, req, &session_token].aug(req)?
}; };
...@@ -1505,7 +1510,7 @@ pub fn start_server<C>(conn: C, config: Config) -> iron::error::HttpResult<iron: ...@@ -1505,7 +1510,7 @@ pub fn start_server<C>(conn: C, config: Config) -> iron::error::HttpResult<iron:
admin_contests: get "/admin/contest/" => admin_contests::<C>, admin_contests: get "/admin/contest/" => admin_contests::<C>,
admin_export_contest: get "/admin/contest/:contestid/export" => admin_export_contest::<C>, admin_export_contest: get "/admin/contest/:contestid/export" => admin_export_contest::<C>,
admin_cleanup: get "/admin/cleanup" => admin_cleanup::<C>, admin_cleanup: get "/admin/cleanup" => admin_cleanup::<C>,
admin_cleanup_post: post "/admin/cleanup" => admin_cleanup::<C>, admin_cleanup_post: post "/admin/cleanup/:type" => admin_cleanup::<C>,
oauth: get "/oauth/:oauthid/" => oauth::<C>, oauth: get "/oauth/:oauthid/" => oauth::<C>,
oauth_school: get "/oauth/:oauthid/:schoolid" => oauth::<C>, oauth_school: get "/oauth/:oauthid/:schoolid" => oauth::<C>,
check_cookie: get "/cookie" => cookie_warning, check_cookie: get "/cookie" => cookie_warning,
......
<h1>Administration</h1> <h1>Administration</h1>
<h2>Alte Daten löschen</h2> <h2>Alte Daten löschen</h2>
<p>Dies Löscht alle verwalteten Schüleraccounts, die länger als 180 Tage nicht genutzt wurden, sowie alle Lehreraccounts länger als 3 Jahre und alle anderen Accounts die länger als 10 Jahre nicht genutzt wurden.</p> <p>Dies Löscht alle verwalteten Schüleraccounts, die länger als 180 Tage nicht genutzt wurden, sowie alle Lehreraccounts länger als 3 Jahre und alle anderen Accounts die länger als 10 Jahre nicht genutzt wurden.</p>
<p>Sollte nicht während eines Wettbewerbes oder nach Beginn der Anmeldephase ausgeführt werden, falls ein Lehrer noch plant ältere Accounts zu verwenden. Bestenfalls vor Anmeldebeginn und nach Ende der zweiten Runde ausführen.</p>
<p>
<form action="cleanup/hard" method="post">
<input type="hidden" name="csrf_token" value="{{csrf_token}}">
<input type="submit" value="Alte Daten löschen!">
</form>
</p>
<h2>Verwaiste Teilnahmen löschen</h2>
<p>Löscht Teilnahmen, deren Accounts gelöscht wurden.</p>
<p>Kann jederzeit gefahrlos ausgeführt werden</p>
<p> <p>
<form action="" method="post"> <form action="cleanup/soft" method="post">
<input type="hidden" name="csrf_token" value="{{csrf_token}}"> <input type="hidden" name="csrf_token" value="{{csrf_token}}">
<input type="submit" value="Alte Daten löschen!"> <input type="submit" value="Alte Daten löschen!">
</form> </form>
......
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