blob: 93a74bee2fbeecebb94b5de03281a8fb956381f6 [file] [log] [blame]
#
# Fetch and maintain a local cache of reporter.apache.org "forgotten" drafts.
# Select projects with drafts for the current agenda and sort by attachment.
#
# Note: reporter will return partial results for non-members. Once full
# results are retrieved, serve from the cache for the next minute.
#
class Reporter
@@partial = true
@@agenda = nil
@@digest = nil
def self.drafts(env, update=nil)
changed = false
agenda_file = File.basename(Dir["#{FOUNDATION_BOARD}/board_agenda_*.txt"].max)
if ENV['RACK_ENV'] == 'test'
return {agenda: agenda_file, drafts: []}
end
cache = File.join(ASF::Config.get(:cache), 'reporter-drafts.json')
# read and prune previous status. May be used to "fill in the blanks"
# should a partial set of reports be received
report_status = (JSON.parse(File.read(cache)) rescue {}).
select {|project, status| status['agenda'] == agenda_file}.to_h
# file updates as they are received
if update
report_status.merge! update
File.write cache, report_status.to_json
changed = true
# if cache is older than a minute, fetch new data
elsif not File.exist? cache or File.mtime(cache) < Time.now-60 or @@partial
# source of truth
uri = URI.parse('https://reporter.apache.org/api/drafts/forgotten')
# fetch and merge in latest report statuses
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
request = Net::HTTP::Get.new(uri)
request.basic_auth env.user, env.password
response = http.request(request)
if response.code == "200"
report_status.merge! JSON.parse(response.body)['report_status']
else
Wunderbar.error "Failed to fetch #{uri}: #{response.code}"
end
if not File.exist? cache or JSON.parse(File.read cache) != report_status
changed = true
end
File.write cache, report_status.to_json
@@partial = (not ASF::Person.find(env.user).asf_member?)
end
end
# extract projects with drafts for this agenda
lastMeeting = ASF::Board.lastMeeting.to_i
drafts = report_status.select do |project, status|
next false unless status['agenda'] == agenda_file
last_draft = status['last_draft']
next false if last_draft and status['draft_timestamp'] <= lastMeeting
last_draft and not last_draft.empty?
end
# return agenda and drafts indexed by attachment
results = {
agenda: agenda_file,
drafts: drafts.map {|project, status|
committee = ASF::Committee.find(project)
[status['attach'], {
project: project,
title: (committee ? committee.display_name : project),
timestamp: status['draft_timestamp'],
author: status['last_author'],
text: status['last_draft']
}]
}.to_h
}
if changed
digest = Digest::MD5.hexdigest(JSON.dump(results[:drafts]))
Events.post type: 'reporter', agenda: agenda_file, digest: digest
end
# filter drafts based on user visibility
user = env.respond_to?(:user) && ASF::Person.find(env.user)
unless !user or user.asf_chair_or_member?
projects = user.committees.map(&:name)
results[:drafts].keep_if do |attach, draft|
projects.include? draft[:project]
end
end
@@digest = Digest::MD5.hexdigest(JSON.dump(results[:drafts]))
@@agenda = agenda_file
results[:digest] = @@digest
results
end
# return digest information
def self.digest
{agenda: @@agenda, digest: @@digest}
end
end