metadata_presenter 1.5.0 → 1.7.2

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: aaea9e08a797416b551fd731c80db49d05f02f75f4ac61ecf68c5861d55af9a3
4
- data.tar.gz: e3938447849a5197cc3567c55caacdf0eea2e80e1e4eb05b42e175850d9b34cd
3
+ metadata.gz: 5ac5fd51e02d3200c324282f394e5d8d3b11e0e440592e627290dfd7b6c767ba
4
+ data.tar.gz: bced641e349533c820815867b08bfca21f7d8c0bbd619ed19615e424edcaa2a6
5
5
  SHA512:
6
- metadata.gz: 4f9ac675b0c24ca6ea9a1a9556b13a1151d499750e8aa16d3fbfc5981ac2a93cb99acc03939a188886eaf1254930bcdc1634fd44461b4345ec029a0243bd4d73
7
- data.tar.gz: bf12bf7eefe0f0376fe67801fa74507d0a8267c08106b8c0198023f09999a093090127d4f6b3e8360d2e570cc90a49a36f4263ed77b63dc2e56587ad605a95fd
6
+ metadata.gz: d38891660bc18dfe3472b43b84efc4a4752be370ba88decb17aad07aa47d779282214602bd52b10db043ad9aec8cc582e2010828deb8916f4b4b40fe069165d7
7
+ data.tar.gz: 289b34c930dd5c2322d6c32bb22d761c2d2f973e8ae8be7edd5104d3af2f429055ac5edbe76a56b9e78b63e48da5958e551f3e1b66a444eaa02572a41ee3febf
data/README.md CHANGED
@@ -83,3 +83,15 @@ service.
83
83
  ## Generate documentation
84
84
 
85
85
  Run `rake doc` and open the doc/index.html
86
+
87
+ ## Flow diagrams
88
+
89
+ You can generate flow diagrams calling a rake task:
90
+
91
+ ```
92
+ brew install graphviz
93
+ SERVICE_METADATA="some-form-metadata" rails metadata:flow
94
+ ```
95
+
96
+ This will generate an image with the flow for that metadata. Open that image
97
+ and profit!
@@ -28,6 +28,7 @@ module MetadataPresenter
28
28
  next_page = NextPage.new(
29
29
  service: service,
30
30
  session: session,
31
+ user_data: load_user_data,
31
32
  current_page_url: page_url
32
33
  ).find
33
34
 
@@ -1,7 +1,7 @@
1
1
  module MetadataPresenter
2
2
  class ChangeAnswerController < EngineController
3
3
  def create
4
- session[:return_to_check_you_answer] = true
4
+ session[:return_to_check_your_answer] = true
5
5
  redirect_to_page params[:url]
6
6
  end
7
7
  end
@@ -6,14 +6,19 @@ module MetadataPresenter
6
6
  default_form_builder GOVUKDesignSystemFormBuilder::FormBuilder
7
7
 
8
8
  def back_link
9
- return if @page.blank?
10
-
11
- previous_page = service.previous_page(
9
+ previous_page = PreviousPage.new(
10
+ service: service,
11
+ user_data: load_user_data,
12
12
  current_page: @page,
13
- referrer: request.referer
14
- )&.url
15
-
16
- @back_link ||= File.join(request.script_name, previous_page) if previous_page
13
+ referrer: request.referrer
14
+ ).page
15
+
16
+ if previous_page
17
+ @back_link ||= File.join(
18
+ request.script_name,
19
+ previous_page.url
20
+ )
21
+ end
17
22
  end
18
23
  helper_method :back_link
19
24
 
@@ -4,24 +4,32 @@ module MetadataPresenter
4
4
  attr_accessor :service, :flow, :user_data
5
5
 
6
6
  def page
7
- results = conditions.map do |condition|
8
- condition.criterias.map do |criteria|
7
+ evaluated_page_uuid = page_uuid || flow.default_next
8
+
9
+ service.find_page_by_uuid(evaluated_page_uuid)
10
+ end
11
+
12
+ def page_uuid
13
+ @results ||= conditions.map do |condition|
14
+ evaluated_criterias = condition.criterias.map do |criteria|
9
15
  criteria.service = service
10
16
 
11
- next unless Operator.new(
17
+ Operator.new(
12
18
  criteria.operator
13
- ).evaluate(criteria.field_label, user_data[criteria.criteria_component.id])
19
+ ).evaluate(
20
+ criteria.field_label,
21
+ user_data[criteria.criteria_component.id]
22
+ )
23
+ end
14
24
 
25
+ if condition.condition_type == 'or' && evaluated_criterias.any?
26
+ condition.next
27
+ elsif evaluated_criterias.all?
15
28
  condition.next
16
29
  end
17
30
  end
18
31
 
