class Plan

Constants

A4_PAGE_HEIGHT
A4_PAGE_WIDTH
FONT_HEIGHT_CONVERSION_FACTOR
FONT_WIDTH_HEIGHT_RATIO
ROUNDING

Public Instance Methods

add_guidance_to_array(guidance_array, guidance_group, theme, guidance) click to toggle source
# File app/models/plan.rb, line 119
def add_guidance_to_array(guidance_array, guidance_group, theme, guidance)
        
        if guidance_array[guidance_group].nil? then
                guidance_array[guidance_group] = {}
        end
        if theme.nil? then
                if guidance_array[guidance_group]["no_theme"].nil? then
                        guidance_array[guidance_group]["no_theme"] = []
                end
                if !guidance_array[guidance_group]["no_theme"].include?(guidance) then
                        guidance_array[guidance_group]["no_theme"].push(guidance)
                end
        else
                if guidance_array[guidance_group][theme].nil? then
                        guidance_array[guidance_group][theme] = []
                end
                if !guidance_array[guidance_group][theme].include?(guidance) then
                        guidance_array[guidance_group][theme].push(guidance)
                end
        end
        
return guidance_array
end
administerable_by(user_id) click to toggle source
# File app/models/plan.rb, line 168
def administerable_by(user_id)
        return project.readable_by(user_id)
end
answer(qid, create_if_missing = true) click to toggle source
# File app/models/plan.rb, line 53
def answer(qid, create_if_missing = true)
        answer = answers.where(:question_id => qid).order("created_at DESC").first
        question = Question.find(qid)
        if answer.nil? && create_if_missing then
                answer = Answer.new
                answer.plan_id = id
                answer.question_id = qid
                answer.text = question.default_value
                default_options = Array.new
                question.options.each do |option|
                        if option.is_default
                                default_options << option
                        end
                end
                answer.options = default_options
        end
        return answer
end
delete_recent_locks(user_id) click to toggle source
# File app/models/plan.rb, line 291
def delete_recent_locks(user_id)
        plan_sections.where(:user_id => user_id).each do |lock|
                lock.delete
        end
end
details() click to toggle source
# File app/models/plan.rb, line 230
def details
        details = {
                "project_title" => project.title,
                "phase_title" => version.phase.title,
                "sections" => {}
        }
        sections.sort_by(&:"number").each do |s|
                details["sections"][s.number] = {}
                details["sections"][s.number]["title"] = s.title
                details["sections"][s.number]["questions"] = {}
                s.questions.order("number").each do |q|
                        details["sections"][s.number]["questions"][q.number] = {}
                        details["sections"][s.number]["questions"][q.number]["question_text"] = q.text
                        answer = answer(q.id, false)
                        if ! answer.nil? then
            q_format = q.question_format
                                if (q_format.title == t("helpers.checkbox") || q_format.title == t("helpers.multi_select_box") ||
                                q_format.title == t("helpers.radio_buttons") || q_format.title == t("helpers.dropdown")) then
                                        details["sections"][s.number]["questions"][q.number]["selections"] = {}
                                        answer.options.each do |o|
                                                details["sections"][s.number]["questions"][q.number]["selections"][o.number] = o.text
                                        end
                                end
                                details["sections"][s.number]["questions"][q.number]["answer_text"] = answer.text
                        end
                end
        end
        return details
end
dmptemplate() click to toggle source
# File app/models/plan.rb, line 36
def dmptemplate
        self.project.try(:dmptemplate) || Dmptemplate.new
end
editable_by(user_id) click to toggle source
# File app/models/plan.rb, line 156
def editable_by(user_id)
        return project.editable_by(user_id)
end
guidance_for_question(question) click to toggle source
# File app/models/plan.rb, line 81
def guidance_for_question(question)
        guidances = {}
        # If project org isn't nil, get guidance by theme from any "non-subset" groups belonging to project org

        unless project.organisation.nil? then
                project.organisation.guidance_groups.each do |group|
                        if !group.optional_subset && (group.dmptemplates.pluck(:id).include?(project.dmptemplate_id) || group.dmptemplates.count == 0) then
                                group.guidances.each do |guidance|
                                        guidance.themes.where("id IN (?)", question.theme_ids).each do |theme|
                                                guidances = self.add_guidance_to_array(guidances, group, theme, guidance)
                                        end
                                end
                        end
                end
        end

        # Get guidance by theme from any guidance groups selected on creation

        project.guidance_groups.each do |group|
                if group.dmptemplates.pluck(:id).include?(project.dmptemplate_id) || group.dmptemplates.count == 0 then
                        group.guidances.each do |guidance|
                                guidance.themes.where("id IN (?)", question.theme_ids).each do |theme|
                                        guidances = self.add_guidance_to_array(guidances, group, theme, guidance)
                                end
                        end
                end 
