aipp 0.2.4 → 0.2.5

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.
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