19
- page_uuid = results.flatten.uniq.compact
20
- if page_uuid.present?
21
- service.find_page_by_uuid(page_uuid.first)
22
- else
23
- service.find_page_by_uuid(flow.default_next)
24
- end
32
+ @results.flatten.compact.first
25
33
  end
26
34
 
27
35
  delegate :conditions, to: :flow
@@ -13,5 +13,9 @@ module MetadataPresenter
13
13
  Condition.new(condition_metadata)
14
14
  end
15
15
  end
16
+
17
+ def group_by_page
18
+ conditions.group_by(&:next)
19
+ end
16
20
  end
17
21
  end
@@ -1,10 +1,10 @@
1
1
  module MetadataPresenter
2
2
  class NextPage
3
3
  include ActiveModel::Model
4
- attr_accessor :service, :session, :current_page_url
4
+ attr_accessor :service, :session, :user_data, :current_page_url
5
5
 
6
6
  def find
7
- return check_answers_page if return_to_check_you_answer?
7
+ return check_answers_page if return_to_check_your_answer?
8
8
 
9
9
  if conditions?
10
10
  evaluate_conditions
@@ -18,12 +18,12 @@ module MetadataPresenter
18
18
  private
19
19
 
20
20
  def check_answers_page
21
- session[:return_to_check_you_answer] = nil
21
+ session[:return_to_check_your_answer] = nil
22
22
  service.pages.find { |page| page.type == 'page.checkanswers' }
23
23
  end
24
24
 
25
- def return_to_check_you_answer?
26
- session[:return_to_check_you_answer].present?
25
+ def return_to_check_your_answer?
26
+ session[:return_to_check_your_answer].present?
27
27
  end
28
28
 
29
29
  def conditions?
@@ -36,7 +36,7 @@ module MetadataPresenter
36
36
  EvaluateConditions.new(
37
37
  service: service,
38
38
  flow: next_flow,
39
- user_data: session[:user_data]
39
+ user_data: user_data
40
40
  ).page
41
41
  end
42
42
 
@@ -62,6 +62,10 @@ module MetadataPresenter
62
62
  components.select(&:upload?)
63
63
  end
64
64
 
65
+ def standalone?
66
+ type == 'page.standalone'
67
+ end
68
+
65
69
  private
66
70
 
67
71
  def to_components(node_components, collection:)
@@ -2,6 +2,7 @@ module MetadataPresenter
2
2
  class PageAnswers
3
3
  include ActiveModel::Model
4
4
  include ActiveModel::Validations
5
+ include ActionView::Helpers
5
6
  attr_reader :page, :answers, :uploaded_files
6
7
 
7
8
  def initialize(page, answers)
@@ -30,7 +31,7 @@ module MetadataPresenter
30
31
  elsif component && component.type == 'checkboxes'
31
32
  answers[method_name.to_s].to_a
32
33
  else
33
- answers[method_name.to_s]
34
+ sanitize(answers[method_name.to_s])
34
35
  end
35
36
  end
36
37
 
@@ -40,10 +41,10 @@ module MetadataPresenter
40
41
  return {} unless file_details
41
42
 
42
43
  if file_details.is_a?(Hash) || file_details.is_a?(ActionController::Parameters)
43
- file_details
44
+ file_details.merge('original_filename' => sanitize(file_details['original_filename']))
44
45
  else
45
46
  {
46
- 'original_filename' => file_details.original_filename,
47
+ 'original_filename' => sanitize(file_details.original_filename),
47
48
  'content_type' => file_details.content_type,
48
49
  'tempfile' => file_details.tempfile.path.to_s
49
50
  }
@@ -62,7 +63,7 @@ module MetadataPresenter
62
63
  GOVUKDesignSystemFormBuilder::Elements::Date::SEGMENTS[:month],
63
64
  GOVUKDesignSystemFormBuilder::Elements::Date::SEGMENTS[:year]
64
65
  ].map do |segment|
65
- answers["#{component_id}(#{segment})"]
66
+ sanitize(answers["#{component_id}(#{segment})"])
66
67
  end
67
68
  end
68
69
  end
@@ -0,0 +1,38 @@
1
+ module MetadataPresenter
2
+ class PreviousPage
3
+ include ActiveModel::Model
4
+ attr_accessor :service, :user_data, :current_page, :referrer
5
+
6
+ def page
7
+ # what happens when a user enters in the middle of the flow
8
+ return if no_current_or_referrer_pages? || service.no_back_link?(current_page)
9
+
10
+ if flow.present?
11
+ return referrer_page if return_to_referrer?
12
+
13
+ TraversedPages.new(service, user_data, current_page).last
14
+ else
15
+ service.previous_page(current_page: current_page, referrer: referrer)
16
+ end
17
+ end
18
+
19
+ def flow
20
+ service.metadata['flow']
21
+ end
22
+
23
+ private
24
+
25
+ def referrer_page
26
+ @referrer_page ||= service.find_page_by_url(URI(referrer).path)
27
+ end
28
+
29
+ def return_to_referrer?
30
+ current_page.standalone? ||
31
+ (referrer_page && referrer_page.standalone?)
32
+ end
33
+
34
+ def no_current_or_referrer_pages?
35
+ current_page.blank? || referrer.nil?
36
+ end
37
+ end
38
+ end
@@ -39,6 +39,8 @@ class MetadataPresenter::Service < MetadataPresenter::Metadata
39
39
  end
