bipm-data-importer 0.2.2 → 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 +4 -4
- data/README.adoc +68 -9
- data/TODO.refactor/01-remove-debug-code.md +18 -0
- data/TODO.refactor/02-fix-gemspec.md +21 -0
- data/TODO.refactor/03-update-readme.md +28 -0
- data/TODO.refactor/04-replace-require-relative-with-autoload.md +34 -0
- data/TODO.refactor/05-fix-outcomes-bugs.md +22 -0
- data/TODO.refactor/06-unify-clause-taxonomy.md +31 -0
- data/TODO.refactor/07-extract-french-parsing.md +21 -0
- data/TODO.refactor/08-extract-data-quirks.md +25 -0
- data/TODO.refactor/09-introduce-body-registry.md +23 -0
- data/TODO.refactor/10-introduce-strategy-pattern.md +34 -0
- data/TODO.refactor/11-extract-cli-class.md +20 -0
- data/TODO.refactor/12-specs-common.md +23 -0
- data/TODO.refactor/13-specs-cgpm.md +22 -0
- data/TODO.refactor/14-specs-asciimath.md +23 -0
- data/TODO.refactor/15-specs-outcomes.md +24 -0
- data/TODO.refactor/16-quarantine-failing-specs.md +23 -0
- data/TODO.refactor/17-typed-domain-models.md +32 -0
- data/TODO.refactor/README.md +62 -0
- data/bipm-data-importer.gemspec +11 -7
- data/exe/bipm-fetch +2 -440
- data/lib/bipm/data/importer/bodies.rb +45 -0
- data/lib/bipm/data/importer/body.rb +43 -0
- data/lib/bipm/data/importer/cgpm.rb +21 -0
- data/lib/bipm/data/importer/clauses.rb +239 -0
- data/lib/bipm/data/importer/cli.rb +83 -0
- data/lib/bipm/data/importer/common.rb +31 -120
- data/lib/bipm/data/importer/fetcher.rb +62 -0
- data/lib/bipm/data/importer/language.rb +66 -0
- data/lib/bipm/data/importer/quirks.rb +73 -0
- data/lib/bipm/data/importer/strategies/base.rb +40 -0
- data/lib/bipm/data/importer/strategies/spa_meetings.rb +340 -0
- data/lib/bipm/data/importer/strategies/static_index.rb +324 -0
- data/lib/bipm/data/importer/strategies.rb +13 -0
- data/lib/bipm/data/importer/text/french.rb +49 -0
- data/lib/bipm/data/importer/text.rb +11 -0
- data/lib/bipm/data/importer/version.rb +1 -1
- data/lib/bipm/data/importer.rb +31 -4
- data/lib/bipm/data/outcomes/action.rb +1 -1
- data/lib/bipm/data/outcomes/approval.rb +1 -1
- data/lib/bipm/data/outcomes/body.rb +1 -1
- data/lib/bipm/data/outcomes/consideration.rb +1 -1
- data/lib/bipm/data/outcomes/localized_body.rb +1 -1
- data/lib/bipm/data/outcomes/meeting.rb +2 -2
- data/lib/bipm/data/outcomes/resolution.rb +1 -1
- data/lib/bipm/data/outcomes.rb +9 -13
- data/lib/bipm-data-importer.rb +1 -1
- metadata +46 -19
- data/.hound.yml +0 -5
- data/.rspec +0 -3
- data/.rubocop.yml +0 -10
- data/exe/bipm-fetch-cgpm +0 -3
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Single source of truth for the clause taxonomy used by every scraping
|
|
4
|
+
# strategy. Replaces the previously-duplicated CONSIDERATIONS/ACTIONS hashes
|
|
5
|
+
# in common.rb and the VERB_TO_TYPE/FR_VERB_TO_EN/CONSIDERATION_VERBS/
|
|
6
|
+
# ACTION_VERBS constants in cgpm.rb.
|
|
7
|
+
#
|
|
8
|
+
# Design principle: French and English are first-class citizens. The VERBS
|
|
9
|
+
# table is keyed by either language's verb form and maps directly to a
|
|
10
|
+
# (type, category) pair — there is no "translate FR → EN then look up"
|
|
11
|
+
# step. Adding a new language means adding new keys to the same table.
|
|
12
|
+
module Bipm
|
|
13
|
+
module Data
|
|
14
|
+
module Importer
|
|
15
|
+
module Clauses
|
|
16
|
+
CATEGORY_CONSIDERATION = :consideration
|
|
17
|
+
CATEGORY_ACTION = :action
|
|
18
|
+
|
|
19
|
+
# Phrase-level regex patterns. Order matters: tried top-to-bottom,
|
|
20
|
+
# first match wins. Two hashes preserved (rather than one merged
|
|
21
|
+
# table) so that consumers can ask "is this text a consideration?"
|
|
22
|
+
# without scanning action patterns. Patterns themselves freely mix
|
|
23
|
+
# English and French alternatives — no language is canonical.
|
|
24
|
+
CONSIDERATIONS = {
|
|
25
|
+
/(?:having(?: regard)?|ayant|concerne|vu la|agissant conformément|sachant|de porter)/i => "having / having regard",
|
|
26
|
+
/(?:noting|to note|took note|note[sd]?|taking note|takes note|constatant|constate|that|notant|notant que|note également|(?:prend|prenant) (?:acte|note))/i => "noting",
|
|
27
|
+
/(?:recognizing|recognizes|reconnaissant|reconnaît)/i => "recognizing",
|
|
28
|
+
/(?:acknowledging|admet|entendu|(?:and |)aware that|anticipa(?:ting|nt))/i => "acknowledging",
|
|
29
|
+
/(?:(?:further )?recall(?:ing|s|ed)|rappelant|rappelle|rappelantla)/i => "recalling / further recalling",
|
|
30
|
+
/(?:re-?affirm(?:ing|s)|réaffirme)/i => "reaffirming",
|
|
31
|
+
/(?:consid(?:er(?:ing|)|érant|ère|ers|ered|érantque|érantle)|après examen|estime|is of the opinion|examinera|en raison|by reason)/i => "considering",
|
|
32
|
+
/(?:The Consultative Committee for Time and Frequency \(CCTF\), at|Le Comité consultatif du temps et des fréquences \(CCTF\), à)/i => "considering",
|
|
33
|
+
/(?:taking into account|(prend|prenant) en considération|taking into consideration|tenant compte|envisager)/i => "taking into account",
|
|
34
|
+
"pursuant to" => "pursuant to",
|
|
35
|
+
/(?:bearing in mind)/i => "bearing in mind",
|
|
36
|
+
/(?:emphasizing|soulignant)/i => "emphasizing",
|
|
37
|
+
"concerned" => "concerned",
|
|
38
|
+
/(?:accept(?:s|ed|ing|e)|acceptant)/i => "accepts",
|
|
39
|
+
/(?:observing|observant que|r[ée]ali(?:zing|sant))/i => "observing",
|
|
40
|
+
/(?:referring|se référant)/i => "referring",
|
|
41
|
+
/(?:acting in accordance|agissant conformément|conformément)/i => "acting",
|
|
42
|
+
/(?:empowered by|habilité par)/i => "empowers",
|
|
43
|
+
}.freeze
|
|
44
|
+
|
|
45
|
+
ACTIONS = {
|
|
46
|
+
/(?:adopts|adopt[eé]d?|convient d'adopter)/ => "adopts",
|
|
47
|
+
/(?:thanks|thanked|expresse[sd](?:[ -]| its )appreciation|appréciant|pays tribute|rend hommage|remercie|support(?:ed|s))/i => "thanks / expresses-appreciation",
|
|
48
|
+
/(?:approu?v[eé][ds]?|approuv[ae]nt|approving|entérine|(?:It was )?agree[sd]?|soutient|exprime son accord|n'est pas d'accord|convient)/i => "approves",
|
|
49
|
+
/(?:d[eé]cid(?:e[ds]?|é)|ratifies?|r[eé]vised?)/i => "decides",
|
|
50
|
+
/(?:d[ée]clares?|d[ée]finition)/i => "declares",
|
|
51
|
+
/(?:The unit of length is|Supplementary units|Derived units|Principl?es|Les Délégués des États|Les v(?:œ|\u{9C})ux ou propositions)/i => "declares",
|
|
52
|
+
/(?:L'unité de longueur|Unités supplémentaires|Unités dérivées|(?:\*_)?New candle|(?:\*_)?New lumen|(?:A\) )?D[ée]finitions (?:of|des)|Cubic decimetre|Clarification of|Revision of)/i => "declares",
|
|
53
|
+
/(?:Unit of force|Définitions des|Décimètre cube|Étalons secondaires|Unité spéciale|Efficacités lumineuses|From three names|Entre les trois termes)/i => "declares",
|
|
54
|
+
/(?:Unité de force|(?:Joule|Watt|Volt|Ohm|Amp[eè]re|Coulomb|Farad|Henry|Weber) \(unité?|Bougie nouvelle|Lumen nouveau|announces that|annonce que)/i => "declares",
|
|
55
|
+
/(?:Les unités photométriques|\(A\) D[eé]finitions|The photometric units|will (?:provide|circulate|issue|identify|notify|contact|review))/i => "declares",
|
|
56
|
+
/(?:Appendix 1 of the|L'Annexe 1 de la|increased|a (?:examiné|préparé)|transmettra|fournira|increased|developed a document|prendra contact)/i => "declares",
|
|
57
|
+
/(?:Le Temps Atomique International |International Atomic Time \(TAI\) |will meet )/i => "declares",
|
|
58
|
+
/(?:ask[s ]|asked|souhaite|souhaiterait)/i => "asks",
|
|
59
|
+
/(?:(?:further |et )?invit(?:[ée][ds]?|era)|renouvelle en conséquence|convient d'inviter)/i => "invites / further invites",
|
|
60
|
+
/(?:resolve[sd]?)/i => "resolves",
|
|
61
|
+
/(?:confirms|confirmed?|confirme que|committed|s'engageant)/i => "confirms",
|
|
62
|
+
/(?:welcom(?:e[sd]?|ing)|accueille favorablement(?:les)?|salu(?:e|ant))/i => "welcomes",
|
|
63
|
+
/(?:recomm(?:ends?|ande(?:nt|)|ended)|endorsed|LISTE DES RADIATIONS|1 Radiations recommandées|LIST OF RECOMMENDED|1 Recommended radiations|aim(?:s|ing)|a pour objectif|should)/i => "recommends",
|
|
64
|
+
/(?:requests?|requested|demande(?:ra)?|requi[eè]r(?:en|)t|must)|l'intention d’examiner/ => "requests",
|
|
65
|
+
/(?:(?:is |are |)(?:to |)(?:re-?|)(?:amend|investigate|delete|help|present|develop|create|refer|add|formalise|update|collaborate|ensure|modify|prepare|look|report|consider|continue|make|bring|post|request|draw|raise|draft|circulate|arrange|provide|send|write|check|amend|forward|distribute|pursue|inform|coordinate|discuss|submit|ask|inquire|put)|will)/i => "requests",
|
|
66
|
+
/(?:congratulate[sd]?|félicite)/i => "congratulates",
|
|
67
|
+
/(?:instructs|instructed|inform[es]|intends to)/i => "instructs",
|
|
68
|
+
/(?:(?:strongly |)urges|prie instamment)/i => "urges",
|
|
69
|
+
/(?:appoints|(?:re)?appointed|granted|reconduit|commended|accorde)/i => "appoints",
|
|
70
|
+
/(?:donn(?:e|ées)|Pendant la période|voted|established a \w+ task group)/i => "appoints",
|
|
71
|
+
/(?:convient d'éablir|transfère|confie|établit|Étant donné que trois sièges|As there will be three vacancies)/i => "appoints",
|
|
72
|
+
/(?:La Recommandation 1 du Groupe|Recommendation 1 of the ad hoc)/i => "appoints",
|
|
73
|
+
/(?:élit|nomme|elected|nominated)/ => "elects",
|
|
74
|
+
/(?:gave the \w+ \w+ the authority|autorise|authorized)/ => "authorizes",
|
|
75
|
+
/(?:charged?)/ => "charges",
|
|
76
|
+
/(?:resolve[sd]? further)/i => "resolves further",
|
|
77
|
+
/(?:calls upon|draws the attention|attire l'attention|lance un appel|called upon)/i => "calls upon",
|
|
78
|
+
/(?:encourages?d?|espère|propose[ds]?)/i => "encourages",
|
|
79
|
+
/(?:affirms|reaffirming|réaffirmant|concurs)/i => "affirms / reaffirming",
|
|
80
|
+
/(?:states)/i => "states",
|
|
81
|
+
/(?:remarks|remarques)/i => "remarks",
|
|
82
|
+
/(?:judges)/i => "judges",
|
|
83
|
+
/(?:sanction(?:s|né?e))/i => "sanctions",
|
|
84
|
+
/(?:abrogates|abroge)/i => "abrogates",
|
|
85
|
+
/(?:empowers|habilite)/i => "empowers",
|
|
86
|
+
}.freeze
|
|
87
|
+
|
|
88
|
+
# Verb → (type, category). Symmetric across English and French —
|
|
89
|
+
# both languages are first-class keys, no canonical form.
|
|
90
|
+
VERBS = {
|
|
91
|
+
# consideration verbs
|
|
92
|
+
"having" => ["having / having regard", CATEGORY_CONSIDERATION],
|
|
93
|
+
"ayant" => ["having / having regard", CATEGORY_CONSIDERATION],
|
|
94
|
+
"vu" => ["having / having regard", CATEGORY_CONSIDERATION],
|
|
95
|
+
"considering" => ["considering", CATEGORY_CONSIDERATION],
|
|
96
|
+
"considérant" => ["considering", CATEGORY_CONSIDERATION],
|
|
97
|
+
"noting" => ["noting", CATEGORY_CONSIDERATION],
|
|
98
|
+
"notant" => ["noting", CATEGORY_CONSIDERATION],
|
|
99
|
+
"notes" => ["notes", CATEGORY_CONSIDERATION],
|
|
100
|
+
"note" => ["notes", CATEGORY_CONSIDERATION],
|
|
101
|
+
"recognizing" => ["recognizing", CATEGORY_CONSIDERATION],
|
|
102
|
+
"reconnaissant" => ["recognizing", CATEGORY_CONSIDERATION],
|
|
103
|
+
"reconnaît" => ["recognizing", CATEGORY_CONSIDERATION],
|
|
104
|
+
"reaffirming" => ["reaffirming", CATEGORY_CONSIDERATION],
|
|
105
|
+
"réaffirmant" => ["reaffirming", CATEGORY_CONSIDERATION],
|
|
106
|
+
"recalling" => ["recalling / further recalling", CATEGORY_CONSIDERATION],
|
|
107
|
+
"rappelant" => ["recalling / further recalling", CATEGORY_CONSIDERATION],
|
|
108
|
+
"rappelle" => ["recalling / further recalling", CATEGORY_CONSIDERATION],
|
|
109
|
+
"acknowledging" => ["acknowledging", CATEGORY_CONSIDERATION],
|
|
110
|
+
"taking" => ["taking into account", CATEGORY_CONSIDERATION],
|
|
111
|
+
"tenant" => ["taking into account", CATEGORY_CONSIDERATION],
|
|
112
|
+
"pursuant" => ["pursuant to", CATEGORY_CONSIDERATION],
|
|
113
|
+
"bearing" => ["bearing in mind", CATEGORY_CONSIDERATION],
|
|
114
|
+
"emphasizing" => ["emphasizing", CATEGORY_CONSIDERATION],
|
|
115
|
+
"soulignant" => ["emphasizing", CATEGORY_CONSIDERATION],
|
|
116
|
+
"concerned" => ["concerned", CATEGORY_CONSIDERATION],
|
|
117
|
+
"concerné" => ["concerned", CATEGORY_CONSIDERATION],
|
|
118
|
+
"accepts" => ["accepts", CATEGORY_CONSIDERATION],
|
|
119
|
+
"accepte" => ["accepts", CATEGORY_CONSIDERATION],
|
|
120
|
+
"observing" => ["observing", CATEGORY_CONSIDERATION],
|
|
121
|
+
"observant" => ["observing", CATEGORY_CONSIDERATION],
|
|
122
|
+
"réalise" => ["observing", CATEGORY_CONSIDERATION],
|
|
123
|
+
"referring" => ["referring", CATEGORY_CONSIDERATION],
|
|
124
|
+
"se référant" => ["referring", CATEGORY_CONSIDERATION],
|
|
125
|
+
"acting" => ["acting", CATEGORY_CONSIDERATION],
|
|
126
|
+
"agissant" => ["acting", CATEGORY_CONSIDERATION],
|
|
127
|
+
"empowers" => ["empowers", CATEGORY_CONSIDERATION],
|
|
128
|
+
"habilité" => ["empowers", CATEGORY_CONSIDERATION],
|
|
129
|
+
"habilite" => ["empowers", CATEGORY_CONSIDERATION],
|
|
130
|
+
# action verbs
|
|
131
|
+
"decides" => ["decides", CATEGORY_ACTION],
|
|
132
|
+
"décide" => ["decides", CATEGORY_ACTION],
|
|
133
|
+
"declares" => ["declares", CATEGORY_ACTION],
|
|
134
|
+
"déclare" => ["declares", CATEGORY_ACTION],
|
|
135
|
+
"invites" => ["invites / further invites", CATEGORY_ACTION],
|
|
136
|
+
"invite" => ["invites / further invites", CATEGORY_ACTION],
|
|
137
|
+
"resolves" => ["resolves", CATEGORY_ACTION],
|
|
138
|
+
"résout" => ["resolves", CATEGORY_ACTION],
|
|
139
|
+
"confirms" => ["confirms", CATEGORY_ACTION],
|
|
140
|
+
"confirme" => ["confirms", CATEGORY_ACTION],
|
|
141
|
+
"welcomes" => ["welcomes", CATEGORY_ACTION],
|
|
142
|
+
"accueille" => ["welcomes", CATEGORY_ACTION],
|
|
143
|
+
"recommends" => ["recommends", CATEGORY_ACTION],
|
|
144
|
+
"recommande" => ["recommends", CATEGORY_ACTION],
|
|
145
|
+
"requests" => ["requests", CATEGORY_ACTION],
|
|
146
|
+
"demande" => ["requests", CATEGORY_ACTION],
|
|
147
|
+
"appoints" => ["appoints", CATEGORY_ACTION],
|
|
148
|
+
"nomme" => ["appoints", CATEGORY_ACTION],
|
|
149
|
+
"encourages" => ["encourages", CATEGORY_ACTION],
|
|
150
|
+
"encourage" => ["encourages", CATEGORY_ACTION],
|
|
151
|
+
"affirms" => ["affirms / reaffirming", CATEGORY_ACTION],
|
|
152
|
+
"affirme" => ["affirms / reaffirming", CATEGORY_ACTION],
|
|
153
|
+
"calls" => ["calls upon", CATEGORY_ACTION],
|
|
154
|
+
"lance un appel" => ["calls upon", CATEGORY_ACTION],
|
|
155
|
+
"states" => ["states", CATEGORY_ACTION],
|
|
156
|
+
"indique" => ["states", CATEGORY_ACTION],
|
|
157
|
+
"remarks" => ["remarks", CATEGORY_ACTION],
|
|
158
|
+
"remarques" => ["remarks", CATEGORY_ACTION],
|
|
159
|
+
"urges" => ["urges", CATEGORY_ACTION],
|
|
160
|
+
"prie" => ["urges", CATEGORY_ACTION],
|
|
161
|
+
"instructs" => ["instructs", CATEGORY_ACTION],
|
|
162
|
+
"informe" => ["instructs", CATEGORY_ACTION],
|
|
163
|
+
"adopts" => ["adopts", CATEGORY_ACTION],
|
|
164
|
+
"adopte" => ["adopts", CATEGORY_ACTION],
|
|
165
|
+
"thanks" => ["thanks / expresses-appreciation", CATEGORY_ACTION],
|
|
166
|
+
"remercie" => ["thanks / expresses-appreciation", CATEGORY_ACTION],
|
|
167
|
+
"approves" => ["approves", CATEGORY_ACTION],
|
|
168
|
+
"approuve" => ["approves", CATEGORY_ACTION],
|
|
169
|
+
"asks" => ["asks", CATEGORY_ACTION],
|
|
170
|
+
"souhaite" => ["asks", CATEGORY_ACTION],
|
|
171
|
+
"congratulates" => ["congratulates", CATEGORY_ACTION],
|
|
172
|
+
"félicite" => ["congratulates", CATEGORY_ACTION],
|
|
173
|
+
"elects" => ["elects", CATEGORY_ACTION],
|
|
174
|
+
"élit" => ["elects", CATEGORY_ACTION],
|
|
175
|
+
"authorizes" => ["authorizes", CATEGORY_ACTION],
|
|
176
|
+
"autorise" => ["authorizes", CATEGORY_ACTION],
|
|
177
|
+
"charges" => ["charges", CATEGORY_ACTION],
|
|
178
|
+
"judges" => ["judges", CATEGORY_ACTION],
|
|
179
|
+
"juge" => ["judges", CATEGORY_ACTION],
|
|
180
|
+
"sanctions" => ["sanctions", CATEGORY_ACTION],
|
|
181
|
+
"sanctionne" => ["sanctions", CATEGORY_ACTION],
|
|
182
|
+
"abrogates" => ["abrogates", CATEGORY_ACTION],
|
|
183
|
+
"abroge" => ["abrogates", CATEGORY_ACTION],
|
|
184
|
+
}.freeze
|
|
185
|
+
|
|
186
|
+
CONSIDERATION_TYPES = CONSIDERATIONS.values.uniq.freeze
|
|
187
|
+
ACTION_TYPES = ACTIONS.values.uniq.freeze
|
|
188
|
+
|
|
189
|
+
PREFIX1 = /(?:The|Le) CIPM |La Conférence |M. Volterra |M. le Président |unanimously |would |a |sont |will |were |did not |strongly |(?:La|The) (?:\d+(?:e|th)|Quinzième) Conférence Générale des Poids et Mesures(?: a |,\s+)?/i
|
|
190
|
+
PREFIX2 = /The \d+th Conférence Générale des Poids et Mesures |The Conference |and |et (?:en |)|has |renouvelle sa |renews its |further |and further |En ce qui |après avoir |\.\.\.\n+\t*/i
|
|
191
|
+
PREFIX3 = /Sur la proposition de M. le Président, la convocation de cette Conférence de Thermométrie est |Le texte corrigé, finalement |(?:The|Le) Comité International(?: des Poids et Mesures)?(?: \(CIPM\))?(?: a |,)?\s*/i
|
|
192
|
+
PREFIX4 = /(?:The |Le |)(?:JCRB|JCGM|CCU|CCTF|CCT|CCRI|CCPR|CCQM|CCM|CCL|CCEM|CCAUV|KCDB),? (?:also |)|Each RMO |fully |The JCRB Rules of Procedure are |Bob Watters and Claudine Thomas /
|
|
193
|
+
PREFIX5 = /(?:The |Le |All |)(?:incoming |)(?:JCRB |KCDB |)(?:documents|(?:Consultative |)Committees?|Office|Chairman(?: and Secretary|)|Joint BIPM[\/-]ILAC Working Group(?: \(see Action 22\))|RMO(?:[- ]JCRB|) Representatives(?: to the JRCB|)|(?:BIPM |)Director(?: of BIPM|)|SIM|(?:Exec(?:utive|) |)Secretary(?:\(ies\)|)|RMOs, except SIM,|RMOs|APMP|\(?(?:[MD]r|Prof) [A-Z][a-zR-]+\)?|CMCs|EUR[AO]MET|COOMET|GULFMET) |It was /
|
|
194
|
+
PREFIX6 = /“|"|« à |All RMO documents related to review procedures |Mr Lam and Dr Kühne |The Prof. Kühne, Mr Jones and the Executive Secretary |Ajchara Charoensook, from APMP, /
|
|
195
|
+
|
|
196
|
+
PREFIX = /(?:#{PREFIX1}|#{PREFIX2}|#{PREFIX3}|#{PREFIX4}|#{PREFIX5}|#{PREFIX6})?/i
|
|
197
|
+
|
|
198
|
+
SUFFIX = / (?:that|que)\b|(?: (?:the |that |le |que les )?((?:[A-Z]|national|laboratoires).{0,80}?)(?: to)?\b|)/
|
|
199
|
+
|
|
200
|
+
LEADING_CONJUNCTIONS = /\A(?:et|and|further|de plus|puis|also)\s+/.freeze
|
|
201
|
+
TRAILING_PUNCTUATION = /[.:,;]\z/.freeze
|
|
202
|
+
|
|
203
|
+
# Strip leading conjunctions and trailing punctuation from a verb token.
|
|
204
|
+
# Returns the verb in whichever language it was supplied — does NOT
|
|
205
|
+
# translate between French and English. Use lookup_verb to find the
|
|
206
|
+
# canonical (type, category) for either language's verb.
|
|
207
|
+
def self.normalize_verb(raw)
|
|
208
|
+
raw.to_s.downcase.strip
|
|
209
|
+
.sub(TRAILING_PUNCTUATION, "")
|
|
210
|
+
.strip
|
|
211
|
+
.sub(LEADING_CONJUNCTIONS, "")
|
|
212
|
+
.strip
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Lookup a normalized verb's (type, category). Verb may be in either
|
|
216
|
+
# English or French — VERBS table has both as first-class keys.
|
|
217
|
+
def self.lookup_verb(verb)
|
|
218
|
+
VERBS.fetch(verb) { [fallback_type(verb), fallback_category(verb)] }
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def self.category_for_type(type)
|
|
222
|
+
return CATEGORY_CONSIDERATION if CONSIDERATION_TYPES.include?(type)
|
|
223
|
+
return CATEGORY_ACTION if ACTION_TYPES.include?(type)
|
|
224
|
+
CATEGORY_ACTION
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def self.fallback_type(verb)
|
|
228
|
+
return "considering" if verb == "having" || verb == "ayant" || verb == "vu"
|
|
229
|
+
"decides"
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def self.fallback_category(verb)
|
|
233
|
+
return CATEGORY_CONSIDERATION if verb == "having" || verb == "ayant" || verb == "vu"
|
|
234
|
+
CATEGORY_ACTION
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "optparse"
|
|
4
|
+
|
|
5
|
+
module Bipm
|
|
6
|
+
module Data
|
|
7
|
+
module Importer
|
|
8
|
+
# Argument parsing and dispatch. The exe/bipm-fetch shim delegates here
|
|
9
|
+
# so the executable stays a one-liner and the CLI is fully testable.
|
|
10
|
+
class CLI
|
|
11
|
+
Options = Struct.new(:body_id, :languages, :base_dir, :fork)
|
|
12
|
+
|
|
13
|
+
def initialize(argv)
|
|
14
|
+
@argv = argv
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def run
|
|
18
|
+
opts = parse(@argv)
|
|
19
|
+
if opts.body_id
|
|
20
|
+
run_one(opts)
|
|
21
|
+
else
|
|
22
|
+
run_all(opts)
|
|
23
|
+
end
|
|
24
|
+
0
|
|
25
|
+
rescue Bodies::UnknownBodyError => e
|
|
26
|
+
warn "error: #{e.message}"
|
|
27
|
+
2
|
|
28
|
+
rescue => e
|
|
29
|
+
warn "fatal: #{e.class}: #{e.message}"
|
|
30
|
+
warn e.backtrace.first(5).join("\n")
|
|
31
|
+
1
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def parse(argv)
|
|
35
|
+
opts = Options.new(nil, nil, "data", false)
|
|
36
|
+
parser = OptionParser.new do |p|
|
|
37
|
+
p.banner = "Usage: bipm-fetch [options]"
|
|
38
|
+
p.on("--body=ID", "Fetch a single body (e.g. cgpm, cipm)") { |v| opts.body_id = v.to_sym }
|
|
39
|
+
p.on("--language=LANG", "Restrict to one language (en or fr)") do |v|
|
|
40
|
+
opts.languages = [Language.find(v)]
|
|
41
|
+
end
|
|
42
|
+
p.on("--base-dir=DIR", "Output directory (default: data)") { |v| opts.base_dir = v }
|
|
43
|
+
p.on("--fork", "Fork one process per body") { opts.fork = true }
|
|
44
|
+
end
|
|
45
|
+
parser.parse!(argv.dup)
|
|
46
|
+
opts
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def run_one(opts)
|
|
52
|
+
fetcher = Fetcher.new(
|
|
53
|
+
body: opts.body_id,
|
|
54
|
+
languages: opts.languages || Language.all,
|
|
55
|
+
base_dir: opts.base_dir,
|
|
56
|
+
)
|
|
57
|
+
fetcher.call
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def run_all(opts)
|
|
61
|
+
languages = opts.languages || Language.all
|
|
62
|
+
fetchers = Bodies.all.map do |body|
|
|
63
|
+
Fetcher.new(body: body, languages: languages, base_dir: opts.base_dir)
|
|
64
|
+
end
|
|
65
|
+
if opts.fork
|
|
66
|
+
run_forked(fetchers)
|
|
67
|
+
else
|
|
68
|
+
fetchers.each(&:call)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def run_forked(fetchers)
|
|
73
|
+
pids = []
|
|
74
|
+
fetchers.each do |f|
|
|
75
|
+
pid = fork { f.call; exit! }
|
|
76
|
+
pids << pid if pid
|
|
77
|
+
end
|
|
78
|
+
pids.each { |pid| Process.wait(pid) }
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -1,93 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "mechanize"
|
|
4
|
+
require "nokogiri"
|
|
2
5
|
require "coradoc/input/html"
|
|
3
6
|
require "vcr"
|
|
4
7
|
require "date"
|
|
5
8
|
require "fileutils"
|
|
6
|
-
require "pry"
|
|
7
|
-
require_relative "asciimath"
|
|
8
9
|
|
|
9
10
|
VCR.configure do |c|
|
|
10
11
|
c.cassette_library_dir = __dir__ + "/../../../../cassettes"
|
|
11
12
|
c.hook_into :webmock
|
|
13
|
+
# Default: replay existing cassettes (deterministic, no network).
|
|
14
|
+
# Set BIPM_VCR_RECORD=all to overwrite cassettes with fresh responses
|
|
15
|
+
# from bipm.org; set BIPM_VCR_RECORD=new_episodes to record only URLs
|
|
16
|
+
# not already in a cassette. VCR's :once (default) raises if a request
|
|
17
|
+
# is missing from its cassette.
|
|
18
|
+
if ENV["BIPM_VCR_RECORD"]
|
|
19
|
+
c.default_cassette_options = { record: ENV["BIPM_VCR_RECORD"].to_sym }
|
|
20
|
+
end
|
|
12
21
|
end
|
|
13
22
|
|
|
14
23
|
module Bipm
|
|
15
24
|
module Data
|
|
16
25
|
module Importer
|
|
17
|
-
CONSIDERATIONS
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
/(?:The Consultative Committee for Time and Frequency \(CCTF\), at|Le Comité consultatif du temps et des fréquences \(CCTF\), à)/i => "considering",
|
|
26
|
-
/(?:taking into account|(prend|prenant) en considération|taking into consideration|tenant compte|envisager)/i => "taking into account",
|
|
27
|
-
"pursuant to" => "pursuant to",
|
|
28
|
-
/(?:bearing in mind)/i => "bearing in mind",
|
|
29
|
-
/(?:emphasizing|soulignant)/i => "emphasizing",
|
|
30
|
-
"concerned" => "concerned",
|
|
31
|
-
/(?:accept(?:s|ed|ing|e)|acceptant)/i => "accepts",
|
|
32
|
-
/(?:observing|observant que|r[ée]ali(?:zing|sant))/i => "observing",
|
|
33
|
-
/(?:referring|se référant)/i => "referring",
|
|
34
|
-
/(?:acting in accordance|agissant conformément|conformément)/i => "acting",
|
|
35
|
-
/(?:empowered by|habilité par)/i => "empowers",
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
ACTIONS = {
|
|
39
|
-
/(?:adopts|adopt[eé]d?|convient d'adopter)/ => "adopts",
|
|
40
|
-
/(?:thanks|thanked|expresse[sd](?:[ -]| its )appreciation|appréciant|pays tribute|rend hommage|remercie|support(?:ed|s))/i => "thanks / expresses-appreciation",
|
|
41
|
-
/(?:approu?v[eé][ds]?|approuv[ae]nt|approving|entérine|(?:It was )?agree[sd]?|soutient|exprime son accord|n'est pas d'accord|convient)/i => "approves",
|
|
42
|
-
/(?:d[eé]cid(?:e[ds]?|é)|ratifies?|r[eé]vised?)/i => "decides",
|
|
43
|
-
/(?:d[ée]clares?|d[ée]finition)/i => "declares",
|
|
44
|
-
/(?:The unit of length is|Supplementary units|Derived units|Principl?es|Les Délégués des États|Les v(?:œ|\u{9C})ux ou propositions)/i => "declares",
|
|
45
|
-
/(?:L'unité de longueur|Unités supplémentaires|Unités dérivées|(?:\*_)?New candle|(?:\*_)?New lumen|(?:A\) )?D[ée]finitions (?:of|des)|Cubic decimetre|Clarification of|Revision of)/i => "declares",
|
|
46
|
-
/(?:Unit of force|Définitions des|Décimètre cube|Étalons secondaires|Unité spéciale|Efficacités lumineuses|From three names|Entre les trois termes)/i => "declares",
|
|
47
|
-
/(?:Unité de force|(?:Joule|Watt|Volt|Ohm|Amp[eè]re|Coulomb|Farad|Henry|Weber) \(unité?|Bougie nouvelle|Lumen nouveau|announces that|annonce que)/i => "declares",
|
|
48
|
-
/(?:Les unités photométriques|\(A\) D[eé]finitions|The photometric units|will (?:provide|circulate|issue|identify|notify|contact|review))/i => "declares",
|
|
49
|
-
/(?:Appendix 1 of the|L'Annexe 1 de la|increased|a (?:examiné|préparé)|transmettra|fournira|increased|developed a document|prendra contact)/i => "declares",
|
|
50
|
-
/(?:Le Temps Atomique International |International Atomic Time \(TAI\) |will meet )/i => "declares",
|
|
51
|
-
/(?:ask[s ]|asked|souhaite|souhaiterait)/i => "asks",
|
|
52
|
-
/(?:(?:further |et )?invit(?:[ée][ds]?|era)|renouvelle en conséquence|convient d'inviter)/i => "invites / further invites",
|
|
53
|
-
/(?:resolve[sd]?)/i => "resolves",
|
|
54
|
-
/(?:confirms|confirmed?|confirme que|committed|s'engageant)/i => "confirms",
|
|
55
|
-
/(?:welcom(?:e[sd]?|ing)|accueille favorablement(?:les)?|salu(?:e|ant))/i => "welcomes",
|
|
56
|
-
/(?:recomm(?:ends?|ande(?:nt|)|ended)|endorsed|LISTE DES RADIATIONS|1 Radiations recommandées|LIST OF RECOMMENDED|1 Recommended radiations|aim(?:s|ing)|a pour objectif|should)/i => "recommends",
|
|
57
|
-
/(?:requests?|requested|demande(?:ra)?|requi[eè]r(?:en|)t|must)|l'intention d’examiner/ => "requests",
|
|
58
|
-
/(?:(?:is |are |)(?:to |)(?:re-?|)(?:amend|investigate|delete|help|present|develop|create|refer|add|formalise|update|collaborate|ensure|modify|prepare|look|report|consider|continue|make|bring|post|request|draw|raise|draft|circulate|arrange|provide|send|write|check|amend|forward|distribute|pursue|inform|coordinate|discuss|submit|ask|inquire|put)|will)/i => "requests",
|
|
59
|
-
/(?:congratulate[sd]?|félicite)/i => "congratulates",
|
|
60
|
-
/(?:instructs|instructed|inform[es]|intends to)/i => "instructs",
|
|
61
|
-
/(?:(?:strongly |)urges|prie instamment)/i => "urges",
|
|
62
|
-
/(?:appoints|(?:re)?appointed|granted|reconduit|commended|accorde)/i => "appoints",
|
|
63
|
-
/(?:donn(?:e|ées)|Pendant la période|voted|established a \w+ task group)/i => "appoints",
|
|
64
|
-
/(?:convient d'éablir|transfère|confie|établit|Étant donné que trois sièges|As there will be three vacancies)/i => "appoints",
|
|
65
|
-
/(?:La Recommandation 1 du Groupe|Recommendation 1 of the ad hoc)/i => "appoints",
|
|
66
|
-
/(?:élit|nomme|elected|nominated)/ => "elects",
|
|
67
|
-
/(?:gave the \w+ \w+ the authority|autorise|authorized)/ => "authorizes",
|
|
68
|
-
/(?:charged?)/ => "charges",
|
|
69
|
-
/(?:resolve[sd]? further)/i => "resolves further",
|
|
70
|
-
/(?:calls upon|draws the attention|attire l'attention|lance un appel|called upon)/i => "calls upon",
|
|
71
|
-
/(?:encourages?d?|espère|propose[ds]?)/i => "encourages",
|
|
72
|
-
/(?:affirms|reaffirming|réaffirmant|concurs)/i => "affirms / reaffirming",
|
|
73
|
-
/(?:states)/i => "states",
|
|
74
|
-
/(?:remarks|remarques)/i => "remarks",
|
|
75
|
-
/(?:judges)/i => "judges",
|
|
76
|
-
/(?:sanction(?:s|né?e))/i => "sanctions",
|
|
77
|
-
/(?:abrogates|abroge)/i => "abrogates",
|
|
78
|
-
/(?:empowers|habilite)/i => "empowers",
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
PREFIX1 = /(?:The|Le) CIPM |La Conférence |M. Volterra |M. le Président |unanimously |would |a |sont |will |were |did not |strongly |(?:La|The) (?:\d+(?:e|th)|Quinzième) Conférence Générale des Poids et Mesures(?: a |,\s+)?/i
|
|
82
|
-
PREFIX2 = /The \d+th Conférence Générale des Poids et Mesures |The Conference |and |et (?:en |)|has |renouvelle sa |renews its |further |and further |En ce qui |après avoir |\.\.\.\n+\t*/i
|
|
83
|
-
PREFIX3 = /Sur la proposition de M. le Président, la convocation de cette Conférence de Thermométrie est |Le texte corrigé, finalement |(?:The|Le) Comité International(?: des Poids et Mesures)?(?: \(CIPM\))?(?: a |,)?\s*/i
|
|
84
|
-
PREFIX4 = /(?:The |Le |)(?:JCRB|JCGM|CCU|CCTF|CCT|CCRI|CCPR|CCQM|CCM|CCL|CCEM|CCAUV|KCDB),? (?:also |)|Each RMO |fully |The JCRB Rules of Procedure are |Bob Watters and Claudine Thomas /
|
|
85
|
-
PREFIX5 = /(?:The |Le |All |)(?:incoming |)(?:JCRB |KCDB |)(?:documents|(?:Consultative |)Committees?|Office|Chairman(?: and Secretary|)|Joint BIPM[\/-]ILAC Working Group(?: \(see Action 22\))|RMO(?:[- ]JCRB|) Representatives(?: to the JRCB|)|(?:BIPM |)Director(?: of BIPM|)|SIM|(?:Exec(?:utive|) |)Secretary(?:\(ies\)|)|RMOs, except SIM,|RMOs|APMP|\(?(?:[MD]r|Prof) [A-Z][a-zR-]+\)?|CMCs|EUR[AO]MET|COOMET|GULFMET) |It was /
|
|
86
|
-
PREFIX6 = /“|"|« à |All RMO documents related to review procedures |Mr Lam and Dr Kühne |The Prof. Kühne, Mr Jones and the Executive Secretary |Ajchara Charoensook, from APMP, /
|
|
87
|
-
|
|
88
|
-
PREFIX = /(?:#{PREFIX1}|#{PREFIX2}|#{PREFIX3}|#{PREFIX4}|#{PREFIX5}|#{PREFIX6})?/i
|
|
89
|
-
|
|
90
|
-
SUFFIX = / (?:that|que)\b|(?: (?:the |that |le |que les )?((?:[A-Z]|national|laboratoires).{0,80}?)(?: to)?\b|)/
|
|
26
|
+
# @deprecated use Clauses::CONSIDERATIONS / Clauses::ACTIONS directly.
|
|
27
|
+
CONSIDERATIONS = Clauses::CONSIDERATIONS
|
|
28
|
+
# @deprecated use Clauses::ACTIONS directly.
|
|
29
|
+
ACTIONS = Clauses::ACTIONS
|
|
30
|
+
# @deprecated use Clauses::PREFIX directly.
|
|
31
|
+
PREFIX = Clauses::PREFIX
|
|
32
|
+
# @deprecated use Clauses::SUFFIX directly.
|
|
33
|
+
SUFFIX = Clauses::SUFFIX
|
|
91
34
|
|
|
92
35
|
DOIREGEX = %r'\s+<p>\s+<b>DOI :</b> (.*?)\s+</p>\n\n'
|
|
93
36
|
|
|
@@ -95,16 +38,7 @@ module Bipm
|
|
|
95
38
|
def replace_links(ps, res, lang)
|
|
96
39
|
ps.css("a[href]").each do |a|
|
|
97
40
|
href = a.attr("href")
|
|
98
|
-
|
|
99
|
-
href = href.gsub(%r'\Ahttps://www\.bipm\.org/', "")
|
|
100
|
-
|
|
101
|
-
# Correct links
|
|
102
|
-
href = href.gsub("/web/guest/", "/#{lang}/")
|
|
103
|
-
|
|
104
|
-
# Account for some mistakes from an upstream document
|
|
105
|
-
href = href.gsub(%r"\A/jen/", "/en/")
|
|
106
|
-
href = href.gsub(%r"\A/en/CGPM/jsp/", "/en/CGPM/db/")
|
|
107
|
-
|
|
41
|
+
href = Quirks.fix_href(href, language: lang)
|
|
108
42
|
href = case href
|
|
109
43
|
when %r'\A/(\w{2})/CGPM/db/(\d+)/(\d+)/(#.*)?\z',
|
|
110
44
|
%r'\A/jsp/(\w{2})/ViewCGPMResolution\.jsp\?CGPM=(\d+)&RES=(\d+)(#.*)?\z',
|
|
@@ -165,7 +99,7 @@ module Bipm
|
|
|
165
99
|
|
|
166
100
|
def format_message(part)
|
|
167
101
|
AsciiMath.asciidoc_extract_math(
|
|
168
|
-
Coradoc::Input::
|
|
102
|
+
Coradoc::Input::Html.convert(part).strip.gsub(" ", " ").gsub(" \n", "\n")
|
|
169
103
|
)
|
|
170
104
|
rescue
|
|
171
105
|
warn "Bug in Coradoc, couldn't parse the following document:"
|
|
@@ -195,7 +129,7 @@ module Bipm
|
|
|
195
129
|
|
|
196
130
|
refs = ng.css(".publication-card_reference a")
|
|
197
131
|
|
|
198
|
-
if rec_type
|
|
132
|
+
if rec_type&.end_with?("?")
|
|
199
133
|
rec_type = case supertitle
|
|
200
134
|
when /\AD[eé]claration/
|
|
201
135
|
"statement"
|
|
@@ -242,8 +176,6 @@ module Bipm
|
|
|
242
176
|
|
|
243
177
|
ps = ng.css("div.journal-content-article").first
|
|
244
178
|
|
|
245
|
-
#binding.pry if ps.count != 1
|
|
246
|
-
|
|
247
179
|
# Replace links
|
|
248
180
|
Common.replace_links(ps, res, lang)
|
|
249
181
|
|
|
@@ -251,7 +183,6 @@ module Bipm
|
|
|
251
183
|
Common.replace_centers(ps)
|
|
252
184
|
|
|
253
185
|
doc = Common.ng_to_string(ps)
|
|
254
|
-
# doc = AsciiMath.html_to_asciimath(doc)
|
|
255
186
|
|
|
256
187
|
if doc.match? DOIREGEX
|
|
257
188
|
doc = doc.sub(DOIREGEX, "")
|
|
@@ -284,8 +215,8 @@ module Bipm
|
|
|
284
215
|
next
|
|
285
216
|
end
|
|
286
217
|
|
|
287
|
-
CONSIDERATIONS.any? do |k, v|
|
|
288
|
-
if parse =~ /\A#{PREFIX}#{k}\b/i
|
|
218
|
+
Clauses::CONSIDERATIONS.any? do |k, v|
|
|
219
|
+
if parse =~ /\A#{Clauses::PREFIX}#{k}\b/i
|
|
289
220
|
r["considerations"] << prev = {
|
|
290
221
|
"type" => v,
|
|
291
222
|
"date_effective" => date.to_s,
|
|
@@ -294,8 +225,8 @@ module Bipm
|
|
|
294
225
|
end
|
|
295
226
|
end && next
|
|
296
227
|
|
|
297
|
-
ACTIONS.any? do |k, v|
|
|
298
|
-
if parse =~ /\A#{PREFIX}#{k}\b/i
|
|
228
|
+
Clauses::ACTIONS.any? do |k, v|
|
|
229
|
+
if parse =~ /\A#{Clauses::PREFIX}#{k}\b/i
|
|
299
230
|
r["actions"] << prev = {
|
|
300
231
|
"type" => v,
|
|
301
232
|
"date_effective" => date.to_s,
|
|
@@ -321,17 +252,17 @@ module Bipm
|
|
|
321
252
|
next if parse =~ /\A(|\[Cliquer ici\]|Click here|\.\.\.)\z/
|
|
322
253
|
|
|
323
254
|
r["x-unparsed"] ||= []
|
|
324
|
-
r["x-unparsed"] << parse
|
|
255
|
+
r["x-unparsed"] << parse
|
|
325
256
|
end
|
|
326
257
|
|
|
327
258
|
%w[considerations actions].each do |type|
|
|
328
|
-
map = type == "actions" ? ACTIONS : CONSIDERATIONS
|
|
259
|
+
map = type == "actions" ? Clauses::ACTIONS : Clauses::CONSIDERATIONS
|
|
329
260
|
r[type] = r[type].map do |i|
|
|
330
261
|
islist = false
|
|
331
262
|
|
|
332
263
|
kk = nil
|
|
333
264
|
|
|
334
|
-
if map.any? { |k, v| (i["message"].split("\n").first =~ /\A\s*([*_]?)(#{PREFIX}#{k})\1?(#{SUFFIX})\1?\s*\z/i) && (kk = k) }
|
|
265
|
+
if map.any? { |k, v| (i["message"].split("\n").first =~ /\A\s*([*_]?)(#{Clauses::PREFIX}#{k})\1?(#{Clauses::SUFFIX})\1?\s*\z/i) && (kk = k) }
|
|
335
266
|
prefix = $2
|
|
336
267
|
suffix = $3
|
|
337
268
|
subject = $4
|
|
@@ -340,7 +271,7 @@ module Bipm
|
|
|
340
271
|
listitems = []
|
|
341
272
|
if (i["message"].split(/(?<!\+)\n(?!\+)/).all? { |j|
|
|
342
273
|
case j
|
|
343
|
-
when /\A\s*[*_]?#{PREFIX}#{kk}/i
|
|
274
|
+
when /\A\s*[*_]?#{Clauses::PREFIX}#{kk}/i
|
|
344
275
|
true
|
|
345
276
|
when /\A\s*\z/
|
|
346
277
|
true
|
|
@@ -357,7 +288,6 @@ module Bipm
|
|
|
357
288
|
end
|
|
358
289
|
|
|
359
290
|
if subject
|
|
360
|
-
#p subject
|
|
361
291
|
r["subject"] ||= []
|
|
362
292
|
r["subject"] << subject
|
|
363
293
|
end
|
|
@@ -400,26 +330,7 @@ module Bipm
|
|
|
400
330
|
end
|
|
401
331
|
|
|
402
332
|
def extract_date(date_str)
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
date = date_str.strip
|
|
406
|
-
.gsub(/\s+/, " ")
|
|
407
|
-
.gsub("février", "february") # 3 first letters must match English
|
|
408
|
-
.gsub("juin", "june")
|
|
409
|
-
.gsub("avril", "april")
|
|
410
|
-
.gsub("mai", "may")
|
|
411
|
-
.gsub("juillet", "july")
|
|
412
|
-
.gsub(/ao[uû]t/, "august")
|
|
413
|
-
.gsub("décembre", "december")
|
|
414
|
-
.split(/, | to | au /) # Get last date
|
|
415
|
-
.last
|
|
416
|
-
date = Date.parse(date)
|
|
417
|
-
|
|
418
|
-
binding.pry if date <= Date.parse("0000-01-01")
|
|
419
|
-
|
|
420
|
-
date
|
|
421
|
-
rescue Date::Error
|
|
422
|
-
binding.pry
|
|
333
|
+
Text::French.parse_date(date_str)
|
|
423
334
|
end
|
|
424
335
|
|
|
425
336
|
extend self
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "mechanize"
|
|
4
|
+
|
|
5
|
+
module Bipm
|
|
6
|
+
module Data
|
|
7
|
+
module Importer
|
|
8
|
+
# Orchestrates a single body's scrape. Picks the strategy declared
|
|
9
|
+
# by the Body, configures VCR/WebMock for the strategy's recording
|
|
10
|
+
# mode, then delegates the actual work. The CLI never touches VCR.
|
|
11
|
+
class Fetcher
|
|
12
|
+
attr_reader :body, :languages, :base_dir
|
|
13
|
+
|
|
14
|
+
def initialize(body:, languages: Language.all, base_dir: "data")
|
|
15
|
+
@body = body.is_a?(Body) ? body : Bodies.find(body)
|
|
16
|
+
@languages = Array(languages).map { |l| l.is_a?(Language) ? l : Language.find(l) }
|
|
17
|
+
@base_dir = base_dir
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def call(agent: Mechanize.new)
|
|
21
|
+
strategy = strategy_class.new(body: body, base_dir: base_dir, languages: languages)
|
|
22
|
+
with_recording_mode(strategy.recording_mode) do
|
|
23
|
+
strategy.call(agent: agent)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def strategy_class
|
|
30
|
+
case body.strategy
|
|
31
|
+
when :static_index then Strategies::StaticIndex
|
|
32
|
+
when :spa_meetings then Strategies::SpaMeetings
|
|
33
|
+
else raise ArgumentError, "unknown strategy #{body.strategy.inspect} for body #{body.id.inspect}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def with_recording_mode(mode)
|
|
38
|
+
return yield if mode == :cassette
|
|
39
|
+
|
|
40
|
+
disable_vcr_and_webmock!
|
|
41
|
+
yield
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def disable_vcr_and_webmock!
|
|
45
|
+
begin
|
|
46
|
+
require "vcr"
|
|
47
|
+
VCR.eject_cassette
|
|
48
|
+
VCR.turn_off!(ignore_cassettes: true)
|
|
49
|
+
rescue LoadError, VCR::Errors::Error
|
|
50
|
+
# VCR not loaded or already turned off.
|
|
51
|
+
end
|
|
52
|
+
begin
|
|
53
|
+
require "webmock"
|
|
54
|
+
WebMock.disable!
|
|
55
|
+
rescue LoadError
|
|
56
|
+
# webmock not loaded.
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|