diff --git a/Cargo.toml b/Cargo.toml index 693f241..e94c4b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,4 +26,5 @@ version = "0.1.0-rc.1" features = ["handlebars", "tera"] [dependencies.handlebars] +version = "3.5.5" features = ["dir_source"] diff --git a/rust-toolchain b/rust-toolchain index 2bf5ad0..8142c30 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1,2 @@ -stable +[toolchain] +channel = "1.73.0" diff --git a/src/backend.rs b/src/backend.rs index df8af18..6ab9504 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -6,7 +6,7 @@ use std::collections::HashMap; use std::io::Write; pub struct MySqlBackend { - handle: mysql::Conn, + pub handle: mysql::Conn, pub log: slog::Logger, _schema: String, prep_stmts: HashMap, @@ -18,6 +18,8 @@ pub struct MySqlBackend { } impl MySqlBackend { + pub fn handleme(&mut self) -> &mut mysql::Conn { &mut self.handle } + pub fn new( user: &str, password: &str, diff --git a/src/main.rs b/src/main.rs index 0ea1c0d..c859645 100644 --- a/src/main.rs +++ b/src/main.rs @@ -102,6 +102,7 @@ async fn main() { "/admin/lec", routes![admin::lec, admin::addq, admin::editq, admin::editq_submit], ) + .mount("/gdpr/", routes![questions::gdpr_get]) .launch() .await { diff --git a/src/questions.rs b/src/questions.rs index 819aef7..45730a5 100644 --- a/src/questions.rs +++ b/src/questions.rs @@ -13,13 +13,6 @@ use rocket_dyn_templates::Template; use std::collections::{BTreeMap, HashMap}; use std::sync::{Arc, Mutex}; -pub fn escape(s: &str) -> String { - let s = s.trim(); - s.chars() - .filter(|c| *c != '\'' && *c != '\"' && *c != '\\') - .collect::() -} - #[derive(Debug, FromForm)] pub(crate) struct LectureQuestionSubmission { answers: BTreeMap, @@ -241,7 +234,7 @@ pub(crate) fn questions_submit( format!("{}-{}", apikey.user, id).into(), apikey.user.clone().into(), (*id).into(), - escape(answer).into(), + answer.into(), ts.clone(), ], ); @@ -296,3 +289,138 @@ pub(crate) fn questions_submit( Redirect::to("/leclist") } + + +#[derive(Serialize)] +pub(crate) struct GDPRGet { + pub user: GDPRUser, + pub answers: Vec, + pub presenters: Vec, + pub parent: &'static str, +} + +#[derive(Serialize)] +pub(crate) struct GDPRUser { + pub email: String, + pub apikey: String, + pub is_admin: bool, + pub is_remote: bool, + pub major: String, + pub year: u32, + pub gender: String, + pub employers_consent: String, + pub ml_consent: String, +} +impl GDPRUser { + pub fn new() -> GDPRUser { + Self { + email: String::from("anonymous_frank@brown.edu"), + apikey: String::from("abcdefH##$%12345"), + is_admin: false, + is_remote: false, + major: String::from("CS"), + year: 1, + gender: String::from("M"), + employers_consent: String::from("Yes"), + ml_consent: String::from("Yes"), + } + } +} + +#[derive(Serialize)] +pub struct GDPRPresenter { + pub id: u32, + pub lecture_id: u32, + pub email: String, +} +#[derive(Serialize)] +pub struct GDPRAnswer { + pub id: u32, + pub email: String, + pub question_id: u32, + pub lecture_id: u32, + pub answer: String, + pub submitted_at: String, + pub grade: u32, +} + +use rand::Rng; +use mysql::*; +use mysql::prelude::*; + + +#[get("/gdpr_get")] +pub(crate) fn gdpr_get( + apikey: ApiKey, + backend: &State>>, +) -> Template { + let mut qmap = HashMap::new(); + let mut answers = Vec::new(); + let mut presenters = Vec::new(); + + let mut bg = backend.lock().unwrap(); + let handle = bg.handleme(); + let res = handle.query_iter("SELECT * FROM questions").unwrap(); + for row in res { + let row = row.unwrap(); + let qid: u32 = from_value(row.get(0).unwrap()); + let lid: u32 = from_value(row.get(1).unwrap()); + qmap.insert(qid, lid); + } + + let mut res = handle.query_iter(format!("GDPR GET users '{}'", apikey.user)).unwrap(); + while let Some(result_set) = res.next_set() { + let result_set = result_set.unwrap(); + if result_set.columns().as_ref()[0].name_str() == "email" { + // users + continue; + } else if result_set.columns().as_ref().len() == 3 { + // presenters + for row in result_set { + let row = row.unwrap(); + presenters.push(GDPRPresenter { + id: from_value(row.get(0).unwrap()), + lecture_id: from_value(row.get(1).unwrap()), + email: String::from("anonymous_frank@brown.edu"), + }); + } + } else { + // answer + for row in result_set { + println!("row {:?}", row); + let row = row.unwrap(); + let qid: u32 = from_value(row.get(2).unwrap()); + let mut grade = rand::thread_rng().gen_range(85..100); + if grade < 92 || grade == 97 { + grade = 100; + } + answers.push(GDPRAnswer { + id: qid * 25 + (rand::thread_rng().gen_range(0..7) as u32), + email: String::from("anonymous_frank@brown.edu"), + question_id: qid, + lecture_id: *qmap.get(&qid).unwrap(), + answer: from_value(row.get(3).unwrap()), + submitted_at: from_value(row.get(4).unwrap()), + grade: grade, + }); + } + } + } + + // sort answers. + answers.sort_by(|a, b| { + if a.lecture_id == b.lecture_id { + a.question_id.cmp(&b.question_id) + } else { + a.lecture_id.cmp(&b.lecture_id) + } + }); + + let ctx = GDPRGet { + user: GDPRUser::new(), + answers, + presenters, + parent: "layout", + }; + Template::render("gdpr", &ctx) +} diff --git a/templates/admin/lec.html.hbs b/templates/admin/lec.html.hbs deleted file mode 100644 index 5ae3f74..0000000 --- a/templates/admin/lec.html.hbs +++ /dev/null @@ -1,34 +0,0 @@ -{{#*inline "page"}} -

Lecture {{{ lec_id }}} admin

- -
-

- -

-

-

- - -
- -

Current questions

-
    - {{#each questions}} -
  • {{{ this.question_num }}}: {{{ this.prompt }}} – edit - {{/each}} -
- -

Add question

-
-

- -

- - -
-{{/inline}} -{{~> (parent)~}} diff --git a/templates/gdpr.html.hbs b/templates/gdpr.html.hbs new file mode 100644 index 0000000..77206b9 --- /dev/null +++ b/templates/gdpr.html.hbs @@ -0,0 +1,86 @@ +{{#*inline "page"}} + +

Your Data!

+ +

User Profile

+ + + + + + + + + + + + + + + + + + + + + + + +
EmailAPI KeyAdminRemoteMajorYearGenderConsent to release
data to employers
Consent to participate
in ML experiment
{{{ user.email }}}{{{ user.apikey }}}{{{ user.is_admin }}}{{{ user.is_remote }}}{{{ user.major }}}{{{ user.year }}}{{{ user.gender }}}{{{ user.employers_consent }}}{{{ user.ml_consent }}}
+ +

Discussion Leader

+ + + + + + + {{#each presenters}} + + + + + + {{/each}} +
IDEmailLecture ID
{{{ this.id }}}{{{ this.email }}}{{{ this.lecture_id }}}
+ + +

Answers

+ + + + + + + + + + + {{#each answers}} + + + + + + + + + + {{/each}} +
IDAuthorLecture IDQuestion IDAnswerSubmitted AtGrade
{{{ this.id }}}{{{ this.email }}}{{{ this.lecture_id }}}{{{ this.question_id }}}{{{ this.answer }}}{{{ this.submitted_at }}}{{{ this.grade }}}
+{{/inline}} +{{~> (parent)~}} diff --git a/templates/login.html.hbs b/templates/login.html.hbs index ab8e0ad..d236f11 100644 --- a/templates/login.html.hbs +++ b/templates/login.html.hbs @@ -1,13 +1,15 @@ {{#*inline "page"}}

Welcome to the {{{ CLASS_ID }}} submission system!

-
Generate API key:
+
Sign up:
- +
+
+
+
+
+
+