-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add NAV17 importer for event kinds (#1348)
- Loading branch information
Showing
8 changed files
with
399 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# frozen_string_literal: true | ||
|
||
# Copyright (c) 2024, Schweizer Alpen-Club. This file is part of | ||
# hitobito_sac_cas and licensed under the Affero General Public License version 3 | ||
# or later. See the COPYING file at the top-level directory or at | ||
# https://github.com/hitobito/hitobito_sac_cas | ||
|
||
class SacImports::CsvSource | ||
# Event::Kinds | ||
# !!! DO NOT CHANGE THE ORDER OF THE KEYS !!! | ||
# they must match the order of the columns in the CSV files | ||
Nav17 = Data.define( | ||
:label_de, # Bezeichnung_DE | ||
:label_fr, # Bezeichnung_FR | ||
:label_it, # Bezeichnung_IT | ||
:short_name, # Kurzname | ||
:kind_category, # Kurskategorie | ||
:general_information_de, # Standardbeschreibung_DE | ||
:general_information_fr, # Standardbeschreibung_FR | ||
:general_information_it, # Standardbeschreibung_IT | ||
:application_conditions_de, # Aufnahmebedingungen_DE | ||
:application_conditions_fr, # Aufnahmebedingungen_FR | ||
:application_conditions_it, # Aufnahmebedingungen_IT | ||
:level, # Kursstufe | ||
:cost_center, # Kostenstelle | ||
:cost_unit, # Kostenträger | ||
:course_compensation_categories, # Vergütungskategorien | ||
:minimum_age, # Mindestalter | ||
:maximum_age, # Maximalalter | ||
:minimum_participants, # Minimale_TN_Zahl | ||
:maximum_participants, # Maximale_TN_Zahl | ||
:ideal_class_size, # Ideale_Klassengrösse | ||
:maximum_class_size, # Maximale_Klassengrösse | ||
:training_days, # Ausbildungstage | ||
:season, # Saison | ||
:accommodation, # Unterkunft | ||
:reserve_accommodation, # Unterkunft_reservieren_durch_SAC | ||
:section_may_create, # Von_Sektion_erstellbar | ||
:precondition, # Vorbedingungen | ||
:qualification, # Qualifiziert_für | ||
:prolongation # Verlängert | ||
) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
# frozen_string_literal: true | ||
|
||
# Copyright (c) 2024, Schweizer Alpen-Club. This file is part of | ||
# hitobito_sac_cas and licensed under the Affero General Public License version 3 | ||
# or later. See the COPYING file at the top-level directory or at | ||
# https://github.com/hitobito/hitobito_sac_cas. | ||
|
||
module SacImports | ||
module Events | ||
class KindEntry | ||
LOCALES = [:de, :fr, :it].freeze | ||
ATTRS_TRANSLATED = [:label, :general_information, :application_conditions] | ||
ATTRS_REGULAR = [:short_name, :minimum_age, :maximum_age, :minimum_participants, | ||
:maximum_participants, :ideal_class_size, :maximum_class_size, :training_days, | ||
:season, :accommodation] | ||
ATTRS_BOOLEAN = [:reserve_accommodation, :section_may_create] | ||
ATTRS_BELONGS_TO = [:kind_category, :level, :cost_center, :cost_unit] | ||
|
||
attr_reader :row, :associations, :warnings | ||
|
||
delegate :valid?, :errors, to: :kind | ||
|
||
def initialize(row, associations) | ||
@row = row | ||
@associations = associations | ||
@warnings = [] | ||
build_kind | ||
end | ||
|
||
def import! | ||
kind.save! | ||
end | ||
|
||
def error_messages | ||
errors.full_messages.join(", ") | ||
end | ||
|
||
def kind | ||
@kind ||= Event::Kind.find_or_initialize_by(short_name: row.short_name) | ||
end | ||
|
||
def build_kind | ||
kind.attributes = regular_attrs | ||
kind.attributes = boolean_attrs | ||
kind.attributes = belongs_to_attrs | ||
LOCALES.each do |locale| | ||
kind.attributes = translated_attrs(locale) | ||
end | ||
kind.course_compensation_category_ids = select_course_compensation_category_ids | ||
build_kind_qualification_kinds | ||
normalize_kind | ||
end | ||
|
||
def regular_attrs | ||
ATTRS_REGULAR.each_with_object({}) do |attr, hash| | ||
hash[attr] = value(attr) | ||
end | ||
end | ||
|
||
def translated_attrs(locale) | ||
ATTRS_TRANSLATED.each_with_object({locale: locale}) do |attr, hash| | ||
val = value(:"#{attr}_#{locale}") | ||
hash[attr] = strip_paragraph(val) unless val.nil? | ||
end | ||
end | ||
|
||
def belongs_to_attrs | ||
ATTRS_BELONGS_TO.each_with_object({}) do |attr, hash| | ||
hash[:"#{attr}_id"] = association_id(attr, value(attr)) | ||
end | ||
end | ||
|
||
def association_id(attr, value) | ||
return nil if value.nil? | ||
|
||
associations.fetch(attr.to_s.pluralize.to_sym).fetch(value) do | ||
@warnings << "#{attr} with value #{value} couldn't be found" | ||
nil | ||
end | ||
end | ||
|
||
def boolean_attrs | ||
ATTRS_BOOLEAN.each_with_object({}) do |attr, hash| | ||
hash[attr] = value(attr) == "1" | ||
end | ||
end | ||
|
||
def select_course_compensation_category_ids | ||
row.course_compensation_categories.to_s.split(",").map do |category| | ||
association_id(:course_compensation_category, category.strip) | ||
end.compact.uniq | ||
end | ||
|
||
def build_kind_qualification_kinds | ||
Event::KindQualificationKind::CATEGORIES.each do |category| | ||
row.public_send(category).to_s.split(",").each do |quali_kind| | ||
quali_kind_id = association_id(:qualification_kind, quali_kind.strip) | ||
next unless quali_kind_id | ||
|
||
kind.event_kind_qualification_kinds.find_or_initialize_by( | ||
qualification_kind_id: quali_kind_id, | ||
category: category, | ||
role: "participant" | ||
) | ||
end | ||
end | ||
end | ||
|
||
def normalize_kind | ||
kind.maximum_age = nil if kind.maximum_age&.zero? | ||
end | ||
|
||
def value(attr) | ||
row.public_send(attr) | ||
end | ||
|
||
def strip_paragraph(text) | ||
match = text.match(/\A<p>(.*?)<\/p>\z/m) | ||
if match && text.scan("<p>").count == 1 | ||
match[1] | ||
else | ||
text | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# frozen_string_literal: true | ||
|
||
# Copyright (c) 2024, Schweizer Alpen-Club. This file is part of | ||
# hitobito_sac_cas and licensed under the Affero General Public License version 3 | ||
# or later. See the COPYING file at the top-level directory or at | ||
# https://github.com/hitobito/hitobito_sac_cas. | ||
|
||
module SacImports | ||
class Nav17EventKindsImporter | ||
include LogCounts | ||
|
||
REPORT_HEADERS = [ | ||
:short_name, | ||
:label, | ||
:status, | ||
:errors | ||
] | ||
|
||
def initialize(output: $stdout, import_spec_fixture: false) | ||
@output = output | ||
# spec fixture includes all sections and it's public data | ||
@import_spec_fixture = import_spec_fixture | ||
@source_file = source_file | ||
@csv_report = SacImports::CsvReport.new("nav17-event-kinds", REPORT_HEADERS, output:) | ||
end | ||
|
||
def create | ||
@csv_report.log("The file contains #{@source_file.lines_count} rows.") | ||
progress = Progress.new(@source_file.lines_count, title: "NAV17 Event Kinds") | ||
|
||
log_counts_delta(@csv_report, Event::Kind.unscoped) do | ||
@source_file.rows do |row| | ||
progress.step | ||
process_row(row) | ||
end | ||
end | ||
|
||
@csv_report.finalize | ||
end | ||
|
||
private | ||
|
||
def source_file | ||
if @import_spec_fixture | ||
CsvSource.new(:NAV17, source_dir: spec_fixture_dir) | ||
else | ||
CsvSource.new(:NAV17) | ||
end | ||
end | ||
|
||
def spec_fixture_dir | ||
Pathname.new(HitobitoSacCas::Wagon.root.join("spec", "fixtures", "files", "sac_imports_src")) | ||
end | ||
|
||
def process_row(row) | ||
entry = Events::KindEntry.new(row, associations) | ||
entry.import! if entry.valid? | ||
report_warnings(entry) | ||
report_errors(entry) | ||
end | ||
|
||
def report_errors(entry) | ||
return if entry.errors.blank? | ||
|
||
@output.puts("#{entry.row.label_de} (#{entry.row.short_name}): ❌ #{entry.error_messages}") | ||
@csv_report.add_row( | ||
short_name: entry.row.short_name, | ||
label: entry.row.label_de, | ||
status: "error", | ||
errors: entry.error_messages | ||
) | ||
end | ||
|
||
def report_warnings(entry) | ||
return if entry.warnings.blank? | ||
|
||
@csv_report.add_row( | ||
short_name: entry.row.short_name, | ||
label: entry.row.label_de, | ||
status: "warning", | ||
errors: entry.warnings.join(", ") | ||
) | ||
end | ||
|
||
def associations | ||
@associations ||= { | ||
kind_categories: Event::KindCategory.pluck(:order, :id).to_h.transform_keys(&:to_s), | ||
qualification_kinds: QualificationKind.pluck(:label, :id).to_h, | ||
levels: Event::Level.pluck(:code, :id).to_h.transform_keys(&:to_s), | ||
cost_centers: CostCenter.pluck(:code, :id).to_h, | ||
cost_units: CostUnit.pluck(:code, :id).to_h, | ||
course_compensation_categories: CourseCompensationCategory.pluck(:short_name, :id).to_h | ||
} | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.