lcp 0.1.0 → 0.2.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 +4 -4
- data/.claude/skills/lcp-custom-field/SKILL.md +10 -1
- data/.claude/skills/lcp-custom-field/agents/openai.yaml +4 -0
- data/.claude/skills/lcp-getting-started/SKILL.md +1 -1
- data/.claude/skills/lcp-getting-started/agents/openai.yaml +4 -0
- data/.claude/skills/lcp-host-binding/agents/openai.yaml +4 -0
- data/.claude/skills/lcp-model/agents/openai.yaml +4 -0
- data/.claude/skills/lcp-permissions/agents/openai.yaml +4 -0
- data/.claude/skills/lcp-presenter/agents/openai.yaml +4 -0
- data/.claude/skills/lcp-workflow/SKILL.md +10 -2
- data/.claude/skills/lcp-workflow/agents/openai.yaml +4 -0
- data/CHANGELOG.md +59 -1
- data/app/assets/javascripts/lcp_ruby/dev_toolbar.js +5 -6
- data/app/controllers/concerns/lcp_ruby/zone_resolution.rb +14 -1
- data/app/helpers/lcp_ruby/display/card_helper.rb +71 -16
- data/app/helpers/lcp_ruby/display_helper.rb +30 -3
- data/app/helpers/lcp_ruby/display_template_helper.rb +3 -3
- data/app/helpers/lcp_ruby/layout_helper.rb +49 -8
- data/app/helpers/lcp_ruby/tree_helper.rb +24 -3
- data/app/models/lcp_ruby/user.rb +10 -0
- data/app/views/layouts/lcp_ruby/application.html.erb +42 -20
- data/app/views/layouts/lcp_ruby/auth.html.erb +6 -1
- data/app/views/lcp_ruby/resources/_show_sections.html.erb +24 -17
- data/app/views/lcp_ruby/resources/_table_index.html.erb +12 -17
- data/app/views/lcp_ruby/widgets/_markdown.html.erb +13 -0
- data/app/views/lcp_ruby/widgets/_presenter_zone.html.erb +23 -7
- data/app/views/lcp_ruby/widgets/_rich_text.html.erb +25 -0
- data/docs/README.md +3 -0
- data/docs/feature-catalog.md +5 -2
- data/docs/feature-catalog.yml +89 -9
- data/docs/getting-started.md +38 -22
- data/docs/guides/dashboards.md +44 -1
- data/docs/guides/display-types.md +6 -0
- data/docs/guides/host-application.md +1 -1
- data/docs/guides/windows-setup.md +211 -0
- data/docs/guides/workflow.md +1 -1
- data/docs/reference/asset-pipeline.md +79 -0
- data/docs/reference/pages.md +50 -1
- data/docs/reference/presenters.md +6 -0
- data/docs/reference/workflow.md +2 -2
- data/examples/crm/Gemfile +3 -0
- data/examples/crm/Gemfile.lock +14 -9
- data/examples/crm/app/assets/config/manifest.js +4 -0
- data/examples/hr/Gemfile +3 -0
- data/examples/hr/Gemfile.lock +14 -9
- data/examples/hr/app/assets/config/manifest.js +4 -0
- data/examples/showcase/Gemfile +3 -0
- data/examples/showcase/Gemfile.lock +12 -9
- data/examples/showcase/app/assets/config/manifest.js +4 -0
- data/examples/showcase/config/lcp_ruby/pages/main_dashboard.yml +17 -2
- data/examples/showcase/config/locales/cs.yml +8 -0
- data/examples/showcase/config/locales/en.yml +8 -0
- data/examples/todo/Gemfile +3 -0
- data/examples/todo/Gemfile.lock +14 -9
- data/examples/todo/app/assets/config/manifest.js +4 -0
- data/exe/lcp +1 -1
- data/lib/generators/lcp_ruby/agent_setup_generator.rb +12 -9
- data/lib/generators/lcp_ruby/claude_skills_generator.rb +23 -19
- data/lib/generators/lcp_ruby/install_generator.rb +171 -3
- data/lib/generators/lcp_ruby/templates/agent_setup/agents_md.md +2 -1
- data/lib/generators/lcp_ruby/templates/agent_setup/claude_md.md +3 -2
- data/lib/generators/lcp_ruby/templates/install/menu.yml.tt +4 -0
- data/lib/generators/lcp_ruby/templates/monitoring/page.yml +5 -0
- data/lib/lcp_ruby/app_template.rb +37 -1
- data/lib/lcp_ruby/array_query.rb +33 -10
- data/lib/lcp_ruby/cli/new_command.rb +9 -4
- data/lib/lcp_ruby/cli/run_command.rb +98 -11
- data/lib/lcp_ruby/cli/skills_command.rb +27 -15
- data/lib/lcp_ruby/cli.rb +5 -1
- data/lib/lcp_ruby/configuration.rb +13 -2
- data/lib/lcp_ruby/custom_fields/applicator.rb +8 -0
- data/lib/lcp_ruby/custom_fields/query.rb +28 -20
- data/lib/lcp_ruby/display/markdown_sanitize.rb +36 -0
- data/lib/lcp_ruby/display/renderers/markdown.rb +6 -13
- data/lib/lcp_ruby/engine.rb +185 -1
- data/lib/lcp_ruby/export/data_generator.rb +5 -1
- data/lib/lcp_ruby/export/value_formatter.rb +34 -0
- data/lib/lcp_ruby/grouped_query/builder.rb +21 -0
- data/lib/lcp_ruby/metadata/configuration_validator.rb +5 -5
- data/lib/lcp_ruby/metadata/field_path.rb +57 -0
- data/lib/lcp_ruby/metadata/loader.rb +33 -1
- data/lib/lcp_ruby/metadata/menu_definition.rb +32 -2
- data/lib/lcp_ruby/metadata/menu_item.rb +53 -4
- data/lib/lcp_ruby/metadata/model_definition.rb +9 -0
- data/lib/lcp_ruby/metadata/zone_definition.rb +12 -5
- data/lib/lcp_ruby/metrics/json_query.rb +5 -0
- data/lib/lcp_ruby/model_factory/builder.rb +5 -0
- data/lib/lcp_ruby/model_factory/json_type_applicator.rb +35 -0
- data/lib/lcp_ruby/model_factory/label_method_builder.rb +3 -19
- data/lib/lcp_ruby/model_factory/schema_manager.rb +19 -0
- data/lib/lcp_ruby/pages/definition_validator.rb +13 -0
- data/lib/lcp_ruby/presenter/field_value_resolver.rb +36 -19
- data/lib/lcp_ruby/schemas/page.json +33 -1
- data/lib/lcp_ruby/search/custom_field_filter.rb +5 -0
- data/lib/lcp_ruby/skills_installer.rb +15 -2
- data/lib/lcp_ruby/tasks/doctor.rb +13 -8
- data/lib/lcp_ruby/version.rb +1 -1
- data/lib/lcp_ruby/widgets/data_resolver.rb +42 -1
- data/lib/lcp_ruby/widgets/date_grouper.rb +19 -0
- data/lib/lcp_ruby/workflow/contract_validator.rb +1 -1
- data/lib/lcp_ruby/workflow/model_source.rb +2 -2
- data/lib/lcp_ruby.rb +18 -0
- data/lib/tasks/lcp_ruby_db.rake +8 -1
- data/lib/tasks/lcp_ruby_i18n_check.rake +8 -0
- metadata +34 -24
- data/examples/crm/erd.md +0 -163
- data/examples/hr/erd.md +0 -396
- data/examples/showcase/erd.md +0 -567
- data/examples/todo/erd.md +0 -21
data/examples/hr/erd.md
DELETED
|
@@ -1,396 +0,0 @@
|
|
|
1
|
-
erDiagram
|
|
2
|
-
AuditLog {
|
|
3
|
-
string auditable_type "NOT NULL"
|
|
4
|
-
int auditable_id "NOT NULL"
|
|
5
|
-
string action "NOT NULL"
|
|
6
|
-
json changes_data
|
|
7
|
-
int user_id
|
|
8
|
-
json user_snapshot
|
|
9
|
-
json metadata
|
|
10
|
-
datetime created_at
|
|
11
|
-
}
|
|
12
|
-
Announcement {
|
|
13
|
-
string title "NOT NULL"
|
|
14
|
-
text body
|
|
15
|
-
string priority
|
|
16
|
-
bool published
|
|
17
|
-
datetime published_at
|
|
18
|
-
date expires_at
|
|
19
|
-
bool pinned
|
|
20
|
-
datetime created_at
|
|
21
|
-
datetime updated_at
|
|
22
|
-
integer organization_unit_id FK
|
|
23
|
-
}
|
|
24
|
-
Asset {
|
|
25
|
-
string name "NOT NULL"
|
|
26
|
-
string asset_tag "NOT NULL"
|
|
27
|
-
string asset_uuid
|
|
28
|
-
string category
|
|
29
|
-
string brand
|
|
30
|
-
string product_model
|
|
31
|
-
string serial_number
|
|
32
|
-
date purchase_date
|
|
33
|
-
float purchase_price
|
|
34
|
-
date warranty_until
|
|
35
|
-
string status
|
|
36
|
-
attachment photo
|
|
37
|
-
string notes
|
|
38
|
-
datetime created_at
|
|
39
|
-
datetime updated_at
|
|
40
|
-
}
|
|
41
|
-
AssetAssignment {
|
|
42
|
-
date assigned_at "NOT NULL"
|
|
43
|
-
date returned_at
|
|
44
|
-
string condition_on_assign
|
|
45
|
-
string condition_on_return
|
|
46
|
-
string notes
|
|
47
|
-
datetime created_at
|
|
48
|
-
datetime updated_at
|
|
49
|
-
integer asset_id FK
|
|
50
|
-
integer employee_id FK
|
|
51
|
-
}
|
|
52
|
-
Candidate {
|
|
53
|
-
string first_name "NOT NULL"
|
|
54
|
-
string last_name "NOT NULL"
|
|
55
|
-
string full_name
|
|
56
|
-
email email "NOT NULL"
|
|
57
|
-
phone phone
|
|
58
|
-
string status
|
|
59
|
-
string source
|
|
60
|
-
attachment resume
|
|
61
|
-
string cover_letter
|
|
62
|
-
int rating
|
|
63
|
-
text notes
|
|
64
|
-
string rejection_reason
|
|
65
|
-
datetime created_at
|
|
66
|
-
datetime updated_at
|
|
67
|
-
integer job_posting_id FK
|
|
68
|
-
}
|
|
69
|
-
CustomFieldDefinition {
|
|
70
|
-
string target_model "NOT NULL"
|
|
71
|
-
string field_name "NOT NULL"
|
|
72
|
-
string custom_type "NOT NULL"
|
|
73
|
-
string label "NOT NULL"
|
|
74
|
-
string description
|
|
75
|
-
string section
|
|
76
|
-
int position
|
|
77
|
-
bool active
|
|
78
|
-
bool required
|
|
79
|
-
string default_value
|
|
80
|
-
string placeholder
|
|
81
|
-
string hint
|
|
82
|
-
int min_length
|
|
83
|
-
int max_length
|
|
84
|
-
float min_value
|
|
85
|
-
float max_value
|
|
86
|
-
int precision
|
|
87
|
-
json enum_values
|
|
88
|
-
bool show_in_table
|
|
89
|
-
bool show_in_form
|
|
90
|
-
bool show_in_show
|
|
91
|
-
bool sortable
|
|
92
|
-
bool searchable
|
|
93
|
-
bool filterable
|
|
94
|
-
string input_type
|
|
95
|
-
string renderer
|
|
96
|
-
json renderer_options
|
|
97
|
-
string column_width
|
|
98
|
-
json extra_validations
|
|
99
|
-
json readable_by_roles
|
|
100
|
-
json writable_by_roles
|
|
101
|
-
datetime created_at
|
|
102
|
-
datetime updated_at
|
|
103
|
-
}
|
|
104
|
-
Dashboard {
|
|
105
|
-
int total_headcount
|
|
106
|
-
int active_employees
|
|
107
|
-
int open_positions
|
|
108
|
-
int pending_leave_requests
|
|
109
|
-
int pending_expense_claims
|
|
110
|
-
float avg_tenure_years
|
|
111
|
-
float turnover_rate_ytd
|
|
112
|
-
int upcoming_trainings
|
|
113
|
-
json headcount_by_unit
|
|
114
|
-
json leave_utilization
|
|
115
|
-
datetime created_at
|
|
116
|
-
datetime updated_at
|
|
117
|
-
}
|
|
118
|
-
Document {
|
|
119
|
-
string title "NOT NULL"
|
|
120
|
-
string category
|
|
121
|
-
string description
|
|
122
|
-
attachment file
|
|
123
|
-
bool confidential
|
|
124
|
-
date valid_from
|
|
125
|
-
date valid_until
|
|
126
|
-
datetime created_at
|
|
127
|
-
datetime updated_at
|
|
128
|
-
integer employee_id FK
|
|
129
|
-
}
|
|
130
|
-
Employee {
|
|
131
|
-
string first_name "NOT NULL"
|
|
132
|
-
string last_name "NOT NULL"
|
|
133
|
-
string full_name
|
|
134
|
-
email personal_email
|
|
135
|
-
email work_email "NOT NULL"
|
|
136
|
-
phone phone
|
|
137
|
-
date date_of_birth
|
|
138
|
-
date hire_date "NOT NULL"
|
|
139
|
-
date termination_date "NOT NULL"
|
|
140
|
-
string status
|
|
141
|
-
string employment_type
|
|
142
|
-
string gender
|
|
143
|
-
float salary
|
|
144
|
-
string currency
|
|
145
|
-
attachment photo
|
|
146
|
-
attachment cv
|
|
147
|
-
json address
|
|
148
|
-
json emergency_contact
|
|
149
|
-
text notes
|
|
150
|
-
datetime created_at
|
|
151
|
-
datetime updated_at
|
|
152
|
-
integer organization_unit_id FK
|
|
153
|
-
integer position_id FK
|
|
154
|
-
integer manager_id FK
|
|
155
|
-
}
|
|
156
|
-
EmployeeSkill {
|
|
157
|
-
string proficiency
|
|
158
|
-
bool certified
|
|
159
|
-
date certified_at
|
|
160
|
-
date expires_at
|
|
161
|
-
attachment certificate
|
|
162
|
-
datetime created_at
|
|
163
|
-
datetime updated_at
|
|
164
|
-
integer employee_id FK
|
|
165
|
-
integer skill_id FK
|
|
166
|
-
}
|
|
167
|
-
ExpenseClaim {
|
|
168
|
-
string title "NOT NULL"
|
|
169
|
-
string description
|
|
170
|
-
float amount "NOT NULL"
|
|
171
|
-
string currency
|
|
172
|
-
string category
|
|
173
|
-
string status
|
|
174
|
-
attachment receipt
|
|
175
|
-
date expense_date "NOT NULL"
|
|
176
|
-
datetime approved_at
|
|
177
|
-
string rejection_note
|
|
178
|
-
json items
|
|
179
|
-
datetime created_at
|
|
180
|
-
datetime updated_at
|
|
181
|
-
integer employee_id FK
|
|
182
|
-
integer approved_by_id FK
|
|
183
|
-
}
|
|
184
|
-
Goal {
|
|
185
|
-
string title "NOT NULL"
|
|
186
|
-
string description
|
|
187
|
-
string status
|
|
188
|
-
string priority
|
|
189
|
-
date due_date
|
|
190
|
-
int progress
|
|
191
|
-
int weight
|
|
192
|
-
int position
|
|
193
|
-
datetime created_at
|
|
194
|
-
datetime updated_at
|
|
195
|
-
integer employee_id FK
|
|
196
|
-
integer performance_review_id FK
|
|
197
|
-
}
|
|
198
|
-
Group {
|
|
199
|
-
string name "NOT NULL"
|
|
200
|
-
string code "NOT NULL"
|
|
201
|
-
string description
|
|
202
|
-
string group_type
|
|
203
|
-
bool active
|
|
204
|
-
datetime created_at
|
|
205
|
-
datetime updated_at
|
|
206
|
-
}
|
|
207
|
-
GroupMembership {
|
|
208
|
-
string role_in_group
|
|
209
|
-
date joined_at "NOT NULL"
|
|
210
|
-
date left_at
|
|
211
|
-
bool active
|
|
212
|
-
datetime created_at
|
|
213
|
-
datetime updated_at
|
|
214
|
-
integer group_id FK
|
|
215
|
-
integer employee_id FK
|
|
216
|
-
}
|
|
217
|
-
Interview {
|
|
218
|
-
string interview_type
|
|
219
|
-
datetime scheduled_at "NOT NULL"
|
|
220
|
-
int duration_minutes
|
|
221
|
-
string location
|
|
222
|
-
url meeting_url
|
|
223
|
-
string status
|
|
224
|
-
int rating
|
|
225
|
-
string feedback
|
|
226
|
-
string recommendation
|
|
227
|
-
json notes
|
|
228
|
-
datetime created_at
|
|
229
|
-
datetime updated_at
|
|
230
|
-
integer candidate_id FK
|
|
231
|
-
integer interviewer_id FK
|
|
232
|
-
}
|
|
233
|
-
JobPosting {
|
|
234
|
-
string title "NOT NULL"
|
|
235
|
-
text description
|
|
236
|
-
string status
|
|
237
|
-
string employment_type
|
|
238
|
-
string location
|
|
239
|
-
string remote_option
|
|
240
|
-
float salary_min
|
|
241
|
-
float salary_max
|
|
242
|
-
string currency
|
|
243
|
-
int headcount
|
|
244
|
-
datetime published_at
|
|
245
|
-
date closes_at
|
|
246
|
-
datetime created_at
|
|
247
|
-
datetime updated_at
|
|
248
|
-
integer organization_unit_id FK
|
|
249
|
-
integer position_id FK
|
|
250
|
-
integer hiring_manager_id FK
|
|
251
|
-
}
|
|
252
|
-
LeaveBalance {
|
|
253
|
-
int year "NOT NULL"
|
|
254
|
-
float total_days "NOT NULL"
|
|
255
|
-
float used_days
|
|
256
|
-
float remaining
|
|
257
|
-
datetime created_at
|
|
258
|
-
datetime updated_at
|
|
259
|
-
integer employee_id FK
|
|
260
|
-
integer leave_type_id FK
|
|
261
|
-
}
|
|
262
|
-
LeaveRequest {
|
|
263
|
-
date start_date "NOT NULL"
|
|
264
|
-
date end_date "NOT NULL"
|
|
265
|
-
float days_count
|
|
266
|
-
string status
|
|
267
|
-
string reason
|
|
268
|
-
string rejection_note
|
|
269
|
-
datetime approved_at
|
|
270
|
-
attachment attachment
|
|
271
|
-
datetime created_at
|
|
272
|
-
datetime updated_at
|
|
273
|
-
integer employee_id FK
|
|
274
|
-
integer leave_type_id FK
|
|
275
|
-
integer approved_by_id FK
|
|
276
|
-
}
|
|
277
|
-
LeaveType {
|
|
278
|
-
string name "NOT NULL"
|
|
279
|
-
string code "NOT NULL"
|
|
280
|
-
color color
|
|
281
|
-
int default_days
|
|
282
|
-
bool requires_approval
|
|
283
|
-
bool requires_document
|
|
284
|
-
bool active
|
|
285
|
-
int position
|
|
286
|
-
datetime created_at
|
|
287
|
-
datetime updated_at
|
|
288
|
-
}
|
|
289
|
-
OrganizationUnit {
|
|
290
|
-
string name "NOT NULL"
|
|
291
|
-
string code "NOT NULL"
|
|
292
|
-
string description
|
|
293
|
-
float budget
|
|
294
|
-
bool active
|
|
295
|
-
int parent_id
|
|
296
|
-
datetime created_at
|
|
297
|
-
datetime updated_at
|
|
298
|
-
integer head_id FK
|
|
299
|
-
}
|
|
300
|
-
PerformanceReview {
|
|
301
|
-
string review_period
|
|
302
|
-
int year "NOT NULL"
|
|
303
|
-
string status
|
|
304
|
-
int self_rating
|
|
305
|
-
int manager_rating
|
|
306
|
-
int overall_rating
|
|
307
|
-
string self_comments
|
|
308
|
-
string manager_comments
|
|
309
|
-
string goals_summary
|
|
310
|
-
string strengths
|
|
311
|
-
string improvements
|
|
312
|
-
datetime completed_at
|
|
313
|
-
datetime created_at
|
|
314
|
-
datetime updated_at
|
|
315
|
-
integer employee_id FK
|
|
316
|
-
integer reviewer_id FK
|
|
317
|
-
}
|
|
318
|
-
Position {
|
|
319
|
-
string title "NOT NULL"
|
|
320
|
-
string code "NOT NULL"
|
|
321
|
-
int level
|
|
322
|
-
float min_salary
|
|
323
|
-
float max_salary
|
|
324
|
-
bool active
|
|
325
|
-
int parent_id
|
|
326
|
-
int position
|
|
327
|
-
datetime created_at
|
|
328
|
-
datetime updated_at
|
|
329
|
-
}
|
|
330
|
-
Skill {
|
|
331
|
-
string name "NOT NULL"
|
|
332
|
-
string description
|
|
333
|
-
string category
|
|
334
|
-
int parent_id
|
|
335
|
-
datetime created_at
|
|
336
|
-
datetime updated_at
|
|
337
|
-
}
|
|
338
|
-
TrainingCourse {
|
|
339
|
-
string title "NOT NULL"
|
|
340
|
-
text description
|
|
341
|
-
string category
|
|
342
|
-
string format
|
|
343
|
-
float duration_hours
|
|
344
|
-
int max_participants
|
|
345
|
-
string instructor
|
|
346
|
-
string location
|
|
347
|
-
url url
|
|
348
|
-
datetime starts_at
|
|
349
|
-
datetime ends_at
|
|
350
|
-
bool active
|
|
351
|
-
datetime created_at
|
|
352
|
-
datetime updated_at
|
|
353
|
-
}
|
|
354
|
-
TrainingEnrollment {
|
|
355
|
-
string status
|
|
356
|
-
datetime completed_at
|
|
357
|
-
int score
|
|
358
|
-
string feedback
|
|
359
|
-
attachment certificate
|
|
360
|
-
datetime created_at
|
|
361
|
-
datetime updated_at
|
|
362
|
-
integer employee_id FK
|
|
363
|
-
integer training_course_id FK
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
Announcement }o--|| OrganizationUnit : "organization_unit"
|
|
367
|
-
AssetAssignment }|--|| Asset : "asset"
|
|
368
|
-
AssetAssignment }|--|| Employee : "employee"
|
|
369
|
-
Candidate }|--|| JobPosting : "job_posting"
|
|
370
|
-
Document }|--|| Employee : "employee"
|
|
371
|
-
Employee }|--|| OrganizationUnit : "organization_unit"
|
|
372
|
-
Employee }|--|| Position : "position"
|
|
373
|
-
Employee }o--|| Employee : "manager"
|
|
374
|
-
EmployeeSkill }|--|| Employee : "employee"
|
|
375
|
-
EmployeeSkill }|--|| Skill : "skill"
|
|
376
|
-
ExpenseClaim }|--|| Employee : "employee"
|
|
377
|
-
ExpenseClaim }o--|| Employee : "approved_by"
|
|
378
|
-
Goal }|--|| Employee : "employee"
|
|
379
|
-
Goal }o--|| PerformanceReview : "performance_review"
|
|
380
|
-
GroupMembership }|--|| Group : "group"
|
|
381
|
-
GroupMembership }|--|| Employee : "employee"
|
|
382
|
-
Interview }|--|| Candidate : "candidate"
|
|
383
|
-
Interview }|--|| Employee : "interviewer"
|
|
384
|
-
JobPosting }|--|| OrganizationUnit : "organization_unit"
|
|
385
|
-
JobPosting }|--|| Position : "position"
|
|
386
|
-
JobPosting }|--|| Employee : "hiring_manager"
|
|
387
|
-
LeaveBalance }|--|| Employee : "employee"
|
|
388
|
-
LeaveBalance }|--|| LeaveType : "leave_type"
|
|
389
|
-
LeaveRequest }|--|| Employee : "employee"
|
|
390
|
-
LeaveRequest }|--|| LeaveType : "leave_type"
|
|
391
|
-
LeaveRequest }o--|| Employee : "approved_by"
|
|
392
|
-
OrganizationUnit }o--|| Employee : "head"
|
|
393
|
-
PerformanceReview }|--|| Employee : "employee"
|
|
394
|
-
PerformanceReview }|--|| Employee : "reviewer"
|
|
395
|
-
TrainingEnrollment }|--|| Employee : "employee"
|
|
396
|
-
TrainingEnrollment }|--|| TrainingCourse : "training_course"
|