relaton-render 0.1.0 → 0.3.3

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.
@@ -1,96 +0,0 @@
1
- class Iso690Parse
2
- def extract_orgname(org)
3
- name = org.at("./name")
4
- name&.text
5
- end
6
-
7
- def extract_personname(person)
8
- surname = person.at("./name/surname") || person.at("./name/completename")
9
- given, middle, initials = given_and_middle_name(person)
10
- { surname: surname&.text,
11
- given: given,
12
- middle: middle,
13
- initials: initials }
14
- end
15
-
16
- def given_and_middle_name(person)
17
- forenames = person.xpath("./name/forename")&.map(&:text)
18
- initials = person.xpath("./name/initial")&.map(&:text)
19
- forenames.empty? and initials.empty? and return [nil, nil, nil]
20
- forenames.empty? and forenames = initials.dup
21
- initials.empty? and initials = forenames.map { |x| x[0] }
22
- [forenames.first, forenames[1..-1], initials]
23
- end
24
-
25
- def extractname(contributor)
26
- org = contributor.at("./organization")
27
- person = contributor.at("./person")
28
- return { surname: extract_orgname(org) } if org
29
- return extract_personname(person) if person
30
-
31
- nil
32
- end
33
-
34
- def contributor_role(contributors)
35
- return nil unless contributors.length.positive?
36
-
37
- desc = contributors[0].at("role/description")&.text
38
- type = contributors[0].at("role/@type")&.text
39
- return nil if %w(author publisher).include?(type) && desc.nil?
40
-
41
- type
42
- end
43
-
44
- def creatornames(doc)
45
- cr = creatornames1(doc)
46
- cr.empty? and return [nil, nil]
47
- [cr.map { |x| extractname(x) }, contributor_role(cr)]
48
- end
49
-
50
- def creatornames1(doc)
51
- cr = []
52
- return cr if doc.nil?
53
-
54
- %w(author performer adapter translator editor publisher distributor)
55
- .each do |r|
56
- add = doc.xpath("./contributor[role/@type = '#{r}']")
57
- next if add.empty?
58
-
59
- cr = add and break
60
- end
61
- cr.empty? and cr = doc.xpath("./contributor")
62
- cr
63
- end
64
-
65
- def date1(date)
66
- on = date.at("./on")
67
- from = date.at("./from")
68
- to = date.at("./to")
69
- return { on: on.text } if on
70
- return { from: from.text, to: to&.text } if from
71
-
72
- nil
73
- end
74
-
75
- def date(doc, host)
76
- x = doc.at("./date[@type = 'issued']") ||
77
- doc.at("./date[@type = 'circulated']") ||
78
- doc.at("./date") ||
79
- host&.at("./date[@type = 'issued']") ||
80
- host&.at("./date[@type = 'circulated']") ||
81
- host&.at("./date") or return nil
82
- date1(x)
83
- end
84
-
85
- def date_updated(doc, host)
86
- x = doc.at("./date[@type = 'updated']") ||
87
- host&.at("./date[@type = 'updated']") or return nil
88
- date1(x)
89
- end
90
-
91
- def date_accessed(doc, host)
92
- x = doc.at("./date[@type = 'accessed']") ||
93
- host&.at("./date[@type = 'accessed']") or return nil
94
- date1(x)
95
- end
96
- end
@@ -1,155 +0,0 @@
1
- class Iso690Parse
2
- def title(doc)
3
- doc&.at("./title")&.text
4
- end
5
-
6
- def medium(doc, host)
7
- x = doc.at("./medium") || host&.at("./medium") or return nil
8
-
9
- %w(content genre form carrier size scale).each_with_object([]) do |i, m|
10
- m << x.at("./#{i}")&.text
11
- end.compact.join(", ")
12
- end
13
-
14
- def blank?(text)
15
- text.nil? || text.empty?
16
- end
17
-
18
- def edition(doc, host)
19
- x = doc.at("./edition") || host&.at("./edition") or return nil
20
-
21
- x.text
22
- end
23
-
24
- def place(doc, host)
25
- x = doc.at("./place") || host&.at("./place") or return nil
26
-
27
- x.text
28
- end
29
-
30
- def publisher(doc, host)
31
- x = doc.at("./contributor[role/@type = 'publisher']/organization/name") ||
32
- host&.at("./contributor[role/@type = 'publisher']/organization/name") or
33
- return nil
34
- x.text
35
- end
36
-
37
- def distributor(doc, host)
38
- x = doc.at("./contributor[role/@type = 'distributor']/organization/name") ||
39
- host&.at("./contributor[role/@type = 'distributor']/organization/name") or
40
- return nil
41
- x.text
42
- end
43
-
44
- def series_title(doc)
45
- doc&.at("./title")&.text || doc&.at("./formattedref")&.text
46
- end
47
-
48
- def series_abbr(doc)
49
- doc&.at("./abbreviation")&.text
50
- end
51
-
52
- def series_num(doc)
53
- doc&.at("./number")&.text
54
- end
55
-
56
- def series_partnumber(doc)
57
- doc&.at("./partnumber")&.text
58
- end
59
-
60
- def series_run(doc)
61
- doc&.at("./run")&.text
62
- end
63
-
64
- def standardidentifier(doc)
65
- doc.xpath("./docidentifier").each_with_object([]) do |id, ret|
66
- ret << id.text unless %w(metanorma metanorma-ordinal).include? id["type"]
67
- end
68
- end
69
-
70
- def uri(doc)
71
- uri = doc.at("./uri[@type = 'doi']") || doc.at("./uri[@type = 'uri']") ||
72
- doc.at("./uri[@type = 'src']") || doc.at("./uri")
73
- uri&.text
74
- end
75
-
76
- def access_location(doc, host)
77
- x = doc.at("./accessLocation") || host&.at("./accessLocation") or
78
- return nil
79
- x.text
80
- end
81
-
82
- def included(type)
83
- ["article", "inbook", "incollection", "inproceedings"].include? type
84
- end
85
-
86
- def wrap(text, startdelim = " ", enddelim = ".")
87
- return "" if blank?(text)
88
-
89
- "#{startdelim}#{text}#{enddelim}"
90
- end
91
-
92
- def type(doc)
93
- type = doc.at("./@type") and return type&.text
94
- doc.at("./relation[@type = 'includedIn']") and return "inbook"
95
- "book"
96
- end
97
-
98
- def extent1(localities)
99
- localities.each_with_object({}) do |l, ret|
100
- ret[(l["type"] || "page").to_sym] = {
101
- from: l.at("./referenceFrom")&.text,
102
- to: l.at("./referenceTo")&.text,
103
- }
104
- end
105
- end
106
-
107
- def extent0(elem, acc, ret1)
108
- case elem.name
109
- when "localityStack"
110
- acc << ret1
111
- ret1 = {}
112
- acc << extent1(elem.elements)
113
- when "locality" then ret1.merge!(extent1([elem]))
114
- when "referenceFrom" then ret1.merge!(extent1([elem.parent]))
115
- end
116
- [acc, ret1]
117
- end
118
-
119
- def extent(doc)
120
- ret1 = {}
121
- ret = doc.xpath("./extent").each_with_object([]) do |e, acc|
122
- e.elements.each do |l|
123
- acc, ret1 = extent0(l, acc, ret1)
124
- break if l.name == "referenceFrom"
125
- end
126
- end
127
- ret << ret1
128
- ret.reject(&:empty?)
129
- end
130
-
131
- def draft(doc)
132
- dr = doc&.at("./status/stage")&.text
133
-
134
- iterord = iter_ordinal(doc)
135
- status = status_print(dr)
136
- status = "#{iterord} #{status}" if iterord
137
- status
138
- end
139
-
140
- def iter_ordinal(isoxml)
141
- return nil unless isoxml.at(("./status/iteration"))
142
-
143
- iter = isoxml.at(("./status/iteration"))&.text || "1"
144
- iter.to_i.localize.to_rbnf_s("SpelloutRules",
145
- "spellout-ordinal").capitalize
146
- end
147
-
148
- def status_print(status)
149
- status
150
- end
151
-
152
- def status(doc)
153
- doc&.at("./status/stage")&.text
154
- end
155
- end
@@ -1,151 +0,0 @@
1
- module Relaton
2
- module Render
3
- class Fields
4
- def initialize(options)
5
- @r = options[:renderer]
6
- end
7
-
8
- def compound_fields_format(hash)
9
- name_fields_format(hash)
10
- role_fields_format(hash)
11
- date_fields_format(hash)
12
- misc_fields_format(hash)
13
- end
14
-
15
- def name_fields_format(hash)
16
- hash[:creatornames] = nameformat(hash[:creators])
17
- hash[:host_creatornames] = nameformat(hash[:host_creators])
18
- end
19
-
20
- def role_fields_format(hash)
21
- hash[:role] = role_inflect(hash[:creators], hash[:role_raw])
22
- hash[:host_role] =
23
- role_inflect(hash[:host_creators], hash[:host_role_raw])
24
- end
25
-
26
- def misc_fields_format(hash)
27
- hash[:series] = seriesformat(hash)
28
- hash[:edition] = editionformat(hash[:edition_raw])
29
- hash[:extent] = extentformat(hash[:extent_raw], hash)
30
- hash
31
- end
32
-
33
- def date_fields_format(hash)
34
- hash[:date] = dateformat(hash[:date], hash)
35
- hash[:date_updated] = dateformat(hash[:date_updated], hash)
36
- hash[:date_accessed] = dateformat(hash[:date_accessed], hash)
37
- end
38
-
39
- def seriesformat(hash)
40
- parts = %i(series_title series_abbr series_num series_partnumber
41
- series_run)
42
- series_out = parts.each_with_object({}) do |i, m|
43
- m[i] = hash[i]
44
- end
45
- t = hash[:type] == "article" ? @r.journaltemplate : @r.seriestemplate
46
- t.render(series_out)
47
- end
48
-
49
- def nameformat(names)
50
- return names if names.nil?
51
-
52
- parts = %i(surname initials given middle)
53
- names_out = names.each_with_object({}) do |n, m|
54
- parts.each do |i|
55
- m[i] ||= []
56
- m[i] << n[i]
57
- end
58
- end
59
- @r.nametemplate.render(names_out)
60
- end
61
-
62
- def role_inflect(contribs, role)
63
- return nil if role.nil? || contribs.size.zero?
64
-
65
- number = contribs.size > 1 ? "pl" : "sg"
66
- @r.i18n.get[role][number] || role
67
- end
68
-
69
- def editionformat(edn)
70
- return edn unless /^\d+$/.match?(edn)
71
-
72
- num = edn.to_i.localize(@r.lang.to_sym)
73
- .to_rbnf_s(*@r.edition_number)
74
- @r.edition.sub(/%/, num)
75
- end
76
-
77
- def extentformat(extent, hash)
78
- extent.map do |e|
79
- extent_out = e.merge(type: hash[:type],
80
- host_title: hash[:host_title])
81
- .transform_values do |v|
82
- v.is_a?(Hash) ? range(v) : v
83
- end
84
- @r.extenttemplate.render(extent_out.merge(orig: e))
85
- end.join("; ")
86
- end
87
-
88
- def range(hash)
89
- if hash[:on] then hash[:on]
90
- elsif hash.has_key?(:from) && hash[:from].nil? then nil
91
- elsif hash[:from]
92
- hash[:to] ? "#{hash[:from]}&#x2013;#{hash[:to]}" : hash[:from]
93
- else hash
94
- end
95
- end
96
-
97
- def date_range(hash)
98
- if hash[:from]
99
- "#{hash[:from]}&#x2013;#{hash[:to]}"
100
- else range(hash)
101
- end
102
- end
103
-
104
- def dateformat(date, hash)
105
- return nil if date.nil?
106
-
107
- %i(from to on).each do |k|
108
- date[k] = daterender(date[k], hash)
109
- end
110
- date_range(date)
111
- end
112
-
113
- def daterender(date, _hash)
114
- return date if date.nil? || /^\d+$/.match?(date)
115
-
116
- daterender1(date, dategranularity(date), hash)
117
- end
118
-
119
- def daterender1(date, format, _hash)
120
- datef = dateparse(date, format, @r.lang.to_sym)
121
- case @r.date[format]
122
- when "to_full_s", "to_long_s", "to_medium_s", "to_short_s"
123
- datef.send @r.date[format]
124
- else
125
- datef.to_additional_s(@r.date[format])
126
- end
127
- end
128
-
129
- private
130
-
131
- def dategranularity(date)
132
- case date
133
- when /^\d+-\d+$/ then "month_year"
134
- when /^\d+-\d+-\d+$/ then "day_month_year"
135
- else "date_time"
136
- end
137
- end
138
-
139
- def dateparse(date, format, lang)
140
- case format
141
- when "date_time" then DateTime.parse(date)
142
- .localize(lang, timezone: "Zulu")
143
- when "day_month_year" then DateTime.parse(date)
144
- .localize(lang, timezone: "Zulu").to_date
145
- when "month_year" then Date.parse(date)
146
- .localize(lang, timezone: "Zulu").to_date
147
- end
148
- end
149
- end
150
- end
151
- end
@@ -1,152 +0,0 @@
1
- require_relative "../utils/utils"
2
-
3
- module Relaton
4
- module Render
5
- class Iso690Template
6
- def initialize(opt = {})
7
- opt = Utils::sym_keys(opt)
8
- @i18n = opt[:i18n]
9
- @template_raw = opt[:template].dup
10
- @template =
11
- case opt[:template]
12
- when Hash
13
- opt[:template].transform_values { |x| template_process(x) }
14
- when Array then opt[:template].map { |x| template_process(x) }
15
- else { default: template_process(opt[:template]) }
16
- end
17
- end
18
-
19
- # denote start and end of field,
20
- # so that we can detect empty fields in postprocessing
21
- FIELD_DELIM = "\u0018".freeze
22
-
23
- # use tab internally for non-spacing delimiter
24
- NON_SPACING_DELIM = "\t".freeze
25
-
26
- def template_process(template)
27
- t = template.gsub(/\{\{/, "#{FIELD_DELIM}{{")
28
- .gsub(/\}\}/, "}}#{FIELD_DELIM}")
29
- .gsub(/\t/, " ")
30
- t1 = t.split(/(\{\{.+?\}\})/).map do |n|
31
- n.include?("{{") ? n : n.gsub(/(?<!\\)\|/, "\t")
32
- end.join
33
- Liquid::Template.parse(t1)
34
- end
35
-
36
- def render(hash)
37
- template_clean(template_select(hash)
38
- .render(liquid_hash(hash.merge("labels" => @i18n.get))))
39
- end
40
-
41
- def template_select(_hash)
42
- @template[:default]
43
- end
44
-
45
- # use tab internally for non-spacing delimiter
46
- def template_clean(str)
47
- str = str.gsub(/\S*#{FIELD_DELIM}#{FIELD_DELIM}\S*/o, "")
48
- .gsub(/#{FIELD_DELIM}/o, "")
49
- .gsub(/_/, " ")
50
- .gsub(/([,:;]\s*)+([,:;](\s|$))/, "\\2")
51
- .gsub(/([,.:;]\s*)+([.](\s|$))/, "\\2")
52
- .gsub(/(:\s+)(&\s)/, "\\2")
53
- .gsub(/\s+([,.:;])/, "\\1")
54
- .gsub(/#{NON_SPACING_DELIM}/o, "").gsub(/\s+/, " ")
55
- str.strip
56
- end
57
-
58
- def liquid_hash(hash)
59
- case hash
60
- when Hash
61
- hash.map { |k, v| [k.to_s, liquid_hash(v)] }.to_h
62
- when Array
63
- hash.map { |v| liquid_hash(v) }
64
- when String
65
- hash.empty? ? nil : hash
66
- else hash
67
- end
68
- end
69
- end
70
-
71
- class Iso690SeriesTemplate < Iso690Template
72
- end
73
-
74
- class Iso690ExtentTemplate < Iso690Template
75
- def template_select(hash)
76
- t = @template_raw[hash[:type].to_sym]
77
- hash.each do |k, _v|
78
- next unless hash[:orig][k].is_a?(Hash)
79
-
80
- num = number(hash[:type], hash[:orig][k])
81
- t = t.gsub(/labels\[['"]extent['"]\]\[['"]#{k}['"]\]/,
82
- "\\0['#{num}']")
83
- end
84
- t = t.gsub(/labels\[['"]extent['"]\]\[['"][^\]'"]+['"]\](?!\[)/,
85
- "\\0['sg']")
86
- template_process(t)
87
- end
88
-
89
- def number(type, value)
90
- return "pl" if value[:to]
91
- return "sg" if %w(article incollection inproceedings inbook)
92
- .include?(type) || value[:host_title]
93
-
94
- value[:from] == "1" ? "sg" : "pl"
95
- end
96
- end
97
-
98
- class Iso690NameTemplate < Iso690Template
99
- def initialize(opt = {})
100
- @etal_count = opt[:template]["etal_count"]
101
- opt[:template].delete("etal_count")
102
- super
103
- end
104
-
105
- def template_select(names)
106
- case names[:surname].size
107
- when 1 then @template[:one]
108
- when 2 then @template[:two]
109
- when 3 then @template[:more]
110
- else
111
- if @etal_count && names.size >= @etal_count
112
- @template[:etal]
113
- else expand_nametemplate(@template_raw[:more], names.size)
114
- end
115
- end
116
- end
117
-
118
- # assumes that template contains, consecutively and not interleaved,
119
- # ...[0], ...[1], ...[2]
120
- def expand_nametemplate(template, size)
121
- t = nametemplate_split(template)
122
- mid = (1..size - 1).each_with_object([]) do |i, m|
123
- m << t[1].gsub(/\[1\]/, "[#{i}]")
124
- end
125
- template_process(t[0] + mid.join + t[2].gsub(/\[2\]/, "[#{size}]"))
126
- end
127
-
128
- def nametemplate_split(template)
129
- curr = 0
130
- prec = ""
131
- t = template.split(/(\{\{.+?\}\})/)
132
- .each_with_object(["", "", ""]) do |n, m|
133
- m, curr, prec = nametemplate_split1(n, m, curr, prec)
134
- m
135
- end
136
- t[-1] += prec
137
- t
138
- end
139
-
140
- def nametemplate_split1(elem, acc, curr, prec)
141
- if match = /\{\{.+?\[(\d)\]/.match(elem)
142
- curr += 1 if match[1].to_i > curr
143
- acc[curr] += prec
144
- prec = ""
145
- acc[curr] += elem
146
- else prec += elem
147
- end
148
- [acc, curr, prec]
149
- end
150
- end
151
- end
152
- end