epo-ops 0.2.6 → 0.3.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.
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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 486c2b78a9d96a95206df411f2414bb58387f873
4
- data.tar.gz: f512f54574ac9b6a98905545e24e0917ae59f333
3
+ metadata.gz: 598720d94ba023a3a3e6b0e2b3f4b018a7f1983e
4
+ data.tar.gz: d6861675f8ef8abd67a13664c881406a9a8e711a
5
5
  SHA512:
6
- metadata.gz: ddca9237f9e562cd45901b8bbd4ae4b2c5b4f5fc460e6b3f4de7406415b8791ba0157e978d9b0dfb8b49803540ddd97c4042ae633058897594815570354736b9
7
- data.tar.gz: 19cfcb49c74da598b5e6e8e5290e83b09e025ad253fcdbe9425b8a461a2afbfefcabbafb11e1bde971fe67e915185d4c50e7b1ee8af4593876725b3b4d98f6ba
6
+ metadata.gz: 31e1b55be1850539337a550d831d1b63881b4248e8456dcbc1c6a4af1b32e6e289d92a76833434038f53dc97612bf214a1680388f955d7220bee2046e423b25c
7
+ data.tar.gz: 50f29328406d721d8515cc0436cad653d1cf2e0fdf6f725f372b950c12401968ddaed942b1d57483ab594778b04c26533d5c3f5a8c47ba7606bd6e7240339dfb
data/.gitignore CHANGED
@@ -35,3 +35,6 @@ Gemfile.lock
35
35
 
36
36
  # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
37
37
  .rvmrc
38
+
39
+ # IDEs
40
+ /.idea/
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - "2.0"
4
+ - "2.1"
5
+ - "2.2"
6
+ - "2.3.0"
data/README.md CHANGED
@@ -4,68 +4,108 @@
4
4
  # epo-ops
5
5
  Ruby interface to the EPO Open Patent Services (OPS).
6
6
 
