rocketjob_mission_control 5.0.0 → 6.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/Rakefile +1 -2
  4. data/app/assets/config/manifest.js +3 -0
  5. data/app/assets/fonts/glyphicons-halflings-regular.eot +0 -0
  6. data/app/assets/fonts/glyphicons-halflings-regular.svg +288 -0
  7. data/app/assets/fonts/glyphicons-halflings-regular.ttf +0 -0
  8. data/app/assets/fonts/glyphicons-halflings-regular.woff +0 -0
  9. data/app/assets/fonts/glyphicons-halflings-regular.woff2 +0 -0
  10. data/app/assets/javascripts/rocket_job_mission_control/application.js +10 -11
  11. data/app/assets/javascripts/rocket_job_mission_control/base.js +1 -1
  12. data/app/assets/javascripts/rocket_job_mission_control/bootstrap.min.js +6 -0
  13. data/app/assets/javascripts/rocket_job_mission_control/{datatable.js → datatable_init.js} +0 -0
  14. data/{vendor/assets/javascripts → app/assets/javascripts/rocket_job_mission_control}/datatables.min.js +0 -0
  15. data/app/assets/javascripts/rocket_job_mission_control/dirmon_entries.js +1 -1
  16. data/app/assets/javascripts/rocket_job_mission_control/jquery-3.5.1.min.js +2 -0
  17. data/app/assets/javascripts/rocket_job_mission_control/nested_fields.js +112 -0
  18. data/app/assets/javascripts/rocket_job_mission_control/selectize.min.js +3 -0
  19. data/app/assets/javascripts/rocket_job_mission_control/selectize_init.js +6 -0
  20. data/app/assets/stylesheets/rocket_job_mission_control/{application.scss → application.css} +9 -8
  21. data/app/assets/stylesheets/rocket_job_mission_control/base.css +420 -0
  22. data/app/assets/stylesheets/rocket_job_mission_control/bootstrap.min.css.erb +6 -0
  23. data/app/assets/stylesheets/rocket_job_mission_control/bootstrap.min.css.map +1 -0
  24. data/app/assets/stylesheets/rocket_job_mission_control/{callout.scss → callout.css} +50 -52
  25. data/{vendor/assets/stylesheets → app/assets/stylesheets/rocket_job_mission_control}/datatables.min.css +0 -0
  26. data/{vendor/assets/stylesheets → app/assets/stylesheets/rocket_job_mission_control}/fontawesome-all.min.css.erb +0 -0
  27. data/app/assets/stylesheets/rocket_job_mission_control/jobs.css +56 -0
  28. data/app/assets/stylesheets/rocket_job_mission_control/selectize.bootstrap3.css +1 -0
  29. data/app/assets/stylesheets/rocket_job_mission_control/selectize.css +19 -0
  30. data/app/assets/stylesheets/rocket_job_mission_control/worker_processes.css +7 -0
  31. data/app/controllers/rocket_job_mission_control/application_controller.rb +3 -1
  32. data/app/controllers/rocket_job_mission_control/dirmon_entries_controller.rb +23 -23
  33. data/app/controllers/rocket_job_mission_control/jobs_controller.rb +50 -7
  34. data/app/controllers/rocket_job_mission_control/servers_controller.rb +7 -7
  35. data/app/datatables/rocket_job_mission_control/dirmon_entries_datatable.rb +1 -1
  36. data/app/datatables/rocket_job_mission_control/jobs_datatable.rb +1 -1
  37. data/app/helpers/rocket_job_mission_control/application_helper.rb +51 -26
  38. data/app/helpers/rocket_job_mission_control/dirmon_entries_helper.rb +12 -0
  39. data/app/helpers/rocket_job_mission_control/jobs_helper.rb +76 -8
  40. data/app/helpers/rocket_job_mission_control/servers_helper.rb +4 -0
  41. data/app/models/rocket_job_mission_control/dirmon_sanitizer.rb +68 -0
  42. data/app/models/rocket_job_mission_control/job_sanitizer.rb +37 -0
  43. data/app/views/layouts/rocket_job_mission_control/application.html.erb +2 -2
  44. data/app/views/layouts/rocket_job_mission_control/partials/_flash.html.erb +4 -4
  45. data/app/views/rocket_job_mission_control/dirmon_entries/_form.html.erb +78 -42
  46. data/app/views/rocket_job_mission_control/dirmon_entries/_input_categories.html.erb +59 -0
  47. data/app/views/rocket_job_mission_control/dirmon_entries/_input_category_fields.html.erb +56 -0
  48. data/app/views/rocket_job_mission_control/dirmon_entries/_output_categories.html.erb +44 -0
  49. data/app/views/rocket_job_mission_control/dirmon_entries/_output_category_fields.html.erb +36 -0
  50. data/app/views/rocket_job_mission_control/dirmon_entries/_status.html.erb +22 -16
  51. data/app/views/rocket_job_mission_control/dirmon_entries/copy.html.erb +1 -1
  52. data/app/views/rocket_job_mission_control/dirmon_entries/edit.html.erb +7 -3
  53. data/app/views/rocket_job_mission_control/dirmon_entries/show.html.erb +2 -3
  54. data/app/views/rocket_job_mission_control/jobs/_attributes.html.erb +28 -0
  55. data/app/views/rocket_job_mission_control/jobs/_dates.html.erb +35 -0
  56. data/app/views/rocket_job_mission_control/jobs/_details.html.erb +125 -0
  57. data/app/views/rocket_job_mission_control/jobs/_exception.html.erb +22 -0
  58. data/app/views/rocket_job_mission_control/jobs/_input_categories.html.erb +76 -0
  59. data/app/views/rocket_job_mission_control/jobs/_input_category_fields.html.erb +56 -0
  60. data/app/views/rocket_job_mission_control/jobs/_output_categories.html.erb +49 -0
  61. data/app/views/rocket_job_mission_control/jobs/_output_category_fields.html.erb +36 -0
  62. data/app/views/rocket_job_mission_control/jobs/_retryable.html.erb +16 -0
  63. data/app/views/rocket_job_mission_control/jobs/_status.html.erb +20 -48
  64. data/app/views/rocket_job_mission_control/jobs/edit.html.erb +28 -0
  65. data/app/views/rocket_job_mission_control/jobs/edit_slice.html.erb +2 -2
  66. data/app/views/rocket_job_mission_control/jobs/exception.html.erb +1 -1
  67. data/app/views/rocket_job_mission_control/jobs/index.html.erb +24 -18
  68. data/app/views/rocket_job_mission_control/jobs/show.html.erb +32 -58
  69. data/app/views/rocket_job_mission_control/jobs/view_slice.html.erb +6 -4
  70. data/config/initializers/assets.rb +3 -4
  71. data/lib/rocket_job_mission_control/engine.rb +2 -2
  72. data/lib/rocket_job_mission_control/version.rb +1 -1
  73. data/test/controllers/rocket_job_mission_control/application_controller_test.rb +4 -12
  74. data/test/controllers/rocket_job_mission_control/dirmon_entries_controller_test.rb +20 -52
  75. data/test/controllers/rocket_job_mission_control/jobs_controller_test.rb +21 -41
  76. data/test/controllers/rocket_job_mission_control/servers_controller_test.rb +3 -3
  77. data/test/models/rocket_job_mission_control/dirmon_sanitizer_test.rb +146 -0
  78. data/test/models/rocket_job_mission_control/job_sanitizer_test.rb +9 -3
  79. data/test/test_helper.rb +6 -12
  80. metadata +73 -53
  81. data/app/assets/javascripts/rocket_job_mission_control/selectize.js +0 -8
  82. data/app/assets/stylesheets/rocket_job_mission_control/base.scss +0 -436
  83. data/app/assets/stylesheets/rocket_job_mission_control/bootstrap_and_overrides.scss +0 -488
  84. data/app/assets/stylesheets/rocket_job_mission_control/jobs.scss +0 -72
  85. data/app/assets/stylesheets/rocket_job_mission_control/worker_processes.scss +0 -9
  86. data/vendor/assets/javascripts/bootstrap.min.js +0 -7
  87. data/vendor/assets/javascripts/microplugin.js +0 -135
  88. data/vendor/assets/javascripts/selectize.js +0 -3058
  89. data/vendor/assets/javascripts/sifter.js +0 -470
  90. data/vendor/assets/stylesheets/bootstrap.min.css +0 -6
  91. data/vendor/assets/stylesheets/bootstrap.min.css.map +0 -1
  92. data/vendor/assets/stylesheets/selectize.bootstrap3.css +0 -408
  93. data/vendor/assets/stylesheets/selectize.css +0 -317
