agreement-design-prototype 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Binary file
data/generate.rb ADDED
@@ -0,0 +1,57 @@
1
+ require_relative "src/diagram"
2
+ require_relative "src/doc"
3
+ require_relative "src/data"
4
+ require_relative 'model/geographic'
5
+ require_relative "model/agreement"
6
+ require_relative "model/fm"
7
+ require_relative "model/supply_teachers"
8
+
9
+ output_path = File.join(File.dirname(__FILE__), "gen")
10
+
11
+ metamodels = [Agreements, Parties, Geographic, SupplyTeacherOfferings]
12
+
13
+ diagram = Diagram.new(output_path, "metamodel")
14
+ diagram.describe *metamodels
15
+
16
+ doc = Document.new(output_path, "metamodel")
17
+ doc.document_metamodel *metamodels
18
+
19
+ models = [
20
+ Agreements::Supply_Teacher_Agreements,
21
+ Geographic::NUTS,
22
+ ]
23
+
24
+ doc = Document.new(output_path, "supply_teachers")
25
+ doc.document Agreements::Supply_Teacher_Agreements
26
+
27
+ data = DataFile.new(output_path, "agreements", fmt: :json)
28
+ data.output *models
29
+ data = DataFile.new(output_path, "agreements", fmt: :yaml)
30
+ data.output *models
31
+ data = DataFile.new(output_path, "agreements", fmt: :jsonlines)
32
+ data.output *models, select: :agreement, index: index_agreement_for_elasticsearch
33
+
34
+ # create a store for all parties in all models
35
+ parties= Parties.new :AllParties do
36
+ party do
37
+ org_name "CCS"
38
+ end
39
+ end
40
+
41
+ # put test catalogues into test output
42
+ def test_data_file(name)
43
+ File.join(File.dirname(__FILE__), "data", name)
44
+ end
45
+
46
+ sm_offers= load_managing_suppliers(test_data_file("teacher-management-test.csv"), parties)
47
+ sr_offers= load_recruitment_suppliers(test_data_file("teacher-recruitment-test.csv"), parties)
48
+
49
+ data= DataFile.new( output_path, "teacher-management-test-offers", fmt: :jsonlines)
50
+ data.output sm_offers, index: index_offering_for_elasticsearch
51
+
52
+ data= DataFile.new( output_path, "teacher-recruitment-test-offers", fmt: :jsonlines)
53
+ data.output sr_offers, index: index_offering_for_elasticsearch
54
+
55
+ # can now do something like:
56
+ #curl -s -H "Content-Type: application/x-ndjson" -XPOST localhost:9200/offerings/offerings/_bulk --data-binary @teacher-management-test-offers.jsonlines
57
+
data/model/README.md ADDED
@@ -0,0 +1,48 @@
1
+ ##Metamodels
2
+
3
+ Metamodels are ruby files that define our agreement
4
+ data metamodels models, and supporting models. For
5
+ example. The main metamodles are
6
+
7
+ - Agreement
8
+ - Party
9
+ - Geographic
10
+
11
+ We also extend these (optionally) for each `Agreement`
12
+ since different agreements may have special Offerings.
13
+ For example the standard `Offering` type has the attributes
14
+ including
15
+
16
+ - Item(s)
17
+ - Name
18
+ - Supplier_id
19
+
20
+ And the [Supply Teacher framework](supply_teachers.rb) lots have Offerings which add
21
+ framework specific attributes such as:
22
+
23
+ - Branch name
24
+ - Branch contact
25
+
26
+ So the Supply Teacher Framework extends our metamodel.
27
+
28
+ Once we have the metamodel, CCS agreements are defined
29
+ by instanciating the relevant model domain, such as (in Ruby)
30
+
31
+ ```ruby
32
+ Agreements.new(:SomeCategory) {
33
+
34
+ agreement {
35
+ kind :Framework
36
+ id SOME_ID
37
+ fwk_number "RM8330"
38
+ version "0.1.0"
39
+ description "This agreement is for the provision of Stuff"
40
+ start_date date(2018, 10, 01)
41
+ }
42
+ }
43
+ ```
44
+
45
+ Specifically, Catalogues are defined by creating instances
46
+ of Offerings, either in data files, in code, or through
47
+ an API. For example see the catalogue output as
48
+ [jsonlines](../gen/data/teacher-recruitment-test-offers.jsonlines)
data/model/agreement.rb CHANGED
@@ -6,7 +6,7 @@ include DataModel
6
6
  domain :Agreements do