end
        
        # Get guidance by question where guidance group was selected on creation or if group is organisation default

        question.guidances.each do |guidance|
                guidance.guidance_groups.each do |group|
                        if (group.organisation == project.organisation && !group.optional_subset) || project.guidance_groups.include?(group) then
                                guidances = self.add_guidance_to_array(guidances, group, nil, guidance)
                        end
    end
        end

        return guidances
end
latest_update() click to toggle source
# File app/models/plan.rb, line 321
def latest_update
        if answers.any? then
                last_answered = answers.order("updated_at DESC").first.updated_at
                if last_answered > updated_at then
                        return last_answered
                else
                        return updated_at
                end
        else
                return updated_at
        end
end
lock_all_sections(user_id) click to toggle source
# File app/models/plan.rb, line 279
def lock_all_sections(user_id)
        sections.each do |s|
                lock_section(s.id, user_id, 1800)
        end
end
lock_section(section_id, user_id, release_time = 60) click to toggle source
# File app/models/plan.rb, line 297
def lock_section(section_id, user_id, release_time = 60)
        status = locked(section_id, user_id)
        if ! status["locked"] then
                plan_section = PlanSection.new
                plan_section.plan_id = id
                plan_section.section_id = section_id
                plan_section.release_time = Time.now + release_time.seconds
                plan_section.user_id = user_id
                plan_section.save
        elsif status["current_user"] then
                plan_section = PlanSection.find(status["id"])
                plan_section.release_time = Time.now + release_time.seconds
                plan_section.save
        else
                return false
        end
end
locked(section_id, user_id) click to toggle source
# File app/models/plan.rb, line 260
def locked(section_id, user_id)
        plan_section = plan_sections.where("section_id = ? AND user_id != ? AND release_time > ?", section_id, user_id, Time.now).last
        if plan_section.nil? then
                status = {
                        "locked" => false,
                        "locked_by" => nil,
                        "timestamp" => nil,
                        "id" => nil
                }
        else
                status = {
                        "locked" => true,
                        "locked_by" => plan_section.user.name,
                        "timestamp" => plan_section.updated_at,
                        "id" => plan_section.id
                }
        end
end
readable_by(user_id) click to toggle source
# File app/models/plan.rb, line 160
def readable_by(user_id)
        if project.nil?
                return false
        else
                return project.readable_by(user_id)
        end
end
section_answers(section_id) click to toggle source
# File app/models/plan.rb, line 334
def section_answers(section_id)
        section = Section.find(section_id)
        section_questions = Array.new
        counter = 0
        section.questions.each do |q|
                section_questions[counter] = {}
                section_questions[counter]["id"] = q.id
                #section_questions[counter]["multiple_choice"] = q.multiple_choice

                q_answer = answer(q.id, false)
                if q_answer.nil? then
                        section_questions[counter]["answer_id"] = nil
                        if q.suggested_answers.find_by_organisation_id(project.organisation_id).nil? then
                                section_questions[counter]["answer_text"] = ""
                        else
                                section_questions[counter]["answer_text"] = q.default_value
                        end
                        section_questions[counter]["answer_timestamp"] = nil
                        section_questions[counter]["answer_options"] = Array.new
                else
                        section_questions[counter]["answer_id"] = q_answer.id
                        section_questions[counter]["answer_text"] = q_answer.text
                        section_questions[counter]["answer_timestamp"] = q_answer.created_at
                        section_questions[counter]["answer_options"] = q_answer.options.pluck(:id)
                end
                counter = counter + 1
        end
        return section_questions
end
sections() click to toggle source
# File app/models/plan.rb, line 72
def sections
        unless project.organisation.nil? then
                sections = version.global_sections + project.organisation.all_sections(version_id)
        else
                sections = version.global_sections
        end
        return sections.uniq.sort_by &:number
end
settings(key) click to toggle source

