+
-
<%= _('New users') %>
+ <%= _('New users') %>
+ <%= _('Completed plans') %>
@@ -76,7 +70,8 @@
-
<%= _('Total users') %>
+ <%= _('Total users') %>
+ <%= _('Total completed plans') %>
@@ -84,4 +79,16 @@
+
+
+
+
<%= _('No. users joined during last year') %>
+
+
+
+
+
+
<%= _('No. completed plans during last year') %>
+
+
\ No newline at end of file
diff --git a/config/routes.rb b/config/routes.rb
index cf050bd..6f7b3db 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -238,6 +238,7 @@
resource :statistics, only: [], controller: "statistics", path: "statistics" do
member do
get :users_joined
+ get :completed_plans
get :using_template
get :plans_by_template
get :plans
diff --git a/db/migrate/20180212124444_add_complete_flag_to_plans.rb b/db/migrate/20180212124444_add_complete_flag_to_plans.rb
new file mode 100644
index 0000000..4cb0b85
--- /dev/null
+++ b/db/migrate/20180212124444_add_complete_flag_to_plans.rb
@@ -0,0 +1,5 @@
+class AddCompleteFlagToPlans < ActiveRecord::Migration
+ def change
+ add_column :plans, :complete, :boolean, default: false
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 25482e5..40a8cae 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20180123161959) do
+ActiveRecord::Schema.define(version: 20180212124444) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -25,8 +25,6 @@
t.datetime "updated_at"
end
- add_index "annotations", ["org_id"], name: "fk_rails_aca7521f72", using: :btree
- add_index "annotations", ["question_id"], name: "fk_rails_0e08e753b6", using: :btree
add_index "annotations", ["question_id"], name: "index_annotations_on_question_id", using: :btree
create_table "answers", force: :cascade do |t|
@@ -36,14 +34,9 @@
t.integer "question_id"
t.datetime "created_at"
t.datetime "updated_at"
- t.integer "lock_version", default: 0
- t.string "label_id", limit: 255
+ t.integer "lock_version", default: 0
end
- add_index "answers", ["plan_id"], name: "fk_rails_84a6005a3e", using: :btree
- add_index "answers", ["question_id"], name: "fk_rails_3d5ed4418f", using: :btree
- add_index "answers", ["user_id"], name: "fk_rails_584be190c2", using: :btree
-
create_table "answers_question_options", id: false, force: :cascade do |t|
t.integer "answer_id", null: false
t.integer "question_option_id", null: false
@@ -54,36 +47,36 @@
create_table "exported_plans", force: :cascade do |t|
t.integer "plan_id"
t.integer "user_id"
- t.string "format", limit: 255
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.string "format"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
t.integer "phase_id"
end
create_table "file_types", force: :cascade do |t|
- t.string "name", limit: 255
- t.string "icon_name", limit: 255
+ t.string "name"
+ t.string "icon_name"
t.integer "icon_size"
- t.string "icon_location", limit: 255
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.string "icon_location"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
end
create_table "file_uploads", force: :cascade do |t|
- t.string "name", limit: 255
- t.string "title", limit: 255
+ t.string "name"
+ t.string "title"
t.text "description"
t.integer "size"
t.boolean "published"
- t.string "location", limit: 255
+ t.string "location"
t.integer "file_type_id"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
end
create_table "friendly_id_slugs", force: :cascade do |t|
- t.string "slug", limit: 255, null: false
- t.integer "sluggable_id", null: false
+ t.string "slug", null: false
+ t.integer "sluggable_id", null: false
t.string "sluggable_type", limit: 40
t.datetime "created_at"
end
@@ -93,15 +86,14 @@
add_index "friendly_id_slugs", ["sluggable_type"], name: "index_friendly_id_slugs_on_sluggable_type", using: :btree
create_table "guidance_groups", force: :cascade do |t|
- t.string "name", limit: 255
+ t.string "name"
t.integer "org_id"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
t.boolean "optional_subset"
t.boolean "published"
end
- add_index "guidance_groups", ["org_id"], name: "fk_rails_819c1dbbc7", using: :btree
add_index "guidance_groups", ["org_id"], name: "index_guidance_groups_on_org_id", using: :btree
create_table "guidances", force: :cascade do |t|
@@ -113,12 +105,11 @@
t.boolean "published"
end
- add_index "guidances", ["guidance_group_id"], name: "fk_rails_20d29da787", using: :btree
add_index "guidances", ["guidance_group_id"], name: "index_guidances_on_guidance_group_id", using: :btree
create_table "identifier_schemes", force: :cascade do |t|
- t.string "name", limit: 255
- t.string "description", limit: 255
+ t.string "name"
+ t.string "description"
t.boolean "active"
t.datetime "created_at"
t.datetime "updated_at"
@@ -127,9 +118,9 @@
end
create_table "languages", force: :cascade do |t|
- t.string "abbreviation", limit: 255
- t.string "description", limit: 255
- t.string "name", limit: 255
+ t.string "abbreviation"
+ t.string "description"
+ t.string "name"
t.boolean "default_language"
end
@@ -143,22 +134,17 @@
t.datetime "updated_at"
end
- add_index "notes", ["answer_id"], name: "fk_rails_907f8d48bf", using: :btree
add_index "notes", ["answer_id"], name: "index_notes_on_answer_id", using: :btree
- add_index "notes", ["user_id"], name: "fk_rails_7f2323ad43", using: :btree
create_table "org_identifiers", force: :cascade do |t|
- t.string "identifier", limit: 255
- t.string "attrs", limit: 255
+ t.string "identifier"
+ t.string "attrs"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "org_id"
t.integer "identifier_scheme_id"
end
- add_index "org_identifiers", ["identifier_scheme_id"], name: "fk_rails_189ad2e573", using: :btree
- add_index "org_identifiers", ["org_id"], name: "fk_rails_36323c0674", using: :btree
-
create_table "org_token_permissions", force: :cascade do |t|
t.integer "org_id"
t.integer "token_permission_type_id"
@@ -166,79 +152,75 @@
t.datetime "updated_at"
end
- add_index "org_token_permissions", ["org_id"], name: "fk_rails_e1db1b22c5", using: :btree
add_index "org_token_permissions", ["org_id"], name: "index_org_token_permissions_on_org_id", using: :btree
- add_index "org_token_permissions", ["token_permission_type_id"], name: "fk_rails_2aa265f538", using: :btree
create_table "orgs", force: :cascade do |t|
- t.string "name", limit: 255
- t.string "abbreviation", limit: 255
- t.string "target_url", limit: 255
- t.string "wayfless_entity", limit: 255
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.string "name"
+ t.string "abbreviation"
+ t.string "target_url"
+ t.string "wayfless_entity"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
t.integer "parent_id"
t.boolean "is_other"
- t.string "sort_name", limit: 255
+ t.string "sort_name"
t.text "banner_text"
- t.string "logo_file_name", limit: 255
+ t.string "logo_file_name"
t.integer "region_id"
t.integer "language_id"
- t.string "logo_uid", limit: 255
- t.string "logo_name", limit: 255
- t.string "contact_email", limit: 255
- t.integer "org_type", default: 0, null: false
- t.string "contact_name", limit: 255
- t.text "links", default: "[]"
- t.boolean "feedback_enabled", default: false
- t.string "feedback_email_subject", limit: 255
+ t.string "logo_uid"
+ t.string "logo_name"
+ t.string "contact_email"
+ t.integer "org_type", default: 0, null: false
+ t.text "links", default: "[]"
+ t.string "contact_name"
+ t.boolean "feedback_enabled", default: false
+ t.string "feedback_email_subject"
t.text "feedback_email_msg"
end
- add_index "orgs", ["language_id"], name: "fk_rails_5640112cab", using: :btree
- add_index "orgs", ["region_id"], name: "fk_rails_5a6adf6bab", using: :btree
-
create_table "perms", force: :cascade do |t|
- t.string "name", limit: 255
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.string "name"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
end
add_index "perms", ["name"], name: "index_perms_on_name", using: :btree
add_index "perms", ["name"], name: "index_roles_on_name_and_resource_type_and_resource_id", using: :btree
create_table "phases", force: :cascade do |t|
- t.string "title", limit: 255
+ t.string "title"
t.text "description"
t.integer "number"
t.integer "template_id"
t.datetime "created_at"
t.datetime "updated_at"
- t.string "slug", limit: 255
+ t.string "slug"
t.boolean "modifiable"
end
add_index "phases", ["template_id"], name: "index_phases_on_template_id", using: :btree
create_table "plans", force: :cascade do |t|
- t.string "title", limit: 255
+ t.string "title"
t.integer "template_id"
t.datetime "created_at"
t.datetime "updated_at"
- t.string "slug", limit: 255
- t.string "data_contact_phone", limit: 255
- t.string "grant_number", limit: 255
- t.string "identifier", limit: 255
+ t.string "slug"
+ t.string "grant_number"
+ t.string "identifier"
t.text "description"
- t.string "principal_investigator", limit: 255
- t.string "principal_investigator_identifier", limit: 255
- t.string "data_contact", limit: 255
- t.string "funder_name", limit: 255
- t.integer "visibility", null: false
- t.string "data_contact_email", limit: 255
- t.string "principal_investigator_email", limit: 255
- t.string "principal_investigator_phone", limit: 255
- t.boolean "feedback_requested", default: false
+ t.string "principal_investigator"
+ t.string "principal_investigator_identifier"
+ t.string "data_contact"
+ t.string "funder_name"
+ t.integer "visibility", null: false
+ t.string "data_contact_email"
+ t.string "data_contact_phone"
+ t.string "principal_investigator_email"
+ t.string "principal_investigator_phone"
+ t.boolean "feedback_requested", default: false
+ t.boolean "complete", default: false
end
add_index "plans", ["template_id"], name: "index_plans_on_template_id", using: :btree
@@ -248,42 +230,29 @@
t.integer "plan_id"
end
- add_index "plans_guidance_groups", ["guidance_group_id"], name: "fk_rails_ec1c5524d7", using: :btree
- add_index "plans_guidance_groups", ["plan_id"], name: "fk_rails_13d0671430", using: :btree
-
create_table "prefs", force: :cascade do |t|
t.text "settings"
t.integer "user_id"
end
- create_table "question_format_labels", id: false, force: :cascade do |t|
- t.integer "id"
- t.string "description", limit: 255
- t.integer "question_id"
- t.integer "number"
- t.datetime "created_at"
- t.datetime "updated_at"
- end
-
create_table "question_formats", force: :cascade do |t|
- t.string "title", limit: 255
+ t.string "title"
t.text "description"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
- t.boolean "option_based", default: false
- t.integer "formattype", default: 0
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.boolean "option_based", default: false
+ t.integer "formattype", default: 0
end
create_table "question_options", force: :cascade do |t|
t.integer "question_id"
- t.string "text", limit: 255
+ t.string "text"
t.integer "number"
t.boolean "is_default"
t.datetime "created_at"
t.datetime "updated_at"
end
- add_index "question_options", ["question_id"], name: "fk_rails_b9c5f61cf9", using: :btree
add_index "question_options", ["question_id"], name: "index_question_options_on_question_id", using: :btree
create_table "questions", force: :cascade do |t|
@@ -298,7 +267,6 @@
t.boolean "modifiable"
end
- add_index "questions", ["question_format_id"], name: "fk_rails_4fbc38c8c7", using: :btree
add_index "questions", ["section_id"], name: "index_questions_on_section_id", using: :btree
create_table "questions_themes", id: false, force: :cascade do |t|
@@ -309,9 +277,9 @@
add_index "questions_themes", ["question_id"], name: "index_questions_themes_on_question_id", using: :btree
create_table "regions", force: :cascade do |t|
- t.string "abbreviation", limit: 255
- t.string "description", limit: 255
- t.string "name", limit: 255
+ t.string "abbreviation"
+ t.string "description"
+ t.string "name"
t.integer "super_region_id"
end
@@ -324,22 +292,11 @@
t.boolean "active", default: true
end
- add_index "roles", ["plan_id"], name: "fk_rails_a1ce6c2772", using: :btree
add_index "roles", ["plan_id"], name: "index_roles_on_plan_id", using: :btree
- add_index "roles", ["user_id"], name: "fk_rails_ab35d699f0", using: :btree
add_index "roles", ["user_id"], name: "index_roles_on_user_id", using: :btree
- create_table "sample_plans", id: false, force: :cascade do |t|
- t.integer "id"
- t.string "url", limit: 255
- t.string "label", limit: 255
- t.integer "template_id"
- t.datetime "created_at"
- t.datetime "updated_at"
- end
-
create_table "sections", force: :cascade do |t|
- t.string "title", limit: 255
+ t.string "title"
t.text "description"
t.integer "number"
t.datetime "created_at"
@@ -352,28 +309,28 @@
add_index "sections", ["phase_id"], name: "index_sections_on_phase_id", using: :btree
create_table "settings", force: :cascade do |t|
- t.string "var", limit: 255, null: false
+ t.string "var", null: false
t.text "value"
- t.integer "target_id", null: false
- t.string "target_type", limit: 255, null: false
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.integer "target_id", null: false
+ t.string "target_type", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
end
add_index "settings", ["target_type", "target_id", "var"], name: "index_settings_on_target_type_and_target_id_and_var", unique: true, using: :btree
create_table "splash_logs", force: :cascade do |t|
- t.string "destination", limit: 255
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.string "destination"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
end
create_table "templates", force: :cascade do |t|
- t.string "title", limit: 255
+ t.string "title"
t.text "description"
t.boolean "published"
t.integer "org_id"
- t.string "locale", limit: 255
+ t.string "locale"
t.boolean "is_default"
t.datetime "created_at"
t.datetime "updated_at"
@@ -382,19 +339,19 @@
t.integer "customization_of"
t.integer "dmptemplate_id"
t.boolean "migrated"
- t.boolean "dirty", default: false
- t.text "links", default: "{\"funder\":[], \"sample_plan\":[]}"
+ t.boolean "dirty", default: false
+ t.text "links", default: "{\"funder\":[], \"sample_plan\":[]}"
end
add_index "templates", ["org_id", "dmptemplate_id"], name: "template_organisation_dmptemplate_index", using: :btree
add_index "templates", ["org_id"], name: "index_templates_on_org_id", using: :btree
create_table "themes", force: :cascade do |t|
- t.string "title", limit: 255
+ t.string "title"
t.text "description"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
- t.string "locale", limit: 255
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.string "locale"
end
create_table "themes_in_guidance", id: false, force: :cascade do |t|
@@ -402,67 +359,61 @@
t.integer "guidance_id"
end
- add_index "themes_in_guidance", ["guidance_id"], name: "fk_rails_a5ab9402df", using: :btree
add_index "themes_in_guidance", ["guidance_id"], name: "index_themes_in_guidance_on_guidance_id", using: :btree
- add_index "themes_in_guidance", ["theme_id"], name: "fk_rails_7d708f6f1e", using: :btree
add_index "themes_in_guidance", ["theme_id"], name: "index_themes_in_guidance_on_theme_id", using: :btree
create_table "token_permission_types", force: :cascade do |t|
- t.string "token_type", limit: 255
+ t.string "token_type"
t.text "text_description"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "user_identifiers", force: :cascade do |t|
- t.string "identifier", limit: 255
+ t.string "identifier"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
t.integer "identifier_scheme_id"
end
- add_index "user_identifiers", ["identifier_scheme_id"], name: "fk_rails_fe95df7db0", using: :btree
- add_index "user_identifiers", ["user_id"], name: "fk_rails_65c9a98cdb", using: :btree
add_index "user_identifiers", ["user_id"], name: "index_user_identifiers_on_user_id", using: :btree
create_table "users", force: :cascade do |t|
- t.string "firstname", limit: 255
- t.string "surname", limit: 255
- t.string "email", limit: 255, default: "", null: false
- t.string "orcid_id", limit: 255
- t.string "shibboleth_id", limit: 255
+ t.string "firstname"
+ t.string "surname"
+ t.string "email", default: "", null: false
+ t.string "orcid_id"
+ t.string "shibboleth_id"
t.datetime "created_at"
t.datetime "updated_at"
- t.string "encrypted_password", limit: 255, default: ""
- t.string "reset_password_token", limit: 255
+ t.string "encrypted_password", default: ""
+ t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
- t.integer "sign_in_count", default: 0
+ t.integer "sign_in_count", default: 0
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
- t.string "current_sign_in_ip", limit: 255
- t.string "last_sign_in_ip", limit: 255
- t.string "confirmation_token", limit: 255
+ t.string "current_sign_in_ip"
+ t.string "last_sign_in_ip"
+ t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
- t.string "invitation_token", limit: 255
+ t.string "invitation_token"
t.datetime "invitation_created_at"
t.datetime "invitation_sent_at"
t.datetime "invitation_accepted_at"
- t.string "other_organisation", limit: 255
+ t.string "other_organisation"
t.boolean "accept_terms"
t.integer "org_id"
- t.string "api_token", limit: 255
+ t.string "api_token"
t.integer "invited_by_id"
- t.string "invited_by_type", limit: 255
+ t.string "invited_by_type"
t.integer "language_id"
- t.string "recovery_email", limit: 255
+ t.string "recovery_email"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
- add_index "users", ["language_id"], name: "fk_rails_45f4f12508", using: :btree
- add_index "users", ["org_id"], name: "fk_rails_e73753bccb", using: :btree
add_index "users", ["org_id"], name: "index_users_on_org_id", using: :btree
create_table "users_perms", id: false, force: :cascade do |t|
@@ -470,7 +421,6 @@
t.integer "perm_id"
end
- add_index "users_perms", ["perm_id"], name: "fk_rails_457217c31c", using: :btree
add_index "users_perms", ["user_id"], name: "index_users_perms_on_user_id", using: :btree
add_foreign_key "annotations", "orgs"
@@ -478,6 +428,8 @@
add_foreign_key "answers", "plans"
add_foreign_key "answers", "questions"
add_foreign_key "answers", "users"
+ add_foreign_key "answers_question_options", "answers"
+ add_foreign_key "answers_question_options", "question_options"
add_foreign_key "guidance_groups", "orgs"
add_foreign_key "guidances", "guidance_groups"
add_foreign_key "notes", "answers"
@@ -495,6 +447,8 @@
add_foreign_key "question_options", "questions"
add_foreign_key "questions", "question_formats"
add_foreign_key "questions", "sections"
+ add_foreign_key "questions_themes", "questions"
+ add_foreign_key "questions_themes", "themes"
add_foreign_key "roles", "plans"
add_foreign_key "roles", "users"
add_foreign_key "sections", "phases"
@@ -505,4 +459,6 @@
add_foreign_key "user_identifiers", "users"
add_foreign_key "users", "languages"
add_foreign_key "users", "orgs"
+ add_foreign_key "users_perms", "perms"
+ add_foreign_key "users_perms", "users"
end
diff --git a/lib/assets/javascripts/views/usage/index.js b/lib/assets/javascripts/views/usage/index.js
index c38673e..f81c668 100644
--- a/lib/assets/javascripts/views/usage/index.js
+++ b/lib/assets/javascripts/views/usage/index.js
@@ -8,7 +8,7 @@
// associated to the attribute data-url of each option
const topicToURL = $(`${usageFormSelector} select[name="topic"]`).find('option').map((i, el) => {
const topic = $(el);
- return { [topic.val()]: $(el).attr('data-url') };
+ return { [topic.val()]: topic.attr('data-url') };
}).get() // An array of objects { topic: URL }
.reduce((acc, value) => Object.assign(acc, value), {}); // Flatten to a single object
// Events
@@ -17,19 +17,32 @@
const target = $(e.target);
const topic = target.find('select[name="topic"]').val();
const orgId = target.find('select[name="org_id"]').val() || target.find('input[name="org_id"]').val();
- $('[data-topic]').hide(); // Hides any data-topic view
+ $('[data-topics]').hide(); // Hides data-topics container
+ $('[data-topic]').hide(); // Hides any data-topic specific
const ajaxSettings = ({ totals = false } = {}) => ({
headers: { Authorization: `Token token="${apiToken}"` },
url: topicToURL[topic],
data: totals ? { topic, org_id: orgId } : target.serialize(),
});
$.when($.ajax(ajaxSettings()), $.ajax(ajaxSettings({ totals: true }))).then((r1, r2) => {
- const view = $(`[data-topic="${topic}"]`);
+ let dataRange;
+ let dataTotals;
if (topic === 'users') {
- view.find('[data-range]').html(r1[0].users_joined);
- view.find('[data-totals]').html(r2[0].users_joined);
- view.show();
+ dataRange = r1[0].users_joined;
+ dataTotals = r2[0].users_joined;
+ } else if (topic === 'completed_plans') {
+ dataRange = r1[0].completed_plans;
+ dataTotals = r2[0].completed_plans;
+ } else {
+ dataRange = null;
+ dataTotals = null;
}
+ const dataTopics = $('[data-topics]');
+ const views = $(`[data-topic="${topic}"]`);
+ dataRange !== null ? dataTopics.find('[data-range]').html(dataRange) : undefined; // eslint-disable-line no-unused-expressions
+ dataTotals !== null ? dataTopics.find('[data-totals]').html(dataTotals) : undefined; // eslint-disable-line no-unused-expressions
+ views.show();
+ dataTopics.show();
}); // TODO request error handling
});
const rangeDatesUpToLastYearFromNow = () => {
@@ -45,35 +58,51 @@
return rangeDates;
};
- const initialise = () => {
- $.ajax({
- headers: { Authorization: `Token token="${apiToken}"` },
- url: topicToURL.users,
- data: { range_dates: rangeDatesUpToLastYearFromNow() },
- }).then((data) => {
- new Chart($('#yearly_users'), { // eslint-disable-line no-new
- type: 'bar',
- data: {
- labels: Object.keys(data),
- datasets: [{
- data: Object.keys(data).map(k => data[k]),
- backgroundColor: '#4F5253', // TODO parameterised according to roadmap main colour instance
- }],
+ const createChart = ({ selector, data, appendTolabel = '' } = {}) => {
+ new Chart($(selector), { // eslint-disable-line no-new
+ type: 'bar',
+ data: {
+ labels: Object.keys(data),
+ datasets: [{
+ data: Object.keys(data).map(k => data[k]),
+ backgroundColor: '#4F5253',
+ // TODO parameterised according to roadmap main colour instance
+ }],
+ },
+ options: {
+ legend: {
+ display: false,
},
- options: {
- legend: {
- display: false,
- },
- tooltips: {
- callbacks: {
- label: tooltipItem => `${tooltipItem.yLabel} users`,
- },
+ tooltips: {
+ callbacks: {
+ label: tooltipItem => `${tooltipItem.yLabel} ${appendTolabel}`,
},
},
- });
- }, (jqXHR) => {
- console.log('error: %o', jqXHR);
+ },
});
};
+ // Sends an AJAX request to our two current endpoints that generate yearly data
+ // (e.g. users_joined_api_v0_statistics_path, completed_plans_api_v0_statistics_path )
+ // and draws a barChart when success response is found
+ const initialise = () => {
+ // Only fire AJAX requests if topicToURL object has keys, i.e. topics mapping to URLs
+ if (Object.keys(topicToURL).length > 0) {
+ const rangeDates = rangeDatesUpToLastYearFromNow();
+ $.ajax({
+ headers: { Authorization: `Token token="${apiToken}"` },
+ url: topicToURL.users,
+ data: { range_dates: rangeDates },
+ }).then((data) => {
+ createChart({ selector: '#yearly_users', data, appendTolabel: 'users' });
+ }); // TODO request error handling
+ $.ajax({
+ headers: { Authorization: `Token token="${apiToken}"` },
+ url: topicToURL.completed_plans,
+ data: { range_dates: rangeDates },
+ }).then((data) => {
+ createChart({ selector: '#yearly_plans', data, appendTolabel: 'completed plans' });
+ }); // TODO request error handling
+ }
+ };
initialise();
});
diff --git a/lib/tasks/bugfix.rake b/lib/tasks/bugfix.rake
index c1582be..927e49c 100644
--- a/lib/tasks/bugfix.rake
+++ b/lib/tasks/bugfix.rake
@@ -78,4 +78,13 @@
template.update_attributes(links: {"funder":[],"sample_plan":[]})
end
end
+
+ desc "Sets completed for plans whose no. questions matches no. valid answers"
+ task set_plan_complete: :environment do
+ Plan.all.each do |p|
+ if p.no_questions_matches_no_answers?
+ p.update_column(:complete, true) # Avoids updating the column updated_at
+ end
+ end
+ end
end
\ No newline at end of file
diff --git a/test/unit/answer_test.rb b/test/unit/answer_test.rb
index a234b4f..aa4ef95 100644
--- a/test/unit/answer_test.rb
+++ b/test/unit/answer_test.rb
@@ -6,71 +6,6 @@
@user = User.last
scaffold_plan
-
- q = @plan.template.questions.select{|q| !q.question_format.option_based }.last
- q = Question.create(text: 'Answer Testing', number: 9,
- section: @plan.template.phases.first.sections.first,
- question_format: QuestionFormat.find_by(option_based: false))
- @answer = Answer.create(user: @user, plan: @plan, question: q, text: 'Testing')
- end
-
- # ---------------------------------------------------
- test "required fields are required" do
- # TODO: an empty answer should not be valid. It should have at least a User, Plan, Question and Text
- # The validation on the model was commented out to get the UI save functionality working
-=begin
- assert_not Answer.new.valid?
-
- # Validate the creation of text based answers
- QuestionFormat.where(option_based: false).each do |qf|
- q = @plan.template.questions.select{|q| q.question_format == qf }.first
-
- assert_not Answer.new(user: @user, question: q, text: 'Testing').valid?, "expected the 'plan' field to be required for a #{qf.title}"
- assert_not Answer.new(plan: @plan, question: q, text: 'Testing').valid?, "expected the 'user' field to be required for a #{qf.title}"
- assert_not Answer.new(user: @user, plan: @plan, text: 'Testing').valid?, "expected the 'question' field to be required for a #{qf.title}"
- assert_not Answer.new(user: @user, question: q, plan: @plan).valid?, "expected the 'text' field to be required for a #{qf.title}"
-
- # Ensure the bar minimum and complete versions are valid
- a = Answer.new(user: @user, plan: @plan, question: q, text: 'Testing')
- assert a.valid?, "expected the 'plan', 'user' and 'question' and 'text' fields to be enough to create an Answer for a #{qf.title}! - #{a.errors.map{|f, m| f.to_s + ' ' + m}.join(', ')}"
- end
-
- # Validate the creation of option based answers (a selection is not required)
- QuestionFormat.where(option_based: true).each do |qf|
- q = @plan.template.questions.select{|q| q.question_format == qf }.first
-
- assert_not Answer.new(user: @user, question: q, question_options: [q.question_options.first]).valid?, "expected the 'plan' field to be required for a #{qf.title}"
- assert_not Answer.new(plan: @plan, question: q, question_options: [q.question_options.first]).valid?, "expected the 'user' field to be required for a #{qf.title}"
- assert_not Answer.new(user: @user, plan: @plan, question_options: [q.question_options.first]).valid?, "expected the 'question' field to be required for a #{qf.title}"
- assert_not Answer.new(user: @user, plan: @plan, question: q).valid?, "expected the 'question_options' field to be required for a #{qf.title}"
-
- # Ensure the bar minimum and complete versions are valid
- a = Answer.new(user: @user, plan: @plan, question: q, question_options: [q.question_options.first])
- assert a.valid?, "expected the 'plan', 'user' and 'question' fields to be enough to create an Answer for a #{qf.title}! - #{a.errors.map{|f, m| f.to_s + ' ' + m}.join(', ')}"
- end
-=end
- end
-
- # ---------------------------------------------------
- test "cannot have multiple answers to the same question within a plan" do
- q = @plan.template.questions.select{|q| !q.question_format.option_based }.first
- Answer.create(user: @user, plan: @plan, question: @plan.questions.first, text: 'Testing')
-
- # TODO: This should pass. We shouldn't be able to add multiple answers to the same question on a Plan.
- # The validation on the model was commented out to get the UI save functionality working
- #assert_not Answer.new(user: @user, plan: @plan, question: @plan.questions.first, text: 'Another answer to the same question!').valid?, "expected to NOT be able to add an answer to a question that already has an answer!"
- end
-
- # ---------------------------------------------------
- test "answer's template must match the plan's template" do
- plan = Plan.new(title: 'Wrong plan test', template: Template.where.not(id: @plan.template.id).first, visibility: :is_test)
- q = @plan.template.questions.select{|q| !q.question_format.option_based }.first
-
- # TODO: This should pass. We shouldn't be able to add an answer to a plan for a question on the wrong template!
- # Uncommenting the validation on the model though causes failures when saving answers from the UI though
- # so we need to reasses the save and re-enable the validation
- #assert_not Answer.new(user: @user, plan: plan, question: @plan.questions.first, text: 'Testing').valid?,
- # "expected to only be able to add an answer if it belongs to the template associated with the plan"
end
# ---------------------------------------------------
@@ -131,24 +66,28 @@
# ---------------------------------------------------
test "can manage belongs_to relationship with User" do
- verify_belongs_to_relationship(@answer, User.last)
+ answer = Answer.create(user: @user, plan: @plan, question: @plan.template.questions.first, text: 'Testing')
+ verify_belongs_to_relationship(answer, User.last)
end
# ---------------------------------------------------
test "can manage belongs_to relationship with Plan" do
- verify_belongs_to_relationship(@answer, @plan)
+ answer = Answer.create(user: @user, plan: @plan, question: @plan.template.questions.first, text: 'Testing')
+ verify_belongs_to_relationship(answer, @plan)
end
# ---------------------------------------------------
test "can manage belongs_to relationship with Question" do
+ answer = Answer.create(user: @user, plan: @plan, question: @plan.template.questions.first, text: 'Testing')
q = @plan.template.phases.first.sections.first.questions.last
- verify_belongs_to_relationship(@answer, q)
+ verify_belongs_to_relationship(answer, q)
end
# ---------------------------------------------------
test "can manage has_many relationship with Notes" do
+ answer = Answer.create(user: @user, plan: @plan, question: @plan.template.questions.first, text: 'Testing')
note = Note.new(text: 'Test Note', user: @user)
- verify_has_many_relationship(@answer, note, @answer.notes.count)
+ verify_has_many_relationship(answer, note, answer.notes.count)
end
test 'is_valid? returns false when no question is associated to an answer' do
@@ -180,4 +119,31 @@
answer = Answer.new(user: @user, plan: @plan, question: q, text: 'foo')
assert(answer.is_valid?)
end
+
+ test 'after_save callback only sets plan complete to true if the number of answers matches the number of questions' do
+ last_question = @plan.template.questions.last
+ @plan.template.questions.each do |q|
+ a = Answer.new(user: @user, plan: @plan, question: q, text: 'foo')
+ if q.question_format.option_based?
+ a.question_options << q.question_options.first
+ end
+ a.save
+ if q == last_question
+ assert(a.plan.complete)
+ else
+ refute(a.plan.complete)
+ end
+ end
+ end
+
+ test 'after_save callback always updates plan.updated_at' do
+ plan_before = nil, plan_after = nil
+ @plan.template.questions.each do |q|
+ a = Answer.new(user: @user, plan: @plan, question: q, text: 'foo')
+ updated_at = a.plan.updated_at
+ a.save
+ new_updated_at = a.plan.updated_at
+ assert(updated_at < new_updated_at)
+ end
+ end
end