@@ -56,8 +56,16 @@ module RocketJobMissionControl
56
56
  values
57
57
  end
58
58
 
59
+ def escape(s)
60
+ s.dump[1..-2]
61
+ end
62
+
63
+ def unescape(s)
64
+ "\"#{s}\"".undump
65
+ end
66
+
59
67
  # Returns the editable field as html for use in editing dynamic fields from a Job class.
60
- def editable_field_html(klass, field_name, value, f, include_nil_selectors = false)
68
+ def editable_field_html(klass, field_name, value, f)
61
69
  # When editing a job the values are of the correct type.
62
70
  # When editing a dirmon entry values are strings.
63
71
  field = klass.fields[field_name.to_s]
@@ -67,44 +75,61 @@ module RocketJobMissionControl
67
75
  placeholder = nil if placeholder.is_a?(Proc)
68
76
 
69
77
  case field.type.name
70
- when "Symbol", "String", "Integer"
78
+ when "Integer"
79
+ options = extract_inclusion_values(klass, field_name)
80
+ f.number_field(field_name, in: options, include_blank: false, value: value, class: "form-control", placeholder: placeholder)
81
+ when "String", "Symbol", "Mongoid::StringifiedSymbol"
71
82
  options = extract_inclusion_values(klass, field_name)
72
- str = "[#{field.type.name}]\n".html_safe
73
83
  if options
