From 07e0ed9282e59eb7fa5f2b75d00ab0358c341944 Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 7 Jul 2022 15:39:00 -0400 Subject: [PATCH 01/50] WIP for publication of schedule --- app/models/published_session_assignment.rb | 6 +- app/services/publication_service.rb | 66 ++++++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 app/services/publication_service.rb diff --git a/app/models/published_session_assignment.rb b/app/models/published_session_assignment.rb index d1415f520..7bd01abe5 100644 --- a/app/models/published_session_assignment.rb +++ b/app/models/published_session_assignment.rb @@ -4,10 +4,10 @@ class PublishedSessionAssignment < ApplicationRecord has_paper_trail versions: { class_name: 'Audit::PublishedSessionVersion' }, ignore: [:updated_at, :created_at] include RankedModel - ranks :sort_order, with_same: [:session_id] + ranks :sort_order, with_same: [:published_session_id] belongs_to :person - belongs_to :published_session, - foreign_key: 'session_id' + belongs_to :published_session + # foreign_key: 'session_id' belongs_to :session_assignment has_one :session_assignment_role_type diff --git a/app/services/publication_service.rb b/app/services/publication_service.rb new file mode 100644 index 000000000..3e1b53427 --- /dev/null +++ b/app/services/publication_service.rb @@ -0,0 +1,66 @@ +module PublicationService + # Publish the schedule + # Copy Sessions, and Assignments to Published versions + def self.publish + # We only want the public participant roles + Session.Transaction do + # Get the session elligible for publication, not draft or dropped and must be public + sessions = Session.where("status != 'draft' and status != 'dropped' and is_public = 'public'") + + # published_sessions + sessions.each do |session| + pub_session = self.publish_session(session: session) + pub_session.save! + + # And for assignments + assignments = session.participant_assignments + .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Invisble')") + .where("session_assignments.visibility = 'public'") + assignments.each do |assignment| + pub_assignment = self.publish_assignment(assignment: assignment, published_session: pub_session) + pub_assignment.save! + end + end + end + end + + # pub_session = PublicationService.publish_session(session: session) + # + # Published the requested session, if the session already has a published + # version then we update that otherwise we create one + def self.publish_session(session:) + pub_session = PublishedSession.find_by session_id: session.id + pub_session ||= PublishedSession.new + + session.attributes.each do |attr, val| + next if val.nil? # if there is nothing to copy skip + next if !pub_session.attributes.key?(attr) # if the published version does not support the attr skip + next if [:lock_version, :created_at, :updated_at, :id].include?(attr) # skip lock and dates + next if pub_session.attributes[attr] == val # skip if the value will not change + + pub_session.send("#{attr}=", val) # the the attr in the publihsed instance + end + pub_session.session_id = session.id unless pub_session.session_id # point published to source session + + pub_session + end + + # Create published versions of the assignments + def self.publish_assignment(assignment:, published_session:) + pub_assignment = PublishedSessionAssignment.find_by session_assignment_id: assignment.id + pub_assignment ||= PublishedSessionAssignment.new + + assignment.attributes.each do |attr, val| + next if val.nil? # if there is nothing to copy skip + next if !pub_assignment.attributes.key?(attr) # if the published version does not support the attr skip + next if [:lock_version, :created_at, :updated_at, :id, :session_id].include?(attr) # skip lock and dates + next if pub_assignment.attributes[attr] == val # skip if the value will not change + + pub_assignment.send("#{attr}=", val) # the the attr in the publihsed instance + end + pub_assignment.session_assignment_id = assignment.id unless pub_assignment.session_assignment_id # point published to source + pub_assignment.published_session_id = published_session.id unless pub_assignment.published_session_id + + pub_assignment + end +end From 6c81757db4640834cf18eabc9035531adc5b5b65 Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 7 Jul 2022 15:40:06 -0400 Subject: [PATCH 02/50] WIP --- app/services/publication_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/publication_service.rb b/app/services/publication_service.rb index 3e1b53427..0d229262a 100644 --- a/app/services/publication_service.rb +++ b/app/services/publication_service.rb @@ -5,7 +5,7 @@ def self.publish # We only want the public participant roles Session.Transaction do # Get the session elligible for publication, not draft or dropped and must be public - sessions = Session.where("status != 'draft' and status != 'dropped' and is_public = 'public'") + sessions = Session.where("status != 'draft' and status != 'dropped' and visibility = 'public'") # published_sessions sessions.each do |session| From ef24eadb1a084a9689927dfa0317900c6efadd0f Mon Sep 17 00:00:00 2001 From: Henry Date: Sun, 10 Jul 2022 15:29:13 -0400 Subject: [PATCH 03/50] Initials code for pubs --- app/controllers/publications_controller.rb | 16 ++++++++++++ app/models/concerns/xml_formattable.rb | 13 ++++++++++ app/models/session.rb | 2 ++ app/services/reports_service.rb | 13 ++++++++++ app/services/xml_formatter.rb | 29 ++++++++++++++++++++++ app/views/xml_templates/schedule.nokogiri | 26 +++++++++++++++++++ app/views/xml_templates/session.nokogiri | 15 +++++++++++ config/routes.rb | 2 ++ 8 files changed, 116 insertions(+) create mode 100644 app/controllers/publications_controller.rb create mode 100644 app/models/concerns/xml_formattable.rb create mode 100644 app/services/xml_formatter.rb create mode 100644 app/views/xml_templates/schedule.nokogiri create mode 100644 app/views/xml_templates/session.nokogiri diff --git a/app/controllers/publications_controller.rb b/app/controllers/publications_controller.rb new file mode 100644 index 000000000..da24ead88 --- /dev/null +++ b/app/controllers/publications_controller.rb @@ -0,0 +1,16 @@ +class PublicationsController < ApplicationController + around_action :set_timezone + + def schedule + sessions = ReportsService.scheduled_sessions + + send_data XmlFormatter.new(sessions).render('schedule', sessions), + filename: "schedule.xml", + disposition: 'attachment' + end + + def set_timezone(&block) + timezone = ConfigService.value('convention_timezone') + Time.use_zone(timezone, &block) + end +end diff --git a/app/models/concerns/xml_formattable.rb b/app/models/concerns/xml_formattable.rb new file mode 100644 index 000000000..8e292060e --- /dev/null +++ b/app/models/concerns/xml_formattable.rb @@ -0,0 +1,13 @@ +module XmlFormattable + def to_xml + formatter.format + end + + def render(file_name, object, options = nil) + formatter.render(file_name, object, options) + end + + def formatter + @formatter ||= XmlFormatter.new(self) + end +end diff --git a/app/models/session.rb b/app/models/session.rb index 40dbdba8a..0db9e1816 100644 --- a/app/models/session.rb +++ b/app/models/session.rb @@ -1,4 +1,6 @@ class Session < ApplicationRecord + include XmlFormattable + validates_presence_of :title validates_numericality_of :duration, allow_nil: true validates_numericality_of :minimum_people, allow_nil: true diff --git a/app/services/reports_service.rb b/app/services/reports_service.rb index 785d3d92e..23d69825e 100644 --- a/app/services/reports_service.rb +++ b/app/services/reports_service.rb @@ -72,6 +72,19 @@ def self.scheduled_session_no_people # .order(:start_time) end + # Get all the schedule sessions + def self.scheduled_sessions + Session.select( + ::Session.arel_table[Arel.star], + 'areas_list.area_list' + ) + .eager_load(:format, :room, {participant_assignments: :person}) + .joins(self.area_subquery) + .where("start_time is not null and room_id is not null") + .where("status != 'dropped' and status != 'draft'") + .order(:start_time) + end + def self.sessions_with_no_moderator sched_table = PersonSchedule.arel_table diff --git a/app/services/xml_formatter.rb b/app/services/xml_formatter.rb new file mode 100644 index 000000000..2429f86a1 --- /dev/null +++ b/app/services/xml_formatter.rb @@ -0,0 +1,29 @@ +class XmlFormatter + attr_reader :formattable + + def initialize(formattable) + @formattable = formattable + # Use Nokogiri to build XML output + @xml = Nokogiri::XML::Builder.new + end + + def format + render file_name, formattable, xml: @xml + end + + def render(file_name, object, options = nil) + file = "#{template_path}/#{file_name}.nokogiri" + scope = Object.new + scope.instance_variable_set :@instance, object + # at the moment we do not have any options ... TODO: check + Tilt.new(file).render(scope) #, options) + end + + def template_path + Rails.root.join("app", "views", "xml_templates") + end + + def file_name + formattable.class.name.downcase + end +end diff --git a/app/views/xml_templates/schedule.nokogiri b/app/views/xml_templates/schedule.nokogiri new file mode 100644 index 000000000..89acdb175 --- /dev/null +++ b/app/views/xml_templates/schedule.nokogiri @@ -0,0 +1,26 @@ +# +# XML template for a collection of sessions +# +xml.scehdule { + @instance.each do |session| + xml.session { + xml.title session.title + xml.description session.description + xml.start_time session.start_time + xml.duration session.duration + xml.areas session.area_list.join(", ") + xml.format session.format.name + xml.room session.room.name + xml.participants { + session.participant_assignments.each do |assignment| + next if assignment.session_assignment_role_type.name == 'Invisible' + + xml.person { + xml.name assignment.person.published_name + xml.role assignment.session_assignment_role_type.name + } + end + } + } + end +} diff --git a/app/views/xml_templates/session.nokogiri b/app/views/xml_templates/session.nokogiri new file mode 100644 index 000000000..2bc9d0a4c --- /dev/null +++ b/app/views/xml_templates/session.nokogiri @@ -0,0 +1,15 @@ +# +# This is an XML template for Session +# TODO: tweak for InDesign +# +xml.session { + xml.title @instance.title + xml.description @instance.description + # Start time is in convention Timezone (TODO) + xml.start_time @instance.start_time + xml.duration @instance.duration + xml.areas @instance.areas + xml.format @instance.format.name + xml.room @instance.room.name + xml.participants @instance.participants +} diff --git a/config/routes.rb b/config/routes.rb index 6102213c9..16dec2780 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -46,6 +46,8 @@ get 'person/:person_id/mailed_surveys', to: 'people#mailed_surveys' get 'person/:person_id/assigned_surveys', to: 'people#assigned_surveys' + get 'publications/schedule', to: 'publications#schedule' + get 'report/participant_selections', to: 'reports#participant_selections' get 'report/session_selections', to: 'reports#session_selections' get 'report/participant_availabilities', to: 'reports#participant_availabilities' From 95d3d1213d928ac7ba646993ccd0cc27f316bbc4 Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 18 Jul 2022 17:13:49 -0400 Subject: [PATCH 04/50] fix to get sessions without people as wel --- app/services/reports_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/reports_service.rb b/app/services/reports_service.rb index 0987ac8f9..769b498e6 100644 --- a/app/services/reports_service.rb +++ b/app/services/reports_service.rb @@ -79,7 +79,7 @@ def self.scheduled_sessions ::Session.arel_table[Arel.star], 'areas_list.area_list' ) - .eager_load(:format, :room, {participant_assignments: :person}) + .includes(:format, :room, {participant_assignments: :person}) .joins(self.area_subquery) .where("start_time is not null and room_id is not null") .where("status != 'dropped' and status != 'draft'") From ae98cbaab871654e8e9f236853af36c5a97b12ab Mon Sep 17 00:00:00 2001 From: Matt Arnold Date: Mon, 18 Jul 2022 21:50:42 -0400 Subject: [PATCH 05/50] Format schedule --- app/controllers/publications_controller.rb | 31 +++++++++++++++++- app/views/xml_templates/schedule.nokogiri | 38 +++++++++++++++------- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/app/controllers/publications_controller.rb b/app/controllers/publications_controller.rb index da24ead88..e14d1dfe8 100644 --- a/app/controllers/publications_controller.rb +++ b/app/controllers/publications_controller.rb @@ -4,7 +4,36 @@ class PublicationsController < ApplicationController def schedule sessions = ReportsService.scheduled_sessions - send_data XmlFormatter.new(sessions).render('schedule', sessions), + send_data XmlFormatter.new(sessions).render('schedule', sessions) + .gsub(/\\n/, '') + .gsub(/\\n /, '') + .gsub(/\\n /, '') + .gsub(/ \/, '') + .gsub(/\n\/, ' - ') + .gsub(/\<\/duration\>/, ' minutes') + .gsub(/\n\<\/timeduration\>/, '') + .gsub(/\n\/, '') + .gsub(/\n\/, ' - ') + .gsub(/\n\/, ', ') + .gsub(/\n\<\/roomareasformat\>/, '') + .gsub(/\\n/, '') + .gsub(/\\n/, '') + .gsub(/\n\/, '') + .gsub(/\\n /, '') + .gsub(/\Participant\<\/role>/, '') + .gsub(/\<\/person\>\/, ' ') + .gsub(/\<\/person\>\/, ', ') + .gsub(/\\n/, '') + .gsub(/\<\/name\>\n/, '') + .gsub(/\n\<\/person\>/, '') + .gsub(/\n\<\/participants\>/, '') + .gsub(/\Participant\<\/role>/, '') + .gsub(/\Moderator\<\/role>/, ' (Moderator)') + .gsub(/\n\<\/session\>/, '') + .gsub(/\n\<\/schedule\>\n/, ''), filename: "schedule.xml", disposition: 'attachment' end diff --git a/app/views/xml_templates/schedule.nokogiri b/app/views/xml_templates/schedule.nokogiri index 89acdb175..4eb24db3a 100644 --- a/app/views/xml_templates/schedule.nokogiri +++ b/app/views/xml_templates/schedule.nokogiri @@ -1,23 +1,39 @@ # # XML template for a collection of sessions # -xml.scehdule { - @instance.each do |session| +xml.schedule { + timeslot_hour = 0 + timeslot_minutes = 0 + @instance.each do |session| + hour = session.start_time.strftime("%k").to_i + minutes = session.start_time.strftime("%M").to_i + if hour == timeslot_hour && minutes > timeslot_minutes + timeslot_minutes = minutes + xml.timeslot(session.start_time.strftime("%l:%M")) + elsif hour > timeslot_hour + timeslot_hour = hour + timeslot_minutes = minutes + xml.timeslot(session.start_time.strftime("%l:%M")) + end xml.session { - xml.title session.title - xml.description session.description - xml.start_time session.start_time - xml.duration session.duration - xml.areas session.area_list.join(", ") - xml.format session.format.name - xml.room session.room.name + xml.title(session.title) + xml.timeduration{ + xml.start_time(session.start_time.strftime("%l:%M")) + xml.duration(session.duration) + } + xml.roomareasformat{ + xml.room(session.room.name) + xml.areas(session.area_list.join(", ")) + xml.format(session.format.name) + } + xml.description(session.description) xml.participants { session.participant_assignments.each do |assignment| next if assignment.session_assignment_role_type.name == 'Invisible' xml.person { - xml.name assignment.person.published_name - xml.role assignment.session_assignment_role_type.name + xml.name(assignment.person.published_name) + xml.role(assignment.session_assignment_role_type.name) } end } From 3dcbb93a5e12e002fbd83ae23b64f06b2fdef024 Mon Sep 17 00:00:00 2001 From: Matt Arnold Date: Tue, 19 Jul 2022 16:35:50 -0400 Subject: [PATCH 06/50] Fix timeslot subheading and add day heading --- app/views/xml_templates/schedule.nokogiri | 27 +++++++++++++---------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/app/views/xml_templates/schedule.nokogiri b/app/views/xml_templates/schedule.nokogiri index 4eb24db3a..f61f24ddd 100644 --- a/app/views/xml_templates/schedule.nokogiri +++ b/app/views/xml_templates/schedule.nokogiri @@ -2,23 +2,26 @@ # XML template for a collection of sessions # xml.schedule { - timeslot_hour = 0 - timeslot_minutes = 0 + day = Date.parse('1970-01-01') + timeslot = Time.parse('1970-01-01 00:00:00 0000') @instance.each do |session| - hour = session.start_time.strftime("%k").to_i - minutes = session.start_time.strftime("%M").to_i - if hour == timeslot_hour && minutes > timeslot_minutes - timeslot_minutes = minutes - xml.timeslot(session.start_time.strftime("%l:%M")) - elsif hour > timeslot_hour - timeslot_hour = hour - timeslot_minutes = minutes - xml.timeslot(session.start_time.strftime("%l:%M")) + # Check if this is the 1st session of the day, in which case, + # create a heading. + this_day = Date.parse(session.start_time.strftime("%Y-%m-%d")) + if this_day > day + day = this_day + xml.day(session.start_time.strftime("%A")) + end + # Check if this is the 1st session at this time, in which case, + # create a subheading. + if session.start_time.utc > timeslot.utc + timeslot = session.start_time.utc + xml.timeslot(session.start_time.strftime("%-l:%M")) end xml.session { xml.title(session.title) xml.timeduration{ - xml.start_time(session.start_time.strftime("%l:%M")) + xml.start_time(session.start_time.strftime("%-l:%M")) xml.duration(session.duration) } xml.roomareasformat{ From c1ee3a1071366790d227335372ae1b74931318da Mon Sep 17 00:00:00 2001 From: Matt Arnold Date: Tue, 19 Jul 2022 17:03:12 -0400 Subject: [PATCH 07/50] Initialize the day before convention --- app/views/xml_templates/schedule.nokogiri | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/views/xml_templates/schedule.nokogiri b/app/views/xml_templates/schedule.nokogiri index f61f24ddd..e6794f595 100644 --- a/app/views/xml_templates/schedule.nokogiri +++ b/app/views/xml_templates/schedule.nokogiri @@ -2,17 +2,21 @@ # XML template for a collection of sessions # xml.schedule { - day = Date.parse('1970-01-01') - timeslot = Time.parse('1970-01-01 00:00:00 0000') + # We need a header at the start of the document, so + # before_1st_session needs to be at least 86400 seconds (the + # number of seconds in a day) earlier than the 1st session + before_1st_session = @instance.first.start_time - 86401 + day = Date.parse(before_1st_session.strftime("%Y-%m-%d")) + timeslot = Time.parse(before_1st_session.strftime("%Y-%m-%d %l:%M")) @instance.each do |session| - # Check if this is the 1st session of the day, in which case, + # Check if this is the 1st session of the day, in which case, # create a heading. this_day = Date.parse(session.start_time.strftime("%Y-%m-%d")) if this_day > day day = this_day xml.day(session.start_time.strftime("%A")) end - # Check if this is the 1st session at this time, in which case, + # Check if this is the 1st session at this time, in which case, # create a subheading. if session.start_time.utc > timeslot.utc timeslot = session.start_time.utc @@ -20,6 +24,10 @@ xml.schedule { end xml.session { xml.title(session.title) + # timeduration, roomareasformat, and participants will each be a + # block-level paragraph with span-level elements. The line + # breaks & spacing will be removed in publications_controller.rb. + # Otherwise InDesign would render the white space in print. xml.timeduration{ xml.start_time(session.start_time.strftime("%-l:%M")) xml.duration(session.duration) From ae4f1e4648182dcdc6dc2cd45c71b002261af27e Mon Sep 17 00:00:00 2001 From: Matt Arnold Date: Wed, 20 Jul 2022 13:17:42 -0400 Subject: [PATCH 08/50] Add comment to XML about InDesign. --- app/controllers/publications_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/publications_controller.rb b/app/controllers/publications_controller.rb index e14d1dfe8..7479fc44a 100644 --- a/app/controllers/publications_controller.rb +++ b/app/controllers/publications_controller.rb @@ -5,7 +5,7 @@ def schedule sessions = ReportsService.scheduled_sessions send_data XmlFormatter.new(sessions).render('schedule', sessions) - .gsub(/\\n/, '') + .gsub(/\<\?xml version="1\.0"\?\>\n/, '') .gsub(/\\n /, '') .gsub(/\\n /, '') .gsub(/ \Participant\<\/role>/, '') .gsub(/\<\/person\>\/, ' ') .gsub(/\<\/person\>\/, ', ') - .gsub(/\\n/, '') + .gsub(/\<\/participants\>\n/, '') .gsub(/\<\/name\>\n/, '') .gsub(/\n\<\/person\>/, '') .gsub(/\n\<\/participants\>/, '') From be6e6c1fa5376929190ffb61989858b750998f86 Mon Sep 17 00:00:00 2001 From: Matt Arnold Date: Wed, 20 Jul 2022 13:19:29 -0400 Subject: [PATCH 09/50] If no visible participants, don't create element. --- app/views/xml_templates/schedule.nokogiri | 26 +++++++++++++++-------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/app/views/xml_templates/schedule.nokogiri b/app/views/xml_templates/schedule.nokogiri index e6794f595..baa889246 100644 --- a/app/views/xml_templates/schedule.nokogiri +++ b/app/views/xml_templates/schedule.nokogiri @@ -38,16 +38,24 @@ xml.schedule { xml.format(session.format.name) } xml.description(session.description) - xml.participants { - session.participant_assignments.each do |assignment| - next if assignment.session_assignment_role_type.name == 'Invisible' - - xml.person { - xml.name(assignment.person.published_name) - xml.role(assignment.session_assignment_role_type.name) - } - end + # If there are no visible participants, do not create an empty + # element, because InDesign would render that + # as an empty space. + non_invisible = session.participant_assignments.map { + |assignment| assignment.session_assignment_role_type.name != 'Invisible' } + if non_invisible.length > 0 + xml.participants { + session.participant_assignments.each do |assignment| + xml.person { + xml.name(assignment.person.published_name) + if assignment.session_assignment_role_type.name == 'Moderator' + xml.role(assignment.session_assignment_role_type.name) + end + } + end + } + end } end } From d5f4e5077cabb08ef2f59d40f9f86f8c4e979b55 Mon Sep 17 00:00:00 2001 From: Matt Arnold Date: Wed, 20 Jul 2022 13:20:25 -0400 Subject: [PATCH 10/50] Correct a typo. --- app/controllers/publications_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/publications_controller.rb b/app/controllers/publications_controller.rb index 7479fc44a..ee510942f 100644 --- a/app/controllers/publications_controller.rb +++ b/app/controllers/publications_controller.rb @@ -5,7 +5,7 @@ def schedule sessions = ReportsService.scheduled_sessions send_data XmlFormatter.new(sessions).render('schedule', sessions) - .gsub(/\<\?xml version="1\.0"\?\>\n/, '') + .gsub(/\<\?xml version="1\.0"\?\>\n/, '') .gsub(/\\n /, '') .gsub(/\\n /, '') .gsub(/ \ Date: Thu, 21 Jul 2022 14:46:51 -0400 Subject: [PATCH 11/50] Event ID and Moderator --- app/controllers/publications_controller.rb | 1 - app/views/xml_templates/schedule.nokogiri | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/publications_controller.rb b/app/controllers/publications_controller.rb index ee510942f..368552f1f 100644 --- a/app/controllers/publications_controller.rb +++ b/app/controllers/publications_controller.rb @@ -31,7 +31,6 @@ def schedule .gsub(/\n\<\/person\>/, '') .gsub(/\n\<\/participants\>/, '') .gsub(/\Participant\<\/role>/, '') - .gsub(/\Moderator\<\/role>/, ' (Moderator)') .gsub(/\n\<\/session\>/, '') .gsub(/\n\<\/schedule\>\n/, ''), filename: "schedule.xml", diff --git a/app/views/xml_templates/schedule.nokogiri b/app/views/xml_templates/schedule.nokogiri index baa889246..2654f82c1 100644 --- a/app/views/xml_templates/schedule.nokogiri +++ b/app/views/xml_templates/schedule.nokogiri @@ -23,7 +23,8 @@ xml.schedule { xml.timeslot(session.start_time.strftime("%-l:%M")) end xml.session { - xml.title(session.title) + session_id = session.pub_reference_number ? "#{xml.id(session.pub_reference_number)} | " : "" + xml.title("#{session_id}#{session.title}") # timeduration, roomareasformat, and participants will each be a # block-level paragraph with span-level elements. The line # breaks & spacing will be removed in publications_controller.rb. @@ -50,7 +51,7 @@ xml.schedule { xml.person { xml.name(assignment.person.published_name) if assignment.session_assignment_role_type.name == 'Moderator' - xml.role(assignment.session_assignment_role_type.name) + xml.role("(#{assignment.session_assignment_role_type.name})") end } end From 082f034ecc6c37f44b840a92cbd582835ff6cc31 Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 22 Jul 2022 08:57:33 -0400 Subject: [PATCH 12/50] method to generate pub ref numbers --- app/services/publication_service.rb | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/app/services/publication_service.rb b/app/services/publication_service.rb index 0d229262a..e5a30f322 100644 --- a/app/services/publication_service.rb +++ b/app/services/publication_service.rb @@ -1,11 +1,30 @@ module PublicationService + + def self.gen_pub_numbers + Session.Transaction do + # Get the session elligible for publication, not draft or dropped and must be public + sessions = Session + .where("status != 'draft' and status != 'dropped' and visibility = 'public'") + .where("room_id is not null and start_time is not null") + + current_ref = 0 + sessions.each do |session| + current_ref += 1 + session.pub_reference_number = current_ref + session.save! + end + end + end + # Publish the schedule # Copy Sessions, and Assignments to Published versions def self.publish # We only want the public participant roles Session.Transaction do # Get the session elligible for publication, not draft or dropped and must be public - sessions = Session.where("status != 'draft' and status != 'dropped' and visibility = 'public'") + sessions = Session + .where("status != 'draft' and status != 'dropped' and visibility = 'public'") + .where("room_id is not null and start_time is not null") # published_sessions sessions.each do |session| From 6078af3eb2138b6f0dab882f9f3d31325f45be41 Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 22 Jul 2022 09:17:33 -0400 Subject: [PATCH 13/50] fix transaction --- app/services/publication_service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/services/publication_service.rb b/app/services/publication_service.rb index e5a30f322..761657e7a 100644 --- a/app/services/publication_service.rb +++ b/app/services/publication_service.rb @@ -1,7 +1,7 @@ module PublicationService def self.gen_pub_numbers - Session.Transaction do + Session.transaction do # Get the session elligible for publication, not draft or dropped and must be public sessions = Session .where("status != 'draft' and status != 'dropped' and visibility = 'public'") @@ -20,7 +20,7 @@ def self.gen_pub_numbers # Copy Sessions, and Assignments to Published versions def self.publish # We only want the public participant roles - Session.Transaction do + Session.transaction do # Get the session elligible for publication, not draft or dropped and must be public sessions = Session .where("status != 'draft' and status != 'dropped' and visibility = 'public'") From 6f57f3e24ac4c2b734ed72097e65087021945d42 Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 22 Jul 2022 10:49:35 -0400 Subject: [PATCH 14/50] notes --- app/serializers/conclar/session_serializer.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/serializers/conclar/session_serializer.rb b/app/serializers/conclar/session_serializer.rb index 5189ff8b9..0ecf029b8 100644 --- a/app/serializers/conclar/session_serializer.rb +++ b/app/serializers/conclar/session_serializer.rb @@ -17,12 +17,15 @@ class Conclar::SessionSerializer < ActiveModel::Serializer res.concat object.areas.collect(&:name) res.concat [object.age_restriction.name] if object.age_restriction - res.concat [object.environment] if object.environment != 'unknown' + res.concat [object.environment] if object.environment != 'unknown' # virtual hybrid etc if object.minors_participation && object.minors_participation.class == Array res.concat object.minors_participation end + # require_signup ???? + # recordrd ??? + res end From 918b33cb64284805676ccb089bc78356054e0a4a Mon Sep 17 00:00:00 2001 From: Matt Arnold Date: Fri, 22 Jul 2022 17:39:09 -0400 Subject: [PATCH 15/50] Correct the session IDs. --- app/controllers/publications_controller.rb | 1 + app/views/xml_templates/schedule.nokogiri | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/controllers/publications_controller.rb b/app/controllers/publications_controller.rb index 368552f1f..52043fdba 100644 --- a/app/controllers/publications_controller.rb +++ b/app/controllers/publications_controller.rb @@ -11,6 +11,7 @@ def schedule .gsub(/ \(\d+)\<\/id\>\n\/, '<title><id>\1</id> | ') .gsub(/\n\<start_time\>/, '<start_time>') .gsub(/\n\<duration\>/, ' - <duration>') .gsub(/\<\/duration\>/, ' minutes</duration>') diff --git a/app/views/xml_templates/schedule.nokogiri b/app/views/xml_templates/schedule.nokogiri index 2654f82c1..92cb7e0c4 100644 --- a/app/views/xml_templates/schedule.nokogiri +++ b/app/views/xml_templates/schedule.nokogiri @@ -23,8 +23,8 @@ xml.schedule { xml.timeslot(session.start_time.strftime("%-l:%M")) end xml.session { - session_id = session.pub_reference_number ? "#{xml.id(session.pub_reference_number)} | " : "" - xml.title("#{session_id}#{session.title}") + xml.id(session.pub_reference_number) + xml.title(session.title) # timeduration, roomareasformat, and participants will each be a # block-level paragraph with span-level elements. The line # breaks & spacing will be removed in publications_controller.rb. From 0876fa992eeb31e1c8c41bf6f434f1993df85e74 Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Mon, 25 Jul 2022 16:21:20 -0400 Subject: [PATCH 16/50] WIP --- app/controllers/schedule_controller.rb | 3 + app/lib/migration_helpers/plano_views.rb | 7 +- app/models/published_session.rb | 25 ++++++ app/serializers/conclar/session_serializer.rb | 20 +++-- app/services/publication_service.rb | 84 ++++++++++++++----- app/services/reports_service.rb | 33 ++++++-- 6 files changed, 139 insertions(+), 33 deletions(-) diff --git a/app/controllers/schedule_controller.rb b/app/controllers/schedule_controller.rb index 02d7fd031..4abe30672 100644 --- a/app/controllers/schedule_controller.rb +++ b/app/controllers/schedule_controller.rb @@ -2,6 +2,9 @@ class ScheduleController < ApplicationController skip_before_action :check_up, :authenticate_person!, only: [:index, :participants] + # 1. If prod always use the published schedule + # 2. If staging or dev use published - if no published then use the live for testing + # 3. Put in a cache mechanism (cache can be popultaed as part of the publish) def index sessions = ReportsService.scheduled_sessions diff --git a/app/lib/migration_helpers/plano_views.rb b/app/lib/migration_helpers/plano_views.rb index ff29ed9cd..04d701766 100644 --- a/app/lib/migration_helpers/plano_views.rb +++ b/app/lib/migration_helpers/plano_views.rb @@ -42,7 +42,12 @@ def self.create_person_schedules sessions.format_id, sessions.participant_notes, sessions.description, - sessions.environment + sessions.environment, + case + when sa.updated_at > sessions.updated_at + then sa.updated_at + else sessions.updated_at + end as updated_at from session_assignments sa join diff --git a/app/models/published_session.rb b/app/models/published_session.rb index 736cb7ef0..e9410b65b 100644 --- a/app/models/published_session.rb +++ b/app/models/published_session.rb @@ -8,6 +8,7 @@ class PublishedSession < ApplicationRecord belongs_to :format belongs_to :session + belongs_to :room, required: false has_many :published_session_assignments, dependent: :destroy do # get the people with the given role @@ -24,6 +25,15 @@ def roles(role_ids) end has_many :people, through: :published_session_assignments + has_many :participant_assignments, + -> { + joins("JOIN session_assignment_role_type as sart ON sart.id = published_session_assignments.session_assignment_role_type_id") + .where("published_session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Reserve')") + .order("sart.sort_order") + }, + class_name: 'PublishedSessionAssignment' + has_many :participants, through: :participant_assignments, source: :person, class_name: 'Person' + enum visibility: { is_public: 'public', is_private: 'private' @@ -47,4 +57,19 @@ def public? def private? visibility == 'public' end + + def self.area_list + sessions = PublishedSession.arel_table + session_areas = Arel::Table.new(SessionArea.table_name) #.alias('session') + areas = Arel::Table.new(Area.table_name) + + sessions.project(sessions[:session_id].as('session_id'), area_aggregate_fn( areas[:name] ).as('area_list')) + .join(session_areas, Arel::Nodes::OuterJoin).on(sessions[:session_id].eq(session_areas[:session_id])) + .join(areas, Arel::Nodes::OuterJoin).on(session_areas[:area_id].eq(areas[:id])) + .group('published_sessions.session_id') + end + + def self.area_aggregate_fn(col) + Arel::Nodes::NamedFunction.new('array_remove',[Arel::Nodes::NamedFunction.new('array_agg',[col]), Arel::Nodes::SqlLiteral.new("NULL")]) + end end diff --git a/app/serializers/conclar/session_serializer.rb b/app/serializers/conclar/session_serializer.rb index 0ecf029b8..b68c307d2 100644 --- a/app/serializers/conclar/session_serializer.rb +++ b/app/serializers/conclar/session_serializer.rb @@ -1,5 +1,9 @@ class Conclar::SessionSerializer < ActiveModel::Serializer - attributes :id, :title + attributes :title + + attribute :id do + object.session_id + end attribute :desc do object.description @@ -14,14 +18,14 @@ class Conclar::SessionSerializer < ActiveModel::Serializer res = [] # TODO: optimize - res.concat object.areas.collect(&:name) + res.concat object.area_list #.collect(&:name) - res.concat [object.age_restriction.name] if object.age_restriction - res.concat [object.environment] if object.environment != 'unknown' # virtual hybrid etc - - if object.minors_participation && object.minors_participation.class == Array - res.concat object.minors_participation - end + # res.concat [object.age_restriction.name] if object.age_restriction + # res.concat [object.environment] if object.environment != 'unknown' # virtual hybrid etc + # + # if object.minors_participation && object.minors_participation.class == Array + # res.concat object.minors_participation + # end # require_signup ???? # recordrd ??? diff --git a/app/services/publication_service.rb b/app/services/publication_service.rb index e5a30f322..fc55a7b71 100644 --- a/app/services/publication_service.rb +++ b/app/services/publication_service.rb @@ -3,6 +3,7 @@ module PublicationService def self.gen_pub_numbers Session.Transaction do # Get the session elligible for publication, not draft or dropped and must be public + # TODO: if the timestamp of session or assignment is newer then before sessions = Session .where("status != 'draft' and status != 'dropped' and visibility = 'public'") .where("room_id is not null and start_time is not null") @@ -18,38 +19,81 @@ def self.gen_pub_numbers # Publish the schedule # Copy Sessions, and Assignments to Published versions + # PublicationDate, timestamp, newitems, modifieditems, removeditems + # PublicationStatus, status, submit_time def self.publish # We only want the public participant roles - Session.Transaction do + Session.transaction do # Get the session elligible for publication, not draft or dropped and must be public sessions = Session .where("status != 'draft' and status != 'dropped' and visibility = 'public'") .where("room_id is not null and start_time is not null") - # published_sessions - sessions.each do |session| - pub_session = self.publish_session(session: session) - pub_session.save! - - # And for assignments - assignments = session.participant_assignments - .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Invisble')") - .where("session_assignments.visibility = 'public'") - assignments.each do |assignment| - pub_assignment = self.publish_assignment(assignment: assignment, published_session: pub_session) - pub_assignment.save! - end + # updated + publish_updated(sessions) + # new + publish_new(sessions) + # dropped + remove_dropped(sessions) + end + end + # POST published - create a cache for the published schedule + + def self.publish_new(sessions) + candidates = PublishedSession.where("session_id in (?)", sessions.collect(&:id)) + count = candidates.count + + sessions.each do |session| + pub_session = self.publish_session(session: session, update: false) + pub_session.save! + + assignments = session.participant_assignments + .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Invisble')") + .where("session_assignments.visibility = 'public'") + assignments.each do |assignment| + pub_assignment = self.publish_assignment(assignment: assignment, update: false) + pub_assignment.save! end end + count + end + + def self.publish_updated(sessions) + end + + # def self.publish_new(sessions) + # sessions.each do |session| + # pub_session = self.publish_session(session: session) + # pub_session.save! + # + # # And for assignments + # assignments = session.participant_assignments + # .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Invisble')") + # .where("session_assignments.visibility = 'public'") + # assignments.each do |assignment| + # # new, update + # pub_assignment = self.publish_assignment(assignment: assignment, published_session: pub_session) + # pub_assignment.save! + # end + # end + # end + + def self.remove_dropped(sessions) + candidates = PublishedSession.where("session_id not in (?)", sessions.collect(&:id)) + count = candidates.count + candidates.destroy_all + count end # pub_session = PublicationService.publish_session(session: session) # # Published the requested session, if the session already has a published # version then we update that otherwise we create one - def self.publish_session(session:) + def self.publish_session(session:, update: true) pub_session = PublishedSession.find_by session_id: session.id - pub_session ||= PublishedSession.new + pub_session ||= PublishedSession.new unless update + + return unless pub_session session.attributes.each do |attr, val| next if val.nil? # if there is nothing to copy skip @@ -65,9 +109,11 @@ def self.publish_session(session:) end # Create published versions of the assignments - def self.publish_assignment(assignment:, published_session:) + def self.publish_assignment(assignment:, update: true) pub_assignment = PublishedSessionAssignment.find_by session_assignment_id: assignment.id - pub_assignment ||= PublishedSessionAssignment.new + pub_assignment ||= PublishedSessionAssignment.new unless update + + return unless pub_assignment assignment.attributes.each do |attr, val| next if val.nil? # if there is nothing to copy skip @@ -78,7 +124,7 @@ def self.publish_assignment(assignment:, published_session:) pub_assignment.send("#{attr}=", val) # the the attr in the publihsed instance end pub_assignment.session_assignment_id = assignment.id unless pub_assignment.session_assignment_id # point published to source - pub_assignment.published_session_id = published_session.id unless pub_assignment.published_session_id + pub_assignment.published_session_id = assignment.session_id unless pub_assignment.published_session_id pub_assignment end diff --git a/app/services/reports_service.rb b/app/services/reports_service.rb index 14b2ad268..dab20d3ba 100644 --- a/app/services/reports_service.rb +++ b/app/services/reports_service.rb @@ -108,15 +108,24 @@ def self.scheduled_session_no_people # Get all the schedule sessions def self.scheduled_sessions - Session.select( - ::Session.arel_table[Arel.star], + PublishedSession.select( + ::PublishedSession.arel_table[Arel.star], 'areas_list.area_list' ) .includes(:format, :room, {participant_assignments: :person}) - .joins(self.area_subquery) - .where("start_time is not null and room_id is not null") - .where("status != 'dropped' and status != 'draft'") + .joins(self.published_area_subquery) .order(:start_time) + # .where("start_time is not null and room_id is not null") + # .where("status != 'dropped' and status != 'draft'") + # Session.select( + # ::Session.arel_table[Arel.star], + # 'areas_list.area_list' + # ) + # .includes(:format, :room, {participant_assignments: :person}) + # .joins(self.area_subquery) + # .where("start_time is not null and room_id is not null") + # .where("status != 'dropped' and status != 'draft'") + # .order(:start_time) end def self.scheduled_people @@ -332,6 +341,20 @@ def self.area_subquery ] end + def self.published_area_subquery + session_table = PublishedSession.arel_table + areas_list = PublishedSession.area_list.as('areas_list') + [ + session_table.create_join( + areas_list, + session_table.create_on( + areas_list[:session_id].eq(session_table[:session_id]) + ), + Arel::Nodes::OuterJoin + ) + ] + end + def self.sessions_and_participant_count format = Format.find_by(name: 'Panel') active_roles = SessionAssignmentRoleType.where("role_type = 'participant' and (name != 'Invisible' and name != 'Reserve')") From 86db3ad0fbc0b24f9d8ca3e8a52f332a840abf41 Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Tue, 26 Jul 2022 10:54:31 -0400 Subject: [PATCH 17/50] PLAN-544 conlcar part 1 --- app/controllers/schedule_controller.rb | 4 +- app/models/person.rb | 25 ++++- app/models/published_session.rb | 9 ++ app/models/published_session_assignment.rb | 5 +- .../conclar/participant_serializer.rb | 63 ++++++------- app/serializers/conclar/session_serializer.rb | 19 ++-- app/services/reports_service.rb | 52 ---------- app/services/session_service.rb | 94 +++++++++++++++++++ ...6130346_add_fields_to_published_session.rb | 6 ++ db/structure.sql | 9 +- 10 files changed, 180 insertions(+), 106 deletions(-) create mode 100644 db/migrate/20220726130346_add_fields_to_published_session.rb diff --git a/app/controllers/schedule_controller.rb b/app/controllers/schedule_controller.rb index 4abe30672..6e34a0750 100644 --- a/app/controllers/schedule_controller.rb +++ b/app/controllers/schedule_controller.rb @@ -6,7 +6,7 @@ class ScheduleController < ApplicationController # 2. If staging or dev use published - if no published then use the live for testing # 3. Put in a cache mechanism (cache can be popultaed as part of the publish) def index - sessions = ReportsService.scheduled_sessions + sessions = SessionService.scheduled_sessions render json: ActiveModel::Serializer::CollectionSerializer.new( sessions, @@ -16,7 +16,7 @@ def index end def participants - participants = ReportsService.scheduled_people + participants = SessionService.scheduled_people render json: ActiveModel::Serializer::CollectionSerializer.new( participants, diff --git a/app/models/person.rb b/app/models/person.rb index eaffeb702..d489ade8e 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -20,8 +20,25 @@ class Person < ApplicationRecord has_many :availabilities - has_many :session_assignments, dependent: :destroy - has_many :sessions, through: :session_assignments + has_many :session_assignments, dependent: :destroy do + def publishable + # get the people with the given role + where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Invisble')") + .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Reserve')") + .where("session_assignments.session_assignment_role_type_id is not null AND session_assignments.state != 'rejected'") + end + end + + has_many :sessions, through: :session_assignments do + def publishable + # get the people with the given role + where("sessions.status != 'draft' and sessions.status != 'dropped' and sessions.visibility = 'public'") + .where("sessions.start_time is not null and sessions.room_id is not null") + .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Invisble')") + .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Reserve')") + .where("session_assignments.session_assignment_role_type_id is not null AND session_assignments.state != 'rejected'") + end + end has_many :person_exclusions, dependent: :destroy has_many :exclusions, through: :person_exclusions @@ -32,8 +49,8 @@ class Person < ApplicationRecord has_many :person_schedule_approvals, dependent: :destroy # We let the publish mechanism do the destroy so that the update service knows what is happening - # has_many :published_session_assignments - # has_many :published_sessions, through: :published_session_assignments + has_many :published_session_assignments + has_many :published_sessions, through: :published_session_assignments has_many :person_mailing_assignments has_many :mailings, through: :person_mailing_assignments diff --git a/app/models/published_session.rb b/app/models/published_session.rb index e9410b65b..a8b27734d 100644 --- a/app/models/published_session.rb +++ b/app/models/published_session.rb @@ -10,6 +10,15 @@ class PublishedSession < ApplicationRecord belongs_to :session belongs_to :room, required: false + belongs_to :age_restriction, required: false + + enum environment: { + unknown: 'unknown', + in_person: 'in_person', + hybrid: 'hybrid', + virtual: 'virtual' + } + has_many :published_session_assignments, dependent: :destroy do # get the people with the given role def role(role) diff --git a/app/models/published_session_assignment.rb b/app/models/published_session_assignment.rb index 7bd01abe5..2dddb0c72 100644 --- a/app/models/published_session_assignment.rb +++ b/app/models/published_session_assignment.rb @@ -5,13 +5,12 @@ class PublishedSessionAssignment < ApplicationRecord include RankedModel ranks :sort_order, with_same: [:published_session_id] + belongs_to :person belongs_to :published_session - # foreign_key: 'session_id' + belongs_to :session_assignment_role_type, required: false belongs_to :session_assignment - has_one :session_assignment_role_type - enum visibility: { is_public: 'public', is_private: 'private' diff --git a/app/serializers/conclar/participant_serializer.rb b/app/serializers/conclar/participant_serializer.rb index 85492d0bb..2fe0eddfa 100644 --- a/app/serializers/conclar/participant_serializer.rb +++ b/app/serializers/conclar/participant_serializer.rb @@ -11,45 +11,40 @@ class Conclar::ParticipantSerializer < ActiveModel::Serializer attribute :prog do res = [] - moderator = SessionAssignmentRoleType.find_by(name: 'Moderator') - participant = SessionAssignmentRoleType.find_by(name: 'Participant') + # TODO: change when we have publish process ??? + if object.published_session_assignments.count > 0 + moderator = SessionAssignmentRoleType.find_by(name: 'Moderator') + participant = SessionAssignmentRoleType.find_by(name: 'Participant') + + object.published_session_assignments.each do |assignment| + next if assignment.session_assignment_role_type_id != moderator.id and assignment.session_assignment_role_type_id != participant.id + + res << assignment.published_session_id + end + else + object.sessions.publishable.each do |session| + res << session.id + end + end + + res + end - object.session_assignments.each do |assignment| - next if assignment.session_assignment_role_type_id != moderator.id and assignment.session_assignment_role_type_id != participant.id + attribute :links do + res = {} - res << assignment.session_id - end + res['twitter'] = "https://twitter.com/#{object.twitter}" unless object.twitter.blank? + res['facebook'] = "https://facebook.com/#{object.facebook}" unless object.facebook.blank? + res['website'] = object.website unless object.website.blank? + res['instagram'] = "https://instagram.com/#{object.instagram}" unless object.instagram.blank? + res['twitch'] = "https://twitch.tv/#{object.twitch}" unless object.twitch.blank? + res['youtube'] = "https://youtube.com/#{object.youtube}" unless object.youtube.blank? + res['tiktok'] = "https://www.tiktok.com/@#{object.tiktok}" unless object.tiktok.blank? + res['linkedin'] = "https://linkedin.com/in/#{object.linkedin}" unless object.linkedin.blank? + res['othersocialmedia'] = object.othersocialmedia unless object.othersocialmedia.blank? res end - # links ???? - # what about social media URLs? # tags - not supported yet end - -# All participants on the schedule ... -# var people = [ -# { -# "id": "4567", -# "name": [ "Friend Andhis Jr." ], -# "sortname" : "Andhis Jr., Friend", -# "tags": [], -# "prog": [ "1234", "614", "801" ], -# "links": [], -# "bio": "Prior art for Adams's satirical point – that humans attach such importance to their automobiles that a visiting extraterrestrial might reasonably mistake them for the planet's dominant life form – can be found in a widely reprinted article from <i>The Rockefeller Institute Review</i> titled <i>Life on Earth (by a Martian)</i> by Paul Weiss. The idea was also expounded by Carl Sagan, though this may have postdated Adams's creation of the character of Ford. The 1967 Oscar-nominated animated film <i>What on Earth!</i> from the National Film Board of Canada is also based on this premise." -# }, -# { -# "id": "1234", -# "name": [ "Galahad", "", "Sir" ], -# "sortname": "Sir Galahad", -# "tags": [ "GoH" ], -# "prog": [ "416" ], -# "links": { -# "img": "/images/galahad.jpg", -# "photo": "/images/galahad.jpg", -# "img_256_url": "/images/galahad.jpg", -# "url": "http://en.wikipedia.org/wiki/Galahad" -# }, -# "bio": "Sir Galahad (/ˈɡæləhæd/; Middle Welsh: Gwalchavad, sometimes referred to as Galeas /ɡəˈliːəs/ or Galath /ˈɡæləθ/), in Arthurian legend, is a knight of King Arthur's Round Table and one of the three achievers of the Holy Grail." -# }, diff --git a/app/serializers/conclar/session_serializer.rb b/app/serializers/conclar/session_serializer.rb index b68c307d2..05b25bebb 100644 --- a/app/serializers/conclar/session_serializer.rb +++ b/app/serializers/conclar/session_serializer.rb @@ -2,7 +2,11 @@ class Conclar::SessionSerializer < ActiveModel::Serializer attributes :title attribute :id do - object.session_id + if object.has_attribute?(:id) + object.id + else + object.session_id + end end attribute :desc do @@ -17,15 +21,14 @@ class Conclar::SessionSerializer < ActiveModel::Serializer attribute :tags do res = [] - # TODO: optimize res.concat object.area_list #.collect(&:name) - # res.concat [object.age_restriction.name] if object.age_restriction - # res.concat [object.environment] if object.environment != 'unknown' # virtual hybrid etc - # - # if object.minors_participation && object.minors_participation.class == Array - # res.concat object.minors_participation - # end + res.concat [object.age_restriction.name] if object.age_restriction + res.concat [object.environment] if object.environment != 'unknown' # virtual hybrid etc + + if object.minors_participation && object.minors_participation.class == Array + res.concat object.minors_participation + end # require_signup ???? # recordrd ??? diff --git a/app/services/reports_service.rb b/app/services/reports_service.rb index dab20d3ba..d1e4d44e3 100644 --- a/app/services/reports_service.rb +++ b/app/services/reports_service.rb @@ -106,44 +106,6 @@ def self.scheduled_session_no_people # .order(:start_time) end - # Get all the schedule sessions - def self.scheduled_sessions - PublishedSession.select( - ::PublishedSession.arel_table[Arel.star], - 'areas_list.area_list' - ) - .includes(:format, :room, {participant_assignments: :person}) - .joins(self.published_area_subquery) - .order(:start_time) - # .where("start_time is not null and room_id is not null") - # .where("status != 'dropped' and status != 'draft'") - # Session.select( - # ::Session.arel_table[Arel.star], - # 'areas_list.area_list' - # ) - # .includes(:format, :room, {participant_assignments: :person}) - # .joins(self.area_subquery) - # .where("start_time is not null and room_id is not null") - # .where("status != 'dropped' and status != 'draft'") - # .order(:start_time) - end - - def self.scheduled_people - moderator = SessionAssignmentRoleType.find_by(name: 'Moderator') - participant = SessionAssignmentRoleType.find_by(name: 'Participant') - - people = Person.includes( - {session_assignments: [:session, :session_assignment_role_type]} - ).references( - {session_assignments: :session} - ) - .where("session_assignments.session_assignment_role_type_id in (?)", [moderator.id, participant.id]) - .where("sessions.start_time is not null and sessions.room_id is not null") - .where("sessions.status != 'dropped' and sessions.status != 'draft'") - .where("people.con_state not in (?)", ['declined', 'rejected']) #.distinct - .order("people.published_name") - end - def self.sessions_with_no_moderator sched_table = PersonSchedule.arel_table session_and_roles = sched_table.project( @@ -341,20 +303,6 @@ def self.area_subquery ] end - def self.published_area_subquery - session_table = PublishedSession.arel_table - areas_list = PublishedSession.area_list.as('areas_list') - [ - session_table.create_join( - areas_list, - session_table.create_on( - areas_list[:session_id].eq(session_table[:session_id]) - ), - Arel::Nodes::OuterJoin - ) - ] - end - def self.sessions_and_participant_count format = Format.find_by(name: 'Panel') active_roles = SessionAssignmentRoleType.where("role_type = 'participant' and (name != 'Invisible' and name != 'Reserve')") diff --git a/app/services/session_service.rb b/app/services/session_service.rb index 897c7b60c..2f33798cf 100644 --- a/app/services/session_service.rb +++ b/app/services/session_service.rb @@ -28,4 +28,98 @@ def self.draft_schedule_for(person:, current_person: nil) PersonScheduleSerializer.new(schedule).serializable_hash end + + # Get all the schedule sessions + # ActiveModel::Serializer::CollectionSerializer.new( + # sessions, + # serializer: Conclar::SessionSerializer + # ) + def self.scheduled_sessions + # TODO: when publish process done change condition + if PublishedSession.count > 0 + self.published_sessions + else + self.live_sessions + end + end + + # ActiveModel::Serializer::CollectionSerializer.new( + # participants, + # serializer: Conclar::ParticipantSerializer + # ), + def self.scheduled_people + # TODO: when publish process done change condition + if PublishedSession.count > 0 + self.published_people + else + self.live_people + end + end + + def self.published_sessions + PublishedSession.select( + ::PublishedSession.arel_table[Arel.star], + 'areas_list.area_list' + ) + .includes(:format, :room, {participant_assignments: :person}) + .joins(self.area_subquery(clazz: PublishedSession)) + end + + def self.live_sessions + Session.select( + ::Session.arel_table[Arel.star], + 'areas_list.area_list' + ) + .includes(:format, :room, {participant_assignments: :person}) + .joins(self.area_subquery) + .where("start_time is not null and room_id is not null") + .where("status != 'dropped' and status != 'draft'") + .order(:start_time) + end + + def self.published_people + moderator = SessionAssignmentRoleType.find_by(name: 'Moderator') + participant = SessionAssignmentRoleType.find_by(name: 'Participant') + + people = Person.includes( + {published_session_assignments: [:published_session, :session_assignment_role_type]} + ).references( + {published_session_assignments: :published_session} + ) + .where("published_session_assignments.session_assignment_role_type_id in (?)", [moderator.id, participant.id]) + .where("published_sessions.start_time is not null and published_sessions.room_id is not null") + .where("people.con_state not in (?)", ['declined', 'rejected']) #.distinct + .order("people.published_name") + end + + def self.live_people + moderator = SessionAssignmentRoleType.find_by(name: 'Moderator') + participant = SessionAssignmentRoleType.find_by(name: 'Participant') + + people = Person.includes( + {session_assignments: [:session, :session_assignment_role_type]} + ).references( + {session_assignments: :session} + ) + .where("session_assignments.session_assignment_role_type_id in (?)", [moderator.id, participant.id]) + .where("sessions.start_time is not null and sessions.room_id is not null") + .where("sessions.status != 'dropped' and sessions.status != 'draft'") + .where("people.con_state not in (?)", ['declined', 'rejected']) #.distinct + .order("people.published_name") + end + + def self.area_subquery(clazz: Session) + session_table = clazz.arel_table + areas_list = clazz.area_list.as('areas_list') + id = (clazz == Session) ? :id : :session_id + [ + session_table.create_join( + areas_list, + session_table.create_on( + areas_list[id].eq(session_table[id]) + ), + Arel::Nodes::OuterJoin + ) + ] + end end diff --git a/db/migrate/20220726130346_add_fields_to_published_session.rb b/db/migrate/20220726130346_add_fields_to_published_session.rb new file mode 100644 index 000000000..b6fc5dd1e --- /dev/null +++ b/db/migrate/20220726130346_add_fields_to_published_session.rb @@ -0,0 +1,6 @@ +class AddFieldsToPublishedSession < ActiveRecord::Migration[6.1] + def change + add_column :published_sessions, :environment, :session_environments_enum, default: 'unknown' + add_column :published_sessions, :minors_participation, :jsonb + end +end diff --git a/db/structure.sql b/db/structure.sql index 6ca2c6e23..676e525eb 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -1205,7 +1205,7 @@ CREATE VIEW public.person_back_to_back_to_back AS psc2.conflict_room_id, psc2.id AS conflict_b2b_id FROM (public.person_back_to_back psc1 - JOIN public.person_back_to_back psc2 ON ((psc2.session_id = psc1.conflict_session_id))); + JOIN public.person_back_to_back psc2 ON (((psc2.session_id = psc1.conflict_session_id) AND (psc2.person_id = psc1.person_id)))); -- @@ -1398,7 +1398,9 @@ CREATE TABLE public.published_sessions ( room_id uuid, visibility public.visibility_enum DEFAULT 'public'::public.visibility_enum, require_signup boolean DEFAULT false, - waiting_list_size integer DEFAULT 0 + waiting_list_size integer DEFAULT 0, + environment public.session_environments_enum DEFAULT 'unknown'::public.session_environments_enum, + minors_participation jsonb ); @@ -3333,6 +3335,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20220713185329'), ('20220714124643'), ('20220714124706'), -('20220719000644'); +('20220719000644'), +('20220726130346'); From b351e23eec2f9858f0ffde5e93c037c7bc59f0a5 Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Tue, 26 Jul 2022 10:58:41 -0400 Subject: [PATCH 18/50] never use test in prod --- app/services/session_service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/services/session_service.rb b/app/services/session_service.rb index 2f33798cf..64b4f5459 100644 --- a/app/services/session_service.rb +++ b/app/services/session_service.rb @@ -36,7 +36,7 @@ def self.draft_schedule_for(person:, current_person: nil) # ) def self.scheduled_sessions # TODO: when publish process done change condition - if PublishedSession.count > 0 + if PublishedSession.count > 0 || Rails.env.production? self.published_sessions else self.live_sessions @@ -49,7 +49,7 @@ def self.scheduled_sessions # ), def self.scheduled_people # TODO: when publish process done change condition - if PublishedSession.count > 0 + if PublishedSession.count > 0 || Rails.env.production? self.published_people else self.live_people From 9aacefc6fbb0636e4972d2c44a1b4a626ad2d11a Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Tue, 26 Jul 2022 15:55:25 -0400 Subject: [PATCH 19/50] moved sessions to new service --- app/controllers/publications_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/publications_controller.rb b/app/controllers/publications_controller.rb index 52043fdba..ffae9d920 100644 --- a/app/controllers/publications_controller.rb +++ b/app/controllers/publications_controller.rb @@ -2,7 +2,7 @@ class PublicationsController < ApplicationController around_action :set_timezone def schedule - sessions = ReportsService.scheduled_sessions + sessions = SessionService.live_sessions send_data XmlFormatter.new(sessions).render('schedule', sessions) .gsub(/\<\?xml version="1\.0"\?\>\n/, '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><!-- Formatted for the special requirements of importing to Adobe InDesign. -->') From 17bf46ce053928378b7e02ad1313f6c60cb8ab4e Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Wed, 27 Jul 2022 08:32:02 -0400 Subject: [PATCH 20/50] fix typo --- app/services/session_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/session_service.rb b/app/services/session_service.rb index 64b4f5459..0103057ca 100644 --- a/app/services/session_service.rb +++ b/app/services/session_service.rb @@ -116,7 +116,7 @@ def self.area_subquery(clazz: Session) session_table.create_join( areas_list, session_table.create_on( - areas_list[id].eq(session_table[id]) + areas_list[:session_id].eq(session_table[id]) ), Arel::Nodes::OuterJoin ) From 91e7a2aa7fab11bbb073e95ce2a181fb92c3d5d4 Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Wed, 27 Jul 2022 10:32:45 -0400 Subject: [PATCH 21/50] plan-611 schedule URL variable for email template --- app/controllers/mailings_controller.rb | 4 +++- app/services/mail_service.rb | 5 ++++- app/services/session_service.rb | 9 +++++++++ app/workers/mailing_worker.rb | 7 ++++++- public/ckeditor/plugins/planobuttons/plugin.js | 5 +++++ 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/app/controllers/mailings_controller.rb b/app/controllers/mailings_controller.rb index 8cf83995e..1f74df3df 100644 --- a/app/controllers/mailings_controller.rb +++ b/app/controllers/mailings_controller.rb @@ -136,10 +136,12 @@ def preview recipient_address = params[:email] addr = EmailAddress.find_by(email: recipient_address) + participant_schedule_url = SessionService.participant_schedule_url content = MailService.preview_email_content( person: addr.person, - mailing: mailing + mailing: mailing, + participant_schedule_url: participant_schedule_url ) # render_object(content) # TODO: verify ok for content diff --git a/app/services/mail_service.rb b/app/services/mail_service.rb index 14929488d..9df5659ba 100644 --- a/app/services/mail_service.rb +++ b/app/services/mail_service.rb @@ -2,6 +2,7 @@ module MailService def self.send_mailing( person:, mailing:, + participant_schedule_url:, tester: nil ) survey = mailing.survey @@ -10,6 +11,7 @@ def self.send_mailing( { person: person, survey: survey, + participant_schedule_url: participant_schedule_url, survey_url: self.generate_survey_url(survey: survey, person: person), login_url: self.generate_login_url(person: person) } @@ -27,12 +29,13 @@ def self.send_mailing( self.post_mail_assign_survey(person: person, survey: survey) unless tester end - def self.preview_email_content(person:, mailing:) + def self.preview_email_content(person:, mailing:, participant_schedule_url:) self.generate_email_content( mailing.content, { person: person, survey: mailing.survey, + participant_schedule_url: participant_schedule_url, survey_url: self.generate_survey_url(survey: mailing.survey, person: person), login_url: self.generate_login_url(person: person) } diff --git a/app/services/session_service.rb b/app/services/session_service.rb index 0103057ca..bef65bbc2 100644 --- a/app/services/session_service.rb +++ b/app/services/session_service.rb @@ -1,4 +1,13 @@ module SessionService + def self.participant_schedule_url + workflow = ScheduleWorkflow.order("updated_at desc").first + + return UrlService.url_for(path: "/#/profile/draft-schedule") if workflow.state == ScheduleWorkflow.states[:draft] + return UrlService.url_for(path: "/#/profile/schedule") if workflow.state == ScheduleWorkflow.states[:firm] + + return UrlService.url_for(path: '/') + end + # SessionService.draft_schedule_for(person: p) def self.draft_schedule_for(person:, current_person: nil) sched_table = ::PersonSchedule.arel_table diff --git a/app/workers/mailing_worker.rb b/app/workers/mailing_worker.rb index 0bbfb2664..4b889c52b 100644 --- a/app/workers/mailing_worker.rb +++ b/app/workers/mailing_worker.rb @@ -22,6 +22,8 @@ def send_mailing(mailing:) # TODO - if test run then send to the requestor return unless mailing.mailing_state == Mailing.mailing_states[:submitted] # Check just in case this is a dup + participant_schedule_url = SessionService.participant_schedule_url + last_person_index = mailing.last_person_idx mailing.people.each_with_index do |person, idx| next unless (last_person_index == -1) || (idx > last_person_index) @@ -29,7 +31,8 @@ def send_mailing(mailing:) begin MailService.send_mailing( person: person, - mailing: mailing + mailing: mailing, + participant_schedule_url: participant_schedule_url ) # note the last person processes so we can continue from there if job stopped and restarted @@ -59,10 +62,12 @@ def send_test_mail(mailing:, test_address:, tester_id:) addr = EmailAddress.find_by(email: test_address) if addr tester = Person.find tester_id + participant_schedule_url = SessionService.participant_schedule_url MailService.send_mailing( person: addr.person, mailing: mailing, + participant_schedule_url: participant_schedule_url, tester: tester ) end diff --git a/public/ckeditor/plugins/planobuttons/plugin.js b/public/ckeditor/plugins/planobuttons/plugin.js index 5ffc54126..c380261c4 100644 --- a/public/ckeditor/plugins/planobuttons/plugin.js +++ b/public/ckeditor/plugins/planobuttons/plugin.js @@ -136,6 +136,11 @@ CKEDITOR.config.planobuttons = [ html:'<%= person.email %>', title:'Person\'s Primary Email' }, + { + name:'participant_schedule_url', + html:'<%= participant_schedule_url %>', + title:'Participant Schedule URL' + }, { name:'survey_name', html:'<%= survey.name %>', From ad52f84b947d70decd5d1d5da3032c5466ef5a8c Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Wed, 27 Jul 2022 11:48:48 -0400 Subject: [PATCH 22/50] plan-525 bulk status edit for sessionms --- app/controllers/sessions_controller.rb | 23 +++++++ .../components/session_state_selector.vue | 40 ++++++++++++ app/javascript/sessions/session_table.vue | 64 +++++++++++++++++++ app/policies/session_policy.rb | 4 ++ config/routes.rb | 1 + lib/tasks/rbac.rake | 9 ++- 6 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 app/javascript/components/session_state_selector.vue diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index a586dc93c..7c8b4ccfd 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -4,6 +4,29 @@ class SessionsController < ResourceController POLICY_SCOPE_CLASS = 'SessionPolicy::Scope'.freeze # DEFAULT_SORTBY = 'name_sort_by' + # Mass update for the sessions (given ids and params) + def update_all + authorize current_person, policy_class: policy_class + ids = params[:ids] + attrs = params.permit(attrs: {})[:attrs].to_h #permit(:attrs) + + Session.transaction do + # Get all the people with given set of ids and update them + people = Session.where(id: ids).update(attrs) + + # return the updated people back to the caller + options = { + params: { + domain: "#{request.base_url}", + current_person: current_person + } + } + + render json: serializer_class.new(people,options).serializable_hash(), + content_type: 'application/json' + end + end + def express_interest # create a session assignment if there is not already one model_class.transaction do diff --git a/app/javascript/components/session_state_selector.vue b/app/javascript/components/session_state_selector.vue new file mode 100644 index 000000000..9478a7348 --- /dev/null +++ b/app/javascript/components/session_state_selector.vue @@ -0,0 +1,40 @@ +<template> + <b-form-select + v-model="selectedValue" + @change="onChange" + v-bind:options="currentSettings.enums.Session.status" + :disabled='disabled' + ></b-form-select> +</template> + +<script> +import settingsMixin from "@/store/settings.mixin"; + +export default { + name: 'SessionStateSelector', + components: { + }, + mixins: [ + settingsMixin + ], + props: { + value: null, + disabled: false + }, + data: () => ({ + selectedValue: null + // options: [] + }), + methods: { + onChange(arg) { + this.$emit('input', arg) + } + }, + mounted() { + this.selectedValue = this.value + } +} +</script> + +<style lang="scss"> +</style> diff --git a/app/javascript/sessions/session_table.vue b/app/javascript/sessions/session_table.vue index 4565e1883..e95694967 100644 --- a/app/javascript/sessions/session_table.vue +++ b/app/javascript/sessions/session_table.vue @@ -1,13 +1,56 @@ <template> <div> + <modal-form + title="Bulk Edit Status" + ref="mass-edit-state" + @save="onConfirmMassEdit" + > + <b-form> + <session-state-selector + v-model="selectedSessionState" + ></session-state-selector> + </b-form> + <template #footer="{ ok, cancel }"> + <b-button variant="link" @click="cancel()">Cancel</b-button> + <b-button variant="primary" @click="ok()">Save</b-button> + </template> + </modal-form> + + <modal-form + title="Bulk Edit Status Confirmation" + ref="mass-edit-confirm" + @save="onSaveMassEdit" + > + <p> + Please confirm that you want to change the + status of {{editableIds.length}} {{editableIds.length == 1 ? 'session' : 'sessions'}} to '{{selectedSessionState}}' + </p> + <template #footer="{ ok, cancel }"> + <b-button variant="link" @click="cancel()">Cancel</b-button> + <b-button variant="primary" @click="ok()">Save</b-button> + </template> + </modal-form> + <table-vue @new="openNewModal" defaultSortBy='sessions.title' :model="model" :columns="columns" stateName="session-table-search-state" + selectMode='multi' ref="sessions-table" > + <template v-slot:left-controls="{ editableIds }"> + <div> + <b-button + variant="primary" + @click="onEditStates(editableIds)" + :disabled="editableIds.length == 0" + >Edit Status(es) + </b-button> + </div> + </template> + <template #cell(title)="{ item }"> <tooltip-overflow v-if="item.title" :title="item.title"> <span v-html="item.title"></span> @@ -58,6 +101,10 @@ import dateTimeMixin from '../components/date_time.mixin' import { areaMixin } from './session_fields.mixin'; import PlanoModal from '@/components/plano_modal.vue'; import { mapActions } from 'vuex'; +import { SESSION_STATUS, SESSION_MUST_UNSCHEDULE } from '@/constants/strings'; +import modelUtilsMixin from "@/store/model_utils.mixin"; + +import SessionStateSelector from '../components/session_state_selector' export default { name: 'SessionTable', @@ -66,14 +113,19 @@ export default { TooltipOverflow, ModalForm, PlanoModal, + SessionStateSelector }, mixins: [ dateTimeMixin, + modelUtilsMixin, areaMixin ], data: () => ({ + SESSION_STATUS, columns, model, + editableIds: [], + selectedSessionState: null, newSessionTitle: null }), methods: { @@ -89,6 +141,18 @@ export default { this.$router.push(`/sessions/edit/${data.id}`) }) }, + onSaveMassEdit() { + if (this.editableIds.length > 0 && this.selectedSessionState) { + this.update_all('session', this.editableIds, {status: this.selectedSessionState}) + } + }, + onConfirmMassEdit() { + this.$refs['mass-edit-confirm'].showModal() + }, + onEditStates(ids) { + this.editableIds = ids + this.$refs['mass-edit-state'].showModal() + }, onSave() { } }, diff --git a/app/policies/session_policy.rb b/app/policies/session_policy.rb index 247c46d97..1538c44bf 100644 --- a/app/policies/session_policy.rb +++ b/app/policies/session_policy.rb @@ -3,6 +3,10 @@ def delete_snapshot? !Rails.env.production? && allowed?(action: :delete_snapshot) end + def update_all? + allowed?(action: :update_all) + end + def take_snapshot? allowed?(action: :take_snapshot) end diff --git a/config/routes.rb b/config/routes.rb index 42eaf0066..664218885 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -139,6 +139,7 @@ get 'session/tags', to: 'sessions#tags' post 'session/import', to: 'sessions#import' + post 'session/update_all', to: 'sessions#update_all' # get sessions/assigned_id - &include=session_assignments&filter[session_assignments][person_id]=person_id resources :sessions, path: 'session' do get 'session_assignments', to: 'session_assignments#index' diff --git a/lib/tasks/rbac.rake b/lib/tasks/rbac.rake index dd5303575..63b3e380b 100644 --- a/lib/tasks/rbac.rake +++ b/lib/tasks/rbac.rake @@ -154,7 +154,8 @@ namespace :rbac do "destroy": false, "index": true, "show": true, - "update": false + "update": false, + "update_all": false }, "format": { "create": false, @@ -409,7 +410,8 @@ namespace :rbac do "destroy": true, "index": true, "show": true, - "update": true + "update": true, + "update_all": true }, "format": { "create": true, @@ -702,7 +704,8 @@ namespace :rbac do "destroy": true, "index": true, "show": true, - "update": true + "update": true, + "update_all": true }, "format": { "create": true, From 47960ba613cc5a6e9f04274be723d1181923cee7 Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Wed, 27 Jul 2022 12:04:25 -0400 Subject: [PATCH 23/50] fix serializer includes --- app/controllers/sessions_controller.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 7c8b4ccfd..2d4786d1d 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -16,6 +16,7 @@ def update_all # return the updated people back to the caller options = { + include: serializer_includes, params: { domain: "#{request.base_url}", current_person: current_person From d0978cf9ace7c5664123e30ae90af64429d30a98 Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Thu, 28 Jul 2022 08:57:37 -0400 Subject: [PATCH 24/50] put in missing person and mailing into history --- app/services/mail_service.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/services/mail_service.rb b/app/services/mail_service.rb index 14929488d..60a7bf00f 100644 --- a/app/services/mail_service.rb +++ b/app/services/mail_service.rb @@ -20,7 +20,9 @@ def self.send_mailing( subject: mailing.subject, title: mailing.title, content: content, - is_test: tester != nil + is_test: tester != nil, + person: person, + mailing: mailing ) self.post_mail_transition(person: person, mailing: mailing) unless tester From 2a38655e6f068bedb24c256d84d8c28ed9c94c20 Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Thu, 28 Jul 2022 10:04:31 -0400 Subject: [PATCH 25/50] task to fix missing person ids by matching emails --- lib/tasks/mailing.rake | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 lib/tasks/mailing.rake diff --git a/lib/tasks/mailing.rake b/lib/tasks/mailing.rake new file mode 100644 index 000000000..5522676c7 --- /dev/null +++ b/lib/tasks/mailing.rake @@ -0,0 +1,17 @@ +desc "Fix missing person from mail history" + +namespace :mail do + task fix_history: :environment do + MailHistory.all.each do |history| + email = JSON.parse(history.email).first + addr = EmailAddress.where("lower(email) = ? and isdefault = true", email.downcase).first + addr = EmailAddress.where("lower(email) = ?", email.downcase).first unless addr + puts "** No person for email: #{email}" unless addr + next unless addr + + history.person_id = addr.person_id unless history.person_id + history.save!(touch: false) + # break + end + end +end From adb4a802792aa20da5754a21c5a359b9bbcfe9a4 Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Thu, 28 Jul 2022 10:09:50 -0400 Subject: [PATCH 26/50] adjust test --- lib/tasks/mailing.rake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/tasks/mailing.rake b/lib/tasks/mailing.rake index 5522676c7..523c42c49 100644 --- a/lib/tasks/mailing.rake +++ b/lib/tasks/mailing.rake @@ -9,8 +9,10 @@ namespace :mail do puts "** No person for email: #{email}" unless addr next unless addr - history.person_id = addr.person_id unless history.person_id - history.save!(touch: false) + if !history.person_id + history.person_id = addr.person_id + history.save!(touch: false) + end # break end end From b4a34b7bf26afd24e37db824311c1078f4933e71 Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Thu, 28 Jul 2022 10:41:24 -0400 Subject: [PATCH 27/50] plan-422 sort mailings by update date --- app/controllers/mailings_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/mailings_controller.rb b/app/controllers/mailings_controller.rb index 1f74df3df..6bae1f9d1 100644 --- a/app/controllers/mailings_controller.rb +++ b/app/controllers/mailings_controller.rb @@ -1,7 +1,7 @@ class MailingsController < ResourceController SERIALIZER_CLASS = 'MailingSerializer'.freeze POLICY_CLASS = 'MailingPolicy'.freeze - DEFAULT_SORTBY = 'title'.freeze + DEFAULT_SORTBY = 'updated_at desc'.freeze def serializer_includes [ From d3735f61d5d75d6ae6f49f0795f9a627cc98debb Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Thu, 28 Jul 2022 12:53:22 -0400 Subject: [PATCH 28/50] WIP --- app/controllers/mail_histories_controller.rb | 36 +++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/app/controllers/mail_histories_controller.rb b/app/controllers/mail_histories_controller.rb index 602e63e42..39f97e32d 100644 --- a/app/controllers/mail_histories_controller.rb +++ b/app/controllers/mail_histories_controller.rb @@ -1,3 +1,37 @@ class MailHistoriesController < ResourceController -# TBD + MODEL_CLASS = 'MailHistory'.freeze + # TODO: the classes below + SERIALIZER_CLASS = 'MailHistorySerializer'.freeze + POLICY_CLASS = 'MailHistoryPolicy'.freeze + POLICY_SCOPE_CLASS = 'MailHistoryPolicy::Scope'.freeze + + def belongs_to_param_id + params[:person_id] + end + + def belong_to_class + Person + end + + def belongs_to_relationship + 'mail_histories' + end + + def paginate + false + end + + # Should be read only + # def allowed_params + # %i[ + # id + # lock_version + # person_id + # mailing_id + # email_status + # date_sent + # content + # subject + # ] + # end end From c78cf9981221f4af5c7a0dccd080650d9e679d2f Mon Sep 17 00:00:00 2001 From: ralphlevan <ralphlevan@gmail.com> Date: Thu, 28 Jul 2022 14:11:27 -0400 Subject: [PATCH 29/50] PLAN-589 Tooltips in the table component were changed as requested --- app/javascript/components/table_vue.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/components/table_vue.vue b/app/javascript/components/table_vue.vue index 64f64af96..bd6d6c56d 100644 --- a/app/javascript/components/table_vue.vue +++ b/app/javascript/components/table_vue.vue @@ -27,12 +27,12 @@ <div class="d-flex justify-content-end"> <div class="d-inline mx-1" title="clone" v-if="showClone"> - <b-button @click="$emit('clone')" variant="primary" title="clone" :disabled='selected_items.length===0' > + <b-button @click="$emit('clone')" variant="primary" title="Duplicate" :disabled='selected_items.length===0' > <b-icon-files></b-icon-files> </b-button> </div> <div class="d-inline mx-1" title="refresh" v-if="showRefresh"> - <b-button @click="onRefresh" variant="primary" title="refresh"> + <b-button @click="onRefresh" variant="primary" title="Refresh"> <b-icon-arrow-repeat></b-icon-arrow-repeat> </b-button> </div> From 1ab5bcad1463912b1971cc1cb9bb01c3aa2ea909 Mon Sep 17 00:00:00 2001 From: ralphlevan <ralphlevan@gmail.com> Date: Thu, 28 Jul 2022 14:43:37 -0400 Subject: [PATCH 30/50] PLAN-393 Button icon changed to globe2 --- app/javascript/surveys/survey_sidebar.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/surveys/survey_sidebar.vue b/app/javascript/surveys/survey_sidebar.vue index 7f3e8cd92..3c0356105 100644 --- a/app/javascript/surveys/survey_sidebar.vue +++ b/app/javascript/surveys/survey_sidebar.vue @@ -19,7 +19,7 @@ </b-col> </b-row> <div class="float-right d-flex justify-content-end"> - <icon-button title="Survey Link" :href="surveyLink" target="_blank" icon="link45deg"></icon-button> + <icon-button title="Survey Link" :href="surveyLink" target="_blank" icon="globe2"></icon-button> <icon-button title="Preview Survey" :href="previewLink" target="_blank" icon="eye-fill"></icon-button> <icon-button title="Edit Survey" :to="editLink" :disabled="survey.public" :disabledTooltip="SURVEY_PUBLIC_NO_EDIT" icon="pencil"></icon-button> <icon-button icon="envelope" disabled disabledTooltip="Send Survey"></icon-button> From f405c8e83779ebf5bb1266133621fc770be944b7 Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Thu, 28 Jul 2022 14:56:14 -0400 Subject: [PATCH 31/50] support for mail history endpoint --- app/controllers/mail_histories_controller.rb | 17 ++------------- app/policies/mail_history_policy.rb | 23 ++++++++++++++++++++ app/serializers/mail_history_serializer.rb | 8 ++++--- 3 files changed, 30 insertions(+), 18 deletions(-) create mode 100644 app/policies/mail_history_policy.rb diff --git a/app/controllers/mail_histories_controller.rb b/app/controllers/mail_histories_controller.rb index 39f97e32d..1c8b450a3 100644 --- a/app/controllers/mail_histories_controller.rb +++ b/app/controllers/mail_histories_controller.rb @@ -1,9 +1,10 @@ class MailHistoriesController < ResourceController MODEL_CLASS = 'MailHistory'.freeze - # TODO: the classes below SERIALIZER_CLASS = 'MailHistorySerializer'.freeze POLICY_CLASS = 'MailHistoryPolicy'.freeze POLICY_SCOPE_CLASS = 'MailHistoryPolicy::Scope'.freeze + DEFAULT_SORTBY = 'date_sent'.freeze + DEFAULT_ORDER = 'desc'.freeze def belongs_to_param_id params[:person_id] @@ -20,18 +21,4 @@ def belongs_to_relationship def paginate false end - - # Should be read only - # def allowed_params - # %i[ - # id - # lock_version - # person_id - # mailing_id - # email_status - # date_sent - # content - # subject - # ] - # end end diff --git a/app/policies/mail_history_policy.rb b/app/policies/mail_history_policy.rb new file mode 100644 index 000000000..3b69ac35b --- /dev/null +++ b/app/policies/mail_history_policy.rb @@ -0,0 +1,23 @@ +class MailHistoryPolicy < PlannerPolicy + def create? + false + end + + def update? + false + end + + def destroy? + false + end + + class Scope < PlannerPolicy::Scope + def resolve + if allowed?(action: :index) + scope.all + else + scope.where(person_id: @person.id) + end + end + end +end diff --git a/app/serializers/mail_history_serializer.rb b/app/serializers/mail_history_serializer.rb index 746d0a72e..3f3562566 100644 --- a/app/serializers/mail_history_serializer.rb +++ b/app/serializers/mail_history_serializer.rb @@ -1,13 +1,15 @@ class MailHistorySerializer include JSONAPI::Serializer - # set id? attributes :id, :lock_version, - :email_status, :date_sent, :email, + :email_status, :date_sent, :content, :testrun, :subject, :created_at, :updated_at - # TODO + # This is because the email can be an array of emails + attribute :email do |object| + JSON.parse object.email + end # belongs_to :person_mailing_assignment # belongs_to :person # belongs_to :mailing From 1bc6fd02ae1a961e20273133ee2d8fc63ea86cd2 Mon Sep 17 00:00:00 2001 From: Gail Terman <gterman@gmail.com> Date: Fri, 29 Jul 2022 11:01:18 -0400 Subject: [PATCH 32/50] PLAN-525 refactor bulk edit modals --- app/javascript/components/bulk_edit_modal.vue | 77 +++++++++++++++++++ app/javascript/components/edit_modal.vue | 8 +- .../components/person_con_state_selector.vue | 26 ++----- app/javascript/components/plano_modal.vue | 12 ++- .../components/session_state_selector.vue | 40 ---------- app/javascript/people/people_table.vue | 56 +++++--------- .../sessions/session_fields.mixin.js | 12 +++ app/javascript/sessions/session_table.vue | 58 +++++--------- 8 files changed, 153 insertions(+), 136 deletions(-) create mode 100644 app/javascript/components/bulk_edit_modal.vue delete mode 100644 app/javascript/components/session_state_selector.vue diff --git a/app/javascript/components/bulk_edit_modal.vue b/app/javascript/components/bulk_edit_modal.vue new file mode 100644 index 000000000..ecf65d9a7 --- /dev/null +++ b/app/javascript/components/bulk_edit_modal.vue @@ -0,0 +1,77 @@ +<template> + <div> + <edit-modal + v-bind="$attrs" + :id="id" + :title="title" + @cancel="$emit('cancel', $event)" + @close="$emit('close', $event)" + @ok="confirm" + ok-title="Confirm" + no-stacking + > + <slot v-for="(_, name) in modalSlots" :name="name" :slot="name"></slot> + <template v-for="(_, name) in modalScopedSlots" :slot="name" slot-scope="slotData"><slot :name="name" v-bind="slotData"></slot></template> + </edit-modal> + <edit-modal + v-on="$listeners" + :id="confirmId" + @cancel="$emit('cancel', $event)" + @close="$emit('close', $event)" + :title="confirmTitle" + > + <slot v-for="(_, name) in confirmSlots" :name="`confirm-${name}`" :slot="name"></slot> + <template v-for="(_, name) in confirmScopedSlots" :slot="name" slot-scope="slotData"><slot :name="`confirm-${name}`" v-bind="slotData"></slot></template> + </edit-modal> + </div> +</template> + +<script> +import EditModal from './edit_modal'; + +export default { + name: "BulkEditModal", + props: { + id: { + type: String, + default: 'bulk-edit' + }, + title: { + type: String, + default: "Bulk Edit" + } + }, + components: { + EditModal + }, + computed: { + confirmId() { + return `${this.id}-confirm` + }, + confirmTitle() { + return `${this.title} Confirmation` + }, + modalSlots() { + return Object.fromEntries(Object.entries(this.$slots).filter(([name, _]) => !name.startsWith('confirm'))) + }, + confirmSlots() { + return Object.fromEntries(Object.entries(this.$slots).filter(([name, _]) => name.startsWith('confirm')).map(([name, val]) => [name.replace(/confirm-/, ''), val])) + }, + modalScopedSlots() { + return Object.fromEntries(Object.entries(this.$scopedSlots).filter(([name, _]) => !name.startsWith('confirm'))) + }, + confirmScopedSlots() { + return Object.fromEntries(Object.entries(this.$scopedSlots).filter(([name, _]) => name.startsWith('confirm')).map(([name, val]) => [name.replace(/confirm-/,''), val])) + } + }, + methods: { + confirm() { + this.$bvModal.show(this.confirmId) + } + } +} +</script> + +<style> + +</style> diff --git a/app/javascript/components/edit_modal.vue b/app/javascript/components/edit_modal.vue index 483bdb879..582d62197 100644 --- a/app/javascript/components/edit_modal.vue +++ b/app/javascript/components/edit_modal.vue @@ -1,7 +1,7 @@ <template> <plano-modal no-close-on-backdrop - ok-title="Save" + :ok-title="okTitle" v-on="$listeners" v-bind="$attrs" > @@ -15,6 +15,12 @@ import PlanoModal from './plano_modal'; export default { name: "EditModal", + props: { + okTitle: { + type: String, + default: 'Save' + } + }, components: { PlanoModal }, diff --git a/app/javascript/components/person_con_state_selector.vue b/app/javascript/components/person_con_state_selector.vue index 4282d46e8..4c03a453a 100644 --- a/app/javascript/components/person_con_state_selector.vue +++ b/app/javascript/components/person_con_state_selector.vue @@ -1,15 +1,14 @@ <template> <b-form-select - v-model="selectedValue" - @change="onChange" - v-bind:options="currentSettings.enums.Person.con_state" - :disabled='disabled' + :options="options" + v-bind="$attrs" + v-on="$listeners" ></b-form-select> - <!-- :multiple="multiple" --> </template> <script> import settingsMixin from "@/store/settings.mixin"; +import { PERSON_CON_STATE } from '@/constants/strings'; export default { name: 'PersonConStateSelector', @@ -18,22 +17,11 @@ export default { mixins: [ settingsMixin ], - props: { - value: null, - disabled: false - }, - data: () => ({ - selectedValue: null - // options: [] - }), - methods: { - onChange(arg) { - this.$emit('input', arg) + computed: { + options() { + return this.currentSettings.enums.Person.con_state.map(value => ({text: PERSON_CON_STATE[value], value})) } }, - mounted() { - this.selectedValue = this.value - } } </script> diff --git a/app/javascript/components/plano_modal.vue b/app/javascript/components/plano_modal.vue index 8c6413d06..9905feee3 100644 --- a/app/javascript/components/plano_modal.vue +++ b/app/javascript/components/plano_modal.vue @@ -6,7 +6,7 @@ scrollable v-on="$listeners" v-bind="$attrs" - ref="plano-modal" + :id="id" > <slot v-for="(_, name) in $slots" :name="name" :slot="name" /> <template v-for="(_, name) in $scopedSlots" :slot="name" slot-scope="slotData"><slot :name="name" v-bind="slotData" /></template> @@ -16,12 +16,18 @@ <script> export default { name: "PlanoModal", + props: { + id: { + type: String, + default: 'plano-modal' + } + }, methods: { show() { - this.$refs['plano-modal'].show() + this.$bvModal.show(this.id) }, hide() { - this.$refs['plano-modal'].hide() + this.$bvModal.show(this.id) } } } diff --git a/app/javascript/components/session_state_selector.vue b/app/javascript/components/session_state_selector.vue deleted file mode 100644 index 9478a7348..000000000 --- a/app/javascript/components/session_state_selector.vue +++ /dev/null @@ -1,40 +0,0 @@ -<template> - <b-form-select - v-model="selectedValue" - @change="onChange" - v-bind:options="currentSettings.enums.Session.status" - :disabled='disabled' - ></b-form-select> -</template> - -<script> -import settingsMixin from "@/store/settings.mixin"; - -export default { - name: 'SessionStateSelector', - components: { - }, - mixins: [ - settingsMixin - ], - props: { - value: null, - disabled: false - }, - data: () => ({ - selectedValue: null - // options: [] - }), - methods: { - onChange(arg) { - this.$emit('input', arg) - } - }, - mounted() { - this.selectedValue = this.value - } -} -</script> - -<style lang="scss"> -</style> diff --git a/app/javascript/people/people_table.vue b/app/javascript/people/people_table.vue index 4af0e13fa..26988dfdc 100644 --- a/app/javascript/people/people_table.vue +++ b/app/javascript/people/people_table.vue @@ -1,38 +1,22 @@ <template> <div> - <modal-form - title="Bulk Edit Status" - ref="mass-edit-state" - @save="onConfirmMassEdit" - > - <b-form> - <person-con-state-selector - v-model="selectedConState" - ></person-con-state-selector> - </b-form> - <template #footer="{ ok, cancel }"> - <b-button variant="link" @click="cancel()">Cancel</b-button> - <b-button variant="primary" @click="ok()">Save</b-button> + <bulk-edit-modal title="Bulk Edit Status" id="bulk-edit-status" @ok="onSaveMassEdit"> + <template #default> + <b-form> + <person-con-state-selector + v-model="selectedConState" + ></person-con-state-selector> + </b-form> </template> - </modal-form> - - <modal-form - title="Bulk Edit Status Confirmation" - ref="mass-edit-confirm" - @save="onSaveMassEdit" - > - <p> - Please confirm that you want to change the - status of {{editableIds.length}} {{editableIds.length == 1 ? 'person' : 'people'}} to '{{selectedConState}}' - <span v-if="declinedRejected">and they will be removed from the below sessions.</span> - </p> - <people-session-names :declinedRejected="declinedRejected" :ids="editableIds"></people-session-names> - <template #footer="{ ok, cancel }"> - <b-button variant="link" @click="cancel()">Cancel</b-button> - <b-button variant="primary" @click="ok()">Save</b-button> + <template #confirm-default> + <p> + Please confirm that you want to change the + status of {{editableIds.length}} {{editableIds.length == 1 ? 'person' : 'people'}} to '{{PERSON_CON_STATE[selectedConState]}}' + <span v-if="declinedRejected">and they will be removed from the below sessions.</span> + </p> + <people-session-names :declinedRejected="declinedRejected" :ids="editableIds"></people-session-names> </template> - </modal-form> - + </bulk-edit-modal> <modal-form title="Add Person" @@ -150,8 +134,9 @@ import searchStateMixin from '../store/search_state.mixin' import { formatPersonScheduleApprovalState } from '@/store/person_schedule_approval'; import { FETCH_WORKFLOWS, scheduleWorkflowMixin } from '@/store/schedule_workflow'; import { mapActions } from 'vuex'; -import { PERSON_NEVER_LOGGED_IN } from '@/constants/strings'; +import { PERSON_NEVER_LOGGED_IN, PERSON_CON_STATE } from '@/constants/strings'; import { DateTime } from 'luxon'; +import BulkEditModal from '@/components/bulk_edit_modal.vue' export default { name: 'PeopleTable', @@ -162,6 +147,7 @@ export default { PersonAdd, PersonConStateSelector, PeopleSessionNames, + BulkEditModal, }, mixins: [ modelUtilsMixin, @@ -175,6 +161,7 @@ export default { selectedConState: null, searchEmails: null, PERSON_NEVER_LOGGED_IN, + PERSON_CON_STATE, DateTime }), computed: { @@ -230,12 +217,9 @@ export default { this.update_all('person', this.editableIds, {con_state: this.selectedConState}) } }, - onConfirmMassEdit() { - this.$refs['mass-edit-confirm'].showModal() - }, onEditStates(ids) { this.editableIds = ids - this.$refs['mass-edit-state'].showModal() + this.$bvModal.show('bulk-edit-status') }, onNew() { this.$refs['add-person-modal'].showModal() diff --git a/app/javascript/sessions/session_fields.mixin.js b/app/javascript/sessions/session_fields.mixin.js index f188105ec..2e88599fe 100644 --- a/app/javascript/sessions/session_fields.mixin.js +++ b/app/javascript/sessions/session_fields.mixin.js @@ -1,5 +1,6 @@ import { conventionTimezoneMixin, settingsMixin } from '@/mixins'; import { DateTime } from 'luxon'; +import {SESSION_STATUS} from '@/constants/strings' export const areaMixin = { computed: { @@ -47,3 +48,14 @@ export const startTimeMixin = { }, } } + +export const sessionStatusMixin = { + mixins: [ + settingsMixin + ], + computed: { + sessionStatusOptions() { + return this.currentSettings?.enums?.Session?.status?.map(value => ({text: SESSION_STATUS[value], value})) + } + } +} diff --git a/app/javascript/sessions/session_table.vue b/app/javascript/sessions/session_table.vue index e95694967..1211c8216 100644 --- a/app/javascript/sessions/session_table.vue +++ b/app/javascript/sessions/session_table.vue @@ -1,35 +1,21 @@ <template> <div> - <modal-form - title="Bulk Edit Status" - ref="mass-edit-state" - @save="onConfirmMassEdit" - > - <b-form> - <session-state-selector + <bulk-edit-modal id="bulk-edit-status" title="Bulk Edit Status(es)" @ok="onSaveMassEdit"> + <template #default> + <b-form-select :options="sessionStatusOptions" v-model="selectedSessionState" - ></session-state-selector> - </b-form> - <template #footer="{ ok, cancel }"> - <b-button variant="link" @click="cancel()">Cancel</b-button> - <b-button variant="primary" @click="ok()">Save</b-button> + ></b-form-select> </template> - </modal-form> - - <modal-form - title="Bulk Edit Status Confirmation" - ref="mass-edit-confirm" - @save="onSaveMassEdit" - > - <p> - Please confirm that you want to change the - status of {{editableIds.length}} {{editableIds.length == 1 ? 'session' : 'sessions'}} to '{{selectedSessionState}}' - </p> - <template #footer="{ ok, cancel }"> - <b-button variant="link" @click="cancel()">Cancel</b-button> - <b-button variant="primary" @click="ok()">Save</b-button> + <template #confirm-default> + <p> + Please confirm that you want to change the + status of {{editableIds.length}} {{editableIds.length == 1 ? 'session' : 'sessions'}} to '{{SESSION_STATUS[selectedSessionState]}}' + </p> + <p v-if="selectedSessionState === 'dropped'"> + If any of these sessions are scheduled, this action will unschedule them. + </p> </template> - </modal-form> + </bulk-edit-modal> <table-vue @new="openNewModal" @@ -98,11 +84,12 @@ import TooltipOverflow from '../shared/tooltip-overflow'; import { session_columns as columns } from './session'; import { NEW_SESSION, sessionModel as model } from '@/store/session.store' import dateTimeMixin from '../components/date_time.mixin' -import { areaMixin } from './session_fields.mixin'; +import { areaMixin, sessionStatusMixin } from './session_fields.mixin'; import PlanoModal from '@/components/plano_modal.vue'; import { mapActions } from 'vuex'; import { SESSION_STATUS, SESSION_MUST_UNSCHEDULE } from '@/constants/strings'; import modelUtilsMixin from "@/store/model_utils.mixin"; +import BulkEditModal from '@/components/bulk_edit_modal.vue'; import SessionStateSelector from '../components/session_state_selector' @@ -113,12 +100,14 @@ export default { TooltipOverflow, ModalForm, PlanoModal, - SessionStateSelector + SessionStateSelector, + BulkEditModal }, mixins: [ dateTimeMixin, modelUtilsMixin, - areaMixin + areaMixin, + sessionStatusMixin, ], data: () => ({ SESSION_STATUS, @@ -134,7 +123,7 @@ export default { }), openNewModal() { this.newSessionTitle = null; - this.$root.$emit('bv::show::modal', 'add-session'); + this.$bvModal.show('add-session'); }, onNew() { this.newSession({title: this.newSessionTitle}).then((data) => { @@ -146,15 +135,10 @@ export default { this.update_all('session', this.editableIds, {status: this.selectedSessionState}) } }, - onConfirmMassEdit() { - this.$refs['mass-edit-confirm'].showModal() - }, onEditStates(ids) { this.editableIds = ids - this.$refs['mass-edit-state'].showModal() + this.$bvModal.show('bulk-edit-status') }, - onSave() { - } }, mounted() { this.$refs['sessions-table'].fetchPaged() From 6c7e9332dda158e5eab39dddbe26cb38aee70c0d Mon Sep 17 00:00:00 2001 From: Gail Terman <gterman@gmail.com> Date: Fri, 29 Jul 2022 11:03:44 -0400 Subject: [PATCH 33/50] PLAN-525 remove unnecessary dependencies --- app/javascript/sessions/session_table.vue | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/javascript/sessions/session_table.vue b/app/javascript/sessions/session_table.vue index 1211c8216..9084a62a5 100644 --- a/app/javascript/sessions/session_table.vue +++ b/app/javascript/sessions/session_table.vue @@ -79,7 +79,6 @@ <script> import TableVue from '../components/table_vue'; -import ModalForm from '../components/modal_form'; import TooltipOverflow from '../shared/tooltip-overflow'; import { session_columns as columns } from './session'; import { NEW_SESSION, sessionModel as model } from '@/store/session.store' @@ -91,16 +90,12 @@ import { SESSION_STATUS, SESSION_MUST_UNSCHEDULE } from '@/constants/strings'; import modelUtilsMixin from "@/store/model_utils.mixin"; import BulkEditModal from '@/components/bulk_edit_modal.vue'; -import SessionStateSelector from '../components/session_state_selector' - export default { name: 'SessionTable', components: { TableVue, TooltipOverflow, - ModalForm, PlanoModal, - SessionStateSelector, BulkEditModal }, mixins: [ From bedd980cffc1785cb58deb1c88af39eb41a53524 Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Fri, 29 Jul 2022 12:37:32 -0400 Subject: [PATCH 34/50] plan-422 sort is also defined in the JS also fix condition for testing workflow for sched url --- app/controllers/mailings_controller.rb | 3 ++- app/javascript/mailings/mailings_table.vue | 3 ++- app/services/session_service.rb | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/controllers/mailings_controller.rb b/app/controllers/mailings_controller.rb index 6bae1f9d1..0af35e960 100644 --- a/app/controllers/mailings_controller.rb +++ b/app/controllers/mailings_controller.rb @@ -1,7 +1,8 @@ class MailingsController < ResourceController SERIALIZER_CLASS = 'MailingSerializer'.freeze POLICY_CLASS = 'MailingPolicy'.freeze - DEFAULT_SORTBY = 'updated_at desc'.freeze + DEFAULT_SORTBY = 'updated_at'.freeze + DEFAULT_ORDER = 'desc'.freeze def serializer_includes [ diff --git a/app/javascript/mailings/mailings_table.vue b/app/javascript/mailings/mailings_table.vue index 06f2477d7..5ed6d5c5b 100644 --- a/app/javascript/mailings/mailings_table.vue +++ b/app/javascript/mailings/mailings_table.vue @@ -1,7 +1,8 @@ <template> <div> <table-vue - defaultSortBy='title' + defaultSortBy='updated_at' + defaultSortDesc="true" :model="model" :columns="columns" :defaultFilter="defaultFilter" diff --git a/app/services/session_service.rb b/app/services/session_service.rb index bef65bbc2..1ac735928 100644 --- a/app/services/session_service.rb +++ b/app/services/session_service.rb @@ -2,8 +2,8 @@ module SessionService def self.participant_schedule_url workflow = ScheduleWorkflow.order("updated_at desc").first - return UrlService.url_for(path: "/#/profile/draft-schedule") if workflow.state == ScheduleWorkflow.states[:draft] - return UrlService.url_for(path: "/#/profile/schedule") if workflow.state == ScheduleWorkflow.states[:firm] + return UrlService.url_for(path: "/#/profile/draft-schedule") if workflow && workflow.state == ScheduleWorkflow.states[:draft] + return UrlService.url_for(path: "/#/profile/schedule") if workflow && workflow.state == ScheduleWorkflow.states[:firm] return UrlService.url_for(path: '/') end From f1ef5834b100b79b5f60881bc38a4e1eedc6d41c Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Fri, 29 Jul 2022 12:43:56 -0400 Subject: [PATCH 35/50] do not lazy load assigned surveys for a person, get them wiht the me ... --- app/controllers/people_controller.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/people_controller.rb b/app/controllers/people_controller.rb index 79a9586e7..9b9d059c4 100644 --- a/app/controllers/people_controller.rb +++ b/app/controllers/people_controller.rb @@ -16,7 +16,8 @@ def me :email_addresses, :convention_roles, :unsigned_agreements, - :session_limits + :session_limits, + :assigned_surveys ] ) end From 31200d01bad64e1a2ce6a98d2e57cb90128b5c19 Mon Sep 17 00:00:00 2001 From: ralphlevan <ralphlevan@gmail.com> Date: Fri, 29 Jul 2022 12:34:49 -0700 Subject: [PATCH 36/50] PLAN-603 Created new tooltip component that preserves newlines --- app/javascript/people/people_table.vue | 12 +++--- .../shared/tooltip-overflow-keep-newlines.vue | 37 +++++++++++++++++++ 2 files changed, 44 insertions(+), 5 deletions(-) create mode 100755 app/javascript/shared/tooltip-overflow-keep-newlines.vue diff --git a/app/javascript/people/people_table.vue b/app/javascript/people/people_table.vue index 4af0e13fa..a219d7c6e 100644 --- a/app/javascript/people/people_table.vue +++ b/app/javascript/people/people_table.vue @@ -111,9 +111,9 @@ </template> <template #cell(draft_comments)="{ item }"> <div v-if="draftSchedule"> - <tooltip-overflow :title="comments(item.person_schedule_approvals, 'draft')"> + <tooltip-overflow-keep-newlines :title="comments(item.person_schedule_approvals, 'draft')"> {{ comments(item.person_schedule_approvals, 'draft') }} - </tooltip-overflow> + </tooltip-overflow-keep-newlines> </div> <div v-else class="text-muted text-center"> — </div> </template> @@ -125,9 +125,9 @@ </template> <template #cell(firm_comments)="{ item }"> <div v-if="firmSchedule"> - <tooltip-overflow :title="comments(item.person_schedule_approvals, 'firm')"> + <tooltip-overflow-keep-newlines :title="comments(item.person_schedule_approvals, 'firm')"> {{ comments(item.person_schedule_approvals, 'firm') }} - </tooltip-overflow> + </tooltip-overflow-keep-newlines> </div> <div v-else class="text-muted text-center"> — </div> </template> @@ -139,6 +139,7 @@ import TableVue from '../components/table_vue'; import ModalForm from '../components/modal_form'; import TooltipOverflow from '../shared/tooltip-overflow'; +import TooltipOverflowKeepNewlines from "@/shared/tooltip-overflow-keep-newlines"; import PersonAdd from '../people/person_add.vue'; import { people_columns as columns } from './people'; import { personModel as model } from '@/store/person.store' @@ -156,6 +157,7 @@ import { DateTime } from 'luxon'; export default { name: 'PeopleTable', components: { + TooltipOverflowKeepNewlines, TableVue, TooltipOverflow, ModalForm, @@ -267,7 +269,7 @@ export default { } </script> -<style> +<style lang="scss"> .col-name-field div { width: 8rem; } diff --git a/app/javascript/shared/tooltip-overflow-keep-newlines.vue b/app/javascript/shared/tooltip-overflow-keep-newlines.vue new file mode 100755 index 000000000..c2d14ff43 --- /dev/null +++ b/app/javascript/shared/tooltip-overflow-keep-newlines.vue @@ -0,0 +1,37 @@ +<template> + <!-- Use v-b-tooltip.html to handle cases when the test is html --> + <span v-b-tooltip.html="{customClass: 'truncated-tooltip'}" + :title="title" + class="text-truncate truncated-span" + > + <slot>{{title}}</slot> + </span> +</template> + +<script> +export default { + name: "TooltipOverflowKeepNewlines", + props: { + title: { + type: String, + } + }, +} +</script> + +<style lang="scss"> +.truncated-tooltip { + .tooltip-inner { + max-width: 30rem; + white-space: pre-wrap; + } +} +.truncated-span { + max-width: 15rem; + display: inline-block; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; +} +</style> From b4a4119bab41ae1fbd5b04a6873003c04cbc1dca Mon Sep 17 00:00:00 2001 From: Gail Terman <gterman@gmail.com> Date: Sun, 31 Jul 2022 15:32:45 -0400 Subject: [PATCH 37/50] PLAN-525 remove dropped from bulk edit --- app/javascript/sessions/session_fields.mixin.js | 3 +++ app/javascript/sessions/session_table.vue | 5 +---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/javascript/sessions/session_fields.mixin.js b/app/javascript/sessions/session_fields.mixin.js index 2e88599fe..5d27ed4de 100644 --- a/app/javascript/sessions/session_fields.mixin.js +++ b/app/javascript/sessions/session_fields.mixin.js @@ -56,6 +56,9 @@ export const sessionStatusMixin = { computed: { sessionStatusOptions() { return this.currentSettings?.enums?.Session?.status?.map(value => ({text: SESSION_STATUS[value], value})) + }, + sessionStatusOptionsNoDropped() { + return this.sessionStatusOptions.filter(({value}) => value !== 'dropped') } } } diff --git a/app/javascript/sessions/session_table.vue b/app/javascript/sessions/session_table.vue index 9084a62a5..a9b4c53e5 100644 --- a/app/javascript/sessions/session_table.vue +++ b/app/javascript/sessions/session_table.vue @@ -2,7 +2,7 @@ <div> <bulk-edit-modal id="bulk-edit-status" title="Bulk Edit Status(es)" @ok="onSaveMassEdit"> <template #default> - <b-form-select :options="sessionStatusOptions" + <b-form-select :options="sessionStatusOptionsNoDropped" v-model="selectedSessionState" ></b-form-select> </template> @@ -11,9 +11,6 @@ Please confirm that you want to change the status of {{editableIds.length}} {{editableIds.length == 1 ? 'session' : 'sessions'}} to '{{SESSION_STATUS[selectedSessionState]}}' </p> - <p v-if="selectedSessionState === 'dropped'"> - If any of these sessions are scheduled, this action will unschedule them. - </p> </template> </bulk-edit-modal> From 504a221c17d1b5013c9ec5e57a830838ec7c3a8a Mon Sep 17 00:00:00 2001 From: Gail Terman <gterman@gmail.com> Date: Sun, 31 Jul 2022 18:51:59 -0400 Subject: [PATCH 38/50] PLAN-450 lightweight email tab. --- app/javascript/people/person_tabs.vue | 14 +++-- app/javascript/profile/person_email_tab.vue | 59 +++++++++++++++++++++ 2 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 app/javascript/profile/person_email_tab.vue diff --git a/app/javascript/people/person_tabs.vue b/app/javascript/people/person_tabs.vue index c9c6f6dee..181a397aa 100644 --- a/app/javascript/people/person_tabs.vue +++ b/app/javascript/people/person_tabs.vue @@ -47,13 +47,14 @@ <b-tab title="Draft Schedule" lazy v-if="displayDraftSchedule" :active="tab === 'draft-schedule'"> <person-draft-schedule></person-draft-schedule> </b-tab> + <b-tab title="Emails" lazy v-if="currentUserIsAdmin || currentUserIsStaff" :active="tab === 'email'"> + <people-email-tab></people-email-tab> + </b-tab> <b-tab title="Admin" lazy v-if="currentUserIsAdmin || currentUserIsStaff" :active="tab === 'admin'"> <people-admin-tab></people-admin-tab> </b-tab> <b-tab title="Surveys" disabled lazy> </b-tab> - <b-tab title="Emails" disabled lazy> - </b-tab> </b-tabs> </model-loading-overlay> </div> @@ -69,6 +70,7 @@ import PersonDemographics from '../profile/person_demographics.vue'; import PersonLiveSchedule from '@/profile/person_live_schedule.vue'; import PersonDraftSchedule from '@/profile/person_draft_schedule.vue'; import PeopleAdminTab from './people_admin_tab.vue'; +import PeopleEmailTab from '@/profile/person_email_tab.vue'; import ModelLoadingOverlay from '@/components/model_loading_overlay.vue'; import { personModel } from '@/store/person.store' @@ -100,6 +102,7 @@ export default { PersonLiveSchedule, PersonDraftSchedule, PeopleAdminTab, + PeopleEmailTab, }, mixins: [ personSessionMixin, @@ -120,13 +123,14 @@ export default { 'availability', 'session-selection', 'session-ranking', - 'admin' ] if (this.displayDraftSchedule) { - baseTabs.splice(5, 0, 'draft_schedule') + baseTabs.push('draft_schedule'); } if (this.currentUserIsAdmin || this.currentUserIsStaff || this.firmSchedule) { - baseTabs.splice(5, 0, 'schedule') + baseTabs.splice(5, 0, 'schedule'); + baseTabs.push('email'); + baseTabs.push('admin'); } return baseTabs; }, diff --git a/app/javascript/profile/person_email_tab.vue b/app/javascript/profile/person_email_tab.vue new file mode 100644 index 000000000..cc78c5da8 --- /dev/null +++ b/app/javascript/profile/person_email_tab.vue @@ -0,0 +1,59 @@ +<template> + <div class="container-fluid"> + <div class="row"> + <div class="col"> + <div v-for="mail in fetchedMailings" :key="mail.id" class="mb-2"> + <h5> {{DateTime.fromISO(mail.date_sent).toFormat("DDDD, t ZZZZ")}} </h5> + <dl> + <dt class="font-weight-bold">Subject</dt> + <dd class="ml-2">{{mail.subject}}</dd> + <dt class="font-weight-bold">Body</dt> + <dd class="ml-2" v-html="mail.content"></dd> + </dl> + </div> + </div> + </div> + </div> +</template> + +<script> +import { DateTime } from 'luxon'; +import { mapActions } from 'vuex'; +import { personModel as model } from '@/store/person.store'; +import { modelMixinNoProp } from '@/mixins'; + +export default { + name: 'PersonEmailTab', + data: () => ({ + fetchedMailings: [], + DateTime, + model, + }), + mixins: [ + modelMixinNoProp + ], + methods: { + ...mapActions({ + get: 'jv/get' + }), + fetchMailings() { + this.get(`/person/${this.selected.id}/mail_histories`).then((data) => { + let {_jv, ...filteredMailings} = data; + let sortableMailings = Object.values(filteredMailings); + sortableMailings.sort((a, b) => DateTime.fromISO(b.date_sent) - DateTime.fromISO(a.date_sent)); + this.fetchedMailings = sortableMailings + }); + } + }, + mounted() { + if (this.selected) { + this.fetchMailings(); + } + }, + +} +</script> + +<style> + +</style> From 725bc42a95bdaa1b27d5fb862bc52e09df266c8b Mon Sep 17 00:00:00 2001 From: Gail Terman <gterman@gmail.com> Date: Sun, 31 Jul 2022 18:58:18 -0400 Subject: [PATCH 39/50] PLAN-641 switch space and time on schedule display --- app/javascript/profile/person_schedule_display.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/profile/person_schedule_display.vue b/app/javascript/profile/person_schedule_display.vue index 36749ca91..2fea45ac4 100644 --- a/app/javascript/profile/person_schedule_display.vue +++ b/app/javascript/profile/person_schedule_display.vue @@ -12,7 +12,7 @@ <div class="col-8" :style="heightHelper"> <b-overlay :show="loading" spinner-variant="primary" variant="white" opacity="1"> <schedule-collapse v-for="(session) in orderedSessions" :key="session.id" :id="session.id" v-model="open[session.id]"> - <template #title><span class="larger-text"><strong class="larger-text">{{session.title}}</strong>, {{session.room}}, {{formatStartTime(session)}}</span></template> + <template #title><span class="larger-text"><strong class="larger-text">{{session.title}}</strong>, {{formatStartTime(session)}}, {{session.room}}</span></template> <dl class="indented-dl"> <dt>Title</dt> <dd>{{session.title}}</dd> From aa8dde72738ab587f85d6d3a9764bfb0ba789c84 Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Mon, 1 Aug 2022 14:03:31 -0400 Subject: [PATCH 40/50] publish process & conclar cache --- app/controllers/schedule_controller.rb | 37 ++-- app/models/publication_date.rb | 8 + app/models/publication_status.rb | 10 +- app/models/publish_snapshot.rb | 5 + app/services/publication_service.rb | 190 +++++++++++++----- app/services/session_service.rb | 30 +++ app/workers/publication_worker.rb | 36 ++++ db/migrate/20220801152151_adjust_pub_dates.rb | 15 ++ ...20220801173704_create_publish_snapshots.rb | 11 + db/structure.sql | 36 +++- test/factories/publish_snapshots.rb | 5 + test/models/publish_snapshot_test.rb | 7 + 12 files changed, 308 insertions(+), 82 deletions(-) create mode 100644 app/models/publish_snapshot.rb create mode 100644 app/workers/publication_worker.rb create mode 100644 db/migrate/20220801152151_adjust_pub_dates.rb create mode 100644 db/migrate/20220801173704_create_publish_snapshots.rb create mode 100644 test/factories/publish_snapshots.rb create mode 100644 test/models/publish_snapshot_test.rb diff --git a/app/controllers/schedule_controller.rb b/app/controllers/schedule_controller.rb index 6e34a0750..bb031c505 100644 --- a/app/controllers/schedule_controller.rb +++ b/app/controllers/schedule_controller.rb @@ -4,24 +4,35 @@ class ScheduleController < ApplicationController # 1. If prod always use the published schedule # 2. If staging or dev use published - if no published then use the live for testing - # 3. Put in a cache mechanism (cache can be popultaed as part of the publish) + # 3. cache mechanism (cache can be popultaed as part of the publish) def index - sessions = SessionService.scheduled_sessions + snapshot = PublicationDate.order('created_at desc').first.publish_snapshots.schedules.first - render json: ActiveModel::Serializer::CollectionSerializer.new( - sessions, - serializer: Conclar::SessionSerializer - ), - content_type: 'application/json' + if snapshot + render json: snapshot, content_type: 'application/json' + else + sessions = SessionService.scheduled_sessions + render json: ActiveModel::Serializer::CollectionSerializer.new( + sessions, + serializer: Conclar::SessionSerializer + ), + content_type: 'application/json' + end end def participants - participants = SessionService.scheduled_people + snapshot = PublicationDate.order('created_at desc').first.publish_snapshots.participants.first - render json: ActiveModel::Serializer::CollectionSerializer.new( - participants, - serializer: Conclar::ParticipantSerializer - ), - content_type: 'application/json' + if snapshot + render json: snapshot, content_type: 'application/json' + else + participants = SessionService.scheduled_people + + render json: ActiveModel::Serializer::CollectionSerializer.new( + participants, + serializer: Conclar::ParticipantSerializer + ), + content_type: 'application/json' + end end end diff --git a/app/models/publication_date.rb b/app/models/publication_date.rb index f82b4812a..a2398849f 100644 --- a/app/models/publication_date.rb +++ b/app/models/publication_date.rb @@ -1,2 +1,10 @@ class PublicationDate < ApplicationRecord + has_many :publish_snapshots do + def schedules + where("publish_snapshots.label = 'schedule'") + end + def participants + where("publish_snapshots.label = 'participants'") + end + end end diff --git a/app/models/publication_status.rb b/app/models/publication_status.rb index bff813615..3900eff2d 100644 --- a/app/models/publication_status.rb +++ b/app/models/publication_status.rb @@ -1,11 +1,3 @@ class PublicationStatus < ApplicationRecord - validates_inclusion_of :status, in: %i[inprogress completed] - - def status - read_attribute(:status).to_sym - end - - def status=(value) - write_attribute(:status, value.to_s) - end + validates_inclusion_of :status, in: %w[inprogress completed] end diff --git a/app/models/publish_snapshot.rb b/app/models/publish_snapshot.rb new file mode 100644 index 000000000..5b6001131 --- /dev/null +++ b/app/models/publish_snapshot.rb @@ -0,0 +1,5 @@ +class PublishSnapshot < ApplicationRecord + validates_inclusion_of :label, in: %w[schedule participants] + + belongs_to :publication_date +end diff --git a/app/services/publication_service.rb b/app/services/publication_service.rb index 2746590f3..8918889b9 100644 --- a/app/services/publication_service.rb +++ b/app/services/publication_service.rb @@ -1,12 +1,19 @@ module PublicationService + def self.start_publish_job + pstatus = PublicationStatus.order('created_at desc').first + pstatus = PublicationStatus.new if pstatus == nil + if pstatus.status != 'inprogress' + pstatus.status = 'inprogress' + pstatus.save! + + PublicationWorker.perform_async + end + end + def self.gen_pub_numbers - Session.transaction do - # Get the session elligible for publication, not draft or dropped and must be public - # TODO: if the timestamp of session or assignment is newer then before - sessions = Session - .where("status != 'draft' and status != 'dropped' and visibility = 'public'") - .where("room_id is not null and start_time is not null") + Session.transaction(joinable: false, requires_new: true) do + sessions = publishable_sessions current_ref = 0 sessions.each do |session| @@ -19,75 +26,97 @@ def self.gen_pub_numbers # Publish the schedule # Copy Sessions, and Assignments to Published versions - # PublicationDate, timestamp, newitems, modifieditems, removeditems - # PublicationStatus, status, submit_time - def self.publish - # We only want the public participant roles - Session.transaction do - # Get the session elligible for publication, not draft or dropped and must be public - sessions = Session - .where("status != 'draft' and status != 'dropped' and visibility = 'public'") - .where("room_id is not null and start_time is not null") - - # updated - publish_updated(sessions) - # new - publish_new(sessions) - # dropped - remove_dropped(sessions) + # only deal with sessions and assignments chances since + def self.publish(since: nil) + res = {} + Session.transaction(joinable: false, requires_new: true) do + session_results = self.publish_sessions(sessions: self.publishable_sessions, since: since) + assignment_results = self.publish_assignments(sessions: self.publishable_sessions, since: since) + + res = session_results.merge(assignment_results) end + res end + # POST published - create a cache for the published schedule - def self.publish_new(sessions) - candidates = PublishedSession.where("session_id in (?)", sessions.collect(&:id)) + def self.publish_sessions(sessions:, since:) + candidates = if since + sessions.where("sessions.updated_at >= ?", since) + else + sessions + end + + # updated + updated_sessions = self.publish_updated_sessions(candidates) + # new + new_sessions = self.publish_new_sessions(candidates) + # dropped + dropped_sessions = self.remove_dropped_sessions(sessions) + + { + new_sessions: new_sessions, + updated_sessions: updated_sessions, + dropped_sessions: dropped_sessions + } + end + + def self.publish_assignments(sessions:, since:) + candidates = if since + self.publishable_assignments(sessions: sessions).where("session_assignments.updated_at >= ?", since) + else + self.publishable_assignments(sessions: sessions) + end + + # updated + updated_assignments = self.publish_updated_assignments(candidates) + # new + new_assignments = self.publish_new_assignments(candidates) + # dropped + dropped_assignments = self.remove_dropped_assignments(self.publishable_assignments(sessions: sessions)) + + { + new_assignments: new_assignments, + updated_assignments: updated_assignments, + dropped_assignments: dropped_assignments + } + end + + def self.publish_new_sessions(sessions) + candidates = if PublishedSession.count > 0 + sessions.where("id not in (?)", PublishedSession.all.collect(&:session_id)) + else + sessions + end count = candidates.count - sessions.each do |session| + candidates.each do |session| pub_session = self.publish_session(session: session, update: false) pub_session.save! - - assignments = session.participant_assignments - .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Invisble')") - .where("session_assignments.visibility = 'public'") - assignments.each do |assignment| - pub_assignment = self.publish_assignment(assignment: assignment, update: false) - pub_assignment.save! - end end count end - def self.publish_updated(sessions) + def self.publish_updated_sessions(sessions) + candidates = sessions.where("id in (?)", PublishedSession.all.collect(&:session_id)) + count = candidates.count + + candidates.each do |session| + pub_session = self.publish_session(session: session) + pub_session.save! + end + + count end - # def self.publish_new(sessions) - # sessions.each do |session| - # pub_session = self.publish_session(session: session) - # pub_session.save! - # - # # And for assignments - # assignments = session.participant_assignments - # .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Invisble')") - # .where("session_assignments.visibility = 'public'") - # assignments.each do |assignment| - # # new, update - # pub_assignment = self.publish_assignment(assignment: assignment, published_session: pub_session) - # pub_assignment.save! - # end - # end - # end - - def self.remove_dropped(sessions) + def self.remove_dropped_sessions(sessions) candidates = PublishedSession.where("session_id not in (?)", sessions.collect(&:id)) count = candidates.count candidates.destroy_all count end - # pub_session = PublicationService.publish_session(session: session) - # - # Published the requested session, if the session already has a published + # Published (create or update) the requested session, if the session already has a published # version then we update that otherwise we create one def self.publish_session(session:, update: true) pub_session = PublishedSession.find_by session_id: session.id @@ -108,6 +137,43 @@ def self.publish_session(session:, update: true) pub_session end + # + def self.publish_new_assignments(assignments) + candidates = if PublishedSessionAssignment.count > 0 + assignments.where("id not in (?)", PublishedSessionAssignment.all.collect(&:session_assignment_id)) + else + assignments + end + + count = candidates.count + + candidates.each do |assignment| + pub_assignment = self.publish_assignment(assignment: assignment, update: false) + pub_assignment.save! + end + + count + end + + def self.publish_updated_assignments(assignments) + candidates = assignments.where("id not in (?)", PublishedSessionAssignment.all.collect(&:session_assignment_id)) + count = candidates.count + + candidates.each do |assignment| + pub_assignment = self.publish_assignment(assignment: assignment) + pub_assignment.save! + end + + count + end + + def self.remove_dropped_assignments(assignments) + candidates = PublishedSessionAssignment.where("session_assignment_id not in (?)", assignments.collect(&:id)) + count = candidates.count + candidates.destroy_all + count + end + # Create published versions of the assignments def self.publish_assignment(assignment:, update: true) pub_assignment = PublishedSessionAssignment.find_by session_assignment_id: assignment.id @@ -128,4 +194,18 @@ def self.publish_assignment(assignment:, update: true) pub_assignment end + + # Get the session elligible for publication, not draft or dropped and must be public + def self.publishable_sessions + Session + .where("status != 'draft' and status != 'dropped' and visibility = 'public'") + .where("room_id is not null and start_time is not null") + end + + def self.publishable_assignments(sessions:) + SessionAssignment + .where("session_assignments.session_id in (?)", sessions.collect(&:id)) + .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Invisble' or session_assignment_role_type.name = 'Reserve')") + .where("session_assignments.visibility = 'public'") + end end diff --git a/app/services/session_service.rb b/app/services/session_service.rb index 1ac735928..74888d0b4 100644 --- a/app/services/session_service.rb +++ b/app/services/session_service.rb @@ -52,6 +52,36 @@ def self.scheduled_sessions end end + def self.cache_published_sessions(publication_date:) + sessions = self.published_sessions + + snapshot = ActiveModel::Serializer::CollectionSerializer.new( + sessions, + serializer: Conclar::SessionSerializer + ) #.serializable_hash + + PublishSnapshot.create!( + snapshot: snapshot, + label: 'schedule', + publication_date: publication_date + ) + end + + def self.cache_published_participants(publication_date:) + participants = self.scheduled_people + + snapshot = ActiveModel::Serializer::CollectionSerializer.new( + participants, + serializer: Conclar::ParticipantSerializer + ) #.serializable_hash + + PublishSnapshot.create!( + snapshot: snapshot, + label: 'participants', + publication_date: publication_date + ) + end + # ActiveModel::Serializer::CollectionSerializer.new( # participants, # serializer: Conclar::ParticipantSerializer diff --git a/app/workers/publication_worker.rb b/app/workers/publication_worker.rb new file mode 100644 index 000000000..1330598f6 --- /dev/null +++ b/app/workers/publication_worker.rb @@ -0,0 +1,36 @@ +class PublicationWorker + include Sidekiq::Worker + + def perform + # 0. we need to set the timezone? + # 2. Publish + # 3. Popultaed any caches that we need + pub_date = nil + PublishedSession.transaction do + pub_date = PublicationDate.new + + pub_date.timestamp = DateTime.current + last_pub = PublicationDate.order('timestamp desc').first + last_time = last_pub&.timestamp + + # DO WORK + result = PublicationService.publish(since: last_time) + + pub_date.update(result) + pub_date.save! + + # do we want the counts in this + # if so we need a completed_at time ????? + pstatus = PublicationStatus.order('created_at desc').first + pstatus = PublicationStatus.new if pstatus == nil + pstatus.status = 'completed' + pstatus.save! + end + + # populate caches + if pub_date + SessionService.cache_published_sessions(publication_date: pub_date) + SessionService.cache_published_participants(publication_date: pub_date) + end + end +end diff --git a/db/migrate/20220801152151_adjust_pub_dates.rb b/db/migrate/20220801152151_adjust_pub_dates.rb new file mode 100644 index 000000000..b5d8314ab --- /dev/null +++ b/db/migrate/20220801152151_adjust_pub_dates.rb @@ -0,0 +1,15 @@ +class AdjustPubDates < ActiveRecord::Migration[6.1] + def change + add_column :publication_dates, :new_sessions, :integer, default: 0 + add_column :publication_dates, :updated_sessions, :integer, default: 0 + add_column :publication_dates, :dropped_sessions, :integer, default: 0 + + add_column :publication_dates, :new_assignments, :integer, default: 0 + add_column :publication_dates, :updated_assignments, :integer, default: 0 + add_column :publication_dates, :dropped_assignments, :integer, default: 0 + + remove_column :publication_dates, :newitems, :integer + remove_column :publication_dates, :modifieditems, :integer + remove_column :publication_dates, :removeditems, :integer + end +end diff --git a/db/migrate/20220801173704_create_publish_snapshots.rb b/db/migrate/20220801173704_create_publish_snapshots.rb new file mode 100644 index 000000000..bdf59303d --- /dev/null +++ b/db/migrate/20220801173704_create_publish_snapshots.rb @@ -0,0 +1,11 @@ +class CreatePublishSnapshots < ActiveRecord::Migration[6.1] + def change + create_table :publish_snapshots, id: :uuid do |t| + t.uuid :publication_date_id + t.string :label + t.jsonb :snapshot + + t.timestamps + end + end +end diff --git a/db/structure.sql b/db/structure.sql index 4cff0cd02..9e0d18e24 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -1340,9 +1340,12 @@ CREATE TABLE public.publication_dates ( "timestamp" timestamp without time zone, created_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL, - newitems integer DEFAULT 0, - modifieditems integer DEFAULT 0, - removeditems integer DEFAULT 0 + new_sessions integer DEFAULT 0, + updated_sessions integer DEFAULT 0, + dropped_sessions integer DEFAULT 0, + new_assignments integer DEFAULT 0, + updated_assignments integer DEFAULT 0, + dropped_assignments integer DEFAULT 0 ); @@ -1360,6 +1363,20 @@ CREATE TABLE public.publication_statuses ( ); +-- +-- Name: publish_snapshots; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.publish_snapshots ( + id uuid DEFAULT public.gen_random_uuid() NOT NULL, + publication_date_id uuid, + label character varying, + snapshot jsonb, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL +); + + -- -- Name: published_session_assignments; Type: TABLE; Schema: public; Owner: - -- @@ -2345,6 +2362,14 @@ ALTER TABLE ONLY public.publication_statuses ADD CONSTRAINT publication_statuses_pkey PRIMARY KEY (id); +-- +-- Name: publish_snapshots publish_snapshots_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.publish_snapshots + ADD CONSTRAINT publish_snapshots_pkey PRIMARY KEY (id); + + -- -- Name: published_session_assignments published_programme_assignments_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -3337,7 +3362,8 @@ INSERT INTO "schema_migrations" (version) VALUES ('20220714124706'), ('20220719000644'), ('20220723213605'), -('20220726130346'); - +('20220726130346'), +('20220801152151'), +('20220801173704'); diff --git a/test/factories/publish_snapshots.rb b/test/factories/publish_snapshots.rb new file mode 100644 index 000000000..21281585b --- /dev/null +++ b/test/factories/publish_snapshots.rb @@ -0,0 +1,5 @@ +FactoryBot.define do + factory :publish_snapshot do + + end +end diff --git a/test/models/publish_snapshot_test.rb b/test/models/publish_snapshot_test.rb new file mode 100644 index 000000000..ca9eb1ef0 --- /dev/null +++ b/test/models/publish_snapshot_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class PublishSnapshotTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end From 1e4ff5931cd69425ba889cdc288b9e9d4843f23d Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Mon, 1 Aug 2022 15:11:57 -0400 Subject: [PATCH 41/50] utility, permissions, and publish endpoint --- app/controllers/application_controller.rb | 4 ++++ app/controllers/sessions_controller.rb | 8 ++++++++ app/policies/session_policy.rb | 4 ++++ app/services/session_service.rb | 1 - app/workers/publication_worker.rb | 8 +++++--- config/routes.rb | 1 + lib/tasks/publications.rake | 13 +++++++++++++ lib/tasks/rbac.rake | 9 ++++++--- 8 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 lib/tasks/publications.rake diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index e55ed2f9f..d60f404e3 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -34,6 +34,10 @@ def check_up if ScheduleSnapshot.where("status = 'in_progress'").count > 0 redirect_to '/maintenance.html', status: 503 end + # Stop people from making changes if we are publishing + if PublicationStatus.where("status = 'inprogress'").count > 0 + redirect_to '/maintenance.html', status: 503 + end end def prevent_cache diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 5ccd688c7..b51535643 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -4,6 +4,14 @@ class SessionsController < ResourceController POLICY_SCOPE_CLASS = 'SessionPolicy::Scope'.freeze # DEFAULT_SORTBY = 'name_sort_by' + def schedule_publish + authorize current_person, policy_class: policy_class + + PublicationService.start_publish_job + + render status: :ok, json: {}.to_json, content_type: 'application/json' + end + # Mass update for the sessions (given ids and params) def update_all authorize current_person, policy_class: policy_class diff --git a/app/policies/session_policy.rb b/app/policies/session_policy.rb index 1538c44bf..6bb20d2c8 100644 --- a/app/policies/session_policy.rb +++ b/app/policies/session_policy.rb @@ -3,6 +3,10 @@ def delete_snapshot? !Rails.env.production? && allowed?(action: :delete_snapshot) end + def schedule_publish? + allowed?(action: :schedule_publish) + end + def update_all? allowed?(action: :update_all) end diff --git a/app/services/session_service.rb b/app/services/session_service.rb index 74888d0b4..05cd2a54a 100644 --- a/app/services/session_service.rb +++ b/app/services/session_service.rb @@ -44,7 +44,6 @@ def self.draft_schedule_for(person:, current_person: nil) # serializer: Conclar::SessionSerializer # ) def self.scheduled_sessions - # TODO: when publish process done change condition if PublishedSession.count > 0 || Rails.env.production? self.published_sessions else diff --git a/app/workers/publication_worker.rb b/app/workers/publication_worker.rb index 1330598f6..cd7284978 100644 --- a/app/workers/publication_worker.rb +++ b/app/workers/publication_worker.rb @@ -27,10 +27,12 @@ def perform pstatus.save! end - # populate caches + # populate basic caches if pub_date - SessionService.cache_published_sessions(publication_date: pub_date) - SessionService.cache_published_participants(publication_date: pub_date) + PublishedSession.transaction do + SessionService.cache_published_sessions(publication_date: pub_date) + SessionService.cache_published_participants(publication_date: pub_date) + end end end end diff --git a/config/routes.rb b/config/routes.rb index 664218885..7069297be 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -138,6 +138,7 @@ resources :tags, path: 'tag' get 'session/tags', to: 'sessions#tags' + get 'session/schedule_publish', to: 'sessions#schedule_publish' post 'session/import', to: 'sessions#import' post 'session/update_all', to: 'sessions#update_all' # get sessions/assigned_id - &include=session_assignments&filter[session_assignments][person_id]=person_id diff --git a/lib/tasks/publications.rake b/lib/tasks/publications.rake new file mode 100644 index 000000000..9596763fb --- /dev/null +++ b/lib/tasks/publications.rake @@ -0,0 +1,13 @@ +desc "Utils for published data" + +namespace :pubs do + # Utility to reset the published info - use while testing + task reset: :environment do + PublishedSession.destroy_all + PublishSnapshot.delete_all + PublicationDate.delete_all + PublicationStatus.delete_all + + Audit::PublishedSessionVersion.delete_all + end +end diff --git a/lib/tasks/rbac.rake b/lib/tasks/rbac.rake index 63b3e380b..0d962279e 100644 --- a/lib/tasks/rbac.rake +++ b/lib/tasks/rbac.rake @@ -155,7 +155,8 @@ namespace :rbac do "index": true, "show": true, "update": false, - "update_all": false + "update_all": false, + "schedule_publish": false }, "format": { "create": false, @@ -411,7 +412,8 @@ namespace :rbac do "index": true, "show": true, "update": true, - "update_all": true + "update_all": true, + "schedule_publish": false }, "format": { "create": true, @@ -705,7 +707,8 @@ namespace :rbac do "index": true, "show": true, "update": true, - "update_all": true + "update_all": true, + "schedule_publish": false }, "format": { "create": true, From 165efd43f56382be33a19d6e80f98524b02b3b09 Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Mon, 1 Aug 2022 16:05:17 -0400 Subject: [PATCH 42/50] PLAN-506 add recorded and streamed attributes --- app/controllers/sessions_controller.rb | 2 ++ app/serializers/conclar/session_serializer.rb | 5 +++-- app/serializers/session_serializer.rb | 3 ++- ...20801195644_add_recorded_etc_to_sessions.rb | 9 +++++++++ db/structure.sql | 18 +++++++++++------- 5 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 db/migrate/20220801195644_add_recorded_etc_to_sessions.rb diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 5ccd688c7..8709f6e69 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -314,6 +314,8 @@ def allowed_params require_signup age_restriction_id room_notes + recorded + streamed ] # Tags # format diff --git a/app/serializers/conclar/session_serializer.rb b/app/serializers/conclar/session_serializer.rb index 05b25bebb..ddd96b135 100644 --- a/app/serializers/conclar/session_serializer.rb +++ b/app/serializers/conclar/session_serializer.rb @@ -30,8 +30,9 @@ class Conclar::SessionSerializer < ActiveModel::Serializer res.concat object.minors_participation end - # require_signup ???? - # recordrd ??? + res.concat ['Require Signup'] if object.require_signup + res.concat ['Recorded'] if object.recorded + res.concat ['Streamed'] if object.streamed res end diff --git a/app/serializers/session_serializer.rb b/app/serializers/session_serializer.rb index 6940511ac..cf3b6d9e3 100644 --- a/app/serializers/session_serializer.rb +++ b/app/serializers/session_serializer.rb @@ -13,7 +13,8 @@ class SessionSerializer :room_id, :proofed, :format_id, :room_set_id, :status, :environment, :tech_notes, :room_notes, - :minors_participation, :age_restriction_id + :minors_participation, :age_restriction_id, + :recorded, :streamed # tag_list attribute :tag_list do |session| diff --git a/db/migrate/20220801195644_add_recorded_etc_to_sessions.rb b/db/migrate/20220801195644_add_recorded_etc_to_sessions.rb new file mode 100644 index 000000000..fd344586b --- /dev/null +++ b/db/migrate/20220801195644_add_recorded_etc_to_sessions.rb @@ -0,0 +1,9 @@ +class AddRecordedEtcToSessions < ActiveRecord::Migration[6.1] + def change + add_column :sessions, :recorded, :boolean, default: false, null: false + add_column :sessions, :streamed, :boolean, default: false, null: false + + add_column :published_sessions, :recorded, :boolean, default: false, null: false + add_column :published_sessions, :streamed, :boolean, default: false, null: false + end +end diff --git a/db/structure.sql b/db/structure.sql index 4cff0cd02..bb32ebe7b 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -697,7 +697,9 @@ CREATE TABLE public.sessions ( age_restriction_id uuid, minors_participation jsonb, room_set_id uuid, - room_notes text + room_notes text, + recorded boolean DEFAULT false NOT NULL, + streamed boolean DEFAULT false NOT NULL ); @@ -1340,9 +1342,9 @@ CREATE TABLE public.publication_dates ( "timestamp" timestamp without time zone, created_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL, - newitems integer DEFAULT 0, - modifieditems integer DEFAULT 0, - removeditems integer DEFAULT 0 + removeditems integer, + modifieditems integer, + newitems integer ); @@ -1400,7 +1402,9 @@ CREATE TABLE public.published_sessions ( require_signup boolean DEFAULT false, waiting_list_size integer DEFAULT 0, environment public.session_environments_enum DEFAULT 'unknown'::public.session_environments_enum, - minors_participation jsonb + minors_participation jsonb, + recorded boolean DEFAULT false NOT NULL, + streamed boolean DEFAULT false NOT NULL ); @@ -3337,7 +3341,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20220714124706'), ('20220719000644'), ('20220723213605'), -('20220726130346'); - +('20220726130346'), +('20220801195644'); From 01317f844d47a77d875a82083e910025b99a8e05 Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Tue, 2 Aug 2022 10:27:12 -0400 Subject: [PATCH 43/50] rooms added --- lib/tasks/chicon.rake | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/tasks/chicon.rake b/lib/tasks/chicon.rake index 760c68991..a4a0cf5c0 100644 --- a/lib/tasks/chicon.rake +++ b/lib/tasks/chicon.rake @@ -206,6 +206,21 @@ namespace :chicon do hyatt_rooms airmeet_rooms + rooms_no_venue + end + + def rooms_no_venue + # as requested by business + candidates = [ + {name: "Offsite", purpose: "Programming", sort_order: 601}, + ] + + candidates.each do |candidate| + room = Room.find_by name: candidate[:name] + next if room + + Room.create!(candidate) + end end def airmeet_rooms @@ -215,8 +230,9 @@ namespace :chicon do {venue_id: airmeet.id, name: "Airmeet 2", floor: "Virtual", purpose: "Programming", sort_order: 502}, {venue_id: airmeet.id, name: "Airmeet 3", floor: "Virtual", purpose: "Programming", sort_order: 503}, {venue_id: airmeet.id, name: "Airmeet 4", floor: "Virtual", purpose: "Programming", sort_order: 504}, - {venue_id: airmeet.id, name: "Airmeet Readings", floor: "Virtual", purpose: "Programming", sort_order: 505}, - {venue_id: airmeet.id, name: "Airmeet Table Talks", floor: "Virtual", purpose: "Programming", sort_order: 506} + {venue_id: airmeet.id, name: "Airmeet 5", floor: "Virtual", purpose: "Programming", sort_order: 505}, + {venue_id: airmeet.id, name: "Airmeet Readings", floor: "Virtual", purpose: "Programming", sort_order: 506}, + {venue_id: airmeet.id, name: "Airmeet Table Talks", floor: "Virtual", purpose: "Programming", sort_order: 507} ] candidates.each do |candidate| From 1d8190d8fe72a98ee12745e0080a50b6ca7c716d Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Tue, 2 Aug 2022 16:00:16 -0400 Subject: [PATCH 44/50] PLAN-648 new report --- .../reports/people_reports_controller.rb | 67 +++++++++++++++++++ app/javascript/reports/reports_screen.vue | 11 +-- app/models/person.rb | 13 +++- app/policies/report_policy.rb | 4 ++ config/routes.rb | 2 + lib/tasks/rbac.rake | 6 +- 6 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 app/controllers/reports/people_reports_controller.rb diff --git a/app/controllers/reports/people_reports_controller.rb b/app/controllers/reports/people_reports_controller.rb new file mode 100644 index 000000000..6cf0460c6 --- /dev/null +++ b/app/controllers/reports/people_reports_controller.rb @@ -0,0 +1,67 @@ +class Reports::PeopleReportsController < ApplicationController + around_action :set_timezone + + def record_stream_permissions + authorize Person, policy_class: ReportPolicy + + # People: moderators, participants.  NO INVIS, NO RESEVER + + active_roles = SessionAssignmentRoleType.where("role_type = 'participant' and (name != 'Invisible' and name != 'Reserve')") + people = Person + .includes({sessions: :room}, :primary_email) + .references(:sessions) + .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Invisible')") + .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Reserve')") + .order('people.published_name asc') + + + # Person published  names, primary email, attendance type, + # participant status, permission to stream, + # exclusions for streaming, permission to record, exclusions to recording, + # their schedule (in one cell with session title time duration room.  If not possible one line per session will have to do) + + workbook = FastExcel.open(constant_memory: true) + worksheet = workbook.add_worksheet("Record and Stream Permissions") + + worksheet.append_row( + [ + 'Name', + 'Published Name', + 'Primary Email', + 'Attendance Type', + 'Participant Status', + 'Permission to Stream', + 'Streaming Exceptions', + 'Permission to Record', + 'Recording Exceptions', + 'Schedule' + ] + ) + + people.each do |person| + worksheet.append_row( + [ + person.name, + person.published_name, + person.primary_email&.email, + person.attendance_type, + person.con_state, + person.can_stream, + person.can_stream_exceptions, + person.can_record, + person.can_record_exceptions, + person.sessions.scheduled.collect{|s| "'#{s.title}' - #{s.start_time.strftime('%Y-%m-%d %H:%M %Z')} - #{s.duration} mins - #{s.room.name}" }.join(";\n") + ] + ) + end + + send_data workbook.read_string, + filename: "PeopleRecordStream-#{Time.now.strftime('%m-%d-%Y')}.xlsx", + disposition: 'attachment' + end + + def set_timezone(&block) + timezone = ConfigService.value('convention_timezone') + Time.use_zone(timezone, &block) + end +end diff --git a/app/javascript/reports/reports_screen.vue b/app/javascript/reports/reports_screen.vue index 6aa0dd9bd..c09a9d9d6 100644 --- a/app/javascript/reports/reports_screen.vue +++ b/app/javascript/reports/reports_screen.vue @@ -9,6 +9,9 @@ </ul> <h5><a id="participants"></a>Participants</h5> <ul> + <li> + <a href="/report/people_reports/record_stream_permissions" target="_blank">Participant Recorsing and Streaming Permissions</a> + </li> <li> <a href="/report/participant_selections" target="_blank">Participant Selections</a><br /> <p class="ml-2"> @@ -41,7 +44,7 @@ <strong><em>Description</em></strong>: List of surveys taken, including day/time submitted, one line per person<br /> <strong><em>Fields</em></strong>: Person name, published name, primary email, attendance type, participant status, surveys taken<br /> <strong><em>Person data included</em></strong>: participant status of applied, probable, vetted, invite_pending, invited, accepted - </p> + </p> </li> <li> <span v-if="currentUserIsStaff" class="text-muted font-italic" title="You do not have the right set of permissions to run this report." v-b-tooltip>Participants and Do Not Assign With</span> @@ -51,7 +54,7 @@ <strong><em>Fields</em></strong>: Person name, published name, session title, area(s) of session, names of other people assigned to the session, names of people not to assign to the same session<br /> <strong><em>Session data included</em></strong>: all scheduled sessions<br /> <strong><em>Person data included</em></strong>: moderators, participants, invisible participants who listed information about who not to assign with - </p> + </p> </li> <li> <a href="/report/session_reports/participants_over_session_limits" target="_blank">Participants over Daily Limits</a> @@ -87,7 +90,7 @@ <strong><em>Fields</em></strong>: Person name, published name, participant status, attendance type (in-person, virtual, hybrid), person’s bio<br /> <strong><em>Session data included</em></strong>: all scheduled sessions<br /> <strong><em>Person data included</em></strong>: people with a participant status of accepted, invited, or invite_pending who are assigned to no sessions, or who are assigned as invisible participants or reserved on one or more sessions - </p> + </p> </li> </ul> @@ -126,7 +129,7 @@ <strong><em>Description</em></strong>: Scheduled sessions with no assigned moderators or participants, one line per session<br /> <strong><em>Fields</em></strong>: Session title, area(s) of session, session start time, room<br /> <strong><em>Session data included</em></strong>: all scheduled sessions - </p> + </p> </li> <li> <a href="/report/session_reports/assigned_sessions_not_scheduled" target="_blank">Sessions with Participants not Scheduled</a> diff --git a/app/models/person.rb b/app/models/person.rb index f524f02a4..8f35ac69a 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -23,18 +23,27 @@ class Person < ApplicationRecord has_many :session_assignments, dependent: :destroy do def publishable # get the people with the given role - where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Invisble')") + where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Invisible')") .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Reserve')") .where("session_assignments.session_assignment_role_type_id is not null AND session_assignments.state != 'rejected'") end end has_many :sessions, through: :session_assignments do + def scheduled + # get the people with the given role + where("sessions.status != 'dropped'") + .where("sessions.start_time is not null and sessions.room_id is not null") + .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Invisible')") + .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Reserve')") + .where("session_assignments.session_assignment_role_type_id is not null AND session_assignments.state != 'rejected'") + end + def publishable # get the people with the given role where("sessions.status != 'draft' and sessions.status != 'dropped' and sessions.visibility = 'public'") .where("sessions.start_time is not null and sessions.room_id is not null") - .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Invisble')") + .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Invisible')") .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Reserve')") .where("session_assignments.session_assignment_role_type_id is not null AND session_assignments.state != 'rejected'") end diff --git a/app/policies/report_policy.rb b/app/policies/report_policy.rb index dc241922f..eea450a4c 100644 --- a/app/policies/report_policy.rb +++ b/app/policies/report_policy.rb @@ -1,5 +1,9 @@ class ReportPolicy < BasePolicy + def record_stream_permissions? + allowed?(action: :record_stream_permissions) + end + def schedule_accpetance? allowed?(action: :schedule_accpetance) end diff --git a/config/routes.rb b/config/routes.rb index 664218885..425d29df3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -84,6 +84,8 @@ get 'report/conflict_reports/all_conflicts', to: 'reports/conflict_reports#all_conflicts' get 'report/conflict_reports/all_ignored_conflicts', to: 'reports/conflict_reports#all_ignored_conflicts' + get 'report/people_reports/record_stream_permissions', to: 'reports/people_reports#record_stream_permissions' + resources :availabilities, path: 'availability', except: [:index] resources :person_exclusions, path: 'person_exclusion', except: [:index] resources :session_limits, path: 'session_limit', except: [:index] diff --git a/lib/tasks/rbac.rake b/lib/tasks/rbac.rake index 63b3e380b..c4b665eb0 100644 --- a/lib/tasks/rbac.rake +++ b/lib/tasks/rbac.rake @@ -521,7 +521,8 @@ namespace :rbac do "schedule_by_person": true, "schedule_by_room_then_time": true, "session_selections": true, - "sessions_with_participants": true + "sessions_with_participants": true, + "record_stream_permissions": true }, "session_report": { "panels_with_too_few_people": true, @@ -815,7 +816,8 @@ namespace :rbac do "schedule_by_person": true, "schedule_by_room_then_time": true, "session_selections": true, - "sessions_with_participants": true + "sessions_with_participants": true, + "record_stream_permissions": true }, "session_report": { "panels_with_too_few_people": true, From 894b65c80c9f38b9847e4a25a7ad44f0eedcec1b Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Tue, 2 Aug 2022 16:01:15 -0400 Subject: [PATCH 45/50] fix invisible typo --- app/services/publication_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/publication_service.rb b/app/services/publication_service.rb index 8918889b9..71f1d3a7e 100644 --- a/app/services/publication_service.rb +++ b/app/services/publication_service.rb @@ -205,7 +205,7 @@ def self.publishable_sessions def self.publishable_assignments(sessions:) SessionAssignment .where("session_assignments.session_id in (?)", sessions.collect(&:id)) - .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Invisble' or session_assignment_role_type.name = 'Reserve')") + .where("session_assignments.session_assignment_role_type_id not in (select id from session_assignment_role_type where session_assignment_role_type.name = 'Invisible' or session_assignment_role_type.name = 'Reserve')") .where("session_assignments.visibility = 'public'") end end From 80181b6db2f9a03265322965169c619c5100f5aa Mon Sep 17 00:00:00 2001 From: Gail Terman <Gailbear@users.noreply.github.com> Date: Tue, 2 Aug 2022 20:52:02 -0400 Subject: [PATCH 46/50] make the draft-schedule tab have - instead of _ --- app/javascript/people/person_tabs.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/people/person_tabs.vue b/app/javascript/people/person_tabs.vue index c9c6f6dee..8029f827f 100644 --- a/app/javascript/people/person_tabs.vue +++ b/app/javascript/people/person_tabs.vue @@ -123,7 +123,7 @@ export default { 'admin' ] if (this.displayDraftSchedule) { - baseTabs.splice(5, 0, 'draft_schedule') + baseTabs.splice(5, 0, 'draft-schedule') } if (this.currentUserIsAdmin || this.currentUserIsStaff || this.firmSchedule) { baseTabs.splice(5, 0, 'schedule') From c1f25eb87a79e96fe88c0755d025af12cb6d86e7 Mon Sep 17 00:00:00 2001 From: Gail Terman <Gailbear@users.noreply.github.com> Date: Tue, 2 Aug 2022 21:02:28 -0400 Subject: [PATCH 47/50] Fix reports page --- app/javascript/reports/reports_screen.vue | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/javascript/reports/reports_screen.vue b/app/javascript/reports/reports_screen.vue index c09a9d9d6..66dcde80e 100644 --- a/app/javascript/reports/reports_screen.vue +++ b/app/javascript/reports/reports_screen.vue @@ -9,9 +9,6 @@ </ul> <h5><a id="participants"></a>Participants</h5> <ul> - <li> - <a href="/report/people_reports/record_stream_permissions" target="_blank">Participant Recorsing and Streaming Permissions</a> - </li> <li> <a href="/report/participant_selections" target="_blank">Participant Selections</a><br /> <p class="ml-2"> @@ -92,6 +89,14 @@ <strong><em>Person data included</em></strong>: people with a participant status of accepted, invited, or invite_pending who are assigned to no sessions, or who are assigned as invisible participants or reserved on one or more sessions </p> </li> + <li> + <a href="/report/people_reports/record_stream_permissions" target="_blank">Participant Recording and Streaming Permissions</a> + <p class="ml-2"> + <strong><em>Description</em></strong>: List of participants with their recording and streaming permissions and exclusions.<br /> + <strong><em>Fields</em></strong>: Person published names, primary email, attendance type, participant status, permission to stream, exclusions for streaming, permission to record, exclusions to recording, and their schedule.<br /> + <strong><em>Person data included</em></strong>: Moderators and participants on scheduled sessions. + </p> + </li> </ul> <h5><a id="sessions"></a>Sessions</h5> From 19cfbe9a4c127009550045c26bfb921526370425 Mon Sep 17 00:00:00 2001 From: Gail Terman <gterman@gmail.com> Date: Tue, 2 Aug 2022 21:32:19 -0400 Subject: [PATCH 48/50] PLAN-506 add recorded and streamed to sessions You can search and sort by them in the table, they appear as disabled switches in the flyout, and as enabled switches in the edit page. --- app/javascript/sessions/session.js | 19 +++++++++++++++- app/javascript/sessions/session_sidebar.vue | 24 +++++++++++++++++++-- app/javascript/sessions/session_summary.vue | 16 ++++++++++++++ db/structure.sql | 3 +-- 4 files changed, 57 insertions(+), 5 deletions(-) diff --git a/app/javascript/sessions/session.js b/app/javascript/sessions/session.js index 04ecb8d76..1820f88d7 100644 --- a/app/javascript/sessions/session.js +++ b/app/javascript/sessions/session.js @@ -81,7 +81,24 @@ export const session_columns = [ label: 'Copy Edited/Proofed', type: "select", choices: [{label: "Yes", value: true}, {label: "No", value: false}], - formatter: (value) => value ? "Yes" : "No" + formatter: (value) => value ? "Yes" : "No", + sortable: true, + }, + { + key: 'recorded', + label: 'Recorded', + type: "select", + choices: [{label: "Yes", value: true}, {label: "No", value: false}], + formatter: (value) => value ? "Yes" : "No", + sortable: true + }, + { + key: 'streamed', + label: 'Livestreamed', + type: "select", + choices: [{label: "Yes", value: true}, {label: "No", value: false}], + formatter: (value) => value ? "Yes" : "No", + sortable: true }, { key: 'environment', diff --git a/app/javascript/sessions/session_sidebar.vue b/app/javascript/sessions/session_sidebar.vue index 0386482c8..76d8c05ee 100644 --- a/app/javascript/sessions/session_sidebar.vue +++ b/app/javascript/sessions/session_sidebar.vue @@ -48,7 +48,7 @@ </div> </b-row> <div class="row"> - <div class="col-12"> + <div class="col-12 col-md-6 col-lg-4"> <b-form-group label="Public Schedule Visibility"> <span class="text-muted ml-2">Not Visible</span> <b-form-checkbox @@ -57,7 +57,27 @@ :checked="selected.visibility === 'is_public'" class="d-inline-block" >Visible</b-form-checkbox> - </b-form-group> + </b-form-group> + </div> + <div class="col-12 col-md-6 col-lg-4"> + <b-form-group> + <b-form-checkbox + id="session-recorded" + switch + v-model="selected.recorded" + disabled + >Will be recorded</b-form-checkbox> + </b-form-group> + </div> + <div class="col-12 col-md-6 col-lg-4"> + <b-form-group> + <b-form-checkbox + id="session-streamed" + switch + v-model="selected.streamed" + disabled + >Will be live-streamed</b-form-checkbox> + </b-form-group> </div> </div> <div class="float-right d-flex justify-content-end"> diff --git a/app/javascript/sessions/session_summary.vue b/app/javascript/sessions/session_summary.vue index 416d614ad..a6e9ec30d 100644 --- a/app/javascript/sessions/session_summary.vue +++ b/app/javascript/sessions/session_summary.vue @@ -79,6 +79,22 @@ class="d-inline-block" >Visible</b-form-checkbox> </b-form-group> + <b-form-group> + <b-form-checkbox + id="session-recorded" + switch + v-model="session.recorded" + @change="saveSession()" + >Will be recorded</b-form-checkbox> + </b-form-group> + <b-form-group> + <b-form-checkbox + id="session-streamed" + switch + v-model="session.streamed" + @change="saveSession()" + >Will be live-streamed</b-form-checkbox> + </b-form-group> <b-form-group label="Status" label-cols="auto"> <b-form-select id="session-status" v-model="session.status" @change="saveSession()"> <b-form-select-option diff --git a/db/structure.sql b/db/structure.sql index 5d1248a49..c6ec3094f 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -1125,7 +1125,7 @@ CREATE VIEW public.person_schedules AS sessions.description, sessions.environment FROM (((public.session_assignments sa - JOIN public.session_assignment_role_type sart ON (((sart.id = sa.session_assignment_role_type_id) AND (sart.role_type = 'participant'::public.assignment_role_enum)))) + JOIN public.session_assignment_role_type sart ON (((sart.id = sa.session_assignment_role_type_id) AND (sart.role_type = 'participant'::public.assignment_role_enum) AND ((sart.name)::text <> 'Reserve'::text)))) JOIN public.people p ON ((p.id = sa.person_id))) LEFT JOIN public.sessions ON ((sessions.id = sa.session_id))) WHERE ((sa.session_assignment_role_type_id IS NOT NULL) AND (sessions.room_id IS NOT NULL) AND (sessions.start_time IS NOT NULL) AND ((sa.state)::text <> 'rejected'::text)); @@ -3372,4 +3372,3 @@ INSERT INTO "schema_migrations" (version) VALUES ('20220801195644'); - From e38660d70d0425b131c56044f52ec1f53d262f2e Mon Sep 17 00:00:00 2001 From: Gail Terman <gterman@gmail.com> Date: Tue, 2 Aug 2022 22:20:22 -0400 Subject: [PATCH 49/50] PLAN-608 add publish controls part 1 - make table - populate with mock data - make radio button diff selection enable/disable the diff button --- app/javascript/mailings/mailings_table.vue | 2 +- app/javascript/schedule/schedule_settings.vue | 55 +++++++++++++++++-- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/app/javascript/mailings/mailings_table.vue b/app/javascript/mailings/mailings_table.vue index 5ed6d5c5b..e4c5cd79e 100644 --- a/app/javascript/mailings/mailings_table.vue +++ b/app/javascript/mailings/mailings_table.vue @@ -2,7 +2,7 @@ <div> <table-vue defaultSortBy='updated_at' - defaultSortDesc="true" + :defaultSortDesc="true" :model="model" :columns="columns" :defaultFilter="defaultFilter" diff --git a/app/javascript/schedule/schedule_settings.vue b/app/javascript/schedule/schedule_settings.vue index 001ca0133..1d48ccce7 100644 --- a/app/javascript/schedule/schedule_settings.vue +++ b/app/javascript/schedule/schedule_settings.vue @@ -2,6 +2,7 @@ <div class="container-fluid"> <div class="row"> <div class="col-12"> + <h5>Release schedule to participants</h5> <b-form-group label-cols="auto" class="align-items-center"> <template #label>Release <strong>Draft</strong> Schedule to Participants</template> <b-form-checkbox switch v-model="localDraftSchedule" :disabled="localDraftSchedule" @change="openDraftConfirm" id="draft-schedule-checkbox" aria-describedby="draft-schedule-date"></b-form-checkbox> @@ -14,10 +15,41 @@ <div v-if="currentSettings.env !== 'production'"> <b-button variant="primary" @click="reset()">Reset for Testing</b-button> <span>THIS DELETES THE SNAPSHOT AND YOU CAN'T EVER GET IT BACK</span> - <div class="mt-3">Note: this minecart isn't actually hooked up to any status yet. So while it does actually produce a snapshot, the toggle won't - reflect reality if you reload. There's some TODOs in here. If you try to snapshot and get an error, reset first. - </div> </div> + <hr /> + </div> + </div> + <div class="row"> + <div class="col-12"> + <h5>Publish schedule to public</h5> + <b-table-simple borderless fixed small> + <b-thead> + <b-tr> + <b-td class="text-center"> + <b-button variant="primary" size="sm" :disabled="!canDiff">Show difference</b-button> + </b-td> + <b-td colspan="3"> + <b-button variant="primary" size="sm">Create a publish snapshot</b-button> + </b-td> + </b-tr> + </b-thead> + </b-table-simple> + <b-table-simple bordered fixed small> + <b-thead class="text-center"> + <b-tr> + <b-td class="text-center">Select 2</b-td> + <b-td colspan="3">Timestamp</b-td> + </b-tr> + </b-thead> + <b-tbody> + <b-tr v-for="(snap, i) in pubSnapshots" :key="snap.id"> + <b-td class="text-center"> + <b-form-checkbox name="pubs-diff" v-model="pubsDiff[i]" :disabled="pubsDiffCount >= 2 && !pubsDiff[i]"></b-form-checkbox> + </b-td> + <b-td colspan="3">{{snap.timestamp}}</b-td> + </b-tr> + </b-tbody> + </b-table-simple> </div> </div> <plano-modal id="confirm-draft-modal" @cancel="cancelDraft()" @close="cancelDraft()" no-close-on-backdrop @ok="confirmDraft()"> @@ -42,6 +74,7 @@ import { import { scheduleWorkflowMixin } from '@/store/schedule_workflow'; import settingsMixin from "@/store/settings.mixin"; +import { DateTime } from 'luxon'; export default { name: "ScheduleSettings", @@ -60,9 +93,23 @@ export default { firmScheduleConfirmed: false, SCHEDULE_DRAFT_CONFIRM_MESSAGE, SCHEDULE_FIRM_CONFIRM_MESSAGE, - NODE_ENV + NODE_ENV, + mockSnapshots: [ + {timestamp: '2022-08-01T09:58:00Z', id: '12345'}, + {timestamp: '2022-08-04T00:24:00Z', id:'67890'} + ], + pubsDiff: [false, false, false], }), computed: { + pubSnapshots() { + return [{timestamp: "Current state", id: null}, ...this.mockSnapshots.map(snap => ({...snap, timestamp: DateTime.fromISO(snap.timestamp).toFormat("DDDD, t ZZZZ")}))] + }, + pubsDiffCount() { + return this.pubsDiff.filter(pd => pd).length + }, + canDiff() { + return this.pubsDiffCount === 2; + }, draftScheduledAtText() { return this.draftScheduleConfirmed ? this.draftScheduledAt : "Pending"; }, From 2845d09d11617243153101cae2c6cea718527153 Mon Sep 17 00:00:00 2001 From: Henry <henry.balen@chicon.org> Date: Wed, 3 Aug 2022 10:49:03 -0400 Subject: [PATCH 50/50] wire in publish action to button adjust audit so that lock version is not used --- app/controllers/schedule_controller.rb | 4 ++-- app/javascript/schedule/schedule_settings.vue | 9 ++++++--- app/models/availability.rb | 2 +- app/models/email_address.rb | 2 +- app/models/person.rb | 2 +- app/models/person_constraints.rb | 2 +- app/models/person_exclusion.rb | 2 +- app/models/published_session.rb | 2 +- app/models/published_session_assignment.rb | 2 +- app/models/session.rb | 2 +- app/models/session_area.rb | 2 +- app/models/session_assignment.rb | 2 +- app/models/session_limit.rb | 2 +- app/models/survey.rb | 2 +- app/models/survey/answer.rb | 2 +- app/models/survey/page.rb | 2 +- app/models/survey/question.rb | 2 +- db/structure.sql | 1 - lib/tasks/publications.rake | 7 +++++++ 19 files changed, 30 insertions(+), 21 deletions(-) diff --git a/app/controllers/schedule_controller.rb b/app/controllers/schedule_controller.rb index bb031c505..4254fc7af 100644 --- a/app/controllers/schedule_controller.rb +++ b/app/controllers/schedule_controller.rb @@ -6,7 +6,7 @@ class ScheduleController < ApplicationController # 2. If staging or dev use published - if no published then use the live for testing # 3. cache mechanism (cache can be popultaed as part of the publish) def index - snapshot = PublicationDate.order('created_at desc').first.publish_snapshots.schedules.first + snapshot = PublicationDate.order('created_at desc').first&.publish_snapshots&.schedules&.first if snapshot render json: snapshot, content_type: 'application/json' @@ -21,7 +21,7 @@ def index end def participants - snapshot = PublicationDate.order('created_at desc').first.publish_snapshots.participants.first + snapshot = PublicationDate.order('created_at desc').first&.publish_snapshots&.participants&.first if snapshot render json: snapshot, content_type: 'application/json' diff --git a/app/javascript/schedule/schedule_settings.vue b/app/javascript/schedule/schedule_settings.vue index 1d48ccce7..fa8386d89 100644 --- a/app/javascript/schedule/schedule_settings.vue +++ b/app/javascript/schedule/schedule_settings.vue @@ -29,7 +29,7 @@ <b-button variant="primary" size="sm" :disabled="!canDiff">Show difference</b-button> </b-td> <b-td colspan="3"> - <b-button variant="primary" size="sm">Create a publish snapshot</b-button> + <b-button variant="primary" size="sm" @click="publishdSchedule()">Create a publish snapshot</b-button> </b-td> </b-tr> </b-thead> @@ -95,8 +95,8 @@ export default { SCHEDULE_FIRM_CONFIRM_MESSAGE, NODE_ENV, mockSnapshots: [ - {timestamp: '2022-08-01T09:58:00Z', id: '12345'}, - {timestamp: '2022-08-04T00:24:00Z', id:'67890'} + // {timestamp: '2022-08-01T09:58:00Z', id: '12345'}, + // {timestamp: '2022-08-04T00:24:00Z', id:'67890'} ], pubsDiff: [false, false, false], }), @@ -148,6 +148,9 @@ export default { this.draftSchedule = false; this.firmSchedule = false; this.toastPromise(http.get('/schedule_workflow/reset'), "succesfully reset workflows") + }, + publishdSchedule() { + this.toastPromise(http.get('/session/schedule_publish'), "Succesfully requested publish") } }, watch: { diff --git a/app/models/availability.rb b/app/models/availability.rb index 94316d462..1510b7a54 100644 --- a/app/models/availability.rb +++ b/app/models/availability.rb @@ -4,5 +4,5 @@ class Availability < ApplicationRecord validates :start_time, presence: true validates :end_time, presence: true - has_paper_trail versions: { class_name: 'Audit::PersonVersion' }, ignore: [:updated_at, :created_at] + has_paper_trail versions: { class_name: 'Audit::PersonVersion' }, ignore: [:updated_at, :created_at, :lock_version] end diff --git a/app/models/email_address.rb b/app/models/email_address.rb index 235aa1abb..83fe4dfde 100644 --- a/app/models/email_address.rb +++ b/app/models/email_address.rb @@ -17,7 +17,7 @@ class EmailAddress < ApplicationRecord before_save :strip_spaces after_save :check_default, :check_contact - has_paper_trail versions: { class_name: 'Audit::PersonVersion' }, ignore: [:updated_at, :created_at] + has_paper_trail versions: { class_name: 'Audit::PersonVersion' }, ignore: [:updated_at, :created_at, :lock_version] validates_with SinglePrimaryEmail diff --git a/app/models/person.rb b/app/models/person.rb index 8f35ac69a..f05651754 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -13,7 +13,7 @@ class Person < ApplicationRecord # acts_as_taggable acts_as_taggable_on :tags - has_paper_trail versions: { class_name: 'Audit::PersonVersion' }, ignore: [:updated_at, :created_at] + has_paper_trail versions: { class_name: 'Audit::PersonVersion' }, ignore: [:updated_at, :created_at, :lock_version] before_destroy :check_if_assigned before_save :check_primary_email diff --git a/app/models/person_constraints.rb b/app/models/person_constraints.rb index de7221cfe..21faf4b13 100644 --- a/app/models/person_constraints.rb +++ b/app/models/person_constraints.rb @@ -1,5 +1,5 @@ class PersonConstraints < ApplicationRecord belongs_to :person - has_paper_trail versions: { class_name: 'Audit::PersonVersion' }, ignore: [:updated_at, :created_at] + has_paper_trail versions: { class_name: 'Audit::PersonVersion' }, ignore: [:updated_at, :created_at, :lock_version] end diff --git a/app/models/person_exclusion.rb b/app/models/person_exclusion.rb index e75e8b889..6c8b3395b 100644 --- a/app/models/person_exclusion.rb +++ b/app/models/person_exclusion.rb @@ -5,5 +5,5 @@ class PersonExclusion < ApplicationRecord validates :person_id, presence: true validates :exclusion_id, presence: true - has_paper_trail versions: { class_name: 'Audit::PersonVersion' }, ignore: [:updated_at, :created_at] + has_paper_trail versions: { class_name: 'Audit::PersonVersion' }, ignore: [:updated_at, :created_at, :lock_version] end diff --git a/app/models/published_session.rb b/app/models/published_session.rb index a8b27734d..c57017801 100644 --- a/app/models/published_session.rb +++ b/app/models/published_session.rb @@ -4,7 +4,7 @@ class PublishedSession < ApplicationRecord self.primary_key = :session_id - has_paper_trail versions: { class_name: 'Audit::PublishedSessionVersion' }, ignore: [:updated_at, :created_at] + has_paper_trail versions: { class_name: 'Audit::PublishedSessionVersion' }, ignore: [:updated_at, :created_at, :lock_version] belongs_to :format belongs_to :session diff --git a/app/models/published_session_assignment.rb b/app/models/published_session_assignment.rb index 2dddb0c72..f3e3777ac 100644 --- a/app/models/published_session_assignment.rb +++ b/app/models/published_session_assignment.rb @@ -1,7 +1,7 @@ class PublishedSessionAssignment < ApplicationRecord self.primary_key = :session_assignment_id - has_paper_trail versions: { class_name: 'Audit::PublishedSessionVersion' }, ignore: [:updated_at, :created_at] + has_paper_trail versions: { class_name: 'Audit::PublishedSessionVersion' }, ignore: [:updated_at, :created_at, :lock_version, :sort_order] include RankedModel ranks :sort_order, with_same: [:published_session_id] diff --git a/app/models/session.rb b/app/models/session.rb index fc420a86b..d95597cf0 100644 --- a/app/models/session.rb +++ b/app/models/session.rb @@ -9,7 +9,7 @@ class Session < ApplicationRecord # NOTE: when we have a config for default duration change to use a lambda attribute :duration, default: 60 - has_paper_trail versions: { class_name: 'Audit::SessionVersion' }, ignore: [:updated_at, :created_at] + has_paper_trail versions: { class_name: 'Audit::SessionVersion' }, ignore: [:updated_at, :created_at, :updated_by, :lock_version, :interest_opened_by, :interest_opened_at] belongs_to :format, required: false has_one :published_session, dependent: :destroy diff --git a/app/models/session_area.rb b/app/models/session_area.rb index d4f6334f7..305ba59d6 100644 --- a/app/models/session_area.rb +++ b/app/models/session_area.rb @@ -7,5 +7,5 @@ class SessionArea < ApplicationRecord accepts_nested_attributes_for :area - has_paper_trail versions: { class_name: 'Audit::SessionVersion' }, ignore: [:updated_at, :created_at] + has_paper_trail versions: { class_name: 'Audit::SessionVersion' }, ignore: [:updated_at, :created_at, :lock_version] end diff --git a/app/models/session_assignment.rb b/app/models/session_assignment.rb index 59ac5bafd..30621ef16 100644 --- a/app/models/session_assignment.rb +++ b/app/models/session_assignment.rb @@ -20,7 +20,7 @@ class SessionAssignment < ApplicationRecord include RankedModel ranks :sort_order, with_same: [:session_id] - has_paper_trail versions: { class_name: 'Audit::SessionVersion' }, ignore: [:updated_at, :created_at] + has_paper_trail versions: { class_name: 'Audit::SessionVersion' }, ignore: [:updated_at, :created_at, :lock_version, :sort_order, :interested, :interest_ranking, :interest_notes, :planner_notes, :interest_role] belongs_to :person belongs_to :session diff --git a/app/models/session_limit.rb b/app/models/session_limit.rb index 410037632..37fc92e73 100644 --- a/app/models/session_limit.rb +++ b/app/models/session_limit.rb @@ -11,7 +11,7 @@ def validate(record) class SessionLimit < ApplicationRecord belongs_to :person - has_paper_trail versions: { class_name: 'Audit::PersonVersion' }, ignore: [:updated_at, :created_at] + has_paper_trail versions: { class_name: 'Audit::PersonVersion' }, ignore: [:updated_at, :created_at, :lock_version] validates :person_id, presence: true # validates :max_sessions, presence: true diff --git a/app/models/survey.rb b/app/models/survey.rb index cc6ed48b0..8e6c0ead6 100644 --- a/app/models/survey.rb +++ b/app/models/survey.rb @@ -6,7 +6,7 @@ class Survey < ApplicationRecord dependent: :destroy accepts_nested_attributes_for :pages, allow_destroy: true - has_paper_trail versions: { class_name: 'Audit::SurveyVersion' }, ignore: [:updated_at, :created_at] + has_paper_trail versions: { class_name: 'Audit::SurveyVersion' }, ignore: [:updated_at, :created_at, :lock_version] has_many :questions, through: :pages, class_name: 'Survey::Question' diff --git a/app/models/survey/answer.rb b/app/models/survey/answer.rb index 93b45f896..8f6cbee74 100644 --- a/app/models/survey/answer.rb +++ b/app/models/survey/answer.rb @@ -2,7 +2,7 @@ class Survey::Answer < ApplicationRecord include RankedModel ranks :sort_order, with_same: :question_id - has_paper_trail versions: { class_name: 'Audit::SurveyVersion' }, ignore: [:updated_at, :created_at] + has_paper_trail versions: { class_name: 'Audit::SurveyVersion' }, ignore: [:updated_at, :created_at, :lock_version, :sort_order] enum next_page_action: { next_page: 'next_page', submit: 'submit' } diff --git a/app/models/survey/page.rb b/app/models/survey/page.rb index d366318a3..db197af7c 100644 --- a/app/models/survey/page.rb +++ b/app/models/survey/page.rb @@ -4,7 +4,7 @@ class Survey::Page < ApplicationRecord default_scope { order(['survey_pages.sort_order asc'])} - has_paper_trail versions: { class_name: 'Audit::SurveyVersion' }, ignore: [:updated_at, :created_at] + has_paper_trail versions: { class_name: 'Audit::SurveyVersion' }, ignore: [:updated_at, :created_at, :lock_version, :sort_order] # scope the questions so we do not include those that were deleted has_many :questions, diff --git a/app/models/survey/question.rb b/app/models/survey/question.rb index cf44043eb..117e10156 100644 --- a/app/models/survey/question.rb +++ b/app/models/survey/question.rb @@ -6,7 +6,7 @@ class Survey::Question < ApplicationRecord scope :not_deleted, -> { where(deleted_at: nil) } scope :deleted, -> { where('survey_questions.deleted_at is not null') } - has_paper_trail versions: { class_name: 'Audit::SurveyVersion' }, ignore: [:updated_at, :created_at] + has_paper_trail versions: { class_name: 'Audit::SurveyVersion' }, ignore: [:updated_at, :created_at, :lock_version, :sort_order] default_scope {includes(:page).order(['survey_pages.sort_order asc', 'survey_questions.sort_order asc'])} diff --git a/db/structure.sql b/db/structure.sql index 5d1248a49..f9aa395eb 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -3372,4 +3372,3 @@ INSERT INTO "schema_migrations" (version) VALUES ('20220801195644'); - diff --git a/lib/tasks/publications.rake b/lib/tasks/publications.rake index 9596763fb..ae07222d1 100644 --- a/lib/tasks/publications.rake +++ b/lib/tasks/publications.rake @@ -10,4 +10,11 @@ namespace :pubs do Audit::PublishedSessionVersion.delete_all end + + task clear_audit: :environment do + Audit::PublishedSessionVersion.delete_all + Audit::SessionVersion.delete_all + Audit::PersonVersion.delete_all + Audit::SurveyVersion.delete_all + end end