Browse Source

Added example database data, several improvements

integrate_codes_to_fields
anghenfil 2 years ago
parent
commit
66ffc5fa44
  1. 121
      data/database.save
  2. 28
      src/bin/cli.rs
  3. 17
      src/database/code_management.rs
  4. 1
      src/database/mod.rs
  5. 18
      src/db_system/code_management.rs
  6. 11
      src/db_system/document.rs
  7. 3
      src/db_system/mod.rs
  8. 27
      src/db_system/storage.rs
  9. 9
      src/main.rs
  10. 3
      src/webserver/delivery.rs
  11. 10
      src/webserver/session_manager.rs
  12. 40
      src/webserver/site_poll_get.rs
  13. 40
      src/webserver/site_poll_post.rs

121
data/database.save

@ -0,0 +1,121 @@
{
"CrYYvV1jPb1IMIvdp1yw": {
"fields": {
"qbc": {
"QuestionBlockContainer": {
"id": 1,
"polltype": 1,
"active": true,
"active_from": 0,
"active_to": 1552176000,
"questionblocks": [
{
"title": "Erster Block",
"description": "Das ist der erste Block",
"questions": [
{
"identifier": "1.1",
"question": "Wie geht es dir",
"optional": false,
"questiontype": 10,
},
{
"identifier": "1.2",
"question": "Wie geht es dir 2",
"optional": false,
"questiontype": 10,
}
]
},
{
"title": "Zweiter Block",
"description": "Das ist der zweite Block",
"questions": [
{
"identifier": "2.1",
"question": "Wie geht es dir",
"optional": true,
"questiontype": 20,
"selection_options": []
},
{
"identifier": "2.2",
"question": "Wie geht es dir 2",
"optional": false,
"questiontype": 10,
"selection_options": []
}
]
}
]
}
}
},
"codes" : [
"ABCDE",
"FGHIJ",
"KLMNO",
"PQRST",
"PQRST",
"UVWXYZ"
]
},
"AbYYvV1jPb1IMIvdp1yw": {
"fields": {
"qbc": {
"QuestionBlockContainer": {
"id": 1,
"polltype": 1,
"active": true,
"active_from": 0,
"active_to": 1552176000,
"questionblocks": [
{
"title": "Erster Block",
"description": "Das ist der erste Block",
"questions": [
{
"identifier": "1.1",
"question": "Wie geht es dir",
"optional": false,
"questiontype": 10,
"selection_options": []
},
{
"identifier": "1.2",
"question": "Wie geht es dir 2",
"optional": false,
"questiontype": 10,
"selection_options": []
}
]
},
{
"title": "Zweiter Block",
"description": "Das ist der zweite Block",
"questions": [
{
"identifier": "2.1",
"question": "Wie geht es dir",
"optional": true,
"questiontype": 20,
"selection_options": []
},
{
"identifier": "2.2",
"question": "Wie geht es dir 2",
"optional": false,
"questiontype": 10,
"selection_options": []
}
]
}
]
}
}
},
"codes" : [
"TEST1"
]
}
}

28
src/bin/cli.rs

@ -0,0 +1,28 @@
extern crate serde_json;
use std::io;
fn main(){
println!("Welcome to Surveyz command line interface. Enter help for a list of commands");
loop {
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
let input = input.trim();
let mut splitted = input.split_whitespace();
let mut first = match splitted.nth(0){
Some(first) => first,
None => {
println!("Invalid input. Please try again or enter help for a list of commands.");
continue;
}
};
match first {
"help" => println!("I will help you. Commands available: \n import <filename> imports file to new survey."),
"import" => println!(""),
_ => println!("Sorry, I couldn't recognize your input.")
}
}
}

17
src/database/code_management.rs

@ -1,17 +0,0 @@
use std::sync::RwLock;
use std::collections::HashMap;
use rocket::response::{status, content};
use rocket::http::{Status, RawStr};
use webserver::templates;
lazy_static! {
//HashMap<Code, PollID>
pub static ref CODE_MANAGER: RwLock<HashMap<String, String>> = RwLock::new(HashMap::new());
}
pub fn code_handling(code : &RawStr) -> Result<String, status::Custom<content::Html<String>>>{
match CODE_MANAGER.read().unwrap().get(&code.to_string()) {
Some(pollid) => Ok(pollid.clone()),
None => Err(status::Custom(Status::NotFound, content::Html(templates::get_error("Error 404: Invalid Code", "The Code you entered is invalid!", vec!("style/bootstrap.css".to_string(), "style/global.css".to_string(), "style/poll.css".to_string()), Vec::new())))),
}
}