74
- str + f.select(field_name, options, {include_blank: options.include?(nil) || include_nil_selectors, selected: value}, {class: "selectize form-control"})
84
+ f.select(field_name, options, {include_blank: options.include?(nil), selected: value}, {class: "selectize form-control"})
75
85
  else
76
- if field.type.name == "Integer"
77
- str + f.number_field(field_name, value: value, class: "form-control", placeholder: placeholder)
78
- else
79
- str + f.text_field(field_name, value: value, class: "form-control", placeholder: placeholder)
80
- end
86
+ f.text_area(field_name, value: value ? value : "", class: "form-control", placeholder: placeholder)
81
87
  end
88
+ when "Boolean", "Mongoid::Boolean"
89
+ options = extract_inclusion_values(klass, field_name) || [nil, "true", "false"]
90
+ f.select(field_name, options, {include_blank: options.include?(nil), selected: value}, {class: "selectize form-control"})
82
91
  when "Hash"
83
92
  "[JSON Hash]\n".html_safe +
84
93
  f.text_field(field_name, value: value ? value.to_json : "", class: "form-control", placeholder: '{"key1":"value1", "key2":"value2", "key3":"value3"}')
85
94
  when "Array"
86
95
  options = Array(value)
87
- "[Array]\n".html_safe +
88
- f.select(field_name, options_for_select(options, options), {include_hidden: false}, {class: "selectize form-control", multiple: true})
89
- when "Mongoid::Boolean"
90
- name = "#{field_name}_true".to_sym
91
- value = value.to_s
92
- str = '<div class="radio-buttons">'.html_safe
93
- str << f.radio_button(field_name, "true", checked: value == "true")
94
- str << " ".html_safe + f.label(name, "true")
95
- str << " ".html_safe + f.radio_button(field_name, "false", checked: value == "false")
96
- str << " ".html_safe + f.label(name, "false")
97
- # Allow this field to be unset (nil).
98
- if include_nil_selectors
99
- str << " ".html_safe + f.radio_button(field_name, "", checked: value == "")
100
- str << " ".html_safe + f.label(name, "nil")
101
- end
102
-
103
- str << "</div>".html_safe
96
+ f.text_field(field_name, {class: "selectize form-control", multiple: true})
104
97
  else
105
98
  "[#{field.type.name}]".html_safe +
106
99
  f.text_field(field_name, value: value, class: "form-control", placeholder: placeholder)
107
100
  end
108
101
  end
