• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pulibrary / pdc_describe / 1916bf36-6a96-449c-8d6f-46663ada0d51

pending completion
1916bf36-6a96-449c-8d6f-46663ada0d51

Pull #836

circleci

Carolyn Cole
Sending a notification to the Depositor and the Collection Administrators when state changes occur Moves the code from the Work Model to it's own class WorkStateTransitionNotification
Pull Request #836: Sending a notification to the Depositor and the Collection Administrators when state changes occur

25 of 25 new or added lines in 2 files covered. (100.0%)

1744 of 1756 relevant lines covered (99.32%)

142.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.19
/app/models/work_activity.rb
1
# frozen_string_literal: true
2

3
require_relative "../lib/diff_tools"
1✔
4

5
# rubocop:disable Metrics/ClassLength
6
class WorkActivity < ApplicationRecord
1✔
7
  CHANGES = "CHANGES"
1✔
8
  COMMENT = "COMMENT"
1✔
9
  FILE_CHANGES = "FILE-CHANGES"
1✔
10
  SYSTEM = "SYSTEM"
1✔
11
  USER_REFERENCE = /@[\w]*/.freeze # e.g. @xy123
1✔
12

13
  include Rails.application.routes.url_helpers
1✔
14

15
  belongs_to :work
1✔
16
  has_many :work_activity_notifications, dependent: :destroy
1✔
17

18
  def self.add_system_activity(work_id, message, user_id, activity_type: "SYSTEM")
1✔
19
    activity = WorkActivity.new(
281✔
20
      work_id: work_id,
21
      activity_type: activity_type,
22
      message: message,
23
      created_by_user_id: user_id
24
    )
25
    activity.save!
281✔
26
    activity.notify_users
281✔
27
    activity
281✔
28
  end
29

30
  # Log notifications for each of the users references on the activity
31
  def notify_users
1✔
32
    users_referenced.each do |uid|
285✔
33
      user_id = User.where(uid: uid).first&.id
154✔
34
      if user_id.nil?
154✔
35
        Rails.logger.info("Message #{id} for work #{work_id} referenced an non-existing user: #{uid}")
3✔
36
      else
37
        WorkActivityNotification.create(work_activity_id: id, user_id: user_id)
151✔
38
      end
39
    end
40
  end
41

42
  # Returns the `uid` of the users referenced on the activity (without the `@` symbol)
43
  def users_referenced
1✔
44
    message.scan(USER_REFERENCE).map { |at_uid| at_uid[1..-1] }
439✔
45
  end
46

47
  def created_by_user
1✔
48
    return nil unless created_by_user_id
162✔
49

50
    User.find(created_by_user_id)
157✔
51
  end
52

53
  def self.unknown_user
1✔
54
    "Unknown user outside the system"
43✔
55
  end
56

57
  def comment_event_type?
1✔
58
    activity_type == COMMENT
288✔
59
  end
60

61
  def changes_event_type?
1✔
62
    activity_type == CHANGES
115✔
63
  end
64

65
  def file_changes_event_type?
1✔
66
    activity_type == FILE_CHANGES
114✔
67
  end
68

69
  def system_event_type?
1✔
70
    activity_type == SYSTEM
189✔
71
  end
72

73
  def log_event_type?
1✔
74
    system_event_type? || file_changes_event_type? || changes_event_type?
108✔
75
  end
76

77
  def event_type
1✔
78
    if comment_event_type?
142✔
79
      "comment"
4✔
80
    else
81
      "log"
138✔
82
    end
83
  end
84

85
  def to_html
1✔
86
    if changes_event_type?
63✔
87
      metadata_changes_html
15✔
88
    elsif file_changes_event_type?
48✔
89
      file_changes_html
7✔
90
    else
91
      comment_html
41✔
92
    end
93
  end
94
  alias message_html to_html
1✔
95

96
  private
1✔
97

98
    def created_at_time
1✔
99
      created_at.time
81✔
100
    end
101

102
    def event_timestamp
