diff --git a/Gemfile b/Gemfile
index dcf00dc..1142c27 100644
--- a/Gemfile
+++ b/Gemfile
@@ -185,6 +185,9 @@
gem 'activerecord-session_store'
+# JSON Schema validation
+gem 'json_schemer'
+
# ------------------------------------------------
# ENVIRONMENT SPECIFIC DEPENDENCIES
diff --git a/Gemfile.lock b/Gemfile.lock
index 0a46c6c..f63dc22 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -118,6 +118,8 @@
dragonfly-s3_data_store (1.3.0)
dragonfly (~> 1.0)
fog-aws
+ ecma-re-validator (0.2.0)
+ regexp_parser (~> 1.2)
erubi (1.9.0)
erubis (2.7.0)
eventmachine (1.2.7)
@@ -186,6 +188,7 @@
guard (~> 2.1)
guard-compat (~> 1.1)
rspec (>= 2.99.0, < 4.0)
+ hana (1.3.5)
hashdiff (1.0.0)
hashie (3.6.0)
highline (2.0.2)
@@ -201,6 +204,11 @@
activesupport (>= 3.0.0)
multi_json (>= 1.2)
json (2.2.0)
+ json_schemer (0.2.10)
+ ecma-re-validator (~> 0.2)
+ hana (~> 1.3)
+ regexp_parser (~> 1.5)
+ uri_template (~> 0.7)
jwt (2.2.1)
kaminari (1.1.1)
activesupport (>= 4.1.0)
@@ -443,6 +451,7 @@
thread_safe (~> 0.1)
unicode-display_width (1.6.0)
uniform_notifier (1.12.1)
+ uri_template (0.7.0)
warden (1.2.7)
rack (>= 1.0)
web-console (3.3.0)
@@ -509,6 +518,7 @@
guard-rspec
htmltoword (= 1.1.0)
jbuilder (~> 2.6.0)
+ json_schemer
kaminari
ledermann-rails-settings
mini_racer
diff --git a/app/controllers/answers_controller.rb b/app/controllers/answers_controller.rb
index d52b8c1..12394a8 100644
--- a/app/controllers/answers_controller.rb
+++ b/app/controllers/answers_controller.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
+# rubocop:disable
class AnswersController < ApplicationController
prepend Dmpopidor::Controllers::Answers
@@ -136,7 +137,7 @@
def permitted_params
permitted = params.require(:answer).permit(:id, :text, :plan_id, :user_id,
:question_id, :lock_version,
- :research_output_id, :is_common,
+ :research_output_id, :is_common,
question_option_ids: [])
# If question_option_ids has been filtered out because it was a
# scalar value (e.g. radiobutton answer)
diff --git a/app/views/branded/questions/_new_edit_question_structured.erb b/app/views/branded/questions/_new_edit_question_structured.erb
index 48986b3..687d535 100644
--- a/app/views/branded/questions/_new_edit_question_structured.erb
+++ b/app/views/branded/questions/_new_edit_question_structured.erb
@@ -3,13 +3,14 @@
<%= question.text %>
<% schema['properties'].each do |key, prop| %>
+ <% value = answer.structured_answer.data[key] unless answer.structured_answer.nil? %>
<% case prop['type'] %>
<% when 'string' %>
- <%= create_text_field(f, nil, key, prop['label']) %>
+ <%= create_text_field(f, value, key, prop['label']) %>
<% when 'integer' %>
- <%= create_number_field(f, nil, key, prop['label']) %>
+ <%= create_number_field(f, value, key, prop['label']) %>
<% when 'boolean' %>
- <%= create_checkbox_field(f, nil, key, prop['label']) %>
+ <%= create_checkbox_field(f, value, key, prop['label']) %>
<% end %>
<% end %>
-
\ No newline at end of file
+
diff --git a/app/views/branded/questions/fields/_text_field.html.erb b/app/views/branded/questions/fields/_text_field.html.erb
index 8938d15..1a88f05 100644
--- a/app/views/branded/questions/fields/_text_field.html.erb
+++ b/app/views/branded/questions/fields/_text_field.html.erb
@@ -9,4 +9,4 @@
Remove
<% end %>
<% end %>
-
\ No newline at end of file
+
diff --git a/lib/dmpopidor/controllers/answers.rb b/lib/dmpopidor/controllers/answers.rb
index 96faa5c..d8ad265 100644
--- a/lib/dmpopidor/controllers/answers.rb
+++ b/lib/dmpopidor/controllers/answers.rb
@@ -34,17 +34,27 @@
# rubocop:disable BlockLength
Answer.transaction do
begin
- @answer = Answer.find_by!({
- plan_id: p_params[:plan_id], question_id: p_params[:question_id],
- research_output_id: p_params[:research_output_id]
+ @answer = Answer.find_by!({
+ plan_id: p_params[:plan_id], question_id: p_params[:question_id],
+ research_output_id: p_params[:research_output_id]
})
authorize @answer
- @answer.update(p_params.merge(user_id: current_user.id))
+ p = p_params.merge(user_id: current_user.id).select { |k, v| !schema_params.include?(k) }
+ @answer.update(p)
if p_params[:question_option_ids].present?
# Saves the record with the updated_at set to the current time.
# Needed if only answer.question_options is updated
@answer.touch()
end
+ if q.question_format.structured
+ form_data = p_params.select { |k, v| schema_params.include?(k) }
+ s_answer = StructuredAnswer.find_or_initialize_by(answer_id: @answer.id) do |sa|
+ sa.answer = @answer
+ sa.structured_data_schema = q.structured_data_schema
+ end
+ s_answer.assign_attributes(data: data_reformater(json_schema, form_data))
+ s_answer.save
+ end
if q.question_format.rda_metadata?
@answer.update_answer_hash(
JSON.parse(params[:standards]), p_params[:text]
@@ -52,9 +62,19 @@
@answer.save!
end
rescue ActiveRecord::RecordNotFound
- @answer = Answer.new(p_params.merge(user_id: current_user.id))
+ p = p_params.merge(user_id: current_user.id).select { |k, v| !schema_params.include?(k) }
+ @answer = Answer.new(p)
@answer.lock_version = 1
authorize @answer
+ if q.question_format.structured
+ form_data = p_params.select { |k, v| schema_params.include?(k) }
+ s_answer = StructuredAnswer.find_or_initialize_by(answer_id: @answer.id) do |sa|
+ sa.answer = @answer
+ sa.structured_data_schema = q.structured_data_schema
+ end
+ s_answer.assign_attributes(data: data_reformater(json_schema, form_data))
+ s_answer.save
+ end
if q.question_format.rda_metadata?
@answer.update_answer_hash(
JSON.parse(params[:standards]), p_params[:text]
@@ -131,6 +151,69 @@
# rubocop:enable LineLength
end
end
+
+ private
+
+ def data_reformater(schema, data)
+ schema["properties"].each do |key, value|
+ case value["type"]
+ when "integer"
+ data[key] = data[key].to_i
+ when "boolean"
+ data[key] = data[key] == "1"
+ when "array"
+ data[key] = data[key].kind_of?(Array) ? data[key] : [data[key]]
+ when "object"
+ if value["dictionnary"]
+ data[key] = JSON.parse(DictionnaryValue.where(id: data[key]).select(:id, :uri, :label).take.to_json)
+ end
+ end
+ end
+ data
+ end
+
+ def permitted_params_from_properties(properties)
+ parameters = Array.new
+ properties.each do |key, prop|
+ if prop["type"] == "array"
+ parameters.append({key => []})
+ else
+ parameters.append(key)
+ end
+ end
+ parameters
+ end
+
+ def json_schema
+ question = Question.find(params['question_id'])
+
+ question.structured_data_schema.schema
+ end
+
+ def schema_params
+ permitted_params_from_properties(json_schema['properties'])
+ end
+
+ def permitted_params
+ permitted = params.require(:answer).permit([:id, :text, :plan_id, :user_id,
+ :question_id, :lock_version,
+ :research_output_id, :is_common,
+ question_option_ids: []].append(schema_params))
+ # If question_option_ids has been filtered out because it was a
+ # scalar value (e.g. radiobutton answer)
+ if !params[:answer][:question_option_ids].nil? &&
+ !permitted[:question_option_ids].present?
+ permitted[:question_option_ids] = [params[:answer][:question_option_ids]]
+ end
+ if !permitted[:id].present?
+ permitted.delete(:id)
+ end
+ # If no question options has been chosen.
+ if params[:answer][:question_option_ids].nil?
+ permitted[:question_option_ids] = []
+ end
+ permitted
+ end
end
end
- end
\ No newline at end of file
+ end
diff --git a/test.json b/test.json
index 1c5d1b4..b440079 100644
--- a/test.json
+++ b/test.json
@@ -25,4 +25,4 @@
"label": "Active?"
}
}
-}
\ No newline at end of file
+}
diff --git a/test.rb b/test.rb
index 4de7ed2..f7f9847 100644
--- a/test.rb
+++ b/test.rb
@@ -5,4 +5,4 @@
data = JSON.load(file)
sds = StructuredDataSchema.create(label: 'test', name: 'test', version: 1, schema: data.to_json, org_id: org.id, object: 'foo')
qf = QuestionFormat.create(title: "Structured", description: "foo", structured: true)
-question.update(question_format: qf, structured_data_schema: sds)
\ No newline at end of file
+question.update(question_format: qf, structured_data_schema: sds)
diff --git a/test2.json b/test2.json
new file mode 100644
index 0000000..ede82b9
--- /dev/null
+++ b/test2.json
@@ -0,0 +1,30 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "description": "A person",
+ "type":"object",
+ "properties": {
+ "lastName":{
+ "description": "The last name of the person",
+ "type": "string",
+ "label": "Nom"
+ },
+ "firstName": {
+ "description": "This person's first name",
+ "type": "string",
+ "label": "Prénom"
+ },
+ "mbox": {
+ "description": "This person's mail addres",
+ "type": "string",
+ "format": "email",
+ "label": "Courriel"
+ },
+ "personId": {
+ "description": "This person's identifier, e.g. ORCID Id",
+ "type": "string",
+ "label": "Identifiant"
+ }
+ },
+
+ "required": ["lastName","firstName","mbox"]
+}
diff --git a/test3.json b/test3.json
new file mode 100644
index 0000000..33b7b2a
--- /dev/null
+++ b/test3.json
@@ -0,0 +1,29 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "description": "The description of license data",
+ "type":"object",
+ "properties": {
+ "title":{
+ "description": "The title of this license. The value is a quoted string followed by '@' and a 2 letter language code",
+ "type": "string",
+ "pattern": "\".*\"@[a-z][a-z]"
+ },
+ "licenseUrl": {
+ "description": "The URL where a description of this certification can be found",
+ "type": "string",
+ "format": "uri"
+ },
+ "startDate": {
+ "description": "The date at which this data is subject to the license",
+ "type": "string",
+ "format": "date"
+ },
+ "endDate": {
+ "description": "The date at which this data is no longer subject to the license",
+ "type": "string",
+ "format": "date"
+ }
+ },
+
+ "required": ["title","certificationUrl","startDate","endDate"]
+}