1
src/database/mod.rs

@ -1,4 +1,3 @@
pub mod code_management;
pub mod tests;
use serde::{Deserialize, Serialize};

18
src/db_system/code_management.rs

@ -0,0 +1,18 @@
use std::sync::RwLock;
use std::collections::HashMap;
use rocket::response::{status, content};
use rocket::http::{Status, RawStr};
use webserver::templates;
use db_system;
lazy_static! {
//HashMap<Code, PollID>
pub static ref CODE_MANAGER: RwLock<HashMap<String, String>> = RwLock::new(db_system::storage::DOCUMENTS.write().unwrap().load_codes());
}
pub fn check_code(code : &str) -> Option<String>{
match CODE_MANAGER.read().unwrap().get(code) {
Some(code) => Some(code.clone()),
None => None
}
}

11
src/db_system/document.rs

@ -5,6 +5,7 @@ use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use std::result::Iter;
use database::QuestionBlockContainer;
use std::vec::Vec;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum DbType {
@ -21,13 +22,15 @@ pub enum DbType {
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Document{
fields : HashMap<String, DbType>
fields : HashMap<String, DbType>,
codes : Vec<String>
}
impl Document{
pub fn new() -> Document{
Document{
fields: HashMap::new()
fields: HashMap::new(),
codes: Vec::new()
}
}
@ -98,6 +101,10 @@ impl Document{
}
}
pub fn get_codes(&self) -> &Vec<String>{
&self.codes
}
pub fn get_doc(&self, key : &str) -> Option<&Document>{
match self.fields.get(key)?{
DbType::Document(value) => Some(value),

3
src/db_system/mod.rs

@ -1,3 +1,4 @@
pub mod document; //data storage to save data
pub(crate) mod storage;
pub mod error;
pub mod error;
pub mod code_management;

27
src/db_system/storage.rs

@ -9,6 +9,7 @@ use std::io::{BufWriter, BufReader, Error, ErrorKind};
use std::error::Error as gerror;
use rand::{thread_rng, Rng};
use rand::distributions::Alphanumeric;
use db_system::code_management;
lazy_static! {
pub static ref DOCUMENTS: RwLock<DocumentStorage> = RwLock::new(DocumentStorage{
@ -32,6 +33,10 @@ impl DocumentStorage{
self.documents.get(id)
}
pub fn list_documents(&self) -> std::collections::hash_map::Keys<String, Document>{
self.documents.keys()
}
pub fn add(&mut self, document : Document) -> String{
let key: String = thread_rng()
.sample_iter(&Alphanumeric)
@ -49,7 +54,14 @@ impl DocumentStorage{
let mut input = File::open(Path::new("data/database.save"));
match input{
Ok(file) => {
let imported_documents = serde_json::from_reader(BufReader::new(file)).unwrap();
let imported_documents = match serde_json::from_reader(BufReader::new(file)){
Ok(imported_documents) => {
self.load_codes();
imported_documents},
Err(error) => {
panic!("Error during database loading: {}", error.description())
}
};
self.documents = imported_documents;
None
}
@ -57,6 +69,19 @@ impl DocumentStorage{
}
}
pub fn load_codes(&mut self) -> HashMap<String, String>{
let mut codemap: HashMap<String, String> = HashMap::new(); //Hashmap<Code, SurveyID>
for document_pair in self.documents.iter() {
let document = document_pair.1;
let codes : Vec<String> = document.get_codes().clone();
for code in codes.iter(){
codemap.insert(code.to_string(), document_pair.0.to_string());
}
}
codemap
}
pub fn save(&self) -> Option<std::io::Error>{
let mut output = File::create(Path::new("data/database.save"));
match output{

9
src/main.rs

@ -14,6 +14,7 @@ extern crate core;
use std::path::Path;
use std::fs::File;
use std::io::Write;
use std::fs;
mod webserver;
mod filecache;
@ -31,6 +32,7 @@ fn main(){
println!("Existing database found.");
}else{
println!("No existing database found. Trying to create new database.");
fs::create_dir("data").ok();
match File::create("data/database.save"){
Ok(mut file) => {
match file.write_all(b"{}") {
@ -46,5 +48,12 @@ fn main(){
Some(error) => panic!("Could not load internal database! Error was: {}", error),
None => println!("Successfully load database")
}
//TODO: Signal handling (if process gets siginit save database to disk etc.)
let loaded_surveys : String = db_system::storage::DOCUMENTS.read().unwrap().list_documents().cloned().map(|survey|{format!("{}\n", survey)}).collect();
println!("Loaded surveys: {}", loaded_surveys);
let loaded_codes : String = db_system::code_management::CODE_MANAGER.read().unwrap().keys().cloned().map(|key|{format!("{}\n", key)}).collect();
println!("Active codes: {}", loaded_codes);
webserver::delivery::deliver();
}

3
src/webserver/delivery.rs

@ -5,7 +5,6 @@ use webserver::styles;
use filecache::cache::Cache;
use std::collections::HashMap;
use std::sync::RwLock;
use database::code_management;
use rocket_contrib::serve::StaticFiles;
lazy_static! {
@ -23,7 +22,7 @@ pub fn deliver() {
CACHE.write().unwrap().add(String::from("poll.js"), String::from("style/poll.js"), 0);
//generate_poll(String::from("poll-1"));
code_management::CODE_MANAGER.write().unwrap().insert(String::from("FLADF"), String::from("poll-1"));
//code_management::CODE_MANAGER.write().unwrap().insert(String::from("FLADF"), String::from("poll-1"));
rocket::ignite()
.mount("/", routes![site_index::index])

10
src/webserver/session_manager.rs

@ -8,8 +8,9 @@ use rocket::response::content;
use rocket::http::RawStr;
use rocket::http::Status;
#[derive(Debug,Clone)]
pub struct Session{
pub step : String,
pub step : usize,
pub complete : bool,
pub poll : String,
pub data : HashMap<String, String>,
@ -19,11 +20,6 @@ lazy_static! {
pub static ref SESSIONS: RwLock<HashMap<String, Session>> = RwLock::new(HashMap::new());
}
/*pub fn test(){
SESSIONS.write().unwrap().insert(String::from("test"), Session{ step: 10, complete: true)});
println!("Example in Session test: {}", SESSIONS.read().unwrap().get("test2").unwrap().example);
}*/
pub fn generate_session_id() -> String{
return rand::thread_rng().sample_iter(&Alphanumeric).take(20).collect();
}
@ -43,7 +39,7 @@ pub fn session_handling(sessionid : Option<&RawStr>, code : &RawStr) -> Result<S
if sessionid.is_none() {
sessionid2 = generate_session_id();
SESSIONS.write().unwrap().insert(sessionid2.clone(), Session{
step: String::from("0"),
step: 1,
complete: false,
poll: code.to_string(),
data: HashMap::new()

40
src/webserver/site_poll_get.rs

@ -5,11 +5,46 @@ use rocket::response::status;
use webserver::{session_manager, templates};
use rocket::http::Status;
use std::io::Write;
use database::code_management;
use std::path::Path;
use db_system::code_management::check_code;
use db_system::storage;
#[get("/poll?<code>&<sessionid>")]
pub fn poll(code: &RawStr, sessionid: Option<&RawStr>) -> Result<content::Html<String>, status::Custom<content::Html<String>>> {
let sessionid: String = session_manager::session_handling(sessionid, code)?; //Aquire valid sessionid or report error
let surveyid = check_code(code).unwrap();
println!("{} -> {}", code, surveyid);
let maxsteps = match storage::DOCUMENTS.read().unwrap().get(&surveyid){
Some(document) => {
match document.get_qbc("qbc"){ //Get QuestionBlockContainer from document
Some(qbc) => qbc.questionblocks.len(),
None => {
eprintln!("KeyNotFound: qbc for survey {}", surveyid);
return Err(status::Custom(Status::InternalServerError, content::Html(templates::get_error("Error 500: Internal Server Error.", "", vec!("style/bootstrap.css".to_string(), "style/global.css".to_string(), "style/poll.css".to_string()), Vec::new()))))
}
}
}
None => {
eprintln!("DocumentNotFound: {}", surveyid);
return Err(status::Custom(Status::InternalServerError, content::Html(templates::get_error("Error 500: Internal Server Error.", "", vec!("style/bootstrap.css".to_string(), "style/global.css".to_string(), "style/poll.css".to_string()), Vec::new()))))
}
};
let mut step = session_manager::SESSIONS.read().unwrap().get(&sessionid).unwrap().step.clone();
if step == 0 {
}else if step < maxsteps {
}else if step == maxsteps{
}
Err(status::Custom(Status::NotFound, content::Html(templates::get_error("Error 404: Not implemented yet.", "", vec!("style/bootstrap.css".to_string(), "style/global.css".to_string(), "style/poll.css".to_string()), Vec::new()))))
/*
let sessionid: String = match session_manager::session_handling(sessionid, code) {
Ok(sessionid) => sessionid,
Err(e) => return Err(e)
@ -30,7 +65,7 @@ pub fn poll(code: &RawStr, sessionid: Option<&RawStr>) -> Result<content::Html<S
let mut step = session_manager::SESSIONS.read().unwrap().get(&sessionid).unwrap().step.clone();
if step == "0" { //First step, change step to first questionblock
if step == 0 { //First step, change step to first questionblock
step = String::from("questionblock-0"); //TODO: Check if at least one questionblock exists
}else{ //Step should look like "questionblock-x"
if !webserver::delivery::CACHE.read().unwrap().check_file(&format!("{}/{}", &poll, &step)) {
@ -64,5 +99,6 @@ pub fn poll(code: &RawStr, sessionid: Option<&RawStr>) -> Result<content::Html<S
} else {
return Err(status::Custom(Status::NotFound, content::Html(templates::get_error("Error 404: Not found", "Couldn't find requested survey", vec!("style/bootstrap.css".to_string(), "style/global.css".to_string(), "style/poll.css".to_string()), Vec::new()))))
}
*/
}

40
src/webserver/site_poll_post.rs

@ -2,16 +2,18 @@ use webserver::formdata::FormData;
use rocket::request::Form;
use rocket::response::{content, status};
use webserver::{session_manager, templates};
use database::code_management;
use rocket::http::{Status, RawStr};
use db_system::storage;
use db_system::{storage, code_management};
use db_system::code_management::check_code;
use webserver;
#[post("/poll?<code>&<sessionid>", data = "<data>")]
pub fn poll(code: &RawStr, data: Form<FormData>, sessionid: Option<&RawStr>) -> Result<content::Html<String>, status::Custom<content::Html<String>>> {
let mut data_fields = data.fields.clone();
let sessionid: String = session_manager::session_handling(sessionid, code)?; //Aquire valid sessionid or report error
let surveyid = code_management::code_handling(code)?; //Check pollid or report error
let surveyid = check_code(code).unwrap();
let mut session : webserver::session_manager::Session= session_manager::SESSIONS.read().unwrap().get(&sessionid).unwrap().clone();
let maxsteps = match storage::DOCUMENTS.read().unwrap().get(&surveyid){
Some(document) => {
@ -29,14 +31,38 @@ pub fn poll(code: &RawStr, data: Form<FormData>, sessionid: Option<&RawStr>) ->
}
};
let mut step = session_manager::SESSIONS.read().unwrap().get(&sessionid).unwrap().step.clone();
if session.step == 0 {
if step == 0 {
}else if session.step < maxsteps {
}else if step < maxsteps {
}else if session.step == maxsteps{
}else if step == maxsteps{
}
let mut submitted_step: usize = match data_fields.get("submit").cloned(){
Some(submitted_step) => match submitted_step.parse::<usize>(){
Ok(submitted_step) => submitted_step,
Err(_) => return Err(status::Custom(Status::BadRequest, content::Html(templates::get_error("Error 400", "Submitted data were invalid", vec!("style/bootstrap.css".to_string(), "style/global.css".to_string(), "style/poll.css".to_string()), Vec::new()))))
},
None => return Err(status::Custom(Status::BadRequest, content::Html(templates::get_error("Error 400", "Submitted data were invalid", vec!("style/bootstrap.css".to_string(), "style/global.css".to_string(), "style/poll.css".to_string()), Vec::new()))))
};
let error;
if submitted_step != session.step{ //user submitted wrong step
if session.step > submitted_step { //Example: User submitted step 6 but should submit step 4
//do not accept step and display expected step (in example step 4)
error = true;
}else if submitted_step < session.step { //Example: user submitted step 3 again, but should be at step 5
//accept step and display next step (in example step 4)
session.step = submitted_step;
session_manager::SESSIONS.write().unwrap().insert(sessionid, session.clone());
}
}
//TODO: Check if every required field is there
data_fields.remove("submit");
Err(status::Custom(Status::NotFound, content::Html(templates::get_error("Error 404: Not implemented yet.", "", vec!("style/bootstrap.css".to_string(), "style/global.css".to_string(), "style/poll.css".to_string()), Vec::new()))))
}
Loading…
Cancel
Save