-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add export to education view #28
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# encoding: utf-8 | ||
|
||
# Copyright (c) 2012-2023, Pfadibewegung Schweiz. This file is part of | ||
# hitobito_youth 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_youth. | ||
|
||
module Export::Tabular::People | ||
class PeopleEducationList < Export::Tabular::Base | ||
|
||
self.model_class = ::Person | ||
self.row_class = PeopleEducationRow | ||
|
||
def attribute_labels | ||
{ first_name: human_attribute(:first_name), | ||
last_name: human_attribute(:last_name), | ||
nickname: human_attribute(:nickname), | ||
email: human_attribute(:email), | ||
birthday: human_attribute(:birthday), | ||
qualifications: Qualification.model_name.human(count: 2), | ||
event_participations: Event::Application.model_name.human(count: 2) } | ||
end | ||
|
||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# encoding: utf-8 | ||
|
||
# Copyright (c) 2012-2023, Pfadibewegung Schweiz. This file is part of | ||
# hitobito_youth 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_youth. | ||
|
||
module Export::Tabular::People | ||
class PeopleEducationRow < Export::Tabular::Row | ||
|
||
def event_participations | ||
today = Time.zone.today | ||
entry.event_participations. | ||
select do |p| | ||
p.event.supports_applications? && | ||
p.event.dates.sort_by(&:start_at).last.start_at >= today | ||
end. | ||
collect do |p| | ||
p.event.name | ||
end. | ||
join(', ') | ||
end | ||
|
||
def qualifications | ||
entry.qualifications. | ||
select(&:reactivateable?). | ||
sort_by(&:start_at). | ||
reverse. | ||
uniq(&:qualification_kind). | ||
collect do |q| | ||
"#{q.qualification_kind.label}".strip | ||
end. | ||
join(', ') | ||
end | ||
|
||
end | ||
end |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,6 @@ | ||||||
-# Copyright (c) 2023 , Pfadibewegung Schweiz. This file is part of | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
-# hitobito_youth 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_youth. | ||||||
|
||||||
= action_button(t('.export_people'), educations_path({format: :csv}.merge(params.to_unsafe_h)), :download) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You added the capability for CSV and XLSX to the controller, but only expose CSV here. Wouldn't XLSX be more valuable? Maybe in the future we might want to color some of the text in the spreadsheet, depending on when some qualifications expire. The CSV is clearly easier to test though. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# encoding: utf-8 | ||
|
||
# Copyright (c) 2012-2023, Pfadibewegung Schweiz. This file is part of | ||
# hitobito_youth 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_youth. | ||
|
||
|
||
require 'spec_helper' | ||
require 'csv' | ||
|
||
describe Export::Tabular::People::PeopleEducationList do | ||
|
||
let(:top_leader) { people(:top_leader) } | ||
let(:sl) { qualification_kinds(:sl) } | ||
Event::Course | ||
let!(:course) { Fabricate(:course, groups: [groups(:top_group)]) } | ||
let!(:dummy) {course.dates.create!(start_at: 3.month.from_now, finish_at: 4.month.from_now)} | ||
let!(:participation) { Fabricate(:event_participation, person: top_leader, event: course) } | ||
let!(:qualification) { Fabricate(:qualification, person: top_leader, qualification_kind: sl, finish_at: 1.year.from_now) } | ||
|
||
let(:data) { Export::Tabular::People::PeopleEducationList.export(:csv, [top_leader]) } | ||
let(:csv) { CSV.parse(data, headers: true, col_sep: Settings.csv.separator) } | ||
|
||
before do | ||
top_leader.update(birthday: Date.new(2020, 02, 02), nickname: "Tonka") | ||
|
||
end | ||
|
||
context 'german' do | ||
let(:lang) { :de } | ||
|
||
it 'has correct headers' do | ||
expect(csv.headers).to eq([ 'Vorname', 'Nachname', 'Übername', 'Haupt-E-Mail', 'Geburtstag', 'Qualifikationen', 'Anmeldungen' ]) | ||
end | ||
|
||
context 'first row' do | ||
subject { csv[0] } | ||
|
||
its(['Vorname']) { should eq 'Top' } | ||
its(['Nachname']) { should eq 'Leader' } | ||
its(['Übername']) { should eq "Tonka" } | ||
its(['Haupt-E-Mail']) { should eq '[email protected]' } | ||
its(['Geburtstag']) { should eq "02.02.2020"} | ||
its(['Qualifikationen']) { should eq 'Super Lead' } | ||
its(['Anmeldungen']) { should eq 'Eventus' } | ||
end | ||
end | ||
|
||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -81,4 +81,86 @@ def create_qualification(attrs) | |
end.to raise_error CanCan::AccessDenied | ||
end | ||
|
||
context 'exports to csv' do | ||
let(:rows) { response.body.split("\n") } | ||
it 'CSV does list leader participations' do | ||
get :index, params: { locale: :de, id: groups(:top_layer).id, range: :layer, filters: { role: { role_type_ids: Group::TopGroup::Leader.id } }, format: :csv } | ||
expect(response).to be_successful | ||
expect(rows.first).to match("Vorname;Nachname;Übername;Haupt-E-Mail;Geburtstag;Qualifikationen;Anmeldungen") | ||
expect(rows.size).to eq(2) | ||
expect(rows.second).to have_content 'Top;Leader' | ||
expect(rows.second).to have_content 'Top Course' | ||
end | ||
|
||
it 'CSV does list participant participations' do | ||
get :index, params: { locale: :de, format: :csv, id: groups(:bottom_layer_one).id } | ||
expect(response).to be_successful | ||
expect(rows.first).to match("Vorname;Nachname;Übername;Haupt-E-Mail;Geburtstag;Qualifikationen;Anmeldungen") | ||
expect(rows.size).to eq(3) | ||
expect(rows.third).to have_content 'Bottom;Member;' | ||
expect(rows.third).to have_content 'Top Course' | ||
end | ||
|
||
it 'CSV does not list completed events' do | ||
events(:top_course).dates.last.update!(start_at: Date.today - 1.month) | ||
get :index, params: { locale: :de, format: :csv, id: groups(:top_layer).id, range: :layer, filters: { role: { role_type_ids: Group::TopGroup::Leader.id } } } | ||
expect(response).to be_successful | ||
expect(rows.first).to match("Vorname;Nachname;Übername;Haupt-E-Mail;Geburtstag;Qualifikationen;Anmeldungen") | ||
expect(rows.second).to eq("Top;Leader;;[email protected];;\"\";\"\"") | ||
end | ||
|
||
it 'CSV lists qualifications' do | ||
create_qualification(start_at: Date.yesterday) | ||
get :index, params: { locale: :de, format: :csv, id: groups(:top_layer).id, range: :layer, filters: { role: { role_type_ids: Group::TopGroup::Leader.id } } } | ||
expect(response).to be_successful | ||
expect(rows.first).to match("Vorname;Nachname;Übername;Haupt-E-Mail;Geburtstag;Qualifikationen;Anmeldungen") | ||
expect(rows.second).to have_content 'Super Lead' | ||
end | ||
|
||
it 'CSV lists qualifications event when expired' do | ||
create_qualification(start_at: Date.today - 3.days, finish_at: Date.yesterday) | ||
get :index, params: { locale: :de, format: :csv, id: groups(:top_layer).id, range: :layer, filters: { role: { role_type_ids: Group::TopGroup::Leader.id } } } | ||
expect(response).to be_successful | ||
expect(rows.first).to match("Vorname;Nachname;Übername;Haupt-E-Mail;Geburtstag;Qualifikationen;Anmeldungen") | ||
expect(rows.second).to have_content 'Super Lead' | ||
end | ||
|
||
it 'CSV filters qualifications positive' do | ||
create_qualification(start_at: Date.yesterday) | ||
get :index, | ||
params: { | ||
locale: :de, | ||
format: :csv, | ||
id: groups(:top_layer).id, | ||
range: :layer, | ||
filters: { qualification: { qualification_kind_ids: qualification_kinds(:sl).id } } | ||
} | ||
expect(response).to be_successful | ||
expect(rows.first).to match("Vorname;Nachname;Übername;Haupt-E-Mail;Geburtstag;Qualifikationen;Anmeldungen") | ||
expect(rows.second).to have_content 'Super Lead' | ||
end | ||
|
||
it 'CSV filters qualifications negative' do | ||
create_qualification(start_at: Date.yesterday) | ||
get :index, | ||
params: { | ||
locale: :de, | ||
format: :csv, | ||
id: groups(:top_layer).id, | ||
range: :layer, | ||
filters: { qualification: { qualification_kind_ids: qualification_kinds(:gl).id } } | ||
} | ||
expect(response).to be_successful | ||
expect(rows.first).to match("Vorname;Nachname;Übername;Haupt-E-Mail;Geburtstag;Qualifikationen;Anmeldungen") | ||
expect(rows.second).not_to have_content 'Super Lead' | ||
end | ||
|
||
it 'CSV raises AccessDenied if not permitted' do | ||
sign_in(people(:bottom_leader)) | ||
expect do | ||
get :index, params: { locale: :de, format: :csv, id: groups(:top_layer).id, range: :layer, filters: { role: { role_type_ids: Group::TopGroup::Leader.id } } } | ||
end.to raise_error CanCan::AccessDenied | ||
end | ||
end | ||
|
||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not great that this logic is duplicated in large parts from the helper to here.
Maybe you could add the common part of the logic to the
Youth::PersonDecorator
(e.g. as methodseducation_event_participations
etc.), and then use that here and in the helper.Either
entry.education_event_participations.join(', ')
orentry.decorate.education_event_participations.join(', ')
should work.