berkeley_library-tind 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|