-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #294 from hathitrust/DEV-562
DEV-562, report count of commitments by phase & org
- Loading branch information
Showing
8 changed files
with
221 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
require "shared_print/finder" | ||
require "shared_print/phases" | ||
require "utils/report_output" | ||
|
||
# Output a .tsv report of which orgs have how many commitments in a phase. | ||
# Defaults to latest (or, rather, _greatest_) phase. | ||
module Reports | ||
class SharedPrintPhaseCount | ||
attr_reader :phase, :finder, :output | ||
def initialize(phase: SharedPrint::Phases.list.max) | ||
@phase = phase.to_i | ||
# Potentially use Reports::Dynamic instead of SharedPrint::Finder | ||
# (but SP::Finder does the trick just fine for now) | ||
@finder = SharedPrint::Finder.new(phase: [@phase]) | ||
@output = Utils::ReportOutput.new("sp_phase#{@phase}_count") | ||
end | ||
|
||
def run | ||
# Get an output handle. | ||
handle = output.handle("w") | ||
puts "Started writing to #{output.file}" | ||
handle.puts(header) | ||
# Get relevant commitments, tally organizations. | ||
organization_tally = {} | ||
commitments do |commitment| | ||
organization_tally[commitment.organization] ||= 0 | ||
organization_tally[commitment.organization] += 1 | ||
end | ||
# Output tally. | ||
organization_tally.keys.sort.each do |org| | ||
handle.puts [org, phase, organization_tally[org]].join("\t") | ||
end | ||
puts "Finished writing to #{output.file}" | ||
ensure | ||
handle.close | ||
end | ||
|
||
def commitments | ||
return enum_for(:commitments) unless block_given? | ||
|
||
finder.commitments.each do |commitment| | ||
yield commitment | ||
end | ||
end | ||
|
||
def header | ||
["organization", "phase", "commitment_count"].join("\t") | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
require "securerandom" | ||
|
||
# An attempt at simplifying the structure of report output dirs and files. | ||
# Give report_name as input. | ||
# The methods and their return: | ||
# id: the "unique-ish" part of the filename | ||
# dir: path to an output dir | ||
# file: path to an output file | ||
# handle("r"): an open read handle for file | ||
# handle("w"): an open write handle for file | ||
# Both dir and file will contain the report name, | ||
# and the file a timestamp and a unique-ish string. | ||
# dir is autovivified with mkdir_p. | ||
# Example: | ||
# output = Utils::ReportOutput.new("foo", ".txt") | ||
# output.dir # -> "/local_reports/foo" | ||
# output.file # -> "/local_reports/foo/foo_YYYYMMDD_abcd1234.txt" | ||
|
||
module Utils | ||
class ReportOutput | ||
attr_reader :report_name, :ext | ||
def initialize(report_name, ext = ".tsv") | ||
@report_name = report_name # e.g. "cost_report" or "cost_report_umich" | ||
@ext = ext | ||
end | ||
|
||
# IO for file | ||
def handle(rw) # "r", "w" etc | ||
File.open(file, rw) | ||
end | ||
|
||
# Full path to a file in a dir (a dir we are pretty sure exists) | ||
def file | ||
@file ||= File.join(dir, id) + ext | ||
end | ||
|
||
# mkdir to guarantee that it exists | ||
def dir | ||
@dir ||= dir_path.tap do |d| | ||
FileUtils.mkdir_p(d).first | ||
end | ||
end | ||
|
||
# Just the path | ||
def dir_path | ||
File.join(Settings.local_report_path || "/tmp", report_name) | ||
end | ||
|
||
def id | ||
if @id.nil? | ||
timestamp = Time.now.strftime("%Y%m%d_%H%M%S") | ||
# Adding a random string in case 2 identical jobs that write a file | ||
# are started in the same second. | ||
rand_str = SecureRandom.hex[0..8] | ||
# e.g. foo_YYYYMMDD_HHMMSS_abcd1234 | ||
@id = [report_name, timestamp, rand_str].join("_") | ||
end | ||
@id | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
require "reports/shared_print_phase_count" | ||
require "spec_helper" | ||
|
||
RSpec.describe Reports::SharedPrintPhaseCount do | ||
let(:org) { "umich" } | ||
let(:phase) { 3 } | ||
let(:report) { described_class.new(phase: phase) } | ||
let(:commitment_count) { 5 } | ||
before(:each) do | ||
Cluster.collection.find.delete_many | ||
end | ||
it "gets commitments per phase, puts them in a file" do | ||
# Make 5 umich commitments with phase:3 (these we want) | ||
# and 5 umich with phase 0 (decoys) | ||
commitments = [] | ||
1.upto(commitment_count) do |_i| | ||
commitments << build(:commitment, organization: org, phase: phase) | ||
commitments << build(:commitment, organization: org, phase: 0) | ||
end | ||
cluster_tap_save(*commitments) | ||
# Verify that the report script sees the 5 with phase 3 and no decoys. | ||
expect(report.commitments.count).to eq 5 | ||
# Actually run the report and check the output file | ||
report.run | ||
lines = File.read(report.output.file).split("\n") | ||
expect(lines.count).to eq 2 | ||
expect(lines.first).to eq ["organization", "phase", "commitment_count"].join("\t") | ||
expect(lines.last).to eq [org, phase, commitment_count].join("\t") | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
require "spec_helper" | ||
require "utils/report_output" | ||
|
||
RSpec.describe Utils::ReportOutput do | ||
let(:report_name) { "foo" } | ||
let(:ext) { ".bar" } | ||
let(:default_ext) { ".tsv" } | ||
let(:report_output) { described_class.new(report_name, ext) } | ||
it "has working attr_readers" do | ||
expect(report_output.report_name).to eq report_name | ||
expect(report_output.ext).to eq ext | ||
end | ||
it "has a default ext: .tsv" do | ||
expect(described_class.new(report_name).ext).to eq default_ext | ||
end | ||
it "makes dir if missing" do | ||
# dir_path only gives the path ... | ||
# dir makes the path if missing, | ||
dir_path = report_output.dir_path | ||
expect(Dir.exist?(dir_path)).to be false | ||
report_output.dir | ||
expect(Dir.exist?(dir_path)).to be true | ||
end | ||
it "gives a predictable singleton-ish file" do | ||
f1 = report_output.file | ||
f2 = report_output.file | ||
expect(f1).to eq f2 | ||
# Grab a new object if you want a new file | ||
report_output2 = described_class.new(report_name, ext) | ||
f3 = report_output.file | ||
f4 = report_output2.file # this one will be different | ||
expect(f2).to eq f3 | ||
# by power of transitivity we know f1-f3 are the same, | ||
# we only expect f4 to be different | ||
expect(f3).to_not eq f4 | ||
end | ||
it "gives a predictable singleton-ish id" do | ||
id1 = report_output.id | ||
id2 = report_output.id | ||
expect(id1).to eq id2 | ||
# Grab a new object if you want a new id | ||
report_output2 = described_class.new(report_name, ext) | ||
id3 = report_output.id | ||
id4 = report_output2.id # this one will be different | ||
expect(id2).to eq id3 | ||
# by power of transitivity we know id1-id3 are the same, | ||
# we only expect id4 to be different | ||
expect(id3).to_not eq id4 | ||
end | ||
it "gives readable/writable handle" do | ||
# write a string to a write-handle | ||
write_handle = report_output.handle("w") | ||
str = "It was a dark and stormy #{report_output.id} ..." | ||
write_handle.puts(str) | ||
write_handle.close | ||
# and then read that string with a read-handle | ||
read_handle = report_output.handle("r") | ||
lines = read_handle.read.split("\n") | ||
read_handle.close | ||
expect(lines.count).to eq 1 | ||
expect(lines).to eq [str] | ||
end | ||
end |