102
+
103
+ # This method creates a link with `data-id` `data-fields` attributes. These attributes are used to create new instances of the nested fields through Javascript.
104
+ def link_to_add_fields(name, f, association, option)
105
+ # Takes an object (@job) and creates a new instance of its associated model (:properties)
106
+ new_object = f.object.send(association).klass.new
107
+
108
+ # Saves the unique ID of the object into a variable.
109
+ # This is needed to ensure the key of the associated array is unique. This is makes parsing the content in the `data-fields` attribute easier through Javascript.
110
+ # We could use another method to achive this.
111
+ id = new_object.object_id
112
+
113
+ # https://api.rubyonrails.org/ fields_for(record_name, record_object = nil, fields_options = {}, &block)
114
+ # record_name = :addresses
115
+ # record_object = new_object
116
+ # fields_options = { child_index: id }
117
+ # child_index` is used to ensure the key of the associated array is unique, and that it matched the value in the `data-id` attribute.
118
+ # `person[addresses_attributes][child_index_value][_destroy]`
119
+ fields = f.fields_for(association, new_object, child_index: id) do |builder|
120
+ # `association.to_s.singularize + "_fields"` ends up evaluating to `address_fields`
121
+ # The render function will then look for `views/people/_address_fields.html.erb`
122
+ # The render function also needs to be passed the value of 'builder', because `views/dirmon_entries/_input_categories.html.erb` needs this to render the form tags.
123
+ render(association.to_s.singularize + "_fields", f: builder)
124
+ end
125
+
126
+ # This renders a simple link, but passes information into `data` attributes.
127
+ # This info can be named anything we want, but in this case we chose `data-id:` and `data-fields:`.
128
+ # The `id:` is from `new_object.object_id`.
129
+ # The `fields:` are rendered from the `fields` blocks.
130
+ # We use `gsub("\n", "")` to remove anywhite space from the rendered partial.
131
+ # The `id:` value needs to match the value used in `child_index: id`.
132
+ link_to(name, '#', class: "add_fields btn btn-#{option}", data: { id: id, fields: fields.gsub("\n", "") })
133
+ end
109
134
  end
110
135
  end
@@ -3,5 +3,17 @@ module RocketJobMissionControl
3
3
  def dirmon_counts_by_state(state)
4
4
  RocketJob::DirmonEntry.counts_by_state.fetch(state.downcase.to_sym, 0)
5
5
  end
6
+
7
+
8
+ def dirmon_entry_find_category(categories, category_name = :main)
9
+ return unless categories
10
+
11
+ categories.each { |category| return category if category_name == (category["name"] || :main).to_sym }
12
+ nil
13
+ end
14
+
15
+ def rocket_job_mission_control
16
+ @@rocket_job_mission_control_engine_url_helpers ||= RocketJobMissionControl::Engine.routes.url_helpers
17
+ end
6
18
  end
7
19
  end
@@ -1,5 +1,23 @@
1
1
  module RocketJobMissionControl
2
2
  module JobsHelper
3
+ # The fields that RJMC already displays, all other will be rendered under the custom section.
4
+ DISPLAYED_FIELDS = %w[
5
+ _id _type
6
+ completed_at created_at cron_schedule
7
+ description destroy_on_complete download_encryption
8
+ exception expires_at
9
+ failed_at_list failure_count
10
+ input_categories
11
+ log_level
12
+ output_categories output_path
13
+ percent_complete priority
14
+ record_count retry_limit run_at
15
+ started_at state statistics sub_state
16
+ throttle_group throttle_running_workers
17
+ upload_file_name
18
+ worker_name
19
+ ]
20
+
3
21
  def job_icon(job)
4
22
  state = job_state(job)
5
23
  state_icon(state)
@@ -15,6 +33,47 @@ module RocketJobMissionControl
15
33
  end
16
34
  end
17
35
 
36
+ def job_state_name(job)
37
+ job_state(@job).to_s.camelcase
38
+ end
39
+
40
+ def job_time(time)
41
+ return "" unless time
42
+ time.in_time_zone(Time.zone)
43
+ end
44
+
45
+ def job_state_time(job)
46
+ return job_time(job.run_at) if job.scheduled?
47
+
48
+ job_time(job.completed_at || job.started_at || job.created_at )
49
+ # job_time(job.running? ? job.started_at : job.completed_at)
50
+ end
51
+
52
+ def job_estimated_time_left(job)
53
+ if job.record_count && job.running? && job.record_count.positive?
54
+ percent = job.percent_complete
55
+ if percent >= 5
56
+ secs = job.seconds.to_f
57
+ RocketJob.seconds_as_duration((((secs / percent) * 100) - secs))
58
+ end
59
+ end
60
+ end
61
+
62
+ def job_records_per_hour(job)
63
+ return unless job.completed?
64
+
65
+ secs = job.seconds.to_f
66
+ ((job.record_count.to_f / secs) * 60 * 60).round if job.record_count&.positive? && (secs > 0.0)
67
+ end
68
+
69
+ def job_custom_fields(job)
70
+ attrs = job.attributes.dup
71
+ DISPLAYED_FIELDS.each { |key| attrs.delete(key) }
72
+ # Convert time zones for any custom time fields
73
+ attrs.keys { |key| attrs[key] = attrs[key].in_time_zone(Time.zone) if attrs[key].is_a?(Time) }
74
+ attrs
75
+ end
76
+
18
77
  def job_states
