ox-tender-abstract 0.9.1 → 0.9.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2dfb0556afbbe98cebec1fbb413fbe4408d5258e6fbd79d090c99df94dc718d6
4
- data.tar.gz: 7d3308d579e05d7c651848f2cd2d1459562ab85d9f3991c2ef4784637803df99
3
+ metadata.gz: 9924cb49a0b35703d8e0093b3ae40b8b8556f7da398990b3ff5f84f185c7d944
4
+ data.tar.gz: 2598c2bb4af463aa3cbf1bde9c1cc76aac352f8e58a193152305dccc4a2cef8a
5
5
  SHA512:
6
- metadata.gz: aa20e0eeebbdebebc3cba7c36112db12a8bbc63bffffe9d6ce79f762520a9f28be0afc587fb47f22e9358facf118e6e80093fbe1b9a9387c356fecc43efcaacf
7
- data.tar.gz: 3437061cb5eaa5c84de842c4d57056ef3fa5d080a0f972708db210c48081a807faa866aeb4b9296d759c4855b4c95c4b541f22ba4be924def0ce64a8afea1348
6
+ metadata.gz: 3c0a783ab40ca1f45be2d0d5c32db63345a40502bdf6140b446018686b73482afba84ce3097d7ab7afe821b106915e06ba32b03b66a35a19adf95fdc4ad2c404
7
+ data.tar.gz: 53a15a50dad376969b8ff1869e88197a669288bdd57b3931ce59105c8288b25f6f9a9768e03b9211f8284570e4bbf219daa98f2702270c39c967c5e1737c4ead
data/.rspec_status CHANGED
@@ -49,26 +49,26 @@ example_id | status | run_time
49
49
  ./spec/oxtenderabstract/result_spec.rb[1:3:2] | passed | 0.00002 seconds |
50
50
  ./spec/oxtenderabstract/result_spec.rb[1:4:1] | passed | 0.00002 seconds |
51
51
  ./spec/oxtenderabstract/result_spec.rb[1:4:2] | passed | 0.00002 seconds |
52
- ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:1:1] | passed | 0.00012 seconds |
53
- ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:2:1] | passed | 0.00004 seconds |
54
- ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:3:1] | failed | 0.00122 seconds |
55
- ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:3:2] | passed | 0.00162 seconds |
56
- ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:3:3] | failed | 0.00097 seconds |
57
- ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:4:1] | passed | 0.00336 seconds |
58
- ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:4:2] | passed | 0.00176 seconds |
59
- ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:4:3] | passed | 0.0009 seconds |
60
- ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:5:1] | passed | 0.00012 seconds |
61
- ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:6:1] | passed | 0.00008 seconds |
62
- ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:7:1] | passed | 0.00008 seconds |
63
- ./spec/oxtenderabstract/xml_parser_spec.rb[1:2:1:1] | failed | 0.0001 seconds |
64
- ./spec/oxtenderabstract/xml_parser_spec.rb[1:2:2:1] | failed | 0.00007 seconds |
65
- ./spec/oxtenderabstract/xml_parser_spec.rb[1:2:3:1] | failed | 0.00012 seconds |
52
+ ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:1:1] | passed | 0.00192 seconds |
53
+ ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:2:1] | passed | 0.00007 seconds |
54
+ ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:3:1] | failed | 0.00615 seconds |
55
+ ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:3:2] | passed | 0.00098 seconds |
56
+ ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:3:3] | failed | 0.00089 seconds |
57
+ ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:4:1] | passed | 0.00194 seconds |
58
+ ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:4:2] | passed | 0.00168 seconds |
59
+ ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:4:3] | passed | 0.00086 seconds |
60
+ ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:5:1] | passed | 0.00008 seconds |
61
+ ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:6:1] | passed | 0.00007 seconds |
62
+ ./spec/oxtenderabstract/xml_parser_spec.rb[1:1:7:1] | passed | 0.00007 seconds |
63
+ ./spec/oxtenderabstract/xml_parser_spec.rb[1:2:1:1] | failed | 0.00016 seconds |
64
+ ./spec/oxtenderabstract/xml_parser_spec.rb[1:2:2:1] | failed | 0.00011 seconds |
65
+ ./spec/oxtenderabstract/xml_parser_spec.rb[1:2:3:1] | failed | 0.00015 seconds |
66
66
  ./spec/oxtenderabstract/xml_parser_spec.rb[1:3:1] | passed | 0.00005 seconds |
