relaton 2.2.0.pre.alpha.1 → 3.0.0.pre.alpha.2
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/{docs/README.adoc → README.adoc} +165 -38
- data/bin/console +0 -1
- data/lib/relaton/3gpp/bibdata.rb +9 -0
- data/lib/relaton/3gpp/bibitem.rb +9 -0
- data/lib/relaton/3gpp/bibliography.rb +123 -0
- data/lib/relaton/3gpp/data_fetcher.rb +303 -0
- data/lib/relaton/3gpp/docidentifier.rb +114 -0
- data/lib/relaton/3gpp/doctype.rb +9 -0
- data/lib/relaton/3gpp/ext.rb +31 -0
- data/lib/relaton/3gpp/item.rb +18 -0
- data/lib/relaton/3gpp/item_data.rb +15 -0
- data/lib/relaton/3gpp/parser.rb +400 -0
- data/lib/relaton/3gpp/processor.rb +71 -0
- data/lib/relaton/3gpp/release.rb +34 -0
- data/lib/relaton/3gpp/util.rb +8 -0
- data/lib/relaton/3gpp.rb +29 -0
- data/lib/relaton/adobe/bibdata.rb +8 -0
- data/lib/relaton/adobe/bibitem.rb +8 -0
- data/lib/relaton/adobe/bibliography.rb +92 -0
- data/lib/relaton/adobe/docidentifier.rb +49 -0
- data/lib/relaton/adobe/doctype.rb +14 -0
- data/lib/relaton/adobe/ext.rb +32 -0
- data/lib/relaton/adobe/item.rb +15 -0
- data/lib/relaton/adobe/item_base.rb +18 -0
- data/lib/relaton/adobe/item_data.rb +6 -0
- data/lib/relaton/adobe/processor.rb +45 -0
- data/lib/relaton/adobe/util.rb +8 -0
- data/lib/relaton/adobe.rb +37 -0
- data/lib/relaton/bib/converter/asciibib/to_asciibib.rb +663 -0
- data/lib/relaton/bib/converter/asciibib.rb +13 -0
- data/lib/relaton/bib/converter/bibtex/from_bibtex.rb +245 -0
- data/lib/relaton/bib/converter/bibtex/to_bibtex.rb +341 -0
- data/lib/relaton/bib/converter/bibtex.rb +23 -0
- data/lib/relaton/bib/converter/bibxml/from_rfcxml.rb +386 -0
- data/lib/relaton/bib/converter/bibxml/from_rfcxml_referencegroup.rb +71 -0
- data/lib/relaton/bib/converter/bibxml/to_rfcxml.rb +308 -0
- data/lib/relaton/bib/converter/bibxml/to_rfcxml_referencegroup.rb +52 -0
- data/lib/relaton/bib/converter/bibxml.rb +51 -0
- data/lib/relaton/bib/hash_parser_v1.rb +767 -0
- data/lib/relaton/bib/item_data.rb +229 -0
- data/lib/relaton/bib/model/abstract.rb +16 -0
- data/lib/relaton/bib/model/address.rb +22 -0
- data/lib/relaton/bib/model/affiliation.rb +16 -0
- data/lib/relaton/bib/model/bibdata.rb +11 -0
- data/lib/relaton/bib/model/bibdata_shared.rb +12 -0
- data/lib/relaton/bib/model/bibitem.rb +11 -0
- data/lib/relaton/bib/model/bibitem_shared.rb +12 -0
- data/lib/relaton/bib/model/contact.rb +18 -0
- data/lib/relaton/bib/model/contribution_info.rb +15 -0
- data/lib/relaton/bib/model/contributor.rb +29 -0
- data/lib/relaton/bib/model/copyright.rb +27 -0
- data/lib/relaton/bib/model/date.rb +31 -0
- data/lib/relaton/bib/model/depiction.rb +16 -0
- data/lib/relaton/bib/model/docidentifier.rb +49 -0
- data/lib/relaton/bib/model/doctype.rb +14 -0
- data/lib/relaton/bib/model/edition.rb +14 -0
- data/lib/relaton/bib/model/ext.rb +39 -0
- data/lib/relaton/bib/model/extent.rb +16 -0
- data/lib/relaton/bib/model/formattedref.rb +43 -0
- data/lib/relaton/bib/model/full_name_type.rb +64 -0
- data/lib/relaton/bib/model/fullname.rb +11 -0
- data/lib/relaton/bib/model/ics.rb +49 -0
- data/lib/relaton/bib/model/image.rb +28 -0
- data/lib/relaton/bib/model/item.rb +96 -0
- data/lib/relaton/bib/model/item_base.rb +20 -0
- data/lib/relaton/bib/model/item_shared.rb +88 -0
- data/lib/relaton/bib/model/keyword.rb +30 -0
- data/lib/relaton/bib/model/locality.rb +18 -0
- data/lib/relaton/bib/model/locality_stack.rb +14 -0
- data/lib/relaton/bib/model/localized_string.rb +48 -0
- data/lib/relaton/bib/model/localized_string_attrs.rb +24 -0
- data/lib/relaton/bib/model/logo.rb +14 -0
- data/lib/relaton/bib/model/medium.rb +22 -0
- data/lib/relaton/bib/model/note.rb +16 -0
- data/lib/relaton/bib/model/organization.rb +13 -0
- data/lib/relaton/bib/model/organization_type.rb +42 -0
- data/lib/relaton/bib/model/person.rb +36 -0
- data/lib/relaton/bib/model/phone.rb +14 -0
- data/lib/relaton/bib/model/place.rb +33 -0
- data/lib/relaton/bib/model/price.rb +14 -0
- data/lib/relaton/bib/model/relation.rb +43 -0
- data/lib/relaton/bib/model/series.rb +34 -0
- data/lib/relaton/bib/model/size.rb +23 -0
- data/lib/relaton/bib/model/source_locality_stack.rb +14 -0
- data/lib/relaton/bib/model/status.rb +27 -0
- data/lib/relaton/bib/model/structured_identifier.rb +49 -0
- data/lib/relaton/bib/model/subdivision.rb +16 -0
- data/lib/relaton/bib/model/title.rb +56 -0
- data/lib/relaton/bib/model/type/plain_date.rb +16 -0
- data/lib/relaton/bib/model/type/string_date.rb +48 -0
- data/lib/relaton/bib/model/uri.rb +18 -0
- data/lib/relaton/bib/model/validity.rb +16 -0
- data/lib/relaton/bib/model/version.rb +43 -0
- data/lib/relaton/bib/namespace_helper.rb +21 -0
- data/lib/relaton/bib/sanitizer.rb +264 -0
- data/lib/relaton/bib/util.rb +18 -0
- data/lib/relaton/bib/versions.json +35 -0
- data/lib/relaton/bib.rb +46 -0
- data/lib/relaton/bipm/bibliography.rb +231 -0
- data/lib/relaton/bipm/converter/asciibib.rb +64 -0
- data/lib/relaton/bipm/data_fetcher.rb +81 -0
- data/lib/relaton/bipm/data_outcomes_parser.rb +656 -0
- data/lib/relaton/bipm/id_parser.rb +278 -0
- data/lib/relaton/bipm/item_data.rb +47 -0
- data/lib/relaton/bipm/model/bibdata.rb +9 -0
- data/lib/relaton/bipm/model/bibitem.rb +9 -0
- data/lib/relaton/bipm/model/comment_period.rb +13 -0
- data/lib/relaton/bipm/model/doctype.rb +12 -0
- data/lib/relaton/bipm/model/ext.rb +38 -0
- data/lib/relaton/bipm/model/item.rb +11 -0
- data/lib/relaton/bipm/model/structured_identifier.rb +36 -0
- data/lib/relaton/bipm/processor.rb +69 -0
- data/lib/relaton/bipm/rawdata_bipm_metrologia/affiliations.rb +111 -0
- data/lib/relaton/bipm/rawdata_bipm_metrologia/fetcher.rb +172 -0
- data/lib/relaton/bipm/rawdata_bipm_metrologia/niso_jats_parser.rb +353 -0
- data/lib/relaton/bipm/si_brochure_parser.rb +188 -0
- data/lib/relaton/bipm/util.rb +8 -0
- data/lib/relaton/bipm.rb +38 -0
- data/lib/relaton/bsi/bibliography.rb +196 -0
- data/lib/relaton/bsi/hit.rb +28 -0
- data/lib/relaton/bsi/hit_collection.rb +113 -0
- data/lib/relaton/bsi/item_data.rb +13 -0
- data/lib/relaton/bsi/model/bibdata.rb +8 -0
- data/lib/relaton/bsi/model/bibitem.rb +8 -0
- data/lib/relaton/bsi/model/docidentifier.rb +107 -0
- data/lib/relaton/bsi/model/doctype.rb +14 -0
- data/lib/relaton/bsi/model/ext.rb +17 -0
- data/lib/relaton/bsi/model/item.rb +17 -0
- data/lib/relaton/bsi/model/item_base.rb +22 -0
- data/lib/relaton/bsi/model/relation.rb +9 -0
- data/lib/relaton/bsi/processor.rb +43 -0
- data/lib/relaton/bsi/schema.json +24882 -0
- data/lib/relaton/bsi/scraper.rb +288 -0
- data/lib/relaton/bsi/util.rb +8 -0
- data/lib/relaton/bsi.rb +25 -0
- data/lib/relaton/calconnect/bibliography.rb +93 -0
- data/lib/relaton/calconnect/data_fetcher.rb +174 -0
- data/lib/relaton/calconnect/docidentifier.rb +80 -0
- data/lib/relaton/calconnect/hit.rb +14 -0
- data/lib/relaton/calconnect/hit_collection.rb +92 -0
- data/lib/relaton/calconnect/item_data.rb +12 -0
- data/lib/relaton/calconnect/model/bibdata.rb +8 -0
- data/lib/relaton/calconnect/model/bibitem.rb +8 -0
- data/lib/relaton/calconnect/model/doctype.rb +11 -0
- data/lib/relaton/calconnect/model/ext.rb +11 -0
- data/lib/relaton/calconnect/model/item.rb +20 -0
- data/lib/relaton/calconnect/processor.rb +72 -0
- data/lib/relaton/calconnect/scraper.rb +87 -0
- data/lib/relaton/calconnect/util.rb +8 -0
- data/lib/relaton/calconnect.rb +36 -0
- data/lib/relaton/ccsds/bibliography.rb +63 -0
- data/lib/relaton/ccsds/data/fetcher.rb +252 -0
- data/lib/relaton/ccsds/data/iso_references.rb +30 -0
- data/lib/relaton/ccsds/data/parser.rb +194 -0
- data/lib/relaton/ccsds/hit.rb +24 -0
- data/lib/relaton/ccsds/hit_collection.rb +47 -0
- data/lib/relaton/ccsds/item_data.rb +9 -0
- data/lib/relaton/ccsds/model/bibdata.rb +8 -0
- data/lib/relaton/ccsds/model/bibitem.rb +8 -0
- data/lib/relaton/ccsds/model/docidentifier.rb +121 -0
- data/lib/relaton/ccsds/model/doctype.rb +9 -0
- data/lib/relaton/ccsds/model/ext.rb +19 -0
- data/lib/relaton/ccsds/model/item.rb +15 -0
- data/lib/relaton/ccsds/processor.rb +68 -0
- data/lib/relaton/ccsds/util.rb +10 -0
- data/lib/relaton/ccsds.rb +33 -0
- data/lib/relaton/cen/bibliography.rb +149 -0
- data/lib/relaton/cen/committees.yaml +66 -0
- data/lib/relaton/cen/hit.rb +31 -0
- data/lib/relaton/cen/hit_collection.rb +116 -0
- data/lib/relaton/cen/item_data.rb +7 -0
- data/lib/relaton/cen/model/bibdata.rb +8 -0
- data/lib/relaton/cen/model/bibitem.rb +8 -0
- data/lib/relaton/cen/model/docidentifier.rb +100 -0
- data/lib/relaton/cen/model/ext.rb +11 -0
- data/lib/relaton/cen/model/item.rb +14 -0
- data/lib/relaton/cen/model/structured_identifier.rb +9 -0
- data/lib/relaton/cen/processor.rb +45 -0
- data/lib/relaton/cen/scraper.rb +218 -0
- data/lib/relaton/cen/util.rb +8 -0
- data/lib/relaton/cen.rb +30 -0
- data/lib/relaton/cie/bibdata.rb +8 -0
- data/lib/relaton/cie/bibitem.rb +8 -0
- data/lib/relaton/cie/bibliography.rb +31 -0
- data/lib/relaton/cie/data_fetcher.rb +540 -0
- data/lib/relaton/cie/ext.rb +7 -0
- data/lib/relaton/cie/item.rb +11 -0
- data/lib/relaton/cie/item_data.rb +6 -0
- data/lib/relaton/cie/processor.rb +68 -0
- data/lib/relaton/cie/scrapper.rb +52 -0
- data/lib/relaton/cie/util.rb +8 -0
- data/lib/relaton/cie.rb +29 -0
- data/lib/relaton/core/array_wrapper.rb +20 -0
- data/lib/relaton/core/data_fetcher.rb +244 -0
- data/lib/relaton/core/date_parser.rb +42 -0
- data/lib/relaton/core/governor.rb +320 -0
- data/lib/relaton/core/hash_keys_sybolizer.rb +19 -0
- data/lib/relaton/core/hit.rb +49 -0
- data/lib/relaton/core/hit_collection.rb +118 -0
- data/lib/relaton/core/pacer.rb +134 -0
- data/lib/relaton/core/processor.rb +67 -0
- data/lib/relaton/core/request_error.rb +14 -0
- data/lib/relaton/core/workers_pool.rb +45 -0
- data/lib/relaton/core.rb +12 -0
- data/lib/relaton/db/registry.rb +44 -4
- data/lib/relaton/db.rb +0 -1
- data/lib/relaton/doi/crossref.rb +89 -0
- data/lib/relaton/doi/parser.rb +921 -0
- data/lib/relaton/doi/processor.rb +65 -0
- data/lib/relaton/doi/util.rb +8 -0
- data/lib/relaton/doi.rb +20 -0
- data/lib/relaton/easc/bibdata.rb +8 -0
- data/lib/relaton/easc/bibitem.rb +8 -0
- data/lib/relaton/easc/bibliography.rb +95 -0
- data/lib/relaton/easc/docidentifier.rb +100 -0
- data/lib/relaton/easc/doctype.rb +14 -0
- data/lib/relaton/easc/ext.rb +44 -0
- data/lib/relaton/easc/item.rb +13 -0
- data/lib/relaton/easc/item_base.rb +18 -0
- data/lib/relaton/easc/item_data.rb +6 -0
- data/lib/relaton/easc/processor.rb +46 -0
- data/lib/relaton/easc/util.rb +8 -0
- data/lib/relaton/easc.rb +35 -0
- data/lib/relaton/ecma/bibdata.rb +8 -0
- data/lib/relaton/ecma/bibitem.rb +8 -0
- data/lib/relaton/ecma/bibliography.rb +149 -0
- data/lib/relaton/ecma/data_fetcher.rb +162 -0
- data/lib/relaton/ecma/data_parser.rb +49 -0
- data/lib/relaton/ecma/docidentifier.rb +124 -0
- data/lib/relaton/ecma/edition_parser.rb +80 -0
- data/lib/relaton/ecma/ext.rb +7 -0
- data/lib/relaton/ecma/item.rb +13 -0
- data/lib/relaton/ecma/item_data.rb +6 -0
- data/lib/relaton/ecma/memento_parser.rb +60 -0
- data/lib/relaton/ecma/page_fetcher.rb +39 -0
- data/lib/relaton/ecma/parser_common.rb +33 -0
- data/lib/relaton/ecma/processor.rb +69 -0
- data/lib/relaton/ecma/standard_parser.rb +134 -0
- data/lib/relaton/ecma/util.rb +8 -0
- data/lib/relaton/ecma.rb +33 -0
- data/lib/relaton/etsi/bibdata.rb +10 -0
- data/lib/relaton/etsi/bibitem.rb +10 -0
- data/lib/relaton/etsi/bibliography.rb +111 -0
- data/lib/relaton/etsi/data_fetcher.rb +167 -0
- data/lib/relaton/etsi/data_parser.rb +208 -0
- data/lib/relaton/etsi/doctype.rb +30 -0
- data/lib/relaton/etsi/ext.rb +31 -0
- data/lib/relaton/etsi/item.rb +15 -0
- data/lib/relaton/etsi/item_data.rb +6 -0
- data/lib/relaton/etsi/processor.rb +71 -0
- data/lib/relaton/etsi/pubid.rb +37 -0
- data/lib/relaton/etsi/status.rb +13 -0
- data/lib/relaton/etsi/util.rb +8 -0
- data/lib/relaton/etsi.rb +26 -0
- data/lib/relaton/gb/bibdata.rb +8 -0
- data/lib/relaton/gb/bibitem.rb +8 -0
- data/lib/relaton/gb/bibliography.rb +171 -0
- data/lib/relaton/gb/ccs.rb +14 -0
- data/lib/relaton/gb/committee.rb +13 -0
- data/lib/relaton/gb/docidentifier.rb +72 -0
- data/lib/relaton/gb/doctype.rb +9 -0
- data/lib/relaton/gb/ext.rb +36 -0
- data/lib/relaton/gb/gb_scraper.rb +61 -0
- data/lib/relaton/gb/gb_type.rb +20 -0
- data/lib/relaton/gb/hit.rb +48 -0
- data/lib/relaton/gb/hit_collection.rb +19 -0
- data/lib/relaton/gb/item.rb +13 -0
- data/lib/relaton/gb/item_data.rb +6 -0
- data/lib/relaton/gb/processor.rb +49 -0
- data/lib/relaton/gb/project_number.rb +38 -0
- data/lib/relaton/gb/scraper.rb +221 -0
- data/lib/relaton/gb/sec_scraper.rb +92 -0
- data/lib/relaton/gb/stage_name.rb +13 -0
- data/lib/relaton/gb/structured_identifier.rb +26 -0
- data/lib/relaton/gb/t_scraper.rb +126 -0
- data/lib/relaton/gb/util.rb +8 -0
- data/lib/relaton/gb/yaml/prefixes.yaml +200 -0
- data/lib/relaton/gb.rb +33 -0
- data/lib/relaton/gost/bibdata.rb +8 -0
- data/lib/relaton/gost/bibitem.rb +8 -0
- data/lib/relaton/gost/bibliography.rb +107 -0
- data/lib/relaton/gost/docidentifier.rb +80 -0
- data/lib/relaton/gost/doctype.rb +16 -0
- data/lib/relaton/gost/ext.rb +46 -0
- data/lib/relaton/gost/item.rb +15 -0
- data/lib/relaton/gost/item_base.rb +18 -0
- data/lib/relaton/gost/item_data.rb +6 -0
- data/lib/relaton/gost/processor.rb +49 -0
- data/lib/relaton/gost/util.rb +8 -0
- data/lib/relaton/gost.rb +36 -0
- data/lib/relaton/iala/bibdata.rb +8 -0
- data/lib/relaton/iala/bibitem.rb +8 -0
- data/lib/relaton/iala/bibliography.rb +146 -0
- data/lib/relaton/iala/docidentifier.rb +89 -0
- data/lib/relaton/iala/doctype.rb +18 -0
- data/lib/relaton/iala/ext.rb +32 -0
- data/lib/relaton/iala/item.rb +21 -0
- data/lib/relaton/iala/item_base.rb +18 -0
- data/lib/relaton/iala/item_data.rb +6 -0
- data/lib/relaton/iala/processor.rb +43 -0
- data/lib/relaton/iala/relation.rb +7 -0
- data/lib/relaton/iala/util.rb +8 -0
- data/lib/relaton/iala.rb +35 -0
- data/lib/relaton/iana/bibdata.rb +8 -0
- data/lib/relaton/iana/bibitem.rb +8 -0
- data/lib/relaton/iana/bibliography.rb +100 -0
- data/lib/relaton/iana/data_fetcher.rb +101 -0
- data/lib/relaton/iana/item.rb +7 -0
- data/lib/relaton/iana/item_data.rb +6 -0
- data/lib/relaton/iana/parser.rb +146 -0
- data/lib/relaton/iana/processor.rb +70 -0
- data/lib/relaton/iana/util.rb +8 -0
- data/lib/relaton/iana.rb +40 -0
- data/lib/relaton/iec/bibliography.rb +283 -0
- data/lib/relaton/iec/data_fetcher.rb +222 -0
- data/lib/relaton/iec/data_parser.rb +391 -0
- data/lib/relaton/iec/hit.rb +26 -0
- data/lib/relaton/iec/hit_collection.rb +138 -0
- data/lib/relaton/iec/item_data.rb +7 -0
- data/lib/relaton/iec/model/bibdata.rb +8 -0
- data/lib/relaton/iec/model/bibitem.rb +8 -0
- data/lib/relaton/iec/model/docidentifier.rb +135 -0
- data/lib/relaton/iec/model/doctype.rb +12 -0
- data/lib/relaton/iec/model/ext.rb +53 -0
- data/lib/relaton/iec/model/item.rb +20 -0
- data/lib/relaton/iec/model/item_base.rb +12 -0
- data/lib/relaton/iec/model/relation.rb +7 -0
- data/lib/relaton/iec/model/stage_name.rb +13 -0
- data/lib/relaton/iec/processor.rb +74 -0
- data/lib/relaton/iec/statuses.yml +199 -0
- data/lib/relaton/iec/util.rb +8 -0
- data/lib/relaton/iec.rb +98 -0
- data/lib/relaton/ieee/balloting_group.rb +13 -0
- data/lib/relaton/ieee/bibdata.rb +8 -0
- data/lib/relaton/ieee/bibitem.rb +8 -0
- data/lib/relaton/ieee/bibliography.rb +73 -0
- data/lib/relaton/ieee/converter/bibxml/from_rfcxml.rb +10 -0
- data/lib/relaton/ieee/converter/bibxml/from_rfcxml_referencegroup.rb +10 -0
- data/lib/relaton/ieee/converter/bibxml.rb +20 -0
- data/lib/relaton/ieee/data_fetcher.rb +771 -0
- data/lib/relaton/ieee/doctype.rb +9 -0
- data/lib/relaton/ieee/editorial_group.rb +19 -0
- data/lib/relaton/ieee/ext.rb +36 -0
- data/lib/relaton/ieee/idams_parser.rb +330 -0
- data/lib/relaton/ieee/item.rb +11 -0
- data/lib/relaton/ieee/item_data.rb +7 -0
- data/lib/relaton/ieee/processor.rb +71 -0
- data/lib/relaton/ieee/rawbib_id_parser.rb +753 -0
- data/lib/relaton/ieee/util.rb +8 -0
- data/lib/relaton/ieee.rb +30 -0
- data/lib/relaton/ietf/bibdata.rb +8 -0
- data/lib/relaton/ietf/bibitem.rb +8 -0
- data/lib/relaton/ietf/bibliography.rb +35 -0
- data/lib/relaton/ietf/bibxml_parser.rb +232 -0
- data/lib/relaton/ietf/data_fetcher.rb +561 -0
- data/lib/relaton/ietf/doctype.rb +9 -0
- data/lib/relaton/ietf/ext.rb +63 -0
- data/lib/relaton/ietf/item.rb +16 -0
- data/lib/relaton/ietf/item_base.rb +18 -0
- data/lib/relaton/ietf/item_data.rb +6 -0
- data/lib/relaton/ietf/processing_instructions.rb +79 -0
- data/lib/relaton/ietf/processor.rb +72 -0
- data/lib/relaton/ietf/relation.rb +9 -0
- data/lib/relaton/ietf/rfc/abstract.rb +19 -0
- data/lib/relaton/ietf/rfc/author.rb +21 -0
- data/lib/relaton/ietf/rfc/entry.rb +446 -0
- data/lib/relaton/ietf/rfc/entry_date.rb +21 -0
- data/lib/relaton/ietf/rfc/format.rb +19 -0
- data/lib/relaton/ietf/rfc/index.rb +46 -0
- data/lib/relaton/ietf/rfc/is_also.rb +21 -0
- data/lib/relaton/ietf/rfc/keywords.rb +19 -0
- data/lib/relaton/ietf/rfc/rfc_index_namespace.rb +11 -0
- data/lib/relaton/ietf/scraper.rb +101 -0
- data/lib/relaton/ietf/util.rb +8 -0
- data/lib/relaton/ietf/wg_name_resolver.rb +42 -0
- data/lib/relaton/ietf.rb +30 -0
- data/lib/relaton/iho/bibdata.rb +8 -0
- data/lib/relaton/iho/bibitem.rb +8 -0
- data/lib/relaton/iho/bibliography.rb +114 -0
- data/lib/relaton/iho/comment_period.rb +13 -0
- data/lib/relaton/iho/docidentifier.rb +31 -0
- data/lib/relaton/iho/doctype.rb +10 -0
- data/lib/relaton/iho/ext.rb +20 -0
- data/lib/relaton/iho/hash_parser_v1.rb +144 -0
- data/lib/relaton/iho/item.rb +20 -0
- data/lib/relaton/iho/item_base.rb +18 -0
- data/lib/relaton/iho/item_data.rb +6 -0
- data/lib/relaton/iho/processor.rb +54 -0
- data/lib/relaton/iho/relation.rb +9 -0
- data/lib/relaton/iho/structured_identifier.rb +20 -0
- data/lib/relaton/iho/util.rb +8 -0
- data/lib/relaton/iho.rb +27 -0
- data/lib/relaton/index/config.rb +52 -0
- data/lib/relaton/index/file_io.rb +305 -0
- data/lib/relaton/index/file_storage.rb +66 -0
- data/lib/relaton/index/pool.rb +46 -0
- data/lib/relaton/index/shard_source.rb +201 -0
- data/lib/relaton/index/type.rb +210 -0
- data/lib/relaton/index/util.rb +18 -0
- data/lib/relaton/index.rb +61 -0
- data/lib/relaton/isbn/isbn.rb +61 -0
- data/lib/relaton/isbn/open_library.rb +43 -0
- data/lib/relaton/isbn/parser.rb +88 -0
- data/lib/relaton/isbn/processor.rb +48 -0
- data/lib/relaton/isbn/util.rb +8 -0
- data/lib/relaton/isbn.rb +19 -0
- data/lib/relaton/iso/bibliography.rb +412 -0
- data/lib/relaton/iso/data_fetcher.rb +338 -0
- data/lib/relaton/iso/data_parser.rb +458 -0
- data/lib/relaton/iso/hash_parser_v1.rb +177 -0
- data/lib/relaton/iso/hit.rb +57 -0
- data/lib/relaton/iso/hit_collection.rb +196 -0
- data/lib/relaton/iso/item_data.rb +50 -0
- data/lib/relaton/iso/model/bibdata.rb +10 -0
- data/lib/relaton/iso/model/bibitem.rb +8 -0
- data/lib/relaton/iso/model/contributor.rb +6 -0
- data/lib/relaton/iso/model/contributor_info.rb +9 -0
- data/lib/relaton/iso/model/docidentifier.rb +146 -0
- data/lib/relaton/iso/model/doctype.rb +13 -0
- data/lib/relaton/iso/model/ext.rb +35 -0
- data/lib/relaton/iso/model/item.rb +17 -0
- data/lib/relaton/iso/model/item_base.rb +22 -0
- data/lib/relaton/iso/model/organization.rb +9 -0
- data/lib/relaton/iso/model/project_number.rb +22 -0
- data/lib/relaton/iso/model/relation.rb +9 -0
- data/lib/relaton/iso/model/stagename.rb +14 -0
- data/lib/relaton/iso/model/structured_identifier.rb +31 -0
- data/lib/relaton/iso/processor.rb +85 -0
- data/lib/relaton/iso/scraper.rb +635 -0
- data/lib/relaton/iso/type/pubid.rb +50 -0
- data/lib/relaton/iso/util.rb +8 -0
- data/lib/relaton/iso.rb +30 -0
- data/lib/relaton/itu/bibliography.rb +95 -0
- data/lib/relaton/itu/data_crawler_r.rb +664 -0
- data/lib/relaton/itu/data_fetcher.rb +550 -0
- data/lib/relaton/itu/data_merge_r.rb +149 -0
- data/lib/relaton/itu/data_parser_r.rb +214 -0
- data/lib/relaton/itu/data_parser_t.rb +228 -0
- data/lib/relaton/itu/family_cache.rb +177 -0
- data/lib/relaton/itu/governor.rb +56 -0
- data/lib/relaton/itu/hit.rb +28 -0
- data/lib/relaton/itu/hit_collection.rb +299 -0
- data/lib/relaton/itu/item_data.rb +6 -0
- data/lib/relaton/itu/model/approval_stage.rb +13 -0
- data/lib/relaton/itu/model/bibdata.rb +8 -0
- data/lib/relaton/itu/model/bibitem.rb +8 -0
- data/lib/relaton/itu/model/docidentifier.rb +75 -0
- data/lib/relaton/itu/model/doctype.rb +13 -0
- data/lib/relaton/itu/model/ext.rb +47 -0
- data/lib/relaton/itu/model/item.rb +13 -0
- data/lib/relaton/itu/model/meeting.rb +13 -0
- data/lib/relaton/itu/model/meeting_date.rb +15 -0
- data/lib/relaton/itu/model/question.rb +13 -0
- data/lib/relaton/itu/model/recommendation_status.rb +11 -0
- data/lib/relaton/itu/model/structured_identifier.rb +38 -0
- data/lib/relaton/itu/processor.rb +72 -0
- data/lib/relaton/itu/pubid.rb +199 -0
- data/lib/relaton/itu/radio_regulations_parser.rb +70 -0
- data/lib/relaton/itu/recommendation_fields.rb +334 -0
- data/lib/relaton/itu/recommendation_parser.rb +30 -0
- data/lib/relaton/itu/scraper.rb +203 -0
- data/lib/relaton/itu/util.rb +8 -0
- data/lib/relaton/itu.rb +34 -0
- data/lib/relaton/jcgm/bibdata.rb +8 -0
- data/lib/relaton/jcgm/bibitem.rb +8 -0
- data/lib/relaton/jcgm/bibliography.rb +97 -0
- data/lib/relaton/jcgm/data_fetcher.rb +81 -0
- data/lib/relaton/jcgm/docidentifier.rb +102 -0
- data/lib/relaton/jcgm/doctype.rb +12 -0
- data/lib/relaton/jcgm/ext.rb +23 -0
- data/lib/relaton/jcgm/item.rb +20 -0
- data/lib/relaton/jcgm/item_base.rb +18 -0
- data/lib/relaton/jcgm/item_data.rb +6 -0
- data/lib/relaton/jcgm/meetings_parser.rb +175 -0
- data/lib/relaton/jcgm/processor.rb +71 -0
- data/lib/relaton/jcgm/relation.rb +9 -0
- data/lib/relaton/jcgm/structured_identifier.rb +40 -0
- data/lib/relaton/jcgm/util.rb +8 -0
- data/lib/relaton/jcgm.rb +24 -0
- data/lib/relaton/jis/bibdata.rb +8 -0
- data/lib/relaton/jis/bibitem.rb +8 -0
- data/lib/relaton/jis/bibliography.rb +73 -0
- data/lib/relaton/jis/data_fetcher.rb +183 -0
- data/lib/relaton/jis/docidentifier.rb +120 -0
- data/lib/relaton/jis/doctype.rb +9 -0
- data/lib/relaton/jis/ext.rb +13 -0
- data/lib/relaton/jis/hit.rb +88 -0
- data/lib/relaton/jis/hit_collection.rb +128 -0
- data/lib/relaton/jis/item.rb +22 -0
- data/lib/relaton/jis/item_base.rb +26 -0
- data/lib/relaton/jis/item_data.rb +8 -0
- data/lib/relaton/jis/processor.rb +55 -0
- data/lib/relaton/jis/relation.rb +11 -0
- data/lib/relaton/jis/scraper.rb +232 -0
- data/lib/relaton/jis/structured_identifier.rb +13 -0
- data/lib/relaton/jis/util.rb +8 -0
- data/lib/relaton/jis.rb +32 -0
- data/lib/relaton/logger/channels/gh_issue.rb +143 -0
- data/lib/relaton/logger/config.rb +34 -0
- data/lib/relaton/logger/formatter_json.rb +8 -0
- data/lib/relaton/logger/formatter_string.rb +12 -0
- data/lib/relaton/logger/log.rb +64 -0
- data/lib/relaton/logger/log_device.rb +27 -0
- data/lib/relaton/logger/pool.rb +24 -0
- data/lib/relaton/logger.rb +18 -0
- data/lib/relaton/nist/bibdata.rb +8 -0
- data/lib/relaton/nist/bibitem.rb +8 -0
- data/lib/relaton/nist/bibliography.rb +191 -0
- data/lib/relaton/nist/comment_period.rb +15 -0
- data/lib/relaton/nist/data_fetcher.rb +134 -0
- data/lib/relaton/nist/date.rb +7 -0
- data/lib/relaton/nist/docidentifier.rb +165 -0
- data/lib/relaton/nist/doctype.rb +7 -0
- data/lib/relaton/nist/ext.rb +16 -0
- data/lib/relaton/nist/hit.rb +18 -0
- data/lib/relaton/nist/hit_collection.rb +362 -0
- data/lib/relaton/nist/item.rb +19 -0
- data/lib/relaton/nist/item_base.rb +16 -0
- data/lib/relaton/nist/item_data.rb +6 -0
- data/lib/relaton/nist/mods_parser.rb +302 -0
- data/lib/relaton/nist/processor.rb +69 -0
- data/lib/relaton/nist/pubs_export.rb +69 -0
- data/lib/relaton/nist/relation.rb +10 -0
- data/lib/relaton/nist/scraper.rb +293 -0
- data/lib/relaton/nist/series.yaml +49 -0
- data/lib/relaton/nist/util.rb +8 -0
- data/lib/relaton/nist.rb +34 -0
- data/lib/relaton/oasis/bibdata.rb +8 -0
- data/lib/relaton/oasis/bibitem.rb +8 -0
- data/lib/relaton/oasis/bibliography.rb +201 -0
- data/lib/relaton/oasis/browser_agent.rb +71 -0
- data/lib/relaton/oasis/data_fetcher.rb +130 -0
- data/lib/relaton/oasis/data_parser.rb +305 -0
- data/lib/relaton/oasis/data_parser_utils.rb +330 -0
- data/lib/relaton/oasis/data_part_parser.rb +239 -0
- data/lib/relaton/oasis/docidentifier.rb +54 -0
- data/lib/relaton/oasis/doctype.rb +7 -0
- data/lib/relaton/oasis/ext.rb +19 -0
- data/lib/relaton/oasis/item.rb +14 -0
- data/lib/relaton/oasis/item_data.rb +6 -0
- data/lib/relaton/oasis/processor.rb +74 -0
- data/lib/relaton/oasis/util.rb +8 -0
- data/lib/relaton/oasis.rb +41 -0
- data/lib/relaton/ogc/bibdata.rb +8 -0
- data/lib/relaton/ogc/bibitem.rb +8 -0
- data/lib/relaton/ogc/bibliography.rb +64 -0
- data/lib/relaton/ogc/data_fetcher.rb +131 -0
- data/lib/relaton/ogc/docidentifier.rb +111 -0
- data/lib/relaton/ogc/doctype.rb +12 -0
- data/lib/relaton/ogc/ext.rb +15 -0
- data/lib/relaton/ogc/hit.rb +18 -0
- data/lib/relaton/ogc/hit_collection.rb +111 -0
- data/lib/relaton/ogc/item.rb +13 -0
- data/lib/relaton/ogc/item_data.rb +6 -0
- data/lib/relaton/ogc/processor.rb +59 -0
- data/lib/relaton/ogc/scraper.rb +225 -0
- data/lib/relaton/ogc/util.rb +8 -0
- data/lib/relaton/ogc.rb +30 -0
- data/lib/relaton/oiml/bibdata.rb +8 -0
- data/lib/relaton/oiml/bibitem.rb +8 -0
- data/lib/relaton/oiml/bibliography.rb +169 -0
- data/lib/relaton/oiml/docidentifier.rb +48 -0
- data/lib/relaton/oiml/doctype.rb +13 -0
- data/lib/relaton/oiml/ext.rb +42 -0
- data/lib/relaton/oiml/item.rb +20 -0
- data/lib/relaton/oiml/item_base.rb +18 -0
- data/lib/relaton/oiml/item_data.rb +6 -0
- data/lib/relaton/oiml/processor.rb +54 -0
- data/lib/relaton/oiml/relation.rb +9 -0
- data/lib/relaton/oiml/util.rb +8 -0
- data/lib/relaton/oiml.rb +24 -0
- data/lib/relaton/omg/bibdata.rb +8 -0
- data/lib/relaton/omg/bibitem.rb +8 -0
- data/lib/relaton/omg/bibliography.rb +31 -0
- data/lib/relaton/omg/docidentifier.rb +67 -0
- data/lib/relaton/omg/ext.rb +7 -0
- data/lib/relaton/omg/item.rb +9 -0
- data/lib/relaton/omg/item_data.rb +6 -0
- data/lib/relaton/omg/processor.rb +35 -0
- data/lib/relaton/omg/scraper.rb +186 -0
- data/lib/relaton/omg/util.rb +8 -0
- data/lib/relaton/omg.rb +25 -0
- data/lib/relaton/plateau/bibdata.rb +8 -0
- data/lib/relaton/plateau/bibitem.rb +8 -0
- data/lib/relaton/plateau/bibliography.rb +31 -0
- data/lib/relaton/plateau/data_fetcher.rb +176 -0
- data/lib/relaton/plateau/doctype.rb +7 -0
- data/lib/relaton/plateau/ext.rb +23 -0
- data/lib/relaton/plateau/handbook_parser.rb +102 -0
- data/lib/relaton/plateau/hit.rb +21 -0
- data/lib/relaton/plateau/hit_collection.rb +79 -0
- data/lib/relaton/plateau/item.rb +12 -0
- data/lib/relaton/plateau/item_data.rb +6 -0
- data/lib/relaton/plateau/parser.rb +101 -0
- data/lib/relaton/plateau/processor.rb +51 -0
- data/lib/relaton/plateau/technical_report_parser.rb +98 -0
- data/lib/relaton/plateau/util.rb +8 -0
- data/lib/relaton/plateau.rb +29 -0
- data/lib/relaton/sdo/config.rb +34 -0
- data/lib/relaton/sdo/fetcher.rb +52 -0
- data/lib/relaton/sdo/logo.rb +95 -0
- data/lib/relaton/sdo/name.rb +26 -0
- data/lib/relaton/sdo/organization.rb +71 -0
- data/lib/relaton/sdo/store.rb +49 -0
- data/lib/relaton/sdo.rb +29 -0
- data/lib/relaton/un/bibdata.rb +8 -0
- data/lib/relaton/un/bibitem.rb +8 -0
- data/lib/relaton/un/bibliography.rb +48 -0
- data/lib/relaton/un/doctype.rb +10 -0
- data/lib/relaton/un/ext.rb +30 -0
- data/lib/relaton/un/hit.rb +56 -0
- data/lib/relaton/un/hit_collection.rb +61 -0
- data/lib/relaton/un/item.rb +11 -0
- data/lib/relaton/un/item_data.rb +6 -0
- data/lib/relaton/un/parser.rb +108 -0
- data/lib/relaton/un/processor.rb +38 -0
- data/lib/relaton/un/session.rb +25 -0
- data/lib/relaton/un/token_generator.rb +105 -0
- data/lib/relaton/un/util.rb +8 -0
- data/lib/relaton/un/wasm/decoder.rb +434 -0
- data/lib/relaton/un/wasm/instance.rb +101 -0
- data/lib/relaton/un/wasm/interpreter.rb +613 -0
- data/lib/relaton/un/wasm/memory.rb +112 -0
- data/lib/relaton/un/wasm/module.rb +47 -0
- data/lib/relaton/un/wasm.rb +17 -0
- data/lib/relaton/un/wasm_v_bg.wasm +0 -0
- data/lib/relaton/un.rb +28 -0
- data/lib/relaton/version.rb +1 -1
- data/lib/relaton/w3c/bibdata.rb +8 -0
- data/lib/relaton/w3c/bibitem.rb +8 -0
- data/lib/relaton/w3c/bibliography.rb +174 -0
- data/lib/relaton/w3c/data_fetcher.rb +487 -0
- data/lib/relaton/w3c/data_parser.rb +379 -0
- data/lib/relaton/w3c/docidentifier.rb +48 -0
- data/lib/relaton/w3c/doctype.rb +7 -0
- data/lib/relaton/w3c/ext.rb +11 -0
- data/lib/relaton/w3c/governor.rb +32 -0
- data/lib/relaton/w3c/item.rb +15 -0
- data/lib/relaton/w3c/item_data.rb +6 -0
- data/lib/relaton/w3c/processor.rb +49 -0
- data/lib/relaton/w3c/pubid.rb +85 -0
- data/lib/relaton/w3c/safe_realize.rb +148 -0
- data/lib/relaton/w3c/util.rb +8 -0
- data/lib/relaton/w3c.rb +36 -0
- data/lib/relaton/xsf/bibdata.rb +8 -0
- data/lib/relaton/xsf/bibitem.rb +8 -0
- data/lib/relaton/xsf/bibliography.rb +84 -0
- data/lib/relaton/xsf/data_fetcher.rb +106 -0
- data/lib/relaton/xsf/docidentifier.rb +46 -0
- data/lib/relaton/xsf/hit.rb +17 -0
- data/lib/relaton/xsf/hit_collection.rb +52 -0
- data/lib/relaton/xsf/item.rb +13 -0
- data/lib/relaton/xsf/item_data.rb +6 -0
- data/lib/relaton/xsf/processor.rb +51 -0
- data/lib/relaton/xsf/util.rb +9 -0
- data/lib/relaton/xsf.rb +34 -0
- data/lib/relaton-core.rb +1 -0
- data/lib/relaton.rb +85 -0
- metadata +775 -161
- data/.github/workflows/rake.yml +0 -14
- data/.github/workflows/release.yml +0 -24
- data/.github/workflows/rubocop.yml +0 -48
- data/.gitignore +0 -12
- data/.rspec +0 -3
- data/CLAUDE.md +0 -78
- data/Gemfile +0 -29
- data/Rakefile +0 -6
- data/docs/CHANGELOG.adoc +0 -16
- data/docs/VERSIONING_POLICY.adoc +0 -38
- data/docs/navigation.adoc +0 -6
- data/lib/relaton/db/version.rb +0 -5
- data/relaton.gemspec +0 -72
- data/spec/relaton/config_spec.rb +0 -10
- data/spec/relaton/db_cache_spec.rb +0 -51
- data/spec/relaton/db_spec.rb +0 -567
- data/spec/relaton/registry_spec.rb +0 -178
- data/spec/relaton/util_spec.rb +0 -3
- data/spec/relaton_meta_spec.rb +0 -25
- data/spec/relaton_spec.rb +0 -800
- data/spec/spec_helper.rb +0 -45
- data/spec/support/gb_t_20223_2006.xml +0 -33
- data/spec/support/iso_111111119115_1.xml +0 -0
- data/spec/support/iso_19115_1.xml +0 -115
- data/spec/support/iso_19115_2.xml +0 -95
- data/spec/support/rfc_8341.xml +0 -46
- data/spec/vcr_cassetes/api_relaton_org.yml +0 -121
- data/spec/vcr_cassetes/api_relaton_org_unavailable.yml +0 -140
- data/spec/vcr_cassetes/cc_dir_10005_2019.yml +0 -202
- data/spec/vcr_cassetes/cipm_meeting_43.yml +0 -1505
- data/spec/vcr_cassetes/gb_t_20223_2006.yml +0 -729
- data/spec/vcr_cassetes/iso_111111119115_1.yml +0 -12951
- data/spec/vcr_cassetes/iso_19115_1.yml +0 -17334
- data/spec/vcr_cassetes/iso_19115_1_2.yml +0 -322
- data/spec/vcr_cassetes/iso_19115_1_std.yml +0 -17334
- data/spec/vcr_cassetes/iso_19115_all_parts.yml +0 -185
- data/spec/vcr_cassetes/iso_19133_2005.yml +0 -144
- data/spec/vcr_cassetes/iso_combined_applied.yml +0 -318
- data/spec/vcr_cassetes/iso_combined_included.yml +0 -318
- data/spec/vcr_cassetes/ogc_19_025r1.yml +0 -374
- data/spec/vcr_cassetes/omg_ami4ccm_1_0.yml +0 -317
- data/spec/vcr_cassetes/rfc_8341.yml +0 -1278
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "English"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "ferrum"
|
|
6
|
+
require "nokogiri"
|
|
7
|
+
require "pubid"
|
|
8
|
+
require "relaton/index"
|
|
9
|
+
require "relaton/bib"
|
|
10
|
+
require "relaton/core/data_fetcher"
|
|
11
|
+
require_relative "../cie"
|
|
12
|
+
|
|
13
|
+
module Relaton
|
|
14
|
+
module Cie
|
|
15
|
+
# Thin Ferrum-backed HTTP agent that mimics the Mechanize#get interface
|
|
16
|
+
# used elsewhere in DataFetcher. Drives headless Chrome with stealth
|
|
17
|
+
# tweaks so the CIE catalogue host (Cloudflare-protected accuristech)
|
|
18
|
+
# serves real HTML instead of a "Just a moment..." challenge.
|
|
19
|
+
class BrowserAgent
|
|
20
|
+
UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 " \
|
|
21
|
+
"(KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
|
|
22
|
+
CHALLENGE_MARKERS = ["Just a moment", "challenge-platform"].freeze
|
|
23
|
+
MAX_CHALLENGE_WAIT = 30
|
|
24
|
+
|
|
25
|
+
# Raised when the Cloudflare challenge ("Just a moment…") is still showing
|
|
26
|
+
# after MAX_CHALLENGE_WAIT seconds. It's a retriable signal (see
|
|
27
|
+
# DataFetcher::RETRIABLE_ERRORS): rather than parse the challenge HTML as a
|
|
28
|
+
# document, the caller (#time_req) backs the worker off and retries.
|
|
29
|
+
class ChallengeError < StandardError; end
|
|
30
|
+
|
|
31
|
+
def initialize
|
|
32
|
+
@browser = Ferrum::Browser.new(
|
|
33
|
+
headless: true,
|
|
34
|
+
timeout: 90,
|
|
35
|
+
process_timeout: 90,
|
|
36
|
+
window_size: [1366, 768],
|
|
37
|
+
browser_options: {
|
|
38
|
+
"disable-blink-features" => "AutomationControlled",
|
|
39
|
+
"disable-quic" => nil,
|
|
40
|
+
"no-sandbox" => nil
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
@browser.headers.set(
|
|
44
|
+
"Accept-Language" => "en-US,en;q=0.9",
|
|
45
|
+
"Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9," \
|
|
46
|
+
"image/webp,*/*;q=0.8",
|
|
47
|
+
"User-Agent" => UA
|
|
48
|
+
)
|
|
49
|
+
# Pre-mask the most common headless-Chrome tells before any page JS runs.
|
|
50
|
+
@browser.evaluate_on_new_document(<<~JS)
|
|
51
|
+
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
|
|
52
|
+
Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
|
|
53
|
+
Object.defineProperty(navigator, 'plugins', { get: () => [1,2,3,4,5] });
|
|
54
|
+
window.chrome = { runtime: {} };
|
|
55
|
+
JS
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def get(url)
|
|
59
|
+
@browser.go_to(url)
|
|
60
|
+
wait_for_challenge
|
|
61
|
+
Nokogiri::HTML(@browser.body)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def quit
|
|
65
|
+
@browser&.quit
|
|
66
|
+
ensure
|
|
67
|
+
@browser = nil
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def wait_for_challenge
|
|
73
|
+
MAX_CHALLENGE_WAIT.times do
|
|
74
|
+
return unless CHALLENGE_MARKERS.any? { |m| @browser.body.include?(m) }
|
|
75
|
+
|
|
76
|
+
sleep 1
|
|
77
|
+
end
|
|
78
|
+
raise ChallengeError, "Cloudflare challenge did not clear after #{MAX_CHALLENGE_WAIT}s"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class DataFetcher < Relaton::Core::DataFetcher
|
|
83
|
+
URL = "https://www.techstreet.com/cie/searches/31156444?page=1&per_page=100"
|
|
84
|
+
|
|
85
|
+
# Default worker-pool size for the parallel detail-fetch phase. Kept small
|
|
86
|
+
# so N stealth Chrome instances don't burst requests fast enough to trip
|
|
87
|
+
# Cloudflare; tune via RELATON_CIE_CONCURRENCY (see .concurrency).
|
|
88
|
+
DEFAULT_CONCURRENCY = 5
|
|
89
|
+
|
|
90
|
+
# Per-worker pacing bounds (seconds). Each worker starts at BASE_GAP and
|
|
91
|
+
# doubles its own gap up to MAX_GAP on trouble (see Pacing / #time_req).
|
|
92
|
+
BASE_GAP = 1
|
|
93
|
+
MAX_GAP = 32
|
|
94
|
+
|
|
95
|
+
# Number of detail-fetch worker threads. Tunable via env var so
|
|
96
|
+
# relaton-data-cie's crawler workflow can dial it up for speed or down to
|
|
97
|
+
# lighten load on techstreet without a code change. Never below 1.
|
|
98
|
+
def self.concurrency
|
|
99
|
+
[(ENV["RELATON_CIE_CONCURRENCY"] || DEFAULT_CONCURRENCY).to_i, 1].max
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def initialize(output, format)
|
|
103
|
+
super
|
|
104
|
+
# Guards the shared bookkeeping (@files, @errors, @seen) and the index
|
|
105
|
+
# mutation while workers fetch detail pages in parallel. The slow
|
|
106
|
+
# agent.get runs outside this lock; only the cheap build+write is held.
|
|
107
|
+
@mutex = Mutex.new
|
|
108
|
+
# output file => catalogue position of the hit that last claimed it, so a
|
|
109
|
+
# duplicate primary id resolves to the same last-by-position winner the
|
|
110
|
+
# serial crawl would pick, regardless of worker completion order.
|
|
111
|
+
@seen = {}
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Factory for a fresh stealth browser agent. The pool builds one per worker
|
|
115
|
+
# (each its own Chrome, each with the full UA/header/`navigator` masking);
|
|
116
|
+
# #agent memoizes a single one for the serial listing phase. Specs stub
|
|
117
|
+
# this so one double can back every worker.
|
|
118
|
+
def build_agent
|
|
119
|
+
BrowserAgent.new
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def agent
|
|
123
|
+
@agent ||= build_agent
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Per-worker adaptive request pacing. Replaces the old single global 4 s
|
|
127
|
+
# gap: each worker owns one Pacing, starts at BASE_GAP, and doubles its gap
|
|
128
|
+
# (capped at MAX_GAP) whenever a request hits trouble (a Cloudflare
|
|
129
|
+
# challenge or a Ferrum/socket error), so a struggling worker slows itself
|
|
130
|
+
# without throttling the healthy ones.
|
|
131
|
+
class Pacing
|
|
132
|
+
def initialize(base: BASE_GAP, max: MAX_GAP)
|
|
133
|
+
@base = base
|
|
134
|
+
@max = max
|
|
135
|
+
@gap = base
|
|
136
|
+
@last = nil
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
attr_reader :gap
|
|
140
|
+
|
|
141
|
+
# Sleep off whatever remains of this worker's gap since its last request,
|
|
142
|
+
# then stamp the clock (in ensure, mirroring the old #time_req).
|
|
143
|
+
def throttle
|
|
144
|
+
sleep [@gap - (Time.now - @last), 0].max if @last
|
|
145
|
+
ensure
|
|
146
|
+
@last = Time.now
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Exponential backoff, capped at @max. Called before a retry on trouble.
|
|
150
|
+
def backoff
|
|
151
|
+
@gap = [@gap * 2, @max].min
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def index
|
|
156
|
+
@index ||= Index.find_or_create :cie, file: "#{INDEXFILE}.yaml",
|
|
157
|
+
pubid_class: ::Pubid::Cie::Identifier
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Parse a docidentifier string into a Pubid::Cie::Identifier, or nil if
|
|
161
|
+
# pubid can't parse it or it won't survive the structured index — so a
|
|
162
|
+
# single bad id never aborts the crawl or corrupts index-v2. The caller
|
|
163
|
+
# (#write_file) records the skip.
|
|
164
|
+
#
|
|
165
|
+
# The guard mirrors the read side's acceptance test
|
|
166
|
+
# (Index::FileIO#id_supported?, `from_hash(to_hash) == to_hash`): the
|
|
167
|
+
# loader raises InvalidIndexError and rejects the WHOLE index on the first
|
|
168
|
+
# id that doesn't round-trip, so an id that serializes but can't be
|
|
169
|
+
# deserialized back must be dropped here rather than poison every lookup.
|
|
170
|
+
def pubid(id)
|
|
171
|
+
pid = ::Pubid::Cie.parse id
|
|
172
|
+
hash = pid.to_hash
|
|
173
|
+
return nil unless ::Pubid::Cie::Identifier.from_hash(hash).to_hash == hash
|
|
174
|
+
|
|
175
|
+
pid
|
|
176
|
+
rescue StandardError
|
|
177
|
+
nil
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def log_error(msg)
|
|
181
|
+
Util.error msg
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# @param hit [Nokogiri::HTML::Document]
|
|
185
|
+
# @param doc [Mechanize::Page]
|
|
186
|
+
# @return [Array<Relaton::Bib::Docidentifier>]
|
|
187
|
+
def fetch_docid(hit, doc)
|
|
188
|
+
code, code2 = parse_code hit, doc
|
|
189
|
+
docid = []
|
|
190
|
+
if code && !code.strip.empty?
|
|
191
|
+
docid << Bib::Docidentifier.new(type: "CIE", content: code, primary: true)
|
|
192
|
+
@errors[:docid_1] &&= false
|
|
193
|
+
else
|
|
194
|
+
@errors[:docid_1] &&= true
|
|
195
|
+
end
|
|
196
|
+
if code2 && !code2.strip.empty?
|
|
197
|
+
type2 = code2.match(/\w+/).to_s
|
|
198
|
+
docid << Relaton::Bib::Docidentifier.new(type: type2, content: code2.strip)
|
|
199
|
+
@errors[:docid_2] &&= false
|
|
200
|
+
else
|
|
201
|
+
@errors[:docid_2] &&= true
|
|
202
|
+
end
|
|
203
|
+
isbn = doc.at('//h3[contains(.,"ISBN")]/following-sibling::span')&.text
|
|
204
|
+
if isbn && !isbn.strip.empty?
|
|
205
|
+
docid << Bib::Docidentifier.new(type: "ISBN", content: isbn)
|
|
206
|
+
@errors[:docid_isbn] &&= false
|
|
207
|
+
else
|
|
208
|
+
@errors[:docid_isbn] &&= true
|
|
209
|
+
end
|
|
210
|
+
docid
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def parse_code(hit, doc = nil)
|
|
214
|
+
code = hit.at("h3/a").text.strip.squeeze(" ").sub(/\u25b9/, "").gsub(" / ", "/")
|
|
215
|
+
c2idx = %r{(?:\(|/)(?<c2>(?:ISO|IEC)\s[^()]+)} =~ code
|
|
216
|
+
code = code[0...c2idx].strip if c2idx
|
|
217
|
+
[primary_code(code, doc), c2]
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def primary_code(code, doc = nil)
|
|
221
|
+
/^(?<code1>[^(]+)(?:\((?<code2>[a-zA-Z]+\d+,(?:\sPages)?[^)]+))?/ =~ code
|
|
222
|
+
if code1&.match?(/^CIE/)
|
|
223
|
+
parse_cie_code code1, code2, doc
|
|
224
|
+
elsif (pcode = doc&.at('//h3[.="Product Code(s):"]/following-sibling::span'))
|
|
225
|
+
"CIE #{pcode.text.strip.match(/[^,]+/)}"
|
|
226
|
+
else
|
|
227
|
+
num = code.match(/(?<=\()\w{2}\d+,.+(?=\))/).to_s.gsub(/,(?=\s)/, "").gsub(/,(?=\S)/, " ")
|
|
228
|
+
"CIE #{num}"
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def parse_cie_code(code1, code2, doc = nil) # rubocop:disable Metrics/CyclomaticComplexity
|
|
233
|
+
code = code1.size > 25 && code2 ? "CIE #{code2.sub(/,(\sPages)?/, '')}" : code1
|
|
234
|
+
add = doc&.at("//hgroup/h2")&.text&.match(/(Add)endum\s(\d+)$/)
|
|
235
|
+
return code unless add
|
|
236
|
+
|
|
237
|
+
"#{code} #{add[1]} #{add[2]}"
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def fetch_docnumber(hit)
|
|
241
|
+
parse_code(hit).first.sub(/^CIE\s(?:ISO\s)?/, "")
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# @param doc [Mechanize::Page]
|
|
245
|
+
# @return [Array<Relaton::Bib::Title>]
|
|
246
|
+
def fetch_title(doc)
|
|
247
|
+
t = doc.at("//hgroup/h2/text()", "//hgroup/h1/text()")
|
|
248
|
+
unless t && !t.text.strip.empty?
|
|
249
|
+
@errors[:title] &&= true
|
|
250
|
+
return []
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
result = Bib::Title.from_string t.text.strip
|
|
254
|
+
@errors[:title] &&= result.empty?
|
|
255
|
+
result
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# @param doc [Mechanize::Page]
|
|
259
|
+
# @return [Array<Relaton::Bib::Date>]
|
|
260
|
+
def fetch_date(doc)
|
|
261
|
+
result = doc.xpath("//h3[.='Published:']/following-sibling::span").map do |d|
|
|
262
|
+
pd = d.text.strip
|
|
263
|
+
on = pd.match?(/^\d{4}(?:[^-]|$)/) ? pd : Date.strptime(pd, "%m/%d/%Y").strftime("%Y-%m-%d")
|
|
264
|
+
Bib::Date.new(type: "published", at: on)
|
|
265
|
+
end
|
|
266
|
+
@errors[:date] &&= result.empty?
|
|
267
|
+
result
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# @param doc [Mechanize::Page]
|
|
271
|
+
# @return [String]
|
|
272
|
+
def fetch_edition(doc)
|
|
273
|
+
ed = doc.at("//h3[.='Edition:']/following-sibling::span")
|
|
274
|
+
@errors[:edition] &&= true
|
|
275
|
+
return unless ed
|
|
276
|
+
|
|
277
|
+
content = ed.text.slice(/^\d+(?=(st|nd|rd|th))/)
|
|
278
|
+
if content
|
|
279
|
+
@errors[:edition] = false
|
|
280
|
+
Bib::Edition.new(content: content)
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# @param doc [Mechanize::Page]
|
|
285
|
+
# @return [Array<Relaton::Cie::Relation>]
|
|
286
|
+
def fetch_relation(doc) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
287
|
+
rels = doc.xpath('//section[@class="history"]/ol/li[not(contains(@class,"selected-product"))]').map do |rel|
|
|
288
|
+
ref = rel.at("a")
|
|
289
|
+
url = "https://www.techstreet.com#{ref[:href]}"
|
|
290
|
+
title = Bib::Title.from_string ref.at('p/span[@class="title"]').text
|
|
291
|
+
did = ref.at("h3").text
|
|
292
|
+
docid = [Bib::Docidentifier.new(type: "CIE", content: did, primary: true)]
|
|
293
|
+
on = ref.at("p/time")
|
|
294
|
+
date = [Bib::Date.new(type: "published", at: on[:datetime])]
|
|
295
|
+
source = [Bib::Uri.new(type: "src", content: url)]
|
|
296
|
+
bibitem = ItemData.new docidentifier: docid, title: title, source: source, date: date
|
|
297
|
+
type = ref.at('//li/i[contains(@class,"historical")]') ? "updates" : "updatedBy"
|
|
298
|
+
Bib::Relation.new(type: type, bibitem: bibitem)
|
|
299
|
+
end
|
|
300
|
+
@errors[:relation] &&= rels.empty?
|
|
301
|
+
rels
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# @param url [String]
|
|
305
|
+
# @return [Array<Relaton::Bib::Uri>]
|
|
306
|
+
def fetch_source(url)
|
|
307
|
+
@errors[:source] &&= url.nil? || url.empty?
|
|
308
|
+
return [] if url.nil? || url.empty?
|
|
309
|
+
|
|
310
|
+
[Bib::Uri.new(type: "src", content: url)]
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
# @param doc [Mechanize::Page]
|
|
314
|
+
# @return [Array<Relaton::Bib::LocalizedMarkedUpString>]
|
|
315
|
+
def fetch_abstract(doc)
|
|
316
|
+
content = doc.at('//div[contains(@class,"description")]')&.text&.strip
|
|
317
|
+
if content.nil? || content.empty?
|
|
318
|
+
@errors[:abstract] &&= true
|
|
319
|
+
return []
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
result = [Bib::Abstract.new(content: content, language: "en", script: "Latn")]
|
|
323
|
+
@errors[:abstract] &&= result.empty?
|
|
324
|
+
result
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# @param doc [Mechanize::Page]
|
|
328
|
+
# @return [Array<Relaton::Bib::Contributor>]
|
|
329
|
+
def fetch_contributor(doc) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/CyclomaticComplexity
|
|
330
|
+
authors = doc.xpath('//hgroup/p[not(@class="pub_date")]').text.gsub "\"", ""
|
|
331
|
+
contribs = []
|
|
332
|
+
until authors.empty?
|
|
333
|
+
/^(?<sname1>\S+(?:\sder?\s)?[^\s,]+)
|
|
334
|
+
(?:,?\s(?<sname2>[\w-]{2,})(?=,\s+\w\.))?
|
|
335
|
+
(?:,?\s(?<fname>W-T\.[\w-]{2,})(?!,\s+\w\.))?
|
|
336
|
+
(?:(?:\s?,\s?|\s)(?<init>(?:\w(?:\s?\.|\s|,|$)[\s-]?)+))?
|
|
337
|
+
(?:(?:[,;]\s*|\s+|\.|(?<=\s))(?:and\s)?)?/x =~ authors
|
|
338
|
+
raise StandardError, "Author name not found in \"#{authors}\"" unless $LAST_MATCH_INFO
|
|
339
|
+
|
|
340
|
+
authors.sub! $LAST_MATCH_INFO.to_s, ""
|
|
341
|
+
sname = [sname1, sname2].compact.join " "
|
|
342
|
+
surname = Bib::LocalizedString.new content: sname, language: "en", script: "Latn"
|
|
343
|
+
forename = []
|
|
344
|
+
forename << Bib::FullNameType::Forename.new(content: fname, language: "en", script: "Latn") if fname
|
|
345
|
+
(init&.strip || "").split(/(?:,|\.)(?:-|\s)?/).each do |int|
|
|
346
|
+
forename << Bib::FullNameType::Forename.new(content: "", initial: int.strip, language: "en", script: "Latn")
|
|
347
|
+
end
|
|
348
|
+
fullname = Bib::FullName.new surname: surname, forename: forename
|
|
349
|
+
person = Bib::Person.new name: fullname
|
|
350
|
+
role = Bib::Contributor::Role.new type: "author"
|
|
351
|
+
contribs << Bib::Contributor.new(person: person, role: [role])
|
|
352
|
+
@errors[:contributor_author] &&= contribs.empty?
|
|
353
|
+
end
|
|
354
|
+
org_name = Bib::TypedLocalizedString.new(content: "Commission Internationale de L'Eclairage")
|
|
355
|
+
abbrev = Bib::LocalizedString.new content: "CIE"
|
|
356
|
+
org_uri = Bib::Uri.new content: "cie.co.at"
|
|
357
|
+
org = Bib::Organization.new(name: [org_name], abbreviation: abbrev, uri: [org_uri])
|
|
358
|
+
org_role = Bib::Contributor::Role.new type: "publisher"
|
|
359
|
+
contribs << Bib::Contributor.new(organization: org, role: [org_role])
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def fetch_ext
|
|
363
|
+
Ext.new(doctype: fetch_doctype, flavor: "cie")
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def fetch_doctype
|
|
367
|
+
Bib::Doctype.new(content: "document")
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
# @param bib [RelatonCie::BibliographicItem]
|
|
371
|
+
# @param pos [Integer, nil] the hit's catalogue position, used only by the
|
|
372
|
+
# parallel pool to keep a deterministic last-by-position winner when two
|
|
373
|
+
# hits map to the same output file. nil (direct calls) keeps the
|
|
374
|
+
# historical warn-and-overwrite behavior.
|
|
375
|
+
def write_file(bib, pos = nil) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
376
|
+
id = bib.docidentifier[0].content
|
|
377
|
+
file = output_file id
|
|
378
|
+
# Index every distinct id (the serial crawl indexes each one, even two
|
|
379
|
+
# that resolve to the same file), before the content-dedup gate below.
|
|
380
|
+
pid = pubid id
|
|
381
|
+
if pid
|
|
382
|
+
index.add_or_update pid, file
|
|
383
|
+
else
|
|
384
|
+
Util.warn { "Unparseable id `#{id}` was not indexed (#{file})" }
|
|
385
|
+
end
|
|
386
|
+
# Only the last-by-position hit writes the shared file's content.
|
|
387
|
+
return if superseded? file, pos
|
|
388
|
+
|
|
389
|
+
if @files.include? file
|
|
390
|
+
Util.warn do
|
|
391
|
+
"File #{file} exists. Docid: #{bib.docidentifier[0].content}\n" \
|
|
392
|
+
"Link: #{bib.source.detect { |l| l.type == 'src' }.content}"
|
|
393
|
+
end
|
|
394
|
+
else @files << file
|
|
395
|
+
end
|
|
396
|
+
File.write file, serialize(bib), encoding: "UTF-8"
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
# True when another hit with a *later* catalogue position has already
|
|
400
|
+
# claimed `file`. The serial crawl is last-writer-wins, so that later hit
|
|
401
|
+
# is canonical and this (earlier or duplicate) content write must be
|
|
402
|
+
# dropped — making the output independent of worker completion order. With
|
|
403
|
+
# pos nil there's no position to compare, so nothing is superseded.
|
|
404
|
+
def superseded?(file, pos)
|
|
405
|
+
return false if pos.nil?
|
|
406
|
+
|
|
407
|
+
prev = @seen[file]
|
|
408
|
+
return true if prev && prev >= pos
|
|
409
|
+
|
|
410
|
+
@seen[file] = pos
|
|
411
|
+
false
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
def to_xml(bib) = bib.to_xml(bibdata: true)
|
|
415
|
+
def to_yaml(bib) = bib.to_yaml
|
|
416
|
+
def to_bibxml(bib) = bib.to_rfcxml
|
|
417
|
+
|
|
418
|
+
# Fetch and store one document. The slow detail-page load
|
|
419
|
+
# (`worker_agent.get`, throttled by this worker's `pacing`) runs outside
|
|
420
|
+
# the lock — that's the parallel win; the cheap build + write is done under
|
|
421
|
+
# @mutex so @files/@errors/@seen and the index stay consistent across
|
|
422
|
+
# workers. Called directly (single stubbed agent) by specs and from the
|
|
423
|
+
# pool with a per-worker agent/pacing/position.
|
|
424
|
+
#
|
|
425
|
+
# @param hit [Nokogiri::HTML::Element]
|
|
426
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
427
|
+
def parse_page(hit, pos = nil, worker_agent = agent, pacing = nil)
|
|
428
|
+
url = hit.at('h3/a')[:href]
|
|
429
|
+
doc = time_req(pacing) { worker_agent.get url }
|
|
430
|
+
@mutex.synchronize do
|
|
431
|
+
item = ItemData.new(
|
|
432
|
+
type: "standard", source: fetch_source(url), docnumber: fetch_docnumber(hit),
|
|
433
|
+
docidentifier: fetch_docid(hit, doc), title: fetch_title(doc),
|
|
434
|
+
abstract: fetch_abstract(doc), date: fetch_date(doc),
|
|
435
|
+
edition: fetch_edition(doc), contributor: fetch_contributor(doc),
|
|
436
|
+
relation: fetch_relation(doc), language: "en", script: "Latn",
|
|
437
|
+
ext: fetch_ext
|
|
438
|
+
)
|
|
439
|
+
write_file item, pos
|
|
440
|
+
end
|
|
441
|
+
rescue StandardError => e
|
|
442
|
+
Util.error do
|
|
443
|
+
"Document: #{url}\n#{e.message}\n#{e.backtrace}"
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
447
|
+
|
|
448
|
+
def fetch(_source = nil)
|
|
449
|
+
process_hits collect_hits
|
|
450
|
+
index.save
|
|
451
|
+
report_errors
|
|
452
|
+
ensure
|
|
453
|
+
@agent&.quit
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
# Phase 1 (serial): walk the search-result pages, following the "next"
|
|
457
|
+
# link, and collect every //li[@data-product] hit into one list (~12 page
|
|
458
|
+
# loads at per_page=100, so serial pacing here is cheap). The detail-page
|
|
459
|
+
# fetches happen in #process_hits.
|
|
460
|
+
def collect_hits(url = URL, hits = [])
|
|
461
|
+
result = time_req(listing_pacing) { agent.get url }
|
|
462
|
+
hits.concat result.xpath("//li[@data-product]").to_a
|
|
463
|
+
np = result.at '//a[@class="next_page"]'
|
|
464
|
+
return hits unless np
|
|
465
|
+
|
|
466
|
+
next_href = np[:href]
|
|
467
|
+
next_url = next_href.start_with?("http") ? next_href : "https://www.techstreet.com#{next_href}"
|
|
468
|
+
collect_hits next_url, hits
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
# Phase 2 (parallel): fan the collected hits out across a bounded worker
|
|
472
|
+
# pool. Each worker owns its own stealth browser agent and its own adaptive
|
|
473
|
+
# pacing. The agents are built up front on this thread so a Chrome-launch
|
|
474
|
+
# failure aborts fast (before any hit is enqueued and before the index is
|
|
475
|
+
# saved) rather than leaving a dead worker that can't drain the queue; and
|
|
476
|
+
# every created agent is quit in the ensure even if enqueuing or a worker
|
|
477
|
+
# raises. Hits carry their catalogue position so #write_file keeps a
|
|
478
|
+
# deterministic winner for any duplicate output file — byte-identical
|
|
479
|
+
# output regardless of completion order.
|
|
480
|
+
def process_hits(hits)
|
|
481
|
+
return if hits.empty?
|
|
482
|
+
|
|
483
|
+
n = self.class.concurrency
|
|
484
|
+
agents = []
|
|
485
|
+
queue = SizedQueue.new(n * 2)
|
|
486
|
+
n.times { agents << build_agent }
|
|
487
|
+
workers = agents.map { |worker_agent| spawn_worker(queue, worker_agent) }
|
|
488
|
+
hits.each_with_index { |hit, pos| queue << [hit, pos] }
|
|
489
|
+
n.times { queue << nil } # poison pills
|
|
490
|
+
workers.each(&:join)
|
|
491
|
+
ensure
|
|
492
|
+
agents&.each(&:quit)
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
# One pool worker: drains the queue with its own browser agent + pacing
|
|
496
|
+
# until the poison pill (nil). parse_page swallows per-document errors, so
|
|
497
|
+
# the thread runs to the pill; the agent is quit by #process_hits.
|
|
498
|
+
def spawn_worker(queue, worker_agent)
|
|
499
|
+
pacing = Pacing.new
|
|
500
|
+
Thread.new do
|
|
501
|
+
while (item = queue.pop)
|
|
502
|
+
hit, pos = item
|
|
503
|
+
parse_page hit, pos, worker_agent, pacing
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
# Pacing for the serial listing phase (Phase 1). Also the fallback pacing
|
|
509
|
+
# for #time_req when no per-worker pacing is passed (direct calls / specs).
|
|
510
|
+
def listing_pacing
|
|
511
|
+
@listing_pacing ||= Pacing.new
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
RETRIABLE_ERRORS = [
|
|
515
|
+
SocketError,
|
|
516
|
+
Ferrum::TimeoutError,
|
|
517
|
+
Ferrum::PendingConnectionsError,
|
|
518
|
+
Ferrum::StatusError,
|
|
519
|
+
BrowserAgent::ChallengeError
|
|
520
|
+
].freeze
|
|
521
|
+
|
|
522
|
+
# Run a throttled request through the given worker's pacing, retrying up to
|
|
523
|
+
# 4 times on a retriable error (challenge / Ferrum / socket) and backing
|
|
524
|
+
# that worker's gap off before each retry.
|
|
525
|
+
def time_req(pacing = listing_pacing)
|
|
526
|
+
pacing ||= listing_pacing
|
|
527
|
+
tries = 0
|
|
528
|
+
begin
|
|
529
|
+
tries += 1
|
|
530
|
+
pacing.throttle
|
|
531
|
+
yield
|
|
532
|
+
rescue *RETRIABLE_ERRORS => e
|
|
533
|
+
pacing.backoff
|
|
534
|
+
retry if tries < 4
|
|
535
|
+
raise e
|
|
536
|
+
end
|
|
537
|
+
end
|
|
538
|
+
end
|
|
539
|
+
end
|
|
540
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require "relaton/core/processor"
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Cie
|
|
5
|
+
class Processor < Relaton::Core::Processor
|
|
6
|
+
def initialize # rubocop:disable Lint/MissingSuper
|
|
7
|
+
@short = :relaton_cie
|
|
8
|
+
@prefix = "CIE"
|
|
9
|
+
@defaultprefix = /^CIE(-|\s)/
|
|
10
|
+
@idtype = "CIE"
|
|
11
|
+
@datasets = %w[cie-techstreet]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @param code [String]
|
|
15
|
+
# @param date [String, NilClass] year
|
|
16
|
+
# @param opts [Hash]
|
|
17
|
+
# @return [Relaton::Cie::ItemData, nil]
|
|
18
|
+
def get(code, date, opts)
|
|
19
|
+
require_relative "../cie"
|
|
20
|
+
Bibliography.get(code, date, opts)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
#
|
|
24
|
+
# Fetch all the docukents from a source
|
|
25
|
+
#
|
|
26
|
+
# @param [String] _source source name
|
|
27
|
+
# @param [Hash] opts
|
|
28
|
+
# @option opts [String] :output directory to output documents
|
|
29
|
+
# @option opts [String] :format
|
|
30
|
+
#
|
|
31
|
+
def fetch_data(_source, opts)
|
|
32
|
+
require_relative "data_fetcher"
|
|
33
|
+
DataFetcher.fetch(**opts)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @param xml [String]
|
|
37
|
+
# @return [Relaton::Cie::ItemData]
|
|
38
|
+
def from_xml(xml)
|
|
39
|
+
require_relative "../cie"
|
|
40
|
+
Item.from_xml xml
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @param hash [String]
|
|
44
|
+
# @return [Relaton::Cie::ItemData]
|
|
45
|
+
def from_yaml(yaml)
|
|
46
|
+
require_relative "../cie"
|
|
47
|
+
Item.from_yaml yaml
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Returns hash of XML grammar
|
|
51
|
+
# @return [String]
|
|
52
|
+
def grammar_hash
|
|
53
|
+
require_relative "../cie"
|
|
54
|
+
@grammar_hash ||= Cie.grammar_hash
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
#
|
|
58
|
+
# Remove index file
|
|
59
|
+
#
|
|
60
|
+
def remove_index_file
|
|
61
|
+
require_relative "../cie"
|
|
62
|
+
Relaton::Index.find_or_create(
|
|
63
|
+
:cie, url: true, file: "#{INDEXFILE}.yaml"
|
|
64
|
+
).remove_file
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require "mechanize"
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Cie
|
|
5
|
+
module Scrapper
|
|
6
|
+
ENDPOINT = "https://raw.githubusercontent.com/relaton/relaton-data-cie/refs/heads/v2/".freeze
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
# @param code [String]
|
|
10
|
+
# @return [Relaton::Cie::ItemData]
|
|
11
|
+
def scrape_page(code)
|
|
12
|
+
# An unrecognized reference raises; like ISO and 3GPP we let it
|
|
13
|
+
# propagate -- relaton-cli rescues Pubid::Errors::Error and renders
|
|
14
|
+
# "... is not a recognized standards identifier". Partial refs
|
|
15
|
+
# (`CIE 001`, `CIE 15`) parse, so nothing valid is lost.
|
|
16
|
+
pubid = ::Pubid::Cie.parse code
|
|
17
|
+
index = Index.find_or_create :cie, url: "#{ENDPOINT}#{INDEXFILE}.zip", file: "#{INDEXFILE}.yaml",
|
|
18
|
+
pubid_class: ::Pubid::Cie::Identifier
|
|
19
|
+
# Pass the parsed pubid (not the raw String) so index-v2 narrows
|
|
20
|
+
# candidates by number via binary search before the block runs; the
|
|
21
|
+
# block keeps the broad substring match the string index gave.
|
|
22
|
+
# Rows are Pubid::Cie::Identifier objects (not Comparable), so pick by
|
|
23
|
+
# the string form.
|
|
24
|
+
needle = pubid.to_s
|
|
25
|
+
row = index.search(pubid) { |r| r[:id].to_s.include?(needle) }
|
|
26
|
+
.min_by { |r| r[:id].to_s }
|
|
27
|
+
return unless row
|
|
28
|
+
|
|
29
|
+
parse_page "#{ENDPOINT}#{row[:file]}", code
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
# @param url [String]
|
|
35
|
+
# @param code [String]
|
|
36
|
+
# @retrurn [Relato::Cie::ItemData]
|
|
37
|
+
def parse_page(url, code)
|
|
38
|
+
resp = Mechanize.new.get url
|
|
39
|
+
Item.from_yaml(resp.body).tap { |item| item.fetched = Date.today.to_s }
|
|
40
|
+
rescue Mechanize::ResponseCodeError => e
|
|
41
|
+
return if e.response_code == "404"
|
|
42
|
+
|
|
43
|
+
raise Relaton::RequestError, "No document found for #{code} reference. #{e.message}"
|
|
44
|
+
rescue Mechanize::RedirectLimitReachedError, Timeout::Error,
|
|
45
|
+
Mechanize::UnauthorizedError, Mechanize::UnsupportedSchemeError,
|
|
46
|
+
Mechanize::ResponseReadError, Mechanize::ChunkedTerminationError => e
|
|
47
|
+
raise Relaton::RequestError, "No document found for #{code} reference. #{e.message}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|