19
78
  @job_states ||= RocketJob::Job.aasm.states.map { |state| state.name.to_s }
20
79
  end
@@ -25,10 +84,10 @@ module RocketJobMissionControl
25
84
 
26
85
  def job_counts_by_state(state)
27
86
  @job_counts ||= begin
28
- counts = RocketJob::Job.counts_by_state
29
- counts[:queued] = counts[:queued_now] || 0
30
- counts
31
- end
87
+ counts = RocketJob::Job.counts_by_state
88
+ counts[:queued] = counts[:queued_now] || 0
89
+ counts
90
+ end
32
91
  @job_counts.fetch(state.downcase.to_sym, 0)
33
92
  end
34
93
 
@@ -38,8 +97,9 @@ module RocketJobMissionControl
38
97
  path,
39
98
  method: http_method,
40
99
  title: "#{action} job",
41
- class: "btn btn-default",
42
- data: {confirm: t(:confirm, scope: %i[job action], action: action)}
100
+ class: "btn btn-default btn-group",
101
+ role: "group",
102
+ data: { confirm: t(:confirm, scope: %i[job action], action: action) }
43
103
  )
44
104
  end
45
105
 
@@ -49,8 +109,9 @@ module RocketJobMissionControl
49
109
  path,
50
110
  method: http_method,
51
111
  title: "#{action} job",
52
- class: "btn btn-primary",
53
- data: {confirm: t(:confirm, scope: %i[job action], action: action)}
112
+ class: "btn btn-default btn-group",
113
+ role: "group",
114
+ data: { confirm: t(:confirm, scope: %i[job action], action: action) }
54
115
  )
55
116
  end
56
117
 
@@ -61,5 +122,12 @@ module RocketJobMissionControl
61
122
  ""
62
123
  end
63
124
  end
125
+
126
+ def job_find_category(categories, category_name = :main)
127
+ return unless categories
128
+
129
+ categories.each { |category| return category if category_name == (category["name"] || :main).to_sym }
130
+ nil
131
+ end
64
132
  end
65
133
  end
@@ -26,5 +26,9 @@ module RocketJobMissionControl
26
26
  map[server.state] || "callout-info"
27
27
  end
28
28
  end
29
+
30
+ def rocket_job_mission_control
31
+ @@rocket_job_mission_control_engine_url_helpers ||= RocketJobMissionControl::Engine.routes.url_helpers
32
+ end
29
33
  end
30
34
  end
@@ -0,0 +1,68 @@
1
+ module RocketJobMissionControl
2
+ module DirmonSanitizer
3
+ DIRMON_FIELDS = %i(archive_directory job_class_name name pattern).freeze
4
+
5
+ def self.sanitize(params, job_class, target)
6
+ permissible_params = {}
7
+
8
+ DIRMON_FIELDS.each do |field_name|
9
+ value = params[field_name]
10
+ next if value.blank?
11
+
12
+ permissible_params[field_name] = value
13
+ end
14
+
15
+ if params.key?(:properties)
16
+ properties = JobSanitizer.sanitize(params[:properties], job_class, target, false)
17
+ permissible_params[:properties] = properties unless properties.blank?
18
+ end
19
+
20
+ permissible_params
21
+ end
22
+
23
+ # Returns [Hash] the difference between the supplied params and those already set in the job itself
24
+ def self.diff_properties(sanitized_properties, dirmon_entry)
25
+ default_job = dirmon_entry.job_class.new
26
+ updated_job = dirmon_entry.job_class.from_properties(sanitized_properties)
27
+ properties = {}
28
+ sanitized_properties&.each_pair do |name, value|
29
+ if name == :input_categories
30
+ categories = []
31
+ value.each do |category_properties|
32
+ category_name = category_properties[:name].to_sym
33
+ props = diff_category(category_properties, updated_job.input_category(category_name), default_job.input_category(category_name))
34
+ categories << props unless props.empty?
35
+ end
36
+ properties[:input_categories] = categories unless categories.empty?
37
+ elsif name == :output_categories
38
+ categories = []
39
+ value.each do |category_properties|
40
+ category_name = category_properties[:name].to_sym
41
+ props = diff_category(category_properties, updated_job.output_category(category_name), default_job.output_category(category_name))
42
+ categories << props unless props.empty?
43
+ end
44
+ properties[:output_categories] = categories unless categories.empty?
45
+ elsif default_job.public_send(name) != updated_job.public_send(name)
46
+ properties[name] = value.is_a?(String) ? value.gsub(/\r\n/, "\n") : value
47
+ end
48
+ end
49
+ properties
50
+ end
51
+
52
+ def self.diff_category(properties, updated_category, default_category)
53
+ diff = {}
54
+ name = nil
55
+ properties&.each_pair do |key, value|
56
+ if key == :name
57
+ name = value
58
+ next
59
+ end
60
+ next if updated_category.public_send(key) == default_category.public_send(key)
61
+
62
+ diff[key] = value
63
+ end
64
+ diff[:name] = name unless diff.empty?
65
+ diff
66
+ end
67
+ end
68
+ end
@@ -1,5 +1,7 @@
1
1
  module RocketJobMissionControl