67
- ./spec/oxtenderabstract/xml_parser_spec.rb[1:3:2] | passed | 0.00005 seconds |
67
+ ./spec/oxtenderabstract/xml_parser_spec.rb[1:3:2] | passed | 0.00004 seconds |
68
68
  ./spec/oxtenderabstract/xml_parser_spec.rb[1:3:3] | passed | 0.00004 seconds |
69
- ./spec/oxtenderabstract/xml_parser_spec.rb[1:3:4] | passed | 0.00005 seconds |
70
- ./spec/oxtenderabstract/xml_parser_spec.rb[1:4:1:1] | failed | 0.00045 seconds |
71
- ./spec/oxtenderabstract/xml_parser_spec.rb[1:4:2:1] | failed | 0.00007 seconds |
69
+ ./spec/oxtenderabstract/xml_parser_spec.rb[1:3:4] | passed | 0.00006 seconds |
70
+ ./spec/oxtenderabstract/xml_parser_spec.rb[1:4:1:1] | failed | 0.00131 seconds |
71
+ ./spec/oxtenderabstract/xml_parser_spec.rb[1:4:2:1] | passed | 0.00015 seconds |
72
72
  ./spec/oxtenderabstract_spec.rb[1:1:1] | passed | 0.00053 seconds |
73
73
  ./spec/oxtenderabstract_spec.rb[1:2:1] | passed | 0.00003 seconds |
74
74
  ./spec/oxtenderabstract_spec.rb[1:3:1] | passed | 0.00003 seconds |
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## [0.9.3] - 2025-07-27
2
+
3
+ - Added support for parsing tender documents
4
+ - Added support for parsing contract documents
5
+ - Added support for parsing organization documents
6
+ - Added support for parsing generic documents
7
+ - Added support for parsing attachments
8
+ - Added support for parsing tender documents
9
+ - Added support for parsing contract documents
10
+ - Added support for parsing organization documents
11
+
1
12
  ## [0.9.0] - 2025-07-15
2
13
 
3
14
  - Initial release
data/README.md CHANGED
@@ -273,6 +273,41 @@ puts result.data[:total_archives] # => 6
273
273
  # Processing typically takes 10-15 seconds for a full day's data
