diff --git a/projects/demo/models/intermediate/forms/all_answers_matrix.sql b/projects/demo/models/intermediate/forms/all_answers_matrix.sql index 0dff3a85..dbdb4ddb 100644 --- a/projects/demo/models/intermediate/forms/all_answers_matrix.sql +++ b/projects/demo/models/intermediate/forms/all_answers_matrix.sql @@ -13,6 +13,7 @@ SELECT DISTINCT decidim_forms_questions.question_type, decidim_forms_questions.position AS position, decidim_forms_answer_choices.body::text AS answer, + decidim_forms_answer_options.body AS option_answer, decidim_forms_question_matrix_rows.body AS sub_matrix_question, COALESCE(decidim_forms_answer_choices.custom_body, '') AS custom_body, -1 AS sorting_position, diff --git a/projects/demo/models/intermediate/forms/schema.yml b/projects/demo/models/intermediate/forms/schema.yml index 4936c11f..b4bc34bc 100644 --- a/projects/demo/models/intermediate/forms/schema.yml +++ b/projects/demo/models/intermediate/forms/schema.yml @@ -1,3 +1,172 @@ +models: + - name: int_forms_answers + config: + contract: {enforced: false} + columns: + - name: id + data_type: BIGINT + constraints: + - type: unique + - type: not_null + - type: primary_key + - name: body + data_type: TEXT + - name: decidim_user_id + data_type: BIGINT + #constraints: + # - type: foreign_key + # to: ref('int_users') + # to_columns: [id] + - name: decidim_questionnaire_id + data_type: BIGINT + constraints: + - type: not_null + - type: foreign_key + to: ref('stg_decidim_forms_questionnaires') + to_columns: [id] + - name: decidim_question_id + data_type: BIGINT + constraints: + - type: not_null + - type: foreign_key + to: ref('stg_decidim_forms_questions') + to_columns: [id] + - name: created_at + data_type: TIMESTAMP + - name: author_status + data_type: TEXT + - name: updated_at + data_type: TIMESTAMP + - name: session_token + description: "The token identifiying the session of the answerer" + data_type: TEXT + - name: ip_hash + data_type: TEXT + + - name: answers_short_and_long_answer + description: "Answers filled from text boxes" + config: + contract: {enforced: false} + columns: + - name: id + data_type: BIGINT + constraints: + - type: unique + - type: primary_key + - name: decidim_user_id + data_type: BIGINT + #constraints: + # - type: foreign_key + # to: ref('int_users') + # to_columns: [id] + - name: session_token + data_type: TEXT + - name: ip_hash + data_type: TEXT + - name: decidim_question_id + data_type: BIGINT + constraints: + - type: not_null + - type: foreign_key + to: ref('stg_decidim_forms_questions') + to_columns: [id] + - name: question_type + data_type: TEXT + - name: position + data_type: BIGINT + - name: answer + data_type: TEXT + - name: sub_matrix_question + data_type: TEXT + - name: custom_body + data_type: TEXT + - name: sorting_position + data_type: INT + - name: decidim_questionnaire_id + constraints: + - type: not_null + - type: foreign_key + to: ref('stg_decidim_forms_questionnaires') + to_columns: [id] + data_type: BIGINT + - name: body + data_type: TEXT + - name: created_at + data_type: TIMESTAMP + - name: author_status + data_type: TEXT + + - name: all_answers_option_and_sorting + description: "Answers picked from choices (single, multiple or by order of preference), including invalid answers" + config: + contract: {enforced: false} + columns: + - name: id + data_type: BIGINT + description: "actually the answer id, not the answer choice id (so, not unique)" + constraints: + - type: not_null + - type: foreign_key + to: ref('int_forms_answers') + to_columns: [id] + - name: decidim_user_id + data_type: BIGINT + #constraints: + # - type: foreign_key + # to: ref('int_users') + # to_columns: [id] + - name: session_token + data_type: TEXT + - name: ip_hash + data_type: TEXT + - name: decidim_question_id + data_type: BIGINT + constraints: + - type: not_null + - type: foreign_key + to: ref('stg_decidim_forms_questions') + to_columns: [id] + - name: question_type + data_type: TEXT + - name: sorting_position + data_type: BIGINT + - name: answer + data_type: TEXT + - name: sub_matrix_question + data_type: TEXT + - name: custom_body + data_type: TEXT + - name: sorting_position + data_type: BIGINT + - name: question_position + data_type: BIGINT + - name: decidim_questionnaire_id + constraints: + - type: not_null + - type: foreign_key + to: ref('stg_decidim_forms_questionnaires') + to_columns: [id] + data_type: BIGINT + - name: body + data_type: TEXT + - name: created_at + data_type: TIMESTAMP + - name: author_status + data_type: TEXT + - name: decidim_answer_option_id + data_type: BIGINT + constraints: + - type: not_null + - type: foreign_key + to: ref('stg_decidim_forms_answer_options') + to_columns: [id] + - name: option_answer + data_type: TEXT + - name: invalid_choice_body + data_type: BOOL + - name: invalid_question_id + data_type: BOOL + unit_tests: - name: int_forms_answers_author_status_check description: "Check that the author status is correctly detected" @@ -299,7 +468,6 @@ unit_tests: format: dict rows: - {id: 41, question_type: 'single_option'} - - {id: 41, question_type: 'single_option'} - input: ref('stg_decidim_forms_answer_choices') format: dict rows: @@ -316,6 +484,77 @@ unit_tests: - {answer: "Nord", option_answer: "Nord", invalid_choice_body: false} - {answer: "Nord", option_answer: "Sud", invalid_choice_body: true} + - name: answers_matrix_options_content_check + description: "Check that matrix answer options match pre-written options" + model: all_answers_matrix + given: + - input: ref('int_forms_answers') + format: dict + rows: + - {id: 901, decidim_question_id: 41} + - input: ref('stg_decidim_forms_questions') + format: dict + rows: + - {id: 41, decidim_questionnaire_id: 101, question_type: 'matrix_single', body: "Vous préférez aller où en vacances ?"} + - input: ref('stg_decidim_forms_answer_choices') + format: dict + rows: + - {decidim_answer_id: 901, decidim_answer_option_id: 1002, body: "À la montagne", decidim_question_matrix_row_id: 1} + - {decidim_answer_id: 901, decidim_answer_option_id: 1001, body: "À la montagne", decidim_question_matrix_row_id: 2} + - input: ref('stg_decidim_forms_answer_options') + format: dict + rows: + - {id: 1001, decidim_question_id: 41, body: "À la mer"} + - {id: 1002, decidim_question_id: 41, body: "À la montagne"} + - input: ref('stg_decidim_forms_question_matrix_rows') + format: dict + rows: + - {id: 1, decidim_question_id: 41, body: "Vacances d'été"} + - {id: 2, decidim_question_id: 41, body: "Vacances d'hiver"} + expect: + format: dict + rows: + - {answer: "À la montagne", option_answer: "À la montagne", invalid_choice_body: false} + - {answer: "À la montagne", option_answer: "À la mer", invalid_choice_body: true} + + - name: answers_matrix_question_check + description: "Check that matrix answers come from options belonging to the answered question" + model: all_answers_matrix + given: + - input: ref('int_forms_answers') + format: dict + rows: + - {id: 901, decidim_question_id: 41} + - input: ref('stg_decidim_forms_questions') + format: dict + rows: + - {id: 41, decidim_questionnaire_id: 101, question_type: 'matrix_single', body: "Vous préférez aller où en vacances ?"} + - {id: 42, decidim_questionnaire_id: 101, question_type: 'matrix_single', body: "Vous préférez quelle saveur à vos repas ?"} + - input: ref('stg_decidim_forms_answer_choices') + format: dict + rows: + - {decidim_answer_id: 901, decidim_answer_option_id: 1002, body: "À la montagne", decidim_question_matrix_row_id: 1} + - {decidim_answer_id: 901, decidim_answer_option_id: 1003, body: "Sucré", decidim_question_matrix_row_id: 3} + - input: ref('stg_decidim_forms_answer_options') + format: dict + rows: + - {id: 1001, decidim_question_id: 41, body: "À la mer"} + - {id: 1002, decidim_question_id: 41, body: "À la montagne"} + - {id: 1003, decidim_question_id: 42, body: "Sucré"} + - {id: 1004, decidim_question_id: 42, body: "Salé"} + - input: ref('stg_decidim_forms_question_matrix_rows') + format: dict + rows: + - {id: 1, decidim_question_id: 41, body: "Vacances d'été"} + - {id: 2, decidim_question_id: 41, body: "Vacances d'hiver"} + - {id: 3, decidim_question_id: 42, body: "Petit déjeuner"} + - {id: 4, decidim_question_id: 42, body: "Heure du goûter"} + expect: + format: dict + rows: + - {answer: "À la montagne", invalid_question_id: false} + - {answer: "Sucré", invalid_question_id: true} + - name: answers_single_option_question_check description: "Check that single option answers come from options belonging to the answered question" model: all_answers_option_and_sorting diff --git a/projects/demo/models/marts/budgets/schema.yml b/projects/demo/models/marts/budgets/schema.yml index 04d2fea1..3ffe5d42 100644 --- a/projects/demo/models/marts/budgets/schema.yml +++ b/projects/demo/models/marts/budgets/schema.yml @@ -52,6 +52,11 @@ models: data_type: BOOL - name: budget_id data_type: BIGINT + constraints: + - type: not_null + - type: foreign_key + to: ref('budgets') + to_columns: [id] - name: budget_title data_type: TEXT - name: resource_type diff --git a/projects/demo/models/marts/forms/schema.yml b/projects/demo/models/marts/forms/schema.yml index 19897be3..fac17ccd 100644 --- a/projects/demo/models/marts/forms/schema.yml +++ b/projects/demo/models/marts/forms/schema.yml @@ -22,6 +22,7 @@ models: to_columns: [id] - name: questionnaire_url data_type: TEXT + - name: all_forms_answers config: contract: {enforced: true} diff --git a/projects/demo/models/staging/decidim/forms/schema.yml b/projects/demo/models/staging/decidim/forms/schema.yml index be072d7f..734b451f 100644 --- a/projects/demo/models/staging/decidim/forms/schema.yml +++ b/projects/demo/models/staging/decidim/forms/schema.yml @@ -17,132 +17,236 @@ models: description: "Table for forms answer choices when the answer is in a matrix question or in a sorting or optional question, this table stores the choice of the answerer." columns: - name: id - description: "Primary key." - data_tests: - - not_null - - unique + data_type: BIGINT + constraints: + - type: unique + - type: not_null + - type: primary_key - name: decidim_answer_id description: "Foreign key to the answer this choice belongs to from the table decidim_forms_answers" - data_tests: - - not_null + data_type: BIGINT + constraints: + - type: not_null + - type: foreign_key + to: ref('stg_decidim_forms_answers') + to_columns: [id] - name: decidim_answer_option_id description: "Foreign key to the answer option from the table decidim_forms_answer_options. (Currently not used)" + data_type: BIGINT + constraints: + - type: not_null + - type: foreign_key + to: ref('stg_decidim_forms_answer_options') + to_columns: [id] - name: position description: "Position of the answer choice when the question is of type sorting." + data_type: BIGINT - name: body description: "Content of the answer choice." + data_type: TEXT data_tests: - not_null - name: custom_body description: "Custom body for the answer choice when the answer is of type free text." + data_type: TEXT - name: decidim_question_matrix_row_id description: "Foreign key to the question matrix row when the question is of type matrix. It matches the matrix rows." + data_type: BIGINT + constraints: + - type: not_null + - type: foreign_key + to: ref('stg_decidim_forms_question_matrix_rows') + to_columns: [id] + + - name: stg_decidim_forms_answer_options + description: "" + config: + contract: {enforced: false} + columns: + - name: id + data_type: BIGINT + constraints: + - type: unique + - type: not_null + - type: primary_key + - name: body + data_type: TEXT + - name: decidim_question_id + data_type: BIGINT + constraints: + - type: not_null + - type: foreign_key + to: ref('stg_decidim_forms_questions') + to_columns: [id] + - name: free_text + data_type: BOOL - name: stg_decidim_forms_answers description: "Table for forms answer when the answer is not in a matrix or optional and sorting question. (i.e long_answer and short_answer)" + config: + contract: {enforced: false} columns: - name: id - description: "Primary key." - data_tests: - - not_null - - unique + data_type: BIGINT + constraints: + - type: unique + - type: not_null + - type: primary_key - name: body description: "Content of the answer. It is null if the question is a matrix or option, or sorting. It can be 'separator' if the question is a separator" + data_type: TEXT - name: decidim_user_id description: "Foreign key to the user who gave the answer." + #constraints: + # - type: foreign_key + # to: ref('stg_decidim_users') + # to_columns: [id] + data_type: BIGINT - name: decidim_questionnaire_id description: "Foreign key to the questionnaire." + data_type: BIGINT + constraints: + - type: not_null + - type: foreign_key + to: ref('stg_decidim_forms_questionnaires') + to_columns: [id] - name: decidim_question_id description: "Foreign key to the question." + data_type: BIGINT + constraints: + - type: not_null + - type: foreign_key + to: ref('stg_decidim_forms_questions') + to_columns: [id] - name: created_at description: "Timestamp when the answer was created." + data_type: TIMESTAMP - name: updated_at description: "Timestamp when the answer was last updated." + data_type: TIMESTAMP - name: session_token description: "Session token of the user." + data_type: TEXT - name: ip_hash description: "Hashed IP address of the user." + data_type: TEXT - name: stg_decidim_forms_question_matrix_rows description: "Table for forms question of type matrix." columns: - name: id - description: "Primary key." - data_tests: - - not_null - - unique + data_type: BIGINT + constraints: + - type: unique + - type: not_null + - type: primary_key - name: decidim_question_id description: "Foreign key to the question." - data_tests: - - not_null + data_type: BIGINT + constraints: + - type: not_null + - type: foreign_key + to: ref('stg_decidim_forms_questions') + to_columns: [id] - name: position description: "Position of the matrix row." + data_type: BIGINT - name: body description: "Content of the matrix row." + data_type: TEXT data_tests: - not_null - name: stg_decidim_forms_questionnaires description: "Table for forms questionnaires" + config: + contract: {enforced: false} columns: - name: id - description: "Primary key." - data_tests: - - not_null - - unique + data_type: BIGINT + constraints: + - type: unique + - type: not_null + - type: primary_key - name: title description: "Title of the questionnaire." + data_type: TEXT - name: description description: "Description of the questionnaire." + data_type: TEXT - name: tos description: "Terms of service associated with the questionnaire." + data_type: TEXT - name: questionnaire_for_type description: "Type of entity the questionnaire is for." + data_type: TEXT - name: questionnaire_for_id description: "ID of the entity the questionnaire is for." + data_type: BIGINT - name: published_at description: "Timestamp when the questionnaire was published." + data_type: TIMESTAMP - name: created_at description: "Timestamp when the questionnaire was created." + data_type: TIMESTAMP - name: updated_at description: "Timestamp when the questionnaire was last updated." + data_type: TIMESTAMP - name: salt description: "Salt used for hashing." + data_type: TEXT - name: stg_decidim_forms_questions description: "Table for forms questions" + config: + contract: {enforced: false} columns: - name: id - description: "Primary key." - data_tests: - - not_null - - unique + data_type: BIGINT + constraints: + - type: unique + - type: not_null + - type: primary_key - name: decidim_questionnaire_id description: "Foreign key to the questionnaire." + data_type: BIGINT + constraints: + - type: not_null + - type: foreign_key + to: ref('stg_decidim_forms_questionnaires') + to_columns: [id] - name: position description: "Position of the question." + data_type: BIGINT - name: question_type description: "Type of the question (e.g., short_answer, long_answer, single_matrix ...)." + data_type: TEXT - name: mandatory description: "Boolean that indicates whether the question is mandatory." + data_type: BOOL - name: body description: "Content of the question." + data_type: TEXT - name: description description: "Description of the question." + data_type: TEXT - name: max_choices description: "Maximum number of choices allowed for the question." + data_type: BIGINT - name: created_at + data_type: TIMESTAMP description: "Timestamp when the question was created." - name: updated_at + data_type: TIMESTAMP description: "Timestamp when the question was last updated." - name: max_characters description: "Maximum number of characters allowed for the question." - - - - - - - - - + data_type: BIGINT + - name: answer_options_count + data_type: BIGINT + - name: display_conditions_count + data_type: BIGINT + - name: display_conditions_for_other_questions_count + data_type: BIGINT + - name: matrix_rows_count + data_type: BIGINT \ No newline at end of file