diff --git a/src/api_data/mod.rs b/src/api_data/mod.rs index 2763d3a..ffdc496 100644 --- a/src/api_data/mod.rs +++ b/src/api_data/mod.rs @@ -31,4 +31,6 @@ pub mod group_member_api; pub mod friend_api; pub mod friendship_status_api; pub mod post_api; -pub mod movie_api; \ No newline at end of file +pub mod movie_api; +pub mod survey_choice_api; +pub mod survey_api; \ No newline at end of file diff --git a/src/api_data/post_api.rs b/src/api_data/post_api.rs index 15da551..e39b71a 100644 --- a/src/api_data/post_api.rs +++ b/src/api_data/post_api.rs @@ -4,10 +4,11 @@ use serde::Serialize; use crate::api_data::movie_api::MovieAPI; +use crate::api_data::survey_api::SurveyAPI; use crate::data::error::ResultBoxError; use crate::data::post::{Post, PostKind}; use crate::data::user::UserID; -use crate::helpers::movies_helper; +use crate::helpers::{movies_helper, survey_helper}; use crate::utils::user_data_utils::user_data_url; #[derive(Serialize)] @@ -40,11 +41,14 @@ pub struct PostAPI { // Countdown timer specific time_end: Option, + + // Survey specific + data_survey: Option, } impl PostAPI { /// Turn a `Post` entry into an API entry - pub fn new(p: &Post) -> ResultBoxError { + pub fn new(p: &Post, user: &Option) -> ResultBoxError { let mut post = PostAPI { ID: p.id, userID: p.user_id.id(), @@ -73,6 +77,9 @@ impl PostAPI { // Countdown timer-specific time_end: None, + + // Survey specific + data_survey: None, }; match &p.kind { @@ -99,7 +106,9 @@ impl PostAPI { PostKind::POST_KIND_COUNTDOWN(time_end) => post.time_end = Some(*time_end), - PostKind::POST_KIND_SURVEY => {} + PostKind::POST_KIND_SURVEY => + post.data_survey = Some(SurveyAPI::new(&survey_helper::get_info(p.id)?, user.clone())?), + PostKind::POST_KIND_YOUTUBE => {} } @@ -107,7 +116,7 @@ impl PostAPI { } /// Turn a list of posts into an API entry - pub fn for_list(l: &Vec) -> ResultBoxError> { - l.iter().map(Self::new).collect() + pub fn for_list(l: &Vec, user_id: Option) -> ResultBoxError> { + l.iter().map(|p| Self::new(p, &user_id)).collect() } } \ No newline at end of file diff --git a/src/api_data/survey_api.rs b/src/api_data/survey_api.rs new file mode 100644 index 0000000..d540bab --- /dev/null +++ b/src/api_data/survey_api.rs @@ -0,0 +1,47 @@ +//! # Survey API information +//! +//! @author Pierre Hubert + +use std::collections::HashMap; + +use serde::Serialize; + +use crate::api_data::survey_choice_api::SurveyChoiceAPI; +use crate::data::error::ResultBoxError; +use crate::data::survey::Survey; +use crate::data::user::UserID; +use crate::helpers::survey_helper; + +#[derive(Serialize)] +#[allow(non_snake_case)] +pub struct SurveyAPI { + ID: u64, + userID: u64, + postID: u64, + creation_time: u64, + question: String, + user_choice: u64, + choices: HashMap, + allowNewChoices: bool, +} + +impl SurveyAPI { + /// Create a new survey API entry + pub fn new(s: &Survey, curr_user_id: Option) -> ResultBoxError { + let user_choice = match &curr_user_id { + None => 0, /* -1 is not possible */ + Some(user_id) => survey_helper::get_user_choice(s.id, user_id)?, + }; + + Ok(SurveyAPI { + ID: s.id, + userID: s.user_id.id(), + postID: s.post_id, + creation_time: s.time_create, + question: s.question.clone(), + user_choice, + choices: SurveyChoiceAPI::for_list(&s.choices), + allowNewChoices: s.allow_new_choices, + }) + } +} \ No newline at end of file diff --git a/src/api_data/survey_choice_api.rs b/src/api_data/survey_choice_api.rs new file mode 100644 index 0000000..769f835 --- /dev/null +++ b/src/api_data/survey_choice_api.rs @@ -0,0 +1,33 @@ +//! # Survey choice API +//! +//! @author Pierre Hubert + +use std::collections::HashMap; + +use serde::Serialize; + +use crate::data::survey::SurveyChoice; + +#[derive(Serialize)] +#[allow(non_snake_case)] +pub struct SurveyChoiceAPI { + choiceID: u64, + name: String, + responses: u64, +} + +impl SurveyChoiceAPI { + pub fn new(c: &SurveyChoice) -> SurveyChoiceAPI { + SurveyChoiceAPI { + choiceID: c.id, + name: c.name.clone(), + responses: c.count, + } + } + + pub fn for_list(c: &Vec) -> HashMap { + let mut map = HashMap::with_capacity(c.len()); + c.iter().for_each(|c| { map.insert(c.id, Self::new(c)); }); + map + } +} \ No newline at end of file diff --git a/src/constants.rs b/src/constants.rs index 891fe9a..a45273d 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -40,6 +40,15 @@ pub mod database_tables_names { /// Movies table pub const MOVIES_TABLE: &str = "galerie_video"; + + /// Survey info table + pub const SURVEY_INFO_TABLE: &str = "sondage"; + + /// Survey choices table + pub const SURVEY_CHOICES_TABLE: &str = "sondage_choix"; + + /// Survey responses table + pub const SURVEY_RESPONSE_TABLE: &str = "sondage_reponse"; } /// The account image to show for user who do not have any diff --git a/src/controllers/posts_controller.rs b/src/controllers/posts_controller.rs index d0d41eb..efed024 100644 --- a/src/controllers/posts_controller.rs +++ b/src/controllers/posts_controller.rs @@ -20,5 +20,5 @@ pub fn get_list_user(r: &mut HttpRequestHandler) -> RequestResult { .set_start_from(start_from) .get_user(&user_id)?; - r.set_response(PostAPI::for_list(&posts)?) + r.set_response(PostAPI::for_list(&posts, r.user_id_opt())?) } \ No newline at end of file diff --git a/src/data/mod.rs b/src/data/mod.rs index c806716..13e827d 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -20,4 +20,5 @@ pub mod global_search_result; pub mod friend; pub mod friendship_status; pub mod post; -pub mod movie; \ No newline at end of file +pub mod movie; +pub mod survey; \ No newline at end of file diff --git a/src/data/survey.rs b/src/data/survey.rs new file mode 100644 index 0000000..08184b6 --- /dev/null +++ b/src/data/survey.rs @@ -0,0 +1,23 @@ +//! # Survey +//! +//! This structure contains all the information about a survey +//! +//! @author Pierre Hubert + +use crate::data::user::UserID; + +pub struct SurveyChoice { + pub id: u64, + pub name: String, + pub count: u64, +} + +pub struct Survey { + pub id: u64, + pub user_id: UserID, + pub time_create: u64, + pub post_id: u64, + pub question: String, + pub choices: Vec, + pub allow_new_choices: bool, +} \ No newline at end of file diff --git a/src/helpers/database.rs b/src/helpers/database.rs index a9c0340..056bfe0 100644 --- a/src/helpers/database.rs +++ b/src/helpers/database.rs @@ -51,6 +51,13 @@ pub fn get_connection() -> Result> { Ok(pool.get_conn()?) } +/// Join type +#[derive(PartialEq)] +pub enum DatabaseQueryJoinType { + NORMAL, + LEFT, +} + /// Structure used to implement JOIN on queries struct QueryJoin { table: String, @@ -64,6 +71,7 @@ pub struct QueryInfo { pub table_alias: Option, /// Joins + joins_type: DatabaseQueryJoinType, joins: Vec, /// Query limits @@ -75,6 +83,9 @@ pub struct QueryInfo { /// Custom WHERE values pub custom_where_ars: Vec, + /// Custom GROUP BY argument + group_by: Option, + /// Limit of the query (0 = no limit) pub limit: u64, @@ -93,10 +104,12 @@ impl QueryInfo { QueryInfo { table: table.to_string(), table_alias: None, + joins_type: DatabaseQueryJoinType::NORMAL, joins: Vec::new(), conditions: collections::HashMap::new(), custom_where: None, custom_where_ars: vec![], + group_by: None, limit: 0, order: None, fields: Vec::new(), @@ -109,6 +122,12 @@ impl QueryInfo { self } + /// Set join type + pub fn set_join_type(mut self, t: DatabaseQueryJoinType) -> QueryInfo { + self.joins_type = t; + self + } + pub fn join(mut self, table: &str, table_alias: &str, cond: &str) -> QueryInfo { self.joins.push(QueryJoin { table: table.to_string(), @@ -194,9 +213,9 @@ impl QueryInfo { self } - /// Set the limit for the request - pub fn set_limit(mut self, value: u64) -> QueryInfo { - self.limit = value; + /// Set GROUP BY clause + pub fn set_group_by(mut self, group_by: &str) -> QueryInfo { + self.group_by = Some(group_by.to_string()); self } @@ -206,6 +225,12 @@ impl QueryInfo { self } + /// Set the limit for the request + pub fn set_limit(mut self, value: u64) -> QueryInfo { + self.limit = value; + self + } + /// Execute query pub fn exec ProcessRowResult>(self, process_function: F) -> Result, Box> { @@ -396,6 +421,10 @@ pub fn query ProcessRowResult>(info: QueryInfo, proce } // Join conditions + if info.joins_type == DatabaseQueryJoinType::LEFT { + query.push_str(" LEFT "); + } + for j in info.joins { query = query.add( format!(" JOIN {} {} ON {} ", j.table, j.table_alias, j.condition).as_ref()); @@ -427,6 +456,11 @@ pub fn query ProcessRowResult>(info: QueryInfo, proce params.append(&mut custom_args); } + // GROUP BY clause + if let Some(group_by) = info.group_by { + query.push_str(format!(" GROUP BY {} ", group_by).as_str()) + } + // ORDER clause if let Some(order) = info.order { query = query.add(format!(" ORDER BY {} ", order).as_str()); diff --git a/src/helpers/mod.rs b/src/helpers/mod.rs index 73ec7c7..41675b3 100644 --- a/src/helpers/mod.rs +++ b/src/helpers/mod.rs @@ -11,4 +11,5 @@ pub mod groups_helper; pub mod posts_helper; pub mod conversations_helper; pub mod virtual_directory_helper; -pub mod movies_helper; \ No newline at end of file +pub mod movies_helper; +pub mod survey_helper; \ No newline at end of file diff --git a/src/helpers/posts_helper.rs b/src/helpers/posts_helper.rs index d331b32..e1d5146 100644 --- a/src/helpers/posts_helper.rs +++ b/src/helpers/posts_helper.rs @@ -5,7 +5,7 @@ use crate::constants::database_tables_names::POSTS_TABLE; use crate::data::error::{ExecError, ResultBoxError}; use crate::data::post::{Post, PostFile, PostKind, PostPageKind, PostVisibilityLevel, PostWebLink}; -use crate::data::post::PostKind::{POST_KIND_COUNTDOWN, POST_KIND_IMAGE, POST_KIND_MOVIE, POST_KIND_PDF, POST_KIND_WEBLINK}; +use crate::data::post::PostKind::{POST_KIND_COUNTDOWN, POST_KIND_IMAGE, POST_KIND_MOVIE, POST_KIND_PDF, POST_KIND_SURVEY, POST_KIND_WEBLINK}; use crate::data::user::UserID; use crate::helpers::{database, friends_helper}; use crate::utils::date_utils::time; @@ -174,6 +174,8 @@ fn db_to_post(res: &database::RowResult) -> ResultBoxError { "count_down" => post.kind = POST_KIND_COUNTDOWN(res.get_u64("time_end").unwrap_or(0)), + "sondage" => post.kind = POST_KIND_SURVEY, + _ => {} } diff --git a/src/helpers/survey_helper.rs b/src/helpers/survey_helper.rs new file mode 100644 index 0000000..b5b34a6 --- /dev/null +++ b/src/helpers/survey_helper.rs @@ -0,0 +1,63 @@ +//! # Survey helper +//! +//! @author Pierre Hubert + +use crate::constants::database_tables_names::{SURVEY_CHOICES_TABLE, SURVEY_INFO_TABLE, SURVEY_RESPONSE_TABLE}; +use crate::data::error::ResultBoxError; +use crate::data::survey::{Survey, SurveyChoice}; +use crate::data::user::UserID; +use crate::helpers::database; + +/// Get information about a survey +pub fn get_info(post_id: u64) -> ResultBoxError { + database::QueryInfo::new(SURVEY_INFO_TABLE) + .cond_u64("ID_texte", post_id) + .query_row(db_to_survey) +} + +/// Get the choices of a survey +fn get_survey_choices(survey_id: u64) -> ResultBoxError> { + database::QueryInfo::new(SURVEY_CHOICES_TABLE) + .alias("c") + .set_join_type(database::DatabaseQueryJoinType::LEFT) + .join(SURVEY_RESPONSE_TABLE, "r", "c.ID = r.ID_sondage_choix") + .cond_u64("c.ID_sondage", survey_id) + .set_group_by("c.ID") + .add_field("c.*") + .add_field("COUNT(r.ID) AS count_choice") + .exec(db_to_survey_choice) +} + +/// Get the choice of a user for a survey +pub fn get_user_choice(survey_id: u64, user_id: &UserID) -> ResultBoxError { + Ok(database::QueryInfo::new(SURVEY_RESPONSE_TABLE) + .cond_u64("ID_sondage", survey_id) + .cond_user_id("ID_utilisateurs", user_id) + .exec(|r| r.get_u64("ID_sondage_choix"))? + .pop() + .unwrap_or(0)) +} + +/// Turn a database entry into a row object +fn db_to_survey(row: &database::RowResult) -> ResultBoxError { + let survey_id = row.get_u64("ID")?; + + Ok(Survey { + id: survey_id, + user_id: row.get_user_id("ID_utilisateurs")?, + time_create: row.get_date_as_time("date_creation")?, + post_id: row.get_u64("ID_texte")?, + question: row.get_str("question")?, + choices: get_survey_choices(survey_id)?, + allow_new_choices: row.get_legacy_bool("allow_new_choices")?, + }) +} + +/// Turn a database row into a survey choice +fn db_to_survey_choice(row: &database::RowResult) -> ResultBoxError { + Ok(SurveyChoice { + id: row.get_u64("ID")?, + name: row.get_str("Choix")?, + count: row.get_u64("count_choice")?, + }) +} \ No newline at end of file