Proxy through to the template settings (or defaults if this plan doesn't have an associated template) if there are no settings stored for this plan. `key` is required by rails-settings, so it's required here, too.

# File app/models/plan.rb, line 29
def settings(key)
        self_settings = self.super_settings(key)
        return self_settings if self_settings.value?

        self.dmptemplate.settings(key)
end
Also aliased as: super_settings
status() click to toggle source
# File app/models/plan.rb, line 172
def status
        status = {
                "num_questions" => 0,
                "num_answers" => 0,
                "sections" => {},
                "questions" => {},
                "space_used" => 0 # percentage of available space in pdf used

        }

        space_used = height_of_text(self.project.title, 2, 2)

        sections.each do |s|
                space_used += height_of_text(s.title, 1, 1)
                section_questions = 0
                section_answers = 0
                status["sections"][s.id] = {}
                status["sections"][s.id]["questions"] = Array.new
                s.questions.each do |q|
                        status["num_questions"] += 1
                        section_questions += 1
                        status["sections"][s.id]["questions"] << q.id
                        status["questions"][q.id] = {}
                        answer = answer(q.id, false)

                        space_used += height_of_text(q.text) unless q.text == s.title
                        space_used += height_of_text(answer.try(:text) || I18n.t('helpers.plan.export.pdf.question_not_answered'))

                        if ! answer.nil? then
                                status["questions"][q.id] = {
                                        "answer_id" => answer.id,
                                        "answer_created_at" => answer.created_at.to_i,
                                        "answer_text" => answer.text,
                                        "answer_option_ids" => answer.option_ids,
                                        "answered_by" => answer.user.name
                                }
            q_format = q.question_format
                                status["num_answers"] += 1 if (q_format.title == I18n.t("helpers.checkbox") || q_format.title == I18n.t("helpers.multi_select_box") ||
                                q_format.title == I18n.t("helpers.radio_buttons") || q_format.title == I18n.t("helpers.dropdown")) || answer.text.present?
                                section_answers += 1
                                #TODO: include selected options in space estimate

                        else
                                status["questions"][q.id] = {
                                        "answer_id" => nil,
                                        "answer_created_at" => nil,
                                        "answer_text" => nil,
                                        "answer_option_ids" => nil,
                                        "answered_by" => nil
                                }
                        end
                        status["sections"][s.id]["num_questions"] = section_questions
                        status["sections"][s.id]["num_answers"] = section_answers
                end
        end

        status['space_used'] = estimate_space_used(space_used)
        return status
end
super_settings(key) click to toggle source
Alias for: settings
title() click to toggle source
# File app/models/plan.rb, line 40
def title
        logger.debug "Title in settings: #{self.settings(:export).title}"
        if self.settings(:export).title == ""
    if !self.version.nil? && !self.version.phase.nil? && !self.version.phase.title? then
        return self.version.phase.title
    else
        return "DMP title"
                end
        else
                return self.settings(:export).title
        end
end
unlock_all_sections(user_id) click to toggle source
# File app/models/plan.rb, line 285
def unlock_all_sections(user_id)
        plan_sections.where(:user_id => user_id).order("created_at DESC").each do |lock|
                lock.delete
        end
end
unlock_section(section_id, user_id) click to toggle source
# File app/models/plan.rb, line 315
def unlock_section(section_id, user_id)
        plan_sections.where(:section_id => section_id, :user_id => user_id).order("created_at DESC").each do |lock|
                lock.delete
        end
end
warning(option_id) click to toggle source
# File app/models/plan.rb, line 148
def warning(option_id)
        if project.organisation.nil?
                return nil
        else
                return project.organisation.warning(option_id)
        end
end

Private Instance Methods

estimate_space_used(used_height) click to toggle source

Based on the height of the text gathered so far and the available vertical space of the pdf, estimate a percentage of how much space has been used. This is highly dependent on the layout in the pdf. A more accurate approach would be to render the pdf and check how much space had been used, but that could be very slow. NOTE: This is only an estimate, rounded up to the nearest 5%; it is intended for guidance when editing plan data, not to be 100% accurate.

# File app/models/plan.rb, line 372
def estimate_space_used(used_height)
        @formatting ||= self.settings(:export).formatting

        return 0 unless @formatting[:font_size] > 0

        margin_height    = @formatting[:margin][:top].to_i + @formatting[:margin][:bottom].to_i
        page_height      = A4_PAGE_HEIGHT - margin_height # 297mm for A4 portrait

        available_height = page_height * self.dmptemplate.settings(:export).max_pages

        percentage = (used_height / available_height) * 100
        (percentage / ROUNDING).ceil * ROUNDING # round up to nearest five

end
height_of_text(text, font_size_inc = 0, vertical_margin = 0) click to toggle source

Take a guess at the vertical height (in mm) of the given text based on the font-size and left/right margins stored in the plan's settings. This assumes a fixed-width for each glyph, which is obviously incorrect for the font-face choices available; the idea is that they'll hopefully average out to that in the long-run. Allows for hinting different font sizes (offset from base via font_size_inc) and vertical margins (i.e. for heading text)

# File app/models/plan.rb, line 392
def height_of_text(text, font_size_inc = 0, vertical_margin = 0)
        @formatting     ||= self.settings(:export).formatting
        @margin_width   ||= @formatting[:margin][:left].to_i + @formatting[:margin][:right].to_i
        @base_font_size ||= @formatting[:font_size]

        return 0 unless @base_font_size > 0

        font_height = FONT_HEIGHT_CONVERSION_FACTOR * (@base_font_size + font_size_inc)
        font_width  = font_height * FONT_WIDTH_HEIGHT_RATIO # Assume glyph width averages at 2/5s the height

        leading     = font_height / 2

        chars_in_line = (A4_PAGE_WIDTH - @margin_width) / font_width # 210mm for A4 portrait

        num_lines = (text.length / chars_in_line).ceil

        (num_lines * font_height) + vertical_margin + leading
end