Browse Source

add mediation values

main
Christoph Marzell 6 days ago
parent
commit
f41b8a8ee8
  1. 418
      app/models/entry.rb
  2. 406
      app/models/user.rb

418
app/models/entry.rb

@ -1,104 +1,281 @@
require "csv"
class Entry < ApplicationRecord class Entry < ApplicationRecord
belongs_to :user
require 'csv'
# ------------------------------------------------------------
# Konstanten
# ------------------------------------------------------------
belongs_to :user
PRAKTIKUMSTYPEN = %w[
propädeutikum
fachspezifikum
mediation
].freeze
ENTRY_ARTEN = [
# Allgemein
"Praktikum",
"Selbsterfahrung",
"Supervision",
"Fortbildung",
"Semesterkosten",
# Mediation
"Präsenzmodul",
"Peergruppenarbeit",
"Fallarbeit",
"Praxisseminare",
"Literatur- und Selbststudium",
# Fachspezifikum
"Gruppenselbsterfahrung",
"Theorie/Methodikseminare",
"Peergruppensupervision",
"Einzel-/Kleingruppensupervision",
"Eigenständige Tätigkeit"
].freeze
LEGACY_ENTRY_ART_ALIASES = {
"Peergroup" => "Peergruppenarbeit",
"Peer-Gruppe" => "Peergruppenarbeit"
}.freeze
# ------------------------------------------------------------
# Validierungen
# ------------------------------------------------------------
validates :date,
presence: true
validates :hours,
numericality: {
only_integer: true,
greater_than_or_equal_to: 0
},
unless: -> { start_time.present? && end_time.nil? }
validates :minutes,
numericality: {
only_integer: true,
greater_than_or_equal_to: 0,
less_than: 60
},
unless: -> { start_time.present? && end_time.nil? }
validates :praktikums_typ,
inclusion: { in: PRAKTIKUMSTYPEN }
validates :date, presence: true
validates :hours, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, unless: -> { start_time.present? && end_time.nil? }
validates :minutes, numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than: 60 }, unless: -> { start_time.present? && end_time.nil? }
validates :entry_art,
inclusion: { in: ENTRY_ARTEN }
validates :distance_km,
numericality: {
only_integer: true,
greater_than_or_equal_to: 0
},
allow_nil: true
# ------------------------------------------------------------
# Callbacks
# ------------------------------------------------------------
before_validation :normalize_legacy_entry_art
before_save :normalize_time before_save :normalize_time
PRAKTIKUMSTYPEN = %w[propädeutikum fachspezifikum mediation]
ENTRY_ARTEN = [
"Praktikum",
"Selbsterfahrung",
"Supervision",
"Fortbildung",
"Semesterkosten",
# Neue differenzierte Einträge (nur sichtbar im fachspezifikum-Kontext)
"Gruppenselbsterfahrung", # 64h
"Theorie/Methodikseminare", # 216h
"Peergruppensupervision", # 168h
"Einzel-/Kleingruppensupervision", # 40h
"Eigenständige Tätigkeit"
]
validates :praktikums_typ, inclusion: { in: PRAKTIKUMSTYPEN }
validates :entry_art, inclusion: { in: ENTRY_ARTEN }
validates :distance_km, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
after_initialize :set_default_date, if: :new_record?
after_initialize :set_default_date,
if: :new_record?
# ------------------------------------------------------------
# Scopes
# ------------------------------------------------------------
scope :propädeutikum, -> {
where(praktikums_typ: "propädeutikum")
}
scope :fachspezifikum, -> {
where(praktikums_typ: "fachspezifikum")
}
scope :mediation, -> {
where(praktikums_typ: "mediation")
}
scope :peergruppenarbeit, -> {
where(entry_art: "Peergruppenarbeit")
}
scope :fallarbeit, -> {
where(entry_art: "Fallarbeit")
}
scope :praxisseminare, -> {
where(entry_art: "Praxisseminare")
}
scope :praesenzmodule, -> {
where(entry_art: "Präsenzmodul")
}
scope :literatur_und_selbststudium, -> {
where(entry_art: "Literatur- und Selbststudium")
}
# ------------------------------------------------------------
# Defaults
# ------------------------------------------------------------
def set_default_date def set_default_date
self.date ||= Date.today
self.date ||= Date.current
end
# ------------------------------------------------------------
# Zeit
# ------------------------------------------------------------
def total_minutes
hours.to_i * 60 + minutes.to_i
end
def total_minutes_including_break
return nil unless start_time.present? && end_time.present?
worked_minutes =
((end_time - start_time) / 60).to_i
worked_minutes -= lunch_break_minutes.to_i
[worked_minutes, 0].max
end end
def hours_and_minutes
mins = total_minutes_including_break
return [hours.to_i, minutes.to_i] if mins.nil?
[
mins / 60,
mins % 60
]
end
# ------------------------------------------------------------
# Datum
# ------------------------------------------------------------
def jahr
date&.year
end
# ------------------------------------------------------------
# Kilometer
# ------------------------------------------------------------
def kilometer_pauschale def kilometer_pauschale
return 0 unless distance_km.present? return 0 unless distance_km.present?
distance_km * 0.42
year = date&.year
rate =
MileageRate.find_by(year: year)&.rate_per_km ||
0.42
(distance_km.to_d * rate.to_d).round(2)
end end
def self.total_kilometer_cost_by_year(user) def self.total_kilometer_cost_by_year(user)
yearly_distances = where(user: user)
.where.not(distance_km: nil)
.group(Arel.sql("EXTRACT(YEAR FROM date)"))
.order(Arel.sql("EXTRACT(YEAR FROM date) DESC"))
.sum(:distance_km)
yearly_distances =
where(user: user)
.where.not(distance_km: nil)
.group(Arel.sql("EXTRACT(YEAR FROM date)"))
.order(Arel.sql("EXTRACT(YEAR FROM date) DESC"))
.sum(:distance_km)
yearly_distances.each_with_object({}) do |(year, km_sum), result| yearly_distances.each_with_object({}) do |(year, km_sum), result|
rate = MileageRate.find_by(year: year.to_i)&.rate_per_km || 0.42
result[year.to_i] = (km_sum * rate).round(2)
rate =
MileageRate.find_by(year: year.to_i)&.rate_per_km ||
0.42
result[year.to_i] =
(km_sum.to_d * rate.to_d).round(2)
end end
end end
# ------------------------------------------------------------
# Kosten
# ------------------------------------------------------------
def self.total_fortbildungskosten_by_year(user) def self.total_fortbildungskosten_by_year(user)
where(user: user) where(user: user)
.where.not(kosten: nil) .where.not(kosten: nil)
.where( .where(
Entry.arel_table[:entry_art].eq('Fortbildung')
.or(Entry.arel_table[:zaehlt_als_fortbildung].eq(true))
Entry.arel_table[:entry_art]
.eq("Fortbildung")
.or(
Entry.arel_table[:zaehlt_als_fortbildung]
.eq(true)
)
) )
.group(Arel.sql("DATE_PART('year', date)")) .group(Arel.sql("DATE_PART('year', date)"))
.order(Arel.sql("DATE_PART('year', date) DESC")) .order(Arel.sql("DATE_PART('year', date) DESC"))
.pluck(Arel.sql("DATE_PART('year', date)::int"), Arel.sql("SUM(kosten)"))
.to_h
end
def self.total_selbsterfahrungskosten_by_year(user)
where(user: user, entry_art: 'Selbsterfahrung')
.where.not(kosten: nil)
.group(Arel.sql("DATE_PART('year', date)"))
.order(Arel.sql("DATE_PART('year', date) DESC"))
.pluck(Arel.sql("DATE_PART('year', date)::int"), Arel.sql("SUM(kosten)"))
.pluck(
Arel.sql("DATE_PART('year', date)::int"),
Arel.sql("SUM(kosten)")
)
.to_h .to_h
end end
def self.total_selbsterfahrungskosten_by_year(user) def self.total_selbsterfahrungskosten_by_year(user)
where(user: user, entry_art: 'Selbsterfahrung')
where(
user: user,
entry_art: [
"Selbsterfahrung",
"Gruppenselbsterfahrung"
]
)
.where.not(kosten: nil) .where.not(kosten: nil)
.group(Arel.sql("DATE_PART('year', date)")) .group(Arel.sql("DATE_PART('year', date)"))
.order(Arel.sql("DATE_PART('year', date) DESC")) .order(Arel.sql("DATE_PART('year', date) DESC"))
.pluck(Arel.sql("DATE_PART('year', date)::int"), Arel.sql("SUM(kosten)"))
.pluck(
Arel.sql("DATE_PART('year', date)::int"),
Arel.sql("SUM(kosten)")
)
.to_h .to_h
end end
def self.total_supervision_by_year(user) def self.total_supervision_by_year(user)
where(user: user, entry_art: 'Supervision')
where(
user: user,
entry_art: [
"Supervision",
"Peergruppensupervision",
"Einzel-/Kleingruppensupervision"
]
)
.where.not(kosten: nil) .where.not(kosten: nil)
.group(Arel.sql("DATE_PART('year', date)")) .group(Arel.sql("DATE_PART('year', date)"))
.order(Arel.sql("DATE_PART('year', date) DESC")) .order(Arel.sql("DATE_PART('year', date) DESC"))
.pluck(Arel.sql("DATE_PART('year', date)::int"), Arel.sql("SUM(kosten)"))
.pluck(
Arel.sql("DATE_PART('year', date)::int"),
Arel.sql("SUM(kosten)")
)
.to_h .to_h
end end
# Der Name bleibt aus Kompatibilitätsgründen erhalten.
#
# Die Methode summiert weiterhin alle eingetragenen Kosten
# eines Jahres, so wie dein bisheriger Code.
def self.total_semesterkosten_by_year(user) def self.total_semesterkosten_by_year(user)
where(user: user) where(user: user)
.where.not(kosten: nil) .where.not(kosten: nil)
.group(Arel.sql("DATE_PART('year', date)")) .group(Arel.sql("DATE_PART('year', date)"))
.order(Arel.sql("DATE_PART('year', date) DESC")) .order(Arel.sql("DATE_PART('year', date) DESC"))
.pluck(Arel.sql("DATE_PART('year', date)::int"), Arel.sql("SUM(kosten)"))
.pluck(
Arel.sql("DATE_PART('year', date)::int"),
Arel.sql("SUM(kosten)")
)
.to_h .to_h
end end
@ -106,27 +283,86 @@ class Entry < ApplicationRecord
semester_by_year = total_semesterkosten_by_year(user) semester_by_year = total_semesterkosten_by_year(user)
km_by_year = total_kilometer_cost_by_year(user) km_by_year = total_kilometer_cost_by_year(user)
years = (semester_by_year.keys + km_by_year.keys).uniq.sort.reverse
years =
(
semester_by_year.keys +
km_by_year.keys
)
.uniq
.sort
.reverse
years.each_with_object({}) do |year, out| years.each_with_object({}) do |year, out|
semester = (semester_by_year[year] || 0).to_d
km = (km_by_year[year] || 0).to_d
out[year] = (semester + km).round(2)
semester =
(semester_by_year[year] || 0).to_d
km =
(km_by_year[year] || 0).to_d
out[year] =
(semester + km).round(2)
end end
end end
# ------------------------------------------------------------
# Mediation
# ------------------------------------------------------------
def jahr
date.year
def mediation?
praktikums_typ == "mediation"
end end
def total_minutes
hours * 60 + minutes
def praesenzmodul?
mediation? &&
entry_art == "Präsenzmodul"
end
def mediation_supervision?
mediation? &&
entry_art == "Supervision"
end
def peergruppenarbeit?
mediation? &&
entry_art == "Peergruppenarbeit"
end
def fallarbeit?
mediation? &&
entry_art == "Fallarbeit"
end
def praxisseminar?
mediation? &&
entry_art == "Praxisseminare"
end
def literatur_und_selbststudium?
mediation? &&
entry_art == "Literatur- und Selbststudium"
end end
# ------------------------------------------------------------
# CSV
# ------------------------------------------------------------
def self.to_csv def self.to_csv
CSV.generate(headers: true, col_sep: ";") do |csv|
csv << %w[Datum Stunden Minuten Typ Art Kilometer User]
CSV.generate(
headers: true,
col_sep: ";"
) do |csv|
csv << [
"Datum",
"Stunden",
"Minuten",
"Typ",
"Art",
"Kilometer",
"Beschreibung",
"Kosten",
"User"
]
all.each do |entry| all.each do |entry|
csv << [ csv << [
@ -144,34 +380,52 @@ class Entry < ApplicationRecord
end end
end end
def total_minutes_including_break
return nil unless start_time && end_time
minutes = ((end_time - start_time) / 60).to_i
minutes -= lunch_break_minutes.to_i
minutes = 0 if minutes < 0
minutes
end
private
def hours_and_minutes
mins = total_minutes_including_break
return [0, 0] unless mins
[mins / 60, mins % 60]
# ------------------------------------------------------------
# Alte Bezeichnungen automatisch migrieren
# ------------------------------------------------------------
def normalize_legacy_entry_art
return if entry_art.blank?
self.entry_art =
LEGACY_ENTRY_ART_ALIASES.fetch(
entry_art,
entry_art
)
end end
private
# ------------------------------------------------------------
# Zeit normalisieren
# ------------------------------------------------------------
def normalize_time def normalize_time
if (hours.to_i + minutes.to_i) == 0 && start_time.present? && end_time.present?
total_minutes_worked = total_minutes_including_break
if total_minutes_worked
self.hours = total_minutes_worked / 60
self.minutes = total_minutes_worked % 60
if hours.to_i.zero? &&
minutes.to_i.zero? &&
start_time.present? &&
end_time.present?
total_worked_minutes =
total_minutes_including_break
if total_worked_minutes
self.hours =
total_worked_minutes / 60
self.minutes =
total_worked_minutes % 60
end end
else else
self.minutes ||= 0
self.hours ||= 0
self.hours += minutes / 60
self.minutes = minutes % 60
total =
hours.to_i * 60 +
minutes.to_i
self.hours =
total / 60
self.minutes =
total % 60
end end
end end
end end

406
app/models/user.rb

@ -1,91 +1,413 @@
class User < ApplicationRecord class User < ApplicationRecord
# Include default devise modules. Others available are: # Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :confirmable
devise :database_authenticatable,
:registerable,
:recoverable,
:rememberable,
:validatable,
:confirmable
has_many :entries, dependent: :destroy has_many :entries, dependent: :destroy
PRAKTIKUMSTYPEN = %w[propädeutikum fachspezifikum mediation]
PRAKTIKUMSTYPEN = %w[
propädeutikum
fachspezifikum
mediation
].freeze
# ------------------------------------------------------------
# Alle möglichen Entry-Arten
# ------------------------------------------------------------
ENTRY_ARTEN = [ ENTRY_ARTEN = [
"Praktikum",
"Selbsterfahrung",
"Supervision",
# Neue differenzierte Einträge (nur sichtbar im fachspezifikum-Kontext)
"Gruppenselbsterfahrung", # 64h
"Theorie/Methodikseminare", # 216h
"Peergruppensupervision", # 168h
"Einzel-/Kleingruppensupervision", # 40h
"Eigenständige Tätigkeit"
]
# Allgemein
"Praktikum",
"Selbsterfahrung",
"Supervision",
"Fortbildung",
"Semesterkosten",
# Mediation
"Präsenzmodul",
"Peergruppenarbeit",
"Fallarbeit",
"Praxisseminare",
"Literatur- und Selbststudium",
# Fachspezifikum
"Gruppenselbsterfahrung",
"Theorie/Methodikseminare",
"Peergruppensupervision",
"Einzel-/Kleingruppensupervision",
"Eigenständige Tätigkeit"
].freeze
# ------------------------------------------------------------
# Arten pro Ausbildung
#
# Kann im Frontend / Controller verwendet werden, damit nicht
# jede Entry-Art bei jedem Ausbildungstyp angezeigt wird.
# ------------------------------------------------------------
ENTRY_ARTEN_BY_TYP = {
"propädeutikum" => [
"Praktikum",
"Selbsterfahrung",
"Supervision",
"Fortbildung",
"Semesterkosten"
],
"fachspezifikum" => [
"Praktikum",
"Selbsterfahrung",
"Supervision",
"Fortbildung",
"Semesterkosten",
"Gruppenselbsterfahrung",
"Theorie/Methodikseminare",
"Peergruppensupervision",
"Einzel-/Kleingruppensupervision",
"Eigenständige Tätigkeit"
],
"mediation" => [
"Präsenzmodul",
"Selbsterfahrung",
"Supervision",
"Peergruppenarbeit",
"Fallarbeit",
"Praxisseminare",
"Literatur- und Selbststudium",
"Fortbildung",
"Semesterkosten"
]
}.freeze
# ------------------------------------------------------------
# Mediation – zusätzliche Anforderungen
# ------------------------------------------------------------
# Vorgabe des konkreten Lehrgangs
MEDIATION_PRAESENZMODULE_TAGE = 19
# ZivMediat-AV:
# Von den insgesamt 26 Supervisionseinheiten müssen mindestens
# 3 Einheiten Einzelsupervision sein.
MEDIATION_EINZELSUPERVISION_MINDESTENS = 3
after_initialize :set_default, if: :new_record? after_initialize :set_default, if: :new_record?
# ------------------------------------------------------------
# Allgemein
# ------------------------------------------------------------
def praepedeutikum_abgeschlossen? def praepedeutikum_abgeschlossen?
praepedeutikum_done praepedeutikum_done
end end
def is_admin? def is_admin?
self.email =="christoph@marzell.net"
email == "christoph@marzell.net"
end end
def self.entry_arten_for(typ)
ENTRY_ARTEN_BY_TYP[typ.to_s] || ENTRY_ARTEN
end
# ------------------------------------------------------------
# Matrizen aktualisieren
# ------------------------------------------------------------
def update_required_matrices! def update_required_matrices!
required = (required_hours_matrix || {}).deep_stringify_keys
weekly = (weekly_target_matrix || {}).deep_stringify_keys
migrate_legacy_peer_group_key!(
required,
use_default_when_zero: true
)
migrate_legacy_peer_group_key!(
weekly,
use_default_when_zero: false
)
PRAKTIKUMSTYPEN.each do |typ| PRAKTIKUMSTYPEN.each do |typ|
required_hours_matrix[typ] ||= {}
weekly_target_matrix[typ] ||= {}
required[typ] ||= {}
weekly[typ] ||= {}
ENTRY_ARTEN.each do |art| ENTRY_ARTEN.each do |art|
required_hours_matrix[typ][art] ||= default_hours_for(typ, art)
weekly_target_matrix[typ][art] ||= default_weekly_target_for(typ, art)
unless required[typ].key?(art)
required[typ][art] = default_hours_for(typ, art)
end
unless weekly[typ].key?(art)
weekly[typ][art] = default_weekly_target_for(typ, art)
end
end end
end end
self.required_hours_matrix = required
self.weekly_target_matrix = weekly
save! save!
end end
# ------------------------------------------------------------
# Default-Wochenziele
# ------------------------------------------------------------
def default_weekly_target_for(typ, art) def default_weekly_target_for(typ, art)
typ = typ.to_s
art = canonical_entry_art(art)
case [typ, art] case [typ, art]
when ["propädeutikum", "Praktikum"] then 12
when ["fachspezifikum", "Praktikum"] then 10
when ["propädeutikum", "Praktikum"]
12
when ["fachspezifikum", "Praktikum"]
10
when ["propädeutikum", "Selbsterfahrung"], when ["propädeutikum", "Selbsterfahrung"],
["fachspezifikum", "Selbsterfahrung"], ["fachspezifikum", "Selbsterfahrung"],
["fachspezifikum", "Gruppenselbsterfahrung"] then 0.25
["fachspezifikum", "Gruppenselbsterfahrung"]
0.25
when ["propädeutikum", "Supervision"], when ["propädeutikum", "Supervision"],
["fachspezifikum", "Supervision"], ["fachspezifikum", "Supervision"],
["fachspezifikum", "Peergruppensupervision"], ["fachspezifikum", "Peergruppensupervision"],
["fachspezifikum", "Einzel-/Kleingruppensupervision"] then 0.25
when ["fachspezifikum", "Eigenständige Tätigkeit"] then 10
else 0
end
end
["fachspezifikum", "Einzel-/Kleingruppensupervision"]
0.25
when ["fachspezifikum", "Eigenständige Tätigkeit"]
10
def set_default
self.required_hours_matrix = PRAKTIKUMSTYPEN.to_h do |typ|
[typ, ENTRY_ARTEN.to_h { |art| [art, default_hours_for(typ, art)] }]
# Mediation findet typischerweise blockweise statt.
# Deshalb kein künstliches Wochenziel.
when ["mediation", "Präsenzmodul"],
["mediation", "Selbsterfahrung"],
["mediation", "Supervision"],
["mediation", "Peergruppenarbeit"],
["mediation", "Fallarbeit"],
["mediation", "Praxisseminare"],
["mediation", "Literatur- und Selbststudium"]
0
else
0
end end
self.weekly_target_matrix = {
"propädeutikum" => { "Praktikum" => 12, "Selbsterfahrung" => 0.25, "Supervision" => 0.25 },
"fachspezifikum" => { "Praktikum" => 10, "Selbsterfahrung" => 0.25, "Supervision" => 0.25 }
}
end end
# ------------------------------------------------------------
# Default-Sollstunden / Einheiten
# ------------------------------------------------------------
def default_hours_for(typ, art) def default_hours_for(typ, art)
typ = typ.to_s
art = canonical_entry_art(art)
case [typ, art] case [typ, art]
when ["propädeutikum", "Praktikum"] then 480
when ["propädeutikum", "Selbsterfahrung"] then 50
when ["propädeutikum", "Supervision"] then 20
when ["fachspezifikum", "Praktikum"] then 600
when ["fachspezifikum", "Selbsterfahrung"] then 80
when ["fachspezifikum", "Supervision"] then 40
when ["fachspezifikum", "Eigenständige Tätigkeit"] then 600
else 0
# ----------------------------------------------------------
# Propädeutikum
# ----------------------------------------------------------
when ["propädeutikum", "Praktikum"]
480
when ["propädeutikum", "Selbsterfahrung"]
50
when ["propädeutikum", "Supervision"]
20
# ----------------------------------------------------------
# Fachspezifikum
# ----------------------------------------------------------
when ["fachspezifikum", "Praktikum"]
600
when ["fachspezifikum", "Selbsterfahrung"]
80
when ["fachspezifikum", "Supervision"]
40
when ["fachspezifikum", "Gruppenselbsterfahrung"]
64
when ["fachspezifikum", "Theorie/Methodikseminare"]
216
when ["fachspezifikum", "Peergruppensupervision"]
168
when ["fachspezifikum", "Einzel-/Kleingruppensupervision"]
40
when ["fachspezifikum", "Eigenständige Tätigkeit"]
600
# ----------------------------------------------------------
# Mediation
# ----------------------------------------------------------
# Wird in Tagen und nicht in Stunden bewertet.
when ["mediation", "Präsenzmodul"]
0
# ZivMediat-AV Anlage 1
when ["mediation", "Selbsterfahrung"]
40
when ["mediation", "Supervision"]
26
when ["mediation", "Peergruppenarbeit"]
24
when ["mediation", "Fallarbeit"]
17
when ["mediation", "Praxisseminare"]
58
# Zusätzliche Lehrgangsvorgabe
when ["mediation", "Literatur- und Selbststudium"]
150
else
0
end end
end end
# ------------------------------------------------------------
# Sollwert aus persönlicher Matrix
# ------------------------------------------------------------
def required_hours_for(typ, art) def required_hours_for(typ, art)
required_hours_matrix.dig(typ, art) || 0
typ = typ.to_s
art = canonical_entry_art(art)
value = required_hours_matrix&.dig(typ, art)
value.nil? ? default_hours_for(typ, art) : value
end
def weekly_target_for(typ, art)
typ = typ.to_s
art = canonical_entry_art(art)
value = weekly_target_matrix&.dig(typ, art)
value.nil? ? default_weekly_target_for(typ, art) : value
end
# ------------------------------------------------------------
# Mediation – Präsenztage
# ------------------------------------------------------------
def mediation_praesenzmodule_required
MEDIATION_PRAESENZMODULE_TAGE
end end
def mediation_praesenzmodule_completed
entries
.where(
praktikums_typ: "mediation",
entry_art: "Präsenzmodul"
)
.distinct
.count(:date)
end
def mediation_praesenzmodule_remaining
[
mediation_praesenzmodule_required -
mediation_praesenzmodule_completed,
0
].max
end
def mediation_praesenzmodule_completed?
mediation_praesenzmodule_completed >=
mediation_praesenzmodule_required
end
# ------------------------------------------------------------
# Mediation – besondere Vorgaben
# ------------------------------------------------------------
def mediation_einzelsupervision_required
MEDIATION_EINZELSUPERVISION_MINDESTENS
end
private
# ------------------------------------------------------------
# Defaults für neuen User
# ------------------------------------------------------------
def set_default
self.required_hours_matrix ||= PRAKTIKUMSTYPEN.to_h do |typ|
[
typ,
ENTRY_ARTEN.to_h do |art|
[art, default_hours_for(typ, art)]
end
]
end
self.weekly_target_matrix ||= PRAKTIKUMSTYPEN.to_h do |typ|
[
typ,
ENTRY_ARTEN.to_h do |art|
[art, default_weekly_target_for(typ, art)]
end
]
end
end
# ------------------------------------------------------------
# Alte Bezeichnungen normalisieren
# ------------------------------------------------------------
def canonical_entry_art(art)
case art.to_s
when "Peergroup", "Peer-Gruppe"
"Peergruppenarbeit"
else
art.to_s
end
end
def migrate_legacy_peer_group_key!(matrix, use_default_when_zero:)
PRAKTIKUMSTYPEN.each do |typ|
values = matrix[typ]
next unless values.is_a?(Hash)
legacy_value = nil
if values.key?("Peergroup")
legacy_value = values.delete("Peergroup")
end
if values.key?("Peer-Gruppe")
legacy_value ||= values.delete("Peer-Gruppe")
end
next if legacy_value.nil?
next if values.key?("Peergruppenarbeit")
if use_default_when_zero &&
typ == "mediation" &&
legacy_value.to_f.zero?
values["Peergruppenarbeit"] =
default_hours_for(
"mediation",
"Peergruppenarbeit"
)
else
values["Peergruppenarbeit"] = legacy_value
end
end
end
end end
Loading…
Cancel
Save