├── test
├── tmp
│ └── .gitkeep
├── fixtures
│ ├── invalid.mp3
│ ├── respondent_phone_numbers_empty.csv
│ ├── respondent_phone_numbers_one.csv
│ ├── invalid_audio.csv
│ ├── respondent_phone_numbers_r.csv
│ ├── audio.aac
│ ├── audio.m4a
│ ├── audio.mp3
│ ├── audio.wav
│ ├── respondent_phone_numbers_invalid.csv
│ ├── respondent_phone_numbers_duplicated2.csv
│ ├── respondent_phone_numbers_newline.csv
│ ├── respondent_phone_numbers_special_characters.csv
│ ├── respondent_phone_numbers_utf16le.csv
│ ├── respondent_phone_numbers.csv
│ ├── respondent_phone_numbers_duplicated.csv
│ └── respondent_phone_numbers_additional_columns.csv
├── support
│ ├── mocks.ex
│ ├── base64_hasher.ex
│ ├── audio_checker.ex
│ ├── log_helper.ex
│ ├── survey_stub.ex
│ ├── guisso.ex
│ └── ask_swoosh_adapter.ex
├── ask_web
│ ├── views
│ │ ├── page_view_test.exs
│ │ └── error_view_test.exs
│ ├── emails
│ │ └── email_test.exs
│ └── controllers
│ │ └── page_controller_test.exs
├── js
│ ├── mocha.opts
│ └── spec_helper.js
├── test_helper.exs
└── ask
│ ├── audio_test.exs
│ ├── url_shortener_test.exs
│ ├── project_test.exs
│ ├── response_test.exs
│ ├── oauth_token_test.exs
│ ├── questionnaire_variable_test.exs
│ ├── survey_questionnaire_test.exs
│ ├── respondent_group_channel_test.exs
│ ├── respondent_disposition_history_test.exs
│ └── translation_test.exs
├── priv
└── repo
│ ├── migrations
│ ├── .keep
│ ├── 20170126172256_confirm_all_users.exs
│ ├── 20161014181536_add_unique_email_index_to_users.exs
│ ├── 20161101174551_add_mode_to_surveys.exs
│ ├── 20161101174731_set_mode_to_existing_surveys.exs
│ ├── 20160909195628_add_state_to_surveys.exs
│ ├── 20160913171925_add_cutoff_to_surveys.exs
│ ├── 20161014221336_set_questionnaires_mode_to_sms.exs
│ ├── 20161104165607_set_timezone_to_existing_surveys.exs
│ ├── 20161123205617_add_quotas_to_survey.exs
│ ├── 20170109180032_add_settings_to_users.exs
│ ├── 20170119184752_add_name_to_users.exs
│ ├── 20170329214637_mark_existing_surveys_as_valid.exs
│ ├── 20161101163857_downcase_questionnaire_modes.exs
│ ├── 20161104145701_add_timezone_to_surveys.exs
│ ├── 20161227192753_index_respondents_by_survey_id_and_state.exs
│ ├── 20161229195124_index_respondents_by_state_and_timeout.exs
│ ├── 20241007192540_pause_channel.exs
│ ├── 20160916194539_add_session_to_respondent.exs
│ ├── 20170130141552_put_sth_in_password_hash.exs
│ ├── 20170301154642_add_scope_to_translation.exs
│ ├── 20171122200548_add_stats_to_respondents.exs
│ ├── 20190329144509_add_unique_constraint_on_name_in_folders.exs
│ ├── 20160906173441_add_modes_to_questionnaire.exs
│ ├── 20160909152241_add_steps_to_questionnaires.exs
│ ├── 20170102165430_add_comparisons_to_surveys.exs
│ ├── 20190328175014_create_folder.exs
│ ├── 20191127150645_add_ended_at_to_surveys.exs
│ ├── 20230402091100_add_batch_size_to_projects.exs
│ ├── 20161110190547_add_started_at_to_surveys.exs
│ ├── 20170105213430_add_inviter_email_to_invites.exs
│ ├── 20170215173627_add_fallback_delay_to_survey.exs
│ ├── 20170315151438_set_default_colour_scheme_to_projects.exs
│ ├── 20170329195359_add_valid_to_questionnaires.exs
│ ├── 20170428143816_add_language_to_respondents.exs
│ ├── 20170731210541_index_respondents_by_sanitized_phone_number.exs
│ ├── 20160914215134_add_state_to_respondent.exs
│ ├── 20161223131256_add_error_msg_to_quesionnaires.exs
│ ├── 20170424180020_add_settings_to_questionnaires.exs
│ ├── 20180131174235_add_archived_to_projects.exs
│ ├── 20220131103226_add_unconfirmed_email_to_users.exs
│ ├── 20161108201151_add_timeout_at_to_respondent.exs
│ ├── 20170519125438_add_simulation_to_surveys.exs
│ ├── 20180226163507_add_state_to_floip_endpoint_table.exs
│ ├── 20190821214420_add_section_order_to_respondent.exs
│ ├── 20160926225157_add_expires_at_to_oauth_tokens.exs
│ ├── 20161004181711_add_completed_at_to_respondents.exs
│ ├── 20161130183834_add_quota_bucket_id_to_respondent.exs
│ ├── 20170524132106_add_effective_modes_to_respondent.exs
│ ├── 20191204182139_add_retry_stat_time_to_respondent.exs
│ ├── 20160906161147_remove_description_from_questionnaires.exs
│ ├── 20170131101552_add_remember_created_at_to_users.exs
│ ├── 20170308202613_add_lock_version_to_respondents.exs
│ ├── 20170622182023_add_deleted_to_questionnaires.exs
│ ├── 20170306224401_add_mode_to_respondent_group_channels.exs
│ ├── 20170315145645_add_colour_scheme_to_projects.exs
│ ├── 20180226195859_add_auth_token_to_floip_endpoint_table.exs
│ ├── 20200907093359_add_archived_to_questionnaires.exs
│ ├── 20210326160009_add_last_window_ends_at_to_surveys.exs
│ ├── 20190621180921_index_respondent_disposition_history_by_survey_id_and_id.exs
│ ├── 20210211131024_add_incentives_enabled_to_surveys.exs
│ ├── 20230317094712_add_batch_limit_per_minute_to_projects.exs
│ ├── 20160812145257_create_user.exs
│ ├── 20161116163735_add_sanitized_phone_number_to_respondent.exs
│ ├── 20170417193442_add_mobile_web_cookie_code_to_respondents.exs
│ ├── 20200211130308_add_respondents_canonical_phone_number.exs
│ ├── 20210323192808_add_first_window_started_at_to_surveys.exs
│ ├── 20170216184642_add_count_partial_results_to_surveys.exs
│ ├── 20170327142610_add_mode_to_respondent_disposition_history.exs
│ ├── 20170508193540_add_questionnaire_quota_completed_steps.exs
│ ├── 20191213131858_remove_invalid_retry_stat_time_in_respondents.exs
│ ├── 20161201181704_add_quota_completed_message_to_questionnaires.exs
│ ├── 20170308192425_add_mobile_web_sms_message_to_questionnaire.exs
│ ├── 20170406224331_add_mobileweb_retry_configuration_to_surveys.exs
│ ├── 20180503032000_modify_survey_log_entry_action_data_to_long_text.exs
│ ├── 20210614154820_add_generates_panel_survey_to_surveys.exs
│ ├── 20160905173031_add_questionnaire_id_to_surveys.exs
│ ├── 20170116175546_add_respondent_group_id_to_respondents.exs
│ ├── 20170503172812_add_snapshot_of_to_questionnaires.exs
│ ├── 20170601040741_index_survey_log_entries_by_survey_id_respondent_hash_and_id.exs
│ ├── 20190328182425_add_project_to_folders.exs
│ ├── 20190401140258_add_folder_id_to_surveys.exs
│ ├── 20170505203058_create_respondents_index_by_hashed_number_and_updated_at.exs
│ ├── 20161109053659_add_retry_configuration_to_surveys.exs
│ ├── 20161118175719_set_languages_and_default_language_to_existing_questionnaires.exs
│ ├── 20171023212559_create_short_links.exs
│ ├── 20170417172528_add_mobile_web_survey_is_over_message_to_questionnaire.exs
│ ├── 20161013170416_change_survey_schedule_start_and_end_to_time.exs
│ ├── 20170109115814_add_questionnaire_and_mode_to_respondents.exs
│ ├── 20170117143633_add_salt_to_projects.exs
│ ├── 20180110182428_add_floip_package_id_to_surveys.exs
│ ├── 20201124112819_add_panel_surveys.exs
│ ├── 20171004213434_add_survey_schedule.exs
│ ├── 20161118174623_add_languages_and_default_language_to_questionnaires.exs
│ ├── 20170117190223_add_sample_and_respondents_count_to_respondent_group.exs
│ ├── 20161110191532_set_default_started_at_for_already_launched_surveys.exs
│ ├── 20170116175403_create_respondent_group.exs
│ ├── 20160929135859_add_schedule_to_surveys.exs
│ ├── 20161013162339_set_default_completed_at_for_completed_respondents.exs
│ ├── 20181022174941_add_description_to_surveys.exs
│ ├── 20211213094856_add_index_to_respondents_canonical_phone_numbers.exs
│ ├── 20191223182932_modify-retry-stats-unique-index.exs
│ ├── 20160816183915_create_project.exs
│ ├── 20170320182452_add_base_url_to_channel_and_oauth_tokens.exs
│ ├── 20180312173843_add_remote_ip_to_activity_log.exs
│ ├── 20210512142133_create_panel_survey.exs
│ ├── 20160830200454_create_survey.exs
│ ├── 20170125165633_add_disposition_to_respondents.exs
│ ├── 20170502170714_clear_session_and_timeout_at_for_failed_respondents.exs
│ ├── 20160906192317_create_survey_channels.exs
│ ├── 20161109034931_create_audios.exs
│ ├── 20161229171246_create_invite.exs
│ ├── 20170119123633_add_hashed_number_to_respondents.exs
│ ├── 20191127152001_set_default_ended_at_for_already_finished_surveys.exs
│ ├── 20200122115222_add_description_to_questionnaires.exs
│ ├── 20160909144711_create_respondent.exs
│ ├── 20161220152839_remove_user_id_from_projects.exs
│ ├── 20170125151039_set_default_comparisons_on_surveys.exs
│ ├── 20160914194540_modify_questionnaires_steps_to_longtext.exs
│ ├── 20200211131441_set_default_respondents_canonical_phone_number.exs
│ ├── 20170117191259_modify_respondent_group_sample_to_long_text.exs
│ ├── 20230217143550_add_settings_to_projects.exs
│ ├── 20191212203052_set_default_retry_stat_time_to_respondents.exs
│ ├── 20230413101342_remove_mobile_web_cookie_code_from_respondents.exs
│ ├── 20160914174501_create_response.exs
│ ├── 20161124170829_create_quota_buckets.exs
│ ├── 20161219200232_create_project_membership.exs
│ ├── 20200420135544_add_partial_relevant_config_to_questionnaires.exs
│ ├── 20160902185110_create_questionnaire.exs
│ ├── 20160924055948_create_oauth_token.exs
│ ├── 20161221101027_remove_questionnaire_id_from_surveys.exs
│ ├── 20170202175824_create_sessions_table.exs
│ ├── 20170420194102_change_questionnaire_mobile_web_sms_message_to_text.exs
│ ├── 20190621174448_add_survey_id_and_respondent_hashed_number_to_respondent_disposition_history.exs
│ ├── 20210326160205_fill_last_window_ends_at_in_surveys.exs
│ ├── 20170119153421_remove_survey_channels.exs
│ ├── 20170125165933_set_disposition_as_completed_where_applies_in_respondents.exs
│ ├── 20160905135419_create_channel.exs
│ ├── 20161115202151_set_default_schedule_start_and_end_times.exs
│ ├── 20200212200209_add-user-stopped-to-respondents.exs
│ ├── 20160908162553_create_questionnaire_step.exs
│ ├── 20160915191129_rename_survey_state_default_to_not_ready.exs
│ ├── 20170206162219_create_respondent_disposition_history.exs
│ ├── 20180315000255_create_project_channels.exs
│ ├── 20210325194736_fill_first_window_started_at_in_surveys.exs
│ ├── 20180302023759_create_activity_log.exs
│ ├── 20170207152142_modify_translation_source_text_and_target_text_to_long_text.exs
│ ├── 20191011174319_create_retry_stats.exs
│ ├── 20160909150600_drop_questionnaire_steps.exs
│ ├── 20161221100014_create_survey_questionnaire.exs
│ ├── 20170818184001_add_default_disposition_to_rejected_respondents.exs
│ ├── 20160913180125_set_default_state_for_surveys.exs
│ ├── 20170113200644_add_coherence_to_user.exs
│ ├── 20170119152252_create_respondent_group_channel.exs
│ ├── 20161219204505_create_questionnaire_variable.exs
│ ├── 20170224213051_create_survey_log_entry.exs
│ ├── 20170307180659_set_default_mode_to_respondent_group_channels.exs
│ ├── 20170322170704_alter_unique_index_of_oauth_tokens.exs
│ ├── 20171023192702_add_completed_respondents_table.exs
│ ├── 20180219132514_add_floip_endpoints_table.exs
│ ├── 20211125141835_optimize_audios_uuid.exs
│ ├── 20171213183513_force_respondent_stats_not_null.exs
│ ├── 20170110002354_set_default_settings_to_users.exs
│ ├── 20171004213436_remove_survey_day_of_week_start_time_and_end_time.exs
│ ├── 20170201123448_add_unique_index_to_invites.exs
│ ├── 20170818182906_add_respondent_stats_table.exs
│ ├── 20181102164026_add_locked_to_surveys.exs
│ ├── 20180504144910_add_patterns_to_channels.exs
│ ├── 20170424193129_remove_old_messages_from_questionnaires.exs
│ ├── 20190621180432_fill_survey_id_and_respondent_hashed_numbnerin_respondent_disposition_history.exs
│ ├── 20161222124950_create_translation.exs
│ ├── 20161220165340_change_survey_modes_to_arary_of_arrays.exs
│ ├── 20170119182423_create_coherence_rememberable.exs
│ ├── 20161013154229_set_default_schedule_day_of_week_for_surveys.exs
│ └── 20180413192752_replace_floip_endpoints_table.exs
│ └── seeds.exs
├── .prettierignore
├── .prettierrc
├── assets
├── js
│ ├── decls
│ │ ├── jquery.js
│ │ ├── mocha.js
│ │ ├── project.js
│ │ ├── channel.js
│ │ └── undo.js
│ ├── jsconfig.json
│ ├── components
│ │ ├── layout
│ │ │ ├── Root.jsx
│ │ │ ├── Root.prod.jsx
│ │ │ ├── Root.dev.jsx
│ │ │ └── Footer.jsx
│ │ ├── questionnaires
│ │ │ ├── withQuestionnaire.jsx.flow
│ │ │ ├── withQuestionnaire.jsx
│ │ │ └── StepDeleteButton.jsx
│ │ ├── surveys
│ │ │ └── PhoneNumberRow.jsx
│ │ ├── timezones
│ │ │ └── util.js
│ │ ├── ui
│ │ │ ├── Card.jsx
│ │ │ ├── UntitledIfEmpty.jsx
│ │ │ └── ScrollToLink.jsx
│ │ ├── charts
│ │ │ ├── utils.js
│ │ │ ├── Stats.jsx
│ │ │ └── References.jsx
│ │ ├── integrations
│ │ │ └── IntegrationRow.jsx
│ │ └── simulation
│ │ │ └── MobileWebWindow.jsx
│ ├── store
│ │ ├── configureStore.js
│ │ └── configureStore.dev.js
│ ├── reducers
│ │ ├── ui
│ │ │ └── index.js
│ │ ├── respondentsStats.js
│ │ ├── surveyStats.js
│ │ ├── surveyRetriesHistograms.js
│ │ ├── guisso.js
│ │ ├── invite.js
│ │ ├── respondentsCount.js
│ │ ├── timezones.js
│ │ ├── unusedSample.js
│ │ └── channels.js
│ ├── debounce.js
│ ├── actions
│ │ ├── autoSaveStatus.js
│ │ ├── guisso.js
│ │ └── timezones.js
│ ├── app.jsx
│ ├── config.js
│ ├── pigeon.js
│ ├── propsAreEqual.js
│ └── surveyModes.js
├── static
│ ├── favicon.ico
│ ├── images
│ │ ├── map.png
│ │ ├── phoenix.png
│ │ ├── header_icon.png
│ │ ├── nuntium@2x.png
│ │ ├── verboice@2x.png
│ │ ├── warning@2x.png
│ │ ├── email-logo@2x.png
│ │ ├── surveda-name.png
│ │ ├── welcome-img01.jpg
│ │ ├── welcome-img01.png
│ │ ├── welcome-img02.png
│ │ ├── welcome-img03.png
│ │ ├── welcome-img04.png
│ │ ├── welcome-main.png
│ │ ├── favicon
│ │ │ ├── favicon.ico
│ │ │ ├── favicon-128.png
│ │ │ ├── favicon-16x16.png
│ │ │ ├── favicon-32x32.png
│ │ │ ├── favicon-96x96.png
│ │ │ ├── mstile-70x70.png
│ │ │ ├── favicon-196x196.png
│ │ │ ├── mstile-144x144.png
│ │ │ ├── mstile-150x150.png
│ │ │ ├── mstile-310x150.png
│ │ │ ├── mstile-310x310.png
│ │ │ ├── apple-touch-icon-57x57.png
│ │ │ ├── apple-touch-icon-60x60.png
│ │ │ ├── apple-touch-icon-72x72.png
│ │ │ ├── apple-touch-icon-76x76.png
│ │ │ ├── apple-touch-icon-114x114.png
│ │ │ ├── apple-touch-icon-120x120.png
│ │ │ ├── apple-touch-icon-144x144.png
│ │ │ └── apple-touch-icon-152x152.png
│ │ ├── file_download_off.svg
│ │ └── header_icon_default.svg
│ ├── fonts
│ │ ├── roboto
│ │ │ ├── Roboto-Bold.woff
│ │ │ ├── Roboto-Thin.woff
│ │ │ ├── Roboto-Bold.woff2
│ │ │ ├── Roboto-Light.woff
│ │ │ ├── Roboto-Light.woff2
│ │ │ ├── Roboto-Medium.woff
│ │ │ ├── Roboto-Medium.woff2
│ │ │ ├── Roboto-Regular.woff
│ │ │ ├── Roboto-Thin.woff2
│ │ │ └── Roboto-Regular.woff2
│ │ └── material-icons
│ │ │ ├── MaterialIcons-Regular.eot
│ │ │ ├── MaterialIcons-Regular.ttf
│ │ │ ├── MaterialIcons-Regular.woff
│ │ │ └── MaterialIcons-Regular.woff2
│ ├── files
│ │ └── phone_numbers_example.csv
│ └── robots.txt
├── mobile_survey
│ └── js
│ │ ├── store
│ │ ├── configureStore.js
│ │ ├── configureStore.dev.js
│ │ └── configureStore.prod.js
│ │ ├── reducers
│ │ ├── index.js
│ │ └── config.js
│ │ ├── components
│ │ ├── Prompt.jsx
│ │ ├── layout
│ │ │ └── Root.jsx
│ │ └── steps
│ │ │ └── EndStep.jsx
│ │ ├── mobileSurvey.jsx
│ │ └── api.js
├── vendor
│ ├── css
│ │ └── materialize
│ │ │ └── components
│ │ │ ├── _mixins.scss
│ │ │ ├── _icons-material-design.scss
│ │ │ ├── _transitions.scss
│ │ │ ├── _ncdvariables.scss
│ │ │ ├── forms
│ │ │ └── _forms.scss
│ │ │ ├── _tooltip.scss
│ │ │ ├── _pulse.scss
│ │ │ └── _table_of_contents.scss
│ └── js
│ │ └── materialize-dropdown-fix.js
└── css
│ └── app.scss
├── .dockerignore
├── lib
├── ask
│ ├── mailer.ex
│ ├── prometheus
│ │ ├── prometheus_exporter.ex
│ │ └── phoenix_instrumenter.ex
│ ├── ecto_types
│ │ ├── schedule_error.ex
│ │ ├── json.ex
│ │ ├── erlang_term.ex
│ │ └── string_list.ex
│ ├── metrics
│ │ └── metrics_endpoint.ex
│ ├── time.ex
│ ├── session.ex
│ ├── model.ex
│ ├── completed_respondents.ex
│ ├── bom_parser.ex
│ ├── questionnaire_variable.ex
│ ├── config_helper.ex
│ ├── survey_questionnaire.ex
│ ├── db_session_store.ex
│ ├── project_channel.ex
│ ├── repo.ex
│ ├── floip_endpoint.ex
│ ├── logger.ex
│ ├── invite.ex
│ ├── file_info.ex
│ ├── respondent_group_channel.ex
│ └── response.ex
├── ask_web
│ ├── templates
│ │ ├── email
│ │ │ ├── channel_down.text.eex
│ │ │ ├── channel_error.text.eex
│ │ │ ├── notify.text.eex
│ │ │ └── invite.text.eex
│ │ ├── coherence
│ │ │ ├── layout
│ │ │ │ ├── email.text.eex
│ │ │ │ └── email.html.eex
│ │ │ ├── email
│ │ │ │ ├── invitation.text.eex
│ │ │ │ ├── confirmation.text.eex
│ │ │ │ ├── password.text.eex
│ │ │ │ ├── unlock.html.eex
│ │ │ │ └── invitation.html.eex
│ │ │ ├── registration
│ │ │ │ ├── edit.html.eex
│ │ │ │ ├── confirmation_expired.html.eex
│ │ │ │ ├── confirmation_sent.html.eex
│ │ │ │ └── show.html.eex
│ │ │ └── password
│ │ │ │ └── password_recovery_sent.html.eex
│ │ ├── page
│ │ │ └── index.html.eex
│ │ ├── o_auth_client
│ │ │ └── callback.html.eex
│ │ ├── mobile_survey
│ │ │ ├── 404.html.eex
│ │ │ ├── index.html.eex
│ │ │ └── unauthorized.html.eex
│ │ ├── mobile_survey_simulation
│ │ │ ├── 404.html.eex
│ │ │ ├── index.html.eex
│ │ │ └── unauthorized.html.eex
│ │ └── layout
│ │ │ ├── app.html.eex
│ │ │ ├── mobile_survey.html.eex
│ │ │ └── mobile_survey_simulation.html.eex
│ ├── views
│ │ ├── email_view.ex
│ │ ├── mobile_survey_view.ex
│ │ ├── coherence
│ │ │ ├── coherence_view.ex
│ │ │ ├── email_view.ex
│ │ │ ├── layout_view.ex
│ │ │ ├── session_view.ex
│ │ │ ├── confirmation_view.ex
│ │ │ └── password_view.ex
│ │ ├── mobile_survey_simulation_view.ex
│ │ ├── page_view.ex
│ │ ├── timezone_view.ex
│ │ ├── audio_view.ex
│ │ ├── user.ex
│ │ ├── oauth_client_view.ex
│ │ └── changeset_view.ex
│ ├── controllers
│ │ ├── coherence
│ │ │ ├── responders
│ │ │ │ └── json.ex
│ │ │ └── confirmation_controller.ex
│ │ ├── callback_controller.ex
│ │ ├── fallback_controller.ex
│ │ ├── audio_delivery_controller.ex
│ │ └── timezone_controller.ex
│ ├── emails
│ │ └── coherence
│ │ │ └── coherence_mailer.ex
│ ├── helpers
│ │ ├── changeset_helper.ex
│ │ └── pagination_helper.ex
│ └── plugs
│ │ ├── sentry_context.ex
│ │ ├── not_allowed_error.ex
│ │ └── unauthorized_error.ex
├── guisso
│ └── token_exchange.ex
└── mix
│ └── tasks
│ └── ecto_dump.ex
├── docs
├── callbacks_flow.png
├── surveda-layers.png
├── verboice_limit.png
├── callcentric_extensions.png
├── callcentric_preferences.png
└── cloud-instance-configs
│ ├── etc-docker-daemon.json
│ ├── haproxy.cfg
│ ├── ona.rancher-compose.yml
│ ├── db.rancher-compose.yml
│ ├── iam.s3-backup-user-policy.json
│ ├── verboice.rancher-compose.yml
│ ├── proxy.docker-compose.yml
│ ├── iam.ses-smtp-user-policy.json
│ └── iam.route53-rancher-user-policy.json
├── integration_tests
├── cypress
│ ├── fixtures
│ │ ├── 2118.zip
│ │ └── respondents_sample.csv
│ ├── integration
│ │ └── projects.spec.js
│ └── support
│ │ └── index.js
├── cypress.sample.json
├── docker-compose.yml
└── package.json
├── .babelrc.js
├── .formatter.exs
├── .vscode
└── settings.json
├── .iex.exs
├── .flowconfig
└── dev-setup.sh
/test/tmp/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/priv/repo/migrations/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/fixtures/invalid.mp3:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | assets/vendor
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | semi: false
2 | printWidth: 100
3 |
--------------------------------------------------------------------------------
/assets/js/decls/jquery.js:
--------------------------------------------------------------------------------
1 | declare var $: Function
2 |
--------------------------------------------------------------------------------
/test/fixtures/respondent_phone_numbers_empty.csv:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/test/fixtures/respondent_phone_numbers_one.csv:
--------------------------------------------------------------------------------
1 | 123456789
--------------------------------------------------------------------------------
/test/support/mocks.ex:
--------------------------------------------------------------------------------
1 | Mox.defmock(Ask.TimeMock, for: Ask.RealTime)
2 |
--------------------------------------------------------------------------------
/test/fixtures/invalid_audio.csv:
--------------------------------------------------------------------------------
1 | Call me! Call me! Baby baby call me now
2 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | .git/
2 | _build/
3 | deps/
4 | node_modules/
5 | **/.DS_Store
6 |
--------------------------------------------------------------------------------
/assets/js/decls/mocha.js:
--------------------------------------------------------------------------------
1 | declare var it: Function
2 | declare var describe: Function
3 |
--------------------------------------------------------------------------------
/lib/ask/mailer.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.Mailer do
2 | use Swoosh.Mailer, otp_app: :ask
3 | end
4 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/email/channel_down.text.eex:
--------------------------------------------------------------------------------
1 | <%= @channel_name %> channel is down
2 |
--------------------------------------------------------------------------------
/test/fixtures/respondent_phone_numbers_r.csv:
--------------------------------------------------------------------------------
1 | 15044020205
15042966031
16302635123
16306979171
--------------------------------------------------------------------------------
/docs/callbacks_flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/docs/callbacks_flow.png
--------------------------------------------------------------------------------
/docs/surveda-layers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/docs/surveda-layers.png
--------------------------------------------------------------------------------
/docs/verboice_limit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/docs/verboice_limit.png
--------------------------------------------------------------------------------
/lib/ask_web/views/email_view.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.EmailView do
2 | use AskWeb, :view
3 | end
4 |
--------------------------------------------------------------------------------
/test/fixtures/audio.aac:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/test/fixtures/audio.aac
--------------------------------------------------------------------------------
/test/fixtures/audio.m4a:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/test/fixtures/audio.m4a
--------------------------------------------------------------------------------
/test/fixtures/audio.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/test/fixtures/audio.mp3
--------------------------------------------------------------------------------
/test/fixtures/audio.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/test/fixtures/audio.wav
--------------------------------------------------------------------------------
/assets/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/favicon.ico
--------------------------------------------------------------------------------
/test/fixtures/respondent_phone_numbers_invalid.csv:
--------------------------------------------------------------------------------
1 | aa15123412
2 | bb523511521
3 | cc125132115123
4 |
--------------------------------------------------------------------------------
/assets/static/images/map.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/map.png
--------------------------------------------------------------------------------
/docs/callcentric_extensions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/docs/callcentric_extensions.png
--------------------------------------------------------------------------------
/lib/ask_web/templates/coherence/layout/email.text.eex:
--------------------------------------------------------------------------------
1 | <%= render @view_module, @view_template, assigns %>
2 |
--------------------------------------------------------------------------------
/lib/ask_web/views/mobile_survey_view.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.MobileSurveyView do
2 | use AskWeb, :view
3 | end
4 |
--------------------------------------------------------------------------------
/assets/static/images/phoenix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/phoenix.png
--------------------------------------------------------------------------------
/docs/callcentric_preferences.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/docs/callcentric_preferences.png
--------------------------------------------------------------------------------
/test/fixtures/respondent_phone_numbers_duplicated2.csv:
--------------------------------------------------------------------------------
1 | +1234567
2 | 1234567
3 | (+1)234567
4 | 1234567
5 | (12) 34-567
--------------------------------------------------------------------------------
/test/fixtures/respondent_phone_numbers_newline.csv:
--------------------------------------------------------------------------------
1 | 15044020205
2 | 15042966031
3 | 16302635123
4 | 16306979171
5 |
--------------------------------------------------------------------------------
/test/fixtures/respondent_phone_numbers_special_characters.csv:
--------------------------------------------------------------------------------
1 | +154 11 1213 2345
2 | 4545-2829
3 | (011)1234-3322
4 |
--------------------------------------------------------------------------------
/assets/static/images/header_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/header_icon.png
--------------------------------------------------------------------------------
/assets/static/images/nuntium@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/nuntium@2x.png
--------------------------------------------------------------------------------
/assets/static/images/verboice@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/verboice@2x.png
--------------------------------------------------------------------------------
/assets/static/images/warning@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/warning@2x.png
--------------------------------------------------------------------------------
/lib/ask/prometheus/prometheus_exporter.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.PrometheusExporter do
2 | use Prometheus.PlugExporter
3 | end
4 |
--------------------------------------------------------------------------------
/test/ask_web/views/page_view_test.exs:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.PageViewTest do
2 | use AskWeb.ConnCase, async: true
3 | end
4 |
--------------------------------------------------------------------------------
/assets/static/images/email-logo@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/email-logo@2x.png
--------------------------------------------------------------------------------
/assets/static/images/surveda-name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/surveda-name.png
--------------------------------------------------------------------------------
/assets/static/images/welcome-img01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/welcome-img01.jpg
--------------------------------------------------------------------------------
/assets/static/images/welcome-img01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/welcome-img01.png
--------------------------------------------------------------------------------
/assets/static/images/welcome-img02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/welcome-img02.png
--------------------------------------------------------------------------------
/assets/static/images/welcome-img03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/welcome-img03.png
--------------------------------------------------------------------------------
/assets/static/images/welcome-img04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/welcome-img04.png
--------------------------------------------------------------------------------
/assets/static/images/welcome-main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/welcome-main.png
--------------------------------------------------------------------------------
/lib/ask/ecto_types/schedule_error.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.ScheduleError do
2 | defexception message: "schedule error"
3 | end
4 |
--------------------------------------------------------------------------------
/lib/ask_web/views/coherence/coherence_view.ex:
--------------------------------------------------------------------------------
1 | defmodule Coherence.CoherenceView do
2 | use AskWeb.Coherence, :view
3 | end
4 |
--------------------------------------------------------------------------------
/lib/ask_web/views/coherence/email_view.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.Coherence.EmailView do
2 | use AskWeb.Coherence, :view
3 | end
4 |
--------------------------------------------------------------------------------
/assets/static/images/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/favicon.ico
--------------------------------------------------------------------------------
/lib/ask/prometheus/phoenix_instrumenter.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.PhoenixInstrumenter do
2 | use Prometheus.PhoenixInstrumenter
3 | end
4 |
--------------------------------------------------------------------------------
/lib/ask_web/controllers/coherence/responders/json.ex:
--------------------------------------------------------------------------------
1 | defmodule Coherence.Responders.Json do
2 | use Responders.Json
3 | end
4 |
--------------------------------------------------------------------------------
/lib/ask_web/views/mobile_survey_simulation_view.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.MobileSurveySimulationView do
2 | use AskWeb, :view
3 | end
4 |
--------------------------------------------------------------------------------
/assets/static/fonts/roboto/Roboto-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/fonts/roboto/Roboto-Bold.woff
--------------------------------------------------------------------------------
/assets/static/fonts/roboto/Roboto-Thin.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/fonts/roboto/Roboto-Thin.woff
--------------------------------------------------------------------------------
/integration_tests/cypress/fixtures/2118.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/integration_tests/cypress/fixtures/2118.zip
--------------------------------------------------------------------------------
/assets/static/fonts/roboto/Roboto-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/fonts/roboto/Roboto-Bold.woff2
--------------------------------------------------------------------------------
/assets/static/fonts/roboto/Roboto-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/fonts/roboto/Roboto-Light.woff
--------------------------------------------------------------------------------
/assets/static/fonts/roboto/Roboto-Light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/fonts/roboto/Roboto-Light.woff2
--------------------------------------------------------------------------------
/assets/static/fonts/roboto/Roboto-Medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/fonts/roboto/Roboto-Medium.woff
--------------------------------------------------------------------------------
/assets/static/fonts/roboto/Roboto-Medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/fonts/roboto/Roboto-Medium.woff2
--------------------------------------------------------------------------------
/assets/static/fonts/roboto/Roboto-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/fonts/roboto/Roboto-Regular.woff
--------------------------------------------------------------------------------
/assets/static/fonts/roboto/Roboto-Thin.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/fonts/roboto/Roboto-Thin.woff2
--------------------------------------------------------------------------------
/assets/static/images/favicon/favicon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/favicon-128.png
--------------------------------------------------------------------------------
/assets/static/images/favicon/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/favicon-16x16.png
--------------------------------------------------------------------------------
/assets/static/images/favicon/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/favicon-32x32.png
--------------------------------------------------------------------------------
/assets/static/images/favicon/favicon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/favicon-96x96.png
--------------------------------------------------------------------------------
/assets/static/images/favicon/mstile-70x70.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/mstile-70x70.png
--------------------------------------------------------------------------------
/assets/static/fonts/roboto/Roboto-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/fonts/roboto/Roboto-Regular.woff2
--------------------------------------------------------------------------------
/assets/static/images/favicon/favicon-196x196.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/favicon-196x196.png
--------------------------------------------------------------------------------
/assets/static/images/favicon/mstile-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/mstile-144x144.png
--------------------------------------------------------------------------------
/assets/static/images/favicon/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/mstile-150x150.png
--------------------------------------------------------------------------------
/assets/static/images/favicon/mstile-310x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/mstile-310x150.png
--------------------------------------------------------------------------------
/assets/static/images/favicon/mstile-310x310.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/mstile-310x310.png
--------------------------------------------------------------------------------
/test/js/mocha.opts:
--------------------------------------------------------------------------------
1 | --compilers js:@babel/register
2 | -r jsdom-global/register
3 | --reporter dot
4 | --recursive
5 | --require ignore-styles
6 |
--------------------------------------------------------------------------------
/test/fixtures/respondent_phone_numbers_utf16le.csv:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/test/fixtures/respondent_phone_numbers_utf16le.csv
--------------------------------------------------------------------------------
/assets/js/decls/project.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | export type Project = {
3 | id: number,
4 | name: string,
5 | updatedAt: string,
6 | readOnly: boolean,
7 | }
8 |
--------------------------------------------------------------------------------
/assets/js/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6",
4 | "module": "es2015",
5 | "experimentalDecorators": true
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/assets/static/images/favicon/apple-touch-icon-57x57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/apple-touch-icon-57x57.png
--------------------------------------------------------------------------------
/assets/static/images/favicon/apple-touch-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/apple-touch-icon-60x60.png
--------------------------------------------------------------------------------
/assets/static/images/favicon/apple-touch-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/apple-touch-icon-72x72.png
--------------------------------------------------------------------------------
/assets/static/images/favicon/apple-touch-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/apple-touch-icon-76x76.png
--------------------------------------------------------------------------------
/lib/ask/metrics/metrics_endpoint.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.MetricsEndpoint do
2 | use Phoenix.Endpoint, otp_app: :ask
3 |
4 | plug Ask.PrometheusExporter
5 | end
6 |
--------------------------------------------------------------------------------
/assets/static/images/favicon/apple-touch-icon-114x114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/apple-touch-icon-114x114.png
--------------------------------------------------------------------------------
/assets/static/images/favicon/apple-touch-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/apple-touch-icon-120x120.png
--------------------------------------------------------------------------------
/assets/static/images/favicon/apple-touch-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/apple-touch-icon-144x144.png
--------------------------------------------------------------------------------
/assets/static/images/favicon/apple-touch-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/images/favicon/apple-touch-icon-152x152.png
--------------------------------------------------------------------------------
/assets/static/fonts/material-icons/MaterialIcons-Regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/fonts/material-icons/MaterialIcons-Regular.eot
--------------------------------------------------------------------------------
/assets/static/fonts/material-icons/MaterialIcons-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/fonts/material-icons/MaterialIcons-Regular.ttf
--------------------------------------------------------------------------------
/assets/static/fonts/material-icons/MaterialIcons-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/fonts/material-icons/MaterialIcons-Regular.woff
--------------------------------------------------------------------------------
/assets/static/fonts/material-icons/MaterialIcons-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instedd/surveda/HEAD/assets/static/fonts/material-icons/MaterialIcons-Regular.woff2
--------------------------------------------------------------------------------
/docs/cloud-instance-configs/etc-docker-daemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "log-driver": "json-file",
3 | "log-opts": {
4 | "max-size": "100m",
5 | "max-file": "3"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/email/channel_error.text.eex:
--------------------------------------------------------------------------------
1 | Error when connecting with <%= @channel_name %> channel
2 |
3 | Follow this link to go to the channel: <%= @url %>
4 |
--------------------------------------------------------------------------------
/.babelrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | ["@babel/preset-env", { targets: "defaults" }],
4 | "@babel/preset-react",
5 | "@babel/preset-flow"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/assets/js/components/layout/Root.jsx:
--------------------------------------------------------------------------------
1 | if (window.mixEnv == "prod") {
2 | module.exports = require("./Root.prod")
3 | } else {
4 | module.exports = require("./Root.dev")
5 | }
6 |
--------------------------------------------------------------------------------
/lib/ask_web/views/page_view.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.PageView do
2 | use AskWeb, :view
3 |
4 | def config_intercom(_conn) do
5 | Ask.Intercom.config_intercom()
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/assets/js/store/configureStore.js:
--------------------------------------------------------------------------------
1 | if (window.mixEnv == "prod") {
2 | module.exports = require("./configureStore.prod")
3 | } else {
4 | module.exports = require("./configureStore.dev")
5 | }
6 |
--------------------------------------------------------------------------------
/docs/cloud-instance-configs/haproxy.cfg:
--------------------------------------------------------------------------------
1 | listen stats
2 | bind 0.0.0.0:9000
3 | mode http
4 | stats enable
5 | stats hide-version
6 | stats realm Haproxy\ Statistics
7 | stats uri /haproxy_stats
8 |
--------------------------------------------------------------------------------
/docs/cloud-instance-configs/ona.rancher-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 | services:
3 | app:
4 | scale: 1
5 | start_on_create: true
6 | db:
7 | scale: 1
8 | start_on_create: true
9 |
--------------------------------------------------------------------------------
/assets/static/files/phone_numbers_example.csv:
--------------------------------------------------------------------------------
1 | (012) 3456-7890
2 | (012) 34 5678-9013
3 | (012) 34 5678-9014
4 | (012) 34 5678-9015
5 | (012) 34 5678-9011
6 | (012) 34 5678-8235
7 | (012) 34 2629-2276
8 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/page/index.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/docs/cloud-instance-configs/db.rancher-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 | services:
3 | mysql:
4 | scale: 1
5 | start_on_create: true
6 | mysql-backup:
7 | scale: 1
8 | start_on_create: true
9 |
--------------------------------------------------------------------------------
/.formatter.exs:
--------------------------------------------------------------------------------
1 | [
2 | import_deps: [:phoenix, :ecto, :ecto_sql],
3 | inputs: ["*.{ex,exs}", "{config,lib,priv,test}/**/*.{ex,exs}"],
4 | locals_without_parens: [
5 | coherence_routes: :*
6 | ]
7 | ]
8 |
--------------------------------------------------------------------------------
/assets/mobile_survey/js/store/configureStore.js:
--------------------------------------------------------------------------------
1 | if (window.mixEnv == "prod") {
2 | module.exports = require("./configureStore.prod")
3 | } else {
4 | module.exports = require("./configureStore.dev")
5 | }
6 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/email/notify.text.eex:
--------------------------------------------------------------------------------
1 | <%= @invited_by %> has added you as a collaborator on <%= @project_name %>.
2 |
3 | <%= @explanation %>
4 |
5 | Follow this link to see the project: <%= @url %>
6 |
--------------------------------------------------------------------------------
/assets/vendor/css/materialize/components/_mixins.scss:
--------------------------------------------------------------------------------
1 | @mixin box-shadow-2($args1, $args2) {
2 | -webkit-box-shadow: $args1, $args2;
3 | -moz-box-shadow: $args1, $args2;
4 | box-shadow: $args1, $args2;
5 | }
--------------------------------------------------------------------------------
/lib/ask_web/emails/coherence/coherence_mailer.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.Coherence.Mailer do
2 | @moduledoc false
3 | if Coherence.Config.mailer?() do
4 | use Swoosh.Mailer, otp_app: :coherence
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/coherence/email/invitation.text.eex:
--------------------------------------------------------------------------------
1 | Hello <%= @name %>!
2 |
3 | You have been invited to create an Account. Use the link below to create an account.
4 |
5 | <%= @url %>
6 |
7 | Thank you!
8 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/email/invite.text.eex:
--------------------------------------------------------------------------------
1 | <%= @invited_by %> has invited you to collaborate on <%= @project_name %>.
2 |
3 | <%= @explanation %>
4 |
5 | Follow this link to accept the invitation: <%= @url %>
6 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "javascript.validate.enable": false,
4 | "[elixir]": {
5 | "editor.formatOnSave": false
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/lib/ask_web/views/coherence/layout_view.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.Coherence.LayoutView do
2 | use AskWeb.Coherence, :view
3 |
4 | def config_intercom(_conn) do
5 | Ask.Intercom.config_intercom()
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/coherence/email/confirmation.text.eex:
--------------------------------------------------------------------------------
1 | Hello <%= @name %>!
2 |
3 | Your new Surveda account is almost ready, follow the link below to confirm and activate it.
4 |
5 | <%= @url %>
6 |
7 | Thank you!
8 |
--------------------------------------------------------------------------------
/test/test_helper.exs:
--------------------------------------------------------------------------------
1 | ExUnit.start(exclude: [:skip])
2 |
3 | {:ok, _} = Application.ensure_all_started(:ex_machina)
4 | {:ok, _} = Application.ensure_all_started(:bypass)
5 | {:ok, _} = Application.ensure_all_started(:mox)
6 |
--------------------------------------------------------------------------------
/.iex.exs:
--------------------------------------------------------------------------------
1 | import Ecto
2 | import Ecto.Query
3 |
4 | alias Ask.{
5 | Repo,
6 | Channel,
7 | Project,
8 | Questionnaire,
9 | Respondent,
10 | Response,
11 | Survey,
12 | SurveyChannel,
13 | User
14 | }
15 |
--------------------------------------------------------------------------------
/lib/ask_web/helpers/changeset_helper.ex:
--------------------------------------------------------------------------------
1 | defmodule Changeset.Helper do
2 | def changed_properties(changeset) do
3 | changes = changeset.changes
4 | Enum.filter(Map.keys(changes), fn key -> changes[key] != [] end)
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/lib/ask_web/views/timezone_view.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.TimezoneView do
2 | use AskWeb, :view
3 |
4 | def render("index.json", %{timezones: timezones}) do
5 | %{
6 | timezones: timezones
7 | }
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170126172256_confirm_all_users.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.ConfirmAllUsers do
2 | use Ecto.Migration
3 |
4 | def change do
5 | Ask.Repo.query!("UPDATE users SET confirmed_at=NOW()")
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/assets/mobile_survey/js/reducers/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { combineReducers } from "redux"
3 | import step from "./step"
4 | import config from "./config"
5 |
6 | export default combineReducers({
7 | step,
8 | config,
9 | })
10 |
--------------------------------------------------------------------------------
/assets/static/robots.txt:
--------------------------------------------------------------------------------
1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 | #
3 | # To ban all spiders from the entire site uncomment the next two lines:
4 | # User-agent: *
5 | # Disallow: /
6 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161014181536_add_unique_email_index_to_users.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddUniqueEmailIndexToUsers do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create unique_index(:users, [:email])
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/test/support/base64_hasher.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.Base64Hasher do
2 | def hashpwsalt(password) do
3 | Base.encode64(password)
4 | end
5 |
6 | def checkpw(password, hash) do
7 | Base.decode64!(hash) == password
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161101174551_add_mode_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddModeToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :mode, :text
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161101174731_set_mode_to_existing_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.SetModeToExistingSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | Ask.Repo.query!("update surveys set mode = '[\"sms\"]'")
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/assets/vendor/css/materialize/components/_icons-material-design.scss:
--------------------------------------------------------------------------------
1 | /* This is needed for some mobile phones to display the Google Icon font properly */
2 | .material-icons {
3 | text-rendering: optimizeLegibility;
4 | font-feature-settings: 'liga';
5 | }
6 |
--------------------------------------------------------------------------------
/lib/ask/time.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.SystemTime do
2 | def time(), do: Application.get_env(:ask, :time, Ask.RealTime)
3 | end
4 |
5 | defmodule Ask.RealTime do
6 | @callback now() :: DateTime
7 | def now() do
8 | DateTime.utc_now()
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/o_auth_client/callback.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160909195628_add_state_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddStateToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :state, :string
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/assets/js/components/questionnaires/withQuestionnaire.jsx.flow:
--------------------------------------------------------------------------------
1 | // @flow
2 | declare function withQuestionnaire(x: Class>):
3 | Class, S>>
4 |
5 | module.exports = withQuestionnaire
6 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/coherence/registration/edit.html.eex:
--------------------------------------------------------------------------------
1 | Edit Account
2 |
3 | <%= render "form.html", changeset: @changeset,
4 | label: "Update", required: [],
5 | action: registration_path(@conn, :update) %>
6 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160913171925_add_cutoff_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddCutoffToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :cutoff, :integer
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161014221336_set_questionnaires_mode_to_sms.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.SetQuestionnairesModeToSms do
2 | use Ecto.Migration
3 |
4 | def change do
5 | Ask.Repo.query!("update questionnaires set modes = 'SMS'")
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161104165607_set_timezone_to_existing_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.SetTimezoneToExistingSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | Ask.Repo.query!("update surveys set timezone = 'Etc/UTC'")
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161123205617_add_quotas_to_survey.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddQuotasToSurvey do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :quota_vars, :text
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170109180032_add_settings_to_users.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddSettingsToUsers do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:users) do
6 | add :settings, :text
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170119184752_add_name_to_users.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddNameToUsers do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:users) do
6 | add :name, :string, default: ""
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170329214637_mark_existing_surveys_as_valid.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.MarkExistingSurveysAsValid do
2 | use Ecto.Migration
3 |
4 | def change do
5 | Ask.Repo.query!("update questionnaires set valid = ?", [true])
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/lib/ask/ecto_types/json.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.Ecto.Type.JSON do
2 | use Ecto.Type
3 | def type, do: :longtext
4 | def cast(any), do: {:ok, any}
5 | def load(string) when is_binary(string), do: Poison.decode(string)
6 | def dump(json), do: Poison.encode(json)
7 | end
8 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161101163857_downcase_questionnaire_modes.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.DowncaseQuestionnaireModes do
2 | use Ecto.Migration
3 |
4 | def change do
5 | Ask.Repo.query!("update questionnaires set modes = lower(modes)")
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161104145701_add_timezone_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddTimezoneToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :timezone, :text
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161227192753_index_respondents_by_survey_id_and_state.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.IndexRespondentsBySurveyIdAndState do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create index(:respondents, [:survey_id, :state])
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161229195124_index_respondents_by_state_and_timeout.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.IndexRespondentsByStateAndTimeout do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create index(:respondents, [:state, :timeout_at])
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20241007192540_pause_channel.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.PauseChannel do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:channels) do
6 | add :paused, :boolean, default: false
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/assets/js/store/configureStore.dev.js:
--------------------------------------------------------------------------------
1 | import prodStore from "./configureStore.prod"
2 | import createLogger from "redux-logger"
3 |
4 | export default function configureStore(preloadedState) {
5 | return prodStore(preloadedState, [createLogger({ collapsed: true })])
6 | }
7 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160916194539_add_session_to_respondent.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddSessionToRespondent do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondents) do
6 | add :session, :text
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170130141552_put_sth_in_password_hash.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.PutSthInPasswordHash do
2 | use Ecto.Migration
3 |
4 | def change do
5 | Ask.Repo.query!("UPDATE users SET password_hash='1' WHERE password_hash IS NULL")
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170301154642_add_scope_to_translation.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddScopeToTranslation do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:translations) do
6 | add :scope, :string
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20171122200548_add_stats_to_respondents.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddStatsToRespondents do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondents) do
6 | add :stats, :longtext
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20190329144509_add_unique_constraint_on_name_in_folders.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddUniqueConstraintOnNameInFolders do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create unique_index(:folders, [:name, :project_id])
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160906173441_add_modes_to_questionnaire.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddModesToQuestionnaire do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:questionnaires) do
6 | add :modes, :string
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160909152241_add_steps_to_questionnaires.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddStepsToQuestionnaires do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:questionnaires) do
6 | add :steps, :text
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170102165430_add_comparisons_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddComparisonsToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :comparisons, :text
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20190328175014_create_folder.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateFolder do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:folders) do
6 | add :name, :string
7 |
8 | timestamps()
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20191127150645_add_ended_at_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddEndedAtToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :ended_at, :naive_datetime
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20230402091100_add_batch_size_to_projects.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddBatchSizeToProjects do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:projects) do
6 | add :batch_size, :integer
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/assets/mobile_survey/js/store/configureStore.dev.js:
--------------------------------------------------------------------------------
1 | import prodStore from "./configureStore.prod"
2 | import createLogger from "redux-logger"
3 |
4 | export default function configureStore(preloadedState) {
5 | return prodStore(preloadedState, [createLogger({ collapsed: true })])
6 | }
7 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161110190547_add_started_at_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddLaunchedAtToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :started_at, :naive_datetime
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170105213430_add_inviter_email_to_invites.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddInviterEmailToInvites do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:invites) do
6 | add :inviter_email, :text
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170215173627_add_fallback_delay_to_survey.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddFallbackDelayToSurvey do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :fallback_delay, :string
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170315151438_set_default_colour_scheme_to_projects.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.SetDefaultColourSchemeToProjects do
2 | use Ecto.Migration
3 |
4 | def change do
5 | Ask.Repo.query!(~s(update projects set colour_scheme = 'default'))
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170329195359_add_valid_to_questionnaires.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddValidToQuestionnaires do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:questionnaires) do
6 | add :valid, :boolean
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170428143816_add_language_to_respondents.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddLanguageToRespondents do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondents) do
6 | add :language, :string
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170731210541_index_respondents_by_sanitized_phone_number.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.IndexRespondentsBySanitizedPhoneNumber do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create index(:respondents, [:sanitized_phone_number])
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/test/js/spec_helper.js:
--------------------------------------------------------------------------------
1 | import each from 'lodash/each'
2 |
3 | export const playActionsFromState = (state, reducer) => (actions) => {
4 | let resultState = state
5 | each(actions, (a) => {
6 | resultState = reducer(resultState, a)
7 | })
8 | return resultState
9 | }
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160914215134_add_state_to_respondent.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddStateToRespondent do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondents) do
6 | add :state, :string, default: "pending"
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161223131256_add_error_msg_to_quesionnaires.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddErrorMsgToQuesionnaires do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:questionnaires) do
6 | add :error_msg, :text
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170424180020_add_settings_to_questionnaires.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddSettingsToQuestionnaires do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:questionnaires) do
6 | add :settings, :text
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20180131174235_add_archived_to_projects.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddArchivedToProjects do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:projects) do
6 | add :archived, :boolean, default: false
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20220131103226_add_unconfirmed_email_to_users.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddUnconfirmedEmailToUsers do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:users) do
6 | add :unconfirmed_email, :string
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/assets/js/decls/channel.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | export type Channel = {
3 | userId?: number,
4 | name: string,
5 | type: string,
6 | projects: Project[],
7 | provider: string,
8 | settings: {},
9 | patterns: Array<{ input: string, output: string }>,
10 | errorsByPath: {},
11 | }
12 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161108201151_add_timeout_at_to_respondent.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddTimeoutAtToRespondent do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondents) do
6 | add :timeout_at, :naive_datetime
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170519125438_add_simulation_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddSimulationToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :simulation, :boolean, default: false
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20180226163507_add_state_to_floip_endpoint_table.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddStateToFloipEndpointTable do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:floip_endpoints) do
6 | add :state, :string
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20190821214420_add_section_order_to_respondent.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddSectionOrderToRespondent do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondents) do
6 | add :section_order, :string
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160926225157_add_expires_at_to_oauth_tokens.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddExpiresAtToOauthTokens do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:oauth_tokens) do
6 | add :expires_at, :naive_datetime
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161004181711_add_completed_at_to_respondents.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddCompletedAtToRespondents do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondents) do
6 | add :completed_at, :utc_datetime
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161130183834_add_quota_bucket_id_to_respondent.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddQuotaBucketIdToRespondent do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondents) do
6 | add :quota_bucket_id, :integer
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170524132106_add_effective_modes_to_respondent.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddEffectiveModesToRespondent do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondents) do
6 | add :effective_modes, :string
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20191204182139_add_retry_stat_time_to_respondent.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddRetryStatTimeToRespondent do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondents) do
6 | add :retry_stat_time, :string
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/integration_tests/cypress.sample.json:
--------------------------------------------------------------------------------
1 | {
2 | "baseUrl": "https://surveda-stg.instedd.org",
3 | "env": {
4 | "guisso_host": "https://login-stg.instedd.org",
5 | "email": "foo@bar.com",
6 | "password": "foobar",
7 | "project_id": "1",
8 | "sms_channel_id": "1"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160906161147_remove_description_from_questionnaires.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.RemoveDescriptionFromQuestionnaires do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:questionnaires) do
6 | remove :description
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170131101552_add_remember_created_at_to_users.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddRememberCreatedAtToUsers do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:users) do
6 | add :remember_created_at, :naive_datetime
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170308202613_add_lock_version_to_respondents.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddLockVersionToRespondents do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondents) do
6 | add :lock_version, :integer, default: 1
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170622182023_add_deleted_to_questionnaires.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddDeletedToQuestionnaires do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:questionnaires) do
6 | add :deleted, :boolean, default: false
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/integration_tests/cypress/fixtures/respondents_sample.csv:
--------------------------------------------------------------------------------
1 | 1000000
2 | 1000001
3 | 1000002
4 | 1000003
5 | 1000004
6 | 1000005
7 | 1000006
8 | 1000007
9 | 1000008
10 | 1000009
11 | 1000010
12 | 1000011
13 | 1000012
14 | 1000013
15 | 1000014
16 | 1000015
17 | 1000016
18 | 1000017
19 | 1000018
20 | 1000019
21 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170306224401_add_mode_to_respondent_group_channels.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddModeToRespondentGroupChannels do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondent_group_channels) do
6 | add :mode, :string
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170315145645_add_colour_scheme_to_projects.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddColourSchemeToProjects do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:projects) do
6 | add :colour_scheme, :string, default: "default"
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20180226195859_add_auth_token_to_floip_endpoint_table.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddAuthTokenToFloipEndpointTable do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:floip_endpoints) do
6 | add :auth_token, :string
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20200907093359_add_archived_to_questionnaires.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddArchivedToQuestionnaires do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:questionnaires) do
6 | add :archived, :boolean, default: false
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20210326160009_add_last_window_ends_at_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddLastWindowEndsAtToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :last_window_ends_at, :naive_datetime
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/lib/ask_web/views/audio_view.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.AudioView do
2 | use AskWeb, :view
3 |
4 | def render("show.json", %{audio: audio}) do
5 | %{data: render_one(audio, AskWeb.AudioView, "audio.json")}
6 | end
7 |
8 | def render("audio.json", %{audio: audio}) do
9 | audio
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20190621180921_index_respondent_disposition_history_by_survey_id_and_id.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.IndexRespondentDispositionHistoryBySurveyIdAndId do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create index(:respondent_disposition_history, [:survey_id, :id])
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20210211131024_add_incentives_enabled_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddIncentivesEnabledToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :incentives_enabled, :boolean, default: true
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20230317094712_add_batch_limit_per_minute_to_projects.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddBatchLimitPerMinuteToProjects do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:projects) do
6 | add :batch_limit_per_minute, :integer
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160812145257_create_user.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateUser do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:users) do
6 | add :email, :string
7 | add :encrypted_password, :string
8 |
9 | timestamps()
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161116163735_add_sanitized_phone_number_to_respondent.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddSanitizedPhoneNumberToRespondent do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondents) do
6 | add :sanitized_phone_number, :string
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170417193442_add_mobile_web_cookie_code_to_respondents.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddMobileWebCookieCodeToRespondents do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondents) do
6 | add :mobile_web_cookie_code, :string
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20200211130308_add_respondents_canonical_phone_number.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddRespondentsCanonicalPhoneNumber do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondents) do
6 | add :canonical_phone_number, :string
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20210323192808_add_first_window_started_at_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddFirstWindowStartedAtToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :first_window_started_at, :naive_datetime
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/lib/ask_web/controllers/callback_controller.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.CallbackController do
2 | use AskWeb, :controller
3 |
4 | alias Ask.Channel
5 |
6 | def callback(conn, params = %{"provider" => provider}) do
7 | channel = Channel.provider(provider)
8 | channel.callback(conn, params)
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/coherence/registration/confirmation_expired.html.eex:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170216184642_add_count_partial_results_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddCountPartialResultsToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :count_partial_results, :boolean, default: false
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170327142610_add_mode_to_respondent_disposition_history.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddModeToRespondentDispositionHistory do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondent_disposition_history) do
6 | add :mode, :string
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170508193540_add_questionnaire_quota_completed_steps.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddQuestionnaireQuotaCompletedSteps do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:questionnaires) do
6 | add :quota_completed_steps, :longtext
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20191213131858_remove_invalid_retry_stat_time_in_respondents.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.RemoveInvalidRetryStatTimeInRespondents do
2 | use Ecto.Migration
3 |
4 | def up do
5 | execute "UPDATE respondents SET retry_stat_time = null WHERE CHAR_LENGTH(retry_stat_time) > 10"
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/assets/js/reducers/ui/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from "redux"
2 | import questionnaireEditor from "./questionnaireEditor"
3 | import surveyWizard from "./surveyWizard"
4 |
5 | const data = combineReducers({
6 | questionnaireEditor,
7 | surveyWizard,
8 | })
9 |
10 | export default combineReducers({ data })
11 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161201181704_add_quota_completed_message_to_questionnaires.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddQuotaCompletedMessageToQuestionnaires do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:questionnaires) do
6 | add :quota_completed_msg, :text
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170308192425_add_mobile_web_sms_message_to_questionnaire.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddMobileWebSmsMessageToQuestionnaire do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:questionnaires) do
6 | add :mobile_web_sms_message, :string
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170406224331_add_mobileweb_retry_configuration_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddMobilewebRetryConfigurationToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :mobileweb_retry_configuration, :text
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20180503032000_modify_survey_log_entry_action_data_to_long_text.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.ModifySurveyLogEntryActionDataToLongText do
2 | use Ecto.Migration
3 |
4 | def up do
5 | alter table(:survey_log_entries) do
6 | modify :action_data, :longtext
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20210614154820_add_generates_panel_survey_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddGeneratesPanelSurveyToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :generates_panel_survey, :boolean, default: false
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/coherence/password/password_recovery_sent.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
3 | email
4 |
Password reset link sent
5 | We sent you an email with a password reset link. Please click that link to reset your password.
6 |
7 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160905173031_add_questionnaire_id_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddQuestionnaireIdToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :questionnaire_id, references(:questionnaires, on_delete: :nothing)
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170116175546_add_respondent_group_id_to_respondents.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddRespondentGroupIdToRespondents do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondents) do
6 | add :respondent_group_id, references(:respondent_groups)
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/docs/cloud-instance-configs/iam.s3-backup-user-policy.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2012-10-17",
3 | "Statement": [
4 | {
5 | "Sid": "VisualEditor0",
6 | "Effect": "Allow",
7 | "Action": "s3:PutObject",
8 | "Resource": "arn:aws:s3:::instedd-backups/surveda-xx/*"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170503172812_add_snapshot_of_to_questionnaires.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddSnapshotOfToQuestionnaires do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:questionnaires) do
6 | add :snapshot_of, references(:questionnaires, on_delete: :nothing)
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170601040741_index_survey_log_entries_by_survey_id_respondent_hash_and_id.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.IndexSurveyLogEntriesBySurveyIdRespondentHashAndId do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create index(:survey_log_entries, [:survey_id, :respondent_hashed_number, :id])
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20190328182425_add_project_to_folders.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddProjectToFolders do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:folders) do
6 | add :project_id, references(:projects)
7 | end
8 |
9 | create index(:folders, [:project_id])
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20190401140258_add_folder_id_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddFolderIdToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :folder_id, references(:folders)
7 | end
8 |
9 | create index(:surveys, [:folder_id])
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/assets/static/images/file_download_off.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/integration_tests/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2.0'
2 |
3 | volumes:
4 | node_modules:
5 |
6 | services:
7 | cypress:
8 | image: cypress/base:12
9 | working_dir: /src
10 | volumes:
11 | - '.:/src'
12 | - 'node_modules:/src/node_modules'
13 | command: >
14 | /bin/sh -c 'yarn && npm run cypress:run'
15 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170505203058_create_respondents_index_by_hashed_number_and_updated_at.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateRespondentsIndexByHashedNumberAndUpdatedAt do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create index(:respondents, [:hashed_number])
6 | create index(:respondents, [:updated_at])
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/assets/vendor/js/materialize-dropdown-fix.js:
--------------------------------------------------------------------------------
1 | // This is a workaround until this PR is released in Materialize: https://github.com/Dogfalo/materialize/pull/6339
2 | // Suggested by @burnEAx: https://github.com/Dogfalo/materialize/issues/6336#issuecomment-523409695
3 |
4 | $(document).on('click', '.select-wrapper', function (e) { e.stopPropagation(); })
5 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/coherence/registration/confirmation_sent.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
3 | email
4 |
Thank you for signing up to Surveda
5 | We sent you an email with a confirmation link. Please confirm your account by clicking on that link.
6 |
7 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161109053659_add_retry_configuration_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddRetryConfigurationToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :sms_retry_configuration, :text
7 | add :ivr_retry_configuration, :text
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161118175719_set_languages_and_default_language_to_existing_questionnaires.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.SetLanguagesAndDefaultLanguageToExistingQuestionnaires do
2 | use Ecto.Migration
3 |
4 | def change do
5 | Ask.Repo.query!(~s(update questionnaires set languages = '["en"]', default_language = 'en'))
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20171023212559_create_short_links.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateShortLinks do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:short_links) do
6 | add :hash, :string
7 | add :name, :string
8 | add :target, :string
9 |
10 | timestamps()
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/assets/mobile_survey/js/store/configureStore.prod.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware, compose } from "redux"
2 | import rootReducer from "../reducers"
3 |
4 | export default function configureStore(preState, middlewares = [], enhancers = []) {
5 | return createStore(rootReducer, preState, compose(applyMiddleware(...middlewares), ...enhancers))
6 | }
7 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170417172528_add_mobile_web_survey_is_over_message_to_questionnaire.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddMobileWebSurveyIsOverMessageToQuestionnaire do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:questionnaires) do
6 | add :mobile_web_survey_is_over_message, :string
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/assets/vendor/css/materialize/components/_transitions.scss:
--------------------------------------------------------------------------------
1 | // Scale transition
2 | .scale-transition {
3 | &.scale-out {
4 | transform: scale(0);
5 | transition: transform .2s !important;
6 | }
7 |
8 | &.scale-in {
9 | transform: scale(1);
10 | }
11 |
12 | transition: transform .3s cubic-bezier(0.53, 0.01, 0.36, 1.63) !important;
13 | }
--------------------------------------------------------------------------------
/lib/ask_web/plugs/sentry_context.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.Plugs.SentryContext do
2 | import User.Helper
3 |
4 | def init(default), do: default
5 |
6 | def call(conn, _) do
7 | case current_user(conn) do
8 | nil -> :ok
9 | user -> Sentry.Context.set_user_context(%{email: user.email})
10 | end
11 |
12 | conn
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161013170416_change_survey_schedule_start_and_end_to_time.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.ChangeSurveyScheduleStartAndEndToTime do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | modify :schedule_start_time, :time
7 | modify :schedule_end_time, :time
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170109115814_add_questionnaire_and_mode_to_respondents.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddQuestionnaireAndModeToRespondents do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondents) do
6 | add :questionnaire_id, references(:questionnaires)
7 | add :mode, :string
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170117143633_add_salt_to_projects.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddSaltToProjects do
2 | use Ecto.Migration
3 |
4 | def up do
5 | alter table(:projects) do
6 | add :salt, :string
7 | end
8 | end
9 |
10 | def down do
11 | alter table(:projects) do
12 | remove :salt
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20180110182428_add_floip_package_id_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddFloipPackageIdToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :floip_package_id, :string
7 | end
8 |
9 | execute "UPDATE surveys SET floip_package_id = UUID();"
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20201124112819_add_panel_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddPanelSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :panel_survey_of, references(:surveys, on_delete: :nothing)
7 | add :latest_panel_survey, :boolean, default: false
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/coherence/email/password.text.eex:
--------------------------------------------------------------------------------
1 | Hello <%= @name %>!
2 |
3 | Someone has requested a link to change your password, and you can do this through the link below.
4 |
5 | <%= @url %>
6 |
7 | If you didn't request this, please ignore this email.
8 |
9 | Your password won't change until you access the link above and create a new one.
10 |
11 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20171004213434_add_survey_schedule.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddSurveySchedule do
2 | use Ecto.Migration
3 |
4 | def up do
5 | alter table(:surveys) do
6 | add :schedule, :text
7 | end
8 | end
9 |
10 | def down do
11 | alter table(:surveys) do
12 | remove :schedule
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/assets/js/components/surveys/PhoneNumberRow.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from "react"
2 |
3 | export const PhoneNumberRow = ({ id, phoneNumber }) => {
4 | return (
5 |
6 | | {phoneNumber} |
7 |
8 | )
9 | }
10 |
11 | PhoneNumberRow.propTypes = {
12 | id: PropTypes.string,
13 | phoneNumber: PropTypes.string,
14 | }
15 |
--------------------------------------------------------------------------------
/assets/js/components/timezones/util.js:
--------------------------------------------------------------------------------
1 | export const formatTimezone = (tz) => {
2 | const split = (tz || "UTC").replace("_", " ").split("/")
3 | switch (split.length) {
4 | case 2:
5 | return `${split[0]} - ${split[1]}`
6 | case 3:
7 | return `${split[0]} - ${split[2]}, ${split[1]}`
8 | default:
9 | return split[0]
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/assets/js/components/ui/Card.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from "react"
2 | import classNames from "classnames/bind"
3 |
4 | export const Card = ({ children, className }) => (
5 | {children}
6 | )
7 |
8 | Card.propTypes = {
9 | children: PropTypes.node,
10 | className: PropTypes.string,
11 | }
12 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161118174623_add_languages_and_default_language_to_questionnaires.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddLanguagesAndDefaultLanguageToQuestionnaires do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:questionnaires) do
6 | add :languages, :text
7 | add :default_language, :string
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170117190223_add_sample_and_respondents_count_to_respondent_group.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddSampleAndRespondentsCountToRespondentGroup do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondent_groups) do
6 | add :sample, :string
7 | add :respondents_count, :integer
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/test/fixtures/respondent_phone_numbers.csv:
--------------------------------------------------------------------------------
1 | (549) 11 4234 2343
2 | (549) 11 2421 3125
3 | (549) 11 1231 2323
4 | (549) 11 1241 2421
5 | (549) 11 4367 6723
6 | (549) 11 5432 1329
7 | (549) 11 6765 4335
8 | (549) 11 3264 3526
9 | (549) 11 9876 5424
10 | (549) 11 1234 5625
11 | (549) 11 2345 6721
12 | (549) 11 5434 5666
13 | (549) 11 2345 6432
14 | (549) 11 6765 4334
15 |
--------------------------------------------------------------------------------
/test/support/audio_checker.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.AudioChecker do
2 | def get_audio_format(data) do
3 | path = "test/tmp/#{Ecto.UUID.generate()}.audio"
4 | File.write(path, data, [:binary])
5 |
6 | try do
7 | %{^path => mime_type} = Ask.FileInfo.get_info(path)
8 | mime_type
9 | after
10 | File.rm(path)
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161110191532_set_default_started_at_for_already_launched_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.SetDefaultStartedAtForAlreadyLaunchedSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | Ask.Repo.query(
6 | "UPDATE surveys SET started_at = inserted_at WHERE state = 'completed' OR state = 'running'"
7 | )
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170116175403_create_respondent_group.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateRespondentGroup do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:respondent_groups) do
6 | add :name, :string
7 | add :survey_id, references(:surveys, on_delete: :nothing)
8 |
9 | timestamps()
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/assets/js/decls/undo.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | export type UndoStore = {
4 | undo: T[],
5 | redo: T[],
6 | }
7 |
8 | export type DataStoreWithUndo = DataStore & UndoStore
9 |
10 | export type UndoReducer = (state: ?DataStoreWithUndo, action: any) => DataStoreWithUndo
11 |
12 | export type UndoActions = {
13 | UNDO: string,
14 | REDO: string,
15 | }
16 |
--------------------------------------------------------------------------------
/assets/js/reducers/respondentsStats.js:
--------------------------------------------------------------------------------
1 | import * as actions from "../actions/respondents"
2 |
3 | export default (state = {}, action) => {
4 | switch (action.type) {
5 | case actions.RECEIVE_RESPONDENTS_STATS:
6 | return {
7 | ...state,
8 | ...action.response.entities.respondents,
9 | }
10 | default:
11 | return state
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160929135859_add_schedule_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddScheduleToSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:surveys) do
6 | add :schedule_day_of_week, :integer
7 | add :schedule_start_time, :utc_datetime
8 | add :schedule_end_time, :utc_datetime
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/coherence/layout/email.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= @email.subject %>
4 |
5 |
6 | <%= render @view_module, @view_template, assigns %>
7 |
8 |
9 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161013162339_set_default_completed_at_for_completed_respondents.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.SetDefaultCompletedAtForCompletedRespondents do
2 | use Ecto.Migration
3 |
4 | def change do
5 | Ask.Repo.query(
6 | "UPDATE respondents SET completed_at = updated_at WHERE state = 'completed' AND completed_at IS NULL"
7 | )
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20181022174941_add_description_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddDescriptionToSurveys do
2 | use Ecto.Migration
3 |
4 | def up do
5 | alter table(:surveys) do
6 | add :description, :text
7 | end
8 | end
9 |
10 | def down do
11 | alter table(:surveys) do
12 | remove :description
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20211213094856_add_index_to_respondents_canonical_phone_numbers.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddIndexToRespondentsCanonicalPhoneNumbers do
2 | use Ecto.Migration
3 |
4 | def up do
5 | create index(:respondents, :canonical_phone_number)
6 | end
7 |
8 | def down do
9 | drop index(:respondents, :canonical_phone_number)
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 | /node_modules/react-timeago
3 | /node_modules/csv-info
4 | /node_modules/draft-js
5 | /node_modules/react-i18next/src/shallowEqual.js
6 | [include]
7 | [libs]
8 | assets/js/decls/
9 | [options]
10 | munge_underscores=true
11 | esproposal.optional_chaining=enable
12 | esproposal.nullish_coalescing=enable
13 |
--------------------------------------------------------------------------------
/assets/js/reducers/surveyStats.js:
--------------------------------------------------------------------------------
1 | import * as actions from "../actions/survey"
2 |
3 | export default (state = {}, action) => {
4 | switch (action.type) {
5 | case actions.RECEIVE_SURVEY_STATS:
6 | return {
7 | ...state,
8 | surveyId: action.surveyId,
9 | stats: action.response,
10 | }
11 | default:
12 | return state
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/assets/vendor/css/materialize/components/_ncdvariables.scss:
--------------------------------------------------------------------------------
1 | /* 1. materialize reset Variables
2 | ========================================================================== */
3 |
4 | $primary-color: color("grey", "darken-3") !default;
5 |
6 | $secondary-color: color("green", "darken-2") !default;
7 |
8 | $input-error-color: #ff5722;
9 |
10 | $radio-fill-color: #000000;
11 |
12 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20191223182932_modify-retry-stats-unique-index.exs:
--------------------------------------------------------------------------------
1 | defmodule :"Elixir.Ask.Repo.Migrations.Modify-retry-stats-unique-index" do
2 | use Ecto.Migration
3 |
4 | def change do
5 | drop unique_index(:retry_stats, [:mode, :attempt, :retry_time, :survey_id])
6 | create unique_index(:retry_stats, [:mode, :attempt, :retry_time, :ivr_active, :survey_id])
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160816183915_create_project.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateProject do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:projects) do
6 | add :name, :string
7 | add :user_id, references(:users, on_delete: :nothing)
8 |
9 | timestamps()
10 | end
11 |
12 | create index(:projects, [:user_id])
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170320182452_add_base_url_to_channel_and_oauth_tokens.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddFriendlyNameToChannelAndOauthTokens do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:channels) do
6 | add :base_url, :string
7 | end
8 |
9 | alter table(:oauth_tokens) do
10 | add :base_url, :string
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20180312173843_add_remote_ip_to_activity_log.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddRemoteIpToActivityLog do
2 | use Ecto.Migration
3 |
4 | def up do
5 | alter table(:activity_log) do
6 | add :remote_ip, :string
7 | end
8 | end
9 |
10 | def down do
11 | alter table(:activity_log) do
12 | remove :remote_ip
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20210512142133_create_panel_survey.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreatePanelSurvey do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:panel_surveys) do
6 | add(:name, :string)
7 | add(:project_id, references(:projects))
8 | add(:folder_id, references(:folders))
9 |
10 | timestamps()
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160830200454_create_survey.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateSurvey do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:surveys) do
6 | add :name, :string
7 | add :project_id, references(:projects, on_delete: :nothing)
8 |
9 | timestamps()
10 | end
11 |
12 | create index(:surveys, [:project_id])
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170125165633_add_disposition_to_respondents.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddDispositionToRespondents do
2 | use Ecto.Migration
3 |
4 | def up do
5 | alter table(:respondents) do
6 | add :disposition, :string
7 | end
8 | end
9 |
10 | def down do
11 | alter table(:respondents) do
12 | remove :disposition
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170502170714_clear_session_and_timeout_at_for_failed_respondents.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.ClearSessionAndTimeoutAtForFailedRespondents do
2 | use Ecto.Migration
3 |
4 | def up do
5 | Ask.Repo.query!(
6 | "UPDATE respondents SET session = NULL, timeout_at = NULL WHERE state = 'failed'"
7 | )
8 | end
9 |
10 | def down do
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/integration_tests/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "surveda_cypress",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "scripts": {
7 | "cypress:run": "cypress run --headless",
8 | "cypress:open": "cypress open"
9 | },
10 | "dependencies": {
11 | "cypress": "^6.1.0",
12 | "cypress-file-upload": "^4.1.1",
13 | "route-parser": "^0.0.5"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/mobile_survey/404.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 | This survey is no longer available
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/priv/repo/seeds.exs:
--------------------------------------------------------------------------------
1 | # Script for populating the database. You can run it as:
2 | #
3 | # mix run priv/repo/seeds.exs
4 | #
5 | # Inside the script, you can read and write to any of your
6 | # repositories directly:
7 | #
8 | # Ask.Repo.insert!(%Ask.SomeModel{})
9 | #
10 | # We recommend using the bang functions (`insert!`, `update!`
11 | # and so on) as they will fail if something goes wrong.
12 |
--------------------------------------------------------------------------------
/test/ask/audio_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.AudioTest do
2 | use Ask.DataCase
3 |
4 | alias Ask.Audio
5 |
6 | test "mime type" do
7 | assert "audio/mpeg" = %Audio{filename: "foo.mp3"} |> Audio.mime_type()
8 | assert "audio/wav" = %Audio{filename: "foo.wav"} |> Audio.mime_type()
9 | assert "application/octet-stream" = %Audio{filename: "foo.bar"} |> Audio.mime_type()
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/docs/cloud-instance-configs/verboice.rancher-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 | services:
3 | db-migrate:
4 | scale: 1
5 | start_on_create: true
6 | asterisk-bridge:
7 | scale: 1
8 | start_on_create: true
9 | web:
10 | scale: 1
11 | start_on_create: true
12 | asterisk:
13 | scale: 1
14 | start_on_create: true
15 | broker:
16 | scale: 1
17 | start_on_create: true
18 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160906192317_create_survey_channels.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateSurveyChannel do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:survey_channels) do
6 | add :survey_id, references(:surveys, on_delete: :nothing)
7 | add :channel_id, references(:channels, on_delete: :nothing)
8 |
9 | timestamps()
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161109034931_create_audios.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateAudios do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:audios) do
6 | add :uuid, :string
7 | add :data, :mediumblob
8 | add :filename, :string
9 | add :source, :string
10 | add :duration, :integer
11 |
12 | timestamps()
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161229171246_create_invite.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateInvite do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:invites) do
6 | add :code, :string
7 | add :level, :string
8 | add :project_id, references(:projects, on_delete: :delete_all)
9 | add :email, :string
10 |
11 | timestamps()
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170119123633_add_hashed_number_to_respondents.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddHashedNumberToRespondents do
2 | use Ecto.Migration
3 |
4 | def up do
5 | alter table(:respondents) do
6 | add :hashed_number, :string
7 | end
8 | end
9 |
10 | def down do
11 | alter table(:respondents) do
12 | remove :hashed_number
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20191127152001_set_default_ended_at_for_already_finished_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.SetDefaultEndedAtForAlreadyFinishedSurveys do
2 | use Ecto.Migration
3 |
4 | def up do
5 | Ask.Repo.query("UPDATE surveys SET ended_at = updated_at WHERE state = 'terminated'")
6 | end
7 |
8 | def down do
9 | # Do nothing since it doesn't make sense
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20200122115222_add_description_to_questionnaires.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddDescriptionToQuestionnaires do
2 | use Ecto.Migration
3 |
4 | def up do
5 | alter table(:questionnaires) do
6 | add :description, :text
7 | end
8 | end
9 |
10 | def down do
11 | alter table(:questionnaires) do
12 | remove :description
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/test/ask/url_shortener_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.UrlShortenerTest do
2 | use ExUnit.Case
3 | alias Ask.UrlShortener
4 |
5 | test "build short url from shorter response" do
6 | body = """
7 | {"key": "P9CKRx","url": "https://google.com"}
8 | """
9 |
10 | short_url = UrlShortener.build_short_url("test.host", body)
11 |
12 | assert short_url == "test.host/P9CKRx"
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/assets/mobile_survey/js/reducers/config.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | // This reducer handles static values for the state
4 | // These values are set once when the React app is served
5 | const initialState = {
6 | introMessage: "",
7 | colorStyle: {},
8 | }
9 |
10 | export default (state: any = initialState, action: any) => {
11 | switch (action.type) {
12 | default:
13 | return state
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/mobile_survey_simulation/404.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 | This survey is no longer available
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/lib/ask_web/views/user.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.UserView do
2 | use AskWeb, :view
3 |
4 | def render("settings.json", %{settings: settings}) do
5 | %{
6 | data: %{
7 | settings: settings
8 | }
9 | }
10 | end
11 |
12 | def render("error.json", %{changeset: changeset}) do
13 | %{
14 | data: %{
15 | changeset: changeset
16 | }
17 | }
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/guisso/token_exchange.ex:
--------------------------------------------------------------------------------
1 | defmodule Guisso.OAuth.Strategy.TokenExchange do
2 | use OAuth2.Strategy
3 |
4 | def get_token(client, params, _headers) do
5 | client
6 | |> put_param(:grant_type, "token_exchange")
7 | |> put_param(:client_id, client.client_id)
8 | |> put_param(:client_secret, client.client_secret)
9 | |> put_param(:access_token, params[:access_token])
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/assets/js/reducers/surveyRetriesHistograms.js:
--------------------------------------------------------------------------------
1 | import * as actions from "../actions/survey"
2 |
3 | export default (state = {}, action) => {
4 | switch (action.type) {
5 | case actions.RECEIVE_SURVEY_RETRIES_HISTOGRAMS:
6 | return {
7 | ...state,
8 | surveyId: action.surveyId,
9 | histograms: action.response,
10 | }
11 | default:
12 | return state
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/lib/ask_web/plugs/not_allowed_error.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.ConflictError do
2 | defexception plug_status: 405, message: "not allowed", conn: nil
3 |
4 | def exception(_) do
5 | %AskWeb.ConflictError{}
6 | end
7 | end
8 |
9 | defimpl Plug.Exception, for: AskWeb.ConflictError do
10 | def status(_), do: 409
11 | def actions(_), do: [%{label: "ConflictError", handler: {IO, :puts, "ConflictError"}}]
12 | end
13 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160909144711_create_respondent.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateRespondent do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:respondents) do
6 | add :phone_number, :string
7 | add :survey_id, references(:surveys, on_delete: :nothing)
8 |
9 | timestamps()
10 | end
11 |
12 | create index(:respondents, [:survey_id])
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161220152839_remove_user_id_from_projects.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.RemoveUserIdFromProjects do
2 | use Ecto.Migration
3 |
4 | def change do
5 | Ask.Repo.transaction(fn ->
6 | Ask.Repo.query!("ALTER TABLE projects DROP FOREIGN KEY projects_user_id_fkey")
7 |
8 | alter table(:projects) do
9 | remove :user_id
10 | end
11 | end)
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170125151039_set_default_comparisons_on_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.SetDefaultComparisonsOnSurveys do
2 | use Ecto.Migration
3 |
4 | def up do
5 | Ask.Repo.query("UPDATE surveys SET comparisons = '[]' WHERE comparisons IS NULL")
6 | end
7 |
8 | def down do
9 | Ask.Repo.query("UPDATE surveys SET comparisons = NULL WHERE comparisons = '[]'")
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/assets/js/reducers/guisso.js:
--------------------------------------------------------------------------------
1 | import * as actions from "../actions/guisso"
2 |
3 | export default (state = {}, action) => {
4 | switch (action.type) {
5 | case actions.GUISSO_TOKEN:
6 | return guissoToken(state, action)
7 | default:
8 | return state
9 | }
10 | }
11 |
12 | const guissoToken = (state, action) => ({
13 | ...state,
14 | [action.app]: {
15 | ...action.token,
16 | },
17 | })
18 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160914194540_modify_questionnaires_steps_to_longtext.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.ModifyQuestionnairesStepsToLongtext do
2 | use Ecto.Migration
3 |
4 | def up do
5 | alter table(:questionnaires) do
6 | modify :steps, :longtext
7 | end
8 | end
9 |
10 | def down do
11 | alter table(:questionnaires) do
12 | modify :steps, :text
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20200211131441_set_default_respondents_canonical_phone_number.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.SetDefaultRespondentsCanonicalPhoneNumber do
2 | use Ecto.Migration
3 |
4 | def up do
5 | execute "UPDATE respondents SET canonical_phone_number = sanitized_phone_number"
6 | end
7 |
8 | def down do
9 | execute "UPDATE respondents SET canonical_phone_number = NULL"
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/test/support/log_helper.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.LogHelper do
2 | defmacro __using__(_) do
3 | quote do
4 | import Ask.LogHelper
5 | end
6 | end
7 |
8 | defmacro without_logging(do: block) do
9 | quote do
10 | Logger.remove_backend(:console)
11 |
12 | try do
13 | unquote(block)
14 | after
15 | Logger.add_backend(:console)
16 | end
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/assets/css/app.scss:
--------------------------------------------------------------------------------
1 | @import "fonts";
2 |
3 | @import "materialize/materialize";
4 | @import "reset-materialize";
5 |
6 | // NCD
7 | @import "charts";
8 | @import "layout";
9 | @import "card-modal";
10 | @import "card-table";
11 | @import "buttons";
12 | @import "forms";
13 | @import "sections";
14 | @import "scrollto";
15 | @import "global";
16 | @import "welcome";
17 | @import "responsive";
18 | @import "quex-simulation";
19 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170117191259_modify_respondent_group_sample_to_long_text.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.ModifyRespondentGroupSampleToLongText do
2 | use Ecto.Migration
3 |
4 | def up do
5 | alter table(:respondent_groups) do
6 | modify :sample, :longtext
7 | end
8 | end
9 |
10 | def down do
11 | alter table(:respondent_groups) do
12 | modify :sample, :string
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20230217143550_add_settings_to_projects.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddSettingsToProjects do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:projects) do
6 | add :timezone, :string
7 | add :initial_success_rate, :float
8 | add :eligibility_rate, :float
9 | add :response_rate, :float
10 | add :valid_respondent_rate, :float
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/test/ask/project_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.ProjectTest do
2 | use Ask.DataCase
3 |
4 | alias Ask.Project
5 |
6 | @valid_attrs %{name: "some content"}
7 |
8 | test "changeset with valid attributes" do
9 | changeset = Project.changeset(%Project{}, @valid_attrs)
10 | assert changeset.valid?
11 | end
12 |
13 | test "default value for archived is false" do
14 | assert %Project{}.archived == false
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/assets/js/components/charts/utils.js:
--------------------------------------------------------------------------------
1 | import * as d3 from "d3"
2 |
3 | export function percentFormat(number) {
4 | return d3.format(".1%")(number)
5 | }
6 |
7 | export function numberFormat(number) {
8 | return number < 0.1 ? d3.format(".1%")(number) : d3.format(".0%")(number)
9 | }
10 |
11 | export function labelFormat(number) {
12 | return number > 0 && number < 1 ? d3.format(".2f")(number) : d3.format(".0f")(number)
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/lib/ask_web/plugs/unauthorized_error.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.UnauthorizedError do
2 | defexception plug_status: 403, message: "unauthorized", conn: nil
3 |
4 | def exception(_) do
5 | %AskWeb.UnauthorizedError{}
6 | end
7 | end
8 |
9 | defimpl Plug.Exception, for: AskWeb.UnauthorizedError do
10 | def status(_), do: 403
11 | def actions(_), do: [%{label: "UnauthorizedError", handler: {IO, :puts, "UnauthorizedError"}}]
12 | end
13 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20191212203052_set_default_retry_stat_time_to_respondents.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.SetDefaultRetryStatTimeToRespondents do
2 | use Ecto.Migration
3 |
4 | def up do
5 | execute "UPDATE respondents SET retry_stat_time = timeout_at WHERE timeout_at IS NOT NULL AND retry_stat_time IS NULL"
6 | end
7 |
8 | def down do
9 | execute "UPDATE respondents SET retry_stat_time = NULL"
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20230413101342_remove_mobile_web_cookie_code_from_respondents.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.RemoveMobileWebCookieCodeFromRespondents do
2 | use Ecto.Migration
3 |
4 | def up do
5 | alter table(:respondents) do
6 | remove :mobile_web_cookie_code
7 | end
8 | end
9 |
10 | def down do
11 | alter table(:respondents) do
12 | add :mobile_web_cookie_code, :string
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160914174501_create_response.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateResponse do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:responses) do
6 | add :field_name, :string
7 | add :value, :string
8 | add :respondent_id, references(:respondents, on_delete: :nothing)
9 |
10 | timestamps()
11 | end
12 |
13 | create index(:responses, [:respondent_id])
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161124170829_create_quota_buckets.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateQuotaBuckets do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:quota_buckets) do
6 | add :condition, :text
7 | add :quota, :integer
8 | add :count, :integer
9 | add :survey_id, references(:surveys, on_delete: :delete_all, on_update: :update_all)
10 |
11 | timestamps()
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161219200232_create_project_membership.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateProjectMembership do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:project_memberships) do
6 | add :user_id, references(:users, on_delete: :delete_all)
7 | add :project_id, references(:projects, on_delete: :delete_all)
8 | add :level, :string
9 |
10 | timestamps()
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20200420135544_add_partial_relevant_config_to_questionnaires.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddPartialRelevantConfigToQuestionnaires do
2 | use Ecto.Migration
3 |
4 | def up do
5 | alter table(:questionnaires) do
6 | add :partial_relevant_config, :string
7 | end
8 | end
9 |
10 | def down do
11 | alter table(:questionnaires) do
12 | remove :partial_relevant_config
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/assets/mobile_survey/js/components/Prompt.jsx:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { Component, PropTypes } from "react"
3 |
4 | type Props = {
5 | text: string,
6 | }
7 |
8 | class Prompt extends Component {
9 | render() {
10 | const { text } = this.props
11 | return (
12 |
13 |
14 |
15 | )
16 | }
17 | }
18 |
19 | export default Prompt
20 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160902185110_create_questionnaire.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateQuestionnaire do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:questionnaires) do
6 | add :name, :string
7 | add :description, :string
8 | add :project_id, references(:projects, on_delete: :nothing)
9 |
10 | timestamps()
11 | end
12 |
13 | create index(:questionnaires, [:project_id])
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160924055948_create_oauth_token.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateOAuthToken do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:oauth_tokens) do
6 | add :provider, :string
7 | add :access_token, :map
8 | add :user_id, references(:users, on_delete: :nothing)
9 |
10 | timestamps()
11 | end
12 |
13 | create unique_index(:oauth_tokens, [:user_id, :provider])
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161221101027_remove_questionnaire_id_from_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.RemoveQuestionnaireIdFromSurveys do
2 | use Ecto.Migration
3 |
4 | def change do
5 | Ask.Repo.query!("update surveys set questionnaire_id = null")
6 | Ask.Repo.query!("alter table surveys drop foreign key surveys_questionnaire_id_fkey")
7 |
8 | alter table(:surveys) do
9 | remove :questionnaire_id
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/integration_tests/cypress/integration/projects.spec.js:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | describe('projects', () => {
4 | beforeEach(() => {
5 | cy.loginGuisso(Cypress.env('email'), Cypress.env('password'))
6 | })
7 |
8 | it('show surveys by default', () => {
9 | const projectId = Cypress.env('project_id')
10 | cy.visit(`/projects/${projectId}`)
11 |
12 | cy.url().should('include', `/projects/${projectId}/surveys`)
13 | })
14 | })
15 |
--------------------------------------------------------------------------------
/lib/ask/session.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.Session do
2 | use Ask.Model
3 |
4 | schema "sessions" do
5 | field :token, :string
6 | field :user_type, :string
7 | field :user_id, :string
8 | timestamps()
9 | end
10 |
11 | @fields ~w(token user_type user_id)a
12 |
13 | def changeset(model, params \\ %{}) do
14 | model
15 | |> cast(params, @fields)
16 | |> validate_required(@fields)
17 | |> unique_constraint(:token)
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170202175824_create_sessions_table.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateSessionsTable do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:sessions) do
6 | add :token, :string, unique: true
7 | add :user_type, :string
8 | add :user_id, :string
9 |
10 | timestamps()
11 | end
12 |
13 | create unique_index(:sessions, [:token])
14 | create index(:sessions, [:user_id])
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170420194102_change_questionnaire_mobile_web_sms_message_to_text.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.ChangeQuestionnaireMobileWebSmsMessageToText do
2 | use Ecto.Migration
3 |
4 | def up do
5 | alter table(:questionnaires) do
6 | modify :mobile_web_sms_message, :text
7 | end
8 | end
9 |
10 | def down do
11 | alter table(:questionnaires) do
12 | modify :mobile_web_sms_message, :string
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20190621174448_add_survey_id_and_respondent_hashed_number_to_respondent_disposition_history.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddSurveyIdAndRespondentHashedNumberToRespondentDispositionHistory do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:respondent_disposition_history) do
6 | add :survey_id, references(:surveys, on_delete: :delete_all)
7 | add :respondent_hashed_number, :string
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20210326160205_fill_last_window_ends_at_in_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.FillLastWindowEndsAtInSurveys do
2 | use Ecto.Migration
3 |
4 | def up do
5 | Ask.Repo.query("UPDATE surveys SET last_window_ends_at = ended_at WHERE ended_at IS NOT NULL")
6 | end
7 |
8 | def down do
9 | Ask.Repo.query(
10 | "UPDATE surveys SET last_window_ends_at = null WHERE last_window_ends_at IS NOT NULL"
11 | )
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/assets/js/debounce.js:
--------------------------------------------------------------------------------
1 | export default (func, wait, immediate) => {
2 | let timeout
3 | return function () {
4 | const context = this
5 | const args = arguments
6 | const later = function () {
7 | timeout = null
8 | if (!immediate) func.apply(context, args)
9 | }
10 | const callNow = immediate && !timeout
11 | clearTimeout(timeout)
12 | timeout = setTimeout(later, wait)
13 | if (callNow) func.apply(context, args)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/ask/model.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.Model do
2 | defmacro __using__(_) do
3 | quote do
4 | use Ecto.Schema
5 |
6 | import Ecto
7 | import Ecto.Changeset
8 | import Ecto.Query
9 |
10 | # Avoid microseconds. Mysql doesn't support them.
11 | # See [usec in datetime](https://hexdocs.pm/ecto_sql/Ecto.Adapters.MyXQL.html#module-usec-in-datetime)
12 | @timestamps_opts [type: :utc_datetime, usec: false]
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/coherence/email/unlock.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
<%= dgettext "coherence", "Hello %{name}!", name: @name %>
3 |
4 | <%= dgettext "coherence", "You requested unlock instructions for your locked account. Please click the link below to unlock your account." %>
5 |
6 |
7 | <%= dgettext "coherence", "Unlock my Account" %>
8 |
9 |
<%= dgettext "coherence", "Thank you!" %>
10 |
11 |
12 |
--------------------------------------------------------------------------------
/lib/mix/tasks/ecto_dump.ex:
--------------------------------------------------------------------------------
1 | defmodule Mix.Tasks.Ask.EctoDump do
2 | @moduledoc ""
3 | @shortdoc ""
4 |
5 | use Mix.Task
6 |
7 | @impl Mix.Task
8 | def run(_args) do
9 | if Mix.env() == :dev do
10 | Mix.Tasks.Ecto.Dump.run([])
11 |
12 | sql = File.read!("priv/repo/structure.sql")
13 | sql = String.replace(sql, ~r/ AUTO_INCREMENT=\d+ /, " ", [global: true])
14 | File.write!("priv/repo/structure.sql", sql)
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170119153421_remove_survey_channels.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.RemoveSurveyChannels do
2 | use Ecto.Migration
3 |
4 | def up do
5 | drop table(:survey_channels)
6 | end
7 |
8 | def down do
9 | create table(:survey_channels) do
10 | add :survey_id, references(:surveys, on_delete: :nothing)
11 | add :channel_id, references(:channels, on_delete: :nothing)
12 |
13 | timestamps()
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170125165933_set_disposition_as_completed_where_applies_in_respondents.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.SetDispositionAsCompletedWhereAppliesInRespondents do
2 | use Ecto.Migration
3 |
4 | def up do
5 | Ask.Repo.query("UPDATE respondents SET disposition = 'completed' WHERE state = 'completed'")
6 | end
7 |
8 | def down do
9 | Ask.Repo.query("UPDATE respondents SET disposition = NULL WHERE state = 'completed'")
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/lib/ask/completed_respondents.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.CompletedRespondents do
2 | use Ask.Model
3 |
4 | @primary_key false
5 | schema "completed_respondents" do
6 | belongs_to :survey, Ask.Survey, primary_key: true
7 | field :questionnaire_id, :integer, primary_key: true
8 | field :quota_bucket_id, :integer, primary_key: true
9 | field :mode, :string, primary_key: true
10 | field :date, :date, primary_key: true
11 | field :count, :integer
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160905135419_create_channel.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateChannel do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:channels) do
6 | add :name, :string
7 | add :type, :string
8 | add :provider, :string
9 | add :settings, :map
10 | add :user_id, references(:users, on_delete: :nothing)
11 |
12 | timestamps()
13 | end
14 |
15 | create index(:channels, [:user_id])
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161115202151_set_default_schedule_start_and_end_times.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.SetDefaultScheduleStartAndEndTimes do
2 | use Ecto.Migration
3 |
4 | def change do
5 | Ask.Repo.query!(
6 | "UPDATE surveys SET schedule_start_time = '09:00:00' WHERE schedule_start_time IS NULL"
7 | )
8 |
9 | Ask.Repo.query!(
10 | "UPDATE surveys SET schedule_end_time = '18:00:00' WHERE schedule_end_time IS NULL"
11 | )
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20200212200209_add-user-stopped-to-respondents.exs:
--------------------------------------------------------------------------------
1 | defmodule :"Elixir.Ask.Repo.Migrations.Add-user-stopped-to-respondents" do
2 | use Ecto.Migration
3 |
4 | def up do
5 | alter table(:respondents), do: add(:user_stopped, :boolean, null: false, default: false)
6 | alter table(:respondents), do: modify(:user_stopped, :boolean, null: false)
7 | end
8 |
9 | def down do
10 | alter table(:respondents), do: remove(:user_stopped)
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/lib/ask_web/controllers/fallback_controller.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.FallbackController do
2 | use Phoenix.Controller
3 |
4 | def call(conn, {:error, :not_found}) do
5 | conn
6 | |> put_status(:not_found)
7 | |> put_view(AskWeb.ErrorView)
8 | |> render(:"404")
9 | end
10 |
11 | def call(conn, {:error, :invalid_simulation}),
12 | do:
13 | conn
14 | |> put_status(:bad_request)
15 | |> put_view(AskWeb.ErrorView)
16 | |> render(:"400")
17 | end
18 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/mobile_survey/index.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160908162553_create_questionnaire_step.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateQuestionnaireStep do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:questionnaire_steps) do
6 | add :type, :string
7 | add :settings, :map
8 | add :questionnaire_id, references(:questionnaires, on_delete: :nothing)
9 |
10 | timestamps()
11 | end
12 |
13 | create index(:questionnaire_steps, [:questionnaire_id])
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160915191129_rename_survey_state_default_to_not_ready.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.RenameSurveyStateDefaultToNotReady do
2 | use Ecto.Migration
3 | import Ecto.Query
4 |
5 | def up do
6 | from(s in Ask.Survey, where: s.state == :pending)
7 | |> Ask.Repo.update_all(set: [state: :not_ready])
8 | end
9 |
10 | def down do
11 | from(s in Ask.Survey, where: s.state == :not_ready)
12 | |> Ask.Repo.update_all(set: [state: :pending])
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170206162219_create_respondent_disposition_history.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateRespondentDispositionHistory do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:respondent_disposition_history) do
6 | add :disposition, :string
7 | add :respondent_id, references(:respondents, on_delete: :nothing)
8 |
9 | timestamps()
10 | end
11 |
12 | create index(:respondent_disposition_history, [:respondent_id])
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20180315000255_create_project_channels.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateProjectChannels do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:project_channels) do
6 | add :channel_id, references(:channels, on_delete: :delete_all)
7 | add :project_id, references(:projects, on_delete: :delete_all)
8 |
9 | timestamps()
10 | end
11 |
12 | create unique_index(:project_channels, [:channel_id, :project_id])
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/ask/bom_parser.ex:
--------------------------------------------------------------------------------
1 | # Parser and stripper of BOM (byte order mark)
2 | defmodule Ask.BomParser do
3 | def parse(string) do
4 | case :unicode.bom_to_encoding(string) do
5 | {_, 0} ->
6 | string
7 |
8 | {encoding, bom_size} ->
9 | # Strip BOM
10 | bom_bits = bom_size * 8
11 | <<_bom::size(bom_bits), string::binary>> = string
12 |
13 | # Convert to UTF-8
14 | :unicode.characters_to_binary(string, encoding)
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/lib/ask_web/views/oauth_client_view.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.OAuthClientView do
2 | use AskWeb, :view
3 |
4 | def render("index.json", %{authorizations: auths}) do
5 | %{
6 | data:
7 | auths
8 | |> Enum.map(fn auth ->
9 | %{
10 | provider: auth.provider,
11 | baseUrl: auth.base_url
12 | }
13 | end)
14 | }
15 | end
16 |
17 | def render("ui_token.json", %{token: token}) do
18 | token.access_token
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/assets/js/reducers/invite.js:
--------------------------------------------------------------------------------
1 | import * as actions from "../actions/invites"
2 |
3 | const initialState = {
4 | fetching: false,
5 | data: null,
6 | }
7 |
8 | export default (state = initialState, action) => {
9 | switch (action.type) {
10 | case actions.RECEIVE_INVITE:
11 | return receiveInvite(state, action)
12 | default:
13 | return state
14 | }
15 | }
16 |
17 | const receiveInvite = (state, action) => {
18 | return {
19 | ...state,
20 | data: action.data,
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lib/ask_web/controllers/audio_delivery_controller.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.AudioDeliveryController do
2 | use AskWeb, :controller
3 |
4 | alias Ask.Audio
5 |
6 | def show(conn, %{"id" => uuid}) do
7 | audio = Repo.get_by!(Audio, uuid: uuid)
8 |
9 | conn
10 | |> put_resp_header("content-type", Audio.mime_type(audio))
11 | |> put_resp_header("content-disposition", "attachment; filename=#{audio.filename}")
12 | |> put_cache_headers()
13 | |> send_resp(200, audio.data)
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20210325194736_fill_first_window_started_at_in_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.FillFirstWindowStartedAtInSurveys do
2 | use Ecto.Migration
3 |
4 | def up do
5 | Ask.Repo.query(
6 | "UPDATE surveys SET first_window_started_at = started_at WHERE started_at IS NOT NULL"
7 | )
8 | end
9 |
10 | def down do
11 | Ask.Repo.query(
12 | "UPDATE surveys SET first_window_started_at = null WHERE first_window_started_at IS NOT NULL"
13 | )
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/assets/vendor/css/materialize/components/forms/_forms.scss:
--------------------------------------------------------------------------------
1 | // Remove Focus Boxes
2 | select:focus {
3 | outline: $select-focus;
4 | }
5 |
6 | button:focus {
7 | outline: none;
8 | background-color: $button-background-focus;
9 | }
10 |
11 | label {
12 | font-size: $label-font-size;
13 | color: $input-border-color;
14 | }
15 |
16 | @import 'input-fields';
17 | @import 'radio-buttons';
18 | @import 'checkboxes';
19 | @import 'switches';
20 | @import 'select';
21 | @import 'file-input';
22 | @import 'range';
23 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20180302023759_create_activity_log.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateActivityLog do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:activity_log) do
6 | add :project_id, references(:projects, on_delete: :nothing)
7 | add :user_id, references(:users, on_delete: :nothing)
8 | add :entity_type, :string
9 | add :entity_id, :integer
10 | add :action, :string
11 | add :metadata, :text
12 |
13 | timestamps()
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/assets/js/actions/autoSaveStatus.js:
--------------------------------------------------------------------------------
1 | export const RECEIVE = "AUTOSAVE_RECEIVE"
2 | export const SAVED = "AUTOSAVE_SAVED"
3 | export const SAVING = "AUTOSAVE_SAVING"
4 | export const ERROR = "AUTOSAVE_ERROR"
5 |
6 | export const receive = (entity) => ({
7 | type: RECEIVE,
8 | data: entity,
9 | })
10 |
11 | export const saved = (entity) => ({
12 | type: SAVED,
13 | data: entity,
14 | })
15 |
16 | export const saving = () => ({
17 | type: SAVING,
18 | })
19 |
20 | export const error = () => ({
21 | type: ERROR,
22 | })
23 |
--------------------------------------------------------------------------------
/assets/mobile_survey/js/components/layout/Root.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from "react"
2 | import { Provider } from "react-redux"
3 | import Step from "../Step"
4 |
5 | export default class Root extends Component {
6 | static propTypes = {
7 | store: PropTypes.object.isRequired,
8 | }
9 |
10 | render() {
11 | const { store } = this.props
12 | return (
13 |
14 |
15 |
16 |
17 |
18 | )
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/docs/cloud-instance-configs/proxy.docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 | services:
3 | lb:
4 | image: rancher/lb-service-haproxy:v0.7.15
5 | ports:
6 | - 80:80/tcp
7 | - 443:443/tcp
8 | labels:
9 | io.rancher.container.agent.role: environmentAdmin,agent
10 | io.rancher.container.agent_service.drain_provider: 'true'
11 | io.rancher.container.create_agent: 'true'
12 | redirect-to-https:
13 | image: instedd/redirect-to-https
14 | labels:
15 | io.rancher.container.pull_image: always
16 |
--------------------------------------------------------------------------------
/test/fixtures/respondent_phone_numbers_duplicated.csv:
--------------------------------------------------------------------------------
1 | (549) 11 4234 2343
2 | (549) 11 2421 3125
3 | (549) 11 1231 2323
4 | (549) 11 1241 2421
5 | (549) 11 4367 6723
6 | (549) 11 5432 1329
7 | (549) 11 6765 4335
8 | (549) 11 3264 3526
9 | (549) 11 9876 5424
10 | (549) 11 1234 5625
11 | (549) 11 2345 6721
12 | (549) 11 5434 5666
13 | (549) 11 2345 6432
14 | (549) 11 6765 4334
15 | (549) 11 1231 2323
16 | (549) 11 1241 2421
17 | (549) 11 4367 6023
18 | (549) 11 9876 5424
19 | (549) 11 1234 5625
20 | (549) 11 2345 1223
21 | (549) 11 5434 5666
22 |
--------------------------------------------------------------------------------
/assets/js/components/questionnaires/withQuestionnaire.jsx:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { connect } from "react-redux"
3 |
4 | const withQuestionnaire = (ComponentClass) => (props: any) => {
5 | if (props.questionnaire) {
6 | return
7 | } else {
8 | return null
9 | }
10 | }
11 |
12 | const mapStateToProps = (state, ownProps) => ({
13 | questionnaire: state.questionnaire.data,
14 | })
15 |
16 | export default (ComponentClass) => connect(mapStateToProps)(withQuestionnaire(ComponentClass))
17 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/mobile_survey_simulation/index.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170207152142_modify_translation_source_text_and_target_text_to_long_text.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.ModifyTranslationSourceTextAndTargetTextToLongText do
2 | use Ecto.Migration
3 |
4 | def up do
5 | alter table(:translations) do
6 | modify :source_text, :longtext
7 | modify :target_text, :longtext
8 | end
9 | end
10 |
11 | def down do
12 | alter table(:translations) do
13 | modify :source_text, :string
14 | modify :target_text, :string
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/assets/js/reducers/respondentsCount.js:
--------------------------------------------------------------------------------
1 | import * as actions from "../actions/respondents"
2 |
3 | export default (state = {}, action) => {
4 | switch (action.type) {
5 | case actions.CREATE_RESPONDENT:
6 | case actions.UPDATE_RESPONDENT:
7 | return 1
8 | case actions.RECEIVE_RESPONDENTS:
9 | if (action.response) {
10 | return action.response.respondentsCount
11 | }
12 | return state
13 | case actions.REMOVE_RESPONDENTS:
14 | return 0
15 | default:
16 | return state
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/lib/ask/questionnaire_variable.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.QuestionnaireVariable do
2 | use Ask.Model
3 |
4 | schema "questionnaire_variables" do
5 | field :name, :string
6 | belongs_to :project, Ask.Project
7 | belongs_to :questionnaire, Ask.Questionnaire
8 |
9 | timestamps()
10 | end
11 |
12 | @doc """
13 | Builds a changeset based on the `struct` and `params`.
14 | """
15 | def changeset(struct, params \\ %{}) do
16 | struct
17 | |> cast(params, [:name])
18 | |> validate_required([:name])
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20191011174319_create_retry_stats.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateRetryStats do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:retry_stats) do
6 | add :mode, :string
7 | add :attempt, :integer
8 | add :retry_time, :string
9 | add :count, :integer
10 | add :survey_id, references(:surveys, on_delete: :delete_all)
11 |
12 | timestamps()
13 | end
14 |
15 | create unique_index(:retry_stats, [:mode, :attempt, :retry_time, :survey_id])
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/test/ask/response_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.ResponseTest do
2 | use Ask.DataCase
3 |
4 | alias Ask.Response
5 |
6 | @valid_attrs %{field_name: "some content", value: "some content"}
7 | @invalid_attrs %{}
8 |
9 | test "changeset with valid attributes" do
10 | changeset = Response.changeset(%Response{}, @valid_attrs)
11 | assert changeset.valid?
12 | end
13 |
14 | test "changeset with invalid attributes" do
15 | changeset = Response.changeset(%Response{}, @invalid_attrs)
16 | refute changeset.valid?
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/lib/ask/config_helper.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.ConfigHelper do
2 | def get_config(module, config_key, sys_config_mapper \\ fn value -> value end) do
3 | case Application.get_env(:ask, module)[config_key] do
4 | {:system, env_var} ->
5 | sys_config_mapper.(System.get_env(env_var))
6 |
7 | {:system, env_var, default} ->
8 | env_value = System.get_env(env_var)
9 | if env_value, do: sys_config_mapper.(env_value), else: default
10 |
11 | simple_config ->
12 | simple_config
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160909150600_drop_questionnaire_steps.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.DropQuestionnaireSteps do
2 | use Ecto.Migration
3 |
4 | def up do
5 | drop table(:questionnaire_steps)
6 | end
7 |
8 | def down do
9 | create table(:questionnaire_steps) do
10 | add :type, :string
11 | add :settings, :map
12 | add :questionnaire_id, references(:questionnaires, on_delete: :nothing)
13 |
14 | timestamps()
15 | end
16 |
17 | create index(:questionnaire_steps, [:questionnaire_id])
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/ask/survey_questionnaire.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.SurveyQuestionnaire do
2 | use Ask.Model
3 |
4 | schema "survey_questionnaires" do
5 | belongs_to :survey, Ask.Survey
6 | belongs_to :questionnaire, Ask.Questionnaire
7 |
8 | timestamps()
9 | end
10 |
11 | def changeset(struct, params \\ %{}) do
12 | struct
13 | |> cast(params, [:survey_id, :questionnaire_id])
14 | |> validate_required([:survey_id, :questionnaire_id])
15 | |> foreign_key_constraint(:survey)
16 | |> foreign_key_constraint(:questionnaire)
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161221100014_create_survey_questionnaire.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateSurveyQuestionnaire do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:survey_questionnaires) do
6 | add :survey_id, references(:surveys, on_delete: :nothing)
7 | add :questionnaire_id, references(:questionnaires, on_delete: :nothing)
8 |
9 | timestamps()
10 | end
11 |
12 | create index(:survey_questionnaires, [:survey_id])
13 | create index(:survey_questionnaires, [:questionnaire_id])
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170818184001_add_default_disposition_to_rejected_respondents.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddDefaultDispositionToRejectedRespondents do
2 | use Ecto.Migration
3 | alias Ask.Repo
4 |
5 | def up do
6 | Repo.transaction(fn ->
7 | Repo.query!(
8 | """
9 | UPDATE respondents
10 | SET disposition = 'rejected'
11 | WHERE state = 'rejected'
12 | AND disposition IS NULL
13 | """,
14 | []
15 | )
16 | end)
17 | end
18 |
19 | def down do
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/mobile_survey/unauthorized.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
13 | You are not authorized to participate in the survey
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/lib/ask_web/views/coherence/session_view.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.Coherence.SessionView do
2 | use AskWeb.Coherence, :view
3 |
4 | def render("session.json", %{user: user}) do
5 | %{
6 | user: %{
7 | id: user.id,
8 | name: user.name,
9 | email: user.email
10 | }
11 | }
12 | end
13 |
14 | def render("error.json", %{error: error}) do
15 | %{
16 | error: error
17 | }
18 | end
19 |
20 | def render("error.json", _opts) do
21 | %{
22 | error: "Invalid credentials"
23 | }
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20160913180125_set_default_state_for_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.SetDefaultStateForSurveys do
2 | use Ecto.Migration
3 | import Ecto.Query
4 |
5 | def up do
6 | alter table(:surveys) do
7 | modify :state, :string, default: :pending
8 | end
9 |
10 | flush()
11 |
12 | from(s in Ask.Survey, where: is_nil(s.state))
13 | |> Ask.Repo.update_all(set: [state: :pending])
14 | end
15 |
16 | def down do
17 | alter table(:surveys) do
18 | modify :state, :string
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/lib/ask_web/controllers/timezone_controller.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.TimezoneController do
2 | use AskWeb, :api_controller
3 |
4 | def timezones(conn, _) do
5 | # only contains the valid (non-deprecated) timezones
6 | canonical_links = Tzdata.canonical_zone_list() |> Enum.map(fn tz -> {tz, tz} end) |> Map.new()
7 | # only contains the deprecated timezones with its links to the 'new' timezones
8 | links = Tzdata.links()
9 |
10 | timezones = Map.merge(canonical_links, links)
11 | render(conn, "index.json", timezones: timezones)
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/test/ask/oauth_token_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.OAuthTokenTest do
2 | use Ask.DataCase
3 |
4 | alias Ask.OAuthToken
5 |
6 | @valid_attrs %{access_token: %{}, provider: "some content", user_id: 1}
7 | @invalid_attrs %{}
8 |
9 | test "changeset with valid attributes" do
10 | changeset = OAuthToken.changeset(%OAuthToken{}, @valid_attrs)
11 | assert changeset.valid?
12 | end
13 |
14 | test "changeset with invalid attributes" do
15 | changeset = OAuthToken.changeset(%OAuthToken{}, @invalid_attrs)
16 | refute changeset.valid?
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/mobile_survey_simulation/unauthorized.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
13 | You are not authorized to participate in the survey
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/test/support/survey_stub.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.Runtime.SurveyStub do
2 | use GenServer
3 |
4 | @server_ref {:global, __MODULE__}
5 | def server_ref, do: @server_ref
6 |
7 | def init(args) do
8 | {:ok, args}
9 | end
10 |
11 | def sync_step(respondent, reply, mode) do
12 | GenServer.call(@server_ref, {:sync_step, respondent, reply, mode})
13 | end
14 |
15 | def handle_cast({:expects, matcher}, _) do
16 | {:noreply, matcher}
17 | end
18 |
19 | def handle_call(call, _from, matcher) do
20 | {:reply, matcher.(call), matcher}
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/assets/js/actions/guisso.js:
--------------------------------------------------------------------------------
1 | export const GUISSO_TOKEN = "GUISSO_TOKEN"
2 |
3 | export const obtainToken = (guissoSession) => {
4 | return (dispatch, getState) => {
5 | const existingToken = getState().guisso[guissoSession.config.appId]
6 | if (existingToken) {
7 | return Promise.resolve(existingToken)
8 | }
9 |
10 | return guissoSession.authorize("token").then((token) => {
11 | dispatch({
12 | type: GUISSO_TOKEN,
13 | app: guissoSession.config.appId,
14 | token,
15 | })
16 | return token
17 | })
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170113200644_add_coherence_to_user.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddCoherenceToUser do
2 | use Ecto.Migration
3 |
4 | def change do
5 | alter table(:users) do
6 | # authenticatable
7 | add :password_hash, :string
8 | # recoverable
9 | add :reset_password_token, :string
10 | add :reset_password_sent_at, :naive_datetime
11 | # confirmable
12 | add :confirmation_token, :string
13 | add :confirmed_at, :naive_datetime
14 | add :confirmation_sent_at, :naive_datetime
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170119152252_create_respondent_group_channel.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateRespondentGroupChannel do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:respondent_group_channels) do
6 | add :respondent_group_id, references(:respondent_groups, on_delete: :nothing)
7 | add :channel_id, references(:channels, on_delete: :nothing)
8 |
9 | timestamps()
10 | end
11 |
12 | create index(:respondent_group_channels, [:respondent_group_id])
13 | create index(:respondent_group_channels, [:channel_id])
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/assets/js/components/integrations/IntegrationRow.jsx:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { Component } from "react"
3 |
4 | type Props = {
5 | integration: Integration,
6 | }
7 |
8 | class IntegrationRow extends Component {
9 | render() {
10 | const { integration } = this.props
11 |
12 | return (
13 |
14 | | {integration.name} |
15 | {integration.uri} |
16 | {integration.authToken} |
17 | {integration.state} |
18 |
19 | )
20 | }
21 | }
22 |
23 | export default IntegrationRow
24 |
--------------------------------------------------------------------------------
/lib/ask/db_session_store.ex:
--------------------------------------------------------------------------------
1 | # lib/my_project/db_store.ex
2 | defimpl Coherence.DbStore, for: Ask.User do
3 | alias Ask.Repo
4 | alias Ask.EctoDbSession
5 |
6 | def get_user_data(user, creds, id_key),
7 | do: EctoDbSession.get_user_data(Repo, user, creds, id_key)
8 |
9 | def put_credentials(user, creds, id_key),
10 | do: EctoDbSession.put_credentials(Repo, user, creds, id_key)
11 |
12 | def delete_credentials(user, creds),
13 | do: EctoDbSession.delete_credentials(user, creds)
14 |
15 | def delete_user_logins(user),
16 | do: EctoDbSession.delete_user_logins(user)
17 | end
18 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20161219204505_create_questionnaire_variable.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateQuestionnaireVariable do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:questionnaire_variables) do
6 | add :name, :string
7 | add :project_id, references(:projects, on_delete: :nothing)
8 | add :questionnaire_id, references(:questionnaires, on_delete: :nothing)
9 |
10 | timestamps()
11 | end
12 |
13 | create index(:questionnaire_variables, [:project_id])
14 | create index(:questionnaire_variables, [:questionnaire_id])
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/assets/js/app.jsx:
--------------------------------------------------------------------------------
1 | import "@babel/polyfill"
2 | import "jquery"
3 | import "./errors"
4 | import React from "react"
5 | import { render } from "react-dom"
6 | import { browserHistory } from "react-router"
7 | import { syncHistoryWithStore } from "react-router-redux"
8 | import Root from "./components/layout/Root"
9 | import configureStore from "./store/configureStore"
10 |
11 | const store = configureStore()
12 | const history = syncHistoryWithStore(browserHistory, store)
13 |
14 | const root = document.getElementById("root")
15 | if (root) {
16 | render(, root)
17 | }
18 |
--------------------------------------------------------------------------------
/assets/mobile_survey/js/mobileSurvey.jsx:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React from "react"
3 | import { render } from "react-dom"
4 | import Root from "./components/layout/Root"
5 | import configureStore from "./store/configureStore"
6 |
7 | const root = document.getElementById("root")
8 | if (root) {
9 | const configAttr = root.getAttribute("data-config")
10 | if (configAttr) {
11 | const config = JSON.parse(configAttr)
12 | const store = configureStore({ config })
13 | render(, root)
14 | } else {
15 | console.error("Missing data-config in root element", root)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170224213051_create_survey_log_entry.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.CreateSurveyLogEntry do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:survey_log_entries) do
6 | add :survey_id, :integer
7 | add :mode, :string
8 | add :respondent_id, :integer
9 | add :respondent_hashed_number, :string
10 | add :channel_id, :integer
11 | add :disposition, :string
12 | add :action_type, :string
13 | add :action_data, :string
14 | add :timestamp, :naive_datetime
15 |
16 | timestamps()
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170307180659_set_default_mode_to_respondent_group_channels.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.SetDefaultModeToRespondentGroupChannels do
2 | use Ecto.Migration
3 | alias Ask.Repo
4 |
5 | def up do
6 | Repo.query!("SELECT ch.id, ch.type
7 | FROM channels AS ch").rows
8 | |> Enum.each(fn [channel_id, channel_type] ->
9 | Repo.query!(
10 | "UPDATE respondent_group_channels
11 | SET mode = ?
12 | WHERE channel_id = ?",
13 | [channel_type, channel_id]
14 | )
15 | end)
16 | end
17 |
18 | def down do
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170322170704_alter_unique_index_of_oauth_tokens.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AlterUniqueIndexOfOauthTokens do
2 | use Ecto.Migration
3 |
4 | def up do
5 | Ask.Repo.query("alter table oauth_tokens drop foreign key oauth_tokens_user_id_fkey")
6 | drop unique_index(:oauth_tokens, [:user_id, :provider])
7 | create unique_index(:oauth_tokens, [:user_id, :provider, :base_url])
8 | end
9 |
10 | def down do
11 | drop unique_index(:oauth_tokens, [:user_id, :provider, :base_url])
12 | create unique_index(:oauth_tokens, [:user_id, :provider])
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/test/ask/questionnaire_variable_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.QuestionnaireVariableTest do
2 | use Ask.DataCase
3 |
4 | alias Ask.QuestionnaireVariable
5 |
6 | @valid_attrs %{name: "some content"}
7 | @invalid_attrs %{}
8 |
9 | test "changeset with valid attributes" do
10 | changeset = QuestionnaireVariable.changeset(%QuestionnaireVariable{}, @valid_attrs)
11 | assert changeset.valid?
12 | end
13 |
14 | test "changeset with invalid attributes" do
15 | changeset = QuestionnaireVariable.changeset(%QuestionnaireVariable{}, @invalid_attrs)
16 | refute changeset.valid?
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/test/ask/survey_questionnaire_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.SurveyQuestionnaireTest do
2 | use Ask.DataCase
3 |
4 | alias Ask.SurveyQuestionnaire
5 |
6 | @invalid_attrs %{}
7 |
8 | test "valid changeset foreign_key_constraints" do
9 | changeset =
10 | SurveyQuestionnaire.changeset(%SurveyQuestionnaire{}, %{survey_id: 1, questionnaire_id: 1})
11 |
12 | assert changeset.valid?
13 | end
14 |
15 | test "invalid changeset foreign_key_constraints" do
16 | changeset = SurveyQuestionnaire.changeset(%SurveyQuestionnaire{}, @invalid_attrs)
17 | refute changeset.valid?
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/assets/js/reducers/timezones.js:
--------------------------------------------------------------------------------
1 | import * as actions from "../actions/timezones"
2 |
3 | const initialState = {
4 | fetching: false,
5 | items: null,
6 | }
7 |
8 | export default (state = initialState, action) => {
9 | switch (action.type) {
10 | case actions.RECEIVE_TIMEZONES: {
11 | return {
12 | ...state,
13 | fetching: false,
14 | items: action.timezones,
15 | }
16 | }
17 | case actions.FETCH_TIMEZONES: {
18 | return {
19 | ...state,
20 | fetching: true,
21 | }
22 | }
23 | default:
24 | return state
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/lib/ask/project_channel.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.ProjectChannel do
2 | use Ask.Model
3 |
4 | schema "project_channels" do
5 | belongs_to :project, Ask.Project
6 | belongs_to :channel, Ask.Channel
7 | field :mode, :string
8 |
9 | timestamps()
10 | end
11 |
12 | def changeset(struct, params \\ %{}) do
13 | struct
14 | |> cast(params, [:project_id, :channel_id, :mode])
15 | |> validate_required([:project_id, :channel_id, :mode])
16 | |> unique_constraint(:channel_id_project_id)
17 | |> foreign_key_constraint(:project)
18 | |> foreign_key_constraint(:channel)
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/lib/ask/ecto_types/erlang_term.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.Ecto.Type.ErlangTerm do
2 | @moduledoc """
3 | A custom Ecto type for handling the serialization of arbitrary
4 | data types stored as binary data in the database. Requires the
5 | underlying DB field to be a binary.
6 | """
7 | use Ecto.Type
8 | def type, do: :binary
9 |
10 | def cast(:any, term), do: {:ok, term}
11 | def cast(term), do: {:ok, term}
12 |
13 | def load(binary) when is_binary(binary) do
14 | {:ok, :erlang.binary_to_term(binary)}
15 | end
16 |
17 | def dump(term) do
18 | {:ok, :erlang.term_to_binary(term)}
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/assets/js/components/layout/Root.prod.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from "react"
2 | import { Provider } from "react-redux"
3 | import routes from "../../routes"
4 | import { Router } from "react-router"
5 |
6 | export default class Root extends Component {
7 | static propTypes = {
8 | store: PropTypes.object.isRequired,
9 | history: PropTypes.object.isRequired,
10 | }
11 |
12 | render() {
13 | const { store, history } = this.props
14 | return (
15 |
16 |
17 |
18 | )
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/assets/js/reducers/unusedSample.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import * as actions from "../actions/surveys"
3 |
4 | const initialState = null
5 |
6 | export default (state : ?[UnusedSampleSurvey] = initialState, action: any) => {
7 | switch (action.type) {
8 | case actions.FETCHING_UNUSED_SAMPLE_SURVEYS:
9 | return initialState
10 | case actions.RECEIVE_UNUSED_SAMPLE_SURVEYS:
11 | return receiveUnusedSampleSurveys(state, action)
12 | default:
13 | return state
14 | }
15 | }
16 |
17 | const receiveUnusedSampleSurveys = (state: ?[UnusedSampleSurvey], action: any) => (
18 | action.surveys
19 | )
20 |
21 |
--------------------------------------------------------------------------------
/assets/mobile_survey/js/components/steps/EndStep.jsx:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { Component, PropTypes } from "react"
3 | import Prompt from "../Prompt"
4 |
5 | type Props = {
6 | step: Object,
7 | }
8 |
9 | class EndStep extends Component {
10 | getValue() {
11 | return ""
12 | }
13 |
14 | clearValue() {}
15 |
16 | render() {
17 | const { step } = this.props
18 |
19 | return (
20 |
21 | {(step.prompts || []).map((prompt) => (
22 |
23 | ))}
24 |
25 | )
26 | }
27 | }
28 |
29 | export default EndStep
30 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20171023192702_add_completed_respondents_table.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddCompletedRespondentsTable do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:completed_respondents, primary_key: false) do
6 | add :survey_id, references(:surveys, on_delete: :delete_all), primary_key: true
7 | add :questionnaire_id, :integer, primary_key: true
8 | add :quota_bucket_id, :integer, primary_key: true
9 | add :mode, :string, primary_key: true
10 | add :date, :date, primary_key: true
11 | add :count, :integer, default: 0
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20180219132514_add_floip_endpoints_table.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddFloipEndpointsTable do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:floip_endpoints, primary_key: false) do
6 | add :survey_id, references(:surveys, on_delete: :delete_all), primary_key: true
7 | add :uri, :string, primary_key: true
8 | add :last_pushed_response_id, references(:responses, on_delete: :nothing)
9 | add :retries, :integer, default: 0
10 | add :name, :string
11 |
12 | timestamps()
13 | end
14 |
15 | create index(:surveys, [:id])
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20211125141835_optimize_audios_uuid.exs:
--------------------------------------------------------------------------------
1 | # Use 36 bytes of fixed storage for storing UUIDs instead of a dynamically
2 | # allocated storage of `1 + 36 * 3` bytes (109 bytes) with UTF8, without
3 | # changing the actual UUID format.
4 | defmodule Ask.Repo.Migrations.OptimizeAudiosUuid do
5 | use Ecto.Migration
6 |
7 | def up do
8 | execute "ALTER TABLE audios MODIFY uuid CHAR(36) CHARACTER SET ascii"
9 | create index(:audios, :uuid)
10 | end
11 |
12 | def down do
13 | drop index(:audios, :uuid)
14 | execute "ALTER TABLE audios MODIFY uuid VARCHAR(255) CHARACTER SET utf8"
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/assets/js/config.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | type ProviderConfig = {
4 | friendlyName: string,
5 | baseUrl: string,
6 | channel_ui: boolean,
7 | }
8 |
9 | type Config = {
10 | nuntium: ProviderConfig[],
11 | verboice: ProviderConfig[],
12 | available_languages_for_numbers: string[],
13 | custom_language_names: {[string]: string},
14 | }
15 |
16 | const defaultConfig = {
17 | available_languages_for_numbers: ["en"],
18 | custom_language_names: {},
19 | user_settings: {
20 | language: "en",
21 | },
22 | nuntium: [],
23 | verboice: [],
24 | }
25 |
26 | export const config: Config = window.appConfig || defaultConfig
27 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20171213183513_force_respondent_stats_not_null.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.ForceRespondentStatsNotNull do
2 | use Ecto.Migration
3 |
4 | def up do
5 | execute "UPDATE respondents SET stats = '{\"total_sent_sms\":0,\"total_received_sms\":0,\"total_call_time\":0,\"current_call_last_interaction_time\":null,\"current_call_first_interaction_time\":null}'"
6 |
7 | alter table(:respondents) do
8 | modify :stats, :longtext, null: false
9 | end
10 | end
11 |
12 | def down do
13 | alter table(:respondents) do
14 | modify :stats, :longtext, null: true
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/docs/cloud-instance-configs/iam.ses-smtp-user-policy.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2012-10-17",
3 | "Statement": [
4 | {
5 | "Sid": "VisualEditor0",
6 | "Effect": "Allow",
7 | "Action": [
8 | "ses:SendEmail",
9 | "ses:SendRawEmail"
10 | ],
11 | "Resource": "*",
12 | "Condition": {
13 | "ForAnyValue:StringLike": {
14 | "ses:FromAddress": [
15 | "*@surveda-xx.org",
16 | "noreply@instedd.org"
17 | ]
18 | }
19 | }
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170110002354_set_default_settings_to_users.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.SetDefaultSettingsToUsers do
2 | use Ecto.Migration
3 | alias Ask.Repo
4 |
5 | defmodule User do
6 | use AskWeb, :model
7 |
8 | schema "users" do
9 | field :settings, Ask.Ecto.Type.JSON
10 | end
11 |
12 | def changeset(struct, params \\ %{}) do
13 | struct
14 | |> cast(params, [:settings])
15 | end
16 | end
17 |
18 | def change do
19 | User
20 | |> Repo.all()
21 | |> Enum.each(fn u ->
22 | u |> User.changeset(%{settings: %{}}) |> Repo.update()
23 | end)
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/test/ask/respondent_group_channel_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.RespondentGroupChannelTest do
2 | use Ask.DataCase
3 |
4 | alias Ask.RespondentGroupChannel
5 |
6 | @valid_attrs %{channel_id: 1, respondent_group_id: 2, mode: "a"}
7 | @invalid_attrs %{}
8 |
9 | test "changeset with valid attributes" do
10 | changeset = RespondentGroupChannel.changeset(%RespondentGroupChannel{}, @valid_attrs)
11 | assert changeset.valid?
12 | end
13 |
14 | test "changeset with invalid attributes" do
15 | changeset = RespondentGroupChannel.changeset(%RespondentGroupChannel{}, @invalid_attrs)
16 | refute changeset.valid?
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/lib/ask/ecto_types/string_list.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.Ecto.Type.StringList do
2 | @moduledoc """
3 | A custom type to map questionnaires modes to the database, because
4 | MySQL doesn't support arrays.
5 |
6 | This type maps from an array of strings to strings, joined by comma.
7 | """
8 | use Ecto.Type
9 | def type, do: :string
10 |
11 | def cast(list) when is_list(list), do: {:ok, list}
12 | def cast(_), do: :error
13 |
14 | def load(string) when is_binary(string), do: {:ok, String.split(string, ",", trim: true)}
15 |
16 | def dump(list) when is_list(list), do: {:ok, Enum.join(list, ",")}
17 | def dump(_), do: :error
18 | end
19 |
--------------------------------------------------------------------------------
/lib/ask_web/controllers/coherence/confirmation_controller.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.Coherence.ConfirmationController do
2 | @moduledoc """
3 | Handle confirmation actions.
4 |
5 | A single action, `edit`, is required for the confirmation module.
6 |
7 | """
8 | use AskWeb.Coherence, :controller
9 | use Coherence.ConfirmationControllerBase, schemas: Ask.Coherence.Schemas
10 |
11 | plug Coherence.ValidateOption, :confirmable
12 |
13 | plug :layout_view,
14 | layout: {AskWeb.Coherence.LayoutView, :app},
15 | view: Coherence.ConfirmationView,
16 | caller: __MODULE__
17 |
18 | plug(:redirect_logged_in when action in [:new])
19 | end
20 |
--------------------------------------------------------------------------------
/lib/ask_web/views/changeset_view.ex:
--------------------------------------------------------------------------------
1 | defmodule AskWeb.ChangesetView do
2 | use AskWeb, :view
3 |
4 | @doc """
5 | Traverses and translates changeset errors.
6 |
7 | See `Ecto.Changeset.traverse_errors/2` and
8 | `AskWeb.ErrorHelpers.translate_error/1` for more details.
9 | """
10 | def translate_errors(changeset) do
11 | Ecto.Changeset.traverse_errors(changeset, &translate_error/1)
12 | end
13 |
14 | def render("error.json", %{changeset: changeset}) do
15 | # When encoded, the changeset returns its errors
16 | # as a JSON object. So we just pass it forward.
17 | %{errors: translate_errors(changeset)}
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20171004213436_remove_survey_day_of_week_start_time_and_end_time.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.RemoveSurveyDayOfWeekStartTimeAndEndTime do
2 | use Ecto.Migration
3 |
4 | def up do
5 | alter table(:surveys) do
6 | remove :schedule_day_of_week
7 | remove :schedule_start_time
8 | remove :schedule_end_time
9 | remove :timezone
10 | end
11 | end
12 |
13 | def down do
14 | alter table(:surveys) do
15 | add :schedule_day_of_week, :integer
16 | add :schedule_start_time, :time
17 | add :schedule_end_time, :time
18 | add :timezone, :string
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/assets/js/components/layout/Root.dev.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from "react"
2 | import { Provider } from "react-redux"
3 | import routes from "../../routes"
4 | import { Router } from "react-router"
5 |
6 | export default class Root extends Component {
7 | static propTypes = {
8 | store: PropTypes.object.isRequired,
9 | history: PropTypes.object.isRequired,
10 | }
11 |
12 | render() {
13 | const { store, history } = this.props
14 | return (
15 |
16 |
17 |
18 |
19 |
20 | )
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/assets/static/images/header_icon_default.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/lib/ask/repo.ex:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo do
2 | use Ecto.Repo,
3 | otp_app: :ask,
4 | adapter: Ecto.Adapters.MyXQL
5 |
6 | # In Ecto 2.2 we can't stream a query that preloads associations. In order to
7 | # overcome the issue, we create a chunk back from the stream, and only preload
8 | # for the given batch of structs, that is eventually flattened.
9 | #
10 | # See .
11 | def stream_preload(stream, preloads, size \\ 500) do
12 | stream
13 | |> Stream.chunk_every(size)
14 | |> Stream.flat_map(fn batch -> preload(batch, preloads) end)
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170201123448_add_unique_index_to_invites.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddUniqueIndexToInvites do
2 | use Ecto.Migration
3 |
4 | def up do
5 | Ask.Repo.query!("CREATE TABLE tmp LIKE invites")
6 | Ask.Repo.query!("ALTER TABLE tmp ADD UNIQUE INDEX(project_id, email)")
7 | Ask.Repo.query!("INSERT INTO tmp SELECT * FROM invites
8 | ON DUPLICATE KEY UPDATE tmp.id=invites.id")
9 | Ask.Repo.query!("RENAME TABLE invites to deleteme, tmp to invites")
10 | Ask.Repo.query!("DROP TABLE deleteme")
11 | end
12 |
13 | def down do
14 | Ask.Repo.query!("ALTER TABLE invites DROP INDEX project_id")
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170818182906_add_respondent_stats_table.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddRespondentStatsTable do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:respondent_stats, primary_key: false) do
6 | add :survey_id, references(:surveys, on_delete: :delete_all), primary_key: true
7 | add :questionnaire_id, :integer, primary_key: true
8 | add :state, :string, primary_key: true
9 | add :disposition, :string, primary_key: true
10 | add :quota_bucket_id, :integer, primary_key: true
11 | add :mode, :string, primary_key: true
12 | add :count, :integer, default: 0
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20181102164026_add_locked_to_surveys.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddLockedToSurveys do
2 | use Ecto.Migration
3 | import Ecto.Query
4 | alias Ask.Repo
5 |
6 | defmodule Survey do
7 | use AskWeb, :model
8 |
9 | schema "surveys" do
10 | field :locked, :boolean
11 | end
12 | end
13 |
14 | def up do
15 | alter table(:surveys) do
16 | add :locked, :boolean, default: false
17 | end
18 |
19 | flush()
20 |
21 | from(s in Survey) |> Repo.update_all(set: [locked: false])
22 | end
23 |
24 | def down do
25 | alter table(:surveys) do
26 | remove :locked
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/lib/ask_web/templates/coherence/registration/show.html.eex:
--------------------------------------------------------------------------------
1 | Show account
2 |
3 | -
4 | Name:
5 | <%= @user.name %>
6 |
7 | <%= unless (login_field = Coherence.Config.login_field) == :email do %>
8 | -
9 | <%= humanize login_field %>
10 | <%= Map.get(@user, login_field) %>
11 |
12 | <% end %>
13 |
14 | -
15 | Email:
16 | <%= @user.email %>
17 |
18 |
19 |
20 |
21 | <%= link "Edit", to: registration_path(@conn, :edit) %> |
22 | <%= link "Delete", to: registration_path(@conn, :delete), method: :delete, data: [confirm: "Are you sure?"] %>
23 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20180504144910_add_patterns_to_channels.exs:
--------------------------------------------------------------------------------
1 | defmodule Ask.Repo.Migrations.AddPatternsToChannels do
2 | use Ecto.Migration
3 | import Ecto.Query
4 | alias Ask.Repo
5 |
6 | defmodule Channel do
7 | use AskWeb, :model
8 |
9 | schema "channels" do
10 | field :patterns, Ask.Ecto.Type.JSON
11 | end
12 | end
13 |
14 | def up do
15 | alter table(:channels) do
16 | add :patterns, :text
17 | end
18 |
19 | flush()
20 |
21 | from(c in Channel) |> Repo.update_all(set: [patterns: []])
22 | end
23 |
24 | def down do
25 | alter table(:channels) do
26 | remove :patterns
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/assets/js/components/charts/Stats.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import * as d3 from "d3"
3 |
4 | const formatter = d3.format(",")
5 |
6 | type Props = {
7 | data: Array