2
2
  module JobSanitizer
3
+ CATEGORIES_FIELDS = %i[id name format format_options mode skip_unknown slice_size columns].freeze
4
+
3
5
  # Returns [Hash] the permissible params for the specified job class, after sanitizing.
4
6
  # Parameters
5
7
  # properties [Hash]
@@ -17,6 +19,7 @@ module RocketJobMissionControl
17
19
  # Default: true
18
20
  def self.sanitize(properties, job_class, target, nil_blank = true)
19
21
  permissible_params = {}
22
+
20
23
  job_class.user_editable_fields.each do |field_name|
21
24
  next unless value = properties[field_name]
22
25
 
@@ -24,6 +27,8 @@ module RocketJobMissionControl
24
27
  next unless field&.type
25
28
 
26
29
  case field.type.name
30
+ when "String"
31
+ value.gsub(/\r\n/, "\n")
27
32
  when "Hash"
28
33
  begin
29
34
  value = value.blank? ? nil : JSON.parse(value)
@@ -38,7 +43,39 @@ module RocketJobMissionControl
38
43
  permissible_params[field_name] = value
39
44
  end
40
45
  end
46
+
47
+ if properties.key?(:input_categories_attributes)
48
+ categories = sanitize_categories(properties[:input_categories_attributes])
49
+ permissible_params[:input_categories] = categories unless categories == [{}]
50
+ end
51
+
52
+ if properties.key?(:output_categories_attributes)
53
+ categories = sanitize_categories(properties[:output_categories_attributes])
54
+ permissible_params[:output_categories] = categories unless categories == [{}]
55
+ end
56
+
41
57
  permissible_params
42
58
  end
59
+
60
+ def self.sanitize_categories(properties)
61
+ categories = []
62
+
63
+ properties.each_pair do |_, category|
64
+ hash = {}
65
+ CATEGORIES_FIELDS.each do |key|
66
+ next unless category.key?(key)
67
+
68
+ value = category[key]
69
+ next if value.blank?
70
+ next if (key == :columns) && value == [""]
71
+
72
+ value = JSON.parse(value) if key == :format_options
73
+ hash[key] = value
74
+ end
75
+ categories << hash
76
+ end
77
+
78
+ categories
79
+ end
43
80
  end
44
81
  end
@@ -17,8 +17,8 @@
17
17
 
18
18
  <%= yield :meta %>
19
19
 
20
- <%= stylesheet_link_tag 'rocket_job_mission_control/application', media: 'all' %>
21
- <%= javascript_include_tag 'rocket_job_mission_control/application' %>
20
+ <%= stylesheet_link_tag "rocket_job_mission_control/application", media: 'all', "data-turbolinks-track": "reload" %>
21
+ <%= javascript_include_tag "rocket_job_mission_control/application", "data-turbolinks-track": "reload" %>
22
22
 
23
23
  <%= csrf_meta_tag %>
24
24
  </head>
@@ -1,13 +1,13 @@
1
1
  <% if flash.present? %>
2
- <div class='flash text-center'>
2
+ <div class="flash text-center">
3
3
  <% flash.each do |key, msg| %>
