rospatent 1.0.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +16 -0
- data/LICENSE.txt +21 -0
- data/README.md +1247 -0
- data/lib/generators/rospatent/install/install_generator.rb +21 -0
- data/lib/generators/rospatent/install/templates/README +29 -0
- data/lib/generators/rospatent/install/templates/initializer.rb +24 -0
- data/lib/rospatent/cache.rb +282 -0
- data/lib/rospatent/client.rb +698 -0
- data/lib/rospatent/configuration.rb +136 -0
- data/lib/rospatent/errors.rb +127 -0
- data/lib/rospatent/input_validator.rb +306 -0
- data/lib/rospatent/logger.rb +286 -0
- data/lib/rospatent/patent_parser.rb +141 -0
- data/lib/rospatent/railtie.rb +26 -0
- data/lib/rospatent/search.rb +222 -0
- data/lib/rospatent/version.rb +5 -0
- data/lib/rospatent.rb +117 -0
- metadata +167 -0
data/README.md
ADDED
@@ -0,0 +1,1247 @@
|
|
1
|
+
# Rospatent
|
2
|
+
|
3
|
+
A comprehensive Ruby client for the Rospatent patent search API with advanced features including intelligent caching, input validation, structured logging, and robust error handling.
|
4
|
+
|
5
|
+
> π·πΊ **[ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ Π½Π° ΡΡΡΡΠΊΠΎΠΌ ΡΠ·ΡΠΊΠ΅](#-Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ-Π½Π°-ΡΡΡΡΠΊΠΎΠΌ-ΡΠ·ΡΠΊΠ΅)** Π΄ΠΎΡΡΡΠΏΠ½Π° Π½ΠΈΠΆΠ΅
|
6
|
+
|
7
|
+
## β¨ Key Features
|
8
|
+
|
9
|
+
- π **Complete API Coverage** - Search, retrieve patents, media files, and datasets
|
10
|
+
- π‘οΈ **Robust Error Handling** - Comprehensive error types with detailed context
|
11
|
+
- β‘ **Intelligent Caching** - In-memory caching with TTL and LRU eviction
|
12
|
+
- β
**Input Validation** - Automatic parameter validation with helpful error messages
|
13
|
+
- π **Structured Logging** - JSON/text logging with request/response tracking
|
14
|
+
- π **Batch Operations** - Process multiple patents concurrently
|
15
|
+
- βοΈ **Environment-Aware** - Different configurations for dev/staging/production
|
16
|
+
- π§ͺ **Comprehensive Testing** - 96% test coverage with integration tests
|
17
|
+
- π **Excellent Documentation** - Detailed examples and API documentation
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
|
21
|
+
Add this line to your application's Gemfile:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
gem 'rospatent'
|
25
|
+
```
|
26
|
+
|
27
|
+
And then execute:
|
28
|
+
|
29
|
+
```bash
|
30
|
+
$ bundle install
|
31
|
+
```
|
32
|
+
|
33
|
+
Or install it yourself as:
|
34
|
+
|
35
|
+
```bash
|
36
|
+
$ gem install rospatent
|
37
|
+
```
|
38
|
+
|
39
|
+
## Quick Start
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
# Basic configuration
|
43
|
+
Rospatent.configure do |config|
|
44
|
+
config.token = "your_jwt_token"
|
45
|
+
end
|
46
|
+
|
47
|
+
# Create a client and search
|
48
|
+
client = Rospatent.client
|
49
|
+
results = client.search(q: "ΡΠ°ΠΊΠ΅ΡΠ°", limit: 10)
|
50
|
+
|
51
|
+
puts "Found #{results.total} results"
|
52
|
+
results.hits.each do |hit|
|
53
|
+
puts "Patent: #{hit['id']} - #{hit.dig('biblio', 'ru', 'title')}"
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
## Configuration
|
58
|
+
|
59
|
+
### Basic Configuration
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
Rospatent.configure do |config|
|
63
|
+
# Required
|
64
|
+
config.token = "your_jwt_token"
|
65
|
+
|
66
|
+
# API settings
|
67
|
+
config.api_url = "https://searchplatform.rospatent.gov.ru/patsearch/v0.2"
|
68
|
+
config.timeout = 30
|
69
|
+
config.retry_count = 3
|
70
|
+
|
71
|
+
# Environment (development, staging, production)
|
72
|
+
config.environment = "production"
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
### Advanced Configuration
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
Rospatent.configure do |config|
|
80
|
+
config.token = "your_jwt_token"
|
81
|
+
|
82
|
+
# Caching (enabled by default)
|
83
|
+
config.cache_enabled = true
|
84
|
+
config.cache_ttl = 300 # 5 minutes
|
85
|
+
config.cache_max_size = 1000 # Maximum cached items
|
86
|
+
|
87
|
+
# Logging
|
88
|
+
config.log_level = :info # :debug, :info, :warn, :error
|
89
|
+
config.log_requests = true # Log API requests
|
90
|
+
config.log_responses = true # Log API responses
|
91
|
+
|
92
|
+
# Connection settings
|
93
|
+
config.connection_pool_size = 5
|
94
|
+
config.connection_keep_alive = true
|
95
|
+
|
96
|
+
# Token management
|
97
|
+
config.token_expires_at = Time.now + 3600
|
98
|
+
config.token_refresh_callback = -> { refresh_token! }
|
99
|
+
end
|
100
|
+
```
|
101
|
+
|
102
|
+
### Environment Variables
|
103
|
+
|
104
|
+
Configure via environment variables:
|
105
|
+
|
106
|
+
```bash
|
107
|
+
ROSPATENT_ENV=production
|
108
|
+
ROSPATENT_CACHE_ENABLED=true
|
109
|
+
ROSPATENT_CACHE_TTL=600
|
110
|
+
ROSPATENT_LOG_LEVEL=info
|
111
|
+
ROSPATENT_POOL_SIZE=10
|
112
|
+
```
|
113
|
+
|
114
|
+
### Environment-Specific Defaults
|
115
|
+
|
116
|
+
The gem automatically adjusts settings based on environment:
|
117
|
+
|
118
|
+
- **Development**: Fast timeouts, verbose logging, short cache TTL
|
119
|
+
- **Staging**: Moderate settings for testing
|
120
|
+
- **Production**: Longer timeouts, optimized for reliability
|
121
|
+
|
122
|
+
## Basic Usage
|
123
|
+
|
124
|
+
### Searching Patents
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
client = Rospatent.client
|
128
|
+
|
129
|
+
# Simple text search
|
130
|
+
results = client.search(q: "ΡΠ°ΠΊΠ΅ΡΠ°")
|
131
|
+
|
132
|
+
# Natural language search
|
133
|
+
results = client.search(qn: "rocket engine design")
|
134
|
+
|
135
|
+
# Advanced search with all options
|
136
|
+
results = client.search(
|
137
|
+
q: "ΡΠ°ΠΊΠ΅ΡΠ° AND Π΄Π²ΠΈΠ³Π°ΡΠ΅Π»Ρ",
|
138
|
+
limit: 20,
|
139
|
+
offset: 0,
|
140
|
+
filter: {
|
141
|
+
"classification.ipc_group": { "values": ["F02K9"] },
|
142
|
+
"biblio.application_date": { "from": "2020-01-01" }
|
143
|
+
},
|
144
|
+
sort: :pub_date,
|
145
|
+
group_by: :patent_family,
|
146
|
+
include_facets: true,
|
147
|
+
highlight: true,
|
148
|
+
pre_tag: "<mark>",
|
149
|
+
post_tag: "</mark>"
|
150
|
+
)
|
151
|
+
|
152
|
+
# Process results
|
153
|
+
puts "Found #{results.total} total results (#{results.available} available)"
|
154
|
+
puts "Showing #{results.count} results"
|
155
|
+
|
156
|
+
results.hits.each do |hit|
|
157
|
+
puts "ID: #{hit['id']}"
|
158
|
+
puts "Title: #{hit.dig('biblio', 'ru', 'title')}"
|
159
|
+
puts "Date: #{hit.dig('biblio', 'publication_date')}"
|
160
|
+
puts "IPC: #{hit.dig('classification', 'ipc')}"
|
161
|
+
puts "---"
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
### Retrieving Patent Documents
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
# Get patent by document ID
|
169
|
+
patent_doc = client.patent("RU134694U1_20131120")
|
170
|
+
|
171
|
+
# Get patent by components
|
172
|
+
patent_doc = client.patent_by_components(
|
173
|
+
"RU", # country_code
|
174
|
+
"134694", # number
|
175
|
+
"U1", # doc_type
|
176
|
+
Date.new(2013, 11, 20) # date (String or Date object)
|
177
|
+
)
|
178
|
+
|
179
|
+
# Access patent data
|
180
|
+
title = patent_doc.dig('biblio', 'ru', 'title')
|
181
|
+
abstract = patent_doc.dig('abstract', 'ru')
|
182
|
+
inventors = patent_doc.dig('biblio', 'ru', 'inventor')
|
183
|
+
```
|
184
|
+
|
185
|
+
### Parsing Patent Content
|
186
|
+
|
187
|
+
Extract clean text or structured content from patents:
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
# Parse abstract
|
191
|
+
abstract_text = client.parse_abstract(patent_doc)
|
192
|
+
abstract_html = client.parse_abstract(patent_doc, format: :html)
|
193
|
+
abstract_en = client.parse_abstract(patent_doc, language: "en")
|
194
|
+
|
195
|
+
# Parse description
|
196
|
+
description_text = client.parse_description(patent_doc)
|
197
|
+
description_html = client.parse_description(patent_doc, format: :html)
|
198
|
+
|
199
|
+
# Get structured sections
|
200
|
+
sections = client.parse_description(patent_doc, format: :sections)
|
201
|
+
sections.each do |section|
|
202
|
+
puts "Section #{section[:number]}: #{section[:content]}"
|
203
|
+
end
|
204
|
+
```
|
205
|
+
|
206
|
+
### Finding Similar Patents
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
# Find similar patents by ID
|
210
|
+
similar = client.similar_patents_by_id("RU134694U1_20131120", count: 50)
|
211
|
+
|
212
|
+
# Find similar patents by text description
|
213
|
+
similar = client.similar_patents_by_text(
|
214
|
+
"Π Π°ΠΊΠ΅ΡΠ½ΡΠΉ Π΄Π²ΠΈΠ³Π°ΡΠ΅Π»Ρ Ρ ΡΠ»ΡΡΡΠ΅Π½Π½ΠΎΠΉ ΡΡΠ³ΠΎΠΉ",
|
215
|
+
count: 25
|
216
|
+
)
|
217
|
+
|
218
|
+
# Process similar patents
|
219
|
+
similar["hits"]&.each do |patent|
|
220
|
+
puts "Similar: #{patent['id']} (score: #{patent['_score']})"
|
221
|
+
end
|
222
|
+
```
|
223
|
+
|
224
|
+
### Classification Search
|
225
|
+
|
226
|
+
Search within patent classification systems (IPC and CPC) and get detailed information about classification codes:
|
227
|
+
|
228
|
+
```ruby
|
229
|
+
# Search for classification codes related to rockets in IPC
|
230
|
+
ipc_results = client.classification_search("ipc", query: "ΡΠ°ΠΊΠ΅ΡΠ°", lang: "ru")
|
231
|
+
puts "Found #{ipc_results['total']} IPC codes"
|
232
|
+
|
233
|
+
ipc_results["hits"]&.each do |hit|
|
234
|
+
puts "#{hit['code']}: #{hit['description']}"
|
235
|
+
end
|
236
|
+
|
237
|
+
# Search for rocket-related codes in CPC using English
|
238
|
+
cpc_results = client.classification_search("cpc", query: "rocket", lang: "en")
|
239
|
+
|
240
|
+
# Get detailed information about a specific classification code
|
241
|
+
code_info = client.classification_code("ipc", code: "F02K9/00", lang: "ru")
|
242
|
+
puts "Code: #{code_info['code']}"
|
243
|
+
puts "Description: #{code_info['description']}"
|
244
|
+
puts "Hierarchy: #{code_info['hierarchy']&.join(' β ')}"
|
245
|
+
|
246
|
+
# Get CPC code information in English
|
247
|
+
cpc_info = client.classification_code("cpc", code: "B63H11/00", lang: "en")
|
248
|
+
```
|
249
|
+
|
250
|
+
**Supported Classification Systems:**
|
251
|
+
- `"ipc"` - International Patent Classification (ΠΠΠ)
|
252
|
+
- `"cpc"` - Cooperative Patent Classification (Π‘ΠΠ)
|
253
|
+
|
254
|
+
**Supported Languages:**
|
255
|
+
- `"ru"` - Russian
|
256
|
+
- `"en"` - English
|
257
|
+
|
258
|
+
### Media and Documents
|
259
|
+
|
260
|
+
```ruby
|
261
|
+
# Download patent PDF
|
262
|
+
pdf_data = client.patent_media(
|
263
|
+
"National", # collection_id
|
264
|
+
"RU", # country_code
|
265
|
+
"U1", # doc_type
|
266
|
+
"2013/11/20", # pub_date
|
267
|
+
"134694", # pub_number
|
268
|
+
"document.pdf" # filename
|
269
|
+
)
|
270
|
+
File.write("patent.pdf", pdf_data)
|
271
|
+
|
272
|
+
# Simplified method using patent ID
|
273
|
+
pdf_data = client.patent_media_by_id(
|
274
|
+
"RU134694U1_20131120",
|
275
|
+
"National",
|
276
|
+
"document.pdf"
|
277
|
+
)
|
278
|
+
|
279
|
+
# Get available datasets
|
280
|
+
datasets = client.datasets_tree
|
281
|
+
datasets.each do |dataset|
|
282
|
+
puts "#{dataset['id']}: #{dataset['name']}"
|
283
|
+
end
|
284
|
+
```
|
285
|
+
|
286
|
+
## Advanced Features
|
287
|
+
|
288
|
+
### Batch Operations
|
289
|
+
|
290
|
+
Process multiple patents efficiently with concurrent requests:
|
291
|
+
|
292
|
+
```ruby
|
293
|
+
document_ids = ["RU134694U1_20131120", "RU2358138C1_20090610", "RU2756123C1_20210927"]
|
294
|
+
|
295
|
+
# Process patents in batches
|
296
|
+
client.batch_patents(document_ids, batch_size: 5) do |patent_doc|
|
297
|
+
if patent_doc[:error]
|
298
|
+
puts "Error for #{patent_doc[:document_id]}: #{patent_doc[:error]}"
|
299
|
+
else
|
300
|
+
puts "Retrieved patent: #{patent_doc['id']}"
|
301
|
+
# Process patent document
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
# Or collect all results
|
306
|
+
patents = []
|
307
|
+
client.batch_patents(document_ids) { |doc| patents << doc }
|
308
|
+
```
|
309
|
+
|
310
|
+
### Caching
|
311
|
+
|
312
|
+
Automatic intelligent caching improves performance:
|
313
|
+
|
314
|
+
```ruby
|
315
|
+
# Caching is automatic and transparent
|
316
|
+
patent1 = client.patent("RU134694U1_20131120") # API call
|
317
|
+
patent2 = client.patent("RU134694U1_20131120") # Cached result
|
318
|
+
|
319
|
+
# Check cache statistics
|
320
|
+
stats = client.statistics
|
321
|
+
puts "Cache hit rate: #{stats[:cache_stats][:hit_rate_percent]}%"
|
322
|
+
puts "Total requests: #{stats[:requests_made]}"
|
323
|
+
puts "Average response time: #{stats[:average_request_time]}s"
|
324
|
+
|
325
|
+
# Use shared cache across clients
|
326
|
+
shared_cache = Rospatent.shared_cache
|
327
|
+
client1 = Rospatent.client(cache: shared_cache)
|
328
|
+
client2 = Rospatent.client(cache: shared_cache)
|
329
|
+
|
330
|
+
# Manual cache management
|
331
|
+
shared_cache.clear # Clear all cached data
|
332
|
+
expired_count = shared_cache.cleanup_expired # Remove expired entries
|
333
|
+
cache_stats = shared_cache.statistics # Get detailed cache statistics
|
334
|
+
```
|
335
|
+
|
336
|
+
### Custom Logging
|
337
|
+
|
338
|
+
Configure detailed logging for monitoring and debugging:
|
339
|
+
|
340
|
+
```ruby
|
341
|
+
# Create custom logger
|
342
|
+
logger = Rospatent::Logger.new(
|
343
|
+
output: Rails.logger, # Or any IO object
|
344
|
+
level: :info,
|
345
|
+
formatter: :json # :json or :text
|
346
|
+
)
|
347
|
+
|
348
|
+
client = Rospatent.client(logger: logger)
|
349
|
+
|
350
|
+
# Logs include:
|
351
|
+
# - API requests/responses with timing
|
352
|
+
# - Cache operations (hits/misses)
|
353
|
+
# - Error details with context
|
354
|
+
# - Performance metrics
|
355
|
+
|
356
|
+
# Access shared logger
|
357
|
+
shared_logger = Rospatent.shared_logger(level: :debug)
|
358
|
+
```
|
359
|
+
|
360
|
+
### Error Handling
|
361
|
+
|
362
|
+
Comprehensive error handling with specific error types:
|
363
|
+
|
364
|
+
```ruby
|
365
|
+
begin
|
366
|
+
patent = client.patent("INVALID_ID")
|
367
|
+
rescue Rospatent::Errors::ValidationError => e
|
368
|
+
puts "Invalid input: #{e.message}"
|
369
|
+
puts "Field errors: #{e.errors}" if e.errors.any?
|
370
|
+
rescue Rospatent::Errors::NotFoundError => e
|
371
|
+
puts "Patent not found: #{e.message}"
|
372
|
+
rescue Rospatent::Errors::RateLimitError => e
|
373
|
+
puts "Rate limited. Retry after: #{e.retry_after} seconds"
|
374
|
+
rescue Rospatent::Errors::AuthenticationError => e
|
375
|
+
puts "Authentication failed: #{e.message}"
|
376
|
+
rescue Rospatent::Errors::ApiError => e
|
377
|
+
puts "API error (#{e.status_code}): #{e.message}"
|
378
|
+
puts "Request ID: #{e.request_id}" if e.request_id
|
379
|
+
retry if e.retryable?
|
380
|
+
rescue Rospatent::Errors::ConnectionError => e
|
381
|
+
puts "Connection error: #{e.message}"
|
382
|
+
puts "Original error: #{e.original_error}"
|
383
|
+
end
|
384
|
+
```
|
385
|
+
|
386
|
+
### Input Validation
|
387
|
+
|
388
|
+
All inputs are automatically validated with helpful error messages:
|
389
|
+
|
390
|
+
```ruby
|
391
|
+
# These will raise ValidationError with specific messages:
|
392
|
+
client.search(limit: 0) # "Limit must be at least 1"
|
393
|
+
client.patent("") # "Document_id cannot be empty"
|
394
|
+
client.similar_patents_by_text("", count: -1) # Multiple validation errors
|
395
|
+
|
396
|
+
# Validation includes:
|
397
|
+
# - Parameter types and formats
|
398
|
+
# - Patent ID format validation
|
399
|
+
# - Date format validation
|
400
|
+
# - Enum value validation
|
401
|
+
# - Required field validation
|
402
|
+
```
|
403
|
+
|
404
|
+
### Performance Monitoring
|
405
|
+
|
406
|
+
Track performance and usage statistics:
|
407
|
+
|
408
|
+
```ruby
|
409
|
+
# Client-specific statistics
|
410
|
+
stats = client.statistics
|
411
|
+
puts "Requests made: #{stats[:requests_made]}"
|
412
|
+
puts "Total duration: #{stats[:total_duration_seconds]}s"
|
413
|
+
puts "Average request time: #{stats[:average_request_time]}s"
|
414
|
+
puts "Cache hit rate: #{stats[:cache_stats][:hit_rate_percent]}%"
|
415
|
+
|
416
|
+
# Global statistics
|
417
|
+
global_stats = Rospatent.statistics
|
418
|
+
puts "Environment: #{global_stats[:configuration][:environment]}"
|
419
|
+
puts "Cache enabled: #{global_stats[:configuration][:cache_enabled]}"
|
420
|
+
puts "API URL: #{global_stats[:configuration][:api_url]}"
|
421
|
+
```
|
422
|
+
|
423
|
+
## Environment Configuration
|
424
|
+
|
425
|
+
### Development Environment
|
426
|
+
|
427
|
+
```ruby
|
428
|
+
# Optimized for development
|
429
|
+
Rospatent.configure do |config|
|
430
|
+
config.environment = "development"
|
431
|
+
config.token = ENV['ROSPATENT_DEV_TOKEN']
|
432
|
+
config.log_level = :debug
|
433
|
+
config.log_requests = true
|
434
|
+
config.log_responses = true
|
435
|
+
config.cache_ttl = 60 # Short cache for development
|
436
|
+
config.timeout = 10 # Fast timeouts for quick feedback
|
437
|
+
end
|
438
|
+
```
|
439
|
+
|
440
|
+
### Production Environment
|
441
|
+
|
442
|
+
```ruby
|
443
|
+
# Optimized for production
|
444
|
+
Rospatent.configure do |config|
|
445
|
+
config.environment = "production"
|
446
|
+
config.token = ENV['ROSPATENT_TOKEN']
|
447
|
+
config.log_level = :warn
|
448
|
+
config.cache_ttl = 600 # Longer cache for performance
|
449
|
+
config.timeout = 60 # Longer timeouts for reliability
|
450
|
+
config.retry_count = 5 # More retries for resilience
|
451
|
+
end
|
452
|
+
```
|
453
|
+
|
454
|
+
### Configuration Validation
|
455
|
+
|
456
|
+
```ruby
|
457
|
+
# Validate current configuration
|
458
|
+
errors = Rospatent.validate_configuration
|
459
|
+
if errors.any?
|
460
|
+
puts "Configuration errors:"
|
461
|
+
errors.each { |error| puts " - #{error}" }
|
462
|
+
else
|
463
|
+
puts "Configuration is valid β"
|
464
|
+
end
|
465
|
+
```
|
466
|
+
|
467
|
+
## Rails Integration
|
468
|
+
|
469
|
+
### Generator
|
470
|
+
|
471
|
+
```bash
|
472
|
+
$ rails generate rospatent:install
|
473
|
+
```
|
474
|
+
|
475
|
+
This creates `config/initializers/rospatent.rb`:
|
476
|
+
|
477
|
+
```ruby
|
478
|
+
Rospatent.configure do |config|
|
479
|
+
config.token = Rails.application.credentials.rospatent_token
|
480
|
+
config.environment = Rails.env
|
481
|
+
config.cache_enabled = Rails.env.production?
|
482
|
+
config.log_level = Rails.env.production? ? :warn : :debug
|
483
|
+
end
|
484
|
+
```
|
485
|
+
|
486
|
+
### Using with Rails Logger
|
487
|
+
|
488
|
+
```ruby
|
489
|
+
# In config/initializers/rospatent.rb
|
490
|
+
Rospatent.configure do |config|
|
491
|
+
config.token = Rails.application.credentials.rospatent_token
|
492
|
+
end
|
493
|
+
|
494
|
+
# Create client with Rails logger
|
495
|
+
logger = Rospatent::Logger.new(
|
496
|
+
output: Rails.logger,
|
497
|
+
level: Rails.env.production? ? :warn : :debug,
|
498
|
+
formatter: :text
|
499
|
+
)
|
500
|
+
|
501
|
+
# Use in controllers/services
|
502
|
+
class PatentService
|
503
|
+
def initialize
|
504
|
+
@client = Rospatent.client(logger: logger)
|
505
|
+
end
|
506
|
+
|
507
|
+
def search_patents(query)
|
508
|
+
@client.search(q: query, limit: 20)
|
509
|
+
rescue Rospatent::Errors::ApiError => e
|
510
|
+
Rails.logger.error "Patent search failed: #{e.message}"
|
511
|
+
raise
|
512
|
+
end
|
513
|
+
end
|
514
|
+
```
|
515
|
+
|
516
|
+
## Testing
|
517
|
+
|
518
|
+
### Running Tests
|
519
|
+
|
520
|
+
```bash
|
521
|
+
# Run all tests
|
522
|
+
$ bundle exec rake test
|
523
|
+
|
524
|
+
# Run specific test file
|
525
|
+
$ bundle exec ruby -Itest test/unit/client_test.rb
|
526
|
+
|
527
|
+
# Run integration tests (requires API token)
|
528
|
+
$ ROSPATENT_INTEGRATION_TESTS=true ROSPATENT_TEST_TOKEN=your_token bundle exec rake test_integration
|
529
|
+
|
530
|
+
# Run with coverage
|
531
|
+
$ bundle exec rake coverage
|
532
|
+
```
|
533
|
+
|
534
|
+
### Test Configuration
|
535
|
+
|
536
|
+
For testing, reset and configure in each test's setup method:
|
537
|
+
|
538
|
+
```ruby
|
539
|
+
# test/test_helper.rb - Base setup for unit tests
|
540
|
+
module Minitest
|
541
|
+
class Test
|
542
|
+
def setup
|
543
|
+
Rospatent.reset # Clean state between tests
|
544
|
+
Rospatent.configure do |config|
|
545
|
+
config.token = ENV.fetch("ROSPATENT_TEST_TOKEN", "test_token")
|
546
|
+
config.environment = "development"
|
547
|
+
config.cache_enabled = false # Disable cache for predictable tests
|
548
|
+
config.log_level = :error # Reduce test noise
|
549
|
+
end
|
550
|
+
end
|
551
|
+
end
|
552
|
+
end
|
553
|
+
|
554
|
+
# For integration tests - stable config, no reset needed
|
555
|
+
class IntegrationTest < Minitest::Test
|
556
|
+
def setup
|
557
|
+
skip unless ENV["ROSPATENT_INTEGRATION_TESTS"]
|
558
|
+
|
559
|
+
@token = ENV.fetch("ROSPATENT_TEST_TOKEN", nil)
|
560
|
+
skip "ROSPATENT_TEST_TOKEN not set" unless @token
|
561
|
+
|
562
|
+
# No reset needed - integration tests use consistent configuration
|
563
|
+
Rospatent.configure do |config|
|
564
|
+
config.token = @token
|
565
|
+
config.environment = "development"
|
566
|
+
config.cache_enabled = true
|
567
|
+
config.log_level = :debug
|
568
|
+
end
|
569
|
+
end
|
570
|
+
end
|
571
|
+
```
|
572
|
+
|
573
|
+
### Custom Assertions (Minitest)
|
574
|
+
|
575
|
+
```ruby
|
576
|
+
# test/test_helper.rb
|
577
|
+
module Minitest
|
578
|
+
class Test
|
579
|
+
def assert_valid_patent_id(patent_id, message = nil)
|
580
|
+
message ||= "Expected #{patent_id} to be a valid patent ID (format: XX12345Y1_YYYYMMDD)"
|
581
|
+
assert patent_id.match?(/^[A-Z]{2}[A-Z0-9]+[A-Z]\d*_\d{8}$/), message
|
582
|
+
end
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
# Usage in tests
|
587
|
+
def test_patent_id_validation
|
588
|
+
assert_valid_patent_id("RU134694U1_20131120")
|
589
|
+
assert_valid_patent_id("RU134694A_20131120")
|
590
|
+
end
|
591
|
+
```
|
592
|
+
|
593
|
+
## Known API Limitations
|
594
|
+
|
595
|
+
The library uses **Faraday** as the HTTP client with redirect support for all endpoints:
|
596
|
+
|
597
|
+
- **All endpoints** (`/search`, `/docs/{id}`, `/similar_search`, `/datasets/tree`, etc.) - β
Working perfectly with Faraday
|
598
|
+
- **Redirect handling**: Configured with `faraday-follow_redirects` middleware to handle server redirects automatically
|
599
|
+
|
600
|
+
β οΈ **Minor server-side limitation**:
|
601
|
+
- **Similar Patents by Text**: Occasionally returns `503 Service Unavailable` (server-side issue, not client implementation)
|
602
|
+
|
603
|
+
All core functionality works perfectly and is production-ready with a unified HTTP approach.
|
604
|
+
|
605
|
+
## Error Reference
|
606
|
+
|
607
|
+
### Error Hierarchy
|
608
|
+
|
609
|
+
```
|
610
|
+
Rospatent::Errors::Error (base)
|
611
|
+
βββ MissingTokenError
|
612
|
+
βββ ApiError
|
613
|
+
β βββ AuthenticationError (401)
|
614
|
+
β βββ NotFoundError (404)
|
615
|
+
β βββ RateLimitError (429)
|
616
|
+
β βββ ServiceUnavailableError (503)
|
617
|
+
βββ ConnectionError
|
618
|
+
β βββ TimeoutError
|
619
|
+
βββ InvalidRequestError
|
620
|
+
βββ ValidationError
|
621
|
+
```
|
622
|
+
|
623
|
+
### Common Error Scenarios
|
624
|
+
|
625
|
+
```ruby
|
626
|
+
# Missing or invalid token
|
627
|
+
Rospatent::Errors::MissingTokenError
|
628
|
+
Rospatent::Errors::AuthenticationError
|
629
|
+
|
630
|
+
# Invalid input parameters
|
631
|
+
Rospatent::Errors::ValidationError
|
632
|
+
|
633
|
+
# Resource not found
|
634
|
+
Rospatent::Errors::NotFoundError
|
635
|
+
|
636
|
+
# Rate limiting
|
637
|
+
Rospatent::Errors::RateLimitError # Check retry_after
|
638
|
+
|
639
|
+
# Network issues
|
640
|
+
Rospatent::Errors::ConnectionError
|
641
|
+
Rospatent::Errors::TimeoutError
|
642
|
+
|
643
|
+
# Server problems
|
644
|
+
Rospatent::Errors::ServiceUnavailableError
|
645
|
+
```
|
646
|
+
|
647
|
+
## Rake Tasks
|
648
|
+
|
649
|
+
Useful development and maintenance tasks:
|
650
|
+
|
651
|
+
```bash
|
652
|
+
# Validate configuration
|
653
|
+
$ bundle exec rake validate
|
654
|
+
|
655
|
+
# Cache management
|
656
|
+
$ bundle exec rake cache:stats
|
657
|
+
$ bundle exec rake cache:clear
|
658
|
+
|
659
|
+
# Generate documentation
|
660
|
+
$ bundle exec rake doc
|
661
|
+
|
662
|
+
# Run integration tests
|
663
|
+
$ bundle exec rake test_integration
|
664
|
+
|
665
|
+
# Performance benchmarks
|
666
|
+
$ bundle exec rake benchmark
|
667
|
+
|
668
|
+
# Setup development environment
|
669
|
+
$ bundle exec rake setup
|
670
|
+
|
671
|
+
# Pre-release checks
|
672
|
+
$ bundle exec rake release_check
|
673
|
+
```
|
674
|
+
|
675
|
+
## Performance Tips
|
676
|
+
|
677
|
+
1. **Use Caching**: Enable caching for repeated requests
|
678
|
+
2. **Batch Operations**: Use `batch_patents` for multiple documents
|
679
|
+
3. **Appropriate Limits**: Don't request more data than needed
|
680
|
+
4. **Connection Reuse**: Use the same client instance when possible
|
681
|
+
5. **Environment Configuration**: Use production settings in production
|
682
|
+
|
683
|
+
```ruby
|
684
|
+
# Good: Reuse client instance
|
685
|
+
client = Rospatent.client
|
686
|
+
patents = patent_ids.map { |id| client.patent(id) }
|
687
|
+
|
688
|
+
# Better: Use batch operations
|
689
|
+
patents = []
|
690
|
+
client.batch_patents(patent_ids) { |doc| patents << doc }
|
691
|
+
|
692
|
+
# Best: Use caching with shared instance
|
693
|
+
shared_client = Rospatent.client(cache: Rospatent.shared_cache)
|
694
|
+
```
|
695
|
+
|
696
|
+
## Troubleshooting
|
697
|
+
|
698
|
+
### Common Issues
|
699
|
+
|
700
|
+
**Authentication Errors**:
|
701
|
+
```ruby
|
702
|
+
# Check token validity
|
703
|
+
errors = Rospatent.validate_configuration
|
704
|
+
puts errors if errors.any?
|
705
|
+
```
|
706
|
+
|
707
|
+
**Network Timeouts**:
|
708
|
+
```ruby
|
709
|
+
# Increase timeout for slow connections
|
710
|
+
Rospatent.configure do |config|
|
711
|
+
config.timeout = 120
|
712
|
+
config.retry_count = 5
|
713
|
+
end
|
714
|
+
```
|
715
|
+
|
716
|
+
**Memory Usage**:
|
717
|
+
```ruby
|
718
|
+
# Limit cache size for memory-constrained environments
|
719
|
+
Rospatent.configure do |config|
|
720
|
+
config.cache_max_size = 100
|
721
|
+
config.cache_ttl = 300
|
722
|
+
end
|
723
|
+
```
|
724
|
+
|
725
|
+
**Debug API Calls**:
|
726
|
+
```ruby
|
727
|
+
# Enable detailed logging
|
728
|
+
Rospatent.configure do |config|
|
729
|
+
config.log_level = :debug
|
730
|
+
config.log_requests = true
|
731
|
+
config.log_responses = true
|
732
|
+
end
|
733
|
+
```
|
734
|
+
|
735
|
+
## Development
|
736
|
+
|
737
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests.
|
738
|
+
|
739
|
+
### Development Setup
|
740
|
+
|
741
|
+
```bash
|
742
|
+
$ git clone https://hub.mos.ru/ad/rospatent.git
|
743
|
+
$ cd rospatent
|
744
|
+
$ bundle install
|
745
|
+
$ bundle exec rake setup
|
746
|
+
```
|
747
|
+
|
748
|
+
### Running Tests
|
749
|
+
|
750
|
+
```bash
|
751
|
+
# Unit tests
|
752
|
+
$ bundle exec rake test
|
753
|
+
|
754
|
+
# Integration tests (requires API token)
|
755
|
+
$ ROSPATENT_INTEGRATION_TESTS=true ROSPATENT_TEST_TOKEN=your_token bundle exec rake test_integration
|
756
|
+
|
757
|
+
# Code style
|
758
|
+
$ bundle exec rubocop
|
759
|
+
|
760
|
+
# All checks
|
761
|
+
$ bundle exec rake ci
|
762
|
+
```
|
763
|
+
|
764
|
+
### Interactive Console
|
765
|
+
|
766
|
+
```bash
|
767
|
+
$ bin/console
|
768
|
+
```
|
769
|
+
|
770
|
+
## Contributing
|
771
|
+
|
772
|
+
Bug reports and pull requests are welcome on MosHub at https://hub.mos.ru/ad/rospatent.
|
773
|
+
|
774
|
+
### Development Guidelines
|
775
|
+
|
776
|
+
1. **Write Tests**: Ensure all new features have corresponding tests
|
777
|
+
2. **Follow Style**: Run `rubocop` and fix any style issues
|
778
|
+
3. **Document Changes**: Update README and CHANGELOG
|
779
|
+
4. **Validate Configuration**: Run `rake validate` before submitting
|
780
|
+
|
781
|
+
### Release Process
|
782
|
+
|
783
|
+
```bash
|
784
|
+
# Pre-release checks
|
785
|
+
$ bundle exec rake release_check
|
786
|
+
|
787
|
+
# Update version and release
|
788
|
+
$ bundle exec rake release
|
789
|
+
```
|
790
|
+
|
791
|
+
---
|
792
|
+
|
793
|
+
# π ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ Π½Π° ΡΡΡΡΠΊΠΎΠΌ ΡΠ·ΡΠΊΠ΅
|
794
|
+
|
795
|
+
## ΠΠΏΠΈΡΠ°Π½ΠΈΠ΅
|
796
|
+
|
797
|
+
**Rospatent** β ΡΡΠΎ ΠΊΠΎΠΌΠΏΠ»Π΅ΠΊΡΠ½ΡΠΉ Ruby-ΠΊΠ»ΠΈΠ΅Π½Ρ Π΄Π»Ρ Π²Π·Π°ΠΈΠΌΠΎΠ΄Π΅ΠΉΡΡΠ²ΠΈΡ Ρ API ΠΏΠΎΠΈΡΠΊΠ° ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ² Π ΠΎΡΠΏΠ°ΡΠ΅Π½ΡΠ°. ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²Π»ΡΠ΅Ρ ΡΠ΄ΠΎΠ±Π½ΡΠΉ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ Π΄Π»Ρ ΠΏΠΎΠΈΡΠΊΠ°, ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ ΠΈ Π°Π½Π°Π»ΠΈΠ·Π° ΠΏΠ°ΡΠ΅Π½ΡΠ½ΠΎΠΉ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ Ρ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠΌ ΠΊΠ΅ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ, Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΠ΅ΠΉ Π·Π°ΠΏΡΠΎΡΠΎΠ² ΠΈ ΠΏΠΎΠ΄ΡΠΎΠ±Π½ΡΠΌ Π»ΠΎΠ³ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ.
|
798
|
+
|
799
|
+
## β¨ ΠΠ»ΡΡΠ΅Π²ΡΠ΅ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠΈ
|
800
|
+
|
801
|
+
- π **ΠΠΎΠ»Π½ΠΎΠ΅ ΠΏΠΎΠΊΡΡΡΠΈΠ΅ API** - ΠΏΠΎΠΈΡΠΊ, ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ², ΠΌΠ΅Π΄ΠΈΠ°ΡΠ°ΠΉΠ»Ρ ΠΈ Π΄Π°ΡΠ°ΡΠ΅ΡΡ
|
802
|
+
- π‘οΈ **ΠΠ°Π΄Π΅ΠΆΠ½Π°Ρ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΎΡΠΈΠ±ΠΎΠΊ** - ΠΊΠΎΠΌΠΏΠ»Π΅ΠΊΡΠ½ΡΠ΅ ΡΠΈΠΏΡ ΠΎΡΠΈΠ±ΠΎΠΊ Ρ Π΄Π΅ΡΠ°Π»ΡΠ½ΡΠΌ ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡΠΎΠΌ
|
803
|
+
- β‘ **ΠΠ½ΡΠ΅Π»Π»Π΅ΠΊΡΡΠ°Π»ΡΠ½ΠΎΠ΅ ΠΊΠ΅ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅** - ΠΊΠ΅ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π² ΠΏΠ°ΠΌΡΡΠΈ Ρ TTL ΠΈ LRU ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ΠΌ
|
804
|
+
- β
**ΠΠ°Π»ΠΈΠ΄Π°ΡΠΈΡ Π²Ρ
ΠΎΠ΄Π½ΡΡ
Π΄Π°Π½Π½ΡΡ
** - Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠ°Ρ Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΡ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΎΠ² Ρ ΠΏΠΎΠ»Π΅Π·Π½ΡΠΌΠΈ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡΠΌΠΈ
|
805
|
+
- π **Π‘ΡΡΡΠΊΡΡΡΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠ΅ Π»ΠΎΠ³ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅** - JSON/ΡΠ΅ΠΊΡΡΠΎΠ²ΠΎΠ΅ Π»ΠΎΠ³ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Ρ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΠ΅ΠΌ Π·Π°ΠΏΡΠΎΡΠΎΠ²/ΠΎΡΠ²Π΅ΡΠΎΠ²
|
806
|
+
- π **ΠΠ°ΠΊΠ΅ΡΠ½ΡΠ΅ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ** - ΠΏΠ°ΡΠ°Π»Π»Π΅Π»ΡΠ½Π°Ρ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΌΠ½ΠΎΠΆΠ΅ΡΡΠ²Π° ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ²
|
807
|
+
- βοΈ **ΠΠ΄Π°ΠΏΡΠΈΠ²Π½ΡΠ΅ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ** - ΡΠ°Π·Π»ΠΈΡΠ½ΡΠ΅ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ Π΄Π»Ρ development/staging/production
|
808
|
+
- π§ͺ **ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡΠ½ΠΎΠ΅ ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅** - 96% ΠΏΠΎΠΊΡΡΡΠΈΠ΅ ΡΠ΅ΡΡΠ°ΠΌΠΈ Ρ ΠΈΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΠΎΠ½Π½ΡΠΌΠΈ ΡΠ΅ΡΡΠ°ΠΌΠΈ
|
809
|
+
- π **ΠΡΠ»ΠΈΡΠ½Π°Ρ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ** - ΠΏΠΎΠ΄ΡΠΎΠ±Π½ΡΠ΅ ΠΏΡΠΈΠΌΠ΅ΡΡ ΠΈ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ API
|
810
|
+
|
811
|
+
## ΠΡΡΡΡΡΠΉ ΡΡΠ°ΡΡ
|
812
|
+
|
813
|
+
### Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ°
|
814
|
+
|
815
|
+
ΠΠΎΠ±Π°Π²ΡΡΠ΅ Π² Π²Π°Ρ Gemfile:
|
816
|
+
|
817
|
+
```ruby
|
818
|
+
gem 'rospatent'
|
819
|
+
```
|
820
|
+
|
821
|
+
ΠΠ»ΠΈ ΡΡΡΠ°Π½ΠΎΠ²ΠΈΡΠ΅ Π½Π°ΠΏΡΡΠΌΡΡ:
|
822
|
+
|
823
|
+
```bash
|
824
|
+
$ gem install rospatent
|
825
|
+
```
|
826
|
+
|
827
|
+
### ΠΠ°Π·ΠΎΠ²Π°Ρ Π½Π°ΡΡΡΠΎΠΉΠΊΠ°
|
828
|
+
|
829
|
+
```ruby
|
830
|
+
require 'rospatent'
|
831
|
+
|
832
|
+
# ΠΠ°ΡΡΡΠΎΠΉΠΊΠ° ΠΊΠ»ΠΈΠ΅Π½ΡΠ°
|
833
|
+
Rospatent.configure do |config|
|
834
|
+
config.token = "Π²Π°Ρ_jwt_ΡΠΎΠΊΠ΅Π½"
|
835
|
+
end
|
836
|
+
|
837
|
+
# Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΊΠ»ΠΈΠ΅Π½ΡΠ°
|
838
|
+
client = Rospatent.client
|
839
|
+
```
|
840
|
+
|
841
|
+
## ΠΡΠ½ΠΎΠ²Π½ΠΎΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅
|
842
|
+
|
843
|
+
### ΠΠΎΠΈΡΠΊ ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ²
|
844
|
+
|
845
|
+
```ruby
|
846
|
+
# ΠΡΠΎΡΡΠΎΠΉ ΠΏΠΎΠΈΡΠΊ
|
847
|
+
results = client.search(q: "ΡΠΎΠ»Π½Π΅ΡΠ½Π°Ρ Π±Π°ΡΠ°ΡΠ΅Ρ")
|
848
|
+
|
849
|
+
# Π Π°ΡΡΠΈΡΠ΅Π½Π½ΡΠΉ ΠΏΠΎΠΈΡΠΊ Ρ ΡΠΈΠ»ΡΡΡΠ°ΠΌΠΈ
|
850
|
+
results = client.search(
|
851
|
+
q: "ΠΈΡΠΊΡΡΡΡΠ²Π΅Π½Π½ΡΠΉ ΠΈΠ½ΡΠ΅Π»Π»Π΅ΠΊΡ",
|
852
|
+
limit: 50,
|
853
|
+
offset: 100,
|
854
|
+
datasets: ["ru_since_1994"],
|
855
|
+
sort: "pub_date:desc",
|
856
|
+
highlight: true
|
857
|
+
)
|
858
|
+
|
859
|
+
# ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠ²
|
860
|
+
puts "ΠΠ°ΠΉΠ΄Π΅Π½ΠΎ: #{results.total} ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ²"
|
861
|
+
results.hits.each do |patent|
|
862
|
+
puts "#{patent['id']}: #{patent['title']}"
|
863
|
+
end
|
864
|
+
```
|
865
|
+
|
866
|
+
### ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠΎΠ² ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ²
|
867
|
+
|
868
|
+
```ruby
|
869
|
+
# ΠΠΎ ΠΈΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡΡ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°
|
870
|
+
patent = client.patent("RU134694U1_20131120")
|
871
|
+
|
872
|
+
# ΠΠ°ΡΡΠΈΠ½Π³ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ³ΠΎ
|
873
|
+
abstract = client.parse_abstract(patent)
|
874
|
+
description = client.parse_description(patent, format: :text)
|
875
|
+
|
876
|
+
puts "Π Π΅ΡΠ΅ΡΠ°Ρ: #{abstract}"
|
877
|
+
puts "ΠΠΏΠΈΡΠ°Π½ΠΈΠ΅: #{description}"
|
878
|
+
```
|
879
|
+
|
880
|
+
### ΠΠΎΠΈΡΠΊ ΠΏΠΎΡ
ΠΎΠΆΠΈΡ
ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ²
|
881
|
+
|
882
|
+
```ruby
|
883
|
+
# ΠΠΎΠΈΡΠΊ ΠΏΠΎΡ
ΠΎΠΆΠΈΡ
ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ² ΠΏΠΎ ID
|
884
|
+
similar = client.similar_patents_by_id("RU134694U1_20131120", count: 50)
|
885
|
+
|
886
|
+
# ΠΠΎΠΈΡΠΊ ΠΏΠΎΡ
ΠΎΠΆΠΈΡ
ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ² ΠΏΠΎ ΠΎΠΏΠΈΡΠ°Π½ΠΈΡ ΡΠ΅ΠΊΡΡΠ°
|
887
|
+
similar = client.similar_patents_by_text(
|
888
|
+
"Π Π°ΠΊΠ΅ΡΠ½ΡΠΉ Π΄Π²ΠΈΠ³Π°ΡΠ΅Π»Ρ Ρ ΡΠ»ΡΡΡΠ΅Π½Π½ΠΎΠΉ ΡΡΠ³ΠΎΠΉ",
|
889
|
+
count: 25
|
890
|
+
)
|
891
|
+
|
892
|
+
# ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΏΠΎΡ
ΠΎΠΆΠΈΡ
ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ²
|
893
|
+
similar["hits"]&.each do |patent|
|
894
|
+
puts "ΠΠΎΡ
ΠΎΠΆΠΈΠΉ: #{patent['id']} (ΠΎΡΠ΅Π½ΠΊΠ°: #{patent['_score']})"
|
895
|
+
end
|
896
|
+
```
|
897
|
+
|
898
|
+
### ΠΠΎΠΈΡΠΊ ΠΏΠΎ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΎΡΠ°ΠΌ
|
899
|
+
|
900
|
+
ΠΠΎΠΈΡΠΊ Π² ΡΠΈΡΡΠ΅ΠΌΠ°Ρ
ΠΏΠ°ΡΠ΅Π½ΡΠ½ΠΎΠΉ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΈ (IPC ΠΈ CPC) ΠΈ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΏΠΎΠ΄ΡΠΎΠ±Π½ΠΎΠΉ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ ΠΎ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΎΠ½Π½ΡΡ
ΠΊΠΎΠ΄Π°Ρ
:
|
901
|
+
|
902
|
+
```ruby
|
903
|
+
# ΠΠΎΠΈΡΠΊ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΎΠ½Π½ΡΡ
ΠΊΠΎΠ΄ΠΎΠ², ΡΠ²ΡΠ·Π°Π½Π½ΡΡ
Ρ ΡΠ°ΠΊΠ΅ΡΠ°ΠΌΠΈ Π² IPC
|
904
|
+
ipc_results = client.classification_search("ipc", query: "ΡΠ°ΠΊΠ΅ΡΠ°", lang: "ru")
|
905
|
+
puts "ΠΠ°ΠΉΠ΄Π΅Π½ΠΎ #{ipc_results['total']} ΠΊΠΎΠ΄ΠΎΠ² IPC"
|
906
|
+
|
907
|
+
ipc_results["hits"]&.each do |hit|
|
908
|
+
puts "#{hit['code']}: #{hit['description']}"
|
909
|
+
end
|
910
|
+
|
911
|
+
# ΠΠΎΠΈΡΠΊ ΠΊΠΎΠ΄ΠΎΠ², ΡΠ²ΡΠ·Π°Π½Π½ΡΡ
Ρ ΡΠ°ΠΊΠ΅ΡΠ°ΠΌΠΈ Π² CPC Π½Π° Π°Π½Π³Π»ΠΈΠΉΡΠΊΠΎΠΌ
|
912
|
+
cpc_results = client.classification_search("cpc", query: "rocket", lang: "en")
|
913
|
+
|
914
|
+
# ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΏΠΎΠ΄ΡΠΎΠ±Π½ΠΎΠΉ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ ΠΎ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΠΎΠΌ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΎΠ½Π½ΠΎΠΌ ΠΊΠΎΠ΄Π΅
|
915
|
+
code_info = client.classification_code("ipc", code: "F02K9/00", lang: "ru")
|
916
|
+
puts "ΠΠΎΠ΄: #{code_info['code']}"
|
917
|
+
puts "ΠΠΏΠΈΡΠ°Π½ΠΈΠ΅: #{code_info['description']}"
|
918
|
+
puts "ΠΠ΅ΡΠ°ΡΡ
ΠΈΡ: #{code_info['hierarchy']&.join(' β ')}"
|
919
|
+
|
920
|
+
# ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ ΠΎ ΠΊΠΎΠ΄Π΅ CPC Π½Π° Π°Π½Π³Π»ΠΈΠΉΡΠΊΠΎΠΌ
|
921
|
+
cpc_info = client.classification_code("cpc", code: "B63H11/00", lang: "en")
|
922
|
+
```
|
923
|
+
|
924
|
+
**ΠΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅ΠΌΡΠ΅ ΡΠΈΡΡΠ΅ΠΌΡ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΈ:**
|
925
|
+
- `"ipc"` - ΠΠ΅ΠΆΠ΄ΡΠ½Π°ΡΠΎΠ΄Π½Π°Ρ ΠΏΠ°ΡΠ΅Π½ΡΠ½Π°Ρ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΈΡ (ΠΠΠ)
|
926
|
+
- `"cpc"` - Π‘ΠΎΠ²ΠΌΠ΅ΡΡΠ½Π°Ρ ΠΏΠ°ΡΠ΅Π½ΡΠ½Π°Ρ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΈΡ (Π‘ΠΠ)
|
927
|
+
|
928
|
+
**ΠΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅ΠΌΡΠ΅ ΡΠ·ΡΠΊΠΈ:**
|
929
|
+
- `"ru"` - Π ΡΡΡΠΊΠΈΠΉ
|
930
|
+
- `"en"` - ΠΠ½Π³Π»ΠΈΠΉΡΠΊΠΈΠΉ
|
931
|
+
|
932
|
+
### ΠΠ΅Π΄ΠΈΠ°ΡΠ°ΠΉΠ»Ρ ΠΈ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΡ
|
933
|
+
|
934
|
+
```ruby
|
935
|
+
# Π‘ΠΊΠ°ΡΠΈΠ²Π°Π½ΠΈΠ΅ PDF ΠΏΠ°ΡΠ΅Π½ΡΠ°
|
936
|
+
pdf_data = client.patent_media(
|
937
|
+
"National", # collection_id
|
938
|
+
"RU", # country_code
|
939
|
+
"U1", # doc_type
|
940
|
+
"2013/11/20", # pub_date
|
941
|
+
"134694", # pub_number
|
942
|
+
"document.pdf" # filename
|
943
|
+
)
|
944
|
+
File.write("patent.pdf", pdf_data)
|
945
|
+
|
946
|
+
# Π£ΠΏΡΠΎΡΠ΅Π½Π½ΡΠΉ ΠΌΠ΅ΡΠΎΠ΄ Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ ID ΠΏΠ°ΡΠ΅Π½ΡΠ°
|
947
|
+
pdf_data = client.patent_media_by_id(
|
948
|
+
"RU134694U1_20131120",
|
949
|
+
"National",
|
950
|
+
"document.pdf"
|
951
|
+
)
|
952
|
+
|
953
|
+
# ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π΄ΠΎΡΡΡΠΏΠ½ΡΡ
Π΄Π°ΡΠ°ΡΠ΅ΡΠΎΠ²
|
954
|
+
datasets = client.datasets_tree
|
955
|
+
datasets.each do |dataset|
|
956
|
+
puts "#{dataset['id']}: #{dataset['name']}"
|
957
|
+
end
|
958
|
+
```
|
959
|
+
|
960
|
+
## Π Π°ΡΡΠΈΡΠ΅Π½Π½ΡΠ΅ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠΈ
|
961
|
+
|
962
|
+
### ΠΠ°ΠΊΠ΅ΡΠ½ΡΠ΅ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ
|
963
|
+
|
964
|
+
```ruby
|
965
|
+
patent_ids = ["RU134694U1_20131120", "RU2358138C1_20090610"]
|
966
|
+
|
967
|
+
client.batch_patents(patent_ids) do |patent|
|
968
|
+
puts "ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ°: #{patent['id']}"
|
969
|
+
# ΠΠ°ΡΠ° Π»ΠΎΠ³ΠΈΠΊΠ° ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ
|
970
|
+
end
|
971
|
+
```
|
972
|
+
|
973
|
+
### ΠΠ΅ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅
|
974
|
+
|
975
|
+
```ruby
|
976
|
+
# ΠΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ ΠΊΠ΅ΡΠ°
|
977
|
+
Rospatent.configure do |config|
|
978
|
+
config.cache_enabled = true
|
979
|
+
config.cache_ttl = 600 # 10 ΠΌΠΈΠ½ΡΡ
|
980
|
+
config.cache_max_size = 1000 # ΠΠ°ΠΊΡΠΈΠΌΡΠΌ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠΎΠ²
|
981
|
+
end
|
982
|
+
|
983
|
+
# Π‘ΡΠ°ΡΠΈΡΡΠΈΠΊΠ° ΠΊΠ΅ΡΠ°
|
984
|
+
stats = client.statistics
|
985
|
+
puts "ΠΠΎΠΏΠ°Π΄Π°Π½ΠΈΠΉ Π² ΠΊΠ΅Ρ: #{stats[:cache_stats][:hits]}"
|
986
|
+
```
|
987
|
+
|
988
|
+
### ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΎΡΠΈΠ±ΠΎΠΊ
|
989
|
+
|
990
|
+
```ruby
|
991
|
+
begin
|
992
|
+
results = client.search(q: "ΠΏΠΎΠΈΡΠΊΠΎΠ²ΡΠΉ Π·Π°ΠΏΡΠΎΡ")
|
993
|
+
rescue Rospatent::Errors::AuthenticationError => e
|
994
|
+
puts "ΠΡΠΈΠ±ΠΊΠ° Π°ΡΡΠ΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΈ: #{e.message}"
|
995
|
+
rescue Rospatent::Errors::RateLimitError => e
|
996
|
+
puts "ΠΡΠ΅Π²ΡΡΠ΅Π½ Π»ΠΈΠΌΠΈΡ Π·Π°ΠΏΡΠΎΡΠΎΠ²: #{e.message}"
|
997
|
+
rescue Rospatent::Errors::ApiError => e
|
998
|
+
puts "ΠΡΠΈΠ±ΠΊΠ° API: #{e.message}"
|
999
|
+
end
|
1000
|
+
```
|
1001
|
+
|
1002
|
+
## ΠΠ°ΡΡΡΠΎΠΉΠΊΠ° ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ
|
1003
|
+
|
1004
|
+
### Π Π°Π·ΡΠ°Π±ΠΎΡΠΊΠ°
|
1005
|
+
|
1006
|
+
```ruby
|
1007
|
+
Rospatent.configure do |config|
|
1008
|
+
config.environment = "development"
|
1009
|
+
config.token = ENV['ROSPATENT_DEV_TOKEN']
|
1010
|
+
config.log_level = :debug
|
1011
|
+
config.log_requests = true
|
1012
|
+
config.cache_ttl = 60
|
1013
|
+
end
|
1014
|
+
```
|
1015
|
+
|
1016
|
+
### ΠΡΠΎΠ΄Π°ΠΊΡΠ½
|
1017
|
+
|
1018
|
+
```ruby
|
1019
|
+
Rospatent.configure do |config|
|
1020
|
+
config.environment = "production"
|
1021
|
+
config.token = ENV['ROSPATENT_TOKEN']
|
1022
|
+
config.log_level = :warn
|
1023
|
+
config.cache_ttl = 600
|
1024
|
+
config.timeout = 60
|
1025
|
+
config.retry_count = 5
|
1026
|
+
end
|
1027
|
+
```
|
1028
|
+
|
1029
|
+
## ΠΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΡ Ρ Rails
|
1030
|
+
|
1031
|
+
```ruby
|
1032
|
+
# config/initializers/rospatent.rb
|
1033
|
+
Rospatent.configure do |config|
|
1034
|
+
config.token = Rails.application.credentials.rospatent_token
|
1035
|
+
config.environment = Rails.env
|
1036
|
+
config.cache_enabled = Rails.env.production?
|
1037
|
+
config.log_level = Rails.env.production? ? :warn : :debug
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
# Π ΠΊΠΎΠ½ΡΡΠΎΠ»Π»Π΅ΡΠ΅ ΠΈΠ»ΠΈ ΡΠ΅ΡΠ²ΠΈΡΠ΅
|
1041
|
+
class PatentService
|
1042
|
+
def initialize
|
1043
|
+
@client = Rospatent.client
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
def search_patents(query, **options)
|
1047
|
+
@client.search(q: query, **options)
|
1048
|
+
end
|
1049
|
+
end
|
1050
|
+
```
|
1051
|
+
|
1052
|
+
## ΠΠ·Π²Π΅ΡΡΠ½ΡΠ΅ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΡ API
|
1053
|
+
|
1054
|
+
ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ **Faraday** Π² ΠΊΠ°ΡΠ΅ΡΡΠ²Π΅ HTTP-ΠΊΠ»ΠΈΠ΅Π½ΡΠ° Ρ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠΎΠΉ ΡΠ΅Π΄ΠΈΡΠ΅ΠΊΡΠΎΠ² Π΄Π»Ρ Π²ΡΠ΅Ρ
endpoints:
|
1055
|
+
|
1056
|
+
- **ΠΡΠ΅ endpoints** (`/search`, `/docs/{id}`, `/similar_search`, `/datasets/tree`, ΠΈ Ρ.Π΄.) - β
Π Π°Π±ΠΎΡΠ°ΡΡ ΠΈΠ΄Π΅Π°Π»ΡΠ½ΠΎ Ρ Faraday
|
1057
|
+
- **ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΡΠ΅Π΄ΠΈΡΠ΅ΠΊΡΠΎΠ²**: ΠΠ°ΡΡΡΠΎΠ΅Π½Π° Ρ middleware `faraday-follow_redirects` Π΄Π»Ρ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠΉ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ ΡΠ΅ΡΠ²Π΅ΡΠ½ΡΡ
ΡΠ΅Π΄ΠΈΡΠ΅ΠΊΡΠΎΠ²
|
1058
|
+
|
1059
|
+
β οΈ **ΠΠ΅Π·Π½Π°ΡΠΈΡΠ΅Π»ΡΠ½ΠΎΠ΅ ΡΠ΅ΡΠ²Π΅ΡΠ½ΠΎΠ΅ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΠ΅**:
|
1060
|
+
- **ΠΠΎΠΈΡΠΊ ΠΏΠΎΡ
ΠΎΠΆΠΈΡ
ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ² ΠΏΠΎ ΡΠ΅ΠΊΡΡΡ**: ΠΠ½ΠΎΠ³Π΄Π° Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ `503 Service Unavailable` (ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ° ΡΠ΅ΡΠ²Π΅ΡΠ°, Π½Π΅ ΠΊΠ»ΠΈΠ΅Π½ΡΡΠΊΠΎΠΉ ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΠΈ)
|
1061
|
+
|
1062
|
+
ΠΡΡ ΠΎΡΠ½ΠΎΠ²Π½Π°Ρ ΡΡΠ½ΠΊΡΠΈΠΎΠ½Π°Π»ΡΠ½ΠΎΡΡΡ ΡΠ΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½Π° ΠΈ Π³ΠΎΡΠΎΠ²Π° Π΄Π»Ρ ΠΏΡΠΎΠ΄Π°ΠΊΡΠ΅Π½Π°.
|
1063
|
+
|
1064
|
+
## Π‘ΠΏΡΠ°Π²ΠΎΡΠ½ΠΈΠΊ ΠΎΡΠΈΠ±ΠΎΠΊ
|
1065
|
+
|
1066
|
+
### ΠΠ΅ΡΠ°ΡΡ
ΠΈΡ ΠΎΡΠΈΠ±ΠΎΠΊ
|
1067
|
+
|
1068
|
+
```
|
1069
|
+
Rospatent::Errors::Error (Π±Π°Π·ΠΎΠ²Π°Ρ)
|
1070
|
+
βββ MissingTokenError
|
1071
|
+
βββ ApiError
|
1072
|
+
β βββ AuthenticationError (401)
|
1073
|
+
β βββ NotFoundError (404)
|
1074
|
+
β βββ RateLimitError (429)
|
1075
|
+
β βββ ServiceUnavailableError (503)
|
1076
|
+
βββ ConnectionError
|
1077
|
+
β βββ TimeoutError
|
1078
|
+
βββ InvalidRequestError
|
1079
|
+
βββ ValidationError
|
1080
|
+
```
|
1081
|
+
|
1082
|
+
### Π Π°ΡΠΏΡΠΎΡΡΡΠ°Π½Π΅Π½Π½ΡΠ΅ ΡΡΠ΅Π½Π°ΡΠΈΠΈ ΠΎΡΠΈΠ±ΠΎΠΊ
|
1083
|
+
|
1084
|
+
```ruby
|
1085
|
+
# ΠΡΡΡΡΡΡΠ²ΡΡΡΠΈΠΉ ΠΈΠ»ΠΈ Π½Π΅Π΄Π΅ΠΉΡΡΠ²ΠΈΡΠ΅Π»ΡΠ½ΡΠΉ ΡΠΎΠΊΠ΅Π½
|
1086
|
+
Rospatent::Errors::MissingTokenError
|
1087
|
+
Rospatent::Errors::AuthenticationError
|
1088
|
+
|
1089
|
+
# ΠΠ΅Π΄Π΅ΠΉΡΡΠ²ΠΈΡΠ΅Π»ΡΠ½ΡΠ΅ Π²Ρ
ΠΎΠ΄Π½ΡΠ΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ
|
1090
|
+
Rospatent::Errors::ValidationError
|
1091
|
+
|
1092
|
+
# Π Π΅ΡΡΡΡ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½
|
1093
|
+
Rospatent::Errors::NotFoundError
|
1094
|
+
|
1095
|
+
# ΠΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΠ΅ ΡΠΊΠΎΡΠΎΡΡΠΈ
|
1096
|
+
Rospatent::Errors::RateLimitError # ΠΡΠΎΠ²Π΅ΡΡΡΠ΅ retry_after
|
1097
|
+
|
1098
|
+
# ΠΡΠΎΠ±Π»Π΅ΠΌΡ Ρ ΡΠ΅ΡΡΡ
|
1099
|
+
Rospatent::Errors::ConnectionError
|
1100
|
+
Rospatent::Errors::TimeoutError
|
1101
|
+
|
1102
|
+
# ΠΡΠΎΠ±Π»Π΅ΠΌΡ ΡΠ΅ΡΠ²Π΅ΡΠ°
|
1103
|
+
Rospatent::Errors::ServiceUnavailableError
|
1104
|
+
```
|
1105
|
+
|
1106
|
+
## Π’Π΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅
|
1107
|
+
|
1108
|
+
### ΠΠ°ΠΏΡΡΠΊ ΡΠ΅ΡΡΠΎΠ²
|
1109
|
+
|
1110
|
+
```bash
|
1111
|
+
# ΠΡΠ΅ ΡΠ΅ΡΡΡ
|
1112
|
+
$ bundle exec rake test
|
1113
|
+
|
1114
|
+
# ΠΠΎΠ½ΠΊΡΠ΅ΡΠ½ΡΠΉ ΡΠ΅ΡΡΠΎΠ²ΡΠΉ ΡΠ°ΠΉΠ»
|
1115
|
+
$ bundle exec ruby -Itest test/unit/client_test.rb
|
1116
|
+
|
1117
|
+
# ΠΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΠΎΠ½Π½ΡΠ΅ ΡΠ΅ΡΡΡ (ΡΡΠ΅Π±ΡΠ΅ΡΡΡ API ΡΠΎΠΊΠ΅Π½)
|
1118
|
+
$ ROSPATENT_INTEGRATION_TESTS=true ROSPATENT_TEST_TOKEN=Π²Π°Ρ_ΡΠΎΠΊΠ΅Π½ bundle exec rake test_integration
|
1119
|
+
|
1120
|
+
# ΠΠ°ΠΏΡΡΠΊ Ρ ΠΏΠΎΠΊΡΡΡΠΈΠ΅ΠΌ
|
1121
|
+
$ bundle exec rake coverage
|
1122
|
+
```
|
1123
|
+
|
1124
|
+
### ΠΠ°ΡΡΡΠΎΠΉΠΊΠ° ΡΠ΅ΡΡΠΎΠ²
|
1125
|
+
|
1126
|
+
```ruby
|
1127
|
+
# test/test_helper.rb
|
1128
|
+
module Minitest
|
1129
|
+
class Test
|
1130
|
+
def setup
|
1131
|
+
Rospatent.reset
|
1132
|
+
Rospatent.configure do |config|
|
1133
|
+
config.token = ENV.fetch("ROSPATENT_TEST_TOKEN", "test_token")
|
1134
|
+
config.environment = "development"
|
1135
|
+
config.cache_enabled = false
|
1136
|
+
config.log_level = :error
|
1137
|
+
end
|
1138
|
+
end
|
1139
|
+
end
|
1140
|
+
end
|
1141
|
+
```
|
1142
|
+
|
1143
|
+
## Π‘ΠΎΠ²Π΅ΡΡ ΠΏΠΎ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΠΈ
|
1144
|
+
|
1145
|
+
1. **ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΠΊΠ΅ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅**: ΠΠΊΠ»ΡΡΠΈΡΠ΅ ΠΊΠ΅ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π΄Π»Ρ ΠΏΠΎΠ²ΡΠΎΡΡΡΡΠΈΡ
ΡΡ Π·Π°ΠΏΡΠΎΡΠΎΠ²
|
1146
|
+
2. **ΠΠ°ΠΊΠ΅ΡΠ½ΡΠ΅ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ**: ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ `batch_patents` Π΄Π»Ρ ΠΌΠ½ΠΎΠΆΠ΅ΡΡΠ²Π° Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠΎΠ²
|
1147
|
+
3. **ΠΠΎΠ΄Ρ
ΠΎΠ΄ΡΡΠΈΠ΅ Π»ΠΈΠΌΠΈΡΡ**: ΠΠ΅ Π·Π°ΠΏΡΠ°ΡΠΈΠ²Π°ΠΉΡΠ΅ Π±ΠΎΠ»ΡΡΠ΅ Π΄Π°Π½Π½ΡΡ
, ΡΠ΅ΠΌ Π½Π΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΠΎ
|
1148
|
+
4. **ΠΠ΅ΡΠ΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠΉ**: ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΠΎΠ΄ΠΈΠ½ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ ΠΊΠ»ΠΈΠ΅Π½ΡΠ° ΠΊΠΎΠ³Π΄Π° Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ
|
1149
|
+
5. **ΠΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ**: ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΠΏΡΠΎΠ΄Π°ΠΊΡΠ½ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ Π² ΠΏΡΠΎΠ΄Π°ΠΊΡΠ½Π΅
|
1150
|
+
|
1151
|
+
```ruby
|
1152
|
+
# Π₯ΠΎΡΠΎΡΠΎ: ΠΠ΅ΡΠ΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡΠ° ΠΊΠ»ΠΈΠ΅Π½ΡΠ°
|
1153
|
+
client = Rospatent.client
|
1154
|
+
patents = patent_ids.map { |id| client.patent(id) }
|
1155
|
+
|
1156
|
+
# ΠΡΡΡΠ΅: ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠ°ΠΊΠ΅ΡΠ½ΡΡ
ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΉ
|
1157
|
+
patents = []
|
1158
|
+
client.batch_patents(patent_ids) { |doc| patents << doc }
|
1159
|
+
|
1160
|
+
# ΠΡΠ»ΠΈΡΠ½ΠΎ: ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΊΠ΅ΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ Ρ ΠΎΠ±ΡΠΈΠΌ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡΠΎΠΌ
|
1161
|
+
shared_client = Rospatent.client(cache: Rospatent.shared_cache)
|
1162
|
+
```
|
1163
|
+
|
1164
|
+
## Π£ΡΡΡΠ°Π½Π΅Π½ΠΈΠ΅ Π½Π΅ΠΏΠΎΠ»Π°Π΄ΠΎΠΊ
|
1165
|
+
|
1166
|
+
### Π§Π°ΡΡΡΠ΅ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ
|
1167
|
+
|
1168
|
+
**ΠΡΠΈΠ±ΠΊΠΈ Π°ΡΡΠ΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΈ**:
|
1169
|
+
```ruby
|
1170
|
+
# ΠΡΠΎΠ²Π΅ΡΠΊΠ° Π²Π°Π»ΠΈΠ΄Π½ΠΎΡΡΠΈ ΡΠΎΠΊΠ΅Π½Π°
|
1171
|
+
errors = Rospatent.validate_configuration
|
1172
|
+
puts errors if errors.any?
|
1173
|
+
```
|
1174
|
+
|
1175
|
+
**Π’Π°ΠΉΠΌΠ°ΡΡΡ ΡΠ΅ΡΠΈ**:
|
1176
|
+
```ruby
|
1177
|
+
# Π£Π²Π΅Π»ΠΈΡΠ΅Π½ΠΈΠ΅ ΡΠ°ΠΉΠΌΠ°ΡΡΠ° Π΄Π»Ρ ΠΌΠ΅Π΄Π»Π΅Π½Π½ΡΡ
ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠΉ
|
1178
|
+
Rospatent.configure do |config|
|
1179
|
+
config.timeout = 120
|
1180
|
+
config.retry_count = 5
|
1181
|
+
end
|
1182
|
+
```
|
1183
|
+
|
1184
|
+
**ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠ°ΠΌΡΡΠΈ**:
|
1185
|
+
```ruby
|
1186
|
+
# ΠΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΠ΅ ΡΠ°Π·ΠΌΠ΅ΡΠ° ΠΊΠ΅ΡΠ° Π΄Π»Ρ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΠΉ Ρ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½Π½ΠΎΠΉ ΠΏΠ°ΠΌΡΡΡΡ
|
1187
|
+
Rospatent.configure do |config|
|
1188
|
+
config.cache_max_size = 100
|
1189
|
+
config.cache_ttl = 300
|
1190
|
+
end
|
1191
|
+
```
|
1192
|
+
|
1193
|
+
**ΠΡΠ»Π°Π΄ΠΊΠ° API Π²ΡΠ·ΠΎΠ²ΠΎΠ²**:
|
1194
|
+
```ruby
|
1195
|
+
# ΠΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΏΠΎΠ΄ΡΠΎΠ±Π½ΠΎΠ³ΠΎ Π»ΠΎΠ³ΠΈΡΠΎΠ²Π°Π½ΠΈΡ
|
1196
|
+
Rospatent.configure do |config|
|
1197
|
+
config.log_level = :debug
|
1198
|
+
config.log_requests = true
|
1199
|
+
config.log_responses = true
|
1200
|
+
end
|
1201
|
+
```
|
1202
|
+
|
1203
|
+
---
|
1204
|
+
|
1205
|
+
## Changelog
|
1206
|
+
|
1207
|
+
See [CHANGELOG.md](CHANGELOG.md) for detailed version history.
|
1208
|
+
|
1209
|
+
## License
|
1210
|
+
|
1211
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
1212
|
+
|
1213
|
+
---
|
1214
|
+
|
1215
|
+
## API Reference
|
1216
|
+
|
1217
|
+
For detailed API documentation, see the [generated documentation](https://rubydoc.info/gems/rospatent) or run:
|
1218
|
+
|
1219
|
+
```bash
|
1220
|
+
$ bundle exec rake doc
|
1221
|
+
$ open doc/index.html
|
1222
|
+
```
|
1223
|
+
|
1224
|
+
**Key Classes**:
|
1225
|
+
- `Rospatent::Client` - Main API client
|
1226
|
+
- `Rospatent::Configuration` - Configuration management
|
1227
|
+
- `Rospatent::Cache` - Caching system
|
1228
|
+
- `Rospatent::Logger` - Structured logging
|
1229
|
+
- `Rospatent::SearchResult` - Search result wrapper
|
1230
|
+
- `Rospatent::PatentParser` - Patent content parsing
|
1231
|
+
|
1232
|
+
**Classification Features**:
|
1233
|
+
- Classification system search (IPC/CPC)
|
1234
|
+
- Detailed classification code information
|
1235
|
+
- Multi-language support (Russian/English)
|
1236
|
+
- Automatic caching of classification data
|
1237
|
+
|
1238
|
+
**Patent Features**:
|
1239
|
+
- Patent search by text
|
1240
|
+
- Patent details retrieval
|
1241
|
+
- Patent classification retrieval
|
1242
|
+
- Patent content parsing
|
1243
|
+
- Patent media retrieval
|
1244
|
+
- Patent similarity search by text
|
1245
|
+
- Patent similarity search by ID
|
1246
|
+
|
1247
|
+
**Supported Ruby Versions**: Ruby 3.3.0+
|