diff --git a/test/controllers/respondent_controller_test.exs b/test/controllers/respondent_controller_test.exs index 4eab9c1f9..a26c122b4 100644 --- a/test/controllers/respondent_controller_test.exs +++ b/test/controllers/respondent_controller_test.exs @@ -5,6 +5,15 @@ defmodule Ask.RespondentControllerTest do alias Ask.{QuotaBucket, Survey, SurveyLogEntry, Response, Respondent, ShortLink, Stats, ActivityLog} + + @empty_stats %{ + "attempts" => nil, + "total_call_time" => nil, + "total_call_time_seconds" => nil, + "total_received_sms" => 0, + "total_sent_sms" => 0 + } + describe "normal" do setup :user @@ -40,7 +49,8 @@ defmodule Ask.RespondentControllerTest do "value" => response.value, "name" => response.field_name } - ] + ], + "stats" => @empty_stats }] end @@ -1054,7 +1064,8 @@ defmodule Ask.RespondentControllerTest do "value" => "No", "name" => "Exercises" } - ] + ], + "stats" => @empty_stats }, %{ "id" => respondent_2.id, @@ -1071,7 +1082,8 @@ defmodule Ask.RespondentControllerTest do "value" => "No", "name" => "Smokes" } - ] + ], + "stats" => @empty_stats } ] end @@ -1110,7 +1122,8 @@ defmodule Ask.RespondentControllerTest do "value" => "No", "name" => "Exercises" } - ] + ], + "stats" => @empty_stats } ] end @@ -1144,7 +1157,8 @@ defmodule Ask.RespondentControllerTest do "value" => "No", "name" => "Smokes" } - ] + ], + "stats" => @empty_stats } ] end @@ -1178,7 +1192,8 @@ defmodule Ask.RespondentControllerTest do "value" => "No", "name" => "Smokes" } - ] + ], + "stats" => @empty_stats } ] end @@ -1267,7 +1282,8 @@ defmodule Ask.RespondentControllerTest do "value" => "No", "name" => "Perfect Number" } - ] + ], + "stats" => @empty_stats }, %{ "id" => respondent_2.id, @@ -1285,7 +1301,8 @@ defmodule Ask.RespondentControllerTest do "value" => "No", "name" => "Smokes" } - ] + ], + "stats" => @empty_stats } ] end diff --git a/web/static/js/components/respondents/RespondentIndex.jsx b/web/static/js/components/respondents/RespondentIndex.jsx index a311f9583..9e9efd463 100644 --- a/web/static/js/components/respondents/RespondentIndex.jsx +++ b/web/static/js/components/respondents/RespondentIndex.jsx @@ -12,6 +12,7 @@ import RespondentRow from './RespondentRow' import * as routes from '../../routes' import { modeLabel } from '../../questionnaire.mode' import find from 'lodash/find' +import flatten from 'lodash/flatten' import { translate } from 'react-i18next' type Props = { @@ -109,6 +110,22 @@ class RespondentIndex extends Component { this.props.actions.sortRespondentsBy(projectId, surveyId, name) } + getModes(surveyModes) { + return [...new Set(flatten(surveyModes))] + } + + getModeAttempts() { + const {survey, sortBy, sortAsc, t} = this.props + let modes = this.getModes(survey.mode) + let attemptsHeader = modes.map(function(mode) { + const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1) + let modeTitle = capitalize(mode) + ' ' + 'Attempts' + return this.sortBy(name)} /> + } + ) + return attemptsHeader + } + resultsAccessLink() { const {survey} = this.props return find(survey.links, (link) => link.name == `survey/${survey.id}/results`) @@ -336,7 +353,6 @@ class RespondentIndex extends Component { ) } - return (
{ {variantHeader} {t('Disposition')} this.sortBy(name)} /> + {this.getModeAttempts()} @@ -423,6 +440,7 @@ class RespondentIndex extends Component { respondent={respondent} responses={responses} variantColumn={variantColumn} + surveyModes={this.getModes(survey.mode)} /> })} diff --git a/web/static/js/components/respondents/RespondentRow.jsx b/web/static/js/components/respondents/RespondentRow.jsx index 93c911dc8..9a27ed03e 100644 --- a/web/static/js/components/respondents/RespondentRow.jsx +++ b/web/static/js/components/respondents/RespondentRow.jsx @@ -1,5 +1,5 @@ // @flow -import React, { Component } from 'react' +import React, {Component} from 'react' import dateformat from 'dateformat' import languageNames from 'language-names' import capitalize from 'lodash/capitalize' @@ -7,13 +7,26 @@ import capitalize from 'lodash/capitalize' type Props = { respondent: Respondent, responses: Response[], - variantColumn: ?React$Element<*> + variantColumn: ?React$Element<*>, + surveyModes: string[] }; class RespondentRow extends Component { - render() { - const { respondent, responses, variantColumn } = this.props + getModeAttemptsValues() { + const {respondent, surveyModes} = this.props + let modeValueRow = surveyModes.map(function(element, index) { + let modeValue = 0 + if (respondent.stats.attempts) { + modeValue = respondent.stats.attempts[element] ? respondent.stats.attempts[element] : 0 + } + return {modeValue} + }) + return modeValueRow + } + + render() { + const {respondent, responses, variantColumn} = this.props return ( {respondent.phoneNumber} @@ -33,6 +46,7 @@ class RespondentRow extends Component { {respondent.date ? dateformat(new Date(respondent.date), 'mmm d, yyyy HH:MM') : '-'} + {this.getModeAttemptsValues()} ) } diff --git a/web/static/js/decls/survey.js b/web/static/js/decls/survey.js index 38d26c223..5e0990ca0 100644 --- a/web/static/js/decls/survey.js +++ b/web/static/js/decls/survey.js @@ -93,7 +93,20 @@ export type Respondent = { disposition: Disposition, date: ?string, responses: Response[], - questionnaireId: number + questionnaireId: number, + stats: Stats +}; + +export type Attempts = { + sms: number, + ivr: number +}; + +export type Stats = { + attempts: Attempts, + totalSentSms: number, + totalReceivedSms: number, + totalCallTime: number }; export type Integration = { diff --git a/web/views/respondent_view.ex b/web/views/respondent_view.ex index 6b7814f5b..ba6c82b24 100644 --- a/web/views/respondent_view.ex +++ b/web/views/respondent_view.ex @@ -27,6 +27,7 @@ defmodule Ask.RespondentView do survey_id: respondent.survey_id, mode: respondent.mode, effective_modes: respondent.effective_modes, + stats: respondent.stats, questionnaire_id: respondent.questionnaire_id, responses: responses, disposition: respondent.disposition, @@ -40,6 +41,7 @@ defmodule Ask.RespondentView do phone_number: respondent.hashed_number, survey_id: respondent.survey_id, effective_modes: respondent.effective_modes, + stats: respondent.stats, mode: respondent.mode, questionnaire_id: respondent.questionnaire_id, responses: responses,