4
- <div class='alert alert-<%=key%> alert-dismissable' role='alert'>
5
- <button class='close' data-dismiss='alert'>
4
+ <div class="alert alert-<%=key%> alert-dismissable" role="alert">
5
+ <button class="close" data-dismiss="alert">
6
6
  <span>&times;</span>
7
7
  </button>
8
8
 
9
9
  <% if msg.kind_of? Array %>
10
- <ul class='list-unstyled margin-bottom-0'>
10
+ <ul class="list-unstyled margin-bottom-0">
11
11
  <% msg.each do |m| %>
12
12
  <li><%= m.html_safe %></li>
13
13
  <% end %>
@@ -1,69 +1,105 @@
1
1
  <% action ||= :create %>
2
2
 
3
3
  <% if @dirmon_entry.errors.present? %>
4
- <div class='alert alert-alert'>Invalid Dirmon entry!</div>
4
+ <div class="alert alert-alert">Invalid Dirmon entry!</div>
5
+
5
6
  <% @dirmon_entry.errors.messages.each_pair do |field, message| %>
6
- <div class='message'><%= field %>: <%= message %></div>
7
+ <div class="message"><%= field %>: <%= message %></div>
7
8
  <% end %>
8
9
  <% end %>
9
10
 
10
- <%= form_for @dirmon_entry, url: {action: action} do |f| %>
11
- <div class='row'>
12
- <div class='col-md-6'>
13
- <div class='name form-group'>
14
- <%= f.label :name %>
15
- <%= f.text_field :name, class: 'form-control' %>
16
- </div>
17
- </div>
11
+ <%= form_for @dirmon_entry, url: { action: action } do |f| %>
12
+ <div class="row">
13
+ <div class="col-md-12">
14
+ <div class="arguments">
15
+ <div class="panel panel-default">
16
+ <div class="panel-body">
17
+ <div class="job_arguments form-group"></div>
18
18
 
19
- <div class='col-md-6'>
20
- <div class='job form-group'>
21
- <%= f.label :job_class_name %>
22
- <%= f.text_field :job_class_name, class: 'form-control', disabled: action != :create %>
23
- </div>
24
- </div>
25
- </div>
19
+ <div class="form-group">
20
+ <%= f.label :name %>
21
+ <%= f.text_field :name, class: "form-control" %>
22
+ </div>
26
23
 
27
- <div class='path form-group'>
28
- <%= f.label :pattern %>
29
- <%= f.text_field :pattern, class: 'form-control' %>
30
- </div>
24
+ <div class="form-group">
25
+ <%= f.label "Job Class" %>
26
+ <%= f.text_field :job_class_name, class: "form-control", disabled: action != :create %>
27
+ </div>
31
28
 
32
- <div class='path form-group'>
33
- <%= f.label :archive_directory %>
34
- <%= f.text_field :archive_directory, class: 'form-control' %>
35
- </div>
29
+ <div class="form-group">
30
+ <%= f.label :pattern %>
31
+ <%= f.text_field :pattern, class: "form-control" %>
32
+ </div>
33
+
34
+ <div class="form-group">
35
+ <%= f.label :archive_directory %>
36
+ <%= f.text_field :archive_directory, class: "form-control" %>
37
+ </div>
38
+ </div>
39
+ </div>
40
+
41
+ <% if @dirmon_entry.job_class %>
42
+ <% @job = @dirmon_entry.job_class.from_properties(@dirmon_entry.properties) %>
43
+ <%= f.fields_for :properties do |p| %>
44
+ <div class="panel panel-default">
45
+ <div class="panel-heading">
46
+ <strong>Properties</strong>
47
+ </div>
48
+
49
+ <div class="panel-body">
50
+ <% @dirmon_entry.job_class.user_editable_fields.sort.each do |property_name| %>
51
+ <% next if property_name == :run_at %>
52
+
53
+ <div class="form-group">
54
+ <%= p.label property_name.to_s %>
55
+ <%= editable_field_html(@dirmon_entry.job_class, property_name, @job.public_send(property_name), p) %>
56
+ </div>
57
+ <% end %>
58
+ </div>
59
+ </div>
60
+
61
+ <% if @dirmon_entry.job_class.respond_to?(:defined_input_categories) && @dirmon_entry.job_class.respond_to?(:defined_output_categories) %>
62
+ <div class="row">
63
+ <div class="col-sm-6">
64
+ <div class="panel panel-primary">
65
+ <div class="panel-body">
66
+ <div class='lead'>Input Categories</div>
36
67
 