40
40
 
41
41
  def previous_page(current_page:, referrer:)
42
+ return if current_page.nil? || referrer.nil?
43
+
42
44
  unless no_back_link?(current_page)
43
45
  flow_page(current_page) || referrer_page(referrer)
44
46
  end
@@ -54,16 +56,16 @@ class MetadataPresenter::Service < MetadataPresenter::Metadata
54
56
  MetadataPresenter::Meta.new(configuration['meta'])
55
57
  end
56
58
 
59
+ def no_back_link?(current_page)
60
+ current_page == start_page || current_page == confirmation_page
61
+ end
62
+
57
63
  private
58
64
 
59
65
  def all_pages
60
66
  @all_pages ||= pages + standalone_pages
61
67
  end
62
68
 
63
- def no_back_link?(current_page)
64
- current_page == start_page || current_page == confirmation_page
65
- end
66
-
67
69
  def flow_page(current_page)
68
70
  page_index = pages.index(current_page)
69
71
  pages[page_index - 1] if page_index.present?
@@ -0,0 +1,40 @@
1
+ module MetadataPresenter
2
+ class TraversedPages
3
+ attr_reader :service, :user_data, :current_page
4
+
5
+ def initialize(service, user_data, current_page)
6
+ @service = service
7
+ @user_data = user_data
8
+ @pages = [service.start_page]
9
+ @current_page = current_page
10
+ end
11
+
12
+ delegate :last, to: :all
13
+
14
+ def all
15
+ page_uuid = service.start_page.uuid
16
+
17
+ service.metadata['flow'].size.times do
18
+ break if page_uuid == current_page.uuid
19
+
20
+ flow_object = service.flow(page_uuid)
21
+
22
+ if flow_object.branch?
23
+ page = EvaluateConditions.new(
24
+ service: service,
25
+ flow: flow_object,
26
+ user_data: user_data
27
+ ).page
28
+ page_uuid = page.uuid
29
+ else
30
+ page_uuid = flow_object.default_next
31
+ page = service.find_page_by_uuid(page_uuid)
32
+ end
33
+
34
+ @pages.push(page) if page && page.uuid != current_page.uuid
35
+ end
36
+
37
+ @pages
38
+ end
39
+ end
40
+ end
@@ -169,7 +169,7 @@
169
169
  "618b7537-b42b-4551-ae7d-053afa4d9ca9": {
170
170
  "_type": "branch",
171
171
  "next": {
172
- "default": "e337070b-f636-49a3-a65c-f506675265f0",
172
+ "default": "dc7454f9-4186-48d7-b055-684d57bbcdc7",
173
173
  "conditions": [
174
174
  {
175
175
  "condition_type": "if",
@@ -201,10 +201,146 @@
201
201
  "bc666714-c0a2-4674-afe5-faff2e20d847": {
202
202
  "_type": "page",
203
203
  "next": {
204
- "default": "e337070b-f636-49a3-a65c-f506675265f0"
204
+ "default": "dc7454f9-4186-48d7-b055-684d57bbcdc7"
205
205
  }
206
206
  },
207
207
  "e2887f44-5e8d-4dc0-b1de-496ab6039430": {
208
+ "_type": "page",
209
+ "next": {
210
+ "default": "dc7454f9-4186-48d7-b055-684d57bbcdc7"
211
+ }
212
+ },
213
+ "dc7454f9-4186-48d7-b055-684d57bbcdc7": {
214
+ "_type": "page",
215
+ "next": {
216
+ "default": "84a347fc-8d4b-486a-9996-6a86fa9544c5"
217
+ }
218
+ },
219
+ "84a347fc-8d4b-486a-9996-6a86fa9544c5": {
220
+ "_type": "branch",
221
+ "next": {
222
+ "default": "48357db5-7c06-4e85-94b1-5e1c9d8f39eb",
223
+ "conditions": [
224
+ {
225
+ "condition_type": "or",
226
+ "next": "2cc66e51-2c14-4023-86bf-ded49887cdb2",
227
+ "criterias": [
228
+ {
229
+ "operator": "is",
230
+ "page": "dc7454f9-4186-48d7-b055-684d57bbcdc7",
231
+ "component": "5ca01e56-94de-4c33-b5fc-74697e5b95cc",
232
+ "field": "bd14bf31-427e-4267-9926-a18c167fe604"
233
+ },
234
+ {
235
+ "operator": "is",
236
+ "page": "dc7454f9-4186-48d7-b055-684d57bbcdc7",
237
+ "component": "5ca01e56-94de-4c33-b5fc-74697e5b95cc",
238
+ "field": "eb720ef7-bcaf-4c73-8972-6fc80dca6245"
239
+ }
240
+ ]
241
+ },
242
+ {
243
+ "condition_type": "and",
244
+ "next": "f6c51f88-7be8-4cb7-bbfc-6c905727a051",
245
+ "criterias": [
246
+ {
247
+ "operator": "is",
248
+ "page": "68fbb180-9a2a-48f6-9da6-545e28b8d35a",
249
+ "component": "ac41be35-914e-4b22-8683-f5477716b7d4",
250
+ "field": "c5571937-9388-4411-b5fa-34ddf9bc4ca0"
251
+ },
252
+ {
253
+ "operator": "is",
254
+ "page": "dc7454f9-4186-48d7-b055-684d57bbcdc7",
255
+ "component": "5ca01e56-94de-4c33-b5fc-74697e5b95cc",
256
+ "field": "0c4192eb-1441-4f10-b00a-0f03b756269e"
257
+ }
258
+ ]
259
+ }
260
+ ]
261
+ }
262
+ },
263
+ "2cc66e51-2c14-4023-86bf-ded49887cdb2": {
264
+ "_type": "page",
265
+ "next": {
266
+ "default": "48357db5-7c06-4e85-94b1-5e1c9d8f39eb"
267
+ }
268
+ },
269
+ "f6c51f88-7be8-4cb7-bbfc-6c905727a051": {
270
+ "_type": "page",
271
+ "next": {
272
+ "default": "48357db5-7c06-4e85-94b1-5e1c9d8f39eb"
273
+ }
274
+ },
275
+ "48357db5-7c06-4e85-94b1-5e1c9d8f39eb": {
276
+ "_type": "page",
277
+ "next": {
278
+ "default": "1079b5b8-abd0-4bf6-aaac-1f01e69e3b39"
279
+ }
280
+ },
281
+ "1079b5b8-abd0-4bf6-aaac-1f01e69e3b39": {
282
+ "_type": "branch",
283
+ "next": {
284
+ "default": "941137d7-a1da-43fd-994a-98a4f9ea6d46",
285
+ "conditions": [
286
+ {
287
+ "condition_type": "and",
288
+ "next": "56e80942-d0a4-405a-85cd-bd1b100013d6",
289
+ "criterias": [
290
+ {
291
+ "operator": "is",
292
+ "page": "48357db5-7c06-4e85-94b1-5e1c9d8f39eb",
293
+ "component": "3a430712-a75f-4412-836d-4ec7b2cb1ac9",
294
+ "field": "30258b55-ec04-4278-86b7-8382cbd34bea"
295
+ },
296
+ {
297
+ "operator": "is",
298
+ "page": "48357db5-7c06-4e85-94b1-5e1c9d8f39eb",
299
+ "component": "3a430712-a75f-4412-836d-4ec7b2cb1ac9",
300
+ "field": "9339d86e-b53c-42c0-ab6e-1ab3e2340873"
301
+ },
302
+ {
303
+ "operator": "is",
304
+ "page": "48357db5-7c06-4e85-94b1-5e1c9d8f39eb",
305
+ "component": "3a430712-a75f-4412-836d-4ec7b2cb1ac9",
306
+ "field": "5d650e5d-57ac-42a1-9348-c1b93248753a"
307
+ }
308
+ ]
309
+ },
310
+ {
311
+ "condition_type": "or",
312
+ "next": "6324cca4-7770-4765-89b9-1cdc41f49c8b",
313
+ "criterias": [
314
+ {
315
+ "operator": "is",
316
+ "page": "48357db5-7c06-4e85-94b1-5e1c9d8f39eb",
317
+ "component": "3a430712-a75f-4412-836d-4ec7b2cb1ac9",
318
+ "field": "6b82b4e4-6dfd-4b3c-b6ed-2d71be0980bf"
319
+ },
320
+ {
321
+ "operator": "is",
322
+ "page": "48357db5-7c06-4e85-94b1-5e1c9d8f39eb",
323
+ "component": "3a430712-a75f-4412-836d-4ec7b2cb1ac9",
324
+ "field": "8c2c6161-7309-4e4f-9f3c-9fa629bcb459"
325
+ }
326
+ ]
327
+ }
328
+ ]
329
+ }
330
+ },
331
+ "941137d7-a1da-43fd-994a-98a4f9ea6d46": {
332
+ "_type": "page",
333
+ "next": {
334
+ "default": "e337070b-f636-49a3-a65c-f506675265f0"
335
+ }
336
+ },
337
+ "56e80942-d0a4-405a-85cd-bd1b100013d6": {
338
+ "_type": "page",
339
+ "next": {
340
+ "default": "e337070b-f636-49a3-a65c-f506675265f0"
341
+ }
342
+ },
343
+ "6324cca4-7770-4765-89b9-1cdc41f49c8b": {
208
344
  "_type": "page",
209
345
  "next": {
210
346
  "default": "e337070b-f636-49a3-a65c-f506675265f0"
@@ -420,6 +556,7 @@
420
556
  "url": "favourite-fruit",
421
557
  "_type": "page.singlequestion",
422
558
  "_uuid": "0b297048-aa4d-49b6-ac74-18e069118185",
559
+ "heading": "",
423
560
  "components": [
424
561
  {
425
562
  "_id": "favourite-fruit_radios_1",
@@ -463,6 +600,7 @@
463
600
  "url": "apple-juice",
464
601
  "_type": "page.singlequestion",
465
602
  "_uuid": "d4342dfd-0d09-4a91-a0ea-d7fd67e706cc",
603
+ "heading": "",
466
604
  "components": [
467
605
  {
468
606
  "_id": "apple-juice_radios_1",
@@ -499,6 +637,7 @@
499
637
  "url": "orange-juice",
500
638
  "_type": "page.singlequestion",
501
639
  "_uuid": "91e9f7c6-2f75-4b7d-9eb5-0cf352f7be66",
640
+ "heading": "",
502
641
  "components": [
503
642
  {
504
643
  "_id": "orange-juice_radios_1",
@@ -535,6 +674,7 @@
535
674
  "url": "favourite-band",
536
675
  "_type": "page.singlequestion",
537
676
  "_uuid": "05c3306c-0a39-42d2-9e0f-93fd49248f4e",
677
+ "heading": "",
538
678
  "components": [
539
679
  {
540
680
  "_id": "favourite-band_radios_1",
@@ -571,6 +711,7 @@
571
711
  "url": "music-app",
572
712
  "_type": "page.singlequestion",
573
713
  "_uuid": "8002df6e-29ab-4cdf-b520-1d7bb931a28f",
714
+ "heading": "",
574
715
  "components": [
575
716
  {
576
717
  "_id": "music-app_radios_1",
@@ -607,6 +748,7 @@
607
748
  "url": "best-formbuilder",
608
749
  "_type": "page.singlequestion",
609
750
  "_uuid": "ef2cafe3-37e2-4533-9b0c-09a970cd38d4",
751
+ "heading": "",
610
752
  "components": [
611
753
  {
612
754
  "_id": "best-formbuilder_radios_1",
@@ -773,6 +915,230 @@
773
915
  ],
774
916
  "section_heading": ""
775
917
  },
918
+ {
919
+ "_id": "page.marvel-series",
920
+ "url": "marvel-series",
921
+ "_type": "page.singlequestion",
922
+ "_uuid": "dc7454f9-4186-48d7-b055-684d57bbcdc7",
923
+ "heading": "",
924
+ "components": [
925
+ {
926
+ "_id": "marvel-series_radios_1",
927
+ "hint": "",
928
+ "name": "marvel-series_radios_1",
929
+ "_type": "radios",
930
+ "_uuid": "5ca01e56-94de-4c33-b5fc-74697e5b95cc",
931
+ "items": [
932
+ {
933
+ "_id": "marvel-series_radios_1_item_1",
934
+ "hint": "",
935
+ "_type": "radio",
936
+ "_uuid": "0c4192eb-1441-4f10-b00a-0f03b756269e",
937
+ "label": "WandaVision"
938
+ },
939
+ {
940
+ "_id": "marvel-series_radios_1_item_2",
941
+ "hint": "",
942
+ "_type": "radio",
943
+ "_uuid": "bd14bf31-427e-4267-9926-a18c167fe604",
944
+ "label": "The Falcon and the Winter Soldier"
945
+ },
946
+ {
947
+ "_id": "marvel-series_radios_1_item_2",
948
+ "hint": "",
949
+ "_type": "radio",
950
+ "_uuid": "eb720ef7-bcaf-4c73-8972-6fc80dca6245",
951
+ "label": "Loki"
952
+ },
953
+ {
954
+ "_id": "marvel-series_radios_1_item_3",
955
+ "hint": "",
956
+ "_type": "radio",
957
+ "_uuid": "d762e328-18dc-4861-bca8-d104609b8299",
958
+ "label": "Hawkeye"
959
+ }
960
+ ],
961
+ "errors": {},
962
+ "legend": "What is the best marvel series?",
963
+ "validation": {
964
+ "required": true
965
+ }
966
+ }
967
+ ]
968
+ },
969
+ {
970
+ "_id": "page.marvel-quotes",
971
+ "url": "marvel-quotes",
972
+ "body": "Marvel quotes",
973
+ "lede": "",
974
+ "_type": "page.content",
975
+ "_uuid": "2cc66e51-2c14-4023-86bf-ded49887cdb2",
976
+ "heading": "Loki",
977
+ "components": [
978
+ {
979
+ "_id": "marvel-quotes_content_1",
980
+ "name": "marvel-quotes_content_1",
981
+ "_type": "content",
982
+ "_uuid": "5cede2fa-5001-4d6a-965c-30addc3c4496",
983
+ "content": "I am Loki of Asgard, and I am burdened with glorious purpose."
984
+ },
985
+ {
986
+ "_id": "marvel-quotes_content_2",
987
+ "name": "marvel-quotes_content_2",
988
+ "_type": "content",
989
+ "_uuid": "816b6afd-4649-455a-b10d-62cb5acb4584",
990
+ "content": "So Who Are You Fighting Now ... Gandalf?"
991
+ }
992
+ ],
993
+ "section_heading": ""
994
+ },
995
+ {
996
+ "_id": "page.other-quotes",
997
+ "url": "other-quotes",
998
+ "body": "Other quotes",
999
+ "lede": "",
1000
+ "_type": "page.content",
1001
+ "_uuid": "f6c51f88-7be8-4cb7-bbfc-6c905727a051",
1002
+ "heading": "Other quotes",
1003
+ "components": [
1004
+ {
1005
+ "_id": "other-quotes_content_1",
1006
+ "name": "other-quotes_content_1",
1007
+ "_type": "content",
1008
+ "_uuid": "2535a867-deab-457b-91aa-c006e3c73d63",
1009
+ "content": "- How we doing? - Same as always... - That bad, huh?"
1010
+ },
1011
+ {
1012
+ "_id": "other-quotes_content_2",
1013
+ "name": "other-quotes_content_2",
1014
+ "_type": "content",
1015
+ "_uuid": "816b6afd-4649-455a-b10d-62cb5acb4584",
1016
+ "content": "Are you using your night vision, Vision?"
1017
+ }
1018
+ ],
1019
+ "section_heading": ""
1020
+ },
1021
+ {
1022
+ "_id": "page.best-arnold-quote",
1023
+ "url": "best-arnold-quote",
1024
+ "body": "Body section",
1025
+ "lede": "",
1026
+ "_type": "page.singlequestion",
1027
+ "_uuid": "48357db5-7c06-4e85-94b1-5e1c9d8f39eb",
1028
+ "heading": "Question",
1029
+ "components": [
1030
+ {
1031
+ "_id": "best-arnold-quote_checkboxes_1",
1032
+ "hint": "",
1033
+ "name": "best-arnold-quote_checkboxes_1",
1034
+ "_type": "checkboxes",
1035
+ "_uuid": "3a430712-a75f-4412-836d-4ec7b2cb1ac9",
1036
+ "items": [
1037
+ {
1038
+ "_id": "best-arnold-quote_checkboxes_1_item_1",
1039
+ "hint": "",
1040
+ "name": "best-arnold-quote_checkboxes_1",
1041
+ "_type": "checkbox",
1042
+ "_uuid": "30258b55-ec04-4278-86b7-8382cbd34bea",
1043
+ "label": "You are not you. You are me"
1044
+ },
1045
+ {
1046
+ "_id": "best-arnold-quote_checkboxes_1_item_2",
1047
+ "hint": "",
1048
+ "name": "best-arnold-quote_checkboxes_1",
1049
+ "_type": "checkbox",
1050
+ "_uuid": "9339d86e-b53c-42c0-ab6e-1ab3e2340873",
1051
+ "label": "Get to the chopper"
1052
+ },
1053
+ {
1054
+ "_id": "best-arnold-quote_checkboxes_1_item_3",
1055
+ "hint": "",
1056
+ "name": "best-arnold-quote_checkboxes_1",
1057
+ "_type": "checkbox",
1058
+ "_uuid": "5d650e5d-57ac-42a1-9348-c1b93248753a",
1059
+ "label": "You have been terminated"
1060
+ },
1061
+ {
1062
+ "_id": "best-arnold-quote_checkboxes_1_item_4",
1063
+ "hint": "",
1064
+ "name": "best-arnold-quote_checkboxes_1",
1065
+ "_type": "checkbox",
1066
+ "_uuid": "6b82b4e4-6dfd-4b3c-b6ed-2d71be0980bf",
1067
+ "label": "I am GROOT"
1068
+ },
1069
+ {
1070
+ "_id": "best-arnold-quote_checkboxes_1_item_5",
1071
+ "hint": "",
1072
+ "name": "best-arnold-quote_checkboxes_1",
1073
+ "_type": "checkbox",
1074
+ "_uuid": "8c2c6161-7309-4e4f-9f3c-9fa629bcb459",
1075
+ "label": "Dance Off, Bro."
1076
+ }
1077
+ ],
1078
+ "legend": "Select all Arnold Schwarzenegger quotes",
1079
+ "validation": {
1080
+ "required": true
1081
+ }
1082
+ }
1083
+ ]
1084
+ },
1085
+ {
1086
+ "_id": "page.arnold-right-answers",
1087
+ "url": "arnold-right-answers",
1088
+ "body": "You are right! Now, talk to the hand!",
1089
+ "lede": "",
1090
+ "_type": "page.content",
1091
+ "_uuid": "56e80942-d0a4-405a-85cd-bd1b100013d6",
1092
+ "heading": "You are right",
1093
+ "components": [
1094
+ {
1095
+ "_id": "arnold-right-answers_content_1",
1096
+ "name": "arnold-right-answers_content_1",
1097
+ "_type": "content",
1098
+ "_uuid": "9a7948f1-9e9a-4273-b52e-e2917c79f37c",
1099
+ "content": "You are right! Now, talk to the hand!"
1100
+ }
1101
+ ],
1102
+ "section_heading": ""
1103
+ },
1104
+ {
1105
+ "_id": "page.arnold-wrong-answers",
1106
+ "url": "arnold-wrong-answers",
1107
+ "body": "You are wrong! These are from the Guardians of the Galaxy!",
1108
+ "lede": "",
1109
+ "_type": "page.content",
1110
+ "_uuid": "6324cca4-7770-4765-89b9-1cdc41f49c8b",
1111
+ "heading": "You are wrong",
1112
+ "components": [
1113
+ {
1114
+ "_id": "arnold-wrong-answers_content_1",
1115
+ "name": "arnold-wrong-answers_content_1",
1116
+ "_type": "content",
1117
+ "_uuid": "3bd3ca1d-fed7-43b0-ab90-b4425b634203",
1118
+ "content": "You need to master the ability of standing so incredibly still, that you become invisible to the eye."
1119
+ }
1120
+ ],
1121
+ "section_heading": ""
1122
+ },
1123
+ {
1124
+ "_id": "page.arnold-incomplete-answers",
1125
+ "url": "arnold-incomplete-answers",
1126
+ "body": "You are wrong! The answers are incomplete.",
1127
+ "lede": "",
1128
+ "_type": "page.content",
1129
+ "_uuid": "941137d7-a1da-43fd-994a-98a4f9ea6d46",
1130
+ "heading": "You are wrong",
1131
+ "components": [
1132
+ {
1133
+ "_id": "arnold-incomplete-answers_content_1",
1134
+ "name": "arnold-incomplete-answers_content_1",
1135
+ "_type": "content",
1136
+ "_uuid": "16252a29-8e00-4247-b47f-3e94c863a540",
1137
+ "content": "The answers are incomplete. You need to remember Arnold saying 'I will be back'. So go back to answer correctly."
1138
+ }
1139
+ ],
1140
+ "section_heading": ""
1141
+ },
776
1142
  {
777
1143
  "_id": "page.check-answers",
778
1144
  "url": "check-answers",
@@ -820,7 +1186,7 @@
820
1186
  "created_by": "099d5bf5-5f7b-444c-86ee-9e189cc1a369",
821
1187
  "service_id": "488edccd-8411-4ffb-a38b-6a96c6ac28d6",
822
1188
  "version_id": "27dc30c9-f7b8-4dec-973a-bd153f6797df",
823
- "service_name": "Version Fixture",
1189
+ "service_name": "Branching Fixture",
824
1190
  "configuration": {
825
1191
  "meta": {
826
1192
  "_id": "config.meta",
@@ -1,3 +1,3 @@
1
1
  module MetadataPresenter
2
- VERSION = '1.5.0'.freeze
2
+ VERSION = '1.7.2'.freeze
3
3
  end
@@ -5,48 +5,85 @@ namespace :metadata do
5
5
 
6
6
  desc 'Represent the flow objects in human readable form'
7
7
  task flow: :environment do
8
- service = MetadataPresenter::Service.new(metadata_fixture('branching'))
9
-
10
- start_page = service.start_page
11
- flow = service.flow(start_page.uuid)
12
- first_page = service.find_page_by_uuid(flow.default_next)
13
-
14
- humanized_flow = {}
15
-
16
- humanized_flow[start_page.url] = {
17
- next: first_page.url
18
- }
19
-
20
- service.metadata['flow'].each do |id, _metadata|
21
- flow = service.flow(id)
22
-
23
- if flow.branch?
24
- page = service.find_page_by_uuid(flow.default_next)
25
- humanized_flow[page.url] = {
26
- conditions: flow.conditions.map do |condition|
27
- {
28
- condition_type: condition.condition_type,
29
- criterias: condition.criterias.map do |criteria|
30
- criteria.service = service
31
- {
32
- operator: criteria.operator,
33
- page: criteria.criteria_page.url,
34
- component: criteria.criteria_component.humanised_title,
35
- field: criteria.field_label
36
- }
37
- end
38
- }
8
+ require 'ruby-graphviz'
9
+ metadata = ENV['SERVICE_METADATA'] || metadata_fixture('branching')
10
+ service = MetadataPresenter::Service.new(metadata)
11
+
12
+ graph = MetadataPresenter::Graph.new(service)
13
+
14
+ graph.draw.generate_image
15
+ puts "Generated file #{graph.filename}"
16
+ system("open #{graph.filename}")
17
+ end
18
+ end
19
+
20
+ module MetadataPresenter
21
+ class Graph
22
+ attr_reader :service, :filename, :nodes
23
+
24
+ delegate :metadata, :start_page, :find_page_by_uuid, :service_slug, to: :service
25
+
26
+ def initialize(service)
27
+ @service = service
28
+ @graphviz = GraphViz.new(:G, type: :digraph)
29
+ @filename = Rails.root.join('tmp', "#{service_slug}.png")
30
+ @nodes = {}
31
+ end
32
+
33
+ def draw
34
+ draw_nodes
35
+ draw_edges
36
+
37
+ self
38
+ end
39
+
40
+ def generate_image
41
+ @graphviz.output(png: filename)
42
+ end
43
+
44
+ private
45
+
46
+ def draw_nodes
47
+ flow.each do |id, _value|
48
+ flow_object = service.flow(id)
49
+
50
+ if flow_object.branch?
51
+ full_description = flow_object.conditions.map.each_with_index do |condition, _index|
52
+ condition.criterias.map { |criteria|
53
+ criteria.service = service
54
+
55
+ "#{criteria.criteria_component.humanised_title} #{criteria.operator} #{criteria.field_label}"
56
+ }.join(" #{condition.condition_type} ")
57
+ end
58
+ nodes[id] = @graphviz.add_nodes(full_description.flatten.join(' / '))
59
+ else
60
+ current_page = find_page_by_uuid(id)
61
+ nodes[id] = @graphviz.add_nodes(current_page.url)
62
+ end
63
+ end
64
+ end
65
+
66
+ def draw_edges
67
+ flow.each do |id, _value|
68
+ flow_object = service.flow(id)
69
+ current_node = nodes[id]
70
+ node_next = nodes[flow_object.default_next]
71
+
72
+ if flow_object.branch?
73
+ @graphviz.add_edges(current_node, node_next, label: 'Conditions are not met', labelfontsize: 8) if node_next
74
+
75
+ flow_object.group_by_page.each do |page_uuid, _conditions|
76
+ conditions_node = nodes[page_uuid]
77
+ @graphviz.add_edges(current_node, conditions_node, label: 'Conditions are met', labelfontsize: 8) if conditions_node
39
78
  end
40
- }
41
- else
42
- page = service.find_page_by_uuid(id)
43
- next_page = service.find_page_by_uuid(flow.default_next)
44
- if next_page
45
- humanized_flow[page.url] = { next: next_page.url }
79
+ elsif node_next
80
+ @graphviz.add_edges(current_node, node_next)
46
81
  end
47
82
  end
48
83
  end
49
84
 
50
- pp humanized_flow
85
+ def flow
86
+ metadata['flow']
87
+ end
51
88
  end
52
89
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metadata_presenter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - MoJ Online
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-29 00:00:00.000000000 Z
11
+ date: 2021-07-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: govuk_design_system_formbuilder
@@ -128,6 +128,20 @@ dependencies:
128
128
  - - ">="
129
129
  - !ruby/object:Gem::Version
130
130
  version: '0'
131
+ - !ruby/object:Gem::Dependency
132
+ name: ruby-graphviz
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
131
145
  - !ruby/object:Gem::Dependency
132
146
  name: rspec-rails
133
147
  requirement: !ruby/object:Gem::Requirement
@@ -277,7 +291,9 @@ files:
277
291
  - app/models/metadata_presenter/offline_upload_adapter.rb
278
292
  - app/models/metadata_presenter/page.rb
279
293
  - app/models/metadata_presenter/page_answers.rb
294
+ - app/models/metadata_presenter/previous_page.rb
280
295
  - app/models/metadata_presenter/service.rb
296
+ - app/models/metadata_presenter/traversed_pages.rb
281
297
  - app/models/metadata_presenter/uploaded_file.rb
282
298
  - app/operators/metadata_presenter/base_operator.rb
283
299
  - app/operators/metadata_presenter/is_answered_operator.rb