274
274
  ```
275
275
 
276
+ ## Error Handling
277
+
278
+ The library uses the `Result` pattern for error handling:
279
+
280
+ ```ruby
281
+ result = OxTenderAbstract.search_tenders(org_region: '77', exact_date: '2024-01-01')
282
+
283
+ if result.success?
284
+ puts "Found tenders: #{result.data[:tenders].size}"
285
+ else
286
+ puts "Error: #{result.error}"
287
+
288
+ # Check error type for special handling
289
+ if result.metadata[:error_type] == :blocked
290
+ retry_after = result.metadata[:retry_after] || 600
291
+ puts "API blocked for #{retry_after} seconds"
292
+ end
293
+ end
294
+ ```
295
+
296
+ ### Handling API Blocks
297
+
298
+ When making frequent requests, the API may block archive downloads for 10 minutes. The library automatically detects such blocks:
299
+
300
+ ```ruby
301
+ result = OxTenderAbstract.search_tenders(org_region: '77', exact_date: '2024-01-01')
302
+
303
+ if result.failure? && result.metadata[:error_type] == :blocked
304
+ retry_after = result.metadata[:retry_after] # 600 seconds (10 minutes)
305
+ puts "Download blocked, retry in #{retry_after} seconds"
306
+ end
307
+ ```
308
+
309
+ For detailed guidance on using with Sidekiq background jobs, see [SIDEKIQ_USAGE.md](SIDEKIQ_USAGE.md).
310
+
276
311
  ## Requirements
277
312
 
278
313
  - Ruby >= 3.0.0
@@ -1,39 +1,172 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'oxtenderabstract/version'
4
- require_relative 'oxtenderabstract/logger'
4
+ require_relative 'oxtenderabstract/configuration'
5
5
  require_relative 'oxtenderabstract/errors'
6
+ require_relative 'oxtenderabstract/logger'
6
7
  require_relative 'oxtenderabstract/result'
7
8
  require_relative 'oxtenderabstract/document_types'
8
- require_relative 'oxtenderabstract/configuration'
9
- require_relative 'oxtenderabstract/xml_parser'
10
9
  require_relative 'oxtenderabstract/archive_processor'
10
+ require_relative 'oxtenderabstract/xml_parser'
11
11
  require_relative 'oxtenderabstract/client'
12
12
 
13
13
  # Main module for OxTenderAbstract library
14
14
  module OxTenderAbstract
15
15
  class Error < StandardError; end
16
16
 
17
- # Convenience method to create a new client
18
- def self.client(token: nil)
19
- Client.new(token: token)
20
- end
17
+ class << self
18
+ def configure
19
+ yield(configuration)
20
+ end
21
21
 
22
- # Search tenders by region and date (convenience method)
23
- def self.search_tenders(org_region:, exact_date:, token: nil, **options)
24
- client = Client.new(token: token)
25
- client.search_tenders(org_region: org_region, exact_date: exact_date, **options)
26
- end
22
+ def configuration
23
+ @configuration ||= Configuration.new
24
+ end
27
25
 
28
- # Enhanced search tenders with detailed information (convenience method)
29
- def self.enhanced_search_tenders(org_region:, exact_date:, token: nil, **options)
30
- client = Client.new(token: token)
31
- client.enhanced_search_tenders(org_region: org_region, exact_date: exact_date, **options)
32
- end
26
+ def reset_configuration!
27
+ @configuration = nil
28
+ end
29
+
30
+ # Convenience method for searching tenders in specific subsystem
31
+ def search_tenders(org_region:, exact_date:, subsystem_type: DocumentTypes::DEFAULT_SUBSYSTEM,
32
+ document_type: DocumentTypes::DEFAULT_DOCUMENT_TYPE)
33
+ client = Client.new
34
+ client.search_tenders(
35
+ org_region: org_region,
36
+ exact_date: exact_date,
37
+ subsystem_type: subsystem_type,
38
+ document_type: document_type
39
+ )
40
+ end
41
+
42
+ # Enhanced method for searching tenders across multiple subsystems
43
+ def search_all_tenders(org_region:, exact_date:, subsystems: nil, document_types: nil)
44
+ # Default subsystems to search
45
+ subsystems ||= %w[PRIZ RPEC RPGZ BTK UR RGK OD223 RD223]
46
+
47
+ client = Client.new
48
+ all_results = {}
49
+ total_tenders = []
50
+ total_archives = 0
51
+
52
+ subsystems.each do |subsystem_type|
53
+ # Get appropriate document types for this subsystem
54
+ available_types = DocumentTypes.document_types_for_subsystem(subsystem_type)
55
+ test_types = document_types || [available_types.first] # Test first type by default
56
+
57
+ subsystem_results = {
58
+ subsystem: subsystem_type,
59
+ description: DocumentTypes.description_for_subsystem(subsystem_type),
60
+ tenders: [],
61
+ archives: 0,
62
+ errors: []
63
+ }
64
+
65
+ test_types.each do |doc_type|
66
+ result = client.search_tenders(
67
+ org_region: org_region,
68
+ exact_date: exact_date,
69
+ subsystem_type: subsystem_type,
70
+ document_type: doc_type
71
+ )
72
+
73
+ if result.success?
74
+ tenders = result.data[:tenders] || []
75
+ archives = result.data[:total_archives] || 0
76
+
77
+ subsystem_results[:tenders].concat(tenders)
78
+ subsystem_results[:archives] += archives
79
+ total_archives += archives
80
+
81
+ # Add subsystem info to each tender
82
+ tenders.each do |tender|
83
+ tender[:subsystem_type] = subsystem_type
84
+ tender[:subsystem_description] = DocumentTypes.description_for_subsystem(subsystem_type)
85
+ tender[:document_type_used] = doc_type
86
+ end
87
+
88
+ total_tenders.concat(tenders)
89
+ else
90
+ subsystem_results[:errors] << "#{doc_type}: #{result.error}"
91
+ end
92
+ rescue StandardError => e
93
+ subsystem_results[:errors] << "#{doc_type}: #{e.message}"
94
+ end
95
+
96
+ all_results[subsystem_type] = subsystem_results
97
+ end
98
+
99
+ Result.success({
100
+ tenders: total_tenders,
101
+ total_archives: total_archives,
102
+ subsystem_results: all_results,
103
+ search_params: {
104
+ org_region: org_region,
105
+ exact_date: exact_date,
106
+ subsystems_searched: subsystems.size
107
+ },
108
+ processed_at: Time.now
109
+ })
110
+ end
111
+
112
+ # Get documents by registry number across subsystems
113
+ def get_docs_by_reestr_number(reestr_number:, subsystem_type: DocumentTypes::DEFAULT_SUBSYSTEM)
114
+ client = Client.new
115
+ client.get_docs_by_reestr_number(
116
+ reestr_number: reestr_number,
117
+ subsystem_type: subsystem_type
118
+ )
119
+ end
120
+
121
+ # Enhanced search with detailed information extraction
122
+ def enhanced_search_tenders(org_region:, exact_date:, subsystem_type: DocumentTypes::DEFAULT_SUBSYSTEM,
123
+ document_type: DocumentTypes::DEFAULT_DOCUMENT_TYPE,
124
+ include_attachments: true)
125
+ client = Client.new
126
+ client.enhanced_search_tenders(
127
+ org_region: org_region,
128
+ exact_date: exact_date,
129
+ subsystem_type: subsystem_type,
130
+ document_type: document_type,
131
+ include_attachments: include_attachments
132
+ )
133
+ end
33
134
 
34
- # Get documents by registry number (convenience method)
35
- def self.get_docs_by_reestr_number(reestr_number:, token: nil, **options)
36
- client = Client.new(token: token)
37
- client.get_docs_by_reestr_number(reestr_number: reestr_number, **options)
135
+ # Search tenders with automatic wait on API blocks and resume capability
136
+ def search_tenders_with_auto_wait(org_region:, exact_date:, subsystem_type: DocumentTypes::DEFAULT_SUBSYSTEM,
137
+ document_type: DocumentTypes::DEFAULT_DOCUMENT_TYPE, resume_state: nil)
138
+ client = Client.new
139
+
140
+ # Если есть состояние для продолжения
141
+ if resume_state
142
+ start_from = resume_state[:next_archive_index] || 0
143
+ client.search_tenders_with_resume(
144
+ org_region: org_region,
145
+ exact_date: exact_date,
146
+ subsystem_type: subsystem_type,
147
+ document_type: document_type,
148
+ start_from_archive: start_from,
149
+ resume_state: resume_state
150
+ )
151
+ else
152
+ # Используем обычный метод если авто-ожидание включено
153
+ if configuration.auto_wait_on_block
154
+ client.search_tenders(
155
+ org_region: org_region,
156
+ exact_date: exact_date,
157
+ subsystem_type: subsystem_type,
158
+ document_type: document_type
159
+ )
160
+ else
161
+ # Используем метод с возможностью продолжения
162
+ client.search_tenders_with_resume(
163
+ org_region: org_region,
164
+ exact_date: exact_date,
165
+ subsystem_type: subsystem_type,
166
+ document_type: document_type
167
+ )
168
+ end
169
+ end
170
+ end
38
171
  end
39
172
  end