8 |
9 |
--------------------------------------------------------------------------------
/db/migrate/20150309071529_create_activities.rb:
--------------------------------------------------------------------------------
1 | class CreateActivities < ActiveRecord::Migration
2 | def change
3 | create_table :activities do |t|
4 | t.string :name
5 | t.string :title
6 | t.integer :dimension_id
7 | t.integer :questionnaire_id
8 |
9 | t.timestamps null: false
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/views/organisation_admin_mailer/help_request.text.erb:
--------------------------------------------------------------------------------
1 | <%= @user.name %>,
2 |
3 | Using the email address <%= @requester_email %>, a user tried to register as the admin of <%= @organisation.title %>. Since you are the current admin of this organisation they were not able to register.
4 |
5 | Message from <%= @requester_email %>:
6 |
7 | <%= @message %>
8 |
--------------------------------------------------------------------------------
/db/migrate/20150309071413_create_questionnaires.rb:
--------------------------------------------------------------------------------
1 | class CreateQuestionnaires < ActiveRecord::Migration
2 | def change
3 | create_table :questionnaires do |t|
4 | t.integer :version
5 | t.string :notes
6 |
7 | t.timestamps null: false
8 | end
9 | add_index(:questionnaires, :version, unique: true)
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/script/cucumber:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | vendored_cucumber_bin = Dir["#{File.dirname(__FILE__)}/../vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
4 | if vendored_cucumber_bin
5 | load File.expand_path(vendored_cucumber_bin)
6 | else
7 | require 'rubygems' unless ENV['NO_RUBYGEMS']
8 | require 'cucumber'
9 | load Cucumber::BINARY
10 | end
11 |
--------------------------------------------------------------------------------
/app/controllers/base_admin_controller.rb:
--------------------------------------------------------------------------------
1 | class BaseAdminController < ApplicationController
2 | before_filter :authenticate_user!, :ensure_admin!
3 |
4 | private
5 |
6 | def ensure_admin!
7 | unless current_user.admin?
8 | sign_out current_user
9 | redirect_to root_path
10 | return false
11 | end
12 | end
13 |
14 | end
15 |
--------------------------------------------------------------------------------
/db/migrate/20150311124917_create_links.rb:
--------------------------------------------------------------------------------
1 | class CreateLinks < ActiveRecord::Migration
2 | def change
3 | create_table :links do |t|
4 | t.references :assessment_answer, index: true
5 | t.string :link
6 | t.string :text
7 |
8 | t.timestamps null: false
9 | end
10 | add_foreign_key :links, :assessment_answers
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20161218004436_create_country_scores_data.rb:
--------------------------------------------------------------------------------
1 | class CreateCountryScoresData < ActiveRecord::Migration
2 | def change
3 | create_table :country_scores_data do |t|
4 | t.string :name
5 | t.string :data
6 |
7 | t.timestamps null: false
8 | end
9 | add_index :country_scores_data, :name, unique: true
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/db/migrate/20161104002256_create_countries.rb:
--------------------------------------------------------------------------------
1 | class CreateCountries < ActiveRecord::Migration
2 | def change
3 | create_table :countries do |t|
4 | t.string :name
5 | t.string :code
6 |
7 | t.timestamps null: false
8 | end
9 | add_index :countries, :name, unique: true
10 | add_index :countries, :code, unique: true
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/views/organisation_admin_mailer/help_request.html.erb:
--------------------------------------------------------------------------------
1 |
<%= @user.name %>,
2 |
3 |
Using the email address <%= @requester_email %>, a user tried to register as the admin of <%= @organisation.title %>. Since you are the current admin of this organisation they were not able to register.
4 |
5 |
Message from <%= @requester_email %>:
6 |
7 |
<%= @message %>
8 |
--------------------------------------------------------------------------------
/spec/models/link_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Link do
4 |
5 | it "should contain a valid url" do
6 | link = FactoryGirl.build(:link)
7 | expect(link.valid?).to eq(true)
8 | end
9 |
10 | it "should not contain a valid url" do
11 | link = FactoryGirl.build(:link, link: "blah")
12 | expect(link.valid?).to eq(false)
13 | end
14 |
15 | end
16 |
--------------------------------------------------------------------------------
/db/migrate/20150309071639_create_questions.rb:
--------------------------------------------------------------------------------
1 | class CreateQuestions < ActiveRecord::Migration
2 | def change
3 | create_table :questions do |t|
4 | t.string :code
5 | t.integer :activity_id
6 | t.integer :questionnaire_id
7 | t.string :text
8 | t.string :notes
9 | t.references :dependency
10 |
11 | t.timestamps null: false
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/db/migrate/20161218043408_add_stats_to_country_scores.rb:
--------------------------------------------------------------------------------
1 | class AddStatsToCountryScores < ActiveRecord::Migration
2 | def change
3 | add_column :country_scores, :initial, :integer
4 | add_column :country_scores, :repeatable, :integer
5 | add_column :country_scores, :defined, :integer
6 | add_column :country_scores, :managed, :integer
7 | add_column :country_scores, :optimising, :integer
8 | end
9 | end
--------------------------------------------------------------------------------
/spec/factories/assessment_answers.rb:
--------------------------------------------------------------------------------
1 | FactoryGirl.define do
2 |
3 | factory :assessment_answer do
4 | question_id 1
5 | answer
6 | assessment_id 1
7 | notes "MyText"
8 | end
9 |
10 | factory :assessment_answer2, class: AssessmentAnswer do
11 | question_id 1
12 | association :answer, factory: :negative_answer
13 | assessment_id 1
14 | notes "MyText"
15 | end
16 |
17 | end
18 |
--------------------------------------------------------------------------------
/db/migrate/20150504121056_add_token_to_assessment.rb:
--------------------------------------------------------------------------------
1 | class AddTokenToAssessment < ActiveRecord::Migration
2 | def up
3 | add_column :assessments, :token, :string
4 | Assessment.where("completion_date is not null").each do |assessment|
5 | assessment.generate_share_token
6 | assessment.save
7 | end
8 | end
9 |
10 | def down
11 | remove_column :assessments, :token
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/test/fixtures/users.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | # This model initially had no columns defined. If you add columns to the
4 | # model remove the '{}' from the fixture names and add the columns immediately
5 | # below each fixture, per the syntax in the comments below
6 | #
7 | one: {}
8 | # column: value
9 | #
10 | two: {}
11 | # column: value
12 |
--------------------------------------------------------------------------------
/features/step_definitions/statistics_steps.rb:
--------------------------------------------------------------------------------
1 | Given(/^the statistics have been generated$/) do
2 | Organisation.create!(title: "All Organisations")
3 | Organisation.create!(title: "All data.gov.uk organisations")
4 |
5 | generator = StatisticsGenerator.new
6 | generator.generate_all
7 | end
8 |
9 | Given /the heatmap threshold is (.+)/ do |t|
10 | ODMAT::Application::HEATMAP_THRESHOLD = Integer.new(t)
11 | end
12 |
--------------------------------------------------------------------------------
/db/migrate/20150309071711_create_answers.rb:
--------------------------------------------------------------------------------
1 | class CreateAnswers < ActiveRecord::Migration
2 | def change
3 | create_table :answers do |t|
4 | t.string :code
5 | t.integer :question_id
6 | t.integer :questionnaire_id
7 | t.string :text
8 | t.string :notes
9 | t.boolean :positive, :default => false
10 | t.integer :score
11 |
12 | t.timestamps null: false
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/db/migrate/20150311110709_create_assessments.rb:
--------------------------------------------------------------------------------
1 | class CreateAssessments < ActiveRecord::Migration
2 | def change
3 | create_table :assessments do |t|
4 | t.references :user, index: true
5 | t.datetime :start_date
6 | t.datetime :completion_date
7 | t.string :title
8 | t.string :notes
9 |
10 | t.timestamps null: false
11 | end
12 | add_foreign_key :assessments, :users
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/bin/rspec:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | #
3 | # This file was generated by Bundler.
4 | #
5 | # The application 'rspec' is installed as part of a gem, and
6 | # this file is here to facilitate running it.
7 | #
8 |
9 | require 'pathname'
10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11 | Pathname.new(__FILE__).realpath)
12 |
13 | require 'rubygems'
14 | require 'bundler/setup'
15 |
16 | load Gem.bin_path('rspec-core', 'rspec')
17 |
--------------------------------------------------------------------------------
/app/models/questionnaire.rb:
--------------------------------------------------------------------------------
1 | class Questionnaire < ActiveRecord::Base
2 | has_many :dimensions
3 | has_many :activities
4 |
5 | validates :version, presence: true
6 |
7 | #which is the current questionnaire that users should be answering?
8 | #TODO: improve this to make it easier to switch which questionaire is "live"
9 | def self.current
10 | return Questionnaire.order("version desc").first
11 | end
12 |
13 | end
14 |
--------------------------------------------------------------------------------
/bin/autospec:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | #
3 | # This file was generated by Bundler.
4 | #
5 | # The application 'autospec' is installed as part of a gem, and
6 | # this file is here to facilitate running it.
7 | #
8 |
9 | require 'pathname'
10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11 | Pathname.new(__FILE__).realpath)
12 |
13 | require 'rubygems'
14 | require 'bundler/setup'
15 |
16 | load Gem.bin_path('rspec-core', 'autospec')
17 |
--------------------------------------------------------------------------------
/bin/cucumber:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | #
3 | # This file was generated by Bundler.
4 | #
5 | # The application 'cucumber' is installed as part of a gem, and
6 | # this file is here to facilitate running it.
7 | #
8 |
9 | require 'pathname'
10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11 | Pathname.new(__FILE__).realpath)
12 |
13 | require 'rubygems'
14 | require 'bundler/setup'
15 |
16 | load Gem.bin_path('cucumber', 'cucumber')
17 |
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/db/migrate/20150311121727_create_assessment_answers.rb:
--------------------------------------------------------------------------------
1 | class CreateAssessmentAnswers < ActiveRecord::Migration
2 | def change
3 | create_table :assessment_answers do |t|
4 | t.references :question, index: true
5 | t.references :answer, index: true
6 | t.text :notes
7 |
8 | t.timestamps null: false
9 | end
10 | add_foreign_key :assessment_answers, :questions
11 | add_foreign_key :assessment_answers, :answers
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/organisation_importer.rb:
--------------------------------------------------------------------------------
1 | class OrganisationImporter
2 | def self.populate(results)
3 | results.each { |result| create_organisation(result, nil) }
4 | end
5 |
6 | def self.create_organisation(result, parent)
7 | org = Organisation.create(parent: parent, name: result['name'], title: result['title'], dgu_id: result['id'])
8 | parent = org.id if parent.nil?
9 | result['children'].each { |child| create_organisation(child, parent) }
10 | end
11 | end
--------------------------------------------------------------------------------
/spec/controllers/admin_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe AdminController do
4 |
5 | it "should allow an admin to view admin pages" do
6 | sign_in FactoryGirl.create(:admin)
7 | get "index"
8 | response.should be_success
9 | end
10 |
11 | it "should not allow other users to view admin pages" do
12 | sign_in FactoryGirl.create(:user)
13 | get "index"
14 | response.code.should eql("302")
15 | end
16 |
17 | end
--------------------------------------------------------------------------------
/app/models/dimension.rb:
--------------------------------------------------------------------------------
1 | class Dimension < ActiveRecord::Base
2 | has_many :activities
3 | belongs_to :questionnaire
4 |
5 | validates :title, presence: true
6 | validates :name, presence: true
7 | validates :name, uniqueness: {scope: :questionnaire_id, message: "dimension name should be unique within questionnaire" }
8 |
9 | def questions
10 | Question.joins(:activity).where(activities: { dimension_id: self.id })
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/views/devise/passwords/edit.html.erb:
--------------------------------------------------------------------------------
1 | <% content_for :title do %>Change your password<% end %>
2 |
3 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %>
4 | <%= devise_error_messages! %>
5 | <%= f.hidden_field :reset_password_token %>
6 |
7 | <%= render "devise/shared/password", f: f %>
8 |
9 |
We received the request to reset your password for your account for Open Data Pathway. Click the link below to reset your password:
4 |
5 |
<%= link_to 'Reset my password now', edit_password_url(@resource, reset_password_token: @token) %>
6 |
7 |
If you have not requested the password reset then don't worry - you can ignore this email. Alternatively you can contact maturity@theodi.org.
8 |
--------------------------------------------------------------------------------
/spec/helpers/targets_helper_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | # Specs in this file have access to a helper object that includes
4 | # the TargetsHelper. For example:
5 | #
6 | # describe TargetsHelper do
7 | # describe "string concat" do
8 | # it "concats two strings with spaces" do
9 | # expect(helper.concat_strings("this","that")).to eq("this that")
10 | # end
11 | # end
12 | # end
13 | describe TargetsHelper do
14 | pending "add some examples to (or delete) #{__FILE__}"
15 | end
16 |
--------------------------------------------------------------------------------
/bin/spring:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # This file loads spring without using Bundler, in order to be fast.
4 | # It gets overwritten when you run the `spring binstub` command.
5 |
6 | unless defined?(Spring)
7 | require "rubygems"
8 | require "bundler"
9 |
10 | if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)
11 | Gem.paths = { "GEM_PATH" => Bundler.bundle_path.to_s }
12 | gem "spring", match[1]
13 | require "spring/binstub"
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3 |
4 | user = User.find_or_create_by!(email: ENV["ADMIN_EMAIL"] ) do |user|
5 | user.password = ENV["ADMIN_PASSWORD"]
6 | user.password_confirmation = ENV["ADMIN_PASSWORD"]
7 | user.terms_of_service = "1"
8 | user.name = "Pathway Administrator"
9 | user.admin = true
10 | end
11 | user.save!
--------------------------------------------------------------------------------
/spec/helpers/assessments_helper_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | # Specs in this file have access to a helper object that includes
4 | # the AssessmentsHelper. For example:
5 | #
6 | # describe AssessmentsHelper do
7 | # describe "string concat" do
8 | # it "concats two strings with spaces" do
9 | # expect(helper.concat_strings("this","that")).to eq("this that")
10 | # end
11 | # end
12 | # end
13 | describe AssessmentsHelper do
14 | pending "add some examples to (or delete) #{__FILE__}"
15 | end
16 |
--------------------------------------------------------------------------------
/spec/helpers/organisations_helper_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | # Specs in this file have access to a helper object that includes
4 | # the OrganisationsHelper. For example:
5 | #
6 | # describe OrganisationsHelper do
7 | # describe "string concat" do
8 | # it "concats two strings with spaces" do
9 | # expect(helper.concat_strings("this","that")).to eq("this that")
10 | # end
11 | # end
12 | # end
13 | describe OrganisationsHelper do
14 | pending "add some examples to (or delete) #{__FILE__}"
15 | end
16 |
--------------------------------------------------------------------------------
/app/views/pages/contact.html.erb:
--------------------------------------------------------------------------------
1 | <% content_for :title do %>Get in touch<% end %>
2 |
3 |
4 | This website is a beta version so its content and features might change.
5 |
6 |
7 |
8 | Please complete our user feedback survey to share your experience with using this tool.
9 |
15 |
--------------------------------------------------------------------------------
/spec/helpers/assessment_answers_helper_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | # Specs in this file have access to a helper object that includes
4 | # the AssessmentAnswersHelper. For example:
5 | #
6 | # describe AssessmentAnswersHelper do
7 | # describe "string concat" do
8 | # it "concats two strings with spaces" do
9 | # expect(helper.concat_strings("this","that")).to eq("this that")
10 | # end
11 | # end
12 | # end
13 | describe AssessmentAnswersHelper do
14 | pending "add some examples to (or delete) #{__FILE__}"
15 | end
16 |
--------------------------------------------------------------------------------
/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = '1.0'
5 |
6 | # Add additional assets to the asset load path
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 |
9 | # Precompile additional assets.
10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
11 | # Rails.application.config.assets.precompile += %w( search.js )
12 |
--------------------------------------------------------------------------------
/app/views/user/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= devise_error_messages! %>
2 |
3 |
14 | <% end %>
15 |
16 | <%= render "devise/shared/links" %>
17 |
--------------------------------------------------------------------------------
/features/admin.feature:
--------------------------------------------------------------------------------
1 | Feature: Administrator Functionality
2 |
3 | Background:
4 | Given I am logged in as an administrator
5 |
6 | Scenario: Viewing the home page as a user
7 | When I go to the homepage
8 | Then I should see a link called "Admin" to "/admin"
9 |
10 | Scenario: Viewing the admin page
11 | Given there is a registered user
12 | When I go to the homepage
13 | And I click on "Admin"
14 | Then I should see "Registered Users"
15 | And I should see "user@example.org"
16 | And I should see "admin@example.org"
17 |
--------------------------------------------------------------------------------
/lib/tasks/countries.rake:
--------------------------------------------------------------------------------
1 | require 'country_importer'
2 | require 'net/http'
3 |
4 | namespace :countries do
5 |
6 | desc "Imports countries from from a source which keeps up to date with ISO 3166-1"
7 | task :import => :environment do
8 | url = URI.parse("https://raw.githubusercontent.com/datasets/country-list/master/data.csv")
9 | response = Net::HTTP.get(url)
10 | results = CSV.parse(response.force_encoding("UTF-8"), :headers => true)
11 | puts "#{results.length} countries found"
12 | CountryImporter.populate(results)
13 | end
14 |
15 | end
16 |
--------------------------------------------------------------------------------
/app/views/organisation_admins/new_contact.html.erb:
--------------------------------------------------------------------------------
1 |
Contact organisation admin
2 |
Somebody is already registered as an admin for that organisation
3 |
You can contact them by filling in the following form
2 |
3 | <%= field_set_tag "" do %>
4 | <%= f.hidden_field :_destroy %>
5 |
6 | <%= f.label :text, title: "The name of the link, e.g. Our Open Data Strategy" %>
7 | <%= f.text_field :text, placeholder: "Link text e.g. Open Data Strategy", class: "form-control" %>
8 |
9 | <%= f.label :link, title: "The URL for the link, e.g. http://example.org" %>
10 | <%= f.text_field :link, placeholder: "http://example.org", class: "form-control" %>
11 |
12 | <%= link_to 'Remove link', '#', class: "btn delete_association" %>
13 |
14 | <% end %>
15 |
16 |
--------------------------------------------------------------------------------
/spec/models/score_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Score do
4 | describe "creation" do
5 |
6 | it "should allow only legal scores" do
7 | score = FactoryGirl.build(:score, score: 6)
8 | score.should_not be_valid
9 |
10 | score = FactoryGirl.build(:score, score: 0)
11 | score.should_not be_valid
12 | end
13 |
14 | it "should allow only legal targets" do
15 | score = FactoryGirl.build(:score, target: 6)
16 | score.should_not be_valid
17 |
18 | score = FactoryGirl.build(:score, target: 0)
19 | score.should_not be_valid
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/controllers/organisation_admins_controller.rb:
--------------------------------------------------------------------------------
1 | class OrganisationAdminsController < ApplicationController
2 |
3 | def new_contact
4 | @organisation = Organisation.find(params[:organisation_id])
5 | @email = params[:email]
6 | end
7 |
8 | def contact
9 | @organisation = Organisation.find(params[:organisation_id])
10 | user = User.where(organisation_id: @organisation.id).first
11 | help_email = OrganisationAdminMailer.help_request(user, @organisation, params[:message], params[:email])
12 | help_email.deliver
13 | redirect_to new_user_registration_path, notice: "A message was sent to the admin of #{@organisation.title}"
14 | end
15 |
16 | end
--------------------------------------------------------------------------------
/app/views/assessments/_first_assessment.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
You haven't started any assessment yet
4 |
5 |
6 |
7 | <%= link_to begin_assessment_path, class: "btn green next" do %>
8 | Start your first assessment
9 | <% end %>
10 |
11 |
12 |
20 |
21 |
--------------------------------------------------------------------------------
/spec/factories/countries.rb:
--------------------------------------------------------------------------------
1 | FactoryGirl.define do
2 | factory :country do
3 | name "United Kingdom"
4 | code "gb"
5 | end
6 |
7 | factory :country1, class: Country do
8 | name "United States"
9 | code "us"
10 | end
11 |
12 | factory :country2, class: Country do
13 | name "Australia"
14 | code "au"
15 | end
16 |
17 | factory :country3, class: Country do
18 | name "Uruguay"
19 | code "uy"
20 | end
21 |
22 | factory :country4, class: Country do
23 | name "United Arab Emirates"
24 | code "ae"
25 | end
26 |
27 | factory :country5, class: Country do
28 | name "New Zealand"
29 | code "nz"
30 | end
31 |
32 | end
33 |
--------------------------------------------------------------------------------
/features/step_definitions/register_steps.rb:
--------------------------------------------------------------------------------
1 | When(/^I fill in Associated organisation with "([^"]*)"$/) do |value|
2 | hidden_field = find :xpath, "//input[@id='user_associated_organisation']", visible: false
3 | hidden_field.set value
4 | end
5 |
6 | When(/^I fill in Associated country with "([^"]*)"$/) do |value|
7 | hidden_field = find :xpath, "//input[@id='user_associated_country']", visible: false
8 | user = FactoryGirl.build(:country)
9 | user.save
10 | hidden_field.set value
11 | end
12 |
13 | Given(/^a user is associated with an organisation named "(.*?)"$/) do |org|
14 | user = FactoryGirl.build(:user)
15 | user.associated_organisation = org
16 | user.save
17 | end
18 |
--------------------------------------------------------------------------------
/lib/tasks/statistics.rake:
--------------------------------------------------------------------------------
1 | require 'statistics_generator'
2 |
3 | namespace :statistics do
4 |
5 | desc "Generates organisation scores for heatmap page"
6 | task :generate => :environment do
7 | generator = StatisticsGenerator.new
8 |
9 | puts "Generating all orgs"
10 | generator.generate_stats_for_all_organisations
11 |
12 | puts "Generating d.g.u orgs"
13 | generator.generate_stats_for_dgu_organisations
14 |
15 | puts "Generating all parent groups"
16 | generator.generate_stats_for_dgu_groups
17 |
18 | puts "Generating all countries orgs"
19 | generator.generate_stats_for_all_countries
20 |
21 | puts "Done"
22 | end
23 |
24 | end
25 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/app/views/assessments/_information_tab.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 | Copy the following link to share your report with colleagues or to publish it openly for others to read.
11 |
12 |
13 |
14 | Anyone accessing the link will be able to read the full report, including your scores, your answers, and the list
15 | of suggested improvements.
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/features/home.feature:
--------------------------------------------------------------------------------
1 | Feature: Home page
2 |
3 | Scenario: Viewing the home page
4 | When I go to the homepage
5 | And I should see a link called "Privacy policy" to "/privacy-policy"
6 | And I should see a link called "Cookie policy" to "/cookie-policy"
7 | And I should see a link called "Terms of use" to "/terms-of-use"
8 | And I should see a link called "Sign in" to "/users/sign_in"
9 | And I should see a link called "Register" to "/users/sign_up"
10 | And I should see a link called "Statistics" to "/statistics"
11 | And I should see a link called "Get in touch" to "/contact"
12 | And I should see a link called "Feedback" to "/contact"
13 | And the page title should read "Open Data Pathway"
14 |
15 | Scenario: Viewing the home page as a user
16 | Given I am logged in as a user
17 | When I go to the homepage
18 | And I should see a link called "Sign out" to "/users/sign_out"
19 | And I should see a link called "Account" to "/users/edit"
20 |
21 | Scenario: Google Analytics
22 | When I go to the homepage
23 | Then I should see "UA-XXXX-Y"
24 |
--------------------------------------------------------------------------------
/LICENCE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | ---------------------
3 |
4 | Copyright (c) 2013 The Open Data Institute
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7 |
8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 |
12 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pathway",
3 | "description": "",
4 | "scripts": {
5 | },
6 | "env": {
7 | "ADMIN_EMAIL": {
8 | "required": true
9 | },
10 | "ADMIN_PASSWORD": {
11 | "required": true
12 | },
13 | "GOOGLE_ANALYTICS_TRACKER": {
14 | "required": true
15 | },
16 | "HEATMAP_THRESHOLD": {
17 | "required": true
18 | },
19 | "HEROKU_POSTGRESQL_BLACK_URL": {
20 | "required": true
21 | },
22 | "HEROKU_POSTGRESQL_ROSE_URL": {
23 | "required": true
24 | },
25 | "LANG": {
26 | "required": true
27 | },
28 | "MANDRILL_APIKEY": {
29 | "required": true
30 | },
31 | "MANDRILL_USERNAME": {
32 | "required": true
33 | },
34 | "RACK_ENV": {
35 | "required": true
36 | },
37 | "RAILS_ENV": {
38 | "required": true
39 | },
40 | "RAILS_SERVE_STATIC_FILES": {
41 | "required": true
42 | },
43 | "SECRET_KEY_BASE": {
44 | "required": true
45 | }
46 | },
47 | "formation": {
48 | },
49 | "addons": [
50 | "scheduler",
51 | "heroku-postgresql"
52 | ],
53 | "buildpacks": [
54 |
55 | ]
56 | }
57 |
--------------------------------------------------------------------------------
/app/models/organisation.rb:
--------------------------------------------------------------------------------
1 | class Organisation < ActiveRecord::Base
2 |
3 | has_many :users
4 | has_many :statistics, class_name: "OrganisationScore"
5 |
6 | validates :title, presence: true
7 | validates :title, uniqueness: true
8 |
9 | before_save :set_name
10 |
11 | def self.dgu
12 | Organisation.where("dgu_id is not null")
13 | end
14 |
15 | def self.all_organisations_group
16 | Organisation.find_by_title("All Organisations")
17 | end
18 |
19 | def self.all_dgu_organisations_group
20 | Organisation.find_by_title("All data.gov.uk organisations")
21 | end
22 |
23 | def to_s
24 | self.title
25 | end
26 |
27 | def as_json(options)
28 | { id: title, text: title }
29 | end
30 |
31 | def set_name
32 | self.name = self.title.parameterize if self.name.blank?
33 | end
34 |
35 | def parent?
36 | Organisation.where(parent: self.id).present?
37 | end
38 |
39 | def dgu_organisation?
40 | return dgu_id.present?
41 | end
42 |
43 | def latest_completed_assessment
44 | user = users.first
45 | return nil unless user.present?
46 | return user.latest_completed_assessment
47 | end
48 |
49 | end
50 |
--------------------------------------------------------------------------------
/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | # Prevent CSRF attacks by raising an exception.
3 | # For APIs, you may want to use :null_session instead.
4 | protect_from_forgery with: :exception
5 |
6 | before_filter :configure_permitted_parameters, if: :devise_controller?
7 |
8 | def index
9 | end
10 |
11 | protected
12 |
13 | def configure_permitted_parameters
14 | devise_parameter_sanitizer.for(:sign_up) << :associated_organisation
15 | devise_parameter_sanitizer.for(:account_update) << :associated_organisation
16 | devise_parameter_sanitizer.for(:sign_up) << :name
17 | devise_parameter_sanitizer.for(:account_update) << :name
18 | end
19 |
20 | rescue_from CanCan::AccessDenied do |exception|
21 | redirect_to(root_url, {:flash => { :alert => exception.message }})
22 | end
23 |
24 | def after_sign_in_path_for(resource)
25 | if current_user.associated_country.blank?
26 | edit_user_registration_path
27 | else
28 | assessments_path
29 | end
30 | end
31 |
32 | def current_ability
33 | @current_ability ||= Ability.new(current_user, params[:token])
34 | end
35 |
36 | end
37 |
--------------------------------------------------------------------------------
/app/views/assessments/_activity_tab.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= render 'theme_label' %>
6 | <%= render 'activity_label' %>
7 | <%= render 'score_label' %>
8 | <%= render 'next_goal_label' %>
9 |
10 |
11 | <% for dimension in @dimensions %>
12 |
13 |
<%= dimension.title %>
14 |
15 | <% dimension.activities.each_with_index do |activity, index| %>
16 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/app/controllers/registrations_controller.rb:
--------------------------------------------------------------------------------
1 | class RegistrationsController < Devise::RegistrationsController
2 | before_filter :configure_permitted_parameters
3 |
4 | def new
5 | super
6 | end
7 |
8 | def create
9 | build_resource(sign_up_params)
10 | resource_saved = resource.save
11 | yield resource if block_given?
12 | if resource_saved
13 | if resource.active_for_authentication?
14 | set_flash_message :notice, :signed_up if is_flashing_format?
15 | sign_up(resource_name, resource)
16 | respond_with resource, location: after_sign_up_path_for(resource)
17 | else
18 | set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_flashing_format?
19 | expire_data_after_sign_in!
20 | respond_with resource, location: after_inactive_sign_up_path_for(resource)
21 | end
22 | else
23 | clean_up_passwords resource
24 | @validatable = devise_mapping.validatable?
25 | if @validatable
26 | @minimum_password_length = resource_class.password_length.min
27 | end
28 |
29 | if resource.errors.include?(:organisation_id)
30 | redirect_to contact_organisation_admin_path(resource.organisation_id, email: resource.email)
31 | else
32 | respond_with resource
33 | end
34 | end
35 | end
36 |
37 | def configure_permitted_parameters
38 | devise_parameter_sanitizer.for(:sign_up) do |u|
39 | u.permit(:name,
40 | :email, :password, :password_confirmation, :associated_organisation, :associated_country, :terms_of_service)
41 | end
42 | devise_parameter_sanitizer.for(:account_update) do |u|
43 | u.permit(:name,
44 | :email, :password, :password_confirmation, :current_password, :associated_organisation, :associated_country, :terms_of_service)
45 | end
46 | end
47 |
48 | end
49 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/question.scss:
--------------------------------------------------------------------------------
1 | // THE ASSESSMENT
2 |
3 | #theAssessment {
4 |
5 | #assessmentHeader {
6 | h2 {
7 | float: left;
8 | margin-top: 0;
9 | font-weight: normal;
10 | color: $odiGrey;
11 | font-weight: 300;
12 | font-size: 21px;
13 | em {
14 | font-weight: 400;
15 | font-style: normal;
16 | }
17 | }
18 | .actions {
19 | float: right;
20 | margin-right: 15px;
21 | margin-bottom: 15px;
22 | a {margin-left: 0.5em;}
23 | }
24 | }
25 |
26 | .assessmentContent {
27 |
28 | >.row { margin-left: 0px !important; margin-right: 0px !important;}
29 | .row:last-child { padding: 1em;}
30 |
31 | border: 1px solid black;
32 |
33 |
34 | #question {
35 | border-right: 1px solid $odiGreyLight;
36 | padding: 1em 1em 1em 0;
37 | margin-bottom: 0;
38 | h3 {margin: 0;}
39 | }
40 |
41 | #additionalInfo {
42 | padding: 1em 1em 1em 0;
43 | border-right: 1px solid $odiGreyLight;
44 | margin-bottom: 0;
45 | textarea {margin-bottom: 1em;}
46 | a.moreLinks {float: right; margin-top: 0.5em; display: block; cursor: pointer;}
47 | }
48 |
49 | footer.row {
50 | border-top: 1px solid $odiGreyLight;
51 | .actionsFull {
52 | a.next {float: right;}
53 | }
54 | }
55 |
56 | #helpColumn {
57 | padding-left: 0;
58 | .helpWrapper {
59 | padding: 15px;
60 | margin: 15px 0;
61 | background: #f5f5f5;
62 | border: 1px solid #e3e3e3;
63 | border-radius: 3px;
64 | }
65 | }
66 | }
67 |
68 | }
69 |
70 | div.nested_object {
71 |
72 | fieldset { margin: 1em; margin-right: 0em; }
73 | a.delete_association { float:right; }
74 | label { display: inline-block; width: 10%; }
75 | input.form-control { width: 69%; display: inline-block; margin-bottom:0.5em; }
76 |
77 | }
78 |
79 | .alert {
80 | margin: 1em 0;
81 | }
82 |
--------------------------------------------------------------------------------
/features/step_definitions/web_steps.rb:
--------------------------------------------------------------------------------
1 | When(/^I go to the homepage$/) do
2 | visit root_path
3 | end
4 |
5 | When(/^I go to the register page$/) do
6 | visit new_user_registration_path
7 | end
8 |
9 | When(/^I go to "(.*?)"$/) do |path|
10 | visit path
11 | end
12 |
13 | When(/^I fill in "([^"]*)" with "([^"]*)"$/) do |field, value|
14 | fill_in field, :with=>value
15 | end
16 |
17 | When(/^I check "(.*?)"$/) do |field|
18 | check field
19 | end
20 |
21 |
22 | When(/^I go back$/) do
23 | visit page.driver.request.env['HTTP_REFERER']
24 | end
25 |
26 | When(/^I choose "([^"]*)"$/) do |value|
27 | choose(value)
28 | end
29 |
30 | Then(/^I should see a link called "(.*?)" to "(.*?)"$/) do |link, url|
31 | page.should have_link(link, :href => url)
32 | end
33 |
34 | Then(/^I should be on the homepage$/) do |text|
35 | assert page.current_path == root_path
36 | end
37 |
38 | Then(/^I should see "(.*?)"$/) do |text|
39 | page.body.should have_text(text)
40 | end
41 |
42 | Then(/^I should not see "(.*?)"$/) do |text|
43 | page.body.should_not have_text(text)
44 | end
45 |
46 | When(/^I click on "(.*?)"$/) do |link|
47 | if link[0].eql?('#') or link[0].eql?('.')
48 | page.execute_script "$('#{link}').trigger('click');"
49 | else
50 | click_link link
51 | end
52 | end
53 |
54 | When(/^I press "(.*?)"$/) do |button|
55 | click_button(button)
56 | end
57 |
58 | Then (/^(?:|I )should be on (.+)$/) do |page_name|
59 | current_path = URI.parse(current_url).path
60 | if current_path.respond_to? :should
61 | current_path.should == path_to(page_name)
62 | else
63 | assert_equal path_to(page_name), current_path
64 | end
65 | end
66 |
67 | Then(/^the page title should read "(.*?)"$/) do |title|
68 | page.has_title? title
69 | end
70 |
71 | Then(/^I should see the page heading "(.*?)"$/) do |title|
72 | expect(find('.page-title').find('h1')).to have_content(title)
73 | end
74 |
--------------------------------------------------------------------------------
/features/sharing.feature:
--------------------------------------------------------------------------------
1 | Feature: Sharing reports
2 | As a user I want to share my reports with other people
3 |
4 | Background:
5 | Given the test survey has been loaded
6 | Given there is an organisation user
7 | Given the following assessments:
8 | | title | notes | start_date | completion_date | user_id | completed |
9 | | 2014 Q4 | End of last year | 2014-12-10 11:07:10 | | 1 | yes |
10 | | 2015 Q1 | Start of year | 2015-02-10 11:07:10 | | 1 | yes |
11 |
12 | Scenario: Attempting to view a report when not logged in
13 | When I go to "/assessments/1/report"
14 | Then I should see "You are not authorized to access this page."
15 |
16 | Scenario: Attempting to view a report when logged in
17 | Given I am logged in as a user
18 | When I go to "/assessments/1/report"
19 | Then I should see "You are not authorized to access this page."
20 |
21 | Scenario: Attempting to view a report with a token
22 | When I click on the sharing link for the "2015 Q1" assessment
23 | Then I should see "Maturity scores"
24 | Then I should see "Assessment answers"
25 | Then I should see "Suggested improvements"
26 | Then I should see "Background notes"
27 | Then I should not see "Share your report"
28 |
29 | Scenario: Attempting to download summary scores CSV with a token
30 | When I click on the sharing link for the "2015 Q1" assessment
31 | And I click on the "Download summary scores" link
32 | Then I should not see "You are not authorized to access this page."
33 | And I should see a CSV
34 |
35 | Scenario: Attempting to download activity scores CSV with a token
36 | When I click on the sharing link for the "2015 Q1" assessment
37 | And I click on the "Download activity scores" link
38 | Then I should not see "You are not authorized to access this page."
39 | And I should see a CSV
40 |
--------------------------------------------------------------------------------
/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Do not eager load code on boot. This avoids loading your whole application
11 | # just for the purpose of running a single test. If you are using a tool that
12 | # preloads Rails for running tests, you may have to set it to true.
13 | config.eager_load = false
14 |
15 | # Configure static file server for tests with Cache-Control for performance.
16 | config.serve_static_files = true
17 | config.static_cache_control = 'public, max-age=3600'
18 |
19 | # Show full error reports and disable caching.
20 | config.consider_all_requests_local = true
21 | config.action_controller.perform_caching = false
22 |
23 | # Raise exceptions instead of rendering exception templates.
24 | config.action_dispatch.show_exceptions = false
25 |
26 | # Disable request forgery protection in test environment.
27 | config.action_controller.allow_forgery_protection = false
28 |
29 | # Tell Action Mailer not to deliver emails to the real world.
30 | # The :test delivery method accumulates sent emails in the
31 | # ActionMailer::Base.deliveries array.
32 | config.action_mailer.delivery_method = :test
33 |
34 | # Randomize the order test cases are executed.
35 | config.active_support.test_order = :random
36 |
37 | # Print deprecation notices to the stderr.
38 | config.active_support.deprecation = :stderr
39 |
40 | # Raises error for missing translations
41 | # config.action_view.raise_on_missing_translations = true
42 |
43 | config.after_initialize do
44 | ActiveRecord::Base.logger = nil
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/lib/assessment_scorer.rb:
--------------------------------------------------------------------------------
1 | class AssessmentScorer
2 |
3 | def initialize(assessment)
4 | @assessment = assessment
5 | @calculator = ProgressCalculator.new(assessment)
6 | end
7 |
8 | def score_activity(activity)
9 | raise "Activity assessment not complete" unless @calculator.activity_completed?(activity)
10 |
11 | #find the assesssment_answers for all questions that are associated with this activity
12 | answers = AssessmentAnswer.joins(:answer, :assessment, question: :activity) \
13 | .where(assessment: @assessment, questions: {activity_id: activity.id}) \
14 | .order("answers.id DESC").limit(1)
15 |
16 | answers.first.answer.score
17 | end
18 |
19 | def score_activities(dimension = nil)
20 | scores = {}
21 | activities = dimension ? dimension.activities : Questionnaire.order("version desc").first.activities
22 | activities.each do |activity|
23 | scores[ activity.name ] = score_activity(activity) if activity.questions.length > 0
24 | end
25 | scores
26 | end
27 |
28 | def score_dimension(dimension)
29 | activities = score_activities(dimension)
30 | return {
31 | score: activities.values.inject(:+),
32 | max: activities.keys.size * 5
33 | }
34 | end
35 |
36 | def score_dimensions
37 | scores = {}
38 | Questionnaire.current.dimensions.each do |dimension|
39 | scores[ dimension.name] = score_dimension(dimension)
40 | end
41 | scores
42 | end
43 |
44 | def score_dimensions_from_saved_results
45 | scores = {}
46 | Questionnaire.current.dimensions.each do |dimension|
47 | scores[ dimension.name ] = { score: 0, max: 0 }
48 | dimension.activities.each do |activity|
49 | scores[ dimension.name ][:max] += 5 if activity.questions.length > 0
50 | saved_score = Score.where( assessment: @assessment, activity: activity).first
51 | scores[ dimension.name ][:score] += saved_score.score if saved_score
52 | end
53 | end
54 | scores
55 | end
56 |
57 | end
--------------------------------------------------------------------------------
/features/step_definitions/question_steps.rb:
--------------------------------------------------------------------------------
1 | Given(/^the test survey has been loaded$/) do
2 | config = File.join( __dir__, "..", "..", "spec", "lib", "test-survey.xls" )
3 | QuestionnaireImporter.load(1, config)
4 | Rails.logger.info "Loaded test survey with: #{Question.count} questions and #{Answer.count} answers.\n"
5 | end
6 |
7 | Given(/^I have started an assessment$/) do
8 | @assessment = FactoryGirl.create(:unfinished_assessment, user_id: @current_user.id)
9 | end
10 |
11 | Given(/^I have completed an assessment$/) do
12 | @assessment = FactoryGirl.create(:assessment, user_id: @current_user.id)
13 | q = Question.where(code: "Q1").first
14 | a = Answer.where(code: "Q1.2").first
15 | @assessment.assessment_answers.create(question: q, answer: a)
16 | @assessment.complete
17 | end
18 |
19 | When(/^I go to the first question$/) do
20 | first_question = Question.where(code: "Q1").first
21 | visit assessment_question_path(@assessment, first_question)
22 | end
23 |
24 | When(/^I go back to the first question$/) do
25 | visit assessment_edit_answer_path(@assessment, @assessment.assessment_answers.first)
26 | end
27 |
28 | Given(/^I have answered the first question including a link$/) do
29 | positive = Answer.where(code: "Q1.1").first
30 | first_question = Question.where(code: "Q1").first
31 | assessment_answer = @assessment.assessment_answers.create(answer: positive, question: first_question)
32 | assessment_answer.links.create(FactoryGirl.attributes_for(:link))
33 | end
34 |
35 | Given(/^I have completed the first activity$/) do
36 | positive = Answer.where(code: "Q1.1").first
37 | first_question = Question.where(code: "Q1").first
38 | negative = Answer.where(code: "Q2.2").first
39 | second_question = Question.where(code: "Q2").first
40 | @assessment.assessment_answers.create(answer: positive, question: first_question)
41 | @assessment.assessment_answers.create(answer: negative, question: second_question, notes: "more info on q2")
42 | Rails.logger.info("\n ------------------------ \n #{negative.to_json} \n #{second_question.to_json} \n")
43 | end
44 |
--------------------------------------------------------------------------------
/app/views/pages/cookie_policy.html.erb:
--------------------------------------------------------------------------------
1 | <% content_for :title do %>Cookie Policy<% end %>
2 |
3 |
4 | Our website uses cookies to distinguish you from other users of our website. This helps us to provide you with a good
5 | experience when you browse our website and also allows us to improve our site. A cookie is a small file of letters and numbers
6 | that we store on your browser or the hard drive of your computer if you agree. Cookies contain information that is
7 | transferred to your computer’s hard drive. You can find more information about the individual cookies we use and the
8 | purposes for which we use them in the table below:
9 |
10 |
11 |
12 |
13 |
14 |
Name(s)
15 |
Purpose
16 |
Expires
17 |
18 |
19 |
20 |
21 |
_ODMAT_session
22 |
This cookie is used to maintain your session with the application.
23 |
2 weeks or until you explicitly logout
24 |
25 |
26 |
_utma
27 | _utmb
28 | _utmc
29 | _utmz
30 |
31 |
32 | These cookies allow us to collect anonymous information about our visitors such that we can improve the website and ensure it caters to peoples needs.
33 | More information.
34 |
35 |
36 | _utma - 2 years
37 | _utmb - 30 minutes
38 | _utmc - when you exit the browser
39 | _utmz - 6 months
40 |
41 |
42 |
43 |
44 |
45 |
46 | You can block these cookies by activating the setting on your browser that allows you to refuse the setting of all or some cookies.
47 | However, if you use your browser settings to block all cookies (including essential cookies) you may not be able to access all or parts of our site.
48 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/home.scss:
--------------------------------------------------------------------------------
1 | #homeContent {
2 |
3 | #cta {
4 |
5 | margin: 1em 0;
6 | text-align: center;
7 | h1 {font-size: 55px; color: $odiGrey; margin-bottom: 30px; }
8 | p {font-size: 24px;}
9 | #takeAssessmnet {
10 | background: $pathwayLightGrey;
11 | border: $odiGrey;
12 | border-radius: 5px;
13 | padding: 25px;
14 | a { padding: 0.5em 1.5em; min-width: 270px; margin: 0 10px;
15 | h2 { margin: 0; font-size: 30px; font-weight: normal; }
16 | }
17 | }
18 | }
19 |
20 | #howItWorks {
21 | text-align: center;
22 | background: $odiOrange;
23 | padding: 2em 0;
24 | margin: 3em 0 ;
25 | color: white;
26 |
27 | .step {
28 |
29 | &.two {border-right: 1px solid darken($odiOrange, 15%); border-left: darken($odiOrange, 15%) 1px solid;}
30 | h4 {
31 | color:white;
32 | margin: 20px 0 20px;
33 | font-weight: normal; font-size: 30px;
34 | span { display: block; font-size: 30px; font-weight: 200; color: white;}
35 | }
36 | .highlight {
37 | font-weight: bold;
38 | text-transform: uppercase;
39 | background: white;
40 | color: $odiOrange;
41 | display: inline-block;
42 | margin: -1em 0 0 0;
43 | padding: 0.25em 0.5em;
44 | position: absolute;
45 | top: 0;
46 | right: 0;
47 | border-radius: 3px 0 0 3px;
48 | color: $odiBlue;
49 | border-right: 1px solid $odiOrange;
50 | }
51 | }
52 | }
53 |
54 |
55 | #benefits, #whatsnext {
56 | .wrapper {
57 | background: $pathwayLightGrey;
58 | border: $odiGrey;
59 | display: inline-block;
60 | border-radius: 5px;
61 | padding: 25px;
62 | display: block;
63 | }
64 | }
65 |
66 | #whatsnext {
67 | h3 {color: $odiBlue; margin-top: 0; font-weight: normal; font-size: 30px;}
68 | }
69 |
70 | #benefits {
71 | h3 {color: $odiBlue; margin-top: 0; font-weight: normal; font-size: 30px;}
72 | ul {
73 | list-style: none;
74 | margin: 0;
75 | padding: 0;
76 | li {
77 | margin-bottom: 5px;
78 | span.glyphicon {color: $odiColour5;}
79 | }
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/features/step_definitions/authentication_steps.rb:
--------------------------------------------------------------------------------
1 | Given(/^I am logged in as an administrator$/) do
2 |
3 | user = FactoryGirl.create(:admin)
4 | @current_user = user
5 |
6 | visit '/users/sign_in'
7 | fill_in "user_email", :with => user.email
8 | fill_in "user_password", :with => user.password
9 | click_button "Sign in"
10 |
11 | end
12 |
13 | Given(/^I am logged in as a user$/) do
14 |
15 | user = FactoryGirl.create(:user)
16 | @current_user = user
17 |
18 | visit '/users/sign_in'
19 | fill_in "user_email", :with => user.email
20 | fill_in "user_password", :with => user.password
21 | click_button "Sign in"
22 |
23 | end
24 |
25 | Given(/^I visit the user admin page$/) do
26 | visit '/users/edit'
27 | end
28 |
29 | Given(/^I am logged in as an organisation user$/) do
30 |
31 | user = FactoryGirl.create(:organisation_user)
32 | @current_user = user
33 |
34 | visit '/users/sign_in'
35 | fill_in "user_email", :with => user.email
36 | fill_in "user_password", :with => user.password
37 | click_button "Sign in"
38 |
39 | end
40 |
41 | Given(/^there is a registered user$/) do
42 | FactoryGirl.create(:user)
43 | end
44 |
45 | Given(/^there is an organisation user$/) do
46 | org = FactoryGirl.create(:organisation)
47 | FactoryGirl.create(:organisation_user, organisation_id: org.id)
48 | end
49 |
50 | Given(/^there is a hierarchy of data.gov.uk organisations$/) do
51 | #1
52 | defra = FactoryGirl.create(:organisation)
53 | #2
54 | ea = FactoryGirl.create(:organisation, title: "Environment Agency", parent: defra.id)
55 | #3
56 | fc = FactoryGirl.create(:organisation, title: "Forestry Commission", parent: defra.id)
57 |
58 | FactoryGirl.create(:organisation_user, organisation_id: ea.id)
59 | @fc_user = FactoryGirl.create(:organisation_user, email: "another@example.org", organisation_id: fc.id)
60 | end
61 |
62 | Given(/^I am logged in as the forestry commission$/) do
63 |
64 | @current_user = @fc_user
65 |
66 | visit '/users/sign_in'
67 | fill_in "user_email", :with => @fc_user.email
68 | fill_in "user_password", :with => @fc_user.password
69 | click_button "Sign in"
70 |
71 | end
72 |
--------------------------------------------------------------------------------
/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // compiled file.
9 | //
10 | // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11 | // about supported directives.
12 | //
13 | //= require jquery
14 | //= require jquery_ujs
15 | //= require turbolinks
16 | //= require bootstrap-sprockets
17 | //= require_tree .
18 | //= require select2
19 | //= require jquery.form-validator
20 |
21 | var attachTypeAhead = function(){
22 | $('.select2').each(function(i, e){
23 | var select = $(e)
24 | options = { minimumInputLength: 3 }
25 | if (select.hasClass('ajax')) {
26 | options.ajax = {
27 | url: select.data('source'),
28 | dataType: 'json',
29 | data: function(term, page) { return { q: term } },
30 | results: function(data, page) { return { results: data } }
31 | }
32 | options.dropdownCssClass = "bigdrop"
33 | options.createSearchChoice = function(term, data) {
34 | if ( $(data).filter( function() {
35 | return this.text.localeCompare(term)===0;
36 | }).length===0) {
37 | return {id:term, text:term};
38 | }
39 | }
40 | options.initSelection = function (element, callback) {
41 | var data = [];
42 | callback({ id: element.val(), text: element.val() });
43 | }
44 | }
45 | select.select2(options)
46 | });
47 | };
48 |
49 | $(document).ready(function() {
50 | $.validate({modules: 'security', form: "#new_user, #edit_user"});
51 | $('[data-toggle="tooltip"]').tooltip();
52 |
53 | $('.activities li a[data-toggle="tab"]').on('show.bs.tab', function (e) {
54 | $('.activities li.active').removeClass('active');
55 | })
56 | });
57 |
--------------------------------------------------------------------------------
/spec/models/activity_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Activity do
4 |
5 | describe "#next_question_for(assessment)" do
6 | let (:assessment) { FactoryGirl.create(:unfinished_assessment) }
7 | let (:activity) do
8 | activity = FactoryGirl.create(:activity)
9 | activity.questions.create(FactoryGirl.attributes_for(:question))
10 | activity.questions.create(FactoryGirl.attributes_for(:question, code: "q2"))
11 | activity.questions.create(FactoryGirl.attributes_for(:question, code: "q3"))
12 | activity
13 | end
14 | let (:positive_answer) { FactoryGirl.create(:answer) }
15 | let (:negative_answer) { FactoryGirl.create(:negative_answer) }
16 |
17 | context 'when no questions have been answered' do
18 | it "should return the first question for the activity" do
19 | expect(activity.next_question_for(assessment)).to eq(activity.questions.order(:id).first)
20 | end
21 | end
22 |
23 | context 'when a question has been answered positively' do
24 | it "should return the second question" do
25 | assessment.assessment_answers.create(question: activity.questions.order(:id).first, answer: positive_answer)
26 | expect(activity.next_question_for(assessment)).to eq(activity.questions.order(:id).second)
27 | end
28 | end
29 | end
30 |
31 | describe "creation" do
32 |
33 | context "valid attributes" do
34 | it "should be valid" do
35 | activity = FactoryGirl.build(:activity)
36 | activity.should be_valid
37 | end
38 | end
39 |
40 | context "invalid attributes" do
41 | it "should not be valid" do
42 | activity = FactoryGirl.build(:activity, name: "")
43 | activity.should_not be_valid
44 |
45 | activity = FactoryGirl.build(:activity, title: "")
46 | activity.should_not be_valid
47 | end
48 |
49 | it "should not support duplicates" do
50 | activity = FactoryGirl.build(:activity)
51 | activity.save
52 | activity2 = FactoryGirl.build(:activity)
53 | activity2.should_not be_valid
54 | end
55 | end
56 |
57 | end
58 | end
59 |
--------------------------------------------------------------------------------
/lib/progress_calculator.rb:
--------------------------------------------------------------------------------
1 | class ProgressCalculator
2 |
3 | attr_reader :assessment
4 |
5 | def initialize(assessment)
6 | @assessment = assessment
7 | end
8 |
9 | #is the assessment complete?
10 | def completed?
11 | @assessment.status == :complete
12 | end
13 |
14 | def activity_completed?(activity)
15 | return progress_for_activity(activity) == :complete
16 | end
17 |
18 | #progress for a single activity
19 | def progress_for_activity(activity)
20 | #if there are no questions, then skipped
21 | return :skipped if activity.questions.empty?
22 |
23 | #find the assesssment_answers for all questions that are associated with this activity
24 | assessment_answers = AssessmentAnswer.joins(:assessment, question: :activity).where(assessment: @assessment.id, questions: {activity_id: activity.id})
25 |
26 | #if there are non, then not started
27 | return :not_started if assessment_answers.empty?
28 |
29 | #if there are same number as questions, then completed
30 | return :complete if assessment_answers.length == activity.questions.length
31 |
32 | #if there are any negative answers, then completed
33 | assessment_answers.each do |aa|
34 | return :complete if !aa.answer.positive?
35 | end
36 |
37 | #otherwise in progress
38 | :in_progress
39 | end
40 |
41 | #summary of progress for all activities
42 | def progress_for_all_activities
43 | progress = {}
44 | Questionnaire.current.activities.each do |activity|
45 | progress[ activity.name ] = progress_for_activity(activity)
46 | end
47 | progress
48 | end
49 |
50 | #percentage completion across the entire maturity assessment
51 | def percentage_progress
52 | progress = progress_for_all_activities.reject{ |k,v| v == :skipped }
53 | completed = progress.values.count {|state| state == :complete }
54 | return (completed.to_f / progress.values.length.to_f * 100).to_i
55 | end
56 |
57 | #can we mark the assessment as completed?
58 | def can_mark_completed?
59 | progress_for_all_activities.reject{ |k,v| v == :skipped || v == :complete }.size == 0
60 | end
61 |
62 | end
--------------------------------------------------------------------------------
/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 | <%= favicon_link_tag 'favicon.ico' %>
15 |
16 | <%= t :site_title %> | <%= yield :title %>
17 |
18 | <%= stylesheet_link_tag "application", :media => "all" %>
19 | <%= javascript_include_tag 'application' %>
20 | <%= csrf_meta_tags %>
21 |
22 |
23 |
24 |
25 | <%= render partial: 'layouts/header', :locals => { :title => t(:site_title) } %>
26 |
27 |
35 | <%= render 'organisations_tab', title: "all peer organisations",
36 | scorer: @scorer,
37 | results: @peer_organisations,
38 | note: "Peer organisations are those that are in the #{@parent_organisation.title} group on data.gov.uk" %>
39 |
40 | <% end %>
41 |
42 |
43 |
44 |
45 |
46 |
47 | <% content_for :javascript_footer do %>
48 | $('.heatmapResults').tooltip({
49 | selector: "*[rel=tooltip]"
50 | })
51 |
52 | <% end %>
53 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | ruby "2.4.1"
3 |
4 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
5 | gem 'rails', '~> 4.2'
6 |
7 | # Bootstrap!
8 | gem 'bootstrap-sass', '~> 3.3'
9 | gem 'sass-rails', '~> 5.0'
10 | gem "font-awesome-rails"
11 |
12 | # Use Uglifier as compressor for JavaScript assets
13 | gem 'uglifier', '>= 3.2.0'
14 | # Use CoffeeScript for .coffee assets and views
15 | gem 'coffee-rails', '~> 4.2'
16 |
17 | # See https://github.com/sstephenson/execjs#readme for more supported runtimes
18 | # gem 'therubyracer', platforms: :ruby
19 |
20 | # Use jquery as the JavaScript library
21 | gem 'jquery-rails'
22 | # Add form validator plugin
23 | gem 'jquery-form-validator-rails'
24 | # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
25 | gem 'turbolinks'
26 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
27 | gem 'jbuilder', '~> 2.7'
28 | # bundle exec rake doc:rails generates the API under doc/api.
29 | gem 'sdoc', '~> 0.4', group: :doc
30 |
31 | # Use ActiveModel has_secure_password
32 | # gem 'bcrypt', '~> 3.1.7'
33 |
34 | # Use Unicorn as the app server
35 | # gem 'unicorn'
36 |
37 | # Use Capistrano for deployment
38 | # gem 'capistrano-rails', group: :development
39 |
40 | gem 'devise', '~> 3.5'
41 | gem 'bcrypt', '~> 3.1'
42 | gem 'cancancan', '~> 2.0'
43 | gem 'spreadsheet'
44 |
45 | gem 'select2-rails', '~> 3.5'
46 | gem 'rack-google-analytics'
47 |
48 | gem 'high_voltage', '~> 3.0'
49 |
50 | group :development do
51 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console
52 | gem 'byebug'
53 | # Access an IRB console on exception pages or by using <%= console %> in views
54 | gem 'web-console', '~> 3.3'
55 | end
56 |
57 | group :development, :test do
58 | # Use sqlite3 as the database for Active Record
59 | gem 'sqlite3'
60 |
61 | gem 'rspec-rails', '~> 3.6'
62 | gem 'cucumber-rails', require: false
63 | gem 'poltergeist'
64 | gem 'factory_girl_rails'
65 | gem 'database_cleaner'
66 | gem 'simplecov', :group => :test
67 |
68 | gem 'dotenv-rails'
69 | end
70 |
71 | group :production do
72 | # Postgres on Heroku in production
73 | gem 'pg', '0.21.0'
74 | gem 'rails_12factor' # For heroku
75 | end
76 |
77 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw]
78 |
--------------------------------------------------------------------------------
/app/views/assessments/_assessment_summary.html.erb:
--------------------------------------------------------------------------------
1 |
33 | <% if assessment.blank? %>
34 | <%= link_to begin_assessment_path, class: "btn green" do %>
35 | Start assessment
36 | <% end %>
37 | <% else %>
38 | <% if current %>
39 | <%= link_to assessment_path(assessment), class: "btn green", role: "button" do %>
40 | Continue assessment »
41 | <% end %>
42 | <% else %>
43 | <%= link_to report_path(assessment), class: "btn btn-default", role: "button" do %>
44 | View assessment »
45 | <% end %>
46 | <% end %>
47 | <%= link_to assessment_path(assessment), method: :delete, data: { confirm: "Are you sure?" }, class: "btn red" do %>
48 | Delete this assessment
49 | <% end %>
50 | <% end %>
51 |
52 |
53 |
--------------------------------------------------------------------------------
/spec/controllers/registrations_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe RegistrationsController do
4 |
5 | before(:each) do
6 | request.env['devise.mapping'] = Devise.mappings[:user]
7 | end
8 |
9 | describe "GET #new" do
10 | it "should new user to view the sign up page" do
11 | get :new
12 | expect(response).to be_success
13 | end
14 | end
15 |
16 | describe "POST #create" do
17 | attrs = FactoryGirl.attributes_for(:user, associated_organisation: "DoSAC")
18 | it "should allow a new user to be created with a new organisation" do
19 | expect{
20 | post :create, user: attrs
21 | }.to change(User,:count).by(1)
22 | end
23 |
24 | it "should create a new organisation for the user" do
25 | attrs = FactoryGirl.attributes_for(:user, associated_organisation: "DoSAC")
26 | expect{
27 | post :create, user: attrs
28 | }.to change(Organisation,:count).by(1)
29 | end
30 |
31 | it "should create a new user, but not a new country" do
32 | attrs = FactoryGirl.attributes_for(:user, associated_country: "foobar")
33 | expect{
34 | post :create, user: attrs
35 | }.to change(User,:count).by(1).and change(Country,:count).by(0)
36 | end
37 |
38 | it "should allow a new user to be created with an existing organisation" do
39 | FactoryGirl.create(:organisation)
40 | expect{
41 | post :create, user: FactoryGirl.attributes_for(:user), associated_organisation: "Department for Environment, Food and Rural Affairs"
42 | }.to change(User,:count).by(1)
43 | end
44 |
45 | it "should allow a new user to be created with an existing country" do
46 | FactoryGirl.create(:organisation)
47 | expect{
48 | post :create, user: FactoryGirl.attributes_for(:user), associated_country: "United Kingdom"
49 | }.to change(User,:count).by(1)
50 | end
51 |
52 | it "should allow a new user to be created without an organisation" do
53 | expect{
54 | post :create, user: FactoryGirl.attributes_for(:user), associated_organisation: ""
55 | }.to change(User,:count).by(1)
56 | end
57 |
58 | it "should allow a new user to be created without a country" do
59 | expect{
60 | post :create, user: FactoryGirl.attributes_for(:user), associated_country: ""
61 | }.to change(User,:count).by(1)
62 | end
63 | end
64 |
65 | end
66 |
--------------------------------------------------------------------------------
/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports and disable caching.
13 | config.consider_all_requests_local = true
14 | config.action_controller.perform_caching = false
15 |
16 | # Don't care if the mailer can't send.
17 | config.action_mailer.raise_delivery_errors = false
18 |
19 | # Print deprecation notices to the Rails logger.
20 | config.active_support.deprecation = :log
21 |
22 | # Raise an error on page load if there are pending migrations.
23 | config.active_record.migration_error = :page_load
24 |
25 | # Debug mode disables concatenation and preprocessing of assets.
26 | # This option may cause significant delays in view rendering with a large
27 | # number of complex assets.
28 | config.assets.debug = true
29 |
30 | # Asset digests allow you to set far-future HTTP expiration dates on all assets,
31 | # yet still be able to expire them through the digest params.
32 | config.assets.digest = true
33 |
34 | # Adds additional error checking when serving assets at runtime.
35 | # Checks for improperly declared sprockets dependencies.
36 | # Raises helpful error messages.
37 | config.assets.raise_runtime_errors = true
38 |
39 | # Raises error for missing translations
40 | # config.action_view.raise_on_missing_translations = true
41 |
42 | config.action_mailer.default_url_options = { :host => 'localhost:3000' }
43 |
44 | config.action_mailer.smtp_settings = {
45 | :address => "smtp.mandrillapp.com",
46 | :port => 2525, # ports 587 and 2525 are also supported with STARTTLS
47 | :enable_starttls_auto => true, # detects and uses STARTTLS
48 | :user_name => ENV["MANDRILL_USERNAME"],
49 | :password => ENV["MANDRILL_APIKEY"], # SMTP password is any valid API key
50 | :authentication => 'plain', # Mandrill supports 'plain' or 'login'
51 | }
52 | config.action_mailer.perform_deliveries = true
53 | config.action_mailer.raise_delivery_errors = true
54 | end
55 |
--------------------------------------------------------------------------------
/app/controllers/assessments_controller.rb:
--------------------------------------------------------------------------------
1 | class AssessmentsController < ApplicationController
2 | def index
3 | @current_assessment = current_user.current_assessment
4 | @assessments = current_user.assessments.completed.order(completion_date: :desc)
5 | @last_assessment = @assessments.first
6 | end
7 |
8 | def begin
9 | if current_user.current_assessment.blank?
10 | authorize! :create, Assessment
11 | @assessment = current_user.assessments.create(title: "New assessment", start_date: Time.now)
12 | @dimensions = Questionnaire.current.dimensions
13 | @progress = ProgressCalculator.new(@assessment)
14 | render 'show'
15 | else
16 | redirect_to assessment_path(current_user.current_assessment)
17 | end
18 | end
19 |
20 | def edit
21 | @assessment = current_user.assessments.find(params[:id])
22 | authorize! :update, @assessment
23 | end
24 |
25 | def update
26 | @assessment = current_user.assessments.find(params[:id])
27 | authorize! :update, @assessment
28 | if @assessment.update_attributes(assessment_params)
29 | redirect_to assessment_path(@assessment)
30 | else
31 | render 'edit'
32 | end
33 | end
34 |
35 | def show
36 | @assessment = Assessment.find(params[:id])
37 | authorize! :read, @assessment
38 | @dimensions = Questionnaire.current.dimensions
39 | @progress = ProgressCalculator.new(@assessment)
40 | end
41 |
42 | def destroy
43 | @assessment = current_user.assessments.find(params[:id])
44 | authorize! :destroy, @assessment
45 | @assessment.destroy
46 | redirect_to assessments_path
47 | end
48 |
49 | def complete
50 | @assessment = current_user.assessments.find(params[:id])
51 | authorize! :update, @assessment
52 | @assessment.complete
53 | redirect_to report_path(@assessment)
54 | end
55 |
56 | def report
57 | @assessment = Assessment.find(params[:id])
58 | @dimensions = Questionnaire.current.dimensions
59 | @scorer = AssessmentScorer.new(@assessment)
60 | @token = params[:token]
61 | authorize! :read, @assessment
62 |
63 | respond_to do |format|
64 | format.html {
65 | render 'report'
66 | }
67 | format.csv {
68 | send_data @assessment.to_csv( params[:style].to_sym ), content_type: "text/csv; charset=utf-8"
69 | }
70 | end
71 | end
72 |
73 | private
74 |
75 | def assessment_params
76 | params.require(:assessment).permit(:title, :notes)
77 | end
78 | end
79 |
--------------------------------------------------------------------------------
/features/report.feature:
--------------------------------------------------------------------------------
1 | Feature: Viewing assessment reports
2 |
3 | Background:
4 | Given the test survey has been loaded
5 | Given I am logged in as a user
6 | Given the following assessments:
7 | | title | notes | start_date | completion_date |
8 | | 2014 Q4 | End of last year | 2015-02-10 11:07:10 | |
9 | And the current assessment is completed
10 |
11 | Scenario: I should see all the tabs
12 | When I go to "/assessments/1/report"
13 | Then I should see "Summary"
14 | And I should see "Maturity scores"
15 | And I should see "Assessment answers"
16 | And I should see "Suggested improvements"
17 | And I should see "Background notes"
18 | And I should see a link called "Download summary scores" to "/assessments/1/report.csv?style=dimension"
19 |
20 | Scenario: I should see the activity scores
21 | When I go to "/assessments/1/report"
22 | Then there should be "5" themes in the "activities" section
23 | And there should be "4" items listed in "#data-management-processes-activities"
24 | Then the element with id "#data-release-process-score" should have the content "1"
25 | And I should see a link called "Download activity scores" to "/assessments/1/report.csv?style=activity"
26 |
27 | Scenario: I should see the report detail
28 | When I go to "/assessments/1/report"
29 | Then the element with id "#Q1-text" should have the content "Have you published any open data?"
30 | And the element with id "#Q1-answer" should have the content "No, we have not yet published any open data"
31 | And there should be "4" items listed in "#data-management-processes-detail"
32 |
33 | Scenario: I should see the report improvements
34 | When I go to "/assessments/1/report"
35 | Then the element with id "#I1" should have the content "Publish at least one open dataset with an open data certificate"
36 |
37 | Scenario: I should see the report information
38 | When I go to "/assessments/1/report"
39 | Then the element with id "#assessor" should have the content "Peter Manion"
40 | Then the element with id "#start-date" should have the content "Tuesday, 10th February 2015"
41 |
42 | Scenario: Obtaining a sharing link
43 | When I go to "/assessments/1/report"
44 | Then I should see a link to share the report
45 |
46 | Scenario: Token doesn't show in report download query string when logged in
47 | When I go to "/assessments/1/report"
48 | Then the CSV link should not contain a token
49 |
--------------------------------------------------------------------------------
/spec/rails_helper.rb:
--------------------------------------------------------------------------------
1 | # This file is copied to spec/ when you run 'rails generate rspec:install'
2 | ENV['RAILS_ENV'] ||= 'test'
3 | require File.expand_path('../../config/environment', __FILE__)
4 | require 'spec_helper'
5 | require 'rspec/rails'
6 | # Add additional requires below this line. Rails is not loaded until this point!
7 |
8 | # Requires supporting ruby files with custom matchers and macros, etc, in
9 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
10 | # run as spec files by default. This means that files in spec/support that end
11 | # in _spec.rb will both be required and run as specs, causing the specs to be
12 | # run twice. It is recommended that you do not name files matching this glob to
13 | # end with _spec.rb. You can configure this pattern with the --pattern
14 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
15 | #
16 | # The following line is provided for convenience purposes. It has the downside
17 | # of increasing the boot-up time by auto-requiring all files in the support
18 | # directory. Alternatively, in the individual `*_spec.rb` files, manually
19 | # require only the support files necessary.
20 | #
21 | # Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
22 |
23 | # Checks for pending migration and applies them before tests are run.
24 | # If you are not using ActiveRecord, you can remove this line.
25 | ActiveRecord::Migration.maintain_test_schema!
26 |
27 | RSpec.configure do |config|
28 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
29 | config.fixture_path = "#{::Rails.root}/spec/fixtures"
30 |
31 | # If you're not using ActiveRecord, or you'd prefer not to run each of your
32 | # examples within a transaction, remove the following line or assign false
33 | # instead of true.
34 | config.use_transactional_fixtures = true
35 |
36 | # RSpec Rails can automatically mix in different behaviours to your tests
37 | # based on their file location, for example enabling you to call `get` and
38 | # `post` in specs under `spec/controllers`.
39 | #
40 | # You can disable this behaviour by removing the line below, and instead
41 | # explicitly tag your specs with their type, e.g.:
42 | #
43 | # RSpec.describe UsersController, :type => :controller do
44 | # # ...
45 | # end
46 | #
47 | # The different available types are documented in the features, such as in
48 | # https://relishapp.com/rspec/rspec-rails/docs
49 | config.infer_spec_type_from_file_location!
50 | end
51 |
--------------------------------------------------------------------------------
/app/views/targets/edit.html.erb:
--------------------------------------------------------------------------------
1 | <% content_for :title do %>Goals for next assessment<% end %>
2 |
3 |
4 |
5 | <%= form_tag assessment_targets_path(@assessment), method: :patch do %>
6 |
7 |
8 | <%= render 'assessments/theme_label' %>
9 |
10 | <%= render 'assessments/activity_label' %>
11 | <%= render 'assessments/score_label' %>
12 | <%= render 'assessments/next_goal_label' %>
13 |
14 |
15 |
16 | <% for dimension in @dimensions %>
17 |
18 |
<%= dimension.title %>
19 |
20 |
21 | <% dimension.activities.each_with_index do |activity, index| %>
22 | <% next if @scores[activity.id].blank? %>
23 |
37 | <% end %>
38 |
39 | <%= submit_tag "Save goals", class: "btn btn-primary next pull-right", id: "submit-bottom" %>
40 | <% end %>
41 |
42 |
43 | <% content_for :javascript_footer do %>
44 | $(function() {
45 | $( "span.more" ).click(function() {
46 | if ( $(this).parent().children('input.yourGoal').val() < 5 ) {
47 | $(this).parent().children('input.yourGoal').val(function(i, oldval) {
48 | return ++oldval;
49 | })
50 | }
51 | });
52 | $( "span.less" ).click(function() {
53 | if ( $(this).parent().children('input.yourGoal').val() > 1 ) {
54 | $(this).parent().children('input.yourGoal').val(function(i, oldval) {
55 | return --oldval;
56 | });
57 | }
58 | })
59 | })
60 | <% end %>
61 |
--------------------------------------------------------------------------------
/spec/models/user_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require "cancan/matchers"
3 |
4 | describe User do
5 |
6 | describe "#associated_organisation=" do
7 | it "should create a valid organisation and associate it with the user" do
8 | user = FactoryGirl.build(:user)
9 | user.associated_organisation = "Department of Social Affairs and Citizenship"
10 | expect(user.associated_organisation.persisted?).to be_truthy
11 | end
12 | end
13 |
14 | describe "#associated_country=" do
15 | it "should create a valid country and associate it with the user" do
16 | user = FactoryGirl.build(:user)
17 | country = FactoryGirl.create(:country)
18 | user.country = country
19 | expect(user.associated_country.persisted?).to be_truthy
20 | end
21 | end
22 |
23 | describe '#current_assessment' do
24 | it "should return the asssessment that has not been completed" do
25 | user = FactoryGirl.create(:user)
26 | a = user.assessments.create(FactoryGirl.attributes_for(:unfinished_assessment))
27 | expect(user.current_assessment).to eql(a)
28 | end
29 |
30 | it "should return nil when all assessments have been completed" do
31 | user = FactoryGirl.create(:user)
32 | a = user.assessments.create(FactoryGirl.attributes_for(:assessment))
33 | expect(user.current_assessment).to eql(nil)
34 | end
35 | end
36 |
37 | describe "creation" do
38 | context "valid attributes" do
39 | it "should be valid" do
40 | user = FactoryGirl.build(:user)
41 | expect(user).to be_valid
42 | end
43 | end
44 |
45 | context "invalid attributes" do
46 | it "should not be valid" do
47 | user = FactoryGirl.build(:user, email: "")
48 | expect(user).to_not be_valid
49 | end
50 |
51 | it "should not allow more than one user to be associated with the same organisation" do
52 | user = FactoryGirl.build(:user)
53 | organisation = FactoryGirl.create(:organisation)
54 | user.organisation = organisation
55 | user.save
56 |
57 | user2 = FactoryGirl.build(:user, email: "user2@example.org")
58 | user2.associated_organisation = "Department for Environment, Food and Rural Affairs"
59 | expect(user2).to_not be_valid
60 | end
61 | end
62 | end
63 |
64 | describe 'abilities' do
65 | let(:user) { FactoryGirl.create(:user) }
66 | let(:assessment) { user.assessments.create(FactoryGirl.attributes_for(:assessment)) }
67 |
68 | subject(:ability) { Ability.new(user) }
69 |
70 | it { should be_able_to :manage, assessment }
71 | end
72 |
73 | end
74 |
--------------------------------------------------------------------------------
/lib/tasks/cucumber.rake:
--------------------------------------------------------------------------------
1 | # IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
2 | # It is recommended to regenerate this file in the future when you upgrade to a
3 | # newer version of cucumber-rails. Consider adding your own code to a new file
4 | # instead of editing this one. Cucumber will automatically load all features/**/*.rb
5 | # files.
6 |
7 |
8 | unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks
9 |
10 | vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
11 | $LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil?
12 |
13 | begin
14 | require 'cucumber/rake/task'
15 |
16 | namespace :cucumber do
17 | Cucumber::Rake::Task.new({:ok => 'test:prepare'}, 'Run features that should pass') do |t|
18 | t.binary = vendored_cucumber_bin # If nil, the gem's binary is used.
19 | t.fork = true # You may get faster startup if you set this to false
20 | t.profile = 'default'
21 | end
22 |
23 | Cucumber::Rake::Task.new({:wip => 'test:prepare'}, 'Run features that are being worked on') do |t|
24 | t.binary = vendored_cucumber_bin
25 | t.fork = true # You may get faster startup if you set this to false
26 | t.profile = 'wip'
27 | end
28 |
29 | Cucumber::Rake::Task.new({:rerun => 'test:prepare'}, 'Record failing features and run only them if any exist') do |t|
30 | t.binary = vendored_cucumber_bin
31 | t.fork = true # You may get faster startup if you set this to false
32 | t.profile = 'rerun'
33 | end
34 |
35 | desc 'Run all features'
36 | task :all => [:ok, :wip]
37 |
38 | task :statsetup do
39 | require 'rails/code_statistics'
40 | ::STATS_DIRECTORIES << %w(Cucumber\ features features) if File.exist?('features')
41 | ::CodeStatistics::TEST_TYPES << "Cucumber features" if File.exist?('features')
42 | end
43 | end
44 | desc 'Alias for cucumber:ok'
45 | task :cucumber => 'cucumber:ok'
46 |
47 | task :default => :cucumber
48 |
49 | task :features => :cucumber do
50 | STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***"
51 | end
52 |
53 | # In case we don't have the generic Rails test:prepare hook, append a no-op task that we can depend upon.
54 | task 'test:prepare' do
55 | end
56 |
57 | task :stats => 'cucumber:statsetup'
58 | rescue LoadError
59 | desc 'cucumber rake task not available (cucumber not installed)'
60 | task :cucumber do
61 | abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
62 | end
63 | end
64 |
65 | end
66 |
--------------------------------------------------------------------------------
/app/views/assessments/_overview.html.erb:
--------------------------------------------------------------------------------
1 |
52 | <% end %>
53 |
--------------------------------------------------------------------------------
/features/step_definitions/assessment_steps.rb:
--------------------------------------------------------------------------------
1 | Given(/^the following assessments:$/) do |table|
2 | table.hashes.each do |attributes|
3 | attributes.merge!(user_id: @current_user.id) unless attributes[:user_id].present?
4 | complete = attributes.delete("completed")
5 | assessment = FactoryGirl.create(:assessment, attributes)
6 | if complete == "yes"
7 | AssessmentAnswer.create( assessment: assessment, question: Question.first, answer: Answer.find_by_code("Q1.2") )
8 | assessment.complete
9 | end
10 | end
11 | end
12 |
13 | Given(/^the current assessment is ready for completion$/) do
14 | AssessmentAnswer.create( assessment: @current_user.current_assessment, question: Question.first, answer: Answer.find_by_code("Q1.2") )
15 | end
16 |
17 | When(/^I complete the assessment$/) do
18 | all('.complete').first.click
19 | end
20 |
21 | When(/^I delete an assessment$/) do
22 | all('.assessment')[0].first(:link, "Delete").click
23 | end
24 |
25 | Then(/^I should see "(.*?)" assessments$/) do |count|
26 | expect(page).to have_selector('.assessment', count: count)
27 | end
28 |
29 | Given(/^the current assessment is completed$/) do
30 | AssessmentAnswer.create( assessment: @current_user.current_assessment, question: Question.first, answer: Answer.find_by_code("Q1.2") )
31 | @current_user.current_assessment.complete
32 | end
33 |
34 | Then(/^the element with id "(.*?)" should have the content "(.*?)"$/) do |selector, text|
35 | page.assert_selector(selector, :text => text)
36 | end
37 |
38 | When(/^there should be "(.*?)" themes in the "(.*?)" section$/) do |count, section|
39 | page.assert_selector(".activities .theme", :count => count.to_i)
40 | end
41 |
42 | Then(/^there should be "(.*?)" items listed in "(.*?)"$/) do |count, selector|
43 | page.assert_selector("#{selector} li", :count => count.to_i)
44 | end
45 |
46 | Then(/^I should see a link to share the report$/) do
47 | page.assert_selector("#share-report-link")
48 | end
49 |
50 | When(/^I click on the sharing link for the "(.*?)" assessment$/) do |title|
51 | assessment = Assessment.where(title: title).first
52 | path = "/assessments/#{assessment.id}/report?token=#{assessment.token}"
53 | visit path
54 | end
55 |
56 | When(/^I click on the "(.*?)" link$/) do |text|
57 | click_link text
58 | end
59 |
60 | Then(/^the response should be "(.*?)"$/) do |code|
61 | expect(page.status_code).to eq(code.to_i)
62 | end
63 |
64 | Then(/^I should see a CSV$/) do
65 | expect(page.response_headers['Content-Type']).to match /text\/csv/
66 | end
67 |
68 | Then(/^the CSV link should not contain a token$/) do
69 | link = find("a", text: /Download summary scores/)
70 |
71 | expect(link[:href]).to_not match(/token=/)
72 | end
73 |
--------------------------------------------------------------------------------
/app/views/devise/registrations/new.html.erb:
--------------------------------------------------------------------------------
1 | <% content_for :title do %><%= t :sign_up_page %><% end %>
2 |
3 |
4 |
5 |
6 | Register for the Open Data Pathway to record your maturity assessment scores and targets.
7 |
8 |
Your results are private unless you decide to share.
9 |
59 |
60 | <% content_for :javascript_footer do %>
61 | $(document).ready(function(){
62 | attachTypeAhead();
63 | });
64 | <% end %>
65 |
--------------------------------------------------------------------------------
/lib/questionnaire_importer.rb:
--------------------------------------------------------------------------------
1 | class QuestionnaireImporter
2 |
3 | def self.load(version, config, notes="")
4 | questionnaire = create_questionnaire(version, notes)
5 | book = Spreadsheet.open config
6 | populate_activities(questionnaire, book.worksheet('activities') )
7 | populate_answers(questionnaire, book.worksheet('questions') )
8 | populate_improvements(questionnaire, book.worksheet('improvements') )
9 | end
10 |
11 | def self.update(version, config)
12 | questionnaire = Questionnaire.find_by_version(version)
13 | book = Spreadsheet.open config
14 | populate_activities(questionnaire, book.worksheet('activities') )
15 | populate_answers(questionnaire, book.worksheet('questions') )
16 | populate_improvements(questionnaire, book.worksheet('improvements') )
17 | end
18 |
19 | def self.create_questionnaire(version, notes="")
20 | Questionnaire.create(version: version, notes: notes)
21 | end
22 |
23 | def self.populate_activities(questionnaire, worksheet)
24 | worksheet.each 1 do |row|
25 | dimension = Dimension.find_or_create_by(questionnaire_id: questionnaire.id, name: create_slug(row[0]))
26 | dimension.title = row[0]
27 | dimension.save
28 | activity = Activity.find_or_create_by(questionnaire_id: questionnaire.id, dimension_id: dimension.id, name: create_slug(row[1]))
29 | activity.title = row[1]
30 | activity.save
31 | end
32 | end
33 |
34 | def self.populate_answers(questionnaire, worksheet)
35 | question = nil
36 | activity = nil
37 | worksheet.each 1 do |row|
38 | if !row[0].blank?
39 | activity = Activity.find_by_name( create_slug( row[2] ) )
40 | question = Question.find_or_create_by(questionnaire_id: questionnaire.id, code: row[0], activity: activity)
41 | question.update_attributes( {
42 | text: row[3],
43 | notes: row[4],
44 | dependency_id: row[5].blank? ? nil : Question.find_by_code( row[5] ).id
45 | })
46 | question.save
47 | end
48 | answer = Answer.find_or_create_by(questionnaire_id: questionnaire.id, question: question, code: row[6])
49 | answer.update_attributes( {
50 | text: row[7],
51 | positive: row[8] == "Y",
52 | score: row[9].blank? ? nil : row[9].to_i
53 | })
54 | answer.save!
55 | end
56 | end
57 |
58 | def self.populate_improvements(questionnaire, worksheet)
59 | worksheet.each 1 do |row|
60 | answer = Answer.find_by_code( row[1] )
61 | improvement = Improvement.find_or_create_by( answer: answer, code: row[0] )
62 | improvement.update_attributes({
63 | notes: row[2]
64 | })
65 | improvement.save
66 | end
67 | end
68 |
69 | def self.create_slug(title)
70 | title.parameterize
71 | end
72 | end
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Open Data Pathway
2 |
3 | Open Data Pathway is open source, and contributions are gratefully accepted!
4 | Details on how to contribute are below. By participating in this project, you agree to abide by our [Code of Conduct](https://github.com/theodi/pathway/blob/CODE_OF_CONDUCT.md).
5 |
6 | Before you start coding on an issue, please reach out to us either on our [gitter channel](https://gitter.im/theodi/toolbox) or leave a comment on the issue ticket you are interested in contributing towards to indicate your interest in helping.
7 |
8 | If this is your first time contributing to the ODI’s codebase you will need to [create a fork of this repository](https://help.github.com/articles/fork-a-repo/).
9 |
10 | Consult our [Getting Started Guide](https://github.com/theodi/toolbox/wiki/Developers-Guide:-Getting-Started) (if necessary) and then follow the [readme instructions](https://github.com/theodi/pathway/blob/master/README.md#development) to get your Development environment running locally
11 |
12 | Ensure that the [tests](https://github.com/theodi/pathway/blob/master/README.md#tests) pass before working on your contribution
13 |
14 | ## Code Review Process
15 |
16 | All contributions to the codebase - whether fork or pull request - will be reviewed per the below criteria.
17 | To increase your chances of your push being accepted please be aware of the following
18 | - Write [well formed commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
19 | - Follow our [style guide recommendations](https://github.com/theodi/toolbox/blob/README.md#code-style-guide)
20 | - Write tests for all changes (additions or refactors of existing code).
21 | - Of the github integrations we use two will be utilised to check appraise your contribution. In order of priority these are
22 | - Travis ensures that all tests (existing and additions) pass
23 | - Travis/Coveralls ensures that overall test coverage for lines of code meets a certain threshold. If this metric dips below what it previously was for the repository you’re pushing to then your PR will be rejected
24 | - Gemnasium ensures dependencies are up to date
25 | - Once your PR is published and passes the above checks a repository administrator will review your contribution. Where appropriate comments may be provided and amendments suggested before your PR is merged into Master.
26 | - Once your PR is accepted you will be granted push access to the repository you have contributed to! Congratulations on joining our community, you’ll no longer need to work from forks.
27 |
28 | If you make a contribution to another repository in the Toolbox you will be expected to repeat this process. Read more about that [here](https://github.com/theodi/toolbox/blob/master/README.md#push-access).
29 |
30 | ## Code Style Guide
31 |
32 | We follow the same code style conventions as detailed in Github’s [Ruby Style Guide](https://github.com/github/rubocop-github/blob/master/STYLEGUIDE.md)
33 |
--------------------------------------------------------------------------------
/app/views/assessments/report.html.erb:
--------------------------------------------------------------------------------
1 | <% content_for :title do %>
2 | <%= @assessment.user.organisation ? @assessment.user.organisation.title : "" %> <%= @assessment.title %>
3 | <% end %>
4 | <% content_for :breadcrumb do %>
5 | <%= breadcrumb("Report") %>
6 | <% end %>
7 |
8 | <% if User.can_share?(current_user, @assessment) %>
9 | <%= render 'share_modal', assessment: @assessment %>
10 | <% end %>
11 |
12 |
63 |
64 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # To learn more, please read the Rails Internationalization guide
20 | # available at http://guides.rubyonrails.org/i18n.html.
21 |
22 | en:
23 | site_title: "Open Data Pathway"
24 |
25 | #Authentication & Registration
26 | sign_up_page: "Registration"
27 | sign_up: "Register"
28 | sign_up_now: "Register now?"
29 | sign_in: "Sign in"
30 | sign_in_now: "Sign in here"
31 | sign_out: "Sign out"
32 | account: "Account"
33 | admin: "Admin"
34 | remember_me: "Keep me signed in"
35 | #Assessments
36 | my_assessments: "My assessments"
37 |
38 | not_started: "Start"
39 | in_progress: "Continue"
40 | complete: "Complete"
41 |
42 | enter_email: "Please enter a valid email address"
43 | accept_terms: "Please check this box if you want to proceed"
44 | name_validation: "Please enter your name"
45 |
46 | password_confirmation: "The password doesn't match"
47 | password_validation: "Your password must be at least 8 characters"
48 | country_validation: "Please enter your country"
49 |
50 | helpers:
51 | label:
52 | user:
53 | email: "Email"
54 | name: "Full name"
55 | associated_organisation: "Organisation"
56 | associated_country: "Country"
57 |
58 | organisation_registration: " (check if your organisation already has an account)"
59 | country_registration: " (we use your country to create aggregate statistics)"
60 | data-management-processes: "key business processes that underpin data management and publication, e.g. quality control, publication workflows, and the adoption of technical standards"
61 | knowledge-skills: "the culture of open data within an organisation by identifying the knowledge sharing, training and learning required to embed an understanding of the benefits of open data"
62 | customer-support-engagement: "the manner in which an organisation engages with both their data sources and their data re-users to provide sufficient support and feedback to make open data successful"
63 | investment-financial-performance: "the level of knowledge an organisation has about the value of their datasets and the budgetary and financial oversight required to support their publication. For data consumers, it covers their understanding of the costs and value associated with the reuse of third-party datasets"
64 | strategic-oversight: "an organisation's strategy for data sharing and reuse, and the leadership in place to deliver that strategy"
65 |
--------------------------------------------------------------------------------