1✔
103
      created_at_time.strftime("%B %d, %Y %H:%M")
81✔
104
    end
105

106
    def event_timestamp_html
1✔
107
      "#{event_timestamp} by " if system_event_type? || log_event_type?
81✔
108
    end
109

110
    # This is working
111
    def comment_timestamp_html
1✔
112
      "at #{event_timestamp}" if comment_event_type?
81✔
113
    end
114

115
    def event_html(children:)
1✔
116
      <<-HTML
81✔
117
<div class="activity-history-#{event_type}-title">
118
  #{event_timestamp_html}
119
  #{created_by_user_html}
120
  #{comment_timestamp_html}
121
</div>
122
<span class="comment-html">#{children.chomp}</span>
123
    HTML
124
    end
125

126
    # Returns the message formatted to display _file_ changes that were logged as an activity
127
    def file_changes_html
1✔
128
      changes = JSON.parse(message)
7✔
129
      changes_html = changes.map do |change|
7✔
130
        icon = if change["action"] == "deleted"
13✔
131
                 '<i class="bi bi-file-earmark-minus-fill file-deleted-icon"></i>'
2✔
132
               else
133
                 '<i class="bi bi-file-earmark-plus-fill file-added-icon"></i>'
11✔
134
               end
135
        "<tr><td>#{icon}</td><td>#{change['action']}</td> <td>#{change['filename']}</td>"
13✔
136
      end
137

138
      children = "<p><b>Files updated:</b></p><table>#{changes_html.join}</table>"
7✔
139
      event_html(children: children)
7✔
140
    end
141

142
    # Returns the message formatted to display _metadata_ changes that were logged as an activity
143
    def metadata_changes_html
1✔
144
      text = ""
15✔
145
      changes = JSON.parse(message)
15✔
146

147
      changes.keys.each do |field|
15✔
148
        change = changes[field]
33✔
149
        mapped = change.map { |value| change_value_html(value) }
66✔
150
        values = mapped.join
33✔
151

152
        children = "<details><summary class='show-changes'>#{field}</summary>#{values}</details>"
33✔
153
        text += event_html(children: children)
33✔
154
      end
155

156
      text
15✔
157
    end
158

159
    # rubocop:disable Metrics/MethodLength
160
    def comment_html
1✔
161
      # convert user references to user links
162
      text = message.gsub(USER_REFERENCE) do |at_uid|
41✔
163
        uid = at_uid[1..-1]
38✔
164
        user_info = self.class.unknown_user
38✔
165

166
        if uid
38✔
167
          user = User.find_by(uid: uid)
38✔
168
          user_info = if user
38✔
169
                        user.display_name_safe
38✔
170
                      else
171
                        uid
×
172
                      end
173
        end
174

175
        "<a class='comment-user-link' title='#{user_info}' href='#{users_path}/#{uid}'>#{at_uid}</a>"
38✔
176
      end
177

178
      # allow ``` for code blocks (Kramdown only supports ~~~)
179
      text = text.gsub("```", "~~~")
41✔
180
      parsed_document = Kramdown::Document.new(text)
41✔
181
      children = parsed_document.to_html
41✔
182

183
      event_html(children: children)
41✔
184
    end
185
    # rubocop:enable Metrics/MethodLength
186

187
    def created_by_user_html
1✔
188
      return self.class.unknown_user unless created_by_user
81✔
189

190
      created_by_user.display_name_safe
76✔
191
    end
192

193
    def created_at_html
1✔
194
      return unless created_at
×
195

196
      created_at_time = created_at.time
×
197
      created_at_time.strftime("%B %d, %Y %H:%M")
×
198
    end
199

200
    def change_value_html(value)
1✔
201
      if value["action"] == "changed"
33✔
202
        DiffTools::SimpleDiff.new(value["from"], value["to"]).to_html
33✔
203
      else
204
        "old change"
×
205
      end
206
    end
207
end
208
# rubocop:enable Metrics/ClassLength
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc