aipp 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +26 -0
  3. data/.ruby-version +1 -1
  4. data/CHANGELOG.md +9 -0
  5. data/README.md +59 -7
  6. data/aipp.gemspec +3 -2
  7. data/lib/aipp.rb +2 -0
  8. data/lib/aipp/aip.rb +4 -12
  9. data/lib/aipp/downloader.rb +21 -21
  10. data/lib/aipp/executable.rb +4 -1
  11. data/lib/aipp/parser.rb +58 -5
  12. data/lib/aipp/regions/LF/AD-1.3.rb +27 -13
  13. data/lib/aipp/regions/LF/AD-1.6.rb +2 -2
  14. data/lib/aipp/regions/LF/AD-2.rb +30 -6
  15. data/lib/aipp/regions/LF/AD-3.1.rb +2 -2
  16. data/lib/aipp/regions/LF/ENR-2.1.rb +1 -1
  17. data/lib/aipp/regions/LF/ENR-4.1.rb +20 -78
  18. data/lib/aipp/regions/LF/ENR-4.3.rb +1 -1
  19. data/lib/aipp/regions/LF/ENR-5.1.rb +44 -26
  20. data/lib/aipp/regions/LF/ENR-5.5.rb +1 -1
  21. data/lib/aipp/regions/LF/helpers/{common.rb → base.rb} +2 -2
  22. data/lib/aipp/regions/LF/helpers/navigational_aid.rb +104 -0
  23. data/lib/aipp/regions/LF/helpers/{AD_radio.rb → radio_AD.rb} +24 -12
  24. data/lib/aipp/version.rb +1 -1
  25. data/lib/core_ext/object.rb +1 -1
  26. data/spec/fixtures/{archive.zip → source.zip} +0 -0
  27. data/spec/lib/aipp/airac_spec.rb +18 -18
  28. data/spec/lib/aipp/border_spec.rb +19 -19
  29. data/spec/lib/aipp/downloader_spec.rb +25 -25
  30. data/spec/lib/aipp/patcher_spec.rb +4 -4
  31. data/spec/lib/aipp/pdf_spec.rb +23 -23
  32. data/spec/lib/aipp/t_hash_spec.rb +6 -6
  33. data/spec/lib/aipp/version_spec.rb +1 -1
  34. data/spec/lib/core_ext/enumberable_spec.rb +15 -15
  35. data/spec/lib/core_ext/hash_spec.rb +4 -4
  36. data/spec/lib/core_ext/integer_spec.rb +2 -2
  37. data/spec/lib/core_ext/nil_class_spec.rb +1 -1
  38. data/spec/lib/core_ext/string_spec.rb +28 -28
  39. data/spec/spec_helper.rb +1 -0
  40. metadata +27 -12
  41. data/.travis.yml +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70da2e53874e0c550cbbacc863ff421cbd38dfc2cb1b7e4d2c77e2821839fdea
4
- data.tar.gz: 62e9d5c5413756490d03f057e8851aba54c7d8802f4012a2f9626f78eb241d6f
3
+ metadata.gz: cddca26d9635de7aa2ded8bde0def36adf97d548e9c133358443f7812c778ef6
4
+ data.tar.gz: cadd71e6709c70f03a6f2ceda4af1ef38b049d70f3aaad2cd45ef3fc792df192
5
5
  SHA512:
6
- metadata.gz: 6508c2a1565fdd92ed4fe2593b883278940e10fa10e68a0ffc75391b7e6d8ba8c66ca19c7e91495da39f60c4257e7882611a235ad9c8a5314b928140625a6e90
7
- data.tar.gz: ade8abd0e5f521faf63b684d719c1e71a85278ec57f56edf4c83349c1ca4fc68422c9948c74f059c064cbb6ec21d66c91216f6418d3749bd53dd6eb1ecca24cc
6
+ metadata.gz: 5d511918c12c5c877c64cb4d5f28e5d968cdd98e08f9dfc72b8f188579da214aa63e953b36a5f17dbdd8b2d5d0281209f29d2693a458c4b7613d0d012c4111e7
7
+ data.tar.gz: b3b4a1dc343049c3aec1c58290e52d09fb43adb476a6c085271d31f0d20c016db19cb9ed0f4355e8eb78f199fa3e6b9580a2f2a30d2e6ff8bcc6548f818d8e08
@@ -0,0 +1,26 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+
8
+ jobs:
9
+ build:
10
+
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ ruby:
15
+ - 2.6.x
16
+ steps:
17
+ - uses: actions/checkout@v1
18
+ - name: Set up Ruby ${{ matrix.ruby }}
19
+ uses: actions/setup-ruby@v1
20
+ with:
21
+ ruby-version: ${{ matrix.ruby }}
22
+ - name: Build and test
23
+ run: |
24
+ gem install bundler
25
+ bundle install --jobs 4 --retry 3
26
+ bundle exec rake
@@ -1 +1 @@
1
- ruby-2.6.3
1
+ ruby-2.6.5
@@ -1,3 +1,12 @@
1
+ ## 0.2.5
2
+
3
+ #### Additions
4
+ * LF/AD-2>2.19 (AD navigational aids relevant to VFR)
5
+ * Write build and manifest to `~/.aipp/<region>/builds`
6
+
7
+ #### Changes
8
+ * Renamed `~/.aipp/<region>/archive` to `~/.aipp/<region>/sources`
9
+
1
10
  ## 0.2.4
2
11
 
3
12
  #### Additions
