ba_upload 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 15804867b016b8c972bbb18385a2d1caec4a9729776ea2d1877dcddaeb73b15d
4
- data.tar.gz: 930ef0bf4b3817c43616873144a43e46e482d5baa1efc5f5db63038dd44fd6e0
3
+ metadata.gz: cceefb5de17f2e510d450c83e0ab44a231c23f8024f3b86c1cf4e55c9e2bee32
4
+ data.tar.gz: 29f249da985d7e5213f1cdc8f56f0d1dfc3e352f26823731413ba66506e6672d
5
5
  SHA512:
6
- metadata.gz: 4b906aa0b7c5860142c9c2d01f1f6955b8ccf8217448fd9164e293081f697ff9d43de2411989ff266a4729eb79a087a73fd56124e79159ec146fb29bd72fe6a2
7
- data.tar.gz: fa9aaa0351fdf5758cb2a770e2b0105c6f599bae516f416ffa1ef25de58727f20b0180574086079474050042b253789dded4d58ce78e0edb55f81c146107e2cf
6
+ metadata.gz: d4ba083e3d90f2d5d750127892b914dba63678a0b4423755b08c455d467cba9e28e02eb4bd5a4d900e19572f258d1befe2109a67af5c144fe10a36e8597552c2
7
+ data.tar.gz: c45d8070153d9aca7842ede6b4dea90098608e6b078bdf4e4370a698e837b021382ddccdfe31b4f47c0121ea76e5e8749e15a86a6f8841f9022b635c7f8d6bca
data/README.md CHANGED
@@ -33,7 +33,8 @@ require 'ba_upload'
33
33
  connection = BaUpload.open_connection(file_path: 'config/Zertifikat-1XXXX.p12', passphrase: 'YOURPASSPHRASE')
34
34
 
35
35
  # Upload a xml-file
36
- connection.upload(file: File.open('/opt/vam-transfer/data/DSP000132700_2016-08-08_05-00-09.xml'))
36
+ file_path = "/opt/vam-transfer/data/DSP000132700_2016-08-08_05-00-09.xml"
37
+ connection.upload(file: file_path))
37
38
 
38
39
  # later cronjob to download all error files
39
40
 
@@ -87,6 +88,188 @@ connection.misc(partner_id: 'P000XXXXXX')
87
88
 
