geo_combine 0.9.2 → 0.11.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3486b76e27b3a26ad35ed97c2ed196890ea7ea0a4f707f8cfebca3967cd1472
4
- data.tar.gz: 0c2d1e07f52144bd96faec67b83facb0a379fbc17f69fcaa36fd584da5b32773
3
+ metadata.gz: 21614ec82bcbe5c5a7bb1291e398795f1baff048552746335f18f315da677c9e
4
+ data.tar.gz: 6c94a18a0f43fd141f5967930a909efac60ab560ac91a0759e48cc45fdab39b5
5
5
  SHA512:
6
- metadata.gz: 71a0cc0aa25959868ef0eb370d8b53561d3505ee3bdf3c8dbd3d8a1553ff66c1049eed1521ea9ba4e665a615c36712a6dd3d025488ab8e46e1c91b2b0536e4f3
7
- data.tar.gz: 95422cfd7054c883a2fad7b31083cd514eb4cecb4ebd978f6df692d35e801484d71b78f4e316a358ad18f67ceb0e13bb1fcb52f6cfee5b10b5c48747294d693b
6
+ metadata.gz: 75ce7b76dfa6ad5905410e6b38a18ee98185c50d5d116c8fc5794eba3376e85a05d2e2ad7d70d6bde8de7b0073df22f15cb28759633e0d3c6e546e24a13dee82
7
+ data.tar.gz: cbd5ccc9eac444e599e0dce59fda1f23ef752cec71508a83fbe32ea1b2785b5a8c52d66c4ebcebc81a913aaccf6f677f5555622a43a8e16e902ab5f3379cbfa0
@@ -0,0 +1,11 @@
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5
+
6
+ version: 2
7
+ updates:
8
+ - package-ecosystem: "github-actions"
9
+ directory: "/"
10
+ schedule:
11
+ interval: "weekly"
@@ -6,11 +6,11 @@ jobs:
6
6
  rubocop:
7
7
  runs-on: ubuntu-latest
8
8
  steps:
9
- - uses: actions/checkout@v2
9
+ - uses: actions/checkout@v6
10
10
  - name: Set up Ruby and install dependencies
11
11
  uses: ruby/setup-ruby@v1
12
12
  with:
13
- ruby-version: 3.1
13
+ ruby-version: 3.2
14
14
  bundler-cache: true
15
15
  - name: Run linter
16
16
  run: bundle exec rubocop
@@ -19,10 +19,10 @@ jobs:
19
19
  runs-on: ubuntu-latest
20
20
  strategy:
21
21
  matrix:
22
- ruby: [3.1, 3.2, 3.3]
22
+ ruby: [3.2, 3.3, 3.4]
23
23
  faraday_version: ['', '~> 1.0'] # Defaults to whatever's the most recent version.
24
24
  steps:
25
- - uses: actions/checkout@v2
25
+ - uses: actions/checkout@v6
26
26
 
27
27
  - name: Set up Ruby and install dependencies
28
28
  uses: ruby/setup-ruby@v1
@@ -32,13 +32,13 @@ jobs:
32
32
  env:
33
33
  FARADAY_VERSION: ${{ matrix.faraday_version }}
34
34
 
35
- - name: Run tests
35
+ - name: "Run tests for Ruby ${{ matrix.ruby }} and Faraday ${{ matrix.faraday_version == '~> 1.0' && '1.x' || 'latest' }}"
36
36
  run: bundle exec rake spec
37
37
  env:
38
38
  FARADAY_VERSION: ${{ matrix.faraday_version }}
39
39
 
40
40
  - name: Upload coverage artifacts
41
- uses: actions/upload-artifact@v2
41
+ uses: actions/upload-artifact@v6
42
42
  with:
43
- name: coverage
43
+ name: "coverage-${{ matrix.ruby }}-${{ matrix.faraday_version == '~> 1.0' && 'faraday-1.x' || 'faraday-latest' }}"
44
44
  path: coverage/
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ mkmf.log
15
15
  .tool-versions
16
16
  .byebug_history
17
17
  .ruby-version