data/README.md CHANGED
@@ -1,20 +1,31 @@
1
1
  [![Version](https://img.shields.io/gem/v/aipp.svg?style=flat)](https://rubygems.org/gems/aipp)
2
- [![Continuous Integration](https://img.shields.io/travis/svoop/aipp/master.svg?style=flat)](https://travis-ci.org/svoop/aipp)
2
+ [![Tests](https://github.com/svoop/aipp/workflows/Test/badge.svg)](https://github.com/svoop/aipp/actions?workflow=Test)
3
3
  [![Donorbox](https://img.shields.io/badge/donate-on_donorbox-yellow.svg)](https://donorbox.org/bitcetera)
4
4
 
5
5
  # AIPP
6
6
 
7
7
  Parser for Aeronautical Information Publication (AIP) available online.
8
8
 
9
- This gem incluces two executables to download and parse aeronautical data as HTML or PDF, then export is as [AIXM](https://github.com/svoop/aixm) or [OFMX](https://github.com/openflightmaps/ofmx/wiki).
9
+ This gem incluces two executables to download and parse aeronautical data as HTML or PDF, then build and export is as [AIXM](https://github.com/svoop/aixm) or [OFMX](https://github.com/openflightmaps/ofmx/wiki).
10
10
 
11
11
  * [Homepage](https://github.com/svoop/aipp)
12
12
  * [Rubydoc](https://www.rubydoc.info/gems/aipp/AIPP)
13
13
  * Author: [Sven Schwyn - Bitcetera](http://www.bitcetera.com)
14
14
 
15
+ ## Table of Contents
16
+
17
+ [Install](#install)<br>
18
+ [Usage](#usage)<br>
19
+ [Storage](#storage)<br>
20
+ [Regions](#regions)<br>
21
+ [AIP Parsers](#aip-parsers)<br>
22
+ [References](#references)<br>
23
+ [AIRAC Date Calculations](#airac-date-calculations)<br>
24
+ [Development](#development)
25
+
15
26
  ## Install
16
27
 
17
- Add this to your <tt>Gemfile</tt>:
28
+ Add this to your <tt>Gemfile</tt> or <tt>gems.rb</tt>:
18
29
 
19
30
  ```ruby
20
31
  gem aipp
@@ -22,15 +33,56 @@ gem aipp
22
33
 
23
34
  ## Usage
24
35
 
36
+ See the built-in help for all options:
37
+
25
38
  ```
26
39
  aip2aixm --help
27
40
  aip2ofmx --help
28
41
  ```
29
42
 
43
+ Say, you with to build the complete OFMX file for the current AIRAC cycle of the region LF:
44
+
45
+ ```
46
+ aip2ofmx -r LF
47
+ ```
48
+
49
+ You'll find the OFMX file in the current directory if the binary exits successfully.
50
+
30
51
  ## Storage
31
52
 
32
53
  AIPP uses a storage directory for configuration, caching and in order to keep the results of previous runs. The default location is `~/.aipp`, however, you can pass a different directory with the `--storage` argument.
33
54
 
55
+ You'll find a directory for each region which contains the following items:
56
+
57
+ * `sources/`<br>This directory contains one ZIP archive per AIRAC cycle which incrementially caches all source files used to build the AIXM/OFMX file. Therefore, to make sure it contains all source files for a region, you have to build at least one complete AIXM/OFMX file for that region.
58
+ * `builds/`<br>This directory contains one ZIP archive per AIRAC cycle which is overwritten on every run. Therefore, to make sure it contains the complete build for a region, you have to make sure that your last run builds the complete AIXM/OFMX for that region. This archive contains:
59
+ * the built AIXM/OFMX file
60
+ * `build.yaml` – context of the build process
61
+ * `manifest.csv` – diffable manifest (see below)
62
+ * `config.yml`<br>This file contains configuration which will be read on subsequent runs, most notably the namespace UUID used to identify the creator of OFMX files.
63
+
64
+ The manifest is a CSV which lists every feature on a separate line along with its hashes and comment. You can `diff` or `git diff` two manifests:
65
+
66
+ ```diff
67
+ $ git diff -U0 2019-09-12/manifest.csv 2019-10-10/manifest.csv
68
+
69
+ --- a/2019-09-12/manifest.csv
70
+ +++ b/2019-10-10/manifest.csv
71
+ @@ -89,0 +90 @@ Aha,449f4791,d6d4bff8,Address: PHONE for LFF958FC61
72
+ +Aha,44b9a044,cf55a3cf,Address: PHONE for LF49929DD6
73
+ @@ -134 +135 @@ Aha,6acfbcc5,c4f57e3f,Address: FAX for LF897E1A6F
74
+ -Aha,6b381b32,fb947716,Address: RADIO for LFPO
75
+ +Aha,6b381b32,b9723b7e,Address: RADIO for LFPO
76
+ @@ -253,0 +255 @@ Aha,d01c4282,7cc8db7f,Address: PHONE for LF0C413AE2
77
+ +Aha,d05cfbff,74fb76bc,Address: FAX for LF49929DD6
78
+ @@ -327 +329 @@ Ahp,068f4f01,1dc2fb5c,Airport: LFEA BELLE ILE
79
+ -Ahp,06d04910,e18af958,Airport: LFFDE9077D TONNERRE CENTRE HOSPITALIER
80
+ +Ahp,06d04910,cc522711,Airport: LFFDE9077D TONNERRE CENTRE HOSPITALIER
81
+ (...)
82
+ ```
83
+
84
+ The advantage of `git diff` is it's ability to hightlight exactly which part of a line has changed. [Check out this post to learn how](https://www.viget.com/articles/dress-up-your-git-diffs-with-word-level-highlights/).
85
+
34
86
  ## Regions
35
87
 
36
88
  The reference implementation is region "LF" (France).
@@ -83,7 +135,7 @@ Inside the `parse` method, you have access to the following methods:
83
135
 
84
136
  * [`read`](https://www.rubydoc.info/gems/aipp/AIPP/AIP#read-instance_method) – download and read an AIP file
85
137
  * [`add`](https://www.rubydoc.info/gems/aipp/AIPP/AIP#add-instance_method) – add a [`AIXM::Feature`](https://www.rubydoc.info/gems/aixm/AIXM/Feature)
86
- * [`select`](https://www.rubydoc.info/gems/aipp/AIPP/AIP#find-instance_method – search previously written [`AIXM::Feature`s](https://www.rubydoc.info/gems/aixm/AIXM/Feature)
138
+ * [`select`](https://www.rubydoc.info/gems/aipp/AIPP/AIP#find-instance_method) – search previously written [`AIXM::Feature`s](https://www.rubydoc.info/gems/aixm/AIXM/Feature)
87
139
  * some core extensions from ActiveSupport – [`Object#blank`](https://www.rubydoc.info/gems/activesupport/Object#blank%3F-instance_method) and [`String`](https://www.rubydoc.info/gems/activesupport/String)
88
140
  * core extensions from this gem – [`Object`](https://www.rubydoc.info/gems/aipp/Object), [`NilClass`](https://www.rubydoc.info/gems/aipp/NilClass), [`Integer`](https://www.rubydoc.info/gems/aipp/Integer), [`String`](https://www.rubydoc.info/gems/aipp/String), [`Hash`](https://www.rubydoc.info/gems/aipp/Hash) and [`Enumerable`](https://www.rubydoc.info/gems/aipp/Enumerable)
89
141
 
@@ -129,7 +181,7 @@ See [`AIPP::Border`](https://www.rubydoc.info/gems/aipp/AIPP/Border) for more on
129
181
 
130
182
  ### Helpers
131
183
 
132
- Helpers are modules defined in the <tt>lib/aipp/regions/{REGION}/helpers/</tt> directory. All helper modules are required automatically.
184
+ Helpers are modules defined in the <tt>lib/aipp/regions/{REGION}/helpers/</tt> directory. All helper modules are required automatically in alphabetic order.
133
185
 
134
186
  There is one mandatory helper called `URL.rb` which must define the following method to build URLs from which to download AIPs:
135
187
 
@@ -155,7 +207,7 @@ Feel free to add more helpers to DRY code which is shared by multiple AIP parser
155
207
  module AIPP
156
208
  module LF
157
209
  module Helpers
158
- module Common
210
+ module Base
159
211
 
160
212
  def source(position:, aip_file: nil)
161
213
  (...)
@@ -174,7 +226,7 @@ module AIPP
174
226
  module LF
175
227
  class AD2 < AIP
176
228
 
177
- include AIPP::LF::Helpers::Common
229
+ include AIPP::LF::Helpers::Base
178
230
 
179
231
  end
180
232
  end
@@ -25,18 +25,19 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency 'minitest'
26
26
  spec.add_development_dependency 'minitest-reporters'
27
27
  spec.add_development_dependency 'minitest-sound'
28
+ spec.add_development_dependency 'minitest-focus'
28
29
  spec.add_development_dependency 'minitest-matchers'
29
30
  spec.add_development_dependency 'spy'
30
31
  spec.add_development_dependency 'guard'
31
32
  spec.add_development_dependency 'guard-minitest'
32
33
 
33
- spec.add_runtime_dependency 'aixm', '>= 0.3.6'
34
+ spec.add_runtime_dependency 'aixm', '>= 0.3.8'
34
35
  spec.add_runtime_dependency 'activesupport', '~> 5'
35
36
  spec.add_runtime_dependency 'nokogiri', '~> 1'
36
37
  spec.add_runtime_dependency 'nokogumbo', '~> 2'
37
38
  spec.add_runtime_dependency 'pdf-reader', '~> 2'
38
39
  spec.add_runtime_dependency 'json', '~> 2'
39
- spec.add_runtime_dependency 'rubyzip', '~> 1'
40
+ spec.add_runtime_dependency 'rubyzip', '~> 2'
40
41
  spec.add_runtime_dependency 'colorize', '~> 0'
41
42
  spec.add_runtime_dependency 'pry', '~> 0'
42
43
  spec.add_runtime_dependency 'pry-rescue', '~> 1'
@@ -4,7 +4,9 @@ require 'pry'
4
4
  require 'pry-rescue'
5
5
  require 'optparse'
6
6
  require 'yaml'
7
+ require 'csv'
7
8
  require 'pathname'
9
+ require 'tmpdir'
8
10
  require 'open-uri'
9
11
  require 'securerandom'
10
12
  require 'tsort'
@@ -35,9 +35,8 @@ module AIPP
35
35
 
36
36
  # Read an AIP source file
37
37
  #
38
- # Depending on whether a local copy of the file exists, either:
39
- # * download from URL to local storage and read from local archive
40
- # * read from local archive
38
+ # Read the cached source file if it exists in the source archive. Otherwise,
39
+ # download it from URL and cache it.
41
40
  #
42
41
  # An URL builder method +url_for(aip_file)+ must be defined either in
43
42
  # +helper.rb+ or in the AIP parser definition (e.g. +ENR-2.1.rb+).
@@ -69,15 +68,8 @@ module AIPP
69
68
  # e.g. :airport or :vor as listed in AIXM::CLASSES are recognized as well
70
69
  # @param attributes [Hash] filter by these attributes and their values
71
70
  # @return [Array<AIXM::Feature>]
72
- def select(klass, attributes={})
73
- klass = AIXM::CLASSES.fetch(klass) if klass.is_a? Symbol
74
- aixm.features.select do |feature|
75
- if feature.is_a? klass
76
- attributes.reduce(true) do |memo, (attribute, value)|
77
- memo && feature.send(attribute) == value
78
- end
79
- end
80
- end
71
+ def select(*args)
72
+ aixm.select_features(*args)
81
73
  end
82
74
  end
83
75
 
@@ -3,15 +3,15 @@ module AIPP
3
3
  # AIP downloader infrastructure
4
4
  #
5
5
  # The downloader operates in the +storage+ directory where it creates two
6
- # subdirectories "archive" and "work". The initializer looks for +archive+
7
- # in "archives" and (if found) unzips its contents into "work". When reading
8
- # a +document+, the downloader looks for the +document+ in "work" and
6
+ # subdirectories "sources" and "work". The initializer looks for the +source+
7
+ # archive in "sources" and (if found) unzips its contents into "work". When
8
+ # reading a +document+, the downloader looks for the +document+ in "work" and
9
9
  # (unless found) downloads it from +url+. HTML documents are parsed to
10
10
  # +Nokogiri::HTML5::Document+, PDF documents are parsed to +AIPP::PDF+.
11
- # Finally, the contents of "work" are written back to +archive+.
11
+ # Finally, the contents of "work" are written back to the +source+ archive.
12
12
  #
13
13
  # @example
14
- # AIPP::Downloader.new(storage: options[:storage], archive: "2018-11-08") do |downloader|
14
+ # AIPP::Downloader.new(storage: options[:storage], source: "2018-11-08") do |downloader|
15
15
  # html = downloader.read(
16
16
  # document: 'ENR-5.1',
17
17
  # url: 'https://www.sia.aviation-civile.gouv.fr/dvd/eAIP_08_NOV_2018/FRANCE/AIRAC-2018-11-08/html/eAIP/FR-ENR-5.1-fr-FR.html'
@@ -26,20 +26,20 @@ module AIPP
26
26
  # @return [Pathname] directory to operate within
27
27
  attr_reader :storage
28
28
 
29
- # @return [String] name of the archive (without extension ".zip")
30
- attr_reader :archive
29
+ # @return [String] name of the source archive (without extension ".zip")
30
+ attr_reader :source
31
31
 
32
- # @return [Pathname] full path to the archive
33
- attr_reader :archive_file
32
+ # @return [Pathname] full path to the source archive
33
+ attr_reader :source_file
34
34
 
35
35
  # @param storage [Pathname] directory to operate within
36
- # @param archive [String] name of the archive (without extension ".zip")
37
- def initialize(storage:, archive:)
38
- @storage, @archive = storage, archive
36
+ # @param source [String] name of the source archive (without extension ".zip")
37
+ def initialize(storage:, source:)
38
+ @storage, @source = storage, source
39
39
  fail(ArgumentError, 'bad storage directory') unless Dir.exist? storage
40
- @archive_file = archives_path.join("#{@archive}.zip")
40
+ @source_file = sources_path.join("#{@source}.zip")
41
41
  prepare
42
- unzip if @archive_file.exist?
42
+ unzip if @source_file.exist?
43
43
  yield self
44
44
  zip
45
45
  ensure
@@ -65,8 +65,8 @@ module AIPP
65
65
 
66
66
  private
67
67
 
68
- def archives_path
69
- @storage.join('archives')
68
+ def sources_path
69
+ @storage.join('sources')
70
70
  end
71
71
 
72
72
  def work_path
@@ -75,7 +75,7 @@ module AIPP
75
75
 
76
76
  def prepare
77
77
  teardown
78
- archives_path.mkpath
78
+ sources_path.mkpath
79
79
  work_path.mkpath
80
80
  end
81
81
 
@@ -87,15 +87,15 @@ module AIPP
87
87
  end
88
88
 
89
89
  def unzip
90
- Zip::File.open(archive_file).each do |entry|
90
+ Zip::File.open(source_file).each do |entry|
91
91
  entry.extract(work_path.join(entry.name))
92
92
  end
93
93
  end
94
94
 
95
95
  def zip
96
- backup_file = archive_file.sub(/$/, '.old') if archive_file.exist?
97
- archive_file.rename(backup_file) if backup_file
98
- Zip::File.open(archive_file, Zip::File::CREATE) do |zip|
96
+ backup_file = source_file.sub(/$/, '.old') if source_file.exist?
97
+ source_file.rename(backup_file) if backup_file
98
+ Zip::File.open(source_file, Zip::File::CREATE) do |zip|
99
99
  work_path.children.each do |entry|
100
100
  zip.add(entry.basename.to_s, entry) unless entry.basename.to_s[0] == '.'
101
101
  end
@@ -8,7 +8,8 @@ module AIPP
8
8
  @options = options
9
9
  @options[:airac] = AIPP::AIRAC.new
10
10
  @options[:storage] = Pathname(Dir.home).join('.aipp')
11
- @options[:force] = $VERBOSE_INFO = $PRY_ON_WARN = $PRY_ON_ERROR = false
11
+ @options[:force] = @options[:mid] = false
12
+ $VERBOSE_INFO = $PRY_ON_WARN = $PRY_ON_ERROR = false
12
13
  OptionParser.new do |o|
13
14
  o.banner = <<~END
14
15
  Download online AIP and convert it to #{options[:schema].upcase}.
@@ -17,6 +18,7 @@ module AIPP
17
18
  o.on('-d', '--airac DATE', String, %Q[AIRAC date (default: "#{@options[:airac].date.xmlschema}")]) { |v| @options[:airac] = AIPP::AIRAC.new(v) }
18
19
  o.on('-r', '--region STRING', String, 'region (e.g. "LF")') { |v| @options[:region] = v.upcase }
19
20
  o.on('-a', '--aip STRING', String, 'process this AIP only (e.g. "ENR-5.1")') { |v| @options[:aip] = v.upcase }
21
+ o.on('-m', '--[no-]mid', 'insert mid attributes into all Uid elements (default: false)') { |v| @options[:mid] = v }
20
22
  o.on('-s', '--storage DIR', String, 'storage directory (default: "~/.aipp")') { |v| @options[:storage] = Pathname(v) }
21
23
  o.on('-f', '--[no-]force', 'ignore XML schema validation (default: false)') { |v| @options[:force] = v }
22
24
  o.on('-v', '--[no-]verbose', 'verbose output (default: false)') { |v| $VERBOSE_INFO = v }
@@ -41,6 +43,7 @@ module AIPP
41
43
  parser.parse_aip
42
44
  parser.validate_aixm
43
45
  parser.write_aixm
46
+ parser.write_build
44
47
  parser.write_config
45
48
  end
46
49
  rescue => error
@@ -3,6 +3,8 @@ module AIPP
3
3
  # AIP parser infrastructure
4
4
  class Parser
5
5
 
6
+ using AIXM::Refinements
7
+
6
8
  # @return [Hash] passed command line arguments
7
9
  attr_reader :options
8
10
 
@@ -24,7 +26,7 @@ module AIPP
24
26
  def initialize(options:)
25
27
  @options = options
26
28
  @options[:storage] = options[:storage].join(options[:region])
27
- @options[:storage].mkpath unless @options[:storage].exist?
29
+ @options[:storage].mkpath
28
30
  @config = {}
29
31
  @aixm = AIXM.document(region: @options[:region], effective_at: @options[:airac].date)
30
32
  @dependencies = THash.new
@@ -76,7 +78,7 @@ module AIPP
76
78
  # Parse AIP by invoking the parser classes for the current region.
77
79
  def parse_aip
78
80
  info("AIRAC #{options[:airac].id} effective #{options[:airac].date}", color: :green)
79
- AIPP::Downloader.new(storage: options[:storage], archive: options[:airac].date.xmlschema) do |downloader|
81
+ AIPP::Downloader.new(storage: options[:storage], source: options[:airac].date.xmlschema) do |downloader|
80
82
  @dependencies.tsort(options[:aip]).each do |aip|
81
83
  info("Parsing #{aip}")
82
84
  ("AIPP::%s::%s" % [options[:region], aip.remove(/\W/).classify]).constantize.new(
@@ -102,9 +104,52 @@ module AIPP
102
104
 
103
105
  # Write the AIXM document.
104
106
  def write_aixm
105
- file = "#{options[:region]}_#{options[:airac].date.xmlschema}.#{options[:schema]}"
106
- info("Writing #{file}")
107
- File.write(file, aixm.to_xml)
107
+ info("Writing #{aixm_file}")
108
+ AIXM.config.mid_region = options[:region] if options[:mid]
109
+ File.write(aixm_file, aixm.to_xml)
110
+ end
111
+
112
+ # Write the AIXM document and context information.
113
+ def write_build
114
+ info("Writing build")
115
+ builds_path.mkpath
116
+ build_file = builds_path.join("#{@options[:airac].date.xmlschema}.zip")
117
+ build_file.delete if build_file.exist?
118
+ Dir.mktmpdir do |tmp_dir|
119
+ tmp_dir = Pathname(tmp_dir)
120
+ AIXM.config.mid_region = options[:region]
121
+ # AIXM/OFMX file
122
+ File.write(tmp_dir.join(aixm_file), aixm.to_xml)
123
+ # Build details
124
+ File.write(
125
+ tmp_dir.join('build.yaml'), {
126
+ version: AIPP::VERSION,
127
+ config: @config,
128
+ options: @options
129
+ }.to_yaml
130
+ )
131
+ # Manifest
132
+ manifest, buffer, feature, uid, comment = [], '', '', '', ''
133
+ File.open(tmp_dir.join(aixm_file)).each do |line|
134
+ buffer << line
135
+ case line
136
+ when /^ {2}<(\w{3}) / then buffer, feature = line, $1
137
+ when /^ {4}<#{feature}Uid mid="(.*?)"/ then uid = $1
138
+ when /^ {2}<!-- (.*) -->/ then comment = $1
139
+ when /^ {2}<\/#{feature}>/
140
+ manifest << [feature, uid[0,8], buffer.payload_hash(region: options[:region])[0,8], comment].to_csv
141
+ feature, uid = '', ''
142
+ end
143
+ end
144
+ manifest = manifest.sort.prepend "Feature,Short Uid Hash,Short Feature Hash,Comment\n"
145
+ File.write(tmp_dir.join('manifest.csv'), manifest.join)
146
+ # Zip it
147
+ Zip::File.open(build_file, Zip::File::CREATE) do |zip|
148
+ tmp_dir.children.each do |entry|
149
+ zip.add(entry.basename.to_s, entry) unless entry.basename.to_s[0] == '.'
150
+ end
151
+ end
152
+ end
108
153
  end
109
154
 
110
155
  # Write the configuration to config.yml.
@@ -115,6 +160,14 @@ module AIPP
115
160
 
116
161
  private
117
162
 
163
+ def aixm_file
164
+ "#{options[:region]}_#{options[:airac].date.xmlschema}.#{options[:schema]}"
165
+ end
166
+
167
+ def builds_path
168
+ options[:storage].join('builds')
169
+ end
170
+
118
171
  def config_file
119
172
  options[:storage].join('config.yml')
120
173
  end