diff --git a/modules/mod_survey/mod_survey.erl b/modules/mod_survey/mod_survey.erl index 0499567539..3f045982d5 100644 --- a/modules/mod_survey/mod_survey.erl +++ b/modules/mod_survey/mod_survey.erl @@ -207,6 +207,7 @@ new_question(Type) -> %% @doc Install the survey models install(Context) -> + m_survey:install(Context), z_datamodel:manage(?MODULE, datamodel(), Context). datamodel() -> diff --git a/modules/mod_survey/models/m_survey.erl b/modules/mod_survey/models/m_survey.erl index 5233ca0a41..0cc58bd4a5 100644 --- a/modules/mod_survey/models/m_survey.erl +++ b/modules/mod_survey/models/m_survey.erl @@ -27,7 +27,11 @@ -export([ m_find_value/3, m_to_list/2, - m_value/2 + m_value/2, + + insert_survey_submission/3, + survey_stats/2, + install/1 ]). -include_lib("zotonic.hrl"). @@ -68,3 +72,92 @@ question_to_value([Id|Ids], Qs, Acc) -> {Id, [{id, Id}, {type, Type}, {name, Name}, {html, Html}]}; question_to_value1(Id, undefined) -> {Id, [{id, Id}, {type, undefined}]}. + + + +%% @doc Save a survey, connect to the current visitor and user (if any) +insert_survey_submission(SurveyId, Answers, Context) -> + UserId = z_acl:user(Context), + %% Delete previous answers of this user, if any + case UserId of + undefined -> nop; + _Other -> z_db:q("delete from survey_answer where survey_id = $1 and user_id = $2", [SurveyId, UserId], Context) + end, + insert_questions(SurveyId, UserId, Answers, Context). + + insert_questions(_SurveyId, _UserId, [], _Context) -> + ok; + insert_questions(SurveyId, UserId, [{QuestionId, Answers}|Rest], Context) -> + insert_answers(SurveyId, UserId, QuestionId, Answers, Context), + insert_questions(SurveyId, UserId, Rest, Context). + + insert_answers(_SurveyId, _UserId, _QuestionId, [], _Context) -> + ok; + insert_answers(SurveyId, UserId, QuestionId, [{Name, Answer}|As], Context) -> + Args = case Answer of + {text, Text} -> [SurveyId, UserId, QuestionId, Name, undefined, Text]; + Value -> [SurveyId, UserId, QuestionId, Name, z_convert:to_list(Value), undefined] + end, + z_db:q("insert into survey_answer (survey_id, user_id, question, name, value, text) values ($1, $2, $3, $4, $5, $6)", Args, Context), + insert_answers(SurveyId, UserId, QuestionId, As, Context). + + +%% @doc Fetch the aggregate answers of a survey, omitting the open text answers. +%% @spec survey_stats(int(), Context) -> [ {QuestionId, [{Name, [{Value,Count}] }] } ] +survey_stats(SurveyId, Context) -> + Rows = z_db:q(" + select question, name, value, count(*) + from survey_answer + where survey_id = $1 and value is not null + group by question, name, value + order by question, name, value", [SurveyId], Context), + group_questions(Rows, []). + + group_questions([], Acc) -> + lists:reverse(Acc); + group_questions([{Question,_,_,_}|_] = Answers, Acc) -> + {Qs,Answers1} = lists:splitwith(fun({Q,_,_,_}) -> Q == Question end, Answers), + NVs = case Qs of + [{_, Name, Value, Count}|QsTail] -> group_values(Name, [{Value, Count}], QsTail, []); + [] -> [] + end, + group_questions(Answers1, [{Question,NVs}|Acc]). + + group_values(Name, Values, [], Acc) -> + [{Name, Values}|Acc]; + group_values(Name, Values, [{_,Name,V,C}|Rs], Acc) -> + group_values(Name, [{V,C}|Values], Rs, Acc); + group_values(Name, Values, [{_,N,V,C}|Rs], Acc) -> + group_values(N, [{V,C}], Rs, [{Name,Values}|Acc]). + + +%% @doc Install tables used for storing survey results +install(Context) -> + z_db:ensure_table(survey_answer, [ + #column_def{name=id, type="serial", is_nullable=false}, + #column_def{name=survey_id, type="integer", is_nullable=false}, + #column_def{name=user_id, type="integer", is_nullable=true}, + #column_def{name=question, type="character varying", length=32, is_nullable=false}, + #column_def{name=name, type="character varying", length=32, is_nullable=false}, + #column_def{name=value, type="character varying", length=80, is_nullable=true}, + #column_def{name=text, type="bytea", is_nullable=true} + ], Context), + + % Add some indices and foreign keys, ignore errors + z_db:equery("create index fki_survey_answer_survey_id on survey_answer(survey_id)", Context), + z_db:equery("alter table survey_answer add + constraint fk_survey_answer_survey_id foreign key (survey_id) references rsc(id) + on update cascade on delete cascade", Context), + + z_db:equery("create index fki_survey_answer_user_id on survey_answer(user_id)", Context), + z_db:equery("alter table survey_answer add + constraint fk_survey_answer_user_id foreign key (user_id) references rsc(id) + on update cascade on delete cascade", Context), + + %% For aggregating answers to survey questions (group by name) + z_db:equery("create index survey_answer_survey_name_key on survey_answer(survey_id, name)", Context), + z_db:equery("create index survey_answer_survey_question_key on survey_answer(survey_id, question)", Context), + z_db:equery("create index survey_answer_survey_user_key on survey_answer(survey_id, user_id)", Context), + + ok. + diff --git a/modules/mod_survey/questions/survey_q_longanswer.erl b/modules/mod_survey/questions/survey_q_longanswer.erl index 9dd9b446b9..5721cedfec 100644 --- a/modules/mod_survey/questions/survey_q_longanswer.erl +++ b/modules/mod_survey/questions/survey_q_longanswer.erl @@ -55,7 +55,7 @@ answer(Q, Context) -> undefined -> {error, missing}; Value -> case z_string:trim(Value) of [] -> {error, missing}; - V -> {ok, [{Name, V}]} + V -> {ok, [{Name, {text, V}}]} end end. diff --git a/modules/mod_survey/questions/survey_q_narrative.erl b/modules/mod_survey/questions/survey_q_narrative.erl index d9cc97f160..76a3045732 100644 --- a/modules/mod_survey/questions/survey_q_narrative.erl +++ b/modules/mod_survey/questions/survey_q_narrative.erl @@ -54,12 +54,14 @@ answer(Q, Context) -> answer_inputs([], _Context, Acc) -> {ok, Acc}; -answer_inputs([Name|Rest], Context, Acc) -> +answer_inputs([{IsSelect,Name}|Rest], Context, Acc) -> case z_context:get_q(Name, Context) of undefined -> {error, missing}; Value -> case z_string:trim(Value) of [] -> {error, missing}; - V -> answer_inputs(Rest, Context, [{Name, V}|Acc]) + V -> + V1 = case IsSelect of true -> V; false -> {text, V} end, + answer_inputs(Rest, Context, [{Name,V1}|Acc]) end end. @@ -73,11 +75,12 @@ parse([$[|T], in_text, [], Acc, InputAcc) -> parse(T, in_input, [], Acc, InputAcc); parse([$]|T], in_input, Input, Acc, InputAcc) -> Input1 = lists:reverse(Input), - {Name, Elt} = case is_select(Input1) of + IsSelect = is_select(Input1), + {Name, Elt} = case IsSelect of true -> build_select(Input1); false -> build_input(Input1) end, - parse(T, in_text, [], [Elt|Acc], [Name|InputAcc]); + parse(T, in_text, [], [Elt|Acc], [{IsSelect, Name}|InputAcc]); parse([H|T], in_input, Input, Acc, InputAcc) -> parse(T, in_input, [H|Input], Acc, InputAcc); parse([10|T], in_text, [], Acc, InputAcc) -> diff --git a/modules/mod_survey/questions/survey_q_shortanswer.erl b/modules/mod_survey/questions/survey_q_shortanswer.erl index 18efa17148..b845197d5a 100644 --- a/modules/mod_survey/questions/survey_q_shortanswer.erl +++ b/modules/mod_survey/questions/survey_q_shortanswer.erl @@ -55,7 +55,7 @@ answer(Q, Context) -> undefined -> {error, missing}; Value -> case z_string:trim(Value) of [] -> {error, missing}; - V -> {ok, [{Name, V}]} + V -> {ok, [{Name, {text, V}}]} end end. diff --git a/modules/mod_survey/support/survey_submit.erl b/modules/mod_survey/support/survey_submit.erl index 8e3fa1dfc9..a4f7180ec1 100644 --- a/modules/mod_survey/support/survey_submit.erl +++ b/modules/mod_survey/support/survey_submit.erl @@ -16,8 +16,17 @@ submit(SurveyId, FormId, Context) -> z_render:growl_error("Could not read survey information.", Context); {survey, QIds, Qs} -> {Answers, Missing, Accepted} = collect_answers(QIds, Qs, Context), - ?DEBUG({Answers, Missing}), - mark_accepted(Accepted, mark_missing(Missing, Context)) + case Missing of + [] -> + m_survey:insert_survey_submission(SurveyId, Answers, Context), + Context1 = mark_accepted(Accepted, mark_missing(Missing, Context)), + z_render:wire([ + {slide_up, [{target, FormId}]}, + {slide_down, [{target, FormId ++ "-success"}]} + ], Context1); + _ -> + mark_accepted(Accepted, mark_missing(Missing, Context)) + end end. @@ -40,7 +49,7 @@ collect_answers(QIds, Qs, Context) -> collect_answers(QIds, Qs, Context, [], [], []). -collect_answers([], Qs, Context, Answers, Missing, Accepted) -> +collect_answers([], _Qs, _Context, Answers, Missing, Accepted) -> {Answers, Missing, Accepted}; collect_answers([QId|QIds], Qs, Context, Answers, Missing, Accepted) -> Q = proplists:get_value(QId, Qs), diff --git a/modules/mod_survey/templates/survey.tpl b/modules/mod_survey/templates/survey.tpl index 7aa5a21d8a..c23355ad86 100644 --- a/modules/mod_survey/templates/survey.tpl +++ b/modules/mod_survey/templates/survey.tpl @@ -19,6 +19,10 @@ {% wire id=#survey type="submit" postback={survey_submit id=id} delegate="mod_survey" %}
+

+ {{ m.rsc[id].summary }} +

+ {% for q_id, q in m.survey.questions[id] %}
{{ q.html }} @@ -30,4 +34,8 @@ + + {% endblock %}