88
89
  ```
89
90
 
91
+ ## Appendix: Berufe
92
+
93
+ Sooner or later, you have to provide a TitleCode = Vocation "Beruf" for each job. To fetch and process the Berufe, we create a ActiveRecord Model in our database:
94
+
95
+ Here an example of a implementation at Empfehlungsbund. You can also use our [search mask](https://login.empfehlungsbund.de/arbeitsagentur) to search for occupations.
96
+
97
+ We put the "help" / "validation" messages, that we found in the appropriate scopes, too, as "Ausbildungen" and "Duale Studiengänge" need different types of professions.
98
+
99
+ <details>
100
+ <summary>ActiveRecord Model for Ba::Profession</summary>
101
+
102
+ ```ruby
103
+ # migration:
104
+ create_table :ba_professions do |t|
105
+ t.string "bkz"
106
+ t.string "typ"
107
+ t.string "lbkgruppe"
108
+ t.string "hochschulberuf"
109
+ t.string "kuenstler"
110
+ t.string "bezeichnung_nl"
111
+ t.string "bezeichnung_nk"
112
+ t.string "suchname_nl"
113
+ t.datetime "created_at"
114
+ t.datetime "updated_at"
115
+ t.integer "ebene"
116
+ t.integer "qualifikationsniveau"
117
+ t.datetime "deleted_on"
118
+ end
119
+
120
+ class Ba::Profession < ApplicationRecord
121
+ has_many :jobs
122
+
123
+ scope :undeleted, -> { where 'deleted_on is null' }
124
+ scope :berufe, -> { where typ: 'B' }
125
+ scope :ausbildungen, -> { where typ: 'A' }
126
+ scope :sorted, -> { order(Arel.sql('deleted_on is not null, bezeichnung_nl')) }
127
+ # Bei Auswahl von „Ausbildung“ (EducationType=0) sind die Berufe mit dem
128
+ # Qualifikationsniveau 2 zulässig. Zusätzlich sind hier alle Berufe folgender
129
+ # berufskundlicher Gruppen erlaubt: [...]
130
+ scope :reine_ausbildungen, -> {
131
+ where(qualifikationsniveau: 2).or(
132
+ where(lbkgruppe: [1150, 3110, 5130])
133
+ ).ausbildungen
134
+ }
135
+ # Wird ein Stellenangebot vom Typ „Duales Studium“ (EducationType=1) übermittelt, sind der
136
+ # Studiengang und der ggf. vorhandene Ausbildungsberuf getrennt anzugeben. Als
137
+ # Studiengang (Course) sind Berufe mit ausschließlich dem Qualifikationsniveau 4 zulässig.
138
+ # Diese Berufe entstammen alle der berufskundlichen Gruppe 3120 („A Grundständige
139
+ # Studienfächer/-gänge“). Der als Ausbildung (TitleCode) angegebene Beruf darf
140
+ # dementsprechend nicht ausschließlich das Qualifikationsniveau 4 haben.
141
+ scope :duale_studiengaenge, -> { ausbildungen.where ebene: 3, qualifikationsniveau: 4 }
142
+
143
+ def duales_studium?
144
+ ebene == 3 && qualifikationsniveau == 4 && typ == 'A'
145
+ end
146
+
147
+ def self.download_from_ba
148
+ require 'tty/prompt'
149
+ prompt = TTY::Prompt.new
150
+ link = Ba::Distributor.ba_connection.misc.last do |link|
151
+ link.click
152
+ target = "public/ba/#{link.href}"
153
+ response = link.click
154
+ File.open(target, "wb+") { |f| f.write(response.body) }
155
+
156
+ puts "Unzipping vam_beruf_kurz.xml..."
157
+ `unzip -o -d public/ba/ #{target} vam_beruf_kurz.xml`
158
+ end
159
+
160
+ def self.import(path: 'public/ba/vam_beruf_kurz.xml')
161
+ doc = Nokogiri::XML.parse(File.open(path))
162
+ berufe_vorher = Ba::Beruf.undeleted.pluck(:id)
163
+ doc.search('beruf').each do |beruf_doc|
164
+ beruf = where(id: beruf_doc['id']).first_or_initialize
165
+
166
+ beruf.bkz = beruf_doc['bkz']
167
+
168
+ beruf.typ = beruf_doc.at('typ').text == 't' ? 'B' : 'A'
169
+ beruf.qualifikationsniveau = beruf_doc.at('qualifikationsNiveau[niveau]')['niveau']
170
+ beruf_doc.search(*%w[lbkgruppe hochschulberuf ebene kuenstler bezeichnung_nl bezeichnung_nk suchname_nl]).each do |i|
171
+ beruf.send("#{i.name}=", i.text)
172
+ end
173
+ beruf.save
174
+ berufe_vorher.delete(beruf.id)
175
+ end
176
+ Ba::Beruf.where(id: berufe_vorher).update_all deleted_on: Time.zone.now if berufe_vorher.any?
177
+ end
178
+ scope :duale_studiengaenge, -> { where ebene: 3, qualifikationsniveau: 4 }
179
+
180
+ def display_name
181
+ prefix = if deleted_on?
182
+ "[!VERALTET!] "
183
+ end
184
+ if typ == 'A'
185
+ if ebene == 3 && qualifikationsniveau == 4
186
+ "#{prefix}#{bezeichnung_nk} (DUALES STUDIUM/praxisorientiert)"
187
+ else
188
+ "#{prefix}#{bezeichnung_nk} (AUSBILDUNG)"
189
+ end
190
+ else
191
+ "#{prefix}#{bezeichnung_nk}"
192
+ end
193
+ end
194
+
195
+ def as_json(opts = {})
196
+ {
197
+ id: id,
198
+ display_name: display_name
199
+ }
200
+ end
201
+ ```
202
+
203
+ </details>
204
+
205
+ ## Appendix: How to construct a Job-Posting XML file to upload
206
+
207
+ - Download the most recent JobPosting xsd from https://baxml.arbeitsagentur.de/geschuetzt/download/
208
+ - You can visualize the xsd here: http://www.xml-tools.net/schemaviewer.html
209
+ - Now, you can construct the file with xml-builder:
210
+
211
+ <details>
212
+ <summary>Example for constructing a feed using XmlBuilder</summary>
213
+
214
+ ```ruby
215
+ xml = Builder::XmlMarkup.new(indent: 1)
216
+ xml.instruct!
217
+ xml.tag!("HRBAXMLJobPositionPosting") do
218
+ xml.tag!("Header") do
219
+ xml.tag!("SupplierId", SUPPLIER_ID)
220
+ xml.tag!("Timestamp", Time.zone.now.to_s(:db).tr(" ", "T"))
221
+ xml.tag!("Amount", obs.count)
222
+ # F: Full
223
+ # D: Diff
224
+ if @only_jobs
225
+ xml.tag!("TypeOfLoad", "D")
226
+ else
227
+ xml.tag!("TypeOfLoad", "F")
228
+ end
229
+ end
230
+ xml.tag!("Data") do
231
+ jobs.each do |job|
232
+ generate_xml_for_job(xml, job)
233
+ end
234
+
235
+ jobs_to_delete.each do |job|
236
+ xml.tag! "DeleteEntry" do
237
+ xml.tag! "EntryId", id
238
+ end
239
+ end
240
+ end
241
+ end
242
+ xml
243
+ ```
244
+ </details>
245
+
246
+ - Then, you should validate your feed:
247
+
248
+ ```ruby
249
+ xsd = Nokogiri::XML::Schema(File.open("vendor/ba/HRBAXML_JobPosition_Current.xsd"))
250
+ doc = Nokogiri::XML(xml.to_s)
251
+ xsd.validate(doc)
252
+ ```
253
+
254
+ - Then, you can put that into a file - so you will need to generate a filename **according to the spec**:
255
+
256
+ <details>
257
+ <summary>Generate a filename</summary>
258
+ ```ruby
259
+ # for historic reasons, you could transmit a bunch of files with the same timestamp using an index/offset, but usually, just putting 0 here should be enought
260
+ index = 0
261
+ number_of_feeds_to_push_now = 1
262
+ ended = index == (number_of_feeds_to_push_now - 1)
263
+ flag = ended ? "E" : "C"
264
+ date = Time.zone.now.strftime "%Y-%m-%d_%H-%M-%S_F#{'%03d' % (index + 1)}#{flag}"
265
+ "DS#{SUPPLIER_ID}_#{date}.xml"
266
+ ```
267
+ </details>
268
+
269
+ - Upload the file using this Gem. You should wait a "couple of minutes" (tip: enqueue a activeJob for 10 minutes later), to fetch the resulting **error file**, and analyse that.
270
+
271
+
272
+
90
273
  ## License
91
274
 
92
275
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -1,3 +1,3 @@
1
1
  module BaUpload
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/ba_upload.rb CHANGED
@@ -1,6 +1,11 @@
1
1
  require "ba_upload/version"
2
2
  require "openssl"
3
3
 
4
+ if OpenSSL::VERSION >= '3.0.0'
5
+ # import legacy
6
+ OpenSSL::Provider.load("legacy")
7
+ end
8
+
4
9
  module BaUpload
5
10
  def self.export_certificate(file_path:, passphrase:)
6
11
  cert = OpenSSL::PKCS12.new(File.read(file_path), passphrase)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ba_upload
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Wienert
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-08 00:00:00.000000000 Z
11
+ date: 2023-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mechanize
@@ -90,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
90
  - !ruby/object:Gem::Version
91
91
  version: '0'
92
92
  requirements: []
93
- rubygems_version: 3.0.3
93
+ rubygems_version: 3.2.33
94
94
  signing_key:
95
95
  specification_version: 4
96
96
  summary: Upload API for Bundesagentur fuer Arbeit (hrbaxml.arbeitsagentur)