Commit a18b1dca authored by Jiri Strojil's avatar Jiri Strojil
Browse files

MR resolved

parents 4195eb9a 7994dbf7
$(() => {
// Only run this on new_semester_process_path
if (Routes.new_semester_process_path() !== window.location.pathname) { return }
$('select.semester-selector').change(({ target }) => {
window.location.href = `${window.location.origin}${Routes.new_semester_process_path({ semester: $(target).val() })}`
})
})
class SemesterProcessVolunteersController < ApplicationController
before_action :prepare_review, only: [:review_semester, :submit_review]
before_action :initialize_feedback, only: [:review_semester, :submit_review]
before_action :set_semester_process_volunteer, only: [:show, :edit, :update]
before_action :set_semester, only: [:index]
include SemesterProcessVolunteerHelper
......@@ -31,6 +33,24 @@ class SemesterProcessVolunteersController < ApplicationController
end
end
def index
authorize SemesterProcessVolunteer
@spvs = SemesterProcessVolunteer.index(Semester.parse(params[:semester])).page(params[:page])
end
def show; end
def edit; end
def update
if @spv.update(semester_process_params)
redirect_to @spv, notice: 'Semester process was successfully updated.'
else
render :edit
end
end
private
def prepare_review
......@@ -48,4 +68,25 @@ class SemesterProcessVolunteersController < ApplicationController
semester_feedback: [:goals, :achievements, :future, :comments, :conversation],
hour: [:hours])
end
def set_semester_process_volunteer
@spv = SemesterProcessVolunteer.find(params[:id])
authorize @spv
@semester_process = @spv.semester_process
@volunteer = @spv.volunteer
end
def set_semester
@semester = Semester.new
if params[:semester]
@selected_semester = Semester.parse(params[:semester])
else
@selected_semester = @semester.previous
params[:semester] = Semester.to_s(@selected_semester)
end
end
def semester_process_volunteer_params
params.require(:semester_process_volunteer).permit(:semester)
end
end
class SemesterProcessesController < ApplicationController
before_action :set_semester_process, only: [:show, :edit, :update, :destroy]
before_action :set_semester_process, only: [:show, :edit, :update]
before_action :set_semester, only: [:new, :create]
def index
authorize SemesterProcess
......@@ -9,18 +10,36 @@ class SemesterProcessesController < ApplicationController
def show; end
def new
@semester_process = SemesterProcess.new
@semester_process = SemesterProcess.new(semester: @selected_semester)
@semester_process.build_semester_volunteers(@volunteers)
authorize @semester_process
if EmailTemplate.half_year_process_email.active.any?
template = EmailTemplate.half_year_process_email.active.first.slice(:subject, :body)
@semester_process.assign_attributes(mail_body_template: template[:body], mail_subject_template: template[:subject])
else
redirect_to new_email_template_path,
notice: 'Sie müssen eine aktive E-Mailvorlage haben,
bevor Sie eine Halbjahres Erinnerung erstellen können.'
end
end
def edit; end
def create
@semester_process = SemesterProcess.new(semester_process_params)
@semester_process = SemesterProcess.new(semester_process_params.slice(:semester))
@semester_process.creator = current_user
authorize @semester_process
@semester_process.assign_attributes(
mail_body_template: semester_process_params[:body],
mail_subject_template: semester_process_params[:subject]
)
@semester_process.build_semester_volunteers(@volunteers, selected_volunteers)
@semester_process.build_volunteers_hours_feedbacks_and_mails
if @semester_process.save
redirect_to @semester_process, notice: 'Semester process was successfully created.'
redirect_to semester_process_volunteers_path, notice: 'Semester process was successfully created.'
else
render :new
end
......@@ -34,14 +53,6 @@ class SemesterProcessesController < ApplicationController
end
end
def destroy
if @semester_process.destroy
redirect_to semester_processes_url, notice: 'Semester process was successfully destroyed.'
else
redirect_to semester_processes_url, notice: 'Failure notice'
end
end
private
def set_semester_process
......@@ -49,7 +60,34 @@ class SemesterProcessesController < ApplicationController
authorize @semester_process
end
def set_semester
@semester = Semester.new
if params[:semester]
@selected_semester = Semester.parse(params[:semester])
else
@selected_semester = @semester.previous
params[:semester] = Semester.to_s(@selected_semester)
end
semester_form_param = Semester.parse(params[:semester_process]&.fetch(:semester))
@volunteers = Volunteer.semester_process_eligible(semester_form_param || @selected_semester)
end
def selected_volunteers
semester_process_params[:semester_process_volunteers_attributes]
.select { |_key, value| value['selected'] == '1' }
.to_h.map { |_key, value| value[:volunteer_id].to_i }
end
def semester_process_params
params.require(:semester_process).permit(:period_start, :period_end)
params.require(:semester_process).permit(
:semester,
:kind,
:subject,
:body,
semester_process_volunteers_attributes: [
:volunteer_id, :selected
]
)
end
end
class EmailTemplate < ApplicationRecord
before_save :ensure_exactly_one_active_per_kind
enum kind: { signup: 0, trial: 1, half_year: 3, termination: 2 }
enum kind: { signup: 0, trial: 1, half_year: 3, termination: 2, half_year_process_email: 4, half_year_process_overdue: 5 }
validates :kind, presence: true
scope :order_by_active, -> { order(active: :desc) }
......@@ -33,7 +33,9 @@ class EmailTemplate < ApplicationRecord
assignment: [:Anrede, :Name, :EinsatzTitel, :FeedbackLink],
trial: ReminderMailing::TEMPLATE_VARNAMES,
half_year: ReminderMailing::TEMPLATE_VARNAMES,
termination: ReminderMailing::TEMPLATE_VARNAMES
termination: ReminderMailing::TEMPLATE_VARNAMES,
half_year_process_email: ReminderMailing::TEMPLATE_VARNAMES + [:Semester],
half_year_process_overdue: ReminderMailing::TEMPLATE_VARNAMES + [:Semester]
}
end
......
......@@ -25,7 +25,7 @@ class ReminderMailing < ApplicationRecord
source_type: 'GroupAssignment'
has_many :process_submitters, through: :reminder_mailing_volunteers, source: :process_submitted_by
enum kind: { half_year: 0, trial_period: 1, termination: 2 }
enum kind: { half_year: 0, trial_period: 1, termination: 2, half_year_process_email: 3, half_year_process_overdue: 4 }
ransacker :kind, formatter: ->(value) { kinds[value] }
validates :subject, presence: true
......
......@@ -24,16 +24,44 @@ class SemesterProcess < ApplicationRecord
semester_process_mails.where(kind: 'reminder')
end
def mail?
self.mail_subject_template && self.mail_body_template
end
def reminder?
self.reminder_mail_subject_template && self.reminder_mail_body_template
end
def kind
if self.mail?
return :mail
elsif self.reminder?
return :reminder
end
end
def subject
self.mail_subject_template || self.reminder_mail_subject_template
end
def body
self.mail_body_template || self.reminder_mail_body_template
end
# will only return an array, not a AD-result
delegate :missions, to: :semester_process_volunteers
# creates semester date range from string '[year],[semester_number]' e.g. '2018,2'
def semester=(semester)
if semester.is_a?(String)
super(Semester.new(*semester.split(',').map(&:to_i)).current)
else
super(semester)
end
def semester=(set_semester)
set_semester = Semester.parse(set_semester) if set_semester.is_a?(String)
# for very strange reason the end of the range is shifted one day after save
# possibly a bug in Active Directory
super(set_semester.begin..set_semester.end.advance(days: -1))
end
def semester_t(short: true)
Semester.i18n_t(semester, short: short)
end
def build_semester_volunteers(volunteers, selected = nil)
......@@ -46,6 +74,6 @@ class SemesterProcess < ApplicationRecord
end
def build_volunteers_hours_feedbacks_and_mails
semester_process_volunteers.map(&:build_hours_feedbacks_and_mails)
semester_process_volunteers.map(&:build_hours_and_mails)
end
end
......@@ -6,4 +6,73 @@ class SemesterProcessMail < ApplicationRecord
scope :mail, -> { where(kind: 'mail') }
scope :reminder, -> { where(kind: 'reminder') }
delegate :volunteer, to: :semester_process_volunteer
delegate :semester_process, to: :semester_process_volunteer
def self.template_varnames
{
mail: EmailTemplate::template_varnames[:half_year_process_email],
reminder: EmailTemplate::template_varnames[:half_year_process_overdue]
}
end
def process_template
{
subject: replace_ruby_template(self.subject),
body: replace_ruby_template(self.body)
}
end
def replace_ruby_template(template)
template % template_variables
end
def template_variables
template_variables = SemesterProcessMail::template_varnames[self.kind.to_sym].map do |varname|
[varname, send(varname.to_s.underscore)]
end.to_h
template_variables.default = ''
template_variables
end
def anrede
I18n.t("salutation.#{volunteer.salutation}")
end
def name
volunteer.contact.natural_name
end
def semester
"#{ I18n.l(semester_process.semester.begin)} - #{I18n.l(semester_process.semester.end)}"
end
def einsatz_start
#I18n.l(reminder_mailable.period_start) if reminder_mailable.period_start
''
end
def einsatz
''
end
def email_absender
"[#{reminder_mailing_creator_name}](mailto:"\
"#{semester_process.creator.email})"
end
def reminder_mailing_creator_name
semester_process.creator.profile&.contact&.natural_name ||
semester_process.creator.email
end
def feedback_link
"[Halbjahres-Rapport erstellen](#{feedback_url})"
end
def feedback_url(options = {})
''
end
end
......@@ -4,6 +4,7 @@ class SemesterProcessVolunteer < ApplicationRecord
belongs_to :volunteer
belongs_to :semester_process
delegate :semester, to: :semester_process
delegate :semester_t, to: :semester_process
delegate :creator, to: :semester_process
belongs_to :responsible, -> { with_deleted }, class_name: 'User',
......@@ -30,6 +31,19 @@ class SemesterProcessVolunteer < ApplicationRecord
validates_associated :hours, :semester_feedbacks, :volunteer
scope :index_joins, lambda {
joins(:semester_process).joins(volunteer: [:contact]).joins(:semester_process_volunteer_missions)
}
scope :index, lambda { |semester = nil|
if semester
index_joins.where('semester_processes.semester && daterange(?,?)', semester.begin, semester.end)
else
index_joins
end
}
# will only return an array, not a AD-result
def missions
semester_process_volunteer_missions.map(&:mission)
......@@ -44,11 +58,12 @@ class SemesterProcessVolunteer < ApplicationRecord
end
end
def build_hours_feedbacks_and_mails
def build_hours_and_mails
missions.each do |mission|
hours << mission.hours.date_between_inclusion(:meeting_date, semester.begin, semester.end)
semester_feedbacks << SemesterFeedback.new(mission: mission, volunteer: mission.volunteer)
end
semester_process_mails << SemesterProcessMail.new(kind: :mail, sent_by: creator)
semester_process_mails << SemesterProcessMail.new(kind: :mail, sent_by: creator,
subject: semester_process.mail_subject_template,
body: semester_process.mail_body_template)
end
end
......@@ -2,6 +2,7 @@ class SemesterProcessPolicy < ApplicationPolicy
class Scope < ApplicationScope
def resolve
return all if superadmin?
none
end
end
......@@ -13,5 +14,4 @@ class SemesterProcessPolicy < ApplicationPolicy
alias_method :edit?, :superadmin?
alias_method :create?, :superadmin?
alias_method :update?, :superadmin?
alias_method :destroy?, :superadmin?
end
class Semester
attr_reader :context
MONTH_NUMBER_MAP = {
12 => 1,
1 => 1,
2 => 1,
3 => 1,
4 => 1,
5 => 1,
6 => 2,
7 => 2,
8 => 2,
9 => 2,
10 => 2,
11 => 2
}.freeze
class << self
def parse(string)
return unless string
year, number = string.split(',').map(&:to_i)
year -= 1 if number == 1
start_date = Time.zone.local(year, (18 - number * 6), 1)
semester_range_from_start(start_date)
end
def to_s(date = nil)
date = to_process_date(date)
"#{year(date)},#{number(date)}"
end
def number(date = nil)
date = to_process_date(date)
MONTH_NUMBER_MAP[date.month]
end
def year(date = nil)
date = to_process_date(date)
date.month == 12 ? date.year + 1 : date.year
end
def to_process_date(date)
if date.blank?
Time.zone.now
elsif date.is_a?(Range)
date.end
else
date
end
end
def i18n_t(semester, short: true)
if short
I18n.t(:semester_short, number: number(semester), year: year(semester))
else
I18n.t(:semester_long, number: number(semester), year: year(semester), begin: I18n.l(semester.begin.to_date),
end: I18n.l(semester.end.to_date))
end
end
def semester_start_time(date_time)
date_time = date_time.to_date
if (6..11).cover?(date_time.month)
Time.zone.local(date_time.year, 6, 1)
elsif date_time.month == 12
Time.zone.local(date_time.year, 12, 1)
else
Time.zone.local(date_time.year - 1, 12, 1)
end
end
def semester_range_from_start(date_time)
date_time = date_time.to_date
date_time..date_time.advance(months: 5).end_of_month
end
end
# params:
# context - default is now, but optionally any other point in time
def initialize(year = nil, semester_number = nil)
@context = if year && semester_number == 2
Time.zone.local(year, 6, 1)
elsif year && semester_number == 1
Time.zone.local(year - 1, 12, 1)
else
# year - integer of year
# semester_number - required if year integer
def initialize(year = nil, number = 1)
@context = if year.nil?
Time.zone.now
else
year -= 1 if number == 1
Time.zone.local(year, (18 - number * 6), 1)
end
end
def number(date_time = nil)
if (6..11).cover?(date_time&.month || @context.month)
2
else
1
end
def year(date = nil)
Semester.year(date || @context)
end
def year_number(count: 1, direction: :previous, set_semester: nil)
string_semester = set_semester || public_send(direction, count)
"#{string_semester.end.year},#{number(string_semester.end)}"
def to_s(date = nil)
Semester.to_s(date || @context)
end
def number(date = nil)
Semester.number(date || @context)
end
# running semester where in now
def current
@current ||= semester_range_from_start(semester_start_time(@context))
@current ||= Semester.semester_range_from_start(
Semester.semester_start_time(@context)
)
end
# params:
......@@ -34,24 +113,32 @@ class Semester
def previous(count = 1)
return current if count == 0
current.begin.advance(months: -(6 * count))..current.end.advance(months: -(6 * count))
Semester.semester_range_from_start(current.begin.advance(months: -6 * count))
end
def previous_s(count = 1)
to_s(previous(count))
end
# params:
# count - shift of semesters - only positive integers
def next(count = 1)
def next_semester(count = 1)
return current if count == 0
current.begin.advance(months: 6 * count)..current.end.advance(months: 6 * count)
Semester.semester_range_from_start(current.begin.advance(months: 6 * count))
end
def next_s(count = 1)
to_s(next_semester(count))
end
# list of semester datetime ranges
#
# params:
# count - amount of semesters
# direction - [:next || :previous]
# direction - [:next_semester || :previous]
# with_current - include current semester as first in array
def list(count = 3, direction = :previous, with_current: true)
def list(count = 3, direction: :previous, with_current: true)
list = (1..count).to_a.map do |step|
public_send(direction, step)
end
......@@ -62,30 +149,9 @@ class Semester
end
end
def collection(count = 3, direction = :previous, with_current: true)
list(count, direction, with_current: with_current).map do |semester|
semester_number = number(semester.end)
semester_year = semester.end.year
[
"#{semester_number}. Semester #{semester_year} (#{I18n.l(semester.begin.to_date)} - #{I18n.l(semester.end.to_date)})",
year_number(set_semester: semester)
]
def collection(count = 3, direction: :previous, with_current: true)
list(count, direction: direction, with_current: with_current).map do |semester|
[Semester.i18n_t(semester, short: false), to_s(semester)]
end
end
private
def semester_start_time(date_time)
if (6..11).cover?(date_time.month)
Time.zone.local(date_time.year, 6, 1)
elsif date_time.month == 12
Time.zone.local(date_time.year, 12, 1)
else
Time.zone.local(date_time.year - 1, 12, 1)
end
end
def semester_range_from_start(date_time)
date_time..date_time.advance(months: 5).end_of_month
end
end
......@@ -22,7 +22,7 @@ nav.navbar.navbar-top.hidden-print
- if policy(GroupOffer).index?
li= link_to 'Gruppenangebote', group_offers_path
- if policy(SemesterProcess).index?
li= link_to 'Semester Prozesse', semester_processes_path
li= link_to 'Semester Prozesse', semester_process_volunteers_path
- if policy(ReminderMailing).index?
li = link_to 'Erinnerungs-Emails', reminder_mailings_path
- if policy(Event).index?
......
......@@ -9,4 +9,4 @@ fieldset
'Text
abbr title="notwendig"
' *
= f.input :body, label_html: { class: 'sr-only' }
= f.input :body, as: :text, label_html: { class: 'sr-only' }, input_html: { class: 'text-bg-body' }
......@@ -13,6 +13,4 @@ nav.navbar.section-navigation
- (0..11).to_a.reverse.map { |count| Time.zone.today.months_ago(count).be