7
- [Documentation can be found here](http://www.rubydoc.info/gems/epo-ops/)
7
+ [Full documentation can be found here](http://www.rubydoc.info/gems/epo-ops/)
8
+
8
9
 
9
- The EPO provides [playground](https://developers.epo.org/), where you can try
10
- out the methods. As well as [Documentation](https://www.epo.org/searching-for-patents/technical/espacenet/ops.html)
11
- of the different endpoints and detailed usage (see the 'Downloads' section).
12
10
 
13
11
  # Usage
14
12
 
15
- ## Authentication
16
- In order to use this gem you need to register at the [EPO for
17
- OAuth](https://developers.epo.org/user/register).
18
- Use your credentials by configuring
13
+ ## Quickstart
14
+ Simply install this gem and start a ruby console.
15
+ ```
16
+ $ gem install epo-ops
17
+ $ irb
18
+ ```
19
+
20
+ and start querying the API
19
21
 
20
22
  ```ruby
21
- Epo::Ops.configure do |conf|
23
+ require 'epo_ops'
24
+
25
+ patent_application = EpoOps::PatentApplication.find("EP14731659")
26
+ patent_application.title # "DEVICE AND METHOD FOR INTRODUCING FIBRES INTO AN EXTRUDER"
27
+ patent_application.classifications # ["B29C47/10", "B29C47/68", "B29C47/92", "B29C45/00", "B01D46/24"]
28
+ patent_application.applicants.first.name # "Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V."
29
+ ```
30
+
31
+ ## Advanced Usage
32
+
33
+ ### OAuth
34
+
35
+ EPO offers an anonymous developer access to their API with very little quota. To get extended quotas you can register
36
+ an account at the [EPO for OAuth](https://developers.epo.org/user/register)
37
+
38
+ After your account has been approved configure your credentials
39
+
40
+ ```ruby
41
+
42
+ EpoOps.configure do |conf|
43
+ conf.authentication = :oauth
22
44
  conf.consumer_key = "YOUR_KEY"
23
45
  conf.consumer_secret = "YOUR_SECRET"
24
46
  end
47
+
25
48
  ```
26
49
 
27
- ## Quickstart
28
- ### Search for Patents
50
+ The temporary access token is kept in memory for subsequent retrievals. To share this between several processes the
51
+ token storage strategy may be changed as shown in `epo/ops/token_store` for redis.
29
52
 
30
- Get references to all Patents on a given date and IPC-class:
31
53
 
32
- ```ruby
33
- Epo::Ops::Register.search("A", Date.new(2016,2 ,3))
34
- # or for all ipc classes
35
- Epo::Ops::Register.search(nil, Date.new(2016,2 ,3))
36
- ```
54
+ ### Querying and searching patent applications
37
55
 
38
- You can now retrieve the bibliographic entries of all these:
56
+ Currently this gem focuses mostly around patent applications.
57
+ It is possible to search for specific applications by application number and to search for applications using a
58
+ CQL search query
59
+
60
+ For example to find all applications in IPC Class _A01_ (and all subclasses) that were updated on 2016-01-01
39
61
 
40
62
  ```ruby
41
- references = Epo::Ops::Register.search(nil, Date.new(2016,2 ,3))
42
- references.map { |ref| Epo::Ops::Register.biblio(ref) }
63
+ query = EpoOps::SearchQueryBuilder.build("A01", Date.parse("2016-01-02"))
64
+ applications = EpoOps::PatentApplication.search(query)
65
+ puts applications.count # 1234
66
+ applications.map {|application| puts application.application_nr } # print all application numbers
67
+ applications.map {|application| application.fetch} # fetch complete bibliographic data for each document
68
+
43
69
  ```
44
- This will return an object that helps parsing the result. See the documentation
45
- for more information
46
70
 
47
- Note that both operations take a considerable amount of time. Also you may not
48
- want to develop and test with many of these requests, as they can quite quickly
49
- excess the API limits. Also note that this methods use the `application`
50
- endpoint.
71
+ The Request will return a search result containing the number of all applications matching the search. By default the
72
+ search will return 10 documents (max is 100). You can use `start_rang` and `end_range` of `EpoOps::SearchQueryBuilder.build`
73
+ to page through all results
74
+
75
+ **Note: EPO will always only return up to 2000 documents even if the search would return more**
51
76
 
52
- ## Custom Retrieval
53
77
 
54
- ### #raw_search
55
- This allows you to build your own CQL query, as
56
- described in the official documentation. With the second parameter set
57
- to true you can get the raw result as a nested Hash, if you want to
58
- parse it yourself.
78
+ ### Search for all Patents on a given Date
79
+
80
+ To circumvent the restriction of max 2000 search results this gem
81
+ offers a convenient method to search for all patents updated on a given date and/or a given IPC class
59
82
 
60
83
  ```ruby
61
- Epo::Ops::Register.raw_search("q=pd=20160203 and ic=D&Range=1-100", true)
84
+ EpoOps::Register.search("A", Date.new(2016,2 ,3))
85
+ # or for all ipc classes
86
+ EpoOps::Register.search(nil, Date.new(2016,2 ,3))
62
87
  ```
63
88
 
64
- ### #raw_biblio
65
- If you do not want to retrieve via the `application` endpoint (say you want
66
- `publication`) this method gives you more fine-grained control. Make sure the
67
- `reference_id` you use matches the type.
89
+ You can now retrieve the bibliographic entries of all these:
68
90
 
69
91
  ```ruby
70
- Epo::Ops::Register.raw_biblio('EP1000000', 'publication')
92
+ references = EpoOps::Register.search(nil, Date.new(2016,2 ,3))
93
+ references.map { |ref| EpoOps::Register.biblio(ref) }
71
94
  ```
95
+
96
+ **Note: Both operations take a considerable amount of time. Also you may not
97
+ want to develop and test with many of these requests, as they can quite quickly exceed your limits.**
98
+
99
+
100
+ # Further Reading
101
+
102
+ The EPO provides [a developer playground](https://developers.epo.org/), where you can test-drive the OPS-API.
103
+ They also provide extensive [documentation](https://www.epo.org/searching-for-patents/technical/espacenet/ops.html)
104
+ of the different endpoints and how to use them (see the 'Downloads' section).
105
+
106
+
107
+ # Development
108
+
109
+ This gem is still an early version and it is far from covering the whole API.
110
+ If you are interested to include different API endpoints than the register it should be easy to include those and we
111
+ are happy to accept pull requests.
@@ -1,11 +1,11 @@
1
1
  # coding: utf-8
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'epo/ops/version'
4
+ require 'epo_ops/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'epo-ops'
8
- spec.version = Epo::Ops::VERSION
8
+ spec.version = EpoOps::VERSION
9
9
  spec.authors = ['Max Kießling', 'Robert Terbach', 'Michael Prilop']
10
10
 
11
11
  spec.summary = 'Ruby interface to the European Patent Office API (OPS)'
@@ -0,0 +1,46 @@
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
+ require 'epo_ops/patent_application'
8
+ require 'epo_ops/name_and_address'
9
+ require 'epo_ops/factories'
10
+ require 'epo_ops/register_search_result'
11
+
12
+ module EpoOps
13
+ # Configure authentication method and credentials
14
+ # @example
15
+ # EpoOps.configure do |conf|
16
+ # conf.consumer_key = "foo"
17
+ # conf.consumer_secret = "bar"
18
+ # conf.token_store = EpoOps::TokenStore::Redis # (defaults to EpoOps::TokenStore)
19
+ # conf.authentication :oauth # or :plain (defaults to :plain)
20
+ # end
21
+ # @yieldparam [Configuration] configuration that is yielded.
22
+ def self.configure
23
+ yield(config)
24
+ end
25
+
26
+ # The {Configuration} used. You may want to call {EpoOps#configure} first.
27
+ # @return [Configuration] the configuration used.
28
+ def self.config
29
+ @configuration ||= Configuration.new
30
+ end
31
+
32
+ class Configuration
33
+ attr_accessor :consumer_key, :consumer_secret, :token_store, :authentication
34
+
35
+ def initialize
36
+ @consumer_key = ''
37
+ @consumer_secret = ''
38
+ @token_store = EpoOps::TokenStore.new
39
+ @authentication = :plain
40
+
41
+ OAuth2::Response.register_parser(:xml, ['application/xml']) do |body|
42
+ MultiXml.parse(body)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,46 @@
1
+ require 'epo_ops/token_store'
2
+ require 'epo_ops/error'
3
+
4
+ module EpoOps
5
+
6
+ # This is a wrapper for OAuth
7
+ class Client
8
+
9
+
10
+ # @return [OAuth2::Response]
11
+ # @option options [Hash] :params query parameter for the request
12
+ # @option options [Hash, String] :body the body of the request
13
+ # @option options [Hash] :headers http request headers
14
+ # @raise [EpoOps::Error] API Error if request was not successful
15
+ def self.request(verb, url, options = {})
16
+ response = case EpoOps.config.authentication
17
+ when :oauth then do_oauth_request(verb, url, options)
18
+ when :plain then do_plain_request(verb,url,options)
19
+ else raise('Unknown authentication strategy!')
20
+ end
21
+ fail Error.from_response(response) unless response.status == 200
22
+ response
23
+ rescue Error::AccessTokenExpired
24
+ EpoOps.config.token_store.reset
25
+ request(verb, url, options)
26
+ end
27
+
28
+ private
29
+
30
+ # Make an oauth request to the EPO API
31
+ # @return [OAuth2::Response]
32
+ def self.do_oauth_request(verb, url, options = {})
33
+ token = EpoOps.config.token_store.token
34
+ token.request(verb, URI.encode(url), options)
35
+ end
36
+
37
+ # Make an anonymous request to the EPO API
38
+ # @return [OAuth2::Response] OAuth2::Reponse is used for convenience and consistency
39
+ def self.do_plain_request(verb, url, options = {})
40
+ conn = Faraday.new("https://ops.epo.org/")
41
+ url = conn.build_url(url, options[:params]).to_s
42
+ response = conn.run_request(verb,url,options[:body], options[:header])
43
+ OAuth2::Response.new(response)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,87 @@
1
+ require 'epo_ops/rate_limit'
2
+
3
+ module EpoOps
4
+ class Error < StandardError
5
+ # @return [Integer]
6
+ attr_reader :code, :rate_limit
7
+
8
+ # Raised when EPO returns a 4xx HTTP status code
9
+ ClientError = Class.new(self)
10
+ # Raised when EPO returns the HTTP status code 400
11
+ BadRequest = Class.new(ClientError)
12
+ # Raised when EPO returns the HTTP status code 401
13
+ Unauthorized = Class.new(ClientError)
14
+ # Raised when EPO returns the HTTP status code 403
15
+ Forbidden = Class.new(ClientError)
16
+ # Raised when EPO returns the HTTP status code 404
17
+ NotFound = Class.new(ClientError)
18
+ # Raised when EPO returns the HTTP status code 406
19
+ NotAcceptable = Class.new(ClientError)
20
+ # Raised when EPO returns the HTTP status code 422
21
+ UnprocessableEntity = Class.new(ClientError)
22
+ # Raised when EPO returns the HTTP status code 429
23
+ TooManyRequests = Class.new(ClientError)
24
+ # Raised when EPO returns a 5xx HTTP status code
25
+ ServerError = Class.new(self)
26
+ # Raised when EPO returns the HTTP status code 500
27
+ InternalServerError = Class.new(ServerError)
28
+ # Raised when EPO returns the HTTP status code 502
29
+ BadGateway = Class.new(ServerError)
30
+ # Raised when EPO returns the HTTP status code 503
31
+ ServiceUnavailable = Class.new(ServerError)
32
+ # Raised when EPO returns the HTTP status code 504
33
+ GatewayTimeout = Class.new(ServerError)
34
+ # AccessToken has expired
35
+ AccessTokenExpired = Class.new(ClientError)
36
+
37
+ ERRORS = {
38
+ 400 => EpoOps::Error::BadRequest,
39
+ 401 => EpoOps::Error::Unauthorized,
40
+ 403 => EpoOps::Error::Forbidden,
41
+ 404 => EpoOps::Error::NotFound,
42
+ 406 => EpoOps::Error::NotAcceptable,
43
+ 422 => EpoOps::Error::UnprocessableEntity,
44
+ 429 => EpoOps::Error::TooManyRequests,
45
+ 500 => EpoOps::Error::InternalServerError,
46
+ 502 => EpoOps::Error::BadGateway,
47
+ 503 => EpoOps::Error::ServiceUnavailable,
48
+ 504 => EpoOps::Error::GatewayTimeout
49
+ }.freeze
50
+ FORBIDDEN_MESSAGES = {
51
+ 'This request has been rejected due to the violation of Fair Use policy' => EpoOps::Error::TooManyRequests
52
+ }.freeze
53
+
54
+ class << self
55
+ # Parses an error from the given response
56
+ # @return [Error]
57
+ def from_response(response)
58
+ code = response.status
59
+ message = parse_error(response.parsed)
60
+
61
+ if code == 403 && FORBIDDEN_MESSAGES[message]
62
+ FORBIDDEN_MESSAGES[message].new(message, response.headers, code)
63
+ elsif code == 400 && response.headers['www-authenticate'] && response.headers['www-authenticate'].include?('Access Token expired')
64
+ Error::AccessTokenExpired.new('Access Token expired', response.headers, code)
65
+ else
66
+ ERRORS[code].new(message, response.headers, code)
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def parse_error(body)
73
+ if body.nil? || body.empty?
74
+ nil
75
+ elsif body['error'] && body['error']['message']
76
+ body['error']['message']
77
+ end
78
+ end
79
+ end
80
+
81
+ def initialize(message = '', rate_limit = {}, code = nil)
82
+ super(message)
83
+ @code = code
84
+ @rate_limit = RateLimit.new(rate_limit)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,9 @@
1
+ require 'epo_ops/factories/patent_application_factory'
2
+ require 'epo_ops/factories/name_and_address_factory'
3
+ require 'epo_ops/factories/register_search_result_factory'
4
+
5
+ module EpoOps
6
+ module Factories
7
+
8
+ end
9
+ end
@@ -0,0 +1,54 @@
1
+ module EpoOps
2
+ module Factories
3
+ # Parses the addressbook data from EPO Ops into an 'NameAndAddress' object
4
+ class NameAndAddressFactory
5
+ class << self
6
+
7
+ # @param raw_data [Hash] raw addressbook data as retrieved included in Epo Ops biblio data
8
+ # @return [EpoOps::NameAndAddress] NameAndAddress filled with parsed data
9
+ def build(raw_data)
10
+ factory = new(raw_data)
11
+
12
+ EpoOps::NameAndAddress.new(
13
+ factory.name,
14
+ factory.address_1,
15
+ factory.address_2,
16
+ factory.address_3,
17
+ factory.address_4,
18
+ factory.address_5,
19
+ factory.country_code,
20
+ factory.cdsid
21
+ )
22
+ end
23
+ end
24
+
25
+ attr_reader :raw_data,
26
+ :name,
27
+ :address_1,
28
+ :address_2,
29
+ :address_3,
30
+ :address_4,
31
+ :address_5,
32
+ :country_code,
33
+ :cdsid
34
+
35
+
36
+ def initialize(raw_data)
37
+ @raw_data = raw_data
38
+
39
+ @name = raw_data['name']
40
+
41
+ if raw_data['address'].is_a? Hash
42
+ @address_1 = raw_data['address']['address_1']
43
+ @address_2 = raw_data['address']['address_2']
44
+ @address_3 = raw_data['address']['address_3']
45
+ @address_4 = raw_data['address']['address_4']
46
+ @address_5 = raw_data['address']['address_5']
47
+ @country_code = raw_data['address']['country']
48
+ end
49
+
50
+ @cdsid = raw_data['cdsid']
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,116 @@
1
+ module EpoOps
2
+ module Factories
3
+ # Parses the patent application data from EPO Ops into an PatentApplication object
4
+ class PatentApplicationFactory
5
+ class << self
6
+
7
+ # @param raw_data [Hash] raw application data as retrieved from Epo Ops
8
+ # @return [EpoOps::PatentApplication] PatentApplication filled with parsed data
9
+ def build(raw_data)
10
+ factory = new(raw_data)
11
+
12
+ EpoOps::PatentApplication.new(
13
+ factory.application_number,
14
+
15
+ raw_data: raw_data,
16
+ title: factory.title,
17
+ status: factory.status,
18
+ agents: factory.agents,
19
+ applicants: factory.applicants,
20
+ inventors: factory.inventors,
21
+ classifications: factory.classifications,
22
+ priority_claims: factory.priority_claims,
23
+ publication_references: factory.publication_references,
24
+ effective_date: factory.effective_date
25
+ )
26
+ end
27
+ end
28
+
29
+ attr_reader :raw_data
30
+
31
+ def initialize(raw_data)
32
+ @raw_data = raw_data
33
+ end
34
+
35
+ # @return [String] Application Number
36
+ # @see EpoOps::PatentApplication#application_nr
37
+ def application_number
38
+ document_id = EpoOps::Util.flat_dig raw_data, data_path('application_reference', 'document_id')
39
+ document_id = document_id.first if document_id.is_a?(Array)
40
+
41
+ "#{document_id['country']}#{document_id['doc_number']}"
42
+ end
43
+
44
+ # @return [Hash] List of titles in different languages
45
+ # @see EpoOps::PatentApplication#title
46
+ def title
47
+ titles = EpoOps::Util.flat_dig raw_data, data_path('invention_title')
48
+ titles.inject({}) do |hash, title|
49
+ hash[title['lang']] = title['__content__'] if title.is_a? Hash
50
+ hash
51
+ end
52
+ end
53
+
54
+ # @return [Array] List of {EpoOps::NameAndAddress}
55
+ # @see EpoOps::PatentApplication#agents
56
+ def agents
57
+ EpoOps::Util.flat_dig(raw_data, data_path('parties', 'agents', 'agent', 'addressbook')).map do |agent|
58
+ EpoOps::Factories::NameAndAddressFactory.build(agent)
59
+ end
60
+ end
61
+
62
+ # @return [Array] List of {EpoOps::NameAndAddress}
63
+ # @see EpoOps::PatentApplication#applicants
64
+ def applicants
65
+ EpoOps::Util.flat_dig(raw_data, data_path('parties', 'applicants', 'applicant', 'addressbook')).map do |applicant|
66
+ EpoOps::Factories::NameAndAddressFactory.build(applicant)
67
+ end
68
+ end
69
+
70
+ # @return [Array] List of {EpoOps::NameAndAddress}
71
+ # @see EpoOps::PatentApplication#inventors
72
+ def inventors
73
+ EpoOps::Util.flat_dig(raw_data, data_path('parties', 'inventors', 'inventor', 'addressbook')).map do |inventor|
74
+ EpoOps::Factories::NameAndAddressFactory.build(inventor)
75
+ end
76
+ end
77
+
78
+ # @return [Array] List of IPC classes
79
+ # @see EpoOps::PatentApplication#classifications
80
+ def classifications
81
+ EpoOps::Util.dig(raw_data, data_path('classifications_ipcr', 'classification_ipcr', 'text')).split(",").map(&:strip)
82
+ end
83
+
84
+ # @return [String] Application status as described by EPO
85
+ # @see EpoOps::PatentApplication#status
86
+ def status
87
+ EpoOps::Util.dig raw_data, data_path('status')
88
+ end
89
+
90
+ # @return [Array] Hashes of priority claims
91
+ # @see EpoOps::PatentApplication#priority_claims
92
+ def priority_claims
93
+ EpoOps::Util.flat_dig raw_data, data_path('priority_claims','priority_claim')
94
+ end
95
+
96
+ # @return [Array]
97
+ # @see EpoOps::PatentApplication#publication_references
98
+ def publication_references
99
+ EpoOps::Util.flat_dig raw_data, data_path('publication_reference', 'document_id')
100
+ end
101
+
102
+ # @return [Date]
103
+ # @see EpoOps::PatentApplication#effective_date
104
+ def effective_date
105
+ dates = EpoOps::Util.flat_dig raw_data, data_path('dates_rights_effective', 'request_for_examination')
106
+ dates.first.nil? ? nil : dates.first['date']
107
+ end
108
+
109
+ private
110
+ def data_path(*path)
111
+ %w(bibliographic_data) + path
112
+ end
113
+
114
+ end
115
+ end
116
+ end