Rospatent
A comprehensive Ruby client for the Rospatent patent search API with advanced features including intelligent caching, input validation, structured logging, and robust error handling.
π·πΊ ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ Π½Π° ΡΡΡΡΠΊΠΎΠΌ ΡΠ·ΡΠΊΠ΅ Π΄ΠΎΡΡΡΠΏΠ½Π° Π½ΠΈΠΆΠ΅
β¨ Key Features
- π Complete API Coverage - Search, retrieve patents, media files, and datasets
- π‘οΈ Robust Error Handling - Comprehensive error types with detailed context
- β‘ Intelligent Caching - In-memory caching with TTL and LRU eviction
- β Input Validation - Automatic parameter validation with helpful error messages
- π Structured Logging - JSON/text logging with request/response tracking
- π Batch Operations - Process multiple patents concurrently
- βοΈ Environment-Aware - Different configurations for dev/staging/production
- π§ͺ Comprehensive Testing - 96% test coverage with integration tests
- π Excellent Documentation - Detailed examples and API documentation
Installation
Add this line to your application's Gemfile:
gem 'rospatent'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install rospatent
Quick Start
# Basic configuration
Rospatent.configure do |config|
config.token = "your_jwt_token"
end
# Create a client and search
client = Rospatent.client
results = client.search(q: "ΡΠ°ΠΊΠ΅ΡΠ°", limit: 10)
puts "Found #{results.total} results"
results.hits.each do |hit|
puts "Patent: #{hit['id']} - #{hit.dig('biblio', 'ru', 'title')}"
end
Configuration
Basic Configuration
Rospatent.configure do |config|
# Required
config.token = "your_jwt_token"
# API settings
config.api_url = "https://searchplatform.rospatent.gov.ru/patsearch/v0.2"
config.timeout = 30
config.retry_count = 3
# Environment (development, staging, production)
config.environment = "production"
end
Advanced Configuration
Rospatent.configure do |config|
config.token = "your_jwt_token"
# Caching (enabled by default)
config.cache_enabled = true
config.cache_ttl = 300 # 5 minutes
config.cache_max_size = 1000 # Maximum cached items
# Logging
config.log_level = :info # :debug, :info, :warn, :error
config.log_requests = true # Log API requests
config.log_responses = true # Log API responses
# Connection settings
config.connection_pool_size = 5
config.connection_keep_alive = true
# Token management
config.token_expires_at = Time.now + 3600
config.token_refresh_callback = -> { refresh_token! }
end
Environment Variables
Configure via environment variables:
ROSPATENT_ENV=production
ROSPATENT_CACHE_ENABLED=true
ROSPATENT_CACHE_TTL=600
ROSPATENT_LOG_LEVEL=info
ROSPATENT_POOL_SIZE=10
Environment-Specific Defaults
The gem automatically adjusts settings based on environment:
- Development: Fast timeouts, verbose logging, short cache TTL
- Staging: Moderate settings for testing
- Production: Longer timeouts, optimized for reliability
Basic Usage
Searching Patents
client = Rospatent.client
# Simple text search
results = client.search(q: "ΡΠ°ΠΊΠ΅ΡΠ°")
# Natural language search
results = client.search(qn: "rocket engine design")
# Advanced search with all options
results = client.search(
q: "ΡΠ°ΠΊΠ΅ΡΠ° AND Π΄Π²ΠΈΠ³Π°ΡΠ΅Π»Ρ",
limit: 20,
offset: 0,
filter: {
"classification.ipc_group": { "values": ["F02K9"] },
"biblio.application_date": { "from": "2020-01-01" }
},
sort: :pub_date,
group_by: :patent_family,
include_facets: true,
highlight: true,
pre_tag: "<mark>",
post_tag: "</mark>"
)
# Process results
puts "Found #{results.total} total results (#{results.available} available)"
puts "Showing #{results.count} results"
results.hits.each do |hit|
puts "ID: #{hit['id']}"
puts "Title: #{hit.dig('biblio', 'ru', 'title')}"
puts "Date: #{hit.dig('biblio', 'publication_date')}"
puts "IPC: #{hit.dig('classification', 'ipc')}"
puts "---"
end
Retrieving Patent Documents
# Get patent by document ID
patent_doc = client.patent("RU134694U1_20131120")
# Get patent by components
patent_doc = client.patent_by_components(
"RU", # country_code
"134694", # number
"U1", # doc_type
Date.new(2013, 11, 20) # date (String or Date object)
)
# Access patent data
title = patent_doc.dig('biblio', 'ru', 'title')
abstract = patent_doc.dig('abstract', 'ru')
inventors = patent_doc.dig('biblio', 'ru', 'inventor')
Parsing Patent Content
Extract clean text or structured content from patents:
# Parse abstract
abstract_text = client.parse_abstract(patent_doc)
abstract_html = client.parse_abstract(patent_doc, format: :html)
abstract_en = client.parse_abstract(patent_doc, language: "en")
# Parse description
description_text = client.parse_description(patent_doc)
description_html = client.parse_description(patent_doc, format: :html)
# Get structured sections
sections = client.parse_description(patent_doc, format: :sections)
sections.each do |section|
puts "Section #{section[:number]}: #{section[:content]}"
end
Finding Similar Patents
# Find similar patents by ID
similar = client.similar_patents_by_id("RU134694U1_20131120", count: 50)
# Find similar patents by text description
similar = client.similar_patents_by_text(
"Π Π°ΠΊΠ΅ΡΠ½ΡΠΉ Π΄Π²ΠΈΠ³Π°ΡΠ΅Π»Ρ Ρ ΡΠ»ΡΡΡΠ΅Π½Π½ΠΎΠΉ ΡΡΠ³ΠΎΠΉ ...", # 50 words in request minimum
count: 25
)
# Process similar patents
similar["data"]&.each do |patent|
puts "Similar: #{patent['id']} (score: #{patent['similarity']} (#{patent['similarity_norm']}))"
end
Classification Search
Search within patent classification systems (IPC and CPC) and get detailed information about classification codes:
# Search for classification codes related to rockets in IPC
ipc_results = client.classification_search("ipc", query: "ΡΠ°ΠΊΠ΅ΡΠ°", lang: "ru")
puts "Found #{ipc_results['total']} IPC codes"
ipc_results["hits"]&.each do |hit|
puts "#{hit['code']}: #{hit['description']}"
end
# Search for rocket-related codes in CPC using English
cpc_results = client.classification_search("cpc", query: "rocket", lang: "en")
# Get detailed information about a specific classification code
code_info = client.classification_code("ipc", code: "F02K9/00", lang: "ru")
puts "Code: #{code_info['code']}"
puts "Description: #{code_info['description']}"
puts "Hierarchy: #{code_info['hierarchy']&.join(' β ')}"
# Get CPC code information in English
cpc_info = client.classification_code("cpc", code: "B63H11/00", lang: "en")
Supported Classification Systems:
"ipc"- International Patent Classification (ΠΠΠ)"cpc"- Cooperative Patent Classification (Π‘ΠΠ)
Supported Languages:
"ru"- Russian"en"- English
Media and Documents
# Download patent PDF
pdf_data = client.patent_media(
"National", # collection_id
"RU", # country_code
"U1", # doc_type
"2013/11/20", # pub_date
"134694", # pub_number
"document.pdf" # filename
)
File.write("patent.pdf", pdf_data)
# Simplified method using patent ID
pdf_data = client.patent_media_by_id(
"RU134694U1_20131120",
"National",
"document.pdf"
)
# Get available datasets
datasets = client.datasets_tree
datasets.each do |dataset|
puts "#{dataset['id']}: #{dataset['name']}"
end
Advanced Features
Batch Operations
Process multiple patents efficiently with concurrent requests:
document_ids = ["RU134694U1_20131120", "RU2358138C1_20090610", "RU2756123C1_20210927"]
# Process patents in batches
client.batch_patents(document_ids, batch_size: 5) do |patent_doc|
if patent_doc[:error]
puts "Error for #{patent_doc[:document_id]}: #{patent_doc[:error]}"
else
puts "Retrieved patent: #{patent_doc['id']}"
# Process patent document
end
end
# Or collect all results
patents = []
client.batch_patents(document_ids) { |doc| patents << doc }
Caching
Automatic intelligent caching improves performance:
# Caching is automatic and transparent
patent1 = client.patent("RU134694U1_20131120") # API call
patent2 = client.patent("RU134694U1_20131120") # Cached result
# Check cache statistics
stats = client.statistics
puts "Cache hit rate: #{stats[:cache_stats][:hit_rate_percent]}%"
puts "Total requests: #{stats[:requests_made]}"
puts "Average response time: #{stats[:average_request_time]}s"
# Use shared cache across clients
shared_cache = Rospatent.shared_cache
client1 = Rospatent.client(cache: shared_cache)
client2 = Rospatent.client(cache: shared_cache)
# Manual cache management
shared_cache.clear # Clear all cached data
expired_count = shared_cache.cleanup_expired # Remove expired entries
cache_stats = shared_cache.statistics # Get detailed cache statistics
Custom Logging
Configure detailed logging for monitoring and debugging:
# Create custom logger
logger = Rospatent::Logger.new(
output: Rails.logger, # Or any IO object
level: :info,
formatter: :json # :json or :text
)
client = Rospatent.client(logger: logger)
# Logs include:
# - API requests/responses with timing
# - Cache operations (hits/misses)
# - Error details with context
# - Performance metrics
# Access shared logger
shared_logger = Rospatent.shared_logger(level: :debug)
Error Handling
Comprehensive error handling with specific error types:
begin
patent = client.patent("INVALID_ID")
rescue Rospatent::Errors::ValidationError => e
puts "Invalid input: #{e.}"
puts "Field errors: #{e.errors}" if e.errors.any?
rescue Rospatent::Errors::NotFoundError => e
puts "Patent not found: #{e.}"
rescue Rospatent::Errors::RateLimitError => e
puts "Rate limited. Retry after: #{e.retry_after} seconds"
rescue Rospatent::Errors::AuthenticationError => e
puts "Authentication failed: #{e.}"
rescue Rospatent::Errors::ApiError => e
puts "API error (#{e.status_code}): #{e.}"
puts "Request ID: #{e.request_id}" if e.request_id
retry if e.retryable?
rescue Rospatent::Errors::ConnectionError => e
puts "Connection error: #{e.}"
puts "Original error: #{e.original_error}"
end
Input Validation
All inputs are automatically validated with helpful error messages:
# These will raise ValidationError with specific messages:
client.search(limit: 0) # "Limit must be at least 1"
client.patent("") # "Document_id cannot be empty"
client.similar_patents_by_text("", count: -1) # Multiple validation errors
# Validation includes:
# - Parameter types and formats
# - Patent ID format validation
# - Date format validation
# - Enum value validation
# - Required field validation
Performance Monitoring
Track performance and usage statistics:
# Client-specific statistics
stats = client.statistics
puts "Requests made: #{stats[:requests_made]}"
puts "Total duration: #{stats[:total_duration_seconds]}s"
puts "Average request time: #{stats[:average_request_time]}s"
puts "Cache hit rate: #{stats[:cache_stats][:hit_rate_percent]}%"
# Global statistics
global_stats = Rospatent.statistics
puts "Environment: #{global_stats[:configuration][:environment]}"
puts "Cache enabled: #{global_stats[:configuration][:cache_enabled]}"
puts "API URL: #{global_stats[:configuration][:api_url]}"
Environment Configuration
Development Environment
# Optimized for development
Rospatent.configure do |config|
config.environment = "development"
config.token = ENV['ROSPATENT_DEV_TOKEN']
config.log_level = :debug
config.log_requests = true
config.log_responses = true
config.cache_ttl = 60 # Short cache for development
config.timeout = 10 # Fast timeouts for quick feedback
end
Staging Environment
# Optimized for staging
Rospatent.configure do |config|
config.environment = "staging"
config.token = ENV['ROSPATENT_TOKEN']
config.log_level = :info
config.cache_ttl = 300 # Longer cache for performance
config.timeout = 45 # Longer timeouts for reliability
config.retry_count = 3 # More retries for resilience
end
Production Environment
# Optimized for production
Rospatent.configure do |config|
config.environment = "production"
config.token = ENV['ROSPATENT_TOKEN']
config.log_level = :warn
config.cache_ttl = 600 # Longer cache for performance
config.timeout = 60 # Longer timeouts for reliability
config.retry_count = 5 # More retries for resilience
end
Configuration Validation
# Validate current configuration
errors = Rospatent.validate_configuration
if errors.any?
puts "Configuration errors:"
errors.each { |error| puts " - #{error}" }
else
puts "Configuration is valid β"
end
Rails Integration
Generator
$ rails generate rospatent:install
This creates config/initializers/rospatent.rb:
Rospatent.configure do |config|
config.token = Rails.application.credentials.rospatent_token
config.environment = Rails.env
config.cache_enabled = Rails.env.production?
config.log_level = Rails.env.production? ? :warn : :debug
end
Using with Rails Logger
# In config/initializers/rospatent.rb
Rospatent.configure do |config|
config.token = Rails.application.credentials.rospatent_token
end
# Create client with Rails logger
logger = Rospatent::Logger.new(
output: Rails.logger,
level: Rails.env.production? ? :warn : :debug,
formatter: :text
)
# Use in controllers/services
class PatentService
def initialize
@client = Rospatent.client(logger: logger)
end
def search_patents(query)
@client.search(q: query, limit: 20)
rescue Rospatent::Errors::ApiError => e
Rails.logger.error "Patent search failed: #{e.}"
raise
end
end
Testing
Running Tests
# Run all tests
$ bundle exec rake test
# Run specific test file
$ bundle exec ruby -Itest test/unit/client_test.rb
# Run integration tests (requires API token)
$ ROSPATENT_INTEGRATION_TESTS=true ROSPATENT_TEST_TOKEN=your_token bundle exec rake test_integration
# Run with coverage
$ bundle exec rake coverage
Test Configuration
For testing, reset and configure in each test's setup method:
# test/test_helper.rb - Base setup for unit tests
module Minitest
class Test
def setup
Rospatent.reset # Clean state between tests
Rospatent.configure do |config|
config.token = ENV.fetch("ROSPATENT_TEST_TOKEN", "test_token")
config.environment = "development"
config.cache_enabled = false # Disable cache for predictable tests
config.log_level = :error # Reduce test noise
end
end
end
end
# For integration tests - stable config, no reset needed
class IntegrationTest < Minitest::Test
def setup
skip unless ENV["ROSPATENT_INTEGRATION_TESTS"]
@token = ENV.fetch("ROSPATENT_TEST_TOKEN", nil)
skip "ROSPATENT_TEST_TOKEN not set" unless @token
# No reset needed - integration tests use consistent configuration
Rospatent.configure do |config|
config.token = @token
config.environment = "development"
config.cache_enabled = true
config.log_level = :debug
end
end
end
Custom Assertions (Minitest)
# test/test_helper.rb
module Minitest
class Test
def assert_valid_patent_id(patent_id, = nil)
||= "Expected #{patent_id} to be a valid patent ID (format: XX12345Y1_YYYYMMDD)"
assert patent_id.match?(/^[A-Z]{2}[A-Z0-9]+[A-Z]\d*_\d{8}$/),
end
end
end
# Usage in tests
def test_patent_id_validation
assert_valid_patent_id("RU134694U1_20131120")
assert_valid_patent_id("RU134694A_20131120")
end
Known API Limitations
The library uses Faraday as the HTTP client with redirect support for all endpoints:
- All endpoints (
/search,/docs/{id},/similar_search,/datasets/tree, etc.) - β Working perfectly with Faraday - Redirect handling: Configured with
faraday-follow_redirectsmiddleware to handle server redirects automatically
β οΈ Minor server-side limitations:
- Similar Patents by Text: Occasionally returns
503 Service Unavailable(a server-side issue, not a client implementation issue) β οΈ Documentation inconsistencies: - Similar Patents: According to the documentation, the array of hits is named
hits, but the real implementation uses the namedata
All core functionality works perfectly and is production-ready with a unified HTTP approach.
Error Reference
Error Hierarchy
Rospatent::Errors::Error (base)
βββ MissingTokenError
βββ ApiError
β βββ AuthenticationError (401)
β βββ NotFoundError (404)
β βββ RateLimitError (429)
β βββ ServiceUnavailableError (503)
βββ ConnectionError
β βββ TimeoutError
βββ InvalidRequestError
βββ ValidationError
Common Error Scenarios
# Missing or invalid token
Rospatent::Errors::MissingTokenError
Rospatent::Errors::AuthenticationError
# Invalid input parameters
Rospatent::Errors::ValidationError
# Resource not found
Rospatent::Errors::NotFoundError
# Rate limiting
Rospatent::Errors::RateLimitError # Check retry_after
# Network issues
Rospatent::Errors::ConnectionError
Rospatent::Errors::TimeoutError
# Server problems
Rospatent::Errors::ServiceUnavailableError
Rake Tasks
Useful development and maintenance tasks:
# Validate configuration
$ bundle exec rake validate
# Cache management
$ bundle exec rake cache:stats
$ bundle exec rake cache:clear
# Generate documentation
$ bundle exec rake doc
# Run integration tests
$ bundle exec rake test_integration
# Performance benchmarks
$ bundle exec rake benchmark
# Setup development environment
$ bundle exec rake setup
# Pre-release checks
$ bundle exec rake release_check
Performance Tips
- Use Caching: Enable caching for repeated requests
- Batch Operations: Use
batch_patentsfor multiple documents - Appropriate Limits: Don't request more data than needed
- Connection Reuse: Use the same client instance when possible
- Environment Configuration: Use production settings in production
# Good: Reuse client instance
client = Rospatent.client
patents = patent_ids.map { |id| client.patent(id) }
# Better: Use batch operations
patents = []
client.batch_patents(patent_ids) { |doc| patents << doc }
# Best: Use caching with shared instance
shared_client = Rospatent.client(cache: Rospatent.shared_cache)
Troubleshooting
Common Issues
Authentication Errors:
# Check token validity
errors = Rospatent.validate_configuration
puts errors if errors.any?
Network Timeouts:
# Increase timeout for slow connections
Rospatent.configure do |config|
config.timeout = 120
config.retry_count = 5
end
Memory Usage:
# Limit cache size for memory-constrained environments
Rospatent.configure do |config|
config.cache_max_size = 100
config.cache_ttl = 300
end
Debug API Calls:
# Enable detailed logging
Rospatent.configure do |config|
config.log_level = :debug
config.log_requests = true
config.log_responses = true
end
Development
After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests.
Development Setup
$ git clone https://hub.mos.ru/ad/rospatent.git
$ cd rospatent
$ bundle install
$ bundle exec rake setup
Running Tests
# Unit tests
$ bundle exec rake test
# Integration tests (requires API token)
$ ROSPATENT_INTEGRATION_TESTS=true ROSPATENT_TEST_TOKEN=your_token bundle exec rake test_integration
# Code style
$ bundle exec rubocop
# All checks
$ bundle exec rake ci
Interactive Console
$ bin/console
Contributing
Bug reports and pull requests are welcome on MosHub at https://hub.mos.ru/ad/rospatent.
Development Guidelines
- Write Tests: Ensure all new features have corresponding tests
- Follow Style: Run
rubocopand fix any style issues - Document Changes: Update README and CHANGELOG
- Validate Configuration: Run
rake validatebefore submitting
Release Process
# Pre-release checks
$ bundle exec rake release_check
# Update version and release
$ bundle exec rake release
π ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ Π½Π° ΡΡΡΡΠΊΠΎΠΌ ΡΠ·ΡΠΊΠ΅
ΠΠΏΠΈΡΠ°Π½ΠΈΠ΅
Rospatent β ΡΡΠΎ ΠΊΠΎΠΌΠΏΠ»Π΅ΠΊΡΠ½ΡΠΉ Ruby-ΠΊΠ»ΠΈΠ΅Π½Ρ Π΄Π»Ρ Π²Π·Π°ΠΈΠΌΠΎΠ΄Π΅ΠΉΡΡΠ²ΠΈΡ Ρ API ΠΏΠΎΠΈΡΠΊΠ° ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ² Π ΠΎΡΠΏΠ°ΡΠ΅Π½ΡΠ°. ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²Π»ΡΠ΅Ρ ΡΠ΄ΠΎΠ±Π½ΡΠΉ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ Π΄Π»Ρ ΠΏΠΎΠΈΡΠΊΠ°, ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ ΠΈ Π°Π½Π°Π»ΠΈΠ·Π° ΠΏΠ°ΡΠ΅Π½ΡΠ½ΠΎΠΉ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ Ρ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠΌ ΠΊΠ΅ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ, Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΠ΅ΠΉ Π·Π°ΠΏΡΠΎΡΠΎΠ² ΠΈ ΠΏΠΎΠ΄ΡΠΎΠ±Π½ΡΠΌ Π»ΠΎΠ³ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ.
β¨ ΠΠ»ΡΡΠ΅Π²ΡΠ΅ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠΈ
- π ΠΠΎΠ»Π½ΠΎΠ΅ ΠΏΠΎΠΊΡΡΡΠΈΠ΅ API - ΠΏΠΎΠΈΡΠΊ, ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ², ΠΌΠ΅Π΄ΠΈΠ°ΡΠ°ΠΉΠ»Ρ ΠΈ Π΄Π°ΡΠ°ΡΠ΅ΡΡ
- π‘οΈ ΠΠ°Π΄Π΅ΠΆΠ½Π°Ρ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΎΡΠΈΠ±ΠΎΠΊ - ΠΊΠΎΠΌΠΏΠ»Π΅ΠΊΡΠ½ΡΠ΅ ΡΠΈΠΏΡ ΠΎΡΠΈΠ±ΠΎΠΊ Ρ Π΄Π΅ΡΠ°Π»ΡΠ½ΡΠΌ ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡΠΎΠΌ
- β‘ ΠΠ½ΡΠ΅Π»Π»Π΅ΠΊΡΡΠ°Π»ΡΠ½ΠΎΠ΅ ΠΊΠ΅ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ - ΠΊΠ΅ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π² ΠΏΠ°ΠΌΡΡΠΈ Ρ TTL ΠΈ LRU ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ΠΌ
- β ΠΠ°Π»ΠΈΠ΄Π°ΡΠΈΡ Π²Ρ ΠΎΠ΄Π½ΡΡ Π΄Π°Π½Π½ΡΡ - Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠ°Ρ Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΡ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΎΠ² Ρ ΠΏΠΎΠ»Π΅Π·Π½ΡΠΌΠΈ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡΠΌΠΈ
- π Π‘ΡΡΡΠΊΡΡΡΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠ΅ Π»ΠΎΠ³ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ - JSON/ΡΠ΅ΠΊΡΡΠΎΠ²ΠΎΠ΅ Π»ΠΎΠ³ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Ρ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΠ΅ΠΌ Π·Π°ΠΏΡΠΎΡΠΎΠ²/ΠΎΡΠ²Π΅ΡΠΎΠ²
- π ΠΠ°ΠΊΠ΅ΡΠ½ΡΠ΅ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ - ΠΏΠ°ΡΠ°Π»Π»Π΅Π»ΡΠ½Π°Ρ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΌΠ½ΠΎΠΆΠ΅ΡΡΠ²Π° ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ²
- βοΈ ΠΠ΄Π°ΠΏΡΠΈΠ²Π½ΡΠ΅ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ - ΡΠ°Π·Π»ΠΈΡΠ½ΡΠ΅ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ Π΄Π»Ρ development/staging/production
- π§ͺ ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡΠ½ΠΎΠ΅ ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ - 96% ΠΏΠΎΠΊΡΡΡΠΈΠ΅ ΡΠ΅ΡΡΠ°ΠΌΠΈ Ρ ΠΈΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΠΎΠ½Π½ΡΠΌΠΈ ΡΠ΅ΡΡΠ°ΠΌΠΈ
- π ΠΡΠ»ΠΈΡΠ½Π°Ρ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ - ΠΏΠΎΠ΄ΡΠΎΠ±Π½ΡΠ΅ ΠΏΡΠΈΠΌΠ΅ΡΡ ΠΈ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ API
ΠΡΡΡΡΡΠΉ ΡΡΠ°ΡΡ
Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ°
ΠΠΎΠ±Π°Π²ΡΡΠ΅ Π² Π²Π°Ρ Gemfile:
gem 'rospatent'
ΠΠ»ΠΈ ΡΡΡΠ°Π½ΠΎΠ²ΠΈΡΠ΅ Π½Π°ΠΏΡΡΠΌΡΡ:
$ gem install rospatent
ΠΠ°Π·ΠΎΠ²Π°Ρ Π½Π°ΡΡΡΠΎΠΉΠΊΠ°
require 'rospatent'
# ΠΠ°ΡΡΡΠΎΠΉΠΊΠ° ΠΊΠ»ΠΈΠ΅Π½ΡΠ°
Rospatent.configure do |config|
config.token = "Π²Π°Ρ_jwt_ΡΠΎΠΊΠ΅Π½"
end
# Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΊΠ»ΠΈΠ΅Π½ΡΠ°
client = Rospatent.client
ΠΡΠ½ΠΎΠ²Π½ΠΎΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅
ΠΠΎΠΈΡΠΊ ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ²
# ΠΡΠΎΡΡΠΎΠΉ ΠΏΠΎΠΈΡΠΊ
results = client.search(q: "ΡΠΎΠ»Π½Π΅ΡΠ½Π°Ρ Π±Π°ΡΠ°ΡΠ΅Ρ")
# Π Π°ΡΡΠΈΡΠ΅Π½Π½ΡΠΉ ΠΏΠΎΠΈΡΠΊ Ρ ΡΠΈΠ»ΡΡΡΠ°ΠΌΠΈ
results = client.search(
q: "ΠΈΡΠΊΡΡΡΡΠ²Π΅Π½Π½ΡΠΉ ΠΈΠ½ΡΠ΅Π»Π»Π΅ΠΊΡ",
limit: 50,
offset: 100,
datasets: ["ru_since_1994"],
sort: "pub_date:desc",
highlight: true
)
# ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠ²
puts "ΠΠ°ΠΉΠ΄Π΅Π½ΠΎ: #{results.total} ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ²"
results.hits.each do |patent|
puts "#{patent['id']}: #{patent['title']}"
end
ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠΎΠ² ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ²
# ΠΠΎ ΠΈΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡΡ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°
patent = client.patent("RU134694U1_20131120")
# ΠΠ°ΡΡΠΈΠ½Π³ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ³ΠΎ
abstract = client.parse_abstract(patent)
description = client.parse_description(patent, format: :text)
puts "Π Π΅ΡΠ΅ΡΠ°Ρ: #{abstract}"
puts "ΠΠΏΠΈΡΠ°Π½ΠΈΠ΅: #{description}"
ΠΠΎΠΈΡΠΊ ΠΏΠΎΡ ΠΎΠΆΠΈΡ ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ²
# ΠΠΎΠΈΡΠΊ ΠΏΠΎΡ
ΠΎΠΆΠΈΡ
ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ² ΠΏΠΎ ID
similar = client.similar_patents_by_id("RU134694U1_20131120", count: 50)
# ΠΠΎΠΈΡΠΊ ΠΏΠΎΡ
ΠΎΠΆΠΈΡ
ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ² ΠΏΠΎ ΠΎΠΏΠΈΡΠ°Π½ΠΈΡ ΡΠ΅ΠΊΡΡΠ°
similar = client.similar_patents_by_text(
"Π Π°ΠΊΠ΅ΡΠ½ΡΠΉ Π΄Π²ΠΈΠ³Π°ΡΠ΅Π»Ρ Ρ ΡΠ»ΡΡΡΠ΅Π½Π½ΠΎΠΉ ΡΡΠ³ΠΎΠΉ ...", # ΠΌΠΈΠ½ΠΈΠΌΡΠΌ 50 ΡΠ»ΠΎΠ² Π² Π·Π°ΠΏΡΠΎΡΠ΅
count: 25
)
# ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΏΠΎΡ
ΠΎΠΆΠΈΡ
ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ²
similar["data"]&.each do |patent|
puts "ΠΠΎΡ
ΠΎΠΆΠΈΠΉ: #{patent['id']} (ΠΎΡΠ΅Π½ΠΊΠ°: #{patent['similarity']} (#{patent['similarity_norm']}))"
end
ΠΠΎΠΈΡΠΊ ΠΏΠΎ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΎΡΠ°ΠΌ
ΠΠΎΠΈΡΠΊ Π² ΡΠΈΡΡΠ΅ΠΌΠ°Ρ ΠΏΠ°ΡΠ΅Π½ΡΠ½ΠΎΠΉ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΈ (IPC ΠΈ CPC) ΠΈ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΏΠΎΠ΄ΡΠΎΠ±Π½ΠΎΠΉ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ ΠΎ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΎΠ½Π½ΡΡ ΠΊΠΎΠ΄Π°Ρ :
# ΠΠΎΠΈΡΠΊ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΎΠ½Π½ΡΡ
ΠΊΠΎΠ΄ΠΎΠ², ΡΠ²ΡΠ·Π°Π½Π½ΡΡ
Ρ ΡΠ°ΠΊΠ΅ΡΠ°ΠΌΠΈ Π² IPC
ipc_results = client.classification_search("ipc", query: "ΡΠ°ΠΊΠ΅ΡΠ°", lang: "ru")
puts "ΠΠ°ΠΉΠ΄Π΅Π½ΠΎ #{ipc_results['total']} ΠΊΠΎΠ΄ΠΎΠ² IPC"
ipc_results["hits"]&.each do |hit|
puts "#{hit['code']}: #{hit['description']}"
end
# ΠΠΎΠΈΡΠΊ ΠΊΠΎΠ΄ΠΎΠ², ΡΠ²ΡΠ·Π°Π½Π½ΡΡ
Ρ ΡΠ°ΠΊΠ΅ΡΠ°ΠΌΠΈ Π² CPC Π½Π° Π°Π½Π³Π»ΠΈΠΉΡΠΊΠΎΠΌ
cpc_results = client.classification_search("cpc", query: "rocket", lang: "en")
# ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΏΠΎΠ΄ΡΠΎΠ±Π½ΠΎΠΉ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ ΠΎ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΠΎΠΌ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΎΠ½Π½ΠΎΠΌ ΠΊΠΎΠ΄Π΅
code_info = client.classification_code("ipc", code: "F02K9/00", lang: "ru")
puts "ΠΠΎΠ΄: #{code_info['code']}"
puts "ΠΠΏΠΈΡΠ°Π½ΠΈΠ΅: #{code_info['description']}"
puts "ΠΠ΅ΡΠ°ΡΡ
ΠΈΡ: #{code_info['hierarchy']&.join(' β ')}"
# ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ ΠΎ ΠΊΠΎΠ΄Π΅ CPC Π½Π° Π°Π½Π³Π»ΠΈΠΉΡΠΊΠΎΠΌ
cpc_info = client.classification_code("cpc", code: "B63H11/00", lang: "en")
ΠΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅ΠΌΡΠ΅ ΡΠΈΡΡΠ΅ΠΌΡ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΈ:
"ipc"- ΠΠ΅ΠΆΠ΄ΡΠ½Π°ΡΠΎΠ΄Π½Π°Ρ ΠΏΠ°ΡΠ΅Π½ΡΠ½Π°Ρ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΈΡ (ΠΠΠ)"cpc"- Π‘ΠΎΠ²ΠΌΠ΅ΡΡΠ½Π°Ρ ΠΏΠ°ΡΠ΅Π½ΡΠ½Π°Ρ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΈΡ (Π‘ΠΠ)
ΠΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅ΠΌΡΠ΅ ΡΠ·ΡΠΊΠΈ:
"ru"- Π ΡΡΡΠΊΠΈΠΉ"en"- ΠΠ½Π³Π»ΠΈΠΉΡΠΊΠΈΠΉ
ΠΠ΅Π΄ΠΈΠ°ΡΠ°ΠΉΠ»Ρ ΠΈ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΡ
# Π‘ΠΊΠ°ΡΠΈΠ²Π°Π½ΠΈΠ΅ PDF ΠΏΠ°ΡΠ΅Π½ΡΠ°
pdf_data = client.patent_media(
"National", # collection_id
"RU", # country_code
"U1", # doc_type
"2013/11/20", # pub_date
"134694", # pub_number
"document.pdf" # filename
)
File.write("patent.pdf", pdf_data)
# Π£ΠΏΡΠΎΡΠ΅Π½Π½ΡΠΉ ΠΌΠ΅ΡΠΎΠ΄ Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ ID ΠΏΠ°ΡΠ΅Π½ΡΠ°
pdf_data = client.patent_media_by_id(
"RU134694U1_20131120",
"National",
"document.pdf"
)
# ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π΄ΠΎΡΡΡΠΏΠ½ΡΡ
Π΄Π°ΡΠ°ΡΠ΅ΡΠΎΠ²
datasets = client.datasets_tree
datasets.each do |dataset|
puts "#{dataset['id']}: #{dataset['name']}"
end
Π Π°ΡΡΠΈΡΠ΅Π½Π½ΡΠ΅ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠΈ
ΠΠ°ΠΊΠ΅ΡΠ½ΡΠ΅ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ
patent_ids = ["RU134694U1_20131120", "RU2358138C1_20090610"]
client.batch_patents(patent_ids) do |patent|
puts "ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ°: #{patent['id']}"
# ΠΠ°ΡΠ° Π»ΠΎΠ³ΠΈΠΊΠ° ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ
end
ΠΠ΅ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅
# ΠΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ ΠΊΠ΅ΡΠ°
Rospatent.configure do |config|
config.cache_enabled = true
config.cache_ttl = 600 # 10 ΠΌΠΈΠ½ΡΡ
config.cache_max_size = 1000 # ΠΠ°ΠΊΡΠΈΠΌΡΠΌ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠΎΠ²
end
# Π‘ΡΠ°ΡΠΈΡΡΠΈΠΊΠ° ΠΊΠ΅ΡΠ°
stats = client.statistics
puts "ΠΠΎΠΏΠ°Π΄Π°Π½ΠΈΠΉ Π² ΠΊΠ΅Ρ: #{stats[:cache_stats][:hits]}"
ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΎΡΠΈΠ±ΠΎΠΊ
begin
results = client.search(q: "ΠΏΠΎΠΈΡΠΊΠΎΠ²ΡΠΉ Π·Π°ΠΏΡΠΎΡ")
rescue Rospatent::Errors::AuthenticationError => e
puts "ΠΡΠΈΠ±ΠΊΠ° Π°ΡΡΠ΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΈ: #{e.}"
rescue Rospatent::Errors::RateLimitError => e
puts "ΠΡΠ΅Π²ΡΡΠ΅Π½ Π»ΠΈΠΌΠΈΡ Π·Π°ΠΏΡΠΎΡΠΎΠ²: #{e.}"
rescue Rospatent::Errors::ApiError => e
puts "ΠΡΠΈΠ±ΠΊΠ° API: #{e.}"
end
ΠΠ°ΡΡΡΠΎΠΉΠΊΠ° ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ
Π Π°Π·ΡΠ°Π±ΠΎΡΠΊΠ°
# ΠΠΏΡΠΈΠΌΠΈΠ·ΠΈΡΠΎΠ²Π°Π½ΠΎ Π΄Π»Ρ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ
Rospatent.configure do |config|
config.environment = "development"
config.token = ENV['ROSPATENT_DEV_TOKEN']
config.log_level = :debug
config.log_requests = true
config.log_responses = true
config.cache_ttl = 60 # ΠΠΎΡΠΎΡΠΊΠΈΠΉ ΠΊΠ΅Ρ Π΄Π»Ρ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ
config.timeout = 10 # ΠΡΡΡΡΡΠ΅ ΡΠ°ΠΉΠΌΠ°ΡΡΡ Π΄Π»Ρ Π±ΡΡΡΡΠΎΠΉ ΠΎΠ±ΡΠ°ΡΠ½ΠΎΠΉ ΡΠ²ΡΠ·ΠΈ
end
Staging
# ΠΠΏΡΠΈΠΌΠΈΠ·ΠΈΡΠΎΠ²Π°Π½ΠΎ Π΄Π»Ρ staging
Rospatent.configure do |config|
config.environment = "staging"
config.token = ENV['ROSPATENT_TOKEN']
config.log_level = :info
config.cache_ttl = 300 # ΠΠΎΠ»Π΅Π΅ Π΄Π»ΠΈΡΠ΅Π»ΡΠ½ΡΠΉ ΠΊΠ΅Ρ Π΄Π»Ρ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΠΈ
config.timeout = 45 # ΠΠΎΠ»Π΅Π΅ Π΄Π»ΠΈΡΠ΅Π»ΡΠ½ΡΠ΅ ΡΠ°ΠΉΠΌΠ°ΡΡΡ Π΄Π»Ρ Π½Π°Π΄Π΅ΠΆΠ½ΠΎΡΡΠΈ
config.retry_count = 3 # ΠΠΎΠ»ΡΡΠ΅ ΠΏΠΎΠ²ΡΠΎΡΠΎΠ² Π΄Π»Ρ ΡΡΡΠΎΠΉΡΠΈΠ²ΠΎΡΡΠΈ
end
ΠΡΠΎΠ΄Π°ΠΊΡΠ½
# ΠΠΏΡΠΈΠΌΠΈΠ·ΠΈΡΠΎΠ²Π°Π½ΠΎ Π΄Π»Ρ ΠΏΡΠΎΠ΄Π°ΠΊΡΠ½Π°
Rospatent.configure do |config|
config.environment = "production"
config.token = ENV['ROSPATENT_TOKEN']
config.log_level = :warn
config.cache_ttl = 600 # ΠΠΎΠ»Π΅Π΅ Π΄Π»ΠΈΡΠ΅Π»ΡΠ½ΡΠΉ ΠΊΠ΅Ρ Π΄Π»Ρ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΠΈ
config.timeout = 60 # ΠΠΎΠ»Π΅Π΅ Π΄Π»ΠΈΡΠ΅Π»ΡΠ½ΡΠ΅ ΡΠ°ΠΉΠΌΠ°ΡΡΡ Π΄Π»Ρ Π½Π°Π΄Π΅ΠΆΠ½ΠΎΡΡΠΈ
config.retry_count = 5 # ΠΠΎΠ»ΡΡΠ΅ ΠΏΠΎΠ²ΡΠΎΡΠΎΠ² Π΄Π»Ρ ΡΡΡΠΎΠΉΡΠΈΠ²ΠΎΡΡΠΈ
end
ΠΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΡ Ρ Rails
# config/initializers/rospatent.rb
Rospatent.configure do |config|
config.token = Rails.application.credentials.rospatent_token
config.environment = Rails.env
config.cache_enabled = Rails.env.production?
config.log_level = Rails.env.production? ? :warn : :debug
end
# Π ΠΊΠΎΠ½ΡΡΠΎΠ»Π»Π΅ΡΠ΅ ΠΈΠ»ΠΈ ΡΠ΅ΡΠ²ΠΈΡΠ΅
class PatentService
def initialize
@client = Rospatent.client
end
def search_patents(query, **)
@client.search(q: query, **)
end
end
ΠΠ·Π²Π΅ΡΡΠ½ΡΠ΅ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΡ API
ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ Faraday Π² ΠΊΠ°ΡΠ΅ΡΡΠ²Π΅ HTTP-ΠΊΠ»ΠΈΠ΅Π½ΡΠ° Ρ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠΎΠΉ ΡΠ΅Π΄ΠΈΡΠ΅ΠΊΡΠΎΠ² Π΄Π»Ρ Π²ΡΠ΅Ρ endpoints:
- ΠΡΠ΅ endpoints (
/search,/docs/{id},/similar_search,/datasets/tree, ΠΈ Ρ.Π΄.) - β Π Π°Π±ΠΎΡΠ°ΡΡ ΠΈΠ΄Π΅Π°Π»ΡΠ½ΠΎ Ρ Faraday - ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΡΠ΅Π΄ΠΈΡΠ΅ΠΊΡΠΎΠ²: ΠΠ°ΡΡΡΠΎΠ΅Π½Π° Ρ middleware
faraday-follow_redirectsΠ΄Π»Ρ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠΉ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ ΡΠ΅ΡΠ²Π΅ΡΠ½ΡΡ ΡΠ΅Π΄ΠΈΡΠ΅ΠΊΡΠΎΠ²
β οΈ ΠΠ΅Π·Π½Π°ΡΠΈΡΠ΅Π»ΡΠ½ΡΠ΅ ΡΠ΅ΡΠ²Π΅ΡΠ½ΡΠ΅ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΡ:
- ΠΠΎΠΈΡΠΊ ΠΏΠΎΡ
ΠΎΠΆΠΈΡ
ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ² ΠΏΠΎ ΡΠ΅ΠΊΡΡΡ: ΠΠ½ΠΎΠ³Π΄Π° Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ
503 Service Unavailable(ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ° ΡΠ΅ΡΠ²Π΅ΡΠ°, Π½Π΅ ΠΊΠ»ΠΈΠ΅Π½ΡΡΠΊΠΎΠΉ ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΠΈ) β οΈ ΠΠ΅ΡΠΎΡΠ½ΠΎΡΡΠΈ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ: - ΠΠΎΠΈΡΠΊ ΠΏΠΎΡ
ΠΎΠΆΠΈΡ
ΠΏΠ°ΡΠ΅Π½ΡΠΎΠ²: ΠΠ°ΡΡΠΈΠ² ΡΠΎΠ²ΠΏΠ°Π΄Π΅Π½ΠΈΠΉ Π² Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ Π½Π°Π·Π²Π°Π½
hits, ΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠ°Ρ ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρdata
ΠΡΡ ΠΎΡΠ½ΠΎΠ²Π½Π°Ρ ΡΡΠ½ΠΊΡΠΈΠΎΠ½Π°Π»ΡΠ½ΠΎΡΡΡ ΡΠ΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½Π° ΠΈ Π³ΠΎΡΠΎΠ²Π° Π΄Π»Ρ ΠΏΡΠΎΠ΄Π°ΠΊΡΠ΅Π½Π°.
Π‘ΠΏΡΠ°Π²ΠΎΡΠ½ΠΈΠΊ ΠΎΡΠΈΠ±ΠΎΠΊ
ΠΠ΅ΡΠ°ΡΡ ΠΈΡ ΠΎΡΠΈΠ±ΠΎΠΊ
Rospatent::Errors::Error (Π±Π°Π·ΠΎΠ²Π°Ρ)
βββ MissingTokenError
βββ ApiError
β βββ AuthenticationError (401)
β βββ NotFoundError (404)
β βββ RateLimitError (429)
β βββ ServiceUnavailableError (503)
βββ ConnectionError
β βββ TimeoutError
βββ InvalidRequestError
βββ ValidationError
Π Π°ΡΠΏΡΠΎΡΡΡΠ°Π½Π΅Π½Π½ΡΠ΅ ΡΡΠ΅Π½Π°ΡΠΈΠΈ ΠΎΡΠΈΠ±ΠΎΠΊ
# ΠΡΡΡΡΡΡΠ²ΡΡΡΠΈΠΉ ΠΈΠ»ΠΈ Π½Π΅Π΄Π΅ΠΉΡΡΠ²ΠΈΡΠ΅Π»ΡΠ½ΡΠΉ ΡΠΎΠΊΠ΅Π½
Rospatent::Errors::MissingTokenError
Rospatent::Errors::AuthenticationError
# ΠΠ΅Π΄Π΅ΠΉΡΡΠ²ΠΈΡΠ΅Π»ΡΠ½ΡΠ΅ Π²Ρ
ΠΎΠ΄Π½ΡΠ΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ
Rospatent::Errors::ValidationError
# Π Π΅ΡΡΡΡ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½
Rospatent::Errors::NotFoundError
# ΠΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΠ΅ ΡΠΊΠΎΡΠΎΡΡΠΈ
Rospatent::Errors::RateLimitError # ΠΡΠΎΠ²Π΅ΡΡΡΠ΅ retry_after
# ΠΡΠΎΠ±Π»Π΅ΠΌΡ Ρ ΡΠ΅ΡΡΡ
Rospatent::Errors::ConnectionError
Rospatent::Errors::TimeoutError
# ΠΡΠΎΠ±Π»Π΅ΠΌΡ ΡΠ΅ΡΠ²Π΅ΡΠ°
Rospatent::Errors::ServiceUnavailableError
Π’Π΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅
ΠΠ°ΠΏΡΡΠΊ ΡΠ΅ΡΡΠΎΠ²
# ΠΡΠ΅ ΡΠ΅ΡΡΡ
$ bundle exec rake test
# ΠΠΎΠ½ΠΊΡΠ΅ΡΠ½ΡΠΉ ΡΠ΅ΡΡΠΎΠ²ΡΠΉ ΡΠ°ΠΉΠ»
$ bundle exec ruby -Itest test/unit/client_test.rb
# ΠΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΠΎΠ½Π½ΡΠ΅ ΡΠ΅ΡΡΡ (ΡΡΠ΅Π±ΡΠ΅ΡΡΡ API ΡΠΎΠΊΠ΅Π½)
$ ROSPATENT_INTEGRATION_TESTS=true ROSPATENT_TEST_TOKEN=Π²Π°Ρ_ΡΠΎΠΊΠ΅Π½ bundle exec rake test_integration
# ΠΠ°ΠΏΡΡΠΊ Ρ ΠΏΠΎΠΊΡΡΡΠΈΠ΅ΠΌ
$ bundle exec rake coverage
ΠΠ°ΡΡΡΠΎΠΉΠΊΠ° ΡΠ΅ΡΡΠΎΠ²
# test/test_helper.rb
module Minitest
class Test
def setup
Rospatent.reset
Rospatent.configure do |config|
config.token = ENV.fetch("ROSPATENT_TEST_TOKEN", "test_token")
config.environment = "development"
config.cache_enabled = false
config.log_level = :error
end
end
end
end
Π‘ΠΎΠ²Π΅ΡΡ ΠΏΠΎ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΠΈ
- ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΠΊΠ΅ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅: ΠΠΊΠ»ΡΡΠΈΡΠ΅ ΠΊΠ΅ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π΄Π»Ρ ΠΏΠΎΠ²ΡΠΎΡΡΡΡΠΈΡ ΡΡ Π·Π°ΠΏΡΠΎΡΠΎΠ²
- ΠΠ°ΠΊΠ΅ΡΠ½ΡΠ΅ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ: ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅
batch_patentsΠ΄Π»Ρ ΠΌΠ½ΠΎΠΆΠ΅ΡΡΠ²Π° Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠΎΠ² - ΠΠΎΠ΄Ρ ΠΎΠ΄ΡΡΠΈΠ΅ Π»ΠΈΠΌΠΈΡΡ: ΠΠ΅ Π·Π°ΠΏΡΠ°ΡΠΈΠ²Π°ΠΉΡΠ΅ Π±ΠΎΠ»ΡΡΠ΅ Π΄Π°Π½Π½ΡΡ , ΡΠ΅ΠΌ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎ
- ΠΠ΅ΡΠ΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠΉ: ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΠΎΠ΄ΠΈΠ½ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ ΠΊΠ»ΠΈΠ΅Π½ΡΠ° ΠΊΠΎΠ³Π΄Π° Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ
- ΠΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ: ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΠΏΡΠΎΠ΄Π°ΠΊΡΠ½ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ Π² ΠΏΡΠΎΠ΄Π°ΠΊΡΠ½Π΅
# Π₯ΠΎΡΠΎΡΠΎ: ΠΠ΅ΡΠ΅ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡΠ° ΠΊΠ»ΠΈΠ΅Π½ΡΠ°
client = Rospatent.client
patents = patent_ids.map { |id| client.patent(id) }
# ΠΡΡΡΠ΅: ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠ°ΠΊΠ΅ΡΠ½ΡΡ
ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΉ
patents = []
client.batch_patents(patent_ids) { |doc| patents << doc }
# ΠΡΠ»ΠΈΡΠ½ΠΎ: ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΊΠ΅ΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ Ρ ΠΎΠ±ΡΠΈΠΌ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡΠΎΠΌ
shared_client = Rospatent.client(cache: Rospatent.shared_cache)
Π£ΡΡΡΠ°Π½Π΅Π½ΠΈΠ΅ Π½Π΅ΠΏΠΎΠ»Π°Π΄ΠΎΠΊ
Π§Π°ΡΡΡΠ΅ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ
ΠΡΠΈΠ±ΠΊΠΈ Π°ΡΡΠ΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΈ:
# ΠΡΠΎΠ²Π΅ΡΠΊΠ° Π²Π°Π»ΠΈΠ΄Π½ΠΎΡΡΠΈ ΡΠΎΠΊΠ΅Π½Π°
errors = Rospatent.validate_configuration
puts errors if errors.any?
Π’Π°ΠΉΠΌΠ°ΡΡΡ ΡΠ΅ΡΠΈ:
# Π£Π²Π΅Π»ΠΈΡΠ΅Π½ΠΈΠ΅ ΡΠ°ΠΉΠΌΠ°ΡΡΠ° Π΄Π»Ρ ΠΌΠ΅Π΄Π»Π΅Π½Π½ΡΡ
ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠΉ
Rospatent.configure do |config|
config.timeout = 120
config.retry_count = 5
end
ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠ°ΠΌΡΡΠΈ:
# ΠΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΠ΅ ΡΠ°Π·ΠΌΠ΅ΡΠ° ΠΊΠ΅ΡΠ° Π΄Π»Ρ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΠΉ Ρ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½Π½ΠΎΠΉ ΠΏΠ°ΠΌΡΡΡΡ
Rospatent.configure do |config|
config.cache_max_size = 100
config.cache_ttl = 300
end
ΠΡΠ»Π°Π΄ΠΊΠ° API Π²ΡΠ·ΠΎΠ²ΠΎΠ²:
# ΠΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΏΠΎΠ΄ΡΠΎΠ±Π½ΠΎΠ³ΠΎ Π»ΠΎΠ³ΠΈΡΠΎΠ²Π°Π½ΠΈΡ
Rospatent.configure do |config|
config.log_level = :debug
config.log_requests = true
config.log_responses = true
end
Changelog
See CHANGELOG.md for detailed version history.
License
The gem is available as open source under the terms of the MIT License.
API Reference
For detailed API documentation, see the generated documentation or run:
$ bundle exec rake doc
$ open doc/index.html
Key Classes:
Rospatent::Client- Main API clientRospatent::Configuration- Configuration managementRospatent::Cache- Caching systemRospatent::Logger- Structured loggingRospatent::SearchResult- Search result wrapperRospatent::PatentParser- Patent content parsing
Classification Features:
- Classification system search (IPC/CPC)
- Detailed classification code information
- Multi-language support (Russian/English)
- Automatic caching of classification data
Patent Features:
- Patent search by text
- Patent details retrieval
- Patent classification retrieval
- Patent content parsing
- Patent media retrieval
- Patent similarity search by text
- Patent similarity search by ID
Supported Ruby Versions: Ruby 3.3.0+