Sprankelprachtig aan/afmeldsysteem

activity.rb 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. # An Activity represents a single continuous event that the members of a group may attend.
  2. # An Activity belongs to a group, and has many participants.
  3. class Activity < ApplicationRecord
  4. # @!attribute name
  5. # @return [String]
  6. # a short name for the activity.
  7. #
  8. # @!attribute description
  9. # @return [String]
  10. # a short text describing the activity. This text is always visible to
  11. # all users.
  12. #
  13. # @!attribute location
  14. # @return [String]
  15. # a short text describing where the activity will take place. Always
  16. # visible to all participants.
  17. #
  18. # @!attribute start
  19. # @return [TimeWithZone]
  20. # when the activity starts.
  21. #
  22. # @!attribute end
  23. # @return [TimeWithZone]
  24. # when the activity ends.
  25. #
  26. # @!attribute deadline
  27. # @return [TimeWithZone]
  28. # when the normal participants (everyone who isn't an organizer or group
  29. # leader) may not change their own attendance anymore. Disabled if set to
  30. # nil.
  31. #
  32. # @!attribute reminder_at
  33. # @return [TimeWithZone]
  34. # when all participants which haven't responded yet (attending is nil)
  35. # will be automatically set to 'present' and emailed. Must be before the
  36. # deadline, disabled if nil.
  37. #
  38. # @!attribute reminder_done
  39. # @return [Boolean]
  40. # whether or not sending the reminder has finished.
  41. belongs_to :group
  42. has_many :participants,
  43. dependent: :destroy
  44. has_many :people, through: :participants
  45. validates :name, presence: true
  46. validates :start, presence: true
  47. validate :deadline_before_start, unless: "self.deadline.blank?"
  48. validate :end_after_start, unless: "self.end.blank?"
  49. after_create :create_missing_participants!
  50. # Get all people (not participants) that are organizers. Does not include
  51. # group leaders, although they may modify the activity as well.
  52. def organizers
  53. self.participants.includes(:person).where(is_organizer: true)
  54. end
  55. # Determine whether the passed Person participates in the activity.
  56. def is_participant?(person)
  57. Participant.exists?(
  58. activity_id: self.id,
  59. person_id: person.id
  60. )
  61. end
  62. # Determine whether the passed Person is an organizer for the activity.
  63. def is_organizer?(person)
  64. Participant.exists?(
  65. person_id: person.id,
  66. activity_id: self.id,
  67. is_organizer: true
  68. )
  69. end
  70. # Query the database to determine the amount of participants that are present/absent/unknown
  71. def state_counts
  72. self.participants.group(:attending).count
  73. end
  74. # Return participants attending, absent, unknown
  75. def human_state_counts
  76. c = self.state_counts
  77. p = c[true]
  78. a = c[false]
  79. u = c[nil]
  80. return "#{p or 0}, #{a or 0}, #{u or 0}"
  81. end
  82. # Determine whether the passed Person may change this activity.
  83. def may_change?(person)
  84. person.is_admin ||
  85. self.is_organizer?(person) ||
  86. self.group.is_leader?(person)
  87. end
  88. # Create Participants for all People that
  89. # 1. are members of the group
  90. # 2. do not have Participants (and thus, no way to confirm) yet
  91. def create_missing_participants!
  92. people = self.group.people
  93. if not self.participants.empty?
  94. people = people.where('people.id NOT IN (?)', self.people.ids)
  95. end
  96. people.each do |p|
  97. Participant.create(
  98. activity: self,
  99. person: p,
  100. )
  101. end
  102. end
  103. # Create multiple Activities from data in a CSV file, assign to a group, return.
  104. def self.from_csv(content, group)
  105. reader = CSV.parse(content, {headers: true, skip_blanks: true})
  106. result = []
  107. reader.each do |row|
  108. a = Activity.new
  109. a.group = group
  110. a.name = row['name']
  111. a.description = row['description']
  112. a.location = row['location']
  113. sd = Date.strptime(row['start_date'])
  114. st = Time.strptime(row['start_time'], '%H:%M')
  115. a.start = Time.zone.local(sd.year, sd.month, sd.day, st.hour, st.min)
  116. if not row['end_date'].blank?
  117. ed = Date.strptime(row['end_date'])
  118. et = Time.strptime(row['end_time'], '%H:%M')
  119. a.end = Time.zone.local(ed.year, ed.month, ed.day, et.hour, et.min)
  120. end
  121. dd = Date.strptime(row['deadline_date'])
  122. dt = Time.strptime(row['deadline_time'], '%H:%M')
  123. a.deadline = Time.zone.local(dd.year, dd.month, dd.day, dt.hour, dt.min)
  124. result << a
  125. end
  126. result
  127. end
  128. # Send a reminder to all participants who haven't responded, and set their
  129. # response to 'attending'.
  130. def send_reminder
  131. # Sanity check that the reminder date didn't change while queued.
  132. return unless !self.reminder_done && self.reminder_at
  133. return if self.reminder_at > Time.zone.now
  134. participants = self.participants.where(attending: nil)
  135. participants.each { |p| p.send_reminder }
  136. end
  137. private
  138. # Assert that the deadline for participants to change the deadline, if any,
  139. # is set before the event starts.
  140. def deadline_before_start
  141. if self.deadline > self.start
  142. errors.add(:deadline, I18n.t('activities.errors.must_be_before_start'))
  143. end
  144. end
  145. # Assert that the activity's end, if any, occurs after the event's start.
  146. def end_after_start
  147. if self.end < self.start
  148. errors.add(:end, I18n.t('activities.errors.must_be_after_start'))
  149. end
  150. end
  151. end