epo-ops 0.2.6 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.travis.yml +6 -0
  4. data/README.md +78 -38
  5. data/epo-ops.gemspec +2 -2
  6. data/lib/epo_ops.rb +46 -0
  7. data/lib/epo_ops/client.rb +46 -0
  8. data/lib/epo_ops/error.rb +87 -0
  9. data/lib/epo_ops/factories.rb +9 -0
  10. data/lib/epo_ops/factories/name_and_address_factory.rb +54 -0
  11. data/lib/epo_ops/factories/patent_application_factory.rb +116 -0
  12. data/lib/epo_ops/factories/register_search_result_factory.rb +42 -0
  13. data/lib/epo_ops/ipc_class_hierarchy.rb +146 -0
  14. data/lib/epo_ops/ipc_class_hierarchy_loader.rb +60 -0
  15. data/lib/epo_ops/ipc_class_util.rb +71 -0
  16. data/lib/epo_ops/limits.rb +20 -0
  17. data/lib/epo_ops/logger.rb +15 -0
  18. data/lib/epo_ops/name_and_address.rb +58 -0
  19. data/lib/epo_ops/patent_application.rb +159 -0
  20. data/lib/epo_ops/rate_limit.rb +47 -0
  21. data/lib/epo_ops/register.rb +100 -0
  22. data/lib/epo_ops/register_search_result.rb +40 -0
  23. data/lib/epo_ops/search_query_builder.rb +65 -0
  24. data/lib/epo_ops/token_store.rb +33 -0
  25. data/lib/epo_ops/token_store/redis.rb +45 -0
  26. data/lib/epo_ops/util.rb +52 -0
  27. data/lib/epo_ops/version.rb +3 -0
  28. metadata +26 -20
  29. data/lib/epo/ops.rb +0 -43
  30. data/lib/epo/ops/address.rb +0 -60
  31. data/lib/epo/ops/bibliographic_document.rb +0 -196
  32. data/lib/epo/ops/client.rb +0 -27
  33. data/lib/epo/ops/error.rb +0 -89
  34. data/lib/epo/ops/ipc_class_hierarchy.rb +0 -148
  35. data/lib/epo/ops/ipc_class_hierarchy_loader.rb +0 -62
  36. data/lib/epo/ops/ipc_class_util.rb +0 -73
  37. data/lib/epo/ops/limits.rb +0 -22
  38. data/lib/epo/ops/logger.rb +0 -11
  39. data/lib/epo/ops/rate_limit.rb +0 -49
  40. data/lib/epo/ops/register.rb +0 -152
  41. data/lib/epo/ops/search_query_builder.rb +0 -65
  42. data/lib/epo/ops/token_store.rb +0 -35
  43. data/lib/epo/ops/token_store/redis.rb +0 -47
  44. data/lib/epo/ops/util.rb +0 -32
  45. data/lib/epo/ops/version.rb +0 -6
