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