18
+ .DS_Store
data/.rubocop.yml CHANGED
@@ -1,5 +1,7 @@
1
1
  require:
2
2
  - rubocop-rspec
3
+
4
+ plugins:
3
5
  - rubocop-rake
4
6
 
5
7
  inherit_from: .rubocop_todo.yml
data/.rubocop_todo.yml CHANGED
@@ -41,7 +41,7 @@ Metrics/CyclomaticComplexity:
41
41
  # Offense count: 13
42
42
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
43
43
  Metrics/MethodLength:
44
- Max: 21
44
+ Max: 25
45
45
 
46
46
  # Offense count: 2
47
47
  # Configuration parameters: AllowedMethods, AllowedPatterns.
data/Gemfile CHANGED
@@ -5,4 +5,5 @@ source 'https://rubygems.org'
5
5
  # Specify your gem's dependencies in geo_combine.gemspec
6
6
  gemspec
7
7
 
8
- gem 'byebug'
8
+ gem 'byebug', require: false
9
+ gem 'rdbg', require: false
data/README.md CHANGED
@@ -1,16 +1,20 @@
1
1
  # GeoCombine
2
2
 
3
3
  ![CI](https://github.com/OpenGeoMetadata/GeoCombine/actions/workflows/ruby.yml/badge.svg)
4
- | [![Coverage Status](https://img.shields.io/badge/coverage-95%25-brightgreen)]()
5
- | [![Gem Version](https://img.shields.io/gem/v/geo_combine.svg)](https://github.com/OpenGeoMetadata/GeoCombine/releases)
4
+ [![Coverage Status](https://img.shields.io/badge/coverage-95%25-brightgreen)]()
5
+ [![Gem Version](https://img.shields.io/gem/v/geo_combine.svg)](https://github.com/OpenGeoMetadata/GeoCombine/releases)
6
6
 
7
7
  A Ruby toolkit for managing geospatial metadata, including:
8
8
 
9
- - tasks for cloning, updating, and indexing OpenGeoMetadata metadata
10
- - library for converting metadata between standards
9
+ - harvesting and indexing records from OpenGeoMetadata
10
+ - harvesting records directly from a GeoBlacklight instance
11
+ - converting geospatial metadata records between standards
11
12
 
12
13
  ## Installation
13
14
 
15
+ > [!TIP]
16
+ > If you're using GeoBlacklight, GeoCombine is already bundled with your installation – you can skip this section.
17
+
14
18
  Add this line to your application's `Gemfile`:
15
19
 
16
20
  ```ruby
@@ -20,111 +24,64 @@ gem 'geo_combine'
20
24
  And then execute:
21
25
 
22
26
  ```sh
23
- $ bundle install
27
+ bundle install
24
28
  ```
25
29
 
26
30
  Or install it yourself as:
27
31
 
28
32
  ```sh
29
- $ gem install geo_combine
33
+ gem install geo_combine
30
34
  ```
31
35
 
32
36
  ## Usage
33
37
 
34
- ### Converting metadata
35
- #### Converting metadata into GeoBlacklight JSON
36
- GeoCombine provides several classes representing different metadata standards that implement the `#to_geoblacklight` method for generating records in the [GeoBlacklight JSON format](https://opengeometadata.org/reference/):
37
- ```ruby
38
- GeoCombine::Iso19139 # ISO 19139 XML
39
- GeoCombine::OGP # OpenGeoPortal JSON
40
- GeoCombine::Fgdc # FGDC XML
41
- GeoCombine::EsriOpenData # Esri Open Data Portal JSON
42
- GeoCombine::CkanMetadata # CKAN JSON
43
- ```
44
- An example for converting an ISO 19139 XML record:
45
- ```ruby
46
- # Create a new ISO19139 object
47
- > iso_metadata = GeoCombine::Iso19139.new('./tmp/opengeometadata/edu.stanford.purl/bb/338/jh/0716/iso19139.xml')
48
-
49
- # Convert to GeoBlacklight's metadata format
50
- > iso_metadata.to_geoblacklight
51
-
52
- # Output it as JSON instead of a Ruby hash
53
- > iso_metadata.to_geoblacklight.to_json
54
- ```
55
- Some formats also support conversion into HTML for display in a web browser:
56
- ```ruby
57
- # Create a new ISO19139 object
58
- > iso_metadata = GeoCombine::Iso19139.new('./tmp/opengeometadata/edu.stanford.purl/bb/338/jh/0716/iso19139.xml')
59
-
60
- # Convert ISO to HTML
61
- > iso_metadata.to_html
62
- ```
63
-
64
- ### Migrating metadata
65
-
66
- You can use the `GeoCombine::Migrators` to migrate metadata from one schema to another.
67
-
68
- Currently, the only migrator is `GeoCombine::Migrators::V1AardvarkMigrator` which migrates from the [GeoBlacklight v1 schema](https://github.com/OpenGeoMetadata/opengeometadata.github.io/blob/main/docs/gbl-1.0.md) to the [Aardvark schema](https://github.com/OpenGeoMetadata/opengeometadata.github.io/blob/main/docs/ogm-aardvark.md)
69
-
70
- ```ruby
71
- # Load a record in geoblacklight v1 schema
72
- record = JSON.parse(File.read('.spec/fixtures/docs/full_geoblacklight.json'))
73
-
74
- # Migrate it to Aardvark schema
75
- GeoCombine::Migrators::V1AardvarkMigrator.new(v1_hash: record).run
76
- ```
38
+ ### Downloading metadata from OpenGeoMetadata
77
39
 
78
- Some fields cannot be migrated automatically. To handle the migration of collection names to IDs when migrating from v1 to Aardvark, you can provide a mapping of collection names to IDs to the migrator:
40
+ #### Schema version
79
41
 
80
- ```ruby
81
- # You can store this mapping as a JSON or CSV file and load it into a hash
82
- id_map = {
83
- 'My Collection 1' => 'institution:my-collection-1',
84
- 'My Collection 2' => 'institution:my-collection-2'
85
- }
42
+ By default, GeoCombine will only fetch and index records using the current schema version. If you instead want to index records using an older schema (e.g. because your GeoBlacklight instance is an older version), you can set the `SCHEMA_VERSION` environment variable:
86
43
 
87
- GeoCombine::Migrators::V1AardvarkMigrator.new(v1_hash: record, collection_id_map: id_map).run
44
+ ```sh
45
+ # Only fetch and index schema version 1.0 records
46
+ export SCHEMA_VERSION=1.0
88
47
  ```
89
48
 
90
- ### Downloading metadata from OpenGeoMetadata
49
+ #### Restricted records
91
50
 
92
- #### Logging
93
-
94
- Some of the tools and scripts in this gem use Ruby's `Logger` class to print information to `$stderr`. By default, the log level is set to `Logger::INFO`. For more verbose information, you can set the `LOG_LEVEL` environment variable to `DEBUG`:
51
+ By default, GeoCombine will only fetch and index records that are not marked as restricted. If you want to include restricted records, you can set the `OGM_SKIP_RESTRICTED` environment variable to `false`:
95
52
 
96
53
  ```sh
97
- $ LOG_LEVEL=DEBUG bundle exec rake geocombine:clone
54
+ export OGM_SKIP_RESTRICTED=false
98
55
  ```
99
56
 
100
57
  #### Clone OpenGeoMetadata repositories locally
101
58
 
102
59
  ```sh
103
- $ bundle exec rake geocombine:clone
60
+ bundle exec rake geocombine:clone
104
61
  ```
105
62
 
106
- Will clone all `edu.*`,` org.*`, and `uk.*` OpenGeoMetadata repositories into `./tmp/opengeometadata`. Location of the OpenGeoMetadata repositories can be configured using the `OGM_PATH` environment variable.
63
+ Will clone all OpenGeoMetadata repositories containing metadata matching the `SCHEMA_VERSION` into `./tmp/opengeometadata`. Location of the OpenGeoMetadata repositories can be configured using the `OGM_PATH` environment variable.
107
64
 
108
65
  ```sh
109
- $ OGM_PATH='my/custom/location' bundle exec rake geocombine:clone
66
+ OGM_PATH='my/custom/location' bundle exec rake geocombine:clone
110
67
  ```
111
68
 
112
69
  You can also specify a single repository:
113
70
 
114
71
  ```sh
115
- $ bundle exec rake geocombine:clone[edu.stanford.purl]
72
+ bundle exec rake geocombine:clone[edu.stanford.purl]
116
73
  ```
117
74
 
118
75
  _Note: If you are using zsh, you will need to use escape characters in front of the brackets:_
119
76
 
120
77
  ```sh
121
- $ bundle exec rake geocombine:clone\[edu.stanford.purl\]
78
+ bundle exec rake geocombine:clone\[edu.stanford.purl\]
122
79
  ```
123
80
 
124
81
  #### Update local OpenGeoMetadata repositories
125
82
 
126
83
  ```sh
127
- $ bundle exec rake geocombine:pull
84
+ bundle exec rake geocombine:pull
128
85
  ```
129
86
 
130
87
  Runs `git pull origin master` on all cloned repositories in `./tmp/opengeometadata` (or custom path with configured environment variable `OGM_PATH`).
@@ -132,37 +89,67 @@ Runs `git pull origin master` on all cloned repositories in `./tmp/opengeometada
132
89
  You can also specify a single repository:
133
90
 
134
91
  ```sh
135
- $ bundle exec rake geocombine:pull[edu.stanford.purl]
92
+ bundle exec rake geocombine:pull[edu.stanford.purl]
136
93
  ```
137
94
 
138
95
  _Note: If you are using zsh, you will need to use escape characters in front of the brackets:_
139
96
 
140
97
  ```sh
141
- $ bundle exec rake geocombine:pull\[edu.stanford.purl\]
98
+ bundle exec rake geocombine:pull\[edu.stanford.purl\]
142
99
  ```
143
100
 
144
101
  #### Index GeoBlacklight documents
145
102
 
103
+ > [!TIP]
104
+ > If you're using GeoBlacklight, GeoCombine will automatically detect the path to your Solr index if you run it in the directory where GeoBlacklight is installed.
105
+
146
106
  To index into Solr, GeoCombine requires a Solr instance that is running the
147
107
  [GeoBlacklight schema](https://github.com/geoblacklight/geoblacklight):
148
108
 
149
109
  ```sh
150
- $ bundle exec rake geocombine:index
110
+ bundle exec rake geocombine:index
151
111
  ```
152
112
 
153
113
  If Blacklight is installed in the ruby environment and a solr index is configured, the rake task will use the solr index configured in the Blacklight application (this is the case when invoking GeoCombine from your GeoBlacklight installation). If Blacklight is unavailable, the rake task will try to find a Solr instance running at `http://localhost:8983/solr/blacklight-core`.
154
114
 
155
- You can also set a the Solr instance URL using `SOLR_URL`:
115
+ You can also set the Solr instance URL using `SOLR_URL`:
116
+
117
+ ```sh
118
+ SOLR_URL=http://www.example.com:1234/solr/collection bundle exec rake geocombine:index
119
+ ```
120
+
121
+ #### Logging
122
+
123
+ Some of the tools and scripts in this gem use Ruby's `Logger` class to print information to `$stderr`. By default, the log level is set to `Logger::INFO`. For more verbose information, you can set the `LOG_LEVEL` environment variable to `DEBUG`:
124
+
125
+ ```sh
126
+ export LOG_LEVEL=DEBUG
127
+ ```
128
+
129
+ ### Indexing local documents
130
+
131
+ To index a collection of records in a local directory, you can pass the directory as a `rake` argument:
156
132
 
157
133
  ```sh
158
- $ SOLR_URL=http://www.example.com:1234/solr/collection bundle exec rake geocombine:index
134
+ bundle exec rake geocombine:index\[/path/to/your/files\]
159
135
  ```
160
136
 
161
- By default, GeoCombine will index only records using the Aardvark metadata format. If you instead want to index records using an older format (e.g. because your GeoBlacklight instance is version 3 or older), you can set the `SCHEMA_VERSION` environment variable:
137
+ Or you can provide it as `OGM_PATH`:
162
138
 
163
139
  ```sh
164
- # Only index schema version 1.0 records
165
- $ SCHEMA_VERSION=1.0 bundle exec rake geocombine:index
140
+ OGM_PATH=/path/to/your/files rake geocombine:index
141
+ ```
142
+
143
+ For a single file, it works the same way:
144
+
145
+ ```sh
146
+ bundle exec rake geocombine:index\[/path/to/your/file.json\]
147
+ ```
148
+
149
+ Or:
150
+
151
+ ```sh
152
+ OGM_PATH=/path/to/your/file.json rake geocombine:index
166
153
  ```
167
154
 
168
155
  ### Harvesting and indexing documents from GeoBlacklight sites
@@ -226,12 +213,75 @@ GeoCombine::GeoBlacklightIndexer.document_transformer = -> (document) do
226
213
  end
227
214
  ```
228
215
 
216
+ ### Converting metadata
217
+
218
+ #### Converting metadata into GeoBlacklight JSON
219
+
220
+ GeoCombine provides several classes representing different metadata standards that implement the `#to_geoblacklight` method for generating records in the [GeoBlacklight JSON format](https://opengeometadata.org/reference/):
221
+
222
+ ```ruby
223
+ GeoCombine::Iso19139 # ISO 19139 XML
224
+ GeoCombine::OGP # OpenGeoPortal JSON
225
+ GeoCombine::Fgdc # FGDC XML
226
+ GeoCombine::EsriOpenData # Esri Open Data Portal JSON
227
+ GeoCombine::CkanMetadata # CKAN JSON
228
+ ```
229
+
230
+ An example for converting an ISO 19139 XML record:
231
+
232
+ ```ruby
233
+ # Create a new ISO19139 object
234
+ > iso_metadata = GeoCombine::Iso19139.new('./tmp/opengeometadata/edu.stanford.purl/bb/338/jh/0716/iso19139.xml')
235
+
236
+ # Convert to GeoBlacklight's metadata format
237
+ > iso_metadata.to_geoblacklight
238
+
239
+ # Output it as JSON instead of a Ruby hash
240
+ > iso_metadata.to_geoblacklight.to_json
241
+ ```
242
+
243
+ Some formats also support conversion into HTML for display in a web browser:
244
+
245
+ ```ruby
246
+ # Create a new ISO19139 object
247
+ > iso_metadata = GeoCombine::Iso19139.new('./tmp/opengeometadata/edu.stanford.purl/bb/338/jh/0716/iso19139.xml')
248
+
249
+ # Convert ISO to HTML
250
+ > iso_metadata.to_html
251
+ ```
252
+
253
+ ### Migrating metadata
254
+
255
+ You can use the `GeoCombine::Migrators` to migrate metadata from one schema to another.
256
+
257
+ Currently, the only migrator is `GeoCombine::Migrators::V1AardvarkMigrator` which migrates from the [GeoBlacklight v1 schema](https://github.com/OpenGeoMetadata/opengeometadata.github.io/blob/main/docs/gbl-1.0.md) to the [Aardvark schema](https://github.com/OpenGeoMetadata/opengeometadata.github.io/blob/main/docs/ogm-aardvark.md)
258
+
259
+ ```ruby
260
+ # Load a record in geoblacklight v1 schema
261
+ record = JSON.parse(File.read('.spec/fixtures/docs/full_geoblacklight.json'))
262
+
263
+ # Migrate it to Aardvark schema
264
+ GeoCombine::Migrators::V1AardvarkMigrator.new(v1_hash: record).run
265
+ ```
266
+
267
+ Some fields cannot be migrated automatically. To handle the migration of collection names to IDs when migrating from v1 to Aardvark, you can provide a mapping of collection names to IDs to the migrator:
268
+
269
+ ```ruby
270
+ # You can store this mapping as a JSON or CSV file and load it into a hash
271
+ id_map = {
272
+ 'My Collection 1' => 'institution:my-collection-1',
273
+ 'My Collection 2' => 'institution:my-collection-2'
274
+ }
275
+
276
+ GeoCombine::Migrators::V1AardvarkMigrator.new(v1_hash: record, collection_id_map: id_map).run
277
+ ```
278
+
229
279
  ## Tests
230
280
 
231
281
  To run the tests, use:
232
282
 
233
283
  ```sh
234
- $ bundle exec rake spec
284
+ bundle exec rake spec
235
285
  ```
236
286
 
237
287
  ## Contributing
data/geo_combine.gemspec CHANGED
@@ -32,8 +32,8 @@ Gem::Specification.new do |spec|
32
32
  spec.add_development_dependency 'rake'
33
33
  spec.add_development_dependency 'rspec'
34
34
  spec.add_development_dependency 'rspec-html-matchers'
35
- spec.add_development_dependency 'rubocop', '~> 1.25'
36
- spec.add_development_dependency 'rubocop-rspec', '~> 2.8'
35
+ spec.add_development_dependency 'rubocop'
36
+ spec.add_development_dependency 'rubocop-rspec'
37
37
  spec.add_development_dependency 'rubocop-rake'
38
38
  spec.add_development_dependency 'simplecov'
39
39
  spec.add_development_dependency 'webmock', '~> 3.14'
data/lib/.DS_Store ADDED
Binary file
@@ -48,8 +48,6 @@ module GeoCombine
48
48
  end
49
49
 
50
50
  def self.from_envelope(envelope)
51
- return if envelope.nil?
52
-
53
51
  envelope = envelope[/.*ENVELOPE\(([^)]*)/, 1].split(',')
54
52
  new(
55
53
  west: envelope[0],
@@ -63,8 +61,6 @@ module GeoCombine
63
61
  # @param [String] spatial w,s,e,n or w s e n
64
62
  # @param [String] delimiter "," or " "
65
63
  def self.from_string_delimiter(spatial, delimiter: ',')
66
- return if spatial.nil?
67
-
68
64
  spatial = spatial.split(delimiter)
69
65
  new(
70
66
  west: spatial[0],
@@ -32,7 +32,7 @@ module GeoCombine
32
32
  layer_slug_s: @metadata['name'],
33
33
  solr_geom: envelope,
34
34
  dc_subject_sm: subjects,
35
- dct_references_s: external_references.to_json.to_s,
35
+ dct_references_s: external_references.to_json,
36
36
  dc_format_s: downloadable? ? 'ZIP' : nil # TODO: we only allow direct ZIP file downloads
37
37
  }.compact
38
38
  end
@@ -4,6 +4,7 @@ module GeoCombine
4
4
  # Data model for ESRI's open data portal metadata
5
5
  class EsriOpenData
6
6
  include GeoCombine::Formatting
7
+
7
8
  attr_reader :metadata
8
9
 
9
10
  ##
@@ -42,6 +42,7 @@ module GeoCombine
42
42
  metadata.each do |key, value|
43
43
  translate_formats(key, value)
44
44
  enhance_subjects(key, value)
45
+ infer_geometry_type_from_subject(key, value)
45
46
  format_proper_date(key, value)
46
47
  fields_should_be_array(key, value)
47
48
  translate_geometry_type(key, value)
@@ -55,20 +56,29 @@ module GeoCombine
55
56
  metadata.to_json(options)
56
57
  end
57
58
 
59
+ # True if the GeoBlacklight-Schema document is valid
60
+ # @return [Boolean]
61
+ def valid?
62
+ validate!
63
+ true
64
+ rescue StandardError
65
+ false
66
+ end
67
+
58
68
  ##
59
69
  # Validates a GeoBlacklight-Schema json document
60
70
  # @return [Boolean]
61
- def valid?
62
- JSON::Validator.validate!(schema, to_json, fragment: '#/definitions/layer') &&
63
- dct_references_validate! &&
64
- spatial_validate!
71
+ def validate!
72
+ JSON::Validator.validate!(schema, to_json, fragment: '#/definitions/layer')
73
+ validate_references!
74
+ validate_spatial!
65
75
  end
66
76
 
67
77
  ##
68
78
  # Validate dct_references_s
69
79
  # @return [Boolean]
70
- def dct_references_validate!
71
- return true unless metadata.key?('dct_references_s') # TODO: shouldn't we require this field?
80
+ def validate_references!
81
+ return unless metadata.key?('dct_references_s') # Not required
72
82
 
73
83
  begin
74
84
  ref = JSON.parse(metadata['dct_references_s'])
@@ -83,8 +93,8 @@ module GeoCombine
83
93
  end
84
94
  end
85
95
 
86
- def spatial_validate!
87
- GeoCombine::BoundingBox.from_envelope(metadata['solr_geom']).valid?
96
+ def validate_spatial!
97
+ raise GeoCombine::Exceptions::InvalidGeometry unless GeoCombine::BoundingBox.from_envelope(metadata['solr_geom']).valid?
88
98
  end
89
99
 
90
100
  private
@@ -106,6 +116,17 @@ module GeoCombine
106
116
  metadata[key] = geometry_types[value]
107
117
  end
108
118
 
119
+ ##
120
+ # Enhances empty 'layer_geom_type_s' field by populating from related subject
121
+ def infer_geometry_type_from_subject(key, value)
122
+ return unless key == 'layer_geom_type_s' && value.blank?
123
+
124
+ # If any subjects match a known geometry type, use the first that does as the geometry type
125
+ metadata['dc_subject_sm'].find { |subject| geometry_types.value?(subject.capitalize) }&.tap do |subject|
126
+ metadata[key] = subject.capitalize
127
+ end
128
+ end
129
+
109
130
  ##
110
131
  # Enhances the 'dc_subject_sm' field by translating subjects to ISO topic
111
132
  # categories
@@ -163,7 +184,7 @@ module GeoCombine
163
184
  metadata.except!(*DEPRECATED_KEYS_V1)
164
185
 
165
186
  # ensure we have a proper v1 record
166
- valid?
187
+ validate!
167
188
  end
168
189
 
169
190
  def schema
@@ -11,35 +11,33 @@ module GeoCombine
11
11
  class Harvester
12
12
  attr_reader :ogm_path, :schema_version
13
13
 
14
- # Non-metadata repositories that shouldn't be harvested
15
- def self.denylist
16
- [
17
- 'GeoCombine',
18
- 'aardvark',
19
- 'metadata-issues',
20
- 'ogm_utils-python',
21
- 'opengeometadata.github.io',
22
- 'opengeometadata-rails',
23
- 'gbl-1_to_aardvark'
24
- ]
25
- end
26
-
27
14
  # GitHub API endpoint for OpenGeoMetadata repositories
28
15
  def self.ogm_api_uri
29
16
  URI('https://api.github.com/orgs/opengeometadata/repos?per_page=1000')
30
17
  end
31
18
 
19
+ # Initialize a new harvester
20
+ # @param ogm_path [String] path to the directory where repositories will be cloned
21
+ # @param skip_repos [Array<String>] list of repository names to skip
22
+ # @param skip_restricted [Boolean] whether to skip indexing restricted documents
23
+ # @param schema_version [String] schema version to filter repositories by
24
+ # @param logger [Logger] logger to use for logging messages
32
25
  def initialize(
33
- ogm_path: ENV.fetch('OGM_PATH', 'tmp/opengeometadata'),
26
+ ogm_path:,
27
+ skip_repos: ENV.fetch('OGM_SKIP_REPOS', '').split(',').map(&:strip),
28
+ skip_restricted: ENV.fetch('OGM_SKIP_RESTRICTED', 'true') == 'true',
34
29
  schema_version: ENV.fetch('SCHEMA_VERSION', 'Aardvark'),
35
30
  logger: GeoCombine::Logger.logger
36
31
  )
37
32
  @ogm_path = ogm_path
38
33
  @schema_version = schema_version
34
+ @skip_repos = skip_repos
35
+ @skip_restricted = skip_restricted
39
36
  @logger = logger
40
37
  end
41
38
 
42
39
  # Enumerable of docs to index, for passing to an indexer
40
+ # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
43
41
  def docs_to_index
44
42
  return to_enum(:docs_to_index) unless block_given?
45
43
 
@@ -53,19 +51,34 @@ module GeoCombine
53
51
 
54
52
  doc = JSON.parse(File.read(path))
55
53
  [doc].flatten.each do |record|
56
- # skip indexing if this record has a different schema version than what we want
57
54
  record_schema = record['gbl_mdVersion_s'] || record['geoblacklight_version']
58
55
  record_id = record['layer_slug_s'] || record['dc_identifier_s']
56
+ record_rights = record['dct_accessRights_s'] || record['dc_rights_s']
57
+
58
+ # skip indexing if no identifiable schema version
59
+ unless record_schema
60
+ @logger.debug "skipping #{record_id || path}; no schema version declared in record"
61
+ next
62
+ end
63
+
64
+ # skip indexing if this record has a different schema version than what we want
59
65
  if record_schema != @schema_version
60
66
  @logger.debug "skipping #{record_id}; schema version #{record_schema} doesn't match #{@schema_version}"
61
67
  next
62
68
  end
63
69
 
70
+ # skip indexing if this record is restricted and we want to skip restricted records
71
+ if @skip_restricted && record_rights == 'Restricted'
72
+ @logger.debug "skipping #{record_id}; access rights are restricted"
73
+ next
74
+ end
75
+
64
76
  @logger.debug "found record #{record_id} at #{path}"
65
77
  yield record, path
66
78
  end
67
79
  end
68
80
  end
81
+ # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
69
82
 
70
83
  # Update a repository via git
71
84
  # If the repository doesn't exist, clone it.
@@ -87,19 +100,20 @@ module GeoCombine
87
100
  end
88
101
 
89
102
  # Clone a repository via git
90
- # If the repository already exists, skip it.
103
+ # Return the name of the repository cloned, or nil if skipped
91
104
  def clone(repo)
92
105
  repo_path = File.join(@ogm_path, repo)
93
106
  repo_info = repository_info(repo)
94
107
  repo_url = "https://github.com/OpenGeoMetadata/#{repo}.git"
95
-
96
- # Skip if exists; warn if archived or empty
97
- if File.directory? repo_path
98
- @logger.warn "skipping clone to #{repo_path}; directory exists"
99
- return nil
108
+ repo_schemas = Array(repo_info.dig('custom_properties', 'supported_schemas'))
109
+
110
+ # Skip if exists, archived, empty, or different schema
111
+ return @logger.warn "skipping clone to #{repo_path}; directory exists" if File.directory? repo_path
112
+ return @logger.warn "repository is archived: #{repo_url}" if repo_info['archived']
113
+ return @logger.warn "repository is empty: #{repo_url}" if repo_info['size'].zero?
114
+ unless repo_schemas.include? @schema_version
115
+ return @logger.warn "repository #{repo_url} clone to #{repo_path}; repository properties don't include schema version #{@schema_version} (found #{repo_schemas.join(', ')})"
100
116
  end
101
- @logger.warn "repository is archived: #{repo_url}" if repo_info['archived']
102
- @logger.warn "repository is empty: #{repo_url}" if repo_info['size'].zero?
103
117
 
104
118
  Git.clone(repo_url, nil, path: ogm_path, depth: 1)
105
119
  @logger.info "cloned #{repo_url} to #{repo_path}"
@@ -119,12 +133,14 @@ module GeoCombine
119
133
  # List of repository names to harvest
120
134
  def repositories
121
135
  @repositories ||= JSON.parse(Net::HTTP.get(self.class.ogm_api_uri))
136
+ .filter { |repo| Array(repo.dig('custom_properties', 'supported_schemas')).include? @schema_version }
122
137
  .filter { |repo| repo['size'].positive? }
123
138
  .reject { |repo| repo['archived'] }
139
+ .reject { |repo| @skip_repos.include?(repo['name']) }
124
140
  .map { |repo| repo['name'] }
125
- .reject { |name| self.class.denylist.include? name }
126
141
  end
127
142
 
143
+ # Fetch repository metadata from GitHub API
128
144
  def repository_info(repo_name)
129
145
  JSON.parse(Net::HTTP.get(URI("https://api.github.com/repos/opengeometadata/#{repo_name}")))
130
146
  end