@@ -1,43 +0,0 @@
1
- require 'epo/ops/version'
2
- require 'epo/ops/token_store'
3
- require 'epo/ops/register'
4
- require 'epo/ops/search_query_builder'
5
- require 'epo/ops/ipc_class_hierarchy_loader'
6
- require 'epo/ops/ipc_class_util'
7
-
8
- module Epo
9
- module Ops
10
- # Configure your OAuth credentials to use with this gem.
11
- # @example
12
- # Epo:Ops.configure do |conf|
13
- # conf.consumer_key = "foo"
14
- # conf.consumer_secret = "bar"
15
- # end
16
- # Optional parameter:
17
- # conf.token_store (defaults to {Epo::Ops::TokenStore})
18
- # @yieldparam [Configuration] configuration that is yielded.
19
- def self.configure
20
- yield(config)
21
- end
22
-
23
- # The {Configuration} used. You may want to call {Epo::Ops#configure} first.
24
- # @return [Configuration] the configuration used.
25
- def self.config
26
- @configuration ||= Configuration.new
27
- end
28
-
29
- class Configuration
30
- attr_accessor :consumer_key, :consumer_secret, :token_store
31
-
32
- def initialize
33
- @consumer_key = ''
34
- @consumer_secret = ''
35
- @token_store = Epo::Ops::TokenStore.new
36
-
37
- OAuth2::Response.register_parser(:xml, ['application/xml']) do |body|
38
- MultiXml.parse(body)
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,60 +0,0 @@
1
- module Epo
2
- module Ops
3
- # Used to represent persons or companies (or both) in patents. Used for
4
- # both, agents and applicants. Most of the time, when `name` is a person
5
- # name, `address1` is a company name. Be aware that the addresses are in
6
- # their respective local format.
7
- #
8
- # Current patents usually at least use the fields address1-3, so they should
9
- # nearly always have values. Nevertheless, older ones often only use 1-2.
10
- # Note also that EPOs schema documents fields like `street` or `city`, but
11
- # by now they have not been used yet.
12
- #
13
- # @attr [String] name the name of an entity (one or more persons or
14
- # companies)
15
- # @attr [String] address1 first address line. May also be a company name
16
- # @attr [String] address2 second address line
17
- # @attr [String] address3 third address line, may be empty
18
- # @attr [String] address4 fourth address line, may be empty
19
- # @attr [String] address5 fifth address line, may be empty
20
- # @attr [String] country_code two letter country code of the address
21
- # @attr [Date] occurred_on the date an address occurred on, usually matching
22
- # the entries change_gazette_num
23
- # @attr [String] cdsid some kind of id the EPO provides, not sure yet if
24
- # usable as reference.
25
- class Address
26
- attr_reader :name, :address1,
27
- :address2,
28
- :address3,
29
- :address4,
30
- :address5,
31
- :country_code,
32
- :occurred_on,
33
- :cdsid
34
- def initialize(name, address1, address2, address3, address4,
35
- address5, country_code, occurred_on,
36
- cdsid)
37
- @address1 = address1
38
- @address2 = address2
39
- @address3 = address3 || ''
40
- @address4 = address4 || ''
41
- @address5 = address5 || ''
42
- @name = name
43
- @country_code = country_code || ''
44
- @occurred_on = occurred_on || ''
45
- @cdsid = cdsid || ''
46
- end
47
-
48
- # Compare addresses by the name and address fields.
49
- # @return [Boolean]
50
- def equal_name_and_address?(other)
51
- name == other.name &&
52
- address1 == other.address1 &&
53
- address2 == other.address2 &&
54
- address3 == other.address3 &&
55
- address4 == other.address4 &&
56
- address5 == other.address5
57
- end
58
- end
59
- end
60
- end
@@ -1,196 +0,0 @@
1
- require 'epo/ops/address'
2
-
3
- module Epo
4
- module Ops
5
- # Parses and simplifies the elements the EPO OPS returns for bibliographic
6
- # documents. Parsing is done lazily.
7
- # Some elements are not yet fully parsed but hashes returned instead.
8
- # Not all information available is parsed (e.g. inventors), if you need
9
- # more fields, add them here.
10
- class BibliographicDocument
11
- # @return [Hash] a nested Hash, which is a parsed XML response of the
12
- # `/biblio` endpoint of the EPO APIs.
13
- # @see Client
14
- attr_reader :raw
15
-
16
- def initialize(raw)
17
- @raw = raw
18
- end
19
-
20
- # A number by which a patent is uniquely identifiable and querieable.
21
- # The first two letters are the country code of the processing patent
22
- # office, for european patents this is EP.
23
- # @return [String] application number.
24
- def application_nr
25
- @application_nr ||= parse_application_nr
26
- end
27
-
28
- # @return [String] The URL at which you can query the original document.
29
- def url
30
- @url ||= "https://ops.epo.org/#{Epo::Ops::API_VERSION}/rest-services/register/application/epodoc/#{application_nr}"
31
- end
32
-
33
- # @return [String] the english title of the patent @note Titles are
34
- # usually available at least in english, french and german.
35
- # Other languages are also possible.
36
- def title
37
- @title ||= parse_title
38
- end
39
-
40
- # @return [Array] a list of the IPC-Classifications, as strings.
41
- # Format is set by EPO, should be similar to: E06B7/23
42
- def classifications
43
- @classifications ||= parse_classification raw
44
- end
45
-
46
- # Agents and applicants are subject to change at EPO, often
47
- # their names or addresses are updated, sometimes other
48
- # people/companies appear or disappear.
49
- #
50
- # @return [Array] Array of {Address}
51
- def agents
52
- @agents ||= parse_agents raw
53
- end
54
-
55
- # (see #agents)
56
- def applicants
57
- @applicants ||= parse_applicants raw
58
- end
59
-
60
- # @return [String] the string representation of the current patent status as
61
- # described by the EPO
62
- def status
63
- @status ||= parse_status raw
64
- end
65
-
66
- # Many fields of the XML the EPO provides have a field
67
- # `change_gazette_num`. It is a commercial date (year + week)
68
- # that describes in which week the element has been
69
- # changed. This method parses them and returns the most recent
70
- # date found.
71
- # @return [Date] the latest date found in the document.
72
- def latest_update
73
- @latest ||= parse_latest_update raw
74
- end
75
-
76
- # The priority date describes the first document that was filed at any
77
- # patent office in the world regarding this patent.
78
- # @return [Hash] a hash which descibes the filed priority with the fields:
79
- # `country` `doc_number`, `date`, `kind`, and `sequence`
80
- def priority_date
81
- @priority_date ||= parse_priority_date raw
82
- end
83
-
84
- # @return [Array] List of hashes containing information about publications
85
- # made, entries exist for multiple types of publications, e.g. A1, B1.
86
- def publication_references
87
- @publication_dates ||= parse_publication_references raw
88
- end
89
-
90
- def effective_date
91
- @effective_date ||= parse_effective_date raw
92
- end
93
-
94
- private
95
-
96
- def parse_title
97
- titles = Util.find_in_data(raw,
98
- path_to_bibliographic_data +
99
- ['invention_title'])
100
- titles.each do |the_title|
101
- return the_title['__content__'] if the_title['lang'] == 'en'
102
- end
103
- # no english title found
104
- titles.first['__content__']
105
- end
106
-
107
- def parse_application_nr
108
- path = %w(world_patent_data register_search query __content__)
109
- Util.find_in_data(raw, path).first.partition('=').last
110
- end
111
-
112
- def parse_priority_date(raw)
113
- priority_claims = Util.find_in_data(raw,
114
- path_to_bibliographic_data +
115
- %w(priority_claims))
116
- .first
117
- if priority_claims.nil?
118
- priority_date = nil
119
- else
120
- priority_date = priority_claims['priority_claim'].is_a?(Hash) ? priority_claims['priority_claim'] : priority_claims['priority_claim'].first
121
- end
122
- priority_date
123
- end
124
-
125
- def parse_publication_references(raw)
126
- Util.parse_hash_flat(
127
- Util.find_in_data(raw,
128
- path_to_bibliographic_data +
129
- %w(publication_reference)), 'document_id')
130
- end
131
-
132
- def parse_effective_date(raw)
133
- effective_date =
134
- Util.find_in_data(raw,
135
- path_to_bibliographic_data +
136
- %w(dates_rights_effective request_for_examination))
137
- effective_date.first.nil? ? nil : effective_date.first['date']
138
- end
139
-
140
- def parse_latest_update(raw)
141
- gazette_nums = Util.parse_hash_flat(raw, 'change_gazette_num')
142
- nums = gazette_nums.map { |num| Util.parse_change_gazette_num(num) }.keep_if { |match| !match.nil? }
143
- nums.max
144
- end
145
-
146
- def parse_status(raw)
147
- Util.find_in_data(raw,
148
- path_to_bibliographic_data + ['status'])
149
- .first
150
- end
151
-
152
- def parse_classification(raw)
153
- Util.find_in_data(raw,
154
- path_to_bibliographic_data +
155
- %w(classifications_ipcr classification_ipcr text))
156
- .first.split(',').map(&:strip)
157
- end
158
-
159
- def parse_agents(raw)
160
- entries = Util.find_in_data(raw,
161
- path_to_bibliographic_data +
162
- %w(parties agents))
163
- parse_address(entries, 'agent')
164
- end
165
-
166
- def parse_applicants(raw)
167
- entries = Util.find_in_data(raw,
168
- path_to_bibliographic_data +
169
- %w(parties applicants))
170
- parse_address(entries, 'applicant')
171
- end
172
-
173
- def parse_address(party_group_entries, group)
174
- party_group_entries.flat_map do |entry|
175
- change_date = Util.parse_change_gazette_num(
176
- entry.fetch('change_gazette_num', '')) || latest_update
177
- Util.find_in_data(entry, [group, 'addressbook']).map do |address|
178
- Address.new(address['name'],
179
- address['address']['address_1'],
180
- address['address']['address_2'],
181
- address['address']['address_3'],
182
- address['address']['address_4'],
183
- address['address']['address_5'],
184
- address['address']['country'],
185
- change_date,
186
- address['cdsid'])
187
- end
188
- end
189
- end
190
-
191
- def path_to_bibliographic_data
192
- %w(world_patent_data register_search register_documents register_document bibliographic_data)
193
- end
194
- end
195
- end
196
- end
@@ -1,27 +0,0 @@
1
- require 'epo/ops/token_store'
2
- require 'epo/ops/error'
3
-
4
- module Epo
5
- module Ops
6
-
7
- # This is a wrapper for OAuth
8
- class Client
9
- # @return [OAuth2::Response]
10
- def self.request(verb, url, options = {})
11
- do_request(verb, url, options)
12
- rescue Error::AccessTokenExpired
13
- Epo::Ops.config.token_store.reset
14
- do_request(verb, url, options)
15
- end
16
-
17
- private
18
-
19
- def self.do_request(verb, url, options = {})
20
- token = Epo::Ops.config.token_store.token
21
- response = token.request(verb, URI.encode(url), options)
22
- fail Error.from_response(response) unless response.status == 200
23
- response
24
- end
25
- end
26
- end
27
- end
@@ -1,89 +0,0 @@
1
- require 'epo/ops/rate_limit'
2
-
3
- module Epo
4
- module Ops
5
- class Error < StandardError
6
- # @return [Integer]
7
- attr_reader :code, :rate_limit
8
-
9
- # Raised when EPO returns a 4xx HTTP status code
10
- ClientError = Class.new(self)
11
- # Raised when EPO returns the HTTP status code 400
12
- BadRequest = Class.new(ClientError)
13
- # Raised when EPO returns the HTTP status code 401
14
- Unauthorized = Class.new(ClientError)
15
- # Raised when EPO returns the HTTP status code 403
16
- Forbidden = Class.new(ClientError)
17
- # Raised when EPO returns the HTTP status code 404
18
- NotFound = Class.new(ClientError)
19
- # Raised when EPO returns the HTTP status code 406
20
- NotAcceptable = Class.new(ClientError)
21
- # Raised when EPO returns the HTTP status code 422
22
- UnprocessableEntity = Class.new(ClientError)
23
- # Raised when EPO returns the HTTP status code 429
24
- TooManyRequests = Class.new(ClientError)
25
- # Raised when EPO returns a 5xx HTTP status code
26
- ServerError = Class.new(self)
27
- # Raised when EPO returns the HTTP status code 500
28
- InternalServerError = Class.new(ServerError)
29
- # Raised when EPO returns the HTTP status code 502
30
- BadGateway = Class.new(ServerError)
31
- # Raised when EPO returns the HTTP status code 503
32
- ServiceUnavailable = Class.new(ServerError)
33
- # Raised when EPO returns the HTTP status code 504
34
- GatewayTimeout = Class.new(ServerError)
35
- # AccessToken has expired
36
- AccessTokenExpired = Class.new(ClientError)
37
-
38
- ERRORS = {
39
- 400 => Epo::Ops::Error::BadRequest,
40
- 401 => Epo::Ops::Error::Unauthorized,
41
- 403 => Epo::Ops::Error::Forbidden,
42
- 404 => Epo::Ops::Error::NotFound,
43
- 406 => Epo::Ops::Error::NotAcceptable,
44
- 422 => Epo::Ops::Error::UnprocessableEntity,
45
- 429 => Epo::Ops::Error::TooManyRequests,
46
- 500 => Epo::Ops::Error::InternalServerError,
47
- 502 => Epo::Ops::Error::BadGateway,
48
- 503 => Epo::Ops::Error::ServiceUnavailable,
49
- 504 => Epo::Ops::Error::GatewayTimeout
50
- }.freeze
51
- FORBIDDEN_MESSAGES = {
52
- 'This request has been rejected due to the violation of Fair Use policy' => Epo::Ops::Error::TooManyRequests
53
- }.freeze
54
-
55
- class << self
56
- # Parses an error from the given response
57
- # @return [Error]
58
- def from_response(response)
59
- code = response.status
60
- message = parse_error(response.parsed)
61
-
62
- if code == 403 && FORBIDDEN_MESSAGES[message]
63
- FORBIDDEN_MESSAGES[message].new(message, response.headers, code)
64
- elsif code == 400 && response.headers['www-authenticate'] && response.headers['www-authenticate'].include?('Access Token expired')
65
- Error::AccessTokenExpired.new('Access Token expired', response.headers, code)
66
- else
67
- ERRORS[code].new(message, response.headers, code)
68
- end
69
- end
70
-
71
- private
72
-
73
- def parse_error(body)
74
- if body.nil? || body.empty?
75
- nil
76
- elsif body['error'] && body['error']['message']
77
- body['error']['message']
78
- end
79
- end
80
- end
81
-
82
- def initialize(message = '', rate_limit = {}, code = nil)
83
- super(message)
84
- @code = code
85
- @rate_limit = RateLimit.new(rate_limit)
86
- end
87
- end
88
- end
89
- end
@@ -1,148 +0,0 @@
1
- module Epo
2
- module Ops
3
- # The hierarchy is a flat Hash, that helps finding all known ipc subclasses
4
- # of a given class. It was parsed from the WIPO. It does not support all
5
- # levels, as it would (currently unnecessarily) blow up this hash. It only
6
- # finds the first two sub class levels, e.g. A45F.
7
- class IpcClassHierarchy
8
- Hierarchy = { 'A' => %w(A01 A21 A22 A23 A24 A41 A42 A43 A44 A45 A46 A47 A61 A62 A63 A99),
9
- 'A01' => %w(A01B A01C A01D A01F A01G A01H A01J A01K A01L A01M A01N A01P),
10
- 'A21' => %w(A21B A21C A21D),
11
- 'A22' => %w(A22B A22C),
12
- 'A23' => %w(A23B A23C A23D A23F A23G A23J A23K A23L A23N A23P),
13
- 'A24' => %w(A24B A24C A24D A24F),
14
- 'A41' => %w(A41B A41C A41D A41F A41G A41H),
15
- 'A42' => %w(A42B A42C),
16
- 'A43' => %w(A43B A43C A43D),
17
- 'A44' => %w(A44B A44C),
18
- 'A45' => %w(A45B A45C A45D A45F),
19
- 'A46' => %w(A46B A46D),
20
- 'A47' => %w(A47B A47C A47D A47F A47G A47H A47J A47K A47L),
21
- 'A61' => %w(A61B A61C A61D A61F A61G A61H A61J A61K A61L A61M A61N A61P A61Q),
22
- 'A62' => %w(A62B A62C A62D),
23
- 'A63' => %w(A63B A63C A63D A63F A63G A63H A63J A63K),
24
- 'A99' => ['A99Z'],
25
- 'B' => %w(B01 B02 B03 B04 B05 B06 B07 B08 B09 B21 B22 B23 B24 B25 B26 B27 B28 B29 B30 B31 B32 B33 B41 B42 B43 B44 B60 B61 B62 B63 B64 B65 B66 B67 B68 B81 B82 B99),
26
- 'B01' => %w(B01B B01D B01F B01J B01L),
27
- 'B02' => %w(B02B B02C),
28
- 'B03' => %w(B03B B03C B03D),
29
- 'B04' => %w(B04B B04C),
30
- 'B05' => %w(B05B B05C B05D),
31
- 'B06' => ['B06B'],
32
- 'B07' => %w(B07B B07C),
33
- 'B08' => ['B08B'],
34
- 'B09' => %w(B09B B09C),
35
- 'B21' => %w(B21B B21C B21D B21F B21G B21H B21J B21K B21L),
36
- 'B22' => %w(B22C B22D B22F),
37
- 'B23' => %w(B23B B23C B23D B23F B23G B23H B23K B23P B23Q),
38
- 'B24' => %w(B24B B24C B24D),
39
- 'B25' => %w(B25B B25C B25D B25F B25G B25H B25J),
40
- 'B26' => %w(B26B B26D B26F),
41
- 'B27' => %w(B27B B27C B27D B27F B27G B27H B27J B27K B27L B27M B27N),
42
- 'B28' => %w(B28B B28C B28D),
43
- 'B29' => %w(B29B B29C B29D B29K B29L),
44
- 'B30' => ['B30B'],
45
- 'B31' => %w(B31B B31C B31D B31F),
46
- 'B32' => ['B32B'],
47
- 'B33' => ['B33Y'],
48
- 'B41' => %w(B41B B41C B41D B41F B41G B41J B41K B41L B41M B41N),
49
- 'B42' => %w(B42B B42C B42D B42F),
50
- 'B43' => %w(B43K B43L B43M),
51
- 'B44' => %w(B44B B44C B44D B44F),
52
- 'B60' => %w(B60B B60C B60D B60F B60G B60H B60J B60K B60L B60M B60N B60P B60Q B60R B60S B60T B60V B60W),
53
- 'B61' => %w(B61B B61C B61D B61F B61G B61H B61J B61K B61L),
54
- 'B62' => %w(B62B B62C B62D B62H B62J B62K B62L B62M),
55
- 'B63' => %w(B63B B63C B63G B63H B63J),
56
- 'B64' => %w(B64B B64C B64D B64F B64G),
57
- 'B65' => %w(B65B B65C B65D B65F B65G B65H),
58
- 'B66' => %w(B66B B66C B66D B66F),
59
- 'B67' => %w(B67B B67C B67D),
60
- 'B68' => %w(B68B B68C B68F B68G),
61
- 'B81' => %w(B81B B81C),
62
- 'B82' => %w(B82B B82Y),
63
- 'B99' => ['B99Z'],
64
- 'C' => %w(C01 C02 C03 C04 C05 C06 C07 C08 C09 C10 C11 C12 C13 C14 C21 C22 C23 C25 C30 C40 C99),
65
- 'C01' => %w(C01B C01C C01D C01F C01G),
66
- 'C02' => ['C02F'],
67
- 'C03' => %w(C03B C03C),
68
- 'C04' => ['C04B'],
69
- 'C05' => %w(C05B C05C C05D C05F C05G),
70
- 'C06' => %w(C06B C06C C06D C06F),
71
- 'C07' => %w(C07B C07C C07D C07F C07G C07H C07J C07K),
72
- 'C08' => %w(C08B C08C C08F C08G C08H C08J C08K C08L),
73
- 'C09' => %w(C09B C09C C09D C09F C09G C09H C09J C09K),
74
- 'C10' => %w(C10B C10C C10F C10G C10H C10J C10K C10L C10M C10N),
75
- 'C11' => %w(C11B C11C C11D),
76
- 'C12' => %w(C12C C12F C12G C12H C12J C12L C12M C12N C12P C12Q C12R),
77
- 'C13' => %w(C13B C13K),
78
- 'C14' => %w(C14B C14C),
79
- 'C21' => %w(C21B C21C C21D),
80
- 'C22' => %w(C22B C22C C22F),
81
- 'C23' => %w(C23C C23D C23F C23G),
82
- 'C25' => %w(C25B C25C C25D C25F),
83
- 'C30' => ['C30B'],
84
- 'C40' => ['C40B'],
85
- 'C99' => ['C99Z'],
86
- 'D' => %w(D01 D02 D03 D04 D05 D06 D07 D21 D99),
87
- 'D01' => %w(D01B D01C D01D D01F D01G D01H),
88
- 'D02' => %w(D02G D02H D02J),
89
- 'D03' => %w(D03C D03D D03J),
90
- 'D04' => %w(D04B D04C D04D D04G D04H),
91
- 'D05' => %w(D05B D05C),
92
- 'D06' => %w(D06B D06C D06F D06G D06H D06J D06L D06M D06N D06P D06Q),
93
- 'D07' => ['D07B'],
94
- 'D21' => %w(D21B D21C D21D D21F D21G D21H D21J),
95
- 'D99' => ['D99Z'],
96
- 'E' => %w(E01 E02 E03 E04 E05 E06 E21 E99),
97
- 'E01' => %w(E01B E01C E01D E01F E01H),
98
- 'E02' => %w(E02B E02C E02D E02F),
99
- 'E03' => %w(E03B E03C E03D E03F),
100
- 'E04' => %w(E04B E04C E04D E04F E04G E04H),
101
- 'E05' => %w(E05B E05C E05D E05F E05G),
102
- 'E06' => %w(E06B E06C),
103
- 'E21' => %w(E21B E21C E21D E21F),
104
- 'E99' => ['E99Z'],
105
- 'F' => %w(F01 F02 F03 F04 F15 F16 F17 F21 F22 F23 F24 F25 F26 F27 F28 F41 F42 F99),
106
- 'F01' => %w(F01B F01C F01D F01K F01L F01M F01N F01P),
107
- 'F02' => %w(F02B F02C F02D F02F F02G F02K F02M F02N F02P),
108
- 'F03' => %w(F03B F03C F03D F03G F03H),
109
- 'F04' => %w(F04B F04C F04D F04F),
110
- 'F15' => %w(F15B F15C F15D),
111
- 'F16' => %w(F16B F16C F16D F16F F16G F16H F16J F16K F16L F16M F16N F16P F16S F16T),
112
- 'F17' => %w(F17B F17C F17D),
113
- 'F21' => %w(F21H F21K F21L F21S F21V F21W F21Y),
114
- 'F22' => %w(F22B F22D F22G),
115
- 'F23' => %w(F23B F23C F23D F23G F23H F23J F23K F23L F23M F23N F23Q F23R),
116
- 'F24' => %w(F24B F24C F24D F24F F24H F24J),
117
- 'F25' => %w(F25B F25C F25D F25J),
118
- 'F26' => ['F26B'],
119
- 'F27' => %w(F27B F27D),
120
- 'F28' => %w(F28B F28C F28D F28F F28G),
121
- 'F41' => %w(F41A F41B F41C F41F F41G F41H F41J),
122
- 'F42' => %w(F42B F42C F42D),
123
- 'F99' => ['F99Z'],
124
- 'G' => %w(G01 G02 G03 G04 G05 G06 G07 G08 G09 G10 G11 G12 G21 G99),
125
- 'G01' => %w(G01B G01C G01D G01F G01G G01H G01J G01K G01L G01M G01N G01P G01Q G01R G01S G01T G01V G01W),
126
- 'G02' => %w(G02B G02C G02F),
127
- 'G03' => %w(G03B G03C G03D G03F G03G G03H),
128
- 'G04' => %w(G04B G04C G04D G04F G04G G04R),
129
- 'G05' => %w(G05B G05D G05F G05G),
130
- 'G06' => %w(G06C G06D G06E G06F G06G G06J G06K G06M G06N G06Q G06T),
131
- 'G07' => %w(G07B G07C G07D G07F G07G),
132
- 'G08' => %w(G08B G08C G08G),
133
- 'G09' => %w(G09B G09C G09D G09F G09G),
134
- 'G10' => %w(G10B G10C G10D G10F G10G G10H G10K G10L),
135
- 'G11' => %w(G11B G11C),
136
- 'G12' => ['G12B'],
137
- 'G21' => %w(G21B G21C G21D G21F G21G G21H G21J G21K),
138
- 'G99' => ['G99Z'],
139
- 'H' => %w(H01 H02 H03 H04 H05 H99),
140
- 'H01' => %w(H01B H01C H01F H01G H01H H01J H01K H01L H01M H01P H01Q H01R H01S H01T),
141
- 'H02' => %w(H02B H02G H02H H02J H02K H02M H02N H02P H02S),
142
- 'H03' => %w(H03B H03C H03D H03F H03G H03H H03J H03K H03L H03M),
143
- 'H04' => %w(H04B H04H H04J H04K H04L H04M H04N H04Q H04R H04S H04W),
144
- 'H05' => %w(H05B H05C H05F H05G H05H H05K),
145
- 'H99' => ['H99Z'] }
146
- end
147
- end
148
- end