berkeley_library-tind 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/build.yml +18 -0
- data/.gitignore +388 -0
- data/.idea/inspectionProfiles/Project_Default.xml +20 -0
- data/.idea/misc.xml +4 -0
- data/.idea/modules.xml +8 -0
- data/.idea/tind.iml +138 -0
- data/.idea/vcs.xml +6 -0
- data/.rubocop.yml +334 -0
- data/.ruby-version +1 -0
- data/.simplecov +8 -0
- data/.yardopts +1 -0
- data/CHANGES.md +58 -0
- data/Dockerfile +57 -0
- data/Gemfile +3 -0
- data/Jenkinsfile +18 -0
- data/LICENSE.md +21 -0
- data/README.md +73 -0
- data/Rakefile +20 -0
- data/berkeley_library-tind.gemspec +50 -0
- data/bin/tind-export +14 -0
- data/docker-compose.yml +15 -0
- data/lib/berkeley_library/tind.rb +3 -0
- data/lib/berkeley_library/tind/api.rb +1 -0
- data/lib/berkeley_library/tind/api/api.rb +132 -0
- data/lib/berkeley_library/tind/api/api_exception.rb +131 -0
- data/lib/berkeley_library/tind/api/collection.rb +82 -0
- data/lib/berkeley_library/tind/api/date_range.rb +67 -0
- data/lib/berkeley_library/tind/api/format.rb +32 -0
- data/lib/berkeley_library/tind/api/search.rb +100 -0
- data/lib/berkeley_library/tind/config.rb +103 -0
- data/lib/berkeley_library/tind/export.rb +1 -0
- data/lib/berkeley_library/tind/export/column.rb +54 -0
- data/lib/berkeley_library/tind/export/column_group.rb +144 -0
- data/lib/berkeley_library/tind/export/column_group_list.rb +131 -0
- data/lib/berkeley_library/tind/export/column_width_calculator.rb +76 -0
- data/lib/berkeley_library/tind/export/config.rb +154 -0
- data/lib/berkeley_library/tind/export/csv_exporter.rb +29 -0
- data/lib/berkeley_library/tind/export/export.rb +47 -0
- data/lib/berkeley_library/tind/export/export_command.rb +168 -0
- data/lib/berkeley_library/tind/export/export_exception.rb +8 -0
- data/lib/berkeley_library/tind/export/export_format.rb +67 -0
- data/lib/berkeley_library/tind/export/exporter.rb +105 -0
- data/lib/berkeley_library/tind/export/filter.rb +52 -0
- data/lib/berkeley_library/tind/export/no_results_error.rb +7 -0
- data/lib/berkeley_library/tind/export/ods_exporter.rb +138 -0
- data/lib/berkeley_library/tind/export/row.rb +24 -0
- data/lib/berkeley_library/tind/export/row_metrics.rb +18 -0
- data/lib/berkeley_library/tind/export/table.rb +175 -0
- data/lib/berkeley_library/tind/export/table_metrics.rb +116 -0
- data/lib/berkeley_library/tind/marc.rb +1 -0
- data/lib/berkeley_library/tind/marc/xml_reader.rb +144 -0
- data/lib/berkeley_library/tind/module_info.rb +14 -0
- data/lib/berkeley_library/util/arrays.rb +178 -0
- data/lib/berkeley_library/util/logging.rb +1 -0
- data/lib/berkeley_library/util/ods/spreadsheet.rb +170 -0
- data/lib/berkeley_library/util/ods/xml/content_doc.rb +26 -0
- data/lib/berkeley_library/util/ods/xml/document_node.rb +57 -0
- data/lib/berkeley_library/util/ods/xml/element_node.rb +106 -0
- data/lib/berkeley_library/util/ods/xml/loext/table_protection.rb +26 -0
- data/lib/berkeley_library/util/ods/xml/manifest/file_entry.rb +42 -0
- data/lib/berkeley_library/util/ods/xml/manifest/manifest.rb +73 -0
- data/lib/berkeley_library/util/ods/xml/manifest_doc.rb +26 -0
- data/lib/berkeley_library/util/ods/xml/namespace.rb +46 -0
- data/lib/berkeley_library/util/ods/xml/office/automatic_styles.rb +181 -0
- data/lib/berkeley_library/util/ods/xml/office/body.rb +17 -0
- data/lib/berkeley_library/util/ods/xml/office/document_content.rb +98 -0
- data/lib/berkeley_library/util/ods/xml/office/document_styles.rb +39 -0
- data/lib/berkeley_library/util/ods/xml/office/font_face_decls.rb +30 -0
- data/lib/berkeley_library/util/ods/xml/office/scripts.rb +17 -0
- data/lib/berkeley_library/util/ods/xml/office/spreadsheet.rb +37 -0
- data/lib/berkeley_library/util/ods/xml/office/styles.rb +39 -0
- data/lib/berkeley_library/util/ods/xml/style/cell_style.rb +58 -0
- data/lib/berkeley_library/util/ods/xml/style/column_style.rb +36 -0
- data/lib/berkeley_library/util/ods/xml/style/default_style.rb +31 -0
- data/lib/berkeley_library/util/ods/xml/style/family.rb +85 -0
- data/lib/berkeley_library/util/ods/xml/style/font_face.rb +46 -0
- data/lib/berkeley_library/util/ods/xml/style/paragraph_properties.rb +30 -0
- data/lib/berkeley_library/util/ods/xml/style/row_style.rb +37 -0
- data/lib/berkeley_library/util/ods/xml/style/style.rb +44 -0
- data/lib/berkeley_library/util/ods/xml/style/table_cell_properties.rb +40 -0
- data/lib/berkeley_library/util/ods/xml/style/table_column_properties.rb +30 -0
- data/lib/berkeley_library/util/ods/xml/style/table_properties.rb +25 -0
- data/lib/berkeley_library/util/ods/xml/style/table_row_properties.rb +28 -0
- data/lib/berkeley_library/util/ods/xml/style/table_style.rb +27 -0
- data/lib/berkeley_library/util/ods/xml/style/text_properties.rb +52 -0
- data/lib/berkeley_library/util/ods/xml/styles_doc.rb +26 -0
- data/lib/berkeley_library/util/ods/xml/table/named_expressions.rb +17 -0
- data/lib/berkeley_library/util/ods/xml/table/repeatable.rb +38 -0
- data/lib/berkeley_library/util/ods/xml/table/table.rb +193 -0
- data/lib/berkeley_library/util/ods/xml/table/table_cell.rb +46 -0
- data/lib/berkeley_library/util/ods/xml/table/table_column.rb +43 -0
- data/lib/berkeley_library/util/ods/xml/table/table_row.rb +136 -0
- data/lib/berkeley_library/util/ods/xml/text/p.rb +118 -0
- data/lib/berkeley_library/util/paths.rb +111 -0
- data/lib/berkeley_library/util/stringios.rb +30 -0
- data/lib/berkeley_library/util/strings.rb +42 -0
- data/lib/berkeley_library/util/sys_exits.rb +15 -0
- data/lib/berkeley_library/util/times.rb +22 -0
- data/lib/berkeley_library/util/uris.rb +44 -0
- data/lib/berkeley_library/util/uris/appender.rb +162 -0
- data/lib/berkeley_library/util/uris/requester.rb +62 -0
- data/lib/berkeley_library/util/uris/validator.rb +32 -0
- data/rakelib/bundle.rake +8 -0
- data/rakelib/coverage.rake +11 -0
- data/rakelib/gem.rake +54 -0
- data/rakelib/rubocop.rake +18 -0
- data/rakelib/spec.rake +2 -0
- data/spec/.rubocop.yml +40 -0
- data/spec/berkeley_library/tind/api/api_exception_spec.rb +91 -0
- data/spec/berkeley_library/tind/api/api_spec.rb +143 -0
- data/spec/berkeley_library/tind/api/collection_spec.rb +74 -0
- data/spec/berkeley_library/tind/api/date_range_spec.rb +110 -0
- data/spec/berkeley_library/tind/api/format_spec.rb +54 -0
- data/spec/berkeley_library/tind/api/search_spec.rb +364 -0
- data/spec/berkeley_library/tind/config_spec.rb +86 -0
- data/spec/berkeley_library/tind/export/column_group_spec.rb +29 -0
- data/spec/berkeley_library/tind/export/column_spec.rb +43 -0
- data/spec/berkeley_library/tind/export/config_spec.rb +206 -0
- data/spec/berkeley_library/tind/export/export_command_spec.rb +169 -0
- data/spec/berkeley_library/tind/export/export_format_spec.rb +59 -0
- data/spec/berkeley_library/tind/export/export_matcher.rb +112 -0
- data/spec/berkeley_library/tind/export/export_spec.rb +150 -0
- data/spec/berkeley_library/tind/export/exporter_spec.rb +125 -0
- data/spec/berkeley_library/tind/export/row_spec.rb +118 -0
- data/spec/berkeley_library/tind/export/table_spec.rb +322 -0
- data/spec/berkeley_library/tind/marc/xml_reader_spec.rb +93 -0
- data/spec/berkeley_library/util/arrays_spec.rb +340 -0
- data/spec/berkeley_library/util/ods/spreadsheet_spec.rb +124 -0
- data/spec/berkeley_library/util/ods/xml/content_doc_spec.rb +121 -0
- data/spec/berkeley_library/util/ods/xml/manifest/file_entry_spec.rb +27 -0
- data/spec/berkeley_library/util/ods/xml/manifest/manifest_spec.rb +33 -0
- data/spec/berkeley_library/util/ods/xml/office/document_content_spec.rb +60 -0
- data/spec/berkeley_library/util/ods/xml/style/automatic_styles_spec.rb +37 -0
- data/spec/berkeley_library/util/ods/xml/style/family_spec.rb +57 -0
- data/spec/berkeley_library/util/ods/xml/table/table_row_spec.rb +179 -0
- data/spec/berkeley_library/util/ods/xml/table/table_spec.rb +218 -0
- data/spec/berkeley_library/util/paths_spec.rb +90 -0
- data/spec/berkeley_library/util/stringios_spec.rb +34 -0
- data/spec/berkeley_library/util/strings_spec.rb +27 -0
- data/spec/berkeley_library/util/times_spec.rb +39 -0
- data/spec/berkeley_library/util/uris_spec.rb +118 -0
- data/spec/data/collection-names.txt +438 -0
- data/spec/data/collections.json +4827 -0
- data/spec/data/disjoint-records.xml +187 -0
- data/spec/data/record-184453.xml +58 -0
- data/spec/data/record-184458.xml +63 -0
- data/spec/data/record-187888.xml +78 -0
- data/spec/data/records-api-search-cjk-p1.xml +6381 -0
- data/spec/data/records-api-search-cjk-p2.xml +5 -0
- data/spec/data/records-api-search-p1.xml +4506 -0
- data/spec/data/records-api-search-p2.xml +4509 -0
- data/spec/data/records-api-search-p3.xml +4506 -0
- data/spec/data/records-api-search-p4.xml +4509 -0
- data/spec/data/records-api-search-p5.xml +4506 -0
- data/spec/data/records-api-search-p6.xml +2436 -0
- data/spec/data/records-api-search-p7.xml +5 -0
- data/spec/data/records-api-search.xml +234 -0
- data/spec/data/records-manual-search.xml +547 -0
- data/spec/spec_helper.rb +30 -0
- data/test/profile/table_from_records_profile.rb +46 -0
- metadata +585 -0
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'berkeley_library/util/arrays'
|
2
|
+
require 'berkeley_library/tind/export/exporter'
|
3
|
+
require 'berkeley_library/tind/export/table_metrics'
|
4
|
+
require 'berkeley_library/util/ods/spreadsheet'
|
5
|
+
|
6
|
+
module BerkeleyLibrary
|
7
|
+
module TIND
|
8
|
+
module Export
|
9
|
+
# Exporter for OpenOffice/LibreOffice format
|
10
|
+
class ODSExporter < Exporter
|
11
|
+
|
12
|
+
LOCKED_CELL_COLOR = '#c0362c'.freeze
|
13
|
+
|
14
|
+
# Round column widths up to nearest quarter inch
|
15
|
+
EIGHTH = '1/8'.to_r
|
16
|
+
|
17
|
+
# Max column width before wrapping
|
18
|
+
MAX_COLUMN_WIDTH_INCHES = 3.0
|
19
|
+
|
20
|
+
# Exports {ExportBase#collection} as an OpenOffice/LibreOffice spreadsheet
|
21
|
+
# @overload export
|
22
|
+
# Exports to a new string.
|
23
|
+
# @return [String] a binary string containing the spreadsheet data.
|
24
|
+
# @overload export(out)
|
25
|
+
# Exports to the specified output stream.
|
26
|
+
# @param out [IO] the output stream
|
27
|
+
# @return[void]
|
28
|
+
# @overload export(path)
|
29
|
+
# Exports to the specified file. If `path` denotes a directory, the
|
30
|
+
# spreadsheet will be written as exploded, pretty-printed XML.
|
31
|
+
# @param path [String, Pathname] the path to the output file or directory
|
32
|
+
# @return[void]
|
33
|
+
# @see BerkeleyLibrary::Util::ODS::Spreadsheet#write_exploded_to
|
34
|
+
def export(out = nil)
|
35
|
+
populate_spreadsheet!
|
36
|
+
spreadsheet.write_to(out)
|
37
|
+
end
|
38
|
+
|
39
|
+
# ------------------------------------------------------------
|
40
|
+
# Private methods
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# ------------------------------
|
45
|
+
# Private accessors
|
46
|
+
|
47
|
+
def spreadsheet
|
48
|
+
@spreadsheet ||= BerkeleyLibrary::Util::ODS::Spreadsheet.new
|
49
|
+
end
|
50
|
+
|
51
|
+
def table
|
52
|
+
@table ||= spreadsheet.add_table(collection, protected: false)
|
53
|
+
end
|
54
|
+
|
55
|
+
def table_metrics
|
56
|
+
@table_metrics ||= TableMetrics.new(export_table)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [BerkeleyLibrary::Util::ODS::XML::Office::AutomaticStyles] the styles
|
60
|
+
def styles
|
61
|
+
spreadsheet.auto_styles
|
62
|
+
end
|
63
|
+
|
64
|
+
def color_for(col_index)
|
65
|
+
can_edit?(col_index) ? nil : LOCKED_CELL_COLOR
|
66
|
+
end
|
67
|
+
|
68
|
+
def header_cell_style_for(col_index)
|
69
|
+
@header_cell_styles ||= []
|
70
|
+
@header_cell_styles[col_index] ||= begin
|
71
|
+
find_or_create_cell_style(
|
72
|
+
color: color_for(col_index),
|
73
|
+
font_weight: 'bold'
|
74
|
+
)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def find_or_create_cell_style(color:, font_weight: nil, wrap: false)
|
79
|
+
styles.find_or_create_cell_style(color: color, font_weight: font_weight, wrap: wrap)
|
80
|
+
end
|
81
|
+
|
82
|
+
def column_style_for(col_index)
|
83
|
+
column_width = table_metrics.formatted_width(col_index)
|
84
|
+
@column_styles_by_width ||= {}
|
85
|
+
@column_styles_by_width[column_width] ||= styles.find_or_create_column_style(column_width)
|
86
|
+
end
|
87
|
+
|
88
|
+
# ------------------------------
|
89
|
+
# Private utility methods
|
90
|
+
|
91
|
+
def populate_spreadsheet!
|
92
|
+
logger.info("Populating spreadsheet for #{collection}")
|
93
|
+
populate_columns!
|
94
|
+
populate_rows!
|
95
|
+
end
|
96
|
+
|
97
|
+
def can_edit?(col_index)
|
98
|
+
raise ArgumentError, "Invalid column index: #{col_index.inspect}" unless (column = export_table.columns[col_index])
|
99
|
+
|
100
|
+
column.can_edit?
|
101
|
+
end
|
102
|
+
|
103
|
+
# @param table [BerkeleyLibrary::Util::ODS::XML::Table::Table] the table
|
104
|
+
def populate_columns!
|
105
|
+
export_table.columns.each_with_index do |export_column, col_index|
|
106
|
+
table.add_column_with_styles(
|
107
|
+
export_column.header,
|
108
|
+
column_style: column_style_for(col_index),
|
109
|
+
header_cell_style: header_cell_style_for(col_index)
|
110
|
+
)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# @param table [BerkeleyLibrary::Util::ODS::XML::Table::Table] the table
|
115
|
+
def populate_rows!
|
116
|
+
export_table.row_count.times(&method(:populate_row))
|
117
|
+
end
|
118
|
+
|
119
|
+
def populate_row(row_index)
|
120
|
+
export_row = export_table.rows[row_index]
|
121
|
+
row_metrics = table_metrics.row_metrics_for(row_index)
|
122
|
+
row_height = row_metrics.formatted_row_height
|
123
|
+
row = table.add_row(row_height)
|
124
|
+
populate_values(export_row, row_metrics, row)
|
125
|
+
end
|
126
|
+
|
127
|
+
def populate_values(export_row, row_metrics, row)
|
128
|
+
export_row.each_value.with_index do |v, col_index|
|
129
|
+
wrap = row_metrics.wrap?(col_index)
|
130
|
+
cell_style = find_or_create_cell_style(color: color_for(col_index), wrap: wrap)
|
131
|
+
row.set_value_at(col_index, v, cell_style)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module BerkeleyLibrary
|
2
|
+
module TIND
|
3
|
+
module Export
|
4
|
+
class Row
|
5
|
+
|
6
|
+
attr_reader :columns
|
7
|
+
attr_reader :row_index
|
8
|
+
|
9
|
+
def initialize(columns, row_index)
|
10
|
+
@columns = columns
|
11
|
+
@row_index = row_index
|
12
|
+
end
|
13
|
+
|
14
|
+
def values
|
15
|
+
columns.map { |c| c.value_at(row_index) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def each_value(&block)
|
19
|
+
columns.map { |c| c.value_at(row_index) }.each(&block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module BerkeleyLibrary
|
2
|
+
module TIND
|
3
|
+
module Export
|
4
|
+
class RowMetrics
|
5
|
+
attr_reader :formatted_row_height
|
6
|
+
|
7
|
+
def initialize(formatted_row_height, wrap_columns)
|
8
|
+
@formatted_row_height = formatted_row_height
|
9
|
+
@wrap_columns = wrap_columns
|
10
|
+
end
|
11
|
+
|
12
|
+
def wrap?(col_index)
|
13
|
+
@wrap_columns.include?(col_index)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
require 'marc_extensions'
|
5
|
+
require 'berkeley_library/util/logging'
|
6
|
+
require 'berkeley_library/tind/export/column_group_list'
|
7
|
+
|
8
|
+
module BerkeleyLibrary
|
9
|
+
module TIND
|
10
|
+
module Export
|
11
|
+
class Table
|
12
|
+
include BerkeleyLibrary::Logging
|
13
|
+
|
14
|
+
# ------------------------------------------------------------
|
15
|
+
# Accessors
|
16
|
+
|
17
|
+
attr_reader :column_groups
|
18
|
+
|
19
|
+
# ------------------------------------------------------------
|
20
|
+
# Initializer
|
21
|
+
|
22
|
+
# Initializes a new Table
|
23
|
+
#
|
24
|
+
# @param exportable_only [Boolean] whether to filter out non-exportable fields
|
25
|
+
# @see Tags
|
26
|
+
def initialize(exportable_only: false)
|
27
|
+
@column_groups = ColumnGroupList.new(exportable_only: exportable_only)
|
28
|
+
end
|
29
|
+
|
30
|
+
# ------------------------------------------------------------
|
31
|
+
# Factory method
|
32
|
+
|
33
|
+
class << self
|
34
|
+
# Returns a new Table for the provided MARC records.
|
35
|
+
#
|
36
|
+
# @param records [Enumerable<MARC::Record>] the records
|
37
|
+
# @param freeze [Boolean] whether to freeze the table
|
38
|
+
# @param exportable_only [Boolean] whether to include only exportable fields
|
39
|
+
# @return [Table] the table
|
40
|
+
def from_records(records, freeze: false, exportable_only: false)
|
41
|
+
Table.new(exportable_only: exportable_only).tap do |table|
|
42
|
+
records.each { |r| table << r }
|
43
|
+
table.freeze if freeze
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# ------------------------------------------------------------
|
49
|
+
# Cell accessors
|
50
|
+
|
51
|
+
def value_at(row, col)
|
52
|
+
return unless (column = columns[col])
|
53
|
+
|
54
|
+
column.value_at(row)
|
55
|
+
end
|
56
|
+
|
57
|
+
# ------------------------------------------------------------
|
58
|
+
# Column accessors
|
59
|
+
|
60
|
+
# The column headers
|
61
|
+
#
|
62
|
+
# @return [Array<String>] the column headers
|
63
|
+
def headers
|
64
|
+
columns.map(&:header)
|
65
|
+
end
|
66
|
+
|
67
|
+
# The columns
|
68
|
+
#
|
69
|
+
# @return [Array<Column>] the columns.
|
70
|
+
def columns
|
71
|
+
# NOTE: this isn't ||= because we only cache on #freeze
|
72
|
+
@columns || column_groups.map(&:columns).flatten
|
73
|
+
end
|
74
|
+
|
75
|
+
def column_count
|
76
|
+
columns.size
|
77
|
+
end
|
78
|
+
|
79
|
+
# ------------------------------------------------------------
|
80
|
+
# Row / MARC::Record accessors
|
81
|
+
|
82
|
+
# The rows
|
83
|
+
#
|
84
|
+
# @return [Array<Row>] the rows
|
85
|
+
def rows
|
86
|
+
# NOTE: this isn't ||= because we only cache on #freeze
|
87
|
+
# noinspection RubyYardReturnMatch
|
88
|
+
@rows || each_row.to_a
|
89
|
+
end
|
90
|
+
|
91
|
+
# @yieldparam row [Row] each row
|
92
|
+
def each_row
|
93
|
+
return to_enum(:each_row) unless block_given?
|
94
|
+
|
95
|
+
(0...row_count).each { |row| yield Row.new(columns, row) }
|
96
|
+
end
|
97
|
+
|
98
|
+
# The number of rows (records)
|
99
|
+
#
|
100
|
+
# @return [Integer] the number of rows
|
101
|
+
def row_count
|
102
|
+
marc_records.size
|
103
|
+
end
|
104
|
+
|
105
|
+
# The MARC records
|
106
|
+
#
|
107
|
+
# @return [Array<MARC::Record>] the records
|
108
|
+
def marc_records
|
109
|
+
@marc_records ||= []
|
110
|
+
end
|
111
|
+
|
112
|
+
def empty?
|
113
|
+
marc_records.empty?
|
114
|
+
end
|
115
|
+
|
116
|
+
# ------------------------------------------------------------
|
117
|
+
# Modifiers
|
118
|
+
|
119
|
+
# Adds the specified record
|
120
|
+
#
|
121
|
+
# @param marc_record [MARC::Record] the record to add
|
122
|
+
def <<(marc_record)
|
123
|
+
raise FrozenError, "can't modify frozen MARCTable" if frozen?
|
124
|
+
|
125
|
+
logger.warn('MARC record is not frozen') unless marc_record.frozen?
|
126
|
+
column_groups.add_data_fields(marc_record, marc_records.size)
|
127
|
+
marc_records << marc_record
|
128
|
+
log_record_added(marc_record)
|
129
|
+
|
130
|
+
self
|
131
|
+
end
|
132
|
+
|
133
|
+
# ------------------------------------------------------------
|
134
|
+
# Object overrides
|
135
|
+
|
136
|
+
def frozen?
|
137
|
+
[marc_records, column_groups].all?(&:frozen?) &&
|
138
|
+
[@rows, @columns].all? { |d| !d.nil? && d.frozen? }
|
139
|
+
end
|
140
|
+
|
141
|
+
def freeze
|
142
|
+
[marc_records, column_groups].each(&:freeze)
|
143
|
+
@columns ||= columns.freeze
|
144
|
+
@rows ||= rows.freeze
|
145
|
+
self
|
146
|
+
end
|
147
|
+
|
148
|
+
# ------------------------------------------------------------
|
149
|
+
# Misc. instance methods
|
150
|
+
|
151
|
+
# TODO: move to BerkeleyLibrary::TIND::Export::CSVExporter
|
152
|
+
def to_csv(out = nil)
|
153
|
+
return write_csv(out) if out
|
154
|
+
|
155
|
+
StringIO.new.tap { |io| write_csv(io) }.string
|
156
|
+
end
|
157
|
+
|
158
|
+
# ------------------------------------------------------------
|
159
|
+
# Private methods
|
160
|
+
|
161
|
+
private
|
162
|
+
|
163
|
+
def log_record_added(marc_record)
|
164
|
+
return logger.debug("Added record no. #{marc_record.record_id}: #{row_count} records total") if marc_record
|
165
|
+
end
|
166
|
+
|
167
|
+
def write_csv(out)
|
168
|
+
csv = out.respond_to?(:write) ? CSV.new(out) : CSV.open(out, 'wb')
|
169
|
+
csv << headers
|
170
|
+
each_row { |row| csv << row.values }
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'berkeley_library/util/logging'
|
2
|
+
require 'berkeley_library/tind/export/row_metrics'
|
3
|
+
require 'berkeley_library/tind/export/config'
|
4
|
+
|
5
|
+
module BerkeleyLibrary
|
6
|
+
module TIND
|
7
|
+
module Export
|
8
|
+
class TableMetrics
|
9
|
+
include BerkeleyLibrary::Logging
|
10
|
+
include Config
|
11
|
+
|
12
|
+
# @return [Table] the table
|
13
|
+
attr_reader :table
|
14
|
+
|
15
|
+
# Initializes a new set of metrics for the specified table.
|
16
|
+
#
|
17
|
+
# @param table [Table] the table
|
18
|
+
def initialize(table)
|
19
|
+
@table = table
|
20
|
+
end
|
21
|
+
|
22
|
+
def formatted_width(col_index)
|
23
|
+
inches_numeric = numeric_column_width(col_index)
|
24
|
+
format_inches(inches_numeric)
|
25
|
+
end
|
26
|
+
|
27
|
+
def row_metrics_for(row_index)
|
28
|
+
@metrics_by_row ||= []
|
29
|
+
@metrics_by_row[row_index] ||= calc_row_metrics(row_index)
|
30
|
+
end
|
31
|
+
|
32
|
+
# ------------------------------------------------------------
|
33
|
+
# Private methods
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# ------------------------------
|
38
|
+
# Private accessors
|
39
|
+
|
40
|
+
def inch_format
|
41
|
+
@inch_format ||= "%0.#{format_digits}fin"
|
42
|
+
end
|
43
|
+
|
44
|
+
def numeric_column_width(col_index)
|
45
|
+
@numeric_column_widths ||= []
|
46
|
+
@numeric_column_widths[col_index] ||= calc_max_width(col_index)
|
47
|
+
end
|
48
|
+
|
49
|
+
def cell_widths_for_column(col_index)
|
50
|
+
@cell_widths_by_column ||= []
|
51
|
+
@cell_widths_by_column[col_index] ||= calc_cell_widths(col_index)
|
52
|
+
end
|
53
|
+
|
54
|
+
# ------------------------------
|
55
|
+
# Private utility methods
|
56
|
+
|
57
|
+
def calc_max_width(col_index)
|
58
|
+
header = table.columns[col_index].header
|
59
|
+
w_header = ColumnWidthCalculator.width_inches(header)
|
60
|
+
w_cell_max = cell_widths_for_column(col_index).max
|
61
|
+
w_max_actual = [w_header, w_cell_max].max
|
62
|
+
w_max_rounded = (w_max_actual / w_incr_in).ceil * w_incr_in
|
63
|
+
[w_max_rounded, max_col_width_in].min
|
64
|
+
end
|
65
|
+
|
66
|
+
def calc_cell_widths(col_index)
|
67
|
+
column = table.columns[col_index]
|
68
|
+
column.each_value.map { |v| ColumnWidthCalculator.width_inches(v) }
|
69
|
+
end
|
70
|
+
|
71
|
+
def format_inches(inches_numeric)
|
72
|
+
format(inch_format, inches_numeric)
|
73
|
+
end
|
74
|
+
|
75
|
+
def calc_row_metrics(row_index)
|
76
|
+
columns_to_wrap = Set.new
|
77
|
+
max_wrapped_lines = 1
|
78
|
+
|
79
|
+
table.column_count.times do |col_index|
|
80
|
+
wrapped_lines = wrapped_lines_for_cell(row_index, col_index)
|
81
|
+
max_wrapped_lines = [wrapped_lines, max_wrapped_lines].max
|
82
|
+
columns_to_wrap << col_index if wrapped_lines > 1
|
83
|
+
end
|
84
|
+
|
85
|
+
formatted_height = format_row_height(max_wrapped_lines)
|
86
|
+
RowMetrics.new(formatted_height, columns_to_wrap)
|
87
|
+
end
|
88
|
+
|
89
|
+
# TODO: something smarter, maybe guess at break locations?
|
90
|
+
def wrapped_lines_for_cell(row_index, col_index)
|
91
|
+
w_col = numeric_column_width(col_index)
|
92
|
+
w_cell = numeric_cell_width(row_index, col_index)
|
93
|
+
(w_cell / w_col).ceil
|
94
|
+
end
|
95
|
+
|
96
|
+
def numeric_cell_width(row_index, col_index)
|
97
|
+
cell_widths_by_row = cell_widths_for_column(col_index)
|
98
|
+
cell_widths_by_row[row_index] || 0
|
99
|
+
end
|
100
|
+
|
101
|
+
def numeric_line_height_inches
|
102
|
+
@numeric_line_height_inches ||= begin
|
103
|
+
h_exact_pts = font_size_pt * line_height_em
|
104
|
+
h_rounded_pts = (h_exact_pts / h_incr_pt).ceil * h_incr_pt
|
105
|
+
h_rounded_pts / 72.0
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def format_row_height(lines)
|
110
|
+
@formatted_row_heights ||= []
|
111
|
+
@formatted_row_heights[lines] ||= format_inches(lines * numeric_line_height_inches)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|