37
- <div class='row'>
38
- <div class='col-md-12'>
39
- <% if @dirmon_entry.job_class %>
40
- <div class='lead'>Job Properties</div>
68
+ <% @job.input_categories.each do |input_categories| %>
69
+ <%= p.fields_for "input_categories_attributes[]", input_categories do |i| %>
70
+ <%= render "input_category_fields.html", f: i %>
71
+ <% end %>
72
+ <% end %>
73
+ </div>
74
+ </div>
75
+ </div>
41
76
 
42
- <div class='arguments'>
43
- <div class='job_arguments form-group'></div>
77
+ <div class="col-sm-6">
78
+ <div class="panel panel-success">
79
+ <div class="panel-body">
80
+ <div class='lead'>Output Categories</div>
44
81
 
45
- <%= f.fields_for :properties do |properties| %>
46
- <% @dirmon_entry.job_class.user_editable_fields.each do |property_name| %>
47
- <div class='form-group'>
48
- <%= properties.label property_name.to_s %>
49
- <%= editable_field_html(@dirmon_entry.job_class, property_name, @dirmon_entry.properties[property_name], properties, true) %>
82
+ <% @job.output_categories.each do |output_categories| %>
83
+ <%= p.fields_for "output_categories_attributes[]", output_categories do |o| %>
84
+ <%= render "output_category_fields.html", f: o %>
85
+ <% end %>
86
+ <% end %>
87
+ </div>
88
+ </div>
89
+ </div>
50
90
  </div>
51
91
  <% end %>
52
92
  <% end %>
53
93
  </div>
54
94
  <% end %>
55
95
 
56
- <div class='buttons pull-right'>
96
+ <div class='buttons'>
57
97
  <%= f.submit action, class: 'btn btn-primary' %>
58
98
  <%= link_to 'cancel', :back, class: 'btn btn-default' %>
59
99
  </div>
60
100
 
61
101
  <% if action == :create %>
62
- <%= button_tag 'properties',
63
- type: 'button',
64
- class: 'btn btn-default',
65
- id: 'properties',
66
- data: { url: rocket_job_mission_control.new_dirmon_entry_path } %>
102
+ <%= button_tag 'properties', type: 'button', class: 'btn btn-default', id: 'properties', data: {url: rocket_job_mission_control.new_dirmon_entry_path} %>
67
103
  <% end %>
68
104
  </div>
69
105
  </div>
@@ -0,0 +1,59 @@
1
+ <div class='lead'>Input Categories:</div>
2
+ <% @job.input_categories.each do |category| %>
3
+ <div class='row'>
4
+ <div class='col-md-12'>
5
+ <div class='status-message'><label><%= category.name.to_s.camelcase %>:</label></div>
6
+ <div class='row'>
7
+ <div class='col-md-12'>
8
+ <table>
9
+ <% if category.name == :main %>
10
+ <tr>
11
+ <td><label>Slice Size:</label></td>
12
+ <td><%= category.slice_size %></td>
13
+ </tr>
14
+ <% end %>
15
+ </tr>
16
+ <tr>
17
+ <td><label>Format:</label></td>
18
+ <td><%= category.format %></td>
19
+ </tr>
20
+ <tr>
21
+ <td><label>Skip Unknown Columns:</label></td>
22
+ <td><%= category.skip_unknown %></td>
23
+ </tr>
24
+ <tr>
25
+ <td><label>Mode:</label></td>
26
+ <td><%= category.mode %></td>
27
+ </tr>
28
+ <% if category.format_options %>
29
+ <tr>
30
+ <td><label>Format Options:</label></td>
31
+ <td>
32
+ <pre><code><%= category.format_options.ai(plain: true, ruby19_syntax: true, sort_keys: true) %></code></pre>
33
+ </td>
34
+ </tr>
35
+ <% end %>
36
+ <% if category.columns %>
37
+ <% first = true %>
38
+ <% category.columns.each do |item| %>
39
+ <% if first %>
40
+ <tr>
41
+ <td><label>Columns:</label></td>
42
+ <td><%= item %></td>
43
+ </tr>
44
+ <% first = false %>
45
+ <% else %>
46
+ <tr>
47
+ <td></td>
48
+ <td><%= item %></td>
49
+ </tr>
50
+ <% end %>
51
+ <% end %>
52
+ <% end %>
53
+ </table>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ <br/>
59
+ <% end %>