diff --git a/CHANGELOG.md b/CHANGELOG.md index caa42abbc..fcbe38ffb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog Fab-manager +## v6.1.1 2023 September 28 + +- Fix a bug: unable to sync projects with openprojects +- Fix a bug: public availabilities (no user) was buggy (server error) +- Fix a bug: unable to generate statistic +- Fix a bug: rss/projects was failing with project without image +- improvement : performance of members#show and reservations#index + +- [TODO DEPLOY] `rails fablab:openlab:bulk_export` +- [TODO DEPLOY] `rails fablab:openlab:bulk_update` +- [TODO DEPLOY] `rails fablab:es:build_stats` +- [TODO DEPLOY] `rails fablab:maintenance:regenerate_statistics[2023,6]` + ## v6.1.0 2023 September 25 - improves api/notification controller to avoid failing when there is a notification with wrong notification_type in db diff --git a/app/controllers/api/reservations_controller.rb b/app/controllers/api/reservations_controller.rb index 260fec568..495b95c9b 100644 --- a/app/controllers/api/reservations_controller.rb +++ b/app/controllers/api/reservations_controller.rb @@ -16,8 +16,10 @@ class API::ReservationsController < API::APIController where_clause[:reservable_id] = params[:reservable_id] if params[:reservable_id] @reservations = Reservation.where(where_clause) + .preload(:reservable, :booking_users, :tickets, { statistic_profile: { user: :profile }, slots_reservations: :slot }) elsif params[:reservable_id] && params[:reservable_type] && (current_user.admin? || current_user.manager?) @reservations = Reservation.where(params.permit(:reservable_id, :reservable_type)) + .preload(:reservable, :booking_users, :tickets, { statistic_profile: { user: :profile }, slots_reservations: :slot }) else @reservations = [] end diff --git a/app/frontend/src/stylesheets/modules/projects/projects.scss b/app/frontend/src/stylesheets/modules/projects/projects.scss index b8ce90a45..cc69f474c 100644 --- a/app/frontend/src/stylesheets/modules/projects/projects.scss +++ b/app/frontend/src/stylesheets/modules/projects/projects.scss @@ -39,6 +39,10 @@ grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); gap: 3.2rem; & > span { grid-column: 1/-1;} + & > a { + grid-column: 1/-1; + justify-self: center; + } } @media (min-width: 1200px) { diff --git a/app/frontend/templates/projects/index.html b/app/frontend/templates/projects/index.html index fde57a138..b2bde70ed 100644 --- a/app/frontend/templates/projects/index.html +++ b/app/frontend/templates/projects/index.html @@ -126,8 +126,7 @@
{{project.name}}
{{ project.app_name }} - - {{ 'app.public.projects_list.load_next_projects' }} + {{ 'app.public.projects_list.load_next_projects' }} diff --git a/app/services/availabilities/availabilities_service.rb b/app/services/availabilities/availabilities_service.rb index 8df177504..c870fd1d3 100644 --- a/app/services/availabilities/availabilities_service.rb +++ b/app/services/availabilities/availabilities_service.rb @@ -40,7 +40,6 @@ class Availabilities::AvailabilitiesService if @level == 'slot' slots = availabilities.map(&:slots).flatten - blocked_slots = Slots::InterblockingService.new.blocked_slots_for_machines(machines, slots) flag_or_remove_blocked_slots(slots, blocked_slots, @current_user) else @@ -141,7 +140,7 @@ class Availabilities::AvailabilitiesService end def flag_or_remove_blocked_slots(slots, blocked_slots, user) - if user.admin? || user.manager? + if user && (user.admin? || user.manager?) blocked_slots.each do |slot| slot.is_blocked = true end diff --git a/app/services/open_lab_service.rb b/app/services/open_lab_service.rb index 0aae0d0ec..c05385e76 100644 --- a/app/services/open_lab_service.rb +++ b/app/services/open_lab_service.rb @@ -3,8 +3,6 @@ # Provides methods to sync projects on OpenLab class OpenLabService class << self - include ActionView::Helpers::SanitizeHelper - def to_hash(project) { id: project.id, @@ -20,9 +18,9 @@ class OpenLabService steps_body: steps_body(project), image_path: project.project_image&.attachment&.medium&.url, project_path: "/#!/projects/#{project.slug}", - updated_at: project.updated_at.to_s(:iso8601), - created_at: project.created_at.to_s(:iso8601), - published_at: project.published_at.to_s(:iso8601) + updated_at: project.updated_at.to_fs(:iso8601), + created_at: project.created_at.to_fs(:iso8601), + published_at: project.published_at.to_fs(:iso8601) } end @@ -32,7 +30,7 @@ class OpenLabService .gsub("\r\n", ' ').gsub("\n\r", ' ') .gsub("\n", ' ').gsub("\r", ' ').gsub("\t", ' ') - strip_tags(concatenated_steps).strip + ActionController::Base.helpers.strip_tags(concatenated_steps).strip end end end diff --git a/app/services/statistics/builder_service.rb b/app/services/statistics/builder_service.rb index 9e84eb697..60fcc57d2 100644 --- a/app/services/statistics/builder_service.rb +++ b/app/services/statistics/builder_service.rb @@ -17,7 +17,7 @@ class Statistics::BuilderService private def default_options - yesterday = Time.current + yesterday = 1.day.ago { start_date: yesterday.beginning_of_day, end_date: yesterday.end_of_day diff --git a/app/views/api/members/show.json.jbuilder b/app/views/api/members/show.json.jbuilder index 0e7a44135..4313c1e38 100644 --- a/app/views/api/members/show.json.jbuilder +++ b/app/views/api/members/show.json.jbuilder @@ -10,7 +10,8 @@ json.trainings @member.trainings do |t| json.id t.id json.name t.name end -json.training_reservations @member.reservations.where(reservable_type: 'Training').map(&:slots_reservations).flatten do |sr| +reservations = @member.reservations.where(reservable_type: 'Training').preload(slots_reservations: [:slot, reservation: :reservable]) +json.training_reservations reservations.select { |r| r.reservable_type == "Training" }.map(&:slots_reservations).flatten do |sr| json.id sr.id json.start_at sr.slot.start_at json.end_at sr.slot.end_at @@ -19,7 +20,7 @@ json.training_reservations @member.reservations.where(reservable_type: 'Training json.is_valid @member.statistic_profile.training_ids.include?(sr.reservation.reservable_id) json.canceled_at sr.canceled_at end -json.machine_reservations @member.reservations.where(reservable_type: 'Machine').map(&:slots_reservations).flatten do |sr| +json.machine_reservations reservations.select { |r| r.reservable_type == "Machine" }.map(&:slots_reservations).flatten do |sr| json.id sr.id json.start_at sr.slot.start_at json.end_at sr.slot.end_at @@ -27,7 +28,7 @@ json.machine_reservations @member.reservations.where(reservable_type: 'Machine') json.reservable_type 'Machine' json.canceled_at sr.canceled_at end -json.space_reservations @member.reservations.where(reservable_type: 'Space').map(&:slots_reservations).flatten do |sr| +json.space_reservations reservations.select { |r| r.reservable_type == "Space" }.map(&:slots_reservations).flatten do |sr| json.id sr.id json.start_at sr.slot.start_at json.end_at sr.slot.end_at diff --git a/app/views/rss/events/index.xml.builder b/app/views/rss/events/index.xml.builder index b0a1942c6..1f03c90c1 100644 --- a/app/views/rss/events/index.xml.builder +++ b/app/views/rss/events/index.xml.builder @@ -22,7 +22,9 @@ xml.rss version: '2.0', 'xmlns:xCal' => 'urn:ietf:params:xml:ns:xcal' do xml.xCal :dtend do xml.text! event.availability.end_at.strftime('%FT%T%:z') end - xml.enclosure url: root_url + event.event_image.attachment.large.url, length: event.event_image.attachment.large.size, type: event.event_image.attachment.content_type if event.event_image + if event.event_image&.attachment? + xml.enclosure url: root_url + event.event_image.attachment.large.url, length: event.event_image.attachment.large.size, type: event.event_image.attachment.content_type + end xml.category event.category.name end end diff --git a/app/views/rss/projects/index.xml.builder b/app/views/rss/projects/index.xml.builder index 29e24057a..54525e2de 100644 --- a/app/views/rss/projects/index.xml.builder +++ b/app/views/rss/projects/index.xml.builder @@ -17,7 +17,9 @@ xml.rss version: '2.0' do xml.link root_url + '#!/projects/' + project.slug xml.author project.author&.user&.profile&.full_name xml.description project.description - xml.enclosure url: root_url + project.project_image.attachment.large.url, length: project.project_image.attachment.large.size, type: project.project_image.attachment.content_type if project.project_image + if project.project_image&.attachment? + xml.enclosure url: root_url + project.project_image.attachment.large.url, length: project.project_image.attachment.large.size, type: project.project_image.attachment.content_type + end end end end diff --git a/package.json b/package.json index 95f7b8ef0..024d9df00 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fab-manager", - "version": "6.1.0", + "version": "6.1.1", "description": "Fab-manager is the FabLab management solution. It provides a comprehensive, web-based, open-source tool to simplify your administrative tasks and your marker's projects.", "keywords": [ "fablab", diff --git a/test/integration/availabilities/as_public_test.rb b/test/integration/availabilities/as_public_test.rb index 1b99204d9..5e5e8ea45 100644 --- a/test/integration/availabilities/as_public_test.rb +++ b/test/integration/availabilities/as_public_test.rb @@ -3,7 +3,7 @@ require 'test_helper' class Availabilities::AsPublicTest < ActionDispatch::IntegrationTest - test 'get public machines availabilities if machines module is active' do + test "[level == 'availability'] get public machines availabilities if machines module is active" do start_date = Time.current.to_date end_date = 7.days.from_now.to_date @@ -24,6 +24,24 @@ class Availabilities::AsPublicTest < ActionDispatch::IntegrationTest end end + test "[level == 'slot'] get public machines availabilities if machines module is active" do + start_date = Time.current.to_date + end_date = start_date + 1.day + + get "/api/availabilities/public?start=#{start_date}&end=#{end_date}&timezone=Europe%2FParis{all_machines}" + + # Check response format & status + assert_equal 200, response.status + assert_match Mime[:json].to_s, response.content_type + + # Check the correct availabilities was returned + availabilities = json_response(response.body) + assert_not_empty availabilities, 'no availabilities were found' + availabilities.each do |a| + assert_not_empty a[:machine_ids] + end + end + test 'get anymore machines availabilities if machines module is inactive' do Setting.set('machines_module', false) start_date = Time.current.to_date @@ -61,6 +79,45 @@ class Availabilities::AsPublicTest < ActionDispatch::IntegrationTest end end + test "[level == 'availability'] get public spaces availabilities" do + start_date = Time.current.to_date + end_date = 7.days.from_now.to_date + + get "/api/availabilities/public?start=#{start_date}&end=#{end_date}&timezone=Europe%2FParis{all_spaces}" + + # Check response format & status + assert_equal 200, response.status + assert_match Mime[:json].to_s, response.content_type + + # Check the correct availabilities was returned + availabilities = json_response(response.body) + assert_not_empty availabilities, 'no availabilities were found' + availabilities.each_with_index do |a, index| + assert_not_nil a, "availability #{index} was unexpectedly nil" + assert_equal 'space', a[:available_type], "availability #{index} is not a space availability" + assert Time.zone.parse(a[:start]) > start_date, "availability #{index} starts before the requested period" + assert Time.zone.parse(a[:end]) < end_date, "availability #{index} ends after the requested period" + end + end + + test "[level == 'slot'] get public spaces availabilities" do + start_date = Time.current.to_date + end_date = start_date + 1.day + + get "/api/availabilities/public?start=#{start_date}&end=#{end_date}&timezone=Europe%2FParis{all_spaces}" + + # Check response format & status + assert_equal 200, response.status + assert_match Mime[:json].to_s, response.content_type + + # Check the correct availabilities was returned + availabilities = json_response(response.body) + assert_not_empty availabilities, 'no availabilities were found' + availabilities.each do |a| + assert_not_nil a[:space_id] + end + end + test 'get public spaces availabilities' do start_date = Time.current.to_date end_date = 7.days.from_now.to_date diff --git a/test/integration/rss/events_test.rb b/test/integration/rss/events_test.rb new file mode 100644 index 000000000..47972f271 --- /dev/null +++ b/test/integration/rss/events_test.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'test_helper' +module Rss; end + +class Rss::EventsTestTest < ActionDispatch::IntegrationTest + test '#index' do + get rss_events_path + + assert_response :success + assert Nokogiri::XML(response.body).errors.empty? + end +end \ No newline at end of file diff --git a/test/integration/rss/projects_test.rb b/test/integration/rss/projects_test.rb new file mode 100644 index 000000000..cf4945f2e --- /dev/null +++ b/test/integration/rss/projects_test.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'test_helper' +module Rss; end + +class Rss::ProjectsTestTest < ActionDispatch::IntegrationTest + test '#index' do + get rss_projects_path + + assert_response :success + assert Nokogiri::XML(response.body).errors.empty? + end +end \ No newline at end of file diff --git a/test/services/open_lab_service_test.rb b/test/services/open_lab_service_test.rb new file mode 100644 index 000000000..03de1a5bd --- /dev/null +++ b/test/services/open_lab_service_test.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require 'test_helper' + +# In the following tests, amounts are expressed in centimes, ie. 1000 = 1000 cts = 10,00 EUR +class OpenLabServiceTest < ActiveSupport::TestCase + test "do not raise any error" do + h = nil + + assert_nothing_raised do + h = OpenLabService.to_hash(projects(:project_1)) + end + + assert_instance_of Hash, h + end +end \ No newline at end of file