7
7
 
8
8
  SECTOR = Selection(:ALL, :Education, :CentralGov, :WiderGov, :Etc)
9
- UNITS = Selection(:Area, :Currency)
9
+ UNITS = Selection(:Area, :Commission, :Commission)
10
10
  CLASSIFICATION_SCHEMES = Selection(:CPV, :CPVS, :UNSPSC, :CPV, :OKDP, :OKPD, :CCS)
11
11
  code(:CCS, description: "CCS invented schemes")
12
12
 
@@ -26,7 +26,7 @@ variable facts in their Offer to supplement the description of how they support
26
26
  attribute :unit, UNITS, " define the units, if one units matches "
27
27
  }
28
28
 
29
- CATEGORY_OF_NEED = Selection(:Budget, :Location, :Service)
29
+ TYPES_OF_EXPRESSION_OF_NEED = Selection(:Budget, :Location, :Service)
30
30
  code(:Budget, description:
31
31
  "What is the budget the buyer has for their need?
32
32
  Match the budget to the value range of the agreement, and the value range of supplier offers.
@@ -38,14 +38,14 @@ Match location needs to locations of offers")
38
38
  "What sort of things do they need?
39
39
  Match the service to item types, their keywords, and offering titles.")
40
40
 
41
- datatype(:Need,
41
+ datatype(:ExpressionOfNeed,
42
42
  description:
43
43
  " Defines a buyer's need which can be matched to agreement items and other details
44
44
  The need matches closely to our definitions of agreements under 'items types' and their classification
45
45
  schemes, but is not a one-to-one match.") {
46
46
  attribute :buyer_id, String, "The buyer expressing the need"
47
- attribute :kind, Selection(:Budget, :Location,)
48
47
  attribute :kind, Selection(:Budget, :Location, :Service)
48
+ attribute :value, String
49
49
  attribute :unit, UNITS, "The units typically used to express the need"
50
50
  }
51
51
 
@@ -65,6 +65,7 @@ schemes, but is not a one-to-one match.") {
65
65
  attribute :fwk_number, String, "Framework (RM) number of related framework if required. @Example RM123"
66
66
  attribute :sf_typ, String, "SalesForce data type"
67
67
  attribute :sf_is, String, "SalesForce row id"
68
+ attribute :offerType, String, "Name of the subclass of the Offering, supporting the Agreement"
68
69
 
69
70
  # structure of agreement
70
71
  attribute :part_of_id, String, "Agreement this is part of, applicable only to Lots", links: :Agreement
@@ -80,7 +81,7 @@ schemes, but is not a one-to-one match.") {
80
81
 
81
82
  datatype(:Item,
82
83
  description: "Specifies the value of an item that is being offered for an agreement") {
83
- attribute :type, String, " type of the item ", links: :ItemType
84
+ attribute :type_id, String, " type of the item ", links: :ItemType
84
85
  attribute :unit, UNITS, " define the units "
85
86
  attribute :value, Object, "an object of the type matching type->units"
86
87
  }
@@ -91,7 +92,6 @@ This may be extended for different agreements. A supplier may provide additional
91
92
  variable facts in their Offer to supplement the description of how they support the agreement. ") {
92
93
  attribute :agreement_id, String, "The agreement this offering relates to", links: :Agreement
93
94
  attribute :supplier_id, String, links: Parties::Party
94
- attribute :offerType, String, "Name of the subclass of the Offering, supporting the Agreement"
95
95
  attribute :name, String
96
96
  attribute :description, String
97
97
  attribute :item, :Item, ZERO_TO_MANY, "details of the item"
data/model/fm.rb CHANGED
@@ -1,13 +1,13 @@
1
1
  require_relative 'agreement'
2
2
 
3
- domain(:FM) {
3
+ domain(:FM_Offerings) {
4
4
  datatype(:FM_Offering, extends: Agreements::Offering,
5
5
  description: " An offer for FM elements ") {
6
6
  attribute :sc_cleared, String
7
7
  }
8
8
  }
9
9
 
10
- Agreements.new :FM_Agreements do
10
+ Agreements.new(:FM_Agreements) {
11
11
 
12
12
  FM_ID = "FM"
13
13
  agreement {
@@ -20,6 +20,17 @@ Agreements.new :FM_Agreements do
20
20
  }
21
21
 
22
22
  ENV_CLEANING = "#{FM_ID}.1a-C.3"
23
+
24
+ cleaning = itemtype {
25
+ id ENV_CLEANING
26
+ scheme_id :CCS
27
+ unit :Commission
28
+ code "CCS-building-area-method"
29
+ keyword "cleaning"
30
+ keyword "washing"
31
+ keyword "janitor"
32
+ }
33
+
23
34
  agreement {
24
35
  kind :Lot
25
36
  id "FM lot 1"
@@ -28,20 +39,12 @@ Agreements.new :FM_Agreements do
28
39
  min_value 0
29
40
  max_value 70000000
30
41
  version "0.1.0"
31
- item_type {
32
- id ENV_CLEANING
33
- scheme_id :CCS
34
- unit :Currency
35
- code "CCS-building-area-method"
36
- keyword "cleaning"
37
- keyword "washing"
38
- keyword "janitor"
39
- }
42
+ cnew = item_type cleaning
40
43
  }
41
44
 
42
- end
45
+ }
43
46
 
44
- FM.new(:FM_Catalogue) {
47
+ FM_Offerings.new(:FM_Catalogue) {
45
48
  fm_offering {
46
49
  supplier_id "XYZ corp"
47
50
  name "XYZ's nifty school cleaning service"
@@ -50,7 +53,7 @@ FM.new(:FM_Catalogue) {
50
53
  sector :Education
51
54
  sc_cleared "TRUE"
52
55
  item {
53
- type ENV_CLEANING
56
+ type_id ENV_CLEANING
54
57
  value 3000
55
58
  }
56
59
  }
data/model/party.rb CHANGED
@@ -10,10 +10,35 @@ be one or the other. The onvolvement of the party with an agreement determine t
10
10
  that contenxt.
11
11
  Details still to be added") {
12
12
  attribute :id, String, "UUID or Salesforce ID?"
13
- #TODO add all the usual fields
13
+ attribute :org_name, String, "UUID or Salesforce ID?"
14
14
  # Supplier registration
15
15
  attribute :supplier_registration_completed, Date
16
16
  # Buyer registration
17
17
  attribute :buyer_registration_completed, Date
18
18
  }
19
+
20
+ datatype(:Address, description: "Address should include at least addres line 1 and ideally post code.
21
+ will contain UPRN if we have derived it.
22
+ ") {
23
+ attribute :address_1, String
24
+ attribute :address_2, String, ZERO_OR_ONE
25
+ attribute :town, String, ZERO_OR_ONE
26
+ attribute :county, String, ZERO_TO_MANY
27
+ attribute :postcode, String, ZERO_TO_MANY
28
+ attribute :uprn, String, ZERO_TO_MANY
29
+ }
30
+
31
+ datatype(:Contact, description: "
32
+ A way of contacting a party. Store contacts in a safe identity store. Do not store personal details elsewhere.
33
+ ") {
34
+ attribute :id, String, "a UUID for the person or contact point"
35
+ attribute :party_id, String, "contact is a link for this party", links: :Party
36
+ attribute :name, String, "address of the contact point"
37
+ attribute :address, :Address, ZERO_OR_ONE, "address of the contact point"
38
+ attribute :phone, String, ZERO_TO_MANY, "phone of the contact point"
39
+ attribute :email, String, ZERO_TO_MANY, "email of the contact point"
40
+ }
41
+
42
+ # TODO add contact links
43
+ # TODO add address
19
44
  }
@@ -0,0 +1,287 @@
1
+ require_relative 'agreement'
2
+ require 'csv'
3
+
4
+ SUPPLY_TEACHER_FRAMEWORK_ID = "ST"
5
+ SUPPLY_TEACHER_MANAGED_SERVICE_LOT_ID = "#{SUPPLY_TEACHER_FRAMEWORK_ID}.MS"
6
+ SUPPLY_TEACHER_RECRUITMENT_LOT_ID = "#{SUPPLY_TEACHER_FRAMEWORK_ID}.AG"
7
+
8
+
9
+ Agreements.new(:Supply_Teacher_Agreements) {
10
+ agreement {
11
+ kind :Framework
12
+ name "Supply Teachers framework"
13
+ id SUPPLY_TEACHER_FRAMEWORK_ID
14
+ fwk_number "RM1234" #TODO proper fwk number for Supply teachers
15
+ version "0.1.0"
16
+ description "This agreement is for the provision of Supply Teachers"
17
+ start_date date(2018, 10, 01) #TODO proper start date for Supply teachers
18
+ }
19
+
20
+ item_coding = lambda do
21
+ scheme_id :CPV; code "79610000/#{id}"; unit :Commission;
22
+ end
23
+
24
+ ST_ROLES = [:QT_NonSEN, :QT_SEN, :NQT_NonSEN, :NQT_SEN, :EdSup_NonSEN, :EdSup_SEN, :Senior, :Admin, :Nom, :FTA]
25
+ ST_DURATIONS = [:Up_to_1_week, :Between_1_and_12_weeks, :Over_12_weeks]
26
+
27
+ # Items common to both Lots
28
+ Common_ST_items = [
29
+ itemtype {
30
+ description "Qualified Teacher: Non-SEN Roles"
31
+ id :QT_NonSEN
32
+ include &item_coding
33
+ keyword "teacher"
34
+ keyword "non-sen"
35
+ },
36
+ itemtype {
37
+ description "Qualified Teacher: SEN Roles"
38
+ id :QT_SEN
39
+ include &item_coding
40
+ keyword "teacher"
41
+ keyword "sen teacher"
42
+ keyword "Special Education Needs"
43
+ keyword "Special Needs"
44
+ },
45
+ itemtype {
46
+ description "Unqualified Teacher: Non-SEN Roles"
47
+ id :NQT_NonSEN
48
+ self.instance_exec &item_coding
49
+ keyword "teacher"
50
+ keyword "non-sen"
51
+ },
52
+ itemtype {
53
+ description "Unqualified Teacher: SEN Roles"
54
+ id :NQT_SEN
55
+ self.instance_exec &item_coding
56
+ keyword "teaching assistant"
57
+ keyword "Special Education Needs"
58
+ keyword "Special Needs"
59
+ },
60
+ itemtype {
61
+ description "Educational Support Staff: Non-SEN roles (incl. Cover Supervisor and Teaching Assistant)"
62
+ id :EdSup_NonSEN
63
+ self.instance_exec &item_coding
64
+ },
65
+ itemtype {
66
+ description "Educational Support Staff: SEN roles (incl. Cover Supervisor and Teaching Assistant)"
67
+ id :EdSup_SEN
68
+ self.instance_exec &item_coding
69
+ },
70
+ itemtype {
71
+ description "Senior Roles"
72
+ description "Other Roles: Headteacher and Senior Leadership positions"
73
+ id :Senior
74
+ self.instance_exec &item_coding
75
+ },
76
+ itemtype {
77
+ description "Other Roles: (Admin & Clerical Staff, IT Staff, Finance Staff, Cleaners etc.)"
78
+ id :Admin
79
+ self.instance_exec &item_coding
80
+ },
81
+ itemtype {
82
+ description "Nominated Workers"
83
+ id :Nom
84
+ self.instance_exec &item_coding
85
+ },
86
+ itemtype {
87
+ description "Fixed Term Role (on School Payroll)"
88
+ id :FTA
89
+ self.instance_exec &item_coding
90
+ },
91
+ ]
92
+
93
+ agreement {
94
+ kind :Lot
95
+ id SUPPLY_TEACHER_RECRUITMENT_LOT_ID
96
+ name "Supply Teachers from Agency"
97
+ part_of_id SUPPLY_TEACHER_FRAMEWORK_ID
98
+ offerType "SupplyTeacherOfferings::ST_Offering"
99
+ version "0.1.0"
100
+ for item in Common_ST_items
101
+ item_type item
102
+ end
103
+
104
+ }
105
+
106
+ agreement {
107
+ kind :Lot
108
+ id SUPPLY_TEACHER_MANAGED_SERVICE_LOT_ID
109
+ name "Supply Teachers Managed Service"
110
+ part_of_id SUPPLY_TEACHER_FRAMEWORK_ID
111
+ offerType "SupplyTeacherOfferings::ST_Offering"
112
+ version "0.1.0"
113
+ for item in Common_ST_items
114
+ item_type item
115
+ end
116
+ }
117
+ }
118
+
119
+ domain(:SupplyTeacherOfferings) {
120
+
121
+ datatype(:ST_Offering, extends: Agreements::Offering,
122
+ description: " An offer for ST supply
123
+ The offerings look the same for both lots - since they both relate to the same items and data") {
124
+ attribute :commission, String, "The percentage the supplier charges for the item"
125
+ attribute :duration, Selection(*ST_DURATIONS)
126
+ attribute :branch_name, String, "branch name from which the offer is supplied"
127
+ attribute :branch_contact_id, String, "links to contact at the address", links: Parties::Contact
128
+ attribute :vendor_type, Selection("Master_Vendor", "Neutral_Vendor"), "for managed service offerings", links: Parties::Contact
129
+ }
130
+ }
131
+
132
+ def colmap_managing_suppliers(row)
133
+ {
134
+ :Vendor_type => row[0],
135
+ :Supplier => row[1],
136
+ :Item_ID => row[2],
137
+ :Job_Type => row[3],
138
+ :Up_to_1_week => row[4],
139
+ :Between_1_and_12_weeks => row[5],
140
+ :Over_12_weeks => row[6],
141
+ :Contact_information => row[7]
142
+ }
143
+ end
144
+
145
+ # expects CSV of the form
146
+ # 0 ,1 ,2 ,3 , 4 , 5 , 6 , 7
147
+ # Vendor type,Supplier ,Item ID,Job Type,Up_to_1_week,Between_1_and_12_weeks,Over_12_weeks,Contact information
148
+ # @param [Domain] parties - all the parties involved; method adds new suppliers if not present by namne
149
+ # @param [Object] filename - name of CSV file
150
+ def load_managing_suppliers filename, parties
151
+
152
+
153
+ # Load the suppliers from the file
154
+ lines = CSV.foreach(filename)
155
+ lines.each do |row|
156
+ col = colmap_managing_suppliers(row)
157
+
158
+ if col[:Vendor_type] != "Vendor type" then
159
+ name = col[:Supplier]
160
+ unless parties.contents[:party].find {|p| p.name == [name]}
161
+ parties.instance_exec do
162
+ party {
163
+ org_name name
164
+ id name # TODO will actually have to match this to a real supplier id, such as in SF
165
+ }
166
+ end
167
+ end
168
+ end
169
+ end
170
+
171
+ def time_options(item_type, colmap)
172
+ if (:Nom == item_type.id || :FTA == item_type.id)
173
+ return [:Over_12_weeks]
174
+ else
175
+ return [:Up_to_1_week, :Between_1_and_12_weeks, :Over_12_weeks]
176
+ end
177
+ end
178
+
179
+
180
+ SupplyTeacherOfferings.new :ST_ManagementOfferings do
181
+ lines.each do |row|
182
+ col = colmap_managing_suppliers(row)
183
+ if row[0] != "Vendor type" then
184
+ item_type = get_st_item (col[:Item_ID])
185
+ if (!item_type)
186
+ puts "Warning can't match item '#{col[:Item_ID]}"
187
+ next
188
+ end
189
+ for row_dur in time_options(item_type, col)
190
+ name = row[1]
191
+ st_offering do
192
+ name "#{name}-#{item_type.description}-#{row_dur}"
193
+ agreement_id SUPPLY_TEACHER_MANAGED_SERVICE_LOT_ID
194
+ supplier_id name # TODO will actually have to match this to a real supplier id, such as in SF
195
+ item do
196
+ type_id item_type.id
197
+ unit :Commission
198
+ value col[row_dur]
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
205
+
206
+ return SupplyTeacherOfferings::ST_ManagementOfferings
207
+ end
208
+
209
+ def col_for_supplier row
210
+ {
211
+ :Col_Supplier_Name => row[0],
212
+ :Col_Branch_Name => row[1],
213
+ :Col_Branch_Contact_name => row[2],
214
+ :Col_Address_1 => row[3],
215
+ :Col_Address_2 => row[4],
216
+ :Col_Town => row[5],
217
+ :Col_County => row[6],
218
+ :Col_Post_Code => row[7],
219
+ :Col_Branch_Contact_Name_Email_Address => row[8],
220
+ :Col_Branch_Telephone_Number => row[9],
221
+ :Col_Region => row[10],
222
+ [:QT_NonSEN, :Up_to_1_week] => row[11],
223
+ [:QT_NonSEN, :Between_1_and_12_weeks] => row[12],
224
+ [:QT_NonSEN, :Over_12_weeks] => row[13],
225
+ [:QT_SEN, :Up_to_1_week] => row[14],
226
+ [:QT_SEN, :Between_1_and_12_weeks] => row[15],
227
+ [:QT_SEN, :Over_12_weeks] => row[16],
228
+ [:NQT_NonSEN, :Up_to_1_week] => row[17],
229
+ [:NQT_NonSEN, :Between_1_and_12_weeks] => row[18],
230
+ [:NQT_NonSEN, :Over_12_weeks] => row[19],
231
+ [:NQT_SEN, :Up_to_1_week] => row[20],
232
+ [:NQT_SEN, :Between_1_and_12_weeks] => row[21],
233
+ [:NQT_SEN, :Over_12_weeks] => row[22],
234
+ [:EdSup_NonSEN, :Up_to_1_week] => row[23],
235
+ [:EdSup_NonSEN, :Between_1_and_12_weeks] => row[24],
236
+ [:EdSup_NonSEN, :Over_12_weeks] => row[25],
237
+ [:EdSup_SEN, :Up_to_1_week] => row[26],
238
+ [:EdSup_SEN, :Between_1_and_12_weeks] => row[27],
239
+ [:EdSup_SEN, :Over_12_weeks] => row[28],
240
+ [:Senior, :Up_to_1_week] => row[29],
241
+ [:Senior, :Between_1_and_12_weeks] => row[30],
242
+ [:Senior, :Over_12_weeks] => row[31],
243
+ [:Admin, :Up_to_1_week] => row[32],
244
+ [:Admin, :Between_1_and_12_weeks] => row[33],
245
+ [:Admin, :Over_12_weeks] => row[34],
246
+ [:Nom, :Nominated_workers] => row[35],
247
+ [:FTA, :Fixed_Term_Contract] => row[36]
248
+ }
249
+ end
250
+
251
+ # expects CSV of the form
252
+ # 0 Supplier Name,1 Branch Name/No.,2 Branch Contact name,3 Address 1,4 Address 2,5 Town,6 County,7 Post Code,8 Branch Contact Name Email Address,9 Branch Telephone Number,10 Region,11 QT_NonSEN/Up to 1 week,12 QT_NonSEN/Between 1 and 12 weeks,13 QT_NonSEN/Over 12 weeks,14 QT_SEN/Up to 1 week,15 QT_SEN/Between 1 and 12 weeks,16 QT_SEN/Over 12 weeks,17 NQT_NonSEN/Up to 1 week,18 NQT_NonSEN/Between 1 and 12 weeks,NQT_NonSEN/Over 12 weeks,NQT_SEN/Up to 1 week,NQT_SEN/Between 1 and 12 weeks,NQT_SEN/Over 12 weeks,EdSup_NonSEN/Up to 1 week,EdSup_NonSEN/Between 1 and 12 weeks,EdSup_NonSEN/Over 12 weeks,EdSup_SEN/Up to 1 week,EdSup_SEN/Between 1 and 12 weeks,EdSup_SEN/Over 12 weeks,Senior/Up to 1 week,Senior/Between 1 and 12 weeks,Senior/Over 12 weeks,Admin/Up to 1 week,Admin/Between 1 and 12 weeks,Over 12 weeks,Nom/Nominated workers,FTA/Fixed Term Contract (school payroll worker)
253
+ # @param [Domain] parties - all the parties involved; method adds new suppliers if not present by namne
254
+ # @param [Object] filename - name of CSV file
255
+ def load_recruitment_suppliers filename, parties
256
+
257
+ SupplyTeacherOfferings.new :ST_RecruitmentOfferings do
258
+ lines = CSV.foreach(filename)
259
+ lines.each do |row|
260
+ col = col_for_supplier row
261
+ if col[:Col_Supplier_Name] != "0 Supplier Name" then
262
+ for dur in ST_DURATIONS
263
+ for role in ST_ROLES
264
+ supplier = col[:Col_Supplier_Name]
265
+ st_offering do
266
+ name "#{supplier}-#{role}-#{dur}"
267
+ agreement_id SUPPLY_TEACHER_RECRUITMENT_LOT_ID
268
+ supplier_id supplier # TODO will actually have to match this to a real supplier id, such as in SF
269
+ item do
270
+ type_id role
271
+ unit :Commission
272
+ value col[[role, dur]]
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end
278
+ end
279
+ end
280
+
281
+ return SupplyTeacherOfferings::ST_RecruitmentOfferings
282
+ end
283
+
284
+
285
+ def get_st_item item_id
286
+ return Common_ST_items.find {|i| i.id.to_s == item_id}
287
+ end