Initial commit
7
__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import controllers
|
||||
from . import models
|
||||
from . import report
|
||||
from . import wizard
|
116
__manifest__.py
Normal file
|
@ -0,0 +1,116 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
{
|
||||
'name': 'Surveys',
|
||||
'version': '3.6',
|
||||
'category': 'Marketing/Surveys',
|
||||
'description': """
|
||||
Create beautiful surveys and visualize answers
|
||||
==============================================
|
||||
|
||||
It depends on the answers or reviews of some questions by different users. A
|
||||
survey may have multiple pages. Each page may contain multiple questions and
|
||||
each question may have multiple answers. Different users may give different
|
||||
answers of question and according to that survey is done. Partners are also
|
||||
sent mails with personal token for the invitation of the survey.
|
||||
""",
|
||||
'summary': 'Send your surveys or share them live.',
|
||||
'website': 'https://www.odoo.com/app/surveys',
|
||||
'depends': [
|
||||
'auth_signup',
|
||||
'http_routing',
|
||||
'mail',
|
||||
'web_tour',
|
||||
'gamification'],
|
||||
'data': [
|
||||
'report/survey_templates.xml',
|
||||
'report/survey_reports.xml',
|
||||
'data/mail_message_subtype_data.xml',
|
||||
'data/mail_template_data.xml',
|
||||
'security/survey_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'views/survey_menus.xml',
|
||||
'views/survey_survey_views.xml',
|
||||
'views/survey_user_views.xml',
|
||||
'views/survey_question_views.xml',
|
||||
'views/survey_templates.xml',
|
||||
'views/survey_templates_management.xml',
|
||||
'views/survey_templates_print.xml',
|
||||
'views/survey_templates_statistics.xml',
|
||||
'views/survey_templates_user_input_session.xml',
|
||||
'views/gamification_badge_views.xml',
|
||||
'wizard/survey_invite_views.xml',
|
||||
'views/res_partner_views.xml',
|
||||
],
|
||||
'demo': [
|
||||
'data/gamification_badge_demo.xml',
|
||||
'data/res_users_demo.xml',
|
||||
'data/survey_demo_feedback.xml',
|
||||
'data/survey_demo_feedback_user_input.xml',
|
||||
'data/survey_demo_feedback_user_input_line.xml',
|
||||
'data/survey_demo_certification.xml',
|
||||
'data/survey_demo_certification_user_input.xml',
|
||||
'data/survey_demo_certification_user_input_line.xml',
|
||||
'data/survey_demo_quiz.xml',
|
||||
'data/survey_demo_quiz_user_input.xml',
|
||||
'data/survey_demo_quiz_user_input_line.xml',
|
||||
'data/survey_demo_conditional.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'application': True,
|
||||
'sequence': 220,
|
||||
'assets': {
|
||||
'survey.survey_assets': [
|
||||
('include', "web.chartjs_lib"),
|
||||
'survey/static/src/js/survey_image_zoomer.js',
|
||||
'/survey/static/src/xml/survey_image_zoomer_templates.xml',
|
||||
'survey/static/src/js/survey_quick_access.js',
|
||||
'survey/static/src/js/survey_timer.js',
|
||||
'survey/static/src/js/survey_breadcrumb.js',
|
||||
'survey/static/src/js/survey_form.js',
|
||||
'survey/static/src/js/survey_preload_image_mixin.js',
|
||||
'survey/static/src/js/survey_print.js',
|
||||
'survey/static/src/js/survey_result.js',
|
||||
('include', 'web._assets_helpers'),
|
||||
('include', 'web._assets_frontend_helpers'),
|
||||
'web/static/src/scss/pre_variables.scss',
|
||||
'web/static/lib/bootstrap/scss/_variables.scss',
|
||||
'survey/static/src/scss/survey_templates_form.scss',
|
||||
'survey/static/src/scss/survey_templates_results.scss',
|
||||
'survey/static/src/xml/survey_breadcrumb_templates.xml',
|
||||
],
|
||||
'survey.survey_user_input_session_assets': [
|
||||
'survey/static/src/js/survey_session_colors.js',
|
||||
'survey/static/src/js/survey_session_chart.js',
|
||||
'survey/static/src/js/survey_session_text_answers.js',
|
||||
'survey/static/src/js/survey_session_leaderboard.js',
|
||||
'survey/static/src/js/survey_session_manage.js',
|
||||
'survey/static/src/xml/survey_session_text_answer_template.xml',
|
||||
],
|
||||
'web.report_assets_common': [
|
||||
'survey/static/src/scss/survey_reports.scss',
|
||||
],
|
||||
'web.assets_backend': [
|
||||
'survey/static/src/question_page/*',
|
||||
'survey/static/src/views/**/*.js',
|
||||
'survey/static/src/views/**/*.xml',
|
||||
'survey/static/src/scss/survey_survey_views.scss',
|
||||
'survey/static/src/scss/survey_question_views.scss',
|
||||
'survey/static/src/scss/survey_templates_results.scss',
|
||||
'survey/static/src/js/tours/survey_tour.js',
|
||||
],
|
||||
"web.assets_web_dark": [
|
||||
'survey/static/src/scss/*.dark.scss',
|
||||
],
|
||||
'web.assets_tests': [
|
||||
'survey/static/tests/tours/*.js',
|
||||
],
|
||||
'web.qunit_suite_tests': [
|
||||
'survey/static/tests/components/*.js',
|
||||
],
|
||||
'web.assets_frontend': [
|
||||
'survey/static/src/js/tours/survey_tour.js',
|
||||
],
|
||||
},
|
||||
'license': 'LGPL-3',
|
||||
}
|
5
controllers/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import main
|
||||
from . import survey_session_manage
|
878
controllers/main.py
Normal file
|
@ -0,0 +1,878 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import json
|
||||
import logging
|
||||
import werkzeug
|
||||
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, timedelta
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import fields, http, SUPERUSER_ID, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.http import request, content_disposition
|
||||
from odoo.osv import expression
|
||||
from odoo.tools import format_datetime, format_date, is_html_empty
|
||||
from odoo.addons.base.models.ir_qweb import keep_query
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Survey(http.Controller):
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# ACCESS
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def _fetch_from_access_token(self, survey_token, answer_token):
|
||||
""" Check that given token matches an answer from the given survey_id.
|
||||
Returns a sudo-ed browse record of survey in order to avoid access rights
|
||||
issues now that access is granted through token. """
|
||||
survey_sudo = request.env['survey.survey'].with_context(active_test=False).sudo().search([('access_token', '=', survey_token)])
|
||||
if not answer_token:
|
||||
answer_sudo = request.env['survey.user_input'].sudo()
|
||||
else:
|
||||
answer_sudo = request.env['survey.user_input'].sudo().search([
|
||||
('survey_id', '=', survey_sudo.id),
|
||||
('access_token', '=', answer_token)
|
||||
], limit=1)
|
||||
return survey_sudo, answer_sudo
|
||||
|
||||
def _check_validity(self, survey_token, answer_token, ensure_token=True, check_partner=True):
|
||||
""" Check survey is open and can be taken. This does not checks for
|
||||
security rules, only functional / business rules. It returns a string key
|
||||
allowing further manipulation of validity issues
|
||||
|
||||
* survey_wrong: survey does not exist;
|
||||
* survey_auth: authentication is required;
|
||||
* survey_closed: survey is closed and does not accept input anymore;
|
||||
* survey_void: survey is void and should not be taken;
|
||||
* token_wrong: given token not recognized;
|
||||
* token_required: no token given although it is necessary to access the
|
||||
survey;
|
||||
* answer_deadline: token linked to an expired answer;
|
||||
|
||||
:param ensure_token: whether user input existence based on given access token
|
||||
should be enforced or not, depending on the route requesting a token or
|
||||
allowing external world calls;
|
||||
|
||||
:param check_partner: Whether we must check that the partner associated to the target
|
||||
answer corresponds to the active user.
|
||||
"""
|
||||
survey_sudo, answer_sudo = self._fetch_from_access_token(survey_token, answer_token)
|
||||
|
||||
if not survey_sudo.exists():
|
||||
return 'survey_wrong'
|
||||
|
||||
if answer_token and not answer_sudo:
|
||||
return 'token_wrong'
|
||||
|
||||
if not answer_sudo and ensure_token:
|
||||
return 'token_required'
|
||||
if not answer_sudo and survey_sudo.access_mode == 'token':
|
||||
return 'token_required'
|
||||
|
||||
if survey_sudo.users_login_required and request.env.user._is_public():
|
||||
return 'survey_auth'
|
||||
|
||||
if not survey_sudo.active and (not answer_sudo or not answer_sudo.test_entry):
|
||||
return 'survey_closed'
|
||||
|
||||
if (not survey_sudo.page_ids and survey_sudo.questions_layout == 'page_per_section') or not survey_sudo.question_ids:
|
||||
return 'survey_void'
|
||||
|
||||
if answer_sudo and check_partner:
|
||||
if request.env.user._is_public() and answer_sudo.partner_id and not answer_token:
|
||||
# answers from public user should not have any partner_id; this indicates probably a cookie issue
|
||||
return 'answer_wrong_user'
|
||||
if not request.env.user._is_public() and answer_sudo.partner_id != request.env.user.partner_id:
|
||||
# partner mismatch, probably a cookie issue
|
||||
return 'answer_wrong_user'
|
||||
|
||||
if answer_sudo and answer_sudo.deadline and answer_sudo.deadline < datetime.now():
|
||||
return 'answer_deadline'
|
||||
|
||||
return True
|
||||
|
||||
def _get_access_data(self, survey_token, answer_token, ensure_token=True, check_partner=True):
|
||||
""" Get back data related to survey and user input, given the ID and access
|
||||
token provided by the route.
|
||||
|
||||
: param ensure_token: whether user input existence should be enforced or not(see ``_check_validity``)
|
||||
: param check_partner: whether the partner of the target answer should be checked (see ``_check_validity``)
|
||||
"""
|
||||
survey_sudo, answer_sudo = request.env['survey.survey'].sudo(), request.env['survey.user_input'].sudo()
|
||||
has_survey_access, can_answer = False, False
|
||||
|
||||
validity_code = self._check_validity(survey_token, answer_token, ensure_token=ensure_token, check_partner=check_partner)
|
||||
if validity_code != 'survey_wrong':
|
||||
survey_sudo, answer_sudo = self._fetch_from_access_token(survey_token, answer_token)
|
||||
try:
|
||||
survey_user = survey_sudo.with_user(request.env.user)
|
||||
survey_user.check_access_rights(self, 'read', raise_exception=True)
|
||||
survey_user.check_access_rule(self, 'read')
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
has_survey_access = True
|
||||
can_answer = bool(answer_sudo)
|
||||
if not can_answer:
|
||||
can_answer = survey_sudo.access_mode == 'public'
|
||||
|
||||
return {
|
||||
'survey_sudo': survey_sudo,
|
||||
'answer_sudo': answer_sudo,
|
||||
'has_survey_access': has_survey_access,
|
||||
'can_answer': can_answer,
|
||||
'validity_code': validity_code,
|
||||
}
|
||||
|
||||
def _redirect_with_error(self, access_data, error_key):
|
||||
survey_sudo = access_data['survey_sudo']
|
||||
answer_sudo = access_data['answer_sudo']
|
||||
|
||||
if error_key == 'survey_void' and access_data['can_answer']:
|
||||
return request.render("survey.survey_void_content", {'survey': survey_sudo, 'answer': answer_sudo})
|
||||
elif error_key == 'survey_closed' and access_data['can_answer']:
|
||||
return request.render("survey.survey_closed_expired", {'survey': survey_sudo})
|
||||
elif error_key == 'survey_auth':
|
||||
if not answer_sudo: # survey is not even started
|
||||
redirect_url = '/web/login?redirect=/survey/start/%s' % survey_sudo.access_token
|
||||
elif answer_sudo.access_token: # survey is started but user is not logged in anymore.
|
||||
if answer_sudo.partner_id and (answer_sudo.partner_id.user_ids or survey_sudo.users_can_signup):
|
||||
if answer_sudo.partner_id.user_ids:
|
||||
answer_sudo.partner_id.signup_cancel()
|
||||
else:
|
||||
answer_sudo.partner_id.signup_prepare(expiration=fields.Datetime.now() + relativedelta(days=1))
|
||||
redirect_url = answer_sudo.partner_id._get_signup_url_for_action(url='/survey/start/%s?answer_token=%s' % (survey_sudo.access_token, answer_sudo.access_token))[answer_sudo.partner_id.id]
|
||||
else:
|
||||
redirect_url = '/web/login?redirect=%s' % ('/survey/start/%s?answer_token=%s' % (survey_sudo.access_token, answer_sudo.access_token))
|
||||
return request.render("survey.survey_auth_required", {'survey': survey_sudo, 'redirect_url': redirect_url})
|
||||
elif error_key == 'answer_deadline' and answer_sudo.access_token:
|
||||
return request.render("survey.survey_closed_expired", {'survey': survey_sudo})
|
||||
elif error_key in ['answer_wrong_user', 'token_wrong']:
|
||||
return request.render("survey.survey_access_error", {'survey': survey_sudo})
|
||||
|
||||
return request.redirect("/")
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# TEST / RETRY SURVEY ROUTES
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@http.route('/survey/test/<string:survey_token>', type='http', auth='user', website=True)
|
||||
def survey_test(self, survey_token, **kwargs):
|
||||
""" Test mode for surveys: create a test answer, only for managers or officers
|
||||
testing their surveys """
|
||||
survey_sudo, dummy = self._fetch_from_access_token(survey_token, False)
|
||||
try:
|
||||
answer_sudo = survey_sudo._create_answer(user=request.env.user, test_entry=True)
|
||||
except:
|
||||
return request.redirect('/')
|
||||
return request.redirect('/survey/start/%s?%s' % (survey_sudo.access_token, keep_query('*', answer_token=answer_sudo.access_token)))
|
||||
|
||||
@http.route('/survey/retry/<string:survey_token>/<string:answer_token>', type='http', auth='public', website=True)
|
||||
def survey_retry(self, survey_token, answer_token, **post):
|
||||
""" This route is called whenever the user has attempts left and hits the 'Retry' button
|
||||
after failing the survey."""
|
||||
access_data = self._get_access_data(survey_token, answer_token, ensure_token=True)
|
||||
if access_data['validity_code'] is not True:
|
||||
return self._redirect_with_error(access_data, access_data['validity_code'])
|
||||
|
||||
survey_sudo, answer_sudo = access_data['survey_sudo'], access_data['answer_sudo']
|
||||
if not answer_sudo:
|
||||
# attempts to 'retry' without having tried first
|
||||
return request.redirect("/")
|
||||
|
||||
try:
|
||||
retry_answer_sudo = survey_sudo._create_answer(
|
||||
user=request.env.user,
|
||||
partner=answer_sudo.partner_id,
|
||||
email=answer_sudo.email,
|
||||
invite_token=answer_sudo.invite_token,
|
||||
test_entry=answer_sudo.test_entry,
|
||||
**self._prepare_retry_additional_values(answer_sudo)
|
||||
)
|
||||
except:
|
||||
return request.redirect("/")
|
||||
return request.redirect('/survey/start/%s?%s' % (survey_sudo.access_token, keep_query('*', answer_token=retry_answer_sudo.access_token)))
|
||||
|
||||
def _prepare_retry_additional_values(self, answer):
|
||||
return {
|
||||
'deadline': answer.deadline,
|
||||
}
|
||||
|
||||
def _prepare_survey_finished_values(self, survey, answer, token=False):
|
||||
values = {'survey': survey, 'answer': answer}
|
||||
if token:
|
||||
values['token'] = token
|
||||
return values
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# TAKING SURVEY ROUTES
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@http.route('/survey/start/<string:survey_token>', type='http', auth='public', website=True)
|
||||
def survey_start(self, survey_token, answer_token=None, email=False, **post):
|
||||
""" Start a survey by providing
|
||||
* a token linked to a survey;
|
||||
* a token linked to an answer or generate a new token if access is allowed;
|
||||
"""
|
||||
# Get the current answer token from cookie
|
||||
answer_from_cookie = False
|
||||
if not answer_token:
|
||||
answer_token = request.httprequest.cookies.get('survey_%s' % survey_token)
|
||||
answer_from_cookie = bool(answer_token)
|
||||
|
||||
access_data = self._get_access_data(survey_token, answer_token, ensure_token=False)
|
||||
|
||||
if answer_from_cookie and access_data['validity_code'] in ('answer_wrong_user', 'token_wrong'):
|
||||
# If the cookie had been generated for another user or does not correspond to any existing answer object
|
||||
# (probably because it has been deleted), ignore it and redo the check.
|
||||
# The cookie will be replaced by a legit value when resolving the URL, so we don't clean it further here.
|
||||
access_data = self._get_access_data(survey_token, None, ensure_token=False)
|
||||
|
||||
if access_data['validity_code'] is not True:
|
||||
return self._redirect_with_error(access_data, access_data['validity_code'])
|
||||
|
||||
survey_sudo, answer_sudo = access_data['survey_sudo'], access_data['answer_sudo']
|
||||
if not answer_sudo:
|
||||
try:
|
||||
answer_sudo = survey_sudo._create_answer(user=request.env.user, email=email)
|
||||
except UserError:
|
||||
answer_sudo = False
|
||||
|
||||
if not answer_sudo:
|
||||
try:
|
||||
survey_sudo.with_user(request.env.user).check_access_rights('read')
|
||||
survey_sudo.with_user(request.env.user).check_access_rule('read')
|
||||
except:
|
||||
return request.redirect("/")
|
||||
else:
|
||||
return request.render("survey.survey_403_page", {'survey': survey_sudo})
|
||||
|
||||
return request.redirect('/survey/%s/%s' % (survey_sudo.access_token, answer_sudo.access_token))
|
||||
|
||||
def _prepare_survey_data(self, survey_sudo, answer_sudo, **post):
|
||||
""" This method prepares all the data needed for template rendering, in function of the survey user input state.
|
||||
:param post:
|
||||
- previous_page_id : come from the breadcrumb or the back button and force the next questions to load
|
||||
to be the previous ones.
|
||||
- next_skipped_page : force the display of next skipped question or page if any."""
|
||||
data = {
|
||||
'is_html_empty': is_html_empty,
|
||||
'survey': survey_sudo,
|
||||
'answer': answer_sudo,
|
||||
'skipped_questions': answer_sudo._get_skipped_questions(),
|
||||
'breadcrumb_pages': [{
|
||||
'id': page.id,
|
||||
'title': page.title,
|
||||
} for page in survey_sudo.page_ids],
|
||||
'format_datetime': lambda dt: format_datetime(request.env, dt, dt_format=False),
|
||||
'format_date': lambda date: format_date(request.env, date)
|
||||
}
|
||||
if survey_sudo.questions_layout != 'page_per_question':
|
||||
triggering_answers_by_question, triggered_questions_by_answer, selected_answers = answer_sudo._get_conditional_values()
|
||||
data.update({
|
||||
'triggering_answers_by_question': {
|
||||
question.id: triggering_answers.ids
|
||||
for question, triggering_answers in triggering_answers_by_question.items() if triggering_answers
|
||||
},
|
||||
'triggered_questions_by_answer': {
|
||||
answer.id: triggered_questions.ids
|
||||
for answer, triggered_questions in triggered_questions_by_answer.items()
|
||||
},
|
||||
'selected_answers': selected_answers.ids
|
||||
})
|
||||
|
||||
if not answer_sudo.is_session_answer and survey_sudo.is_time_limited and answer_sudo.start_datetime:
|
||||
data.update({
|
||||
'server_time': fields.Datetime.now(),
|
||||
'timer_start': answer_sudo.start_datetime.isoformat(),
|
||||
'time_limit_minutes': survey_sudo.time_limit
|
||||
})
|
||||
|
||||
page_or_question_key = 'question' if survey_sudo.questions_layout == 'page_per_question' else 'page'
|
||||
|
||||
# Bypass all if page_id is specified (comes from breadcrumb or previous button)
|
||||
if 'previous_page_id' in post:
|
||||
previous_page_or_question_id = int(post['previous_page_id'])
|
||||
new_previous_id = survey_sudo._get_next_page_or_question(answer_sudo, previous_page_or_question_id, go_back=True).id
|
||||
page_or_question = request.env['survey.question'].sudo().browse(previous_page_or_question_id)
|
||||
data.update({
|
||||
page_or_question_key: page_or_question,
|
||||
'previous_page_id': new_previous_id,
|
||||
'has_answered': answer_sudo.user_input_line_ids.filtered(lambda line: line.question_id.id == new_previous_id),
|
||||
'can_go_back': survey_sudo._can_go_back(answer_sudo, page_or_question),
|
||||
})
|
||||
return data
|
||||
|
||||
if answer_sudo.state == 'in_progress':
|
||||
next_page_or_question = None
|
||||
if answer_sudo.is_session_answer:
|
||||
next_page_or_question = survey_sudo.session_question_id
|
||||
else:
|
||||
if 'next_skipped_page' in post:
|
||||
next_page_or_question = answer_sudo._get_next_skipped_page_or_question()
|
||||
if not next_page_or_question:
|
||||
next_page_or_question = survey_sudo._get_next_page_or_question(
|
||||
answer_sudo,
|
||||
answer_sudo.last_displayed_page_id.id if answer_sudo.last_displayed_page_id else 0)
|
||||
|
||||
if next_page_or_question:
|
||||
if answer_sudo.survey_first_submitted:
|
||||
survey_last = answer_sudo._is_last_skipped_page_or_question(next_page_or_question)
|
||||
else:
|
||||
survey_last = survey_sudo._is_last_page_or_question(answer_sudo, next_page_or_question)
|
||||
data.update({'survey_last': survey_last})
|
||||
|
||||
if answer_sudo.is_session_answer and next_page_or_question.is_time_limited:
|
||||
data.update({
|
||||
'timer_start': survey_sudo.session_question_start_time.isoformat(),
|
||||
'time_limit_minutes': next_page_or_question.time_limit / 60
|
||||
})
|
||||
|
||||
data.update({
|
||||
page_or_question_key: next_page_or_question,
|
||||
'has_answered': answer_sudo.user_input_line_ids.filtered(lambda line: line.question_id == next_page_or_question),
|
||||
'can_go_back': survey_sudo._can_go_back(answer_sudo, next_page_or_question),
|
||||
})
|
||||
if survey_sudo.questions_layout != 'one_page':
|
||||
data.update({
|
||||
'previous_page_id': survey_sudo._get_next_page_or_question(answer_sudo, next_page_or_question.id, go_back=True).id
|
||||
})
|
||||
elif answer_sudo.state == 'done' or answer_sudo.survey_time_limit_reached:
|
||||
# Display success message
|
||||
return self._prepare_survey_finished_values(survey_sudo, answer_sudo)
|
||||
|
||||
return data
|
||||
|
||||
def _prepare_question_html(self, survey_sudo, answer_sudo, **post):
|
||||
""" Survey page navigation is done in AJAX. This function prepare the 'next page' to display in html
|
||||
and send back this html to the survey_form widget that will inject it into the page.
|
||||
Background url must be given to the caller in order to process its refresh as we don't have the next question
|
||||
object at frontend side."""
|
||||
survey_data = self._prepare_survey_data(survey_sudo, answer_sudo, **post)
|
||||
|
||||
if answer_sudo.state == 'done':
|
||||
survey_content = request.env['ir.qweb']._render('survey.survey_fill_form_done', survey_data)
|
||||
else:
|
||||
survey_content = request.env['ir.qweb']._render('survey.survey_fill_form_in_progress', survey_data)
|
||||
|
||||
survey_progress = False
|
||||
if answer_sudo.state == 'in_progress' and not survey_data.get('question', request.env['survey.question']).is_page:
|
||||
if survey_sudo.questions_layout == 'page_per_section':
|
||||
page_ids = survey_sudo.page_ids.ids
|
||||
survey_progress = request.env['ir.qweb']._render('survey.survey_progression', {
|
||||
'survey': survey_sudo,
|
||||
'page_ids': page_ids,
|
||||
'page_number': page_ids.index(survey_data['page'].id) + (1 if survey_sudo.progression_mode == 'number' else 0)
|
||||
})
|
||||
elif survey_sudo.questions_layout == 'page_per_question':
|
||||
page_ids = (answer_sudo.predefined_question_ids.ids
|
||||
if not answer_sudo.is_session_answer and survey_sudo.questions_selection == 'random'
|
||||
else survey_sudo.question_ids.ids)
|
||||
survey_progress = request.env['ir.qweb']._render('survey.survey_progression', {
|
||||
'survey': survey_sudo,
|
||||
'page_ids': page_ids,
|
||||
'page_number': page_ids.index(survey_data['question'].id)
|
||||
})
|
||||
|
||||
background_image_url = survey_sudo.background_image_url
|
||||
if 'question' in survey_data:
|
||||
background_image_url = survey_data['question'].background_image_url
|
||||
elif 'page' in survey_data:
|
||||
background_image_url = survey_data['page'].background_image_url
|
||||
|
||||
return {
|
||||
'has_skipped_questions': any(answer_sudo._get_skipped_questions()),
|
||||
'survey_content': survey_content,
|
||||
'survey_progress': survey_progress,
|
||||
'survey_navigation': request.env['ir.qweb']._render('survey.survey_navigation', survey_data),
|
||||
'background_image_url': background_image_url
|
||||
}
|
||||
|
||||
@http.route('/survey/<string:survey_token>/<string:answer_token>', type='http', auth='public', website=True)
|
||||
def survey_display_page(self, survey_token, answer_token, **post):
|
||||
access_data = self._get_access_data(survey_token, answer_token, ensure_token=True)
|
||||
if access_data['validity_code'] is not True:
|
||||
return self._redirect_with_error(access_data, access_data['validity_code'])
|
||||
|
||||
answer_sudo = access_data['answer_sudo']
|
||||
if answer_sudo.state != 'done' and answer_sudo.survey_time_limit_reached:
|
||||
answer_sudo._mark_done()
|
||||
|
||||
return request.render('survey.survey_page_fill',
|
||||
self._prepare_survey_data(access_data['survey_sudo'], answer_sudo, **post))
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# ROUTES to handle question images + survey background transitions + Tool
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
@http.route('/survey/<string:survey_token>/get_background_image',
|
||||
type='http', auth="public", website=True, sitemap=False)
|
||||
def survey_get_background(self, survey_token):
|
||||
survey_sudo, dummy = self._fetch_from_access_token(survey_token, False)
|
||||
return request.env['ir.binary']._get_image_stream_from(
|
||||
survey_sudo, 'background_image'
|
||||
).get_response()
|
||||
|
||||
@http.route('/survey/<string:survey_token>/<int:section_id>/get_background_image',
|
||||
type='http', auth="public", website=True, sitemap=False)
|
||||
def survey_section_get_background(self, survey_token, section_id):
|
||||
survey_sudo, dummy = self._fetch_from_access_token(survey_token, False)
|
||||
|
||||
section = survey_sudo.page_ids.filtered(lambda q: q.id == section_id)
|
||||
if not section:
|
||||
# trying to access a question that is not in this survey
|
||||
raise werkzeug.exceptions.Forbidden()
|
||||
|
||||
return request.env['ir.binary']._get_image_stream_from(
|
||||
section, 'background_image'
|
||||
).get_response()
|
||||
|
||||
@http.route('/survey/get_question_image/<string:survey_token>/<string:answer_token>/<int:question_id>/<int:suggested_answer_id>', type='http', auth="public", website=True, sitemap=False)
|
||||
def survey_get_question_image(self, survey_token, answer_token, question_id, suggested_answer_id):
|
||||
access_data = self._get_access_data(survey_token, answer_token, ensure_token=True)
|
||||
if access_data['validity_code'] is not True:
|
||||
return werkzeug.exceptions.Forbidden()
|
||||
|
||||
survey_sudo, answer_sudo = access_data['survey_sudo'], access_data['answer_sudo']
|
||||
|
||||
suggested_answer = False
|
||||
if int(question_id) in survey_sudo.question_ids.ids:
|
||||
suggested_answer = request.env['survey.question.answer'].sudo().search([
|
||||
('id', '=', int(suggested_answer_id)),
|
||||
('question_id', '=', int(question_id)),
|
||||
('question_id.survey_id', '=', survey_sudo.id),
|
||||
])
|
||||
|
||||
if not suggested_answer:
|
||||
return werkzeug.exceptions.NotFound()
|
||||
|
||||
return request.env['ir.binary']._get_image_stream_from(
|
||||
suggested_answer, 'value_image'
|
||||
).get_response()
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# JSON ROUTES to begin / continue survey (ajax navigation) + Tools
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
@http.route('/survey/begin/<string:survey_token>/<string:answer_token>', type='json', auth='public', website=True)
|
||||
def survey_begin(self, survey_token, answer_token, **post):
|
||||
""" Route used to start the survey user input and display the first survey page.
|
||||
Returns an empty dict for the correct answers and the first page html. """
|
||||
access_data = self._get_access_data(survey_token, answer_token, ensure_token=True)
|
||||
if access_data['validity_code'] is not True:
|
||||
return {}, {'error': access_data['validity_code']}
|
||||
survey_sudo, answer_sudo = access_data['survey_sudo'], access_data['answer_sudo']
|
||||
|
||||
if answer_sudo.state != "new":
|
||||
return {}, {'error': _("The survey has already started.")}
|
||||
|
||||
answer_sudo._mark_in_progress()
|
||||
return {}, self._prepare_question_html(survey_sudo, answer_sudo, **post)
|
||||
|
||||
@http.route('/survey/next_question/<string:survey_token>/<string:answer_token>', type='json', auth='public', website=True)
|
||||
def survey_next_question(self, survey_token, answer_token, **post):
|
||||
""" Method used to display the next survey question in an ongoing session.
|
||||
Triggered on all attendees screens when the host goes to the next question. """
|
||||
access_data = self._get_access_data(survey_token, answer_token, ensure_token=True)
|
||||
if access_data['validity_code'] is not True:
|
||||
return {}, {'error': access_data['validity_code']}
|
||||
survey_sudo, answer_sudo = access_data['survey_sudo'], access_data['answer_sudo']
|
||||
|
||||
if answer_sudo.state == 'new' and answer_sudo.is_session_answer:
|
||||
answer_sudo._mark_in_progress()
|
||||
|
||||
return {}, self._prepare_question_html(survey_sudo, answer_sudo, **post)
|
||||
|
||||
@http.route('/survey/submit/<string:survey_token>/<string:answer_token>', type='json', auth='public', website=True)
|
||||
def survey_submit(self, survey_token, answer_token, **post):
|
||||
""" Submit a page from the survey.
|
||||
This will take into account the validation errors and store the answers to the questions.
|
||||
If the time limit is reached, errors will be skipped, answers will be ignored and
|
||||
survey state will be forced to 'done'.
|
||||
Also returns the correct answers if the scoring type is 'scoring_with_answers_after_page'."""
|
||||
# Survey Validation
|
||||
access_data = self._get_access_data(survey_token, answer_token, ensure_token=True)
|
||||
if access_data['validity_code'] is not True:
|
||||
return {}, {'error': access_data['validity_code']}
|
||||
survey_sudo, answer_sudo = access_data['survey_sudo'], access_data['answer_sudo']
|
||||
|
||||
if answer_sudo.state == 'done':
|
||||
return {}, {'error': 'unauthorized'}
|
||||
|
||||
questions, page_or_question_id = survey_sudo._get_survey_questions(answer=answer_sudo,
|
||||
page_id=post.get('page_id'),
|
||||
question_id=post.get('question_id'))
|
||||
|
||||
if not answer_sudo.test_entry and not survey_sudo._has_attempts_left(answer_sudo.partner_id, answer_sudo.email, answer_sudo.invite_token):
|
||||
# prevent cheating with users creating multiple 'user_input' before their last attempt
|
||||
return {}, {'error': 'unauthorized'}
|
||||
|
||||
if answer_sudo.survey_time_limit_reached or answer_sudo.question_time_limit_reached:
|
||||
if answer_sudo.question_time_limit_reached:
|
||||
time_limit = survey_sudo.session_question_start_time + relativedelta(
|
||||
seconds=survey_sudo.session_question_id.time_limit
|
||||
)
|
||||
time_limit += timedelta(seconds=3)
|
||||
else:
|
||||
time_limit = answer_sudo.start_datetime + timedelta(minutes=survey_sudo.time_limit)
|
||||
time_limit += timedelta(seconds=10)
|
||||
if fields.Datetime.now() > time_limit:
|
||||
# prevent cheating with users blocking the JS timer and taking all their time to answer
|
||||
return {}, {'error': 'unauthorized'}
|
||||
|
||||
errors = {}
|
||||
# Prepare answers / comment by question, validate and save answers
|
||||
for question in questions:
|
||||
inactive_questions = request.env['survey.question'] if answer_sudo.is_session_answer else answer_sudo._get_inactive_conditional_questions()
|
||||
if question in inactive_questions: # if question is inactive, skip validation and save
|
||||
continue
|
||||
answer, comment = self._extract_comment_from_answers(question, post.get(str(question.id)))
|
||||
errors.update(question.validate_question(answer, comment))
|
||||
if not errors.get(question.id):
|
||||
answer_sudo._save_lines(question, answer, comment, overwrite_existing=survey_sudo.users_can_go_back)
|
||||
|
||||
if errors and not (answer_sudo.survey_time_limit_reached or answer_sudo.question_time_limit_reached):
|
||||
return {}, {'error': 'validation', 'fields': errors}
|
||||
|
||||
if not answer_sudo.is_session_answer:
|
||||
answer_sudo._clear_inactive_conditional_answers()
|
||||
|
||||
# Get the page questions correct answers if scoring type is scoring after page
|
||||
correct_answers = {}
|
||||
if survey_sudo.scoring_type == 'scoring_with_answers_after_page':
|
||||
scorable_questions = (questions - answer_sudo._get_inactive_conditional_questions()).filtered('is_scored_question')
|
||||
correct_answers = scorable_questions._get_correct_answers()
|
||||
|
||||
if answer_sudo.survey_time_limit_reached or survey_sudo.questions_layout == 'one_page':
|
||||
answer_sudo._mark_done()
|
||||
elif 'previous_page_id' in post:
|
||||
# when going back, save the last displayed to reload the survey where the user left it.
|
||||
answer_sudo.last_displayed_page_id = post['previous_page_id']
|
||||
# Go back to specific page using the breadcrumb. Lines are saved and survey continues
|
||||
return correct_answers, self._prepare_question_html(survey_sudo, answer_sudo, **post)
|
||||
elif 'next_skipped_page_or_question' in post:
|
||||
answer_sudo.last_displayed_page_id = page_or_question_id
|
||||
return correct_answers, self._prepare_question_html(survey_sudo, answer_sudo, next_skipped_page=True)
|
||||
else:
|
||||
if not answer_sudo.is_session_answer:
|
||||
page_or_question = request.env['survey.question'].sudo().browse(page_or_question_id)
|
||||
if answer_sudo.survey_first_submitted and answer_sudo._is_last_skipped_page_or_question(page_or_question):
|
||||
next_page = request.env['survey.question']
|
||||
else:
|
||||
next_page = survey_sudo._get_next_page_or_question(answer_sudo, page_or_question_id)
|
||||
if not next_page:
|
||||
if survey_sudo.users_can_go_back and answer_sudo.user_input_line_ids.filtered(
|
||||
lambda a: a.skipped and a.question_id.constr_mandatory):
|
||||
answer_sudo.write({
|
||||
'last_displayed_page_id': page_or_question_id,
|
||||
'survey_first_submitted': True,
|
||||
})
|
||||
return correct_answers, self._prepare_question_html(survey_sudo, answer_sudo, next_skipped_page=True)
|
||||
else:
|
||||
answer_sudo._mark_done()
|
||||
|
||||
answer_sudo.last_displayed_page_id = page_or_question_id
|
||||
|
||||
return correct_answers, self._prepare_question_html(survey_sudo, answer_sudo)
|
||||
|
||||
def _extract_comment_from_answers(self, question, answers):
|
||||
""" Answers is a custom structure depending of the question type
|
||||
that can contain question answers but also comments that need to be
|
||||
extracted before validating and saving answers.
|
||||
If multiple answers, they are listed in an array, except for matrix
|
||||
where answers are structured differently. See input and output for
|
||||
more info on data structures.
|
||||
:param question: survey.question
|
||||
:param answers:
|
||||
* question_type: free_text, text_box, numerical_box, date, datetime
|
||||
answers is a string containing the value
|
||||
* question_type: simple_choice with no comment
|
||||
answers is a string containing the value ('question_id_1')
|
||||
* question_type: simple_choice with comment
|
||||
['question_id_1', {'comment': str}]
|
||||
* question_type: multiple choice
|
||||
['question_id_1', 'question_id_2'] + [{'comment': str}] if holds a comment
|
||||
* question_type: matrix
|
||||
{'matrix_row_id_1': ['question_id_1', 'question_id_2'],
|
||||
'matrix_row_id_2': ['question_id_1', 'question_id_2']
|
||||
} + {'comment': str} if holds a comment
|
||||
:return: tuple(
|
||||
same structure without comment,
|
||||
extracted comment for given question
|
||||
) """
|
||||
comment = None
|
||||
answers_no_comment = []
|
||||
if answers:
|
||||
if question.question_type == 'matrix':
|
||||
if 'comment' in answers:
|
||||
comment = answers['comment'].strip()
|
||||
answers.pop('comment')
|
||||
answers_no_comment = answers
|
||||
else:
|
||||
if not isinstance(answers, list):
|
||||
answers = [answers]
|
||||
for answer in answers:
|
||||
if isinstance(answer, dict) and 'comment' in answer:
|
||||
comment = answer['comment'].strip()
|
||||
else:
|
||||
answers_no_comment.append(answer)
|
||||
if len(answers_no_comment) == 1:
|
||||
answers_no_comment = answers_no_comment[0]
|
||||
return answers_no_comment, comment
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# COMPLETED SURVEY ROUTES
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@http.route('/survey/print/<string:survey_token>', type='http', auth='public', website=True, sitemap=False)
|
||||
def survey_print(self, survey_token, review=False, answer_token=None, **post):
|
||||
'''Display an survey in printable view; if <answer_token> is set, it will
|
||||
grab the answers of the user_input_id that has <answer_token>.'''
|
||||
access_data = self._get_access_data(survey_token, answer_token, ensure_token=False, check_partner=False)
|
||||
if access_data['validity_code'] is not True and (
|
||||
access_data['has_survey_access'] or
|
||||
access_data['validity_code'] not in ['token_required', 'survey_closed', 'survey_void', 'answer_deadline']):
|
||||
return self._redirect_with_error(access_data, access_data['validity_code'])
|
||||
|
||||
survey_sudo, answer_sudo = access_data['survey_sudo'], access_data['answer_sudo']
|
||||
return request.render('survey.survey_page_print', {
|
||||
'is_html_empty': is_html_empty,
|
||||
'review': review,
|
||||
'survey': survey_sudo,
|
||||
'answer': answer_sudo if survey_sudo.scoring_type != 'scoring_without_answers' else answer_sudo.browse(),
|
||||
'questions_to_display': answer_sudo._get_print_questions(),
|
||||
'scoring_display_correction': survey_sudo.scoring_type in ['scoring_with_answers', 'scoring_with_answers_after_page'] and answer_sudo,
|
||||
'format_datetime': lambda dt: format_datetime(request.env, dt, dt_format=False),
|
||||
'format_date': lambda date: format_date(request.env, date),
|
||||
'graph_data': json.dumps(answer_sudo._prepare_statistics()[answer_sudo])
|
||||
if answer_sudo and survey_sudo.scoring_type in ['scoring_with_answers', 'scoring_with_answers_after_page'] else False,
|
||||
})
|
||||
|
||||
@http.route('/survey/<model("survey.survey"):survey>/certification_preview', type="http", auth="user", website=True)
|
||||
def show_certification_pdf(self, survey, **kwargs):
|
||||
preview_url = '/survey/%s/get_certification_preview' % survey.id
|
||||
return request.render('survey.certification_preview', {
|
||||
'preview_url': preview_url,
|
||||
'page_title': survey.title,
|
||||
})
|
||||
|
||||
@http.route(['/survey/<model("survey.survey"):survey>/get_certification_preview'], type="http", auth="user", methods=['GET'], website=True)
|
||||
def survey_get_certification_preview(self, survey, **kwargs):
|
||||
if not request.env.user.has_group('survey.group_survey_user'):
|
||||
raise werkzeug.exceptions.Forbidden()
|
||||
|
||||
fake_user_input = survey._create_answer(user=request.env.user, test_entry=True)
|
||||
response = self._generate_report(fake_user_input, download=False)
|
||||
fake_user_input.sudo().unlink()
|
||||
return response
|
||||
|
||||
@http.route(['/survey/<int:survey_id>/get_certification'], type='http', auth='user', methods=['GET'], website=True)
|
||||
def survey_get_certification(self, survey_id, **kwargs):
|
||||
""" The certification document can be downloaded as long as the user has succeeded the certification """
|
||||
survey = request.env['survey.survey'].sudo().search([
|
||||
('id', '=', survey_id),
|
||||
('certification', '=', True)
|
||||
])
|
||||
|
||||
if not survey:
|
||||
# no certification found
|
||||
return request.redirect("/")
|
||||
|
||||
succeeded_attempt = request.env['survey.user_input'].sudo().search([
|
||||
('partner_id', '=', request.env.user.partner_id.id),
|
||||
('survey_id', '=', survey_id),
|
||||
('scoring_success', '=', True)
|
||||
], limit=1)
|
||||
|
||||
if not succeeded_attempt:
|
||||
raise UserError(_("The user has not succeeded the certification"))
|
||||
|
||||
return self._generate_report(succeeded_attempt, download=True)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# REPORTING SURVEY ROUTES AND TOOLS
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@http.route('/survey/results/<model("survey.survey"):survey>', type='http', auth='user', website=True)
|
||||
def survey_report(self, survey, answer_token=None, **post):
|
||||
""" Display survey Results & Statistics for given survey.
|
||||
|
||||
New structure: {
|
||||
'survey': current survey browse record,
|
||||
'question_and_page_data': see ``SurveyQuestion._prepare_statistics()``,
|
||||
'survey_data'= see ``SurveySurvey._prepare_statistics()``
|
||||
'search_filters': [],
|
||||
'search_finished': either filter on finished inputs only or not,
|
||||
'search_passed': either filter on passed inputs only or not,
|
||||
'search_failed': either filter on failed inputs only or not,
|
||||
}
|
||||
"""
|
||||
user_input_lines, search_filters = self._extract_filters_data(survey, post)
|
||||
survey_data = survey._prepare_statistics(user_input_lines)
|
||||
question_and_page_data = survey.question_and_page_ids._prepare_statistics(user_input_lines)
|
||||
|
||||
template_values = {
|
||||
# survey and its statistics
|
||||
'survey': survey,
|
||||
'question_and_page_data': question_and_page_data,
|
||||
'survey_data': survey_data,
|
||||
# search
|
||||
'search_filters': search_filters,
|
||||
'search_finished': post.get('finished') == 'true',
|
||||
'search_failed': post.get('failed') == 'true',
|
||||
'search_passed': post.get('passed') == 'true',
|
||||
}
|
||||
|
||||
if survey.session_show_leaderboard:
|
||||
template_values['leaderboard'] = survey._prepare_leaderboard_values()
|
||||
|
||||
return request.render('survey.survey_page_statistics', template_values)
|
||||
|
||||
def _generate_report(self, user_input, download=True):
|
||||
report = request.env["ir.actions.report"].sudo()._render_qweb_pdf('survey.certification_report', [user_input.id], data={'report_type': 'pdf'})[0]
|
||||
|
||||
report_content_disposition = content_disposition('Certification.pdf')
|
||||
if not download:
|
||||
content_split = report_content_disposition.split(';')
|
||||
content_split[0] = 'inline'
|
||||
report_content_disposition = ';'.join(content_split)
|
||||
|
||||
return request.make_response(report, headers=[
|
||||
('Content-Type', 'application/pdf'),
|
||||
('Content-Length', len(report)),
|
||||
('Content-Disposition', report_content_disposition),
|
||||
])
|
||||
|
||||
def _get_results_page_user_input_domain(self, survey, **post):
|
||||
user_input_domain = ['&', ('test_entry', '=', False), ('survey_id', '=', survey.id)]
|
||||
if post.get('finished'):
|
||||
user_input_domain = expression.AND([[('state', '=', 'done')], user_input_domain])
|
||||
else:
|
||||
user_input_domain = expression.AND([[('state', '!=', 'new')], user_input_domain])
|
||||
if post.get('failed'):
|
||||
user_input_domain = expression.AND([[('scoring_success', '=', False)], user_input_domain])
|
||||
elif post.get('passed'):
|
||||
user_input_domain = expression.AND([[('scoring_success', '=', True)], user_input_domain])
|
||||
|
||||
return user_input_domain
|
||||
|
||||
def _extract_filters_data(self, survey, post):
|
||||
""" Extracts the filters from the URL to returns the related user_input_lines and
|
||||
the parameters used to render/remove the filters on the results page (search_filters).
|
||||
|
||||
The matching user_input_lines are all the lines tied to the user inputs which respect
|
||||
the survey base domain and which have lines matching all the filters.
|
||||
For example, with the filter 'Where do you live?|Brussels', we need to display ALL the lines
|
||||
of the survey user inputs which have answered 'Brussels' to this question.
|
||||
|
||||
:return (recordset, List[dict]): all matching user input lines, each search filter data
|
||||
"""
|
||||
user_input_line_subdomains = []
|
||||
search_filters = []
|
||||
|
||||
answer_by_column, user_input_lines_ids = self._get_filters_from_post(post)
|
||||
|
||||
# Matrix, Multiple choice, Simple choice filters
|
||||
if answer_by_column:
|
||||
answer_ids, row_ids = [], []
|
||||
for answer_column_id, answer_row_ids in answer_by_column.items():
|
||||
answer_ids.append(answer_column_id)
|
||||
row_ids += answer_row_ids
|
||||
|
||||
answers_and_rows = request.env['survey.question.answer'].browse(answer_ids+row_ids)
|
||||
# For performance, accessing 'a.matrix_question_id' caches all useful fields of the
|
||||
# answers and rows records, avoiding unnecessary queries.
|
||||
answers = answers_and_rows.filtered(lambda a: not a.matrix_question_id)
|
||||
|
||||
for answer in answers:
|
||||
if not answer_by_column[answer.id]:
|
||||
# Simple/Multiple choice
|
||||
user_input_line_subdomains.append(answer._get_answer_matching_domain())
|
||||
search_filters.append(self._prepare_search_filter_answer(answer))
|
||||
else:
|
||||
# Matrix
|
||||
for row_id in answer_by_column[answer.id]:
|
||||
row = answers_and_rows.filtered(lambda answer_or_row: answer_or_row.id == row_id)
|
||||
user_input_line_subdomains.append(answer._get_answer_matching_domain(row_id))
|
||||
search_filters.append(self._prepare_search_filter_answer(answer, row))
|
||||
|
||||
# Char_box, Text_box, Numerical_box, Date, Datetime filters
|
||||
if user_input_lines_ids:
|
||||
user_input_lines = request.env['survey.user_input.line'].browse(user_input_lines_ids)
|
||||
for input_line in user_input_lines:
|
||||
user_input_line_subdomains.append(input_line._get_answer_matching_domain())
|
||||
search_filters.append(self._prepare_search_filter_input_line(input_line))
|
||||
|
||||
# Compute base domain
|
||||
user_input_domain = self._get_results_page_user_input_domain(survey, **post)
|
||||
|
||||
# Add filters domain to the base domain
|
||||
if user_input_line_subdomains:
|
||||
all_required_lines_domains = [
|
||||
[('user_input_line_ids', 'in', request.env['survey.user_input.line'].sudo()._search(subdomain))]
|
||||
for subdomain in user_input_line_subdomains
|
||||
]
|
||||
user_input_domain = expression.AND([user_input_domain, *all_required_lines_domains])
|
||||
|
||||
# Get the matching user input lines
|
||||
user_inputs_query = request.env['survey.user_input'].sudo()._search(user_input_domain)
|
||||
user_input_lines = request.env['survey.user_input.line'].search([('user_input_id', 'in', user_inputs_query)])
|
||||
|
||||
return user_input_lines, search_filters
|
||||
|
||||
def _get_filters_from_post(self, post):
|
||||
""" Extract the filters from post depending on the model that needs to be called to retrieve the filtered answer data.
|
||||
Simple choice and multiple choice question types are mapped onto empty row_id.
|
||||
Input/output example with respectively matrix, simple_choice and char_box filters:
|
||||
input: 'A,1,24|A,0,13|L,0,36'
|
||||
output:
|
||||
answer_by_column: {24: [1], 13: []}
|
||||
user_input_lines_ids: [36]
|
||||
|
||||
* Model short key = 'A' : Match a `survey.question.answer` record (simple_choice, multiple_choice, matrix)
|
||||
* Model short key = 'L' : Match a `survey.user_input.line` record (char_box, text_box, numerical_box, date, datetime)
|
||||
:rtype: (collections.defaultdict[int, list[int]], list[int])
|
||||
"""
|
||||
answer_by_column = defaultdict(list)
|
||||
user_input_lines_ids = []
|
||||
|
||||
for data in post.get('filters', '').split('|'):
|
||||
if not data:
|
||||
break
|
||||
model_short_key, row_id, answer_id = data.split(',')
|
||||
row_id, answer_id = int(row_id), int(answer_id)
|
||||
if model_short_key == 'A':
|
||||
if row_id:
|
||||
answer_by_column[answer_id].append(row_id)
|
||||
else:
|
||||
answer_by_column[answer_id] = []
|
||||
elif model_short_key == 'L' and not row_id:
|
||||
user_input_lines_ids.append(answer_id)
|
||||
|
||||
return answer_by_column, user_input_lines_ids
|
||||
|
||||
def _prepare_search_filter_answer(self, answer, row=False):
|
||||
""" Format parameters used to render/remove this filter on the results page."""
|
||||
return {
|
||||
'question_id': answer.question_id.id,
|
||||
'question': answer.question_id.title,
|
||||
'row_id': row.id if row else 0,
|
||||
'answer': '%s : %s' % (row.value, answer.value) if row else answer.value,
|
||||
'model_short_key': 'A',
|
||||
'record_id': answer.id,
|
||||
}
|
||||
|
||||
def _prepare_search_filter_input_line(self, user_input_line):
|
||||
""" Format parameters used to render/remove this filter on the results page."""
|
||||
return {
|
||||
'question_id': user_input_line.question_id.id,
|
||||
'question': user_input_line.question_id.title,
|
||||
'row_id': 0,
|
||||
'answer': user_input_line._get_answer_value(),
|
||||
'model_short_key': 'L',
|
||||
'record_id': user_input_line.id,
|
||||
}
|
251
controllers/survey_session_manage.py
Normal file
|
@ -0,0 +1,251 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import datetime
|
||||
import json
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
from odoo import fields, http
|
||||
from odoo.http import request
|
||||
from odoo.tools import is_html_empty
|
||||
|
||||
|
||||
class UserInputSession(http.Controller):
|
||||
def _fetch_from_token(self, survey_token):
|
||||
""" Check that given survey_token matches a survey 'access_token'.
|
||||
Unlike the regular survey controller, user trying to access the survey must have full access rights! """
|
||||
return request.env['survey.survey'].search([('access_token', '=', survey_token)])
|
||||
|
||||
def _fetch_from_session_code(self, session_code):
|
||||
""" Matches a survey against a passed session_code, and checks if it is valid.
|
||||
If it is valid, returns the start url. Else, the error type."""
|
||||
if not session_code:
|
||||
return None, {'error': 'survey_wrong'}
|
||||
survey = request.env['survey.survey'].sudo().search([('session_code', '=', session_code)], limit=1)
|
||||
if not survey or survey.certification:
|
||||
return None, {'error': 'survey_wrong'}
|
||||
if survey.session_state in ['ready', 'in_progress']:
|
||||
return survey, None
|
||||
if request.env.user.has_group("survey.group_survey_user"):
|
||||
return None, {'error': 'survey_session_not_launched', 'survey_id': survey.id}
|
||||
return None, {'error': 'survey_session_not_launched'}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# SURVEY SESSION MANAGEMENT
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@http.route('/survey/session/manage/<string:survey_token>', type='http', auth='user', website=True)
|
||||
def survey_session_manage(self, survey_token, **kwargs):
|
||||
""" Main route used by the host to 'manager' the session.
|
||||
- If the state of the session is 'ready'
|
||||
We render a template allowing the host to showcase the different options of the session
|
||||
and to actually start the session.
|
||||
If there are no questions, a "void content" is displayed instead to avoid displaying a
|
||||
blank survey.
|
||||
- If the state of the session is 'in_progress'
|
||||
We render a template allowing the host to show the question results, display the attendees
|
||||
leaderboard or go to the next question of the session. """
|
||||
|
||||
survey = self._fetch_from_token(survey_token)
|
||||
|
||||
if not survey:
|
||||
return NotFound()
|
||||
|
||||
if survey.session_state == 'ready':
|
||||
if not survey.question_ids:
|
||||
return request.render('survey.survey_void_content', {
|
||||
'survey': survey,
|
||||
'answer': request.env['survey.user_input'],
|
||||
})
|
||||
return request.render('survey.user_input_session_open', {
|
||||
'survey': survey
|
||||
})
|
||||
# Note that at this stage survey.session_state can be False meaning that the survey has ended (session closed)
|
||||
return request.render('survey.user_input_session_manage', self._prepare_manage_session_values(survey))
|
||||
|
||||
@http.route('/survey/session/next_question/<string:survey_token>', type='json', auth='user', website=True)
|
||||
def survey_session_next_question(self, survey_token, go_back=False, **kwargs):
|
||||
""" This route is called when the host goes to the next question of the session.
|
||||
|
||||
It's not a regular 'request.render' route because we handle the transition between
|
||||
questions using a AJAX call to be able to display a bioutiful fade in/out effect.
|
||||
|
||||
It triggers the next question of the session.
|
||||
|
||||
We artificially add 1 second to the 'current_question_start_time' to account for server delay.
|
||||
As the timing can influence the attendees score, we try to be fair with everyone by giving them
|
||||
an extra second before we start counting down.
|
||||
|
||||
Frontend should take the delay into account by displaying the appropriate animations.
|
||||
|
||||
Writing the next question on the survey is sudo'ed to avoid potential access right issues.
|
||||
e.g: a survey user can create a live session from any survey but they can only write
|
||||
on their own survey.
|
||||
|
||||
In addition to return a pre-rendered html template with the next question, we also return the background
|
||||
to display. Background image depends on the next question to display and cannot be extracted from the
|
||||
html rendered question template. The background needs to be changed at frontend side on a specific selector."""
|
||||
|
||||
survey = self._fetch_from_token(survey_token)
|
||||
|
||||
if not survey or not survey.session_state:
|
||||
# no open session
|
||||
return {}
|
||||
|
||||
if survey.session_state == 'ready':
|
||||
survey._session_open()
|
||||
|
||||
next_question = survey._get_session_next_question(go_back)
|
||||
|
||||
# using datetime.datetime because we want the millis portion
|
||||
if next_question:
|
||||
now = datetime.datetime.now()
|
||||
survey.sudo().write({
|
||||
'session_question_id': next_question.id,
|
||||
'session_question_start_time': fields.Datetime.now() + relativedelta(seconds=1)
|
||||
})
|
||||
request.env['bus.bus']._sendone(survey.access_token, 'next_question', {
|
||||
'question_start': now.timestamp()
|
||||
})
|
||||
|
||||
template_values = self._prepare_manage_session_values(survey)
|
||||
template_values['is_rpc_call'] = True
|
||||
|
||||
return {
|
||||
'background_image_url': survey.session_question_id.background_image_url,
|
||||
'question_html': request.env['ir.qweb']._render('survey.user_input_session_manage_content', template_values)
|
||||
}
|
||||
else:
|
||||
return {}
|
||||
|
||||
@http.route('/survey/session/results/<string:survey_token>', type='json', auth='user', website=True)
|
||||
def survey_session_results(self, survey_token, **kwargs):
|
||||
""" This route is called when the host shows the current question's results.
|
||||
|
||||
It's not a regular 'request.render' route because we handle the display of results using
|
||||
an AJAX request to be able to include the results in the currently displayed page. """
|
||||
|
||||
survey = self._fetch_from_token(survey_token)
|
||||
|
||||
if not survey or survey.session_state != 'in_progress':
|
||||
# no open session
|
||||
return False
|
||||
|
||||
user_input_lines = request.env['survey.user_input.line'].search([
|
||||
('survey_id', '=', survey.id),
|
||||
('question_id', '=', survey.session_question_id.id),
|
||||
('create_date', '>=', survey.session_start_time)
|
||||
])
|
||||
|
||||
return self._prepare_question_results_values(survey, user_input_lines)
|
||||
|
||||
@http.route('/survey/session/leaderboard/<string:survey_token>', type='json', auth='user', website=True)
|
||||
def survey_session_leaderboard(self, survey_token, **kwargs):
|
||||
""" This route is called when the host shows the current question's attendees leaderboard.
|
||||
|
||||
It's not a regular 'request.render' route because we handle the display of the leaderboard
|
||||
using an AJAX request to be able to include the results in the currently displayed page. """
|
||||
|
||||
survey = self._fetch_from_token(survey_token)
|
||||
|
||||
if not survey or survey.session_state != 'in_progress':
|
||||
# no open session
|
||||
return ''
|
||||
|
||||
return request.env['ir.qweb']._render('survey.user_input_session_leaderboard', {
|
||||
'animate': True,
|
||||
'leaderboard': survey._prepare_leaderboard_values()
|
||||
})
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# QUICK ACCESS SURVEY ROUTES
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@http.route('/s', type='http', auth='public', website=True, sitemap=False)
|
||||
def survey_session_code(self, **post):
|
||||
""" Renders the survey session code page route.
|
||||
This page allows the user to enter the session code of the survey.
|
||||
It is mainly used to ease survey access for attendees in session mode. """
|
||||
return request.render("survey.survey_session_code")
|
||||
|
||||
@http.route('/s/<string:session_code>', type='http', auth='public', website=True)
|
||||
def survey_start_short(self, session_code):
|
||||
"""" Redirects to 'survey_start' route using a shortened link & token.
|
||||
Shows an error message if the survey is not valid.
|
||||
This route is used in survey sessions where we need short links for people to type. """
|
||||
survey, survey_error = self._fetch_from_session_code(session_code)
|
||||
|
||||
if survey_error:
|
||||
return request.render('survey.survey_session_code',
|
||||
dict(**survey_error, session_code=session_code))
|
||||
return request.redirect(survey.get_start_url())
|
||||
|
||||
@http.route('/survey/check_session_code/<string:session_code>', type='json', auth='public', website=True)
|
||||
def survey_check_session_code(self, session_code):
|
||||
""" Checks if the given code is matching a survey session_code.
|
||||
If yes, redirect to /s/code route.
|
||||
If not, return error. The user is invited to type again the code."""
|
||||
survey, survey_error = self._fetch_from_session_code(session_code)
|
||||
if survey_error:
|
||||
return survey_error
|
||||
return {'survey_url': survey.get_start_url()}
|
||||
|
||||
def _prepare_manage_session_values(self, survey):
|
||||
is_first_question, is_last_question = False, False
|
||||
if survey.question_ids:
|
||||
most_voted_answers = survey._get_session_most_voted_answers()
|
||||
is_first_question = survey._is_first_page_or_question(survey.session_question_id)
|
||||
is_last_question = survey._is_last_page_or_question(most_voted_answers, survey.session_question_id)
|
||||
|
||||
values = {
|
||||
'survey': survey,
|
||||
'is_last_question': is_last_question,
|
||||
'is_first_question': is_first_question,
|
||||
'is_session_closed': not survey.session_state,
|
||||
}
|
||||
|
||||
values.update(self._prepare_question_results_values(survey, request.env['survey.user_input.line']))
|
||||
|
||||
return values
|
||||
|
||||
def _prepare_question_results_values(self, survey, user_input_lines):
|
||||
""" Prepares usefull values to display during the host session:
|
||||
|
||||
- question_statistics_graph
|
||||
The graph data to display the bar chart for questions of type 'choice'
|
||||
- input_lines_values
|
||||
The answer values to text/date/datetime questions
|
||||
- answers_validity
|
||||
An array containing the is_correct value for all question answers.
|
||||
We need this special variable because of Chartjs data structure.
|
||||
The library determines the parameters (color/label/...) by only passing the answer 'index'
|
||||
(and not the id or anything else we can identify).
|
||||
In other words, we need to know if the answer at index 2 is correct or not.
|
||||
- answer_count
|
||||
The number of answers to the current question. """
|
||||
|
||||
question = survey.session_question_id
|
||||
answers_validity = []
|
||||
if (any(answer.is_correct for answer in question.suggested_answer_ids)):
|
||||
answers_validity = [answer.is_correct for answer in question.suggested_answer_ids]
|
||||
if question.comment_count_as_answer:
|
||||
answers_validity.append(False)
|
||||
|
||||
full_statistics = question._prepare_statistics(user_input_lines)[0]
|
||||
input_line_values = []
|
||||
if question.question_type in ['char_box', 'date', 'datetime']:
|
||||
input_line_values = [{
|
||||
'id': line.id,
|
||||
'value': line['value_%s' % question.question_type]
|
||||
} for line in full_statistics.get('table_data', request.env['survey.user_input.line'])[:100]]
|
||||
|
||||
return {
|
||||
'is_html_empty': is_html_empty,
|
||||
'question_statistics_graph': full_statistics.get('graph_data'),
|
||||
'input_line_values': input_line_values,
|
||||
'answers_validity': json.dumps(answers_validity),
|
||||
'answer_count': survey.session_question_answer_count,
|
||||
'attendees_count': survey.session_answer_count,
|
||||
}
|
11
data/gamification_badge_demo.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data noupdate="1">
|
||||
|
||||
<record id="vendor_certification_badge" model="gamification.badge">
|
||||
<field name="name">MyCompany Vendor</field>
|
||||
<field name="description">Congratulations, you are now official vendor of MyCompany</field>
|
||||
<field name="rule_auth">nobody</field>
|
||||
<field name="image_1920" type="base64" file="gamification/static/img/badge_good_job-image.png"/>
|
||||
</record>
|
||||
|
||||
</data></odoo>
|
20
data/mail_message_subtype_data.xml
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo noupdate="1">
|
||||
<record id="mt_survey_user_input_completed" model="mail.message.subtype">
|
||||
<field name="name">Participation completed</field>
|
||||
<field name="res_model">survey.user_input</field>
|
||||
<field name="default" eval="False"/>
|
||||
<field name="description">Participation completed.</field>
|
||||
<field name="hidden" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record id="mt_survey_survey_user_input_completed" model="mail.message.subtype">
|
||||
<field name="name">Participation completed</field>
|
||||
<field name="res_model">survey.survey</field>
|
||||
<field name="default" eval="False"/>
|
||||
<field name="hidden" eval="False"/>
|
||||
<field name="description">New participation completed.</field>
|
||||
<field name="parent_id" ref="mt_survey_user_input_completed"/>
|
||||
<field name="relation_field">survey_id</field>
|
||||
</record>
|
||||
</odoo>
|
90
data/mail_template_data.xml
Normal file
|
@ -0,0 +1,90 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="mail_template_user_input_invite" model="mail.template">
|
||||
<field name="name">Survey: Invite</field>
|
||||
<field name="model_id" ref="model_survey_user_input" />
|
||||
<field name="subject">Participate to {{ object.survey_id.display_name }} survey</field>
|
||||
<field name="email_from">{{ user.email_formatted }}</field>
|
||||
<field name="email_to">{{ (object.partner_id.email_formatted or object.email) }}</field>
|
||||
<field name="description">Sent to participant when you share a survey</field>
|
||||
<field name="body_html" type="html">
|
||||
<div style="margin: 0px; padding: 0px; font-size: 13px;">
|
||||
<p style="margin: 0px; padding: 0px; font-size: 13px;">
|
||||
Dear <t t-out="object.partner_id.name or 'participant'">participant</t><br/><br/>
|
||||
<t t-if="object.survey_id.certification">
|
||||
You have been invited to take a new certification.
|
||||
</t>
|
||||
<t t-else="">
|
||||
We are conducting a survey and your response would be appreciated.
|
||||
</t>
|
||||
<div style="margin: 16px 0px 16px 0px;">
|
||||
<a t-att-href="(object.get_start_url())"
|
||||
style="background-color: #875A7B; padding: 8px 16px 8px 16px; text-decoration: none; color: #fff; border-radius: 5px; font-size:13px;">
|
||||
<t t-if="object.survey_id.certification">
|
||||
Start Certification
|
||||
</t>
|
||||
<t t-else="">
|
||||
Start Survey
|
||||
</t>
|
||||
</a>
|
||||
</div>
|
||||
<t t-if="object.deadline">
|
||||
Please answer the survey for <t t-out="format_date(object.deadline) or ''">05/05/2021</t>.<br/><br/>
|
||||
</t>
|
||||
<t t-if="object.survey_id.certification">
|
||||
We wish you good luck!
|
||||
</t>
|
||||
<t t-else="">
|
||||
Thank you in advance for your participation.
|
||||
</t>
|
||||
</p>
|
||||
</div>
|
||||
</field>
|
||||
<field name="lang">{{ object.partner_id.lang }}</field>
|
||||
<field name="auto_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<!-- Certification Email template -->
|
||||
<record id="mail_template_certification" model="mail.template">
|
||||
<field name="name">Survey: Certification Success</field>
|
||||
<field name="model_id" ref="survey.model_survey_user_input"/>
|
||||
<field name="subject">Certification: {{ object.survey_id.display_name }}</field>
|
||||
<field name="email_from">{{ (object.survey_id.create_uid.email_formatted or user.email_formatted or user.company_id.catchall_formatted) }}</field>
|
||||
<field name="email_to">{{ (object.partner_id.email_formatted or object.email) }}</field>
|
||||
<field name="description">Sent to participant if they succeeded the certification</field>
|
||||
<field name="body_html" type="html">
|
||||
<div style="background:#F0F0F0;color:#515166;padding:10px 0px;font-family:Arial,Helvetica,sans-serif;font-size:14px;">
|
||||
<table style="width:600px;margin:5px auto;">
|
||||
<tbody>
|
||||
<tr><td>
|
||||
<!-- We use the logo of the company that created the survey (to handle multi company cases) -->
|
||||
<a href="/"><img t-if="not object.survey_id.create_uid.company_id.uses_default_logo"
|
||||
t-attf-src="/logo.png?company={{ object.survey_id.create_uid.company_id.id }}"
|
||||
style="vertical-align:baseline;max-width:100px;" /></a>
|
||||
</td><td style="text-align:right;vertical-align:middle;">
|
||||
Certification: <t t-out="object.survey_id.display_name or ''">Feedback Form</t>
|
||||
</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table style="width:600px;margin:0px auto;background:white;border:1px solid #e1e1e1;">
|
||||
<tbody>
|
||||
<tr><td style="padding:15px 20px 10px 20px;">
|
||||
<p>Dear <span t-out="object.partner_id.name or 'participant'">participant</span></p>
|
||||
<p>
|
||||
Please find attached your
|
||||
<strong t-out="object.survey_id.display_name or ''">Furniture Creation</strong>
|
||||
certification
|
||||
</p>
|
||||
<p>Congratulations for passing the test with a score of <strong t-out="object.scoring_percentage"/>%!</p>
|
||||
</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</field>
|
||||
<field name="report_template_ids" eval="[(4, ref('survey.certification_report'))]"/>
|
||||
<field name="lang">{{ object.partner_id.lang }}</field>
|
||||
<field name="auto_delete" eval="True"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
8
data/res_users_demo.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="base.user_demo" model="res.users">
|
||||
<field name="groups_id" eval="[(3, ref('survey.group_survey_manager'))]"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
366
data/survey_demo_certification.xml
Normal file
|
@ -0,0 +1,366 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- Odoo Vendor Certification -->
|
||||
<record model="survey.survey" id="vendor_certification">
|
||||
<field name="title">MyCompany Vendor Certification</field>
|
||||
<field name="access_token">4ead4bc8-b8f2-4760-a682-1fde8ddb95ac</field>
|
||||
<field name="user_id" ref="base.user_admin"/>
|
||||
<field name="access_mode">public</field>
|
||||
<field name="questions_layout">page_per_question</field>
|
||||
<field name="users_can_go_back" eval="True" />
|
||||
<field name="users_login_required" eval="True" />
|
||||
<field name="scoring_type" >scoring_with_answers</field>
|
||||
<field name="certification" eval="True"/>
|
||||
<field name="certification_mail_template_id" ref="mail_template_certification"></field>
|
||||
<field name="is_time_limited" >limited</field>
|
||||
<field name="time_limit" >10.0</field>
|
||||
<field name="is_attempts_limited" eval="True" />
|
||||
<field name="attempts_limit">2</field>
|
||||
<field name="description" type="html"><p>Test your vendor skills!</p></field>
|
||||
<field name="certification_give_badge">True</field>
|
||||
<field name="certification_badge_id" ref="vendor_certification_badge"/>
|
||||
<field name="background_image" type="base64" file="survey/static/src/img/survey_background_2.jpg"/>
|
||||
</record>
|
||||
<!-- Page 1 -->
|
||||
<record model="survey.question" id="vendor_certification_page_1">
|
||||
<field name="title">Products</field>
|
||||
<field name="survey_id" ref="vendor_certification" />
|
||||
<field name="sequence">1</field>
|
||||
<field name="is_page" eval="True"/>
|
||||
<field name="question_type" eval="False" />
|
||||
<field name="description" type="html"><p>Test your knowledge of your products!</p></field>
|
||||
</record>
|
||||
<!-- Question and predefined answer 1 -->
|
||||
<record model="survey.question" id="vendor_certification_page_1_question_1">
|
||||
<field name="survey_id" ref="vendor_certification" />
|
||||
<field name="sequence">2</field>
|
||||
<field name="title">Do we sell Acoustic Bloc Screens?</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True" />
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_1_question_1_choice_1">
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_1"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">No</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_1_question_1_choice_2">
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_1"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Yes</field>
|
||||
<field name="is_correct" eval="True" />
|
||||
<field name="answer_score">2.0</field>
|
||||
</record>
|
||||
<!-- Question and predefined answer 2 -->
|
||||
<record model="survey.question" id="vendor_certification_page_1_question_2">
|
||||
<field name="survey_id" ref="vendor_certification" />
|
||||
<field name="sequence">3</field>
|
||||
<field name="title">Select all the existing products</field>
|
||||
<field name="question_type">multiple_choice</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_1_question_2_choice_1">
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_2"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Chair floor protection</field>
|
||||
<field name="is_correct" eval="True" />
|
||||
<field name="answer_score">1.0</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_1_question_2_choice_2">
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_2"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Fanta</field>
|
||||
<field name="answer_score">-1.0</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_1_question_2_choice_3">
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_2"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Conference chair</field>
|
||||
<field name="is_correct" eval="True" />
|
||||
<field name="answer_score">1.0</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_1_question_2_choice_4">
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_2"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">Drawer</field>
|
||||
<field name="is_correct" eval="True" />
|
||||
<field name="answer_score">1.0</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_1_question_2_choice_5">
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_2"/>
|
||||
<field name="sequence">5</field>
|
||||
<field name="value">Customizable Lamp</field>
|
||||
<field name="answer_score">-1.0</field>
|
||||
</record>
|
||||
<!-- Question and predefined answer 3 -->
|
||||
<record model="survey.question" id="vendor_certification_page_1_question_3">
|
||||
<field name="survey_id" ref="vendor_certification" />
|
||||
<field name="sequence">4</field>
|
||||
<field name="title">Select all the available customizations for our Customizable Desk</field>
|
||||
<field name="question_type">multiple_choice</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_1_question_3_choice_1">
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_3"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Color</field>
|
||||
<field name="is_correct" eval="True" />
|
||||
<field name="answer_score">1.0</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_1_question_3_choice_2">
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_3"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Height</field>
|
||||
<field name="answer_score">-1.0</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_1_question_3_choice_3">
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_3"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Width</field>
|
||||
<field name="is_correct" eval="True" />
|
||||
<field name="answer_score">1.0</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_1_question_3_choice_4">
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_3"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">Legs</field>
|
||||
<field name="is_correct" eval="True" />
|
||||
<field name="answer_score">1.0</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_1_question_3_choice_5">
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_3"/>
|
||||
<field name="sequence">5</field>
|
||||
<field name="value">Number of drawers</field>
|
||||
<field name="answer_score">-1.0</field>
|
||||
</record>
|
||||
<!-- Question and predefined answer 4 -->
|
||||
<record model="survey.question" id="vendor_certification_page_1_question_4">
|
||||
<field name="survey_id" ref="vendor_certification" />
|
||||
<field name="sequence">5</field>
|
||||
<field name="title">How many versions of the Corner Desk do we have?</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True" />
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_1_question_4_choice_1">
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_4"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">1</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_1_question_4_choice_2">
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_4"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">2</field>
|
||||
<field name="is_correct" eval="True" />
|
||||
<field name="answer_score">2.0</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_1_question_4_choice_3">
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_4"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">3</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_1_question_4_choice_4">
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_4"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">4</field>
|
||||
</record>
|
||||
<!-- Question and predefined answer 5 -->
|
||||
<record model="survey.question" id="vendor_certification_page_1_question_5">
|
||||
<field name="survey_id" ref="vendor_certification" />
|
||||
<field name="sequence">6</field>
|
||||
<field name="title">Do you think we have missing products in our catalog? (not rated)</field>
|
||||
<field name="question_type">text_box</field>
|
||||
<field name="question_placeholder">If yes, explain what you think is missing, give examples.</field>
|
||||
</record>
|
||||
<!-- Page 2 -->
|
||||
<record model="survey.question" id="vendor_certification_page_2">
|
||||
<field name="title">Prices</field>
|
||||
<field name="survey_id" ref="vendor_certification" />
|
||||
<field name="is_page" eval="True"/>
|
||||
<field name="question_type" eval="False" />
|
||||
<field name="sequence">7</field>
|
||||
<field name="description"><p>Test your knowledge of our prices.</p></field>
|
||||
</record>
|
||||
<!-- Question and predefined answer 6 -->
|
||||
<record model="survey.question" id="vendor_certification_page_2_question_1">
|
||||
<field name="survey_id" ref="vendor_certification" />
|
||||
<field name="sequence">8</field>
|
||||
<field name="title">How much do we sell our Cable Management Box?</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True" />
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_2_question_1_choice_1">
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_1"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">$20</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_2_question_1_choice_2">
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_1"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">$50</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_2_question_1_choice_3">
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_1"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">$80</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_2_question_1_choice_4">
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_1"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">$100</field>
|
||||
<field name="is_correct" eval="True" />
|
||||
<field name="answer_score">2.0</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_2_question_1_choice_5">
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_1"/>
|
||||
<field name="sequence">5</field>
|
||||
<field name="value">$200</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_2_question_1_choice_6">
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_1"/>
|
||||
<field name="sequence">6</field>
|
||||
<field name="value">$300</field>
|
||||
</record>
|
||||
<!-- Question and predefined answer 7 -->
|
||||
<record model="survey.question" id="vendor_certification_page_2_question_2">
|
||||
<field name="survey_id" ref="vendor_certification" />
|
||||
<field name="sequence">9</field>
|
||||
<field name="title">Select all the products that sell for $100 or more</field>
|
||||
<field name="question_type">multiple_choice</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_2_question_2_choice_1">
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_2"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Corner Desk Right Sit</field>
|
||||
<field name="answer_score">1.0</field>
|
||||
<field name="is_correct" eval="True" />
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_2_question_2_choice_2">
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_2"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Desk Combination</field>
|
||||
<field name="answer_score">1.0</field>
|
||||
<field name="is_correct" eval="True" />
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_2_question_2_choice_3">
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_2"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Cabinet with Doors</field>
|
||||
<field name="answer_score">-1.0</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_2_question_2_choice_4">
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_2"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">Large Desk</field>
|
||||
<field name="answer_score">1.0</field>
|
||||
<field name="is_correct" eval="True" />
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_2_question_2_choice_5">
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_2"/>
|
||||
<field name="sequence">5</field>
|
||||
<field name="value">Letter Tray</field>
|
||||
<field name="answer_score">-1.0</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_2_question_2_choice_5">
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_2"/>
|
||||
<field name="sequence">6</field>
|
||||
<field name="value">Office Chair Black</field>
|
||||
<field name="answer_score">-1.0</field>
|
||||
</record>
|
||||
<!-- Question and predefined answer 8 -->
|
||||
<record model="survey.question" id="vendor_certification_page_2_question_3">
|
||||
<field name="survey_id" ref="vendor_certification" />
|
||||
<field name="sequence">10</field>
|
||||
<field name="title">What do you think about our prices (not rated)?</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True" />
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_2_question_3_choice_1">
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_3"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Very underpriced</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_2_question_3_choice_2">
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_3"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Underpriced</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_2_question_3_choice_3">
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_3"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Correctly priced</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_2_question_3_choice_4">
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_3"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">A little bit overpriced</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="vendor_certification_page_2_question_3_choice_5">
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_3"/>
|
||||
<field name="sequence">5</field>
|
||||
<field name="value">A lot overpriced</field>
|
||||
</record>
|
||||
<!-- Page 3 -->
|
||||
<record model="survey.question" id="vendor_certification_page_3">
|
||||
<field name="title">Policies</field>
|
||||
<field name="survey_id" ref="vendor_certification" />
|
||||
<field name="is_page" eval="True"/>
|
||||
<field name="question_type" eval="False" />
|
||||
<field name="sequence">11</field>
|
||||
<field name="description"><p>Test your knowledge of our policies.</p></field>
|
||||
</record>
|
||||
<!-- Question and predefined answer 9 -->
|
||||
<record model="survey.question" id="vendor_certification_page_3_question_1">
|
||||
<field name="survey_id" ref="vendor_certification" />
|
||||
<field name="sequence">12</field>
|
||||
<field name="title">How many days is our money-back guarantee?</field>
|
||||
<field name="question_type">numerical_box</field>
|
||||
<field name="is_scored_question" eval="True" />
|
||||
<field name="answer_numerical_box">30</field>
|
||||
<field name="answer_score">1.0</field>
|
||||
<field name="constr_mandatory" eval="True" />
|
||||
</record>
|
||||
<!-- Question and predefined answer 10 -->
|
||||
<record model="survey.question" id="vendor_certification_page_3_question_2">
|
||||
<field name="survey_id" ref="vendor_certification" />
|
||||
<field name="sequence">13</field>
|
||||
<field name="title">If a customer purchases a product on 6 January 2020, what is the latest day we expect to ship it?</field>
|
||||
<field name="question_type">date</field>
|
||||
<field name="is_scored_question" eval="True" />
|
||||
<field name="answer_date">2020-01-08</field>
|
||||
<field name="answer_score">1.0</field>
|
||||
</record>
|
||||
<!-- Question and predefined answer 11 -->
|
||||
<record model="survey.question" id="vendor_certification_page_3_question_3">
|
||||
<field name="survey_id" ref="vendor_certification" />
|
||||
<field name="sequence">14</field>
|
||||
<field name="title">If a customer purchases a 1 year warranty on 6 January 2020, when do we expect the warranty to expire?</field>
|
||||
<field name="question_type">datetime</field>
|
||||
<field name="is_scored_question" eval="True" />
|
||||
<field name="answer_datetime">2021-01-07 00:00:01</field>
|
||||
<field name="answer_score">1.0</field>
|
||||
<field name="question_placeholder">Beware of leap years!</field>
|
||||
</record>
|
||||
<!-- Question and predefined answer 12 -->
|
||||
<record model="survey.question" id="vendor_certification_page_3_question_4">
|
||||
<field name="survey_id" ref="vendor_certification" />
|
||||
<field name="sequence">15</field>
|
||||
<field name="title">What day to you think is best for us to start having an annual sale (not rated)?</field>
|
||||
<field name="question_type">date</field>
|
||||
<field name="answer_score">0</field>
|
||||
</record>
|
||||
<!-- Question and predefined answer 13 -->
|
||||
<record model="survey.question" id="vendor_certification_page_3_question_5">
|
||||
<field name="survey_id" ref="vendor_certification" />
|
||||
<field name="sequence">16</field>
|
||||
<field name="title">What day and time do you think most customers are most likely to call customer service (not rated)?</field>
|
||||
<field name="question_type">datetime</field>
|
||||
<field name="answer_score">0</field>
|
||||
</record>
|
||||
<!-- Question and predefined answer 14 -->
|
||||
<record model="survey.question" id="vendor_certification_page_3_question_6">
|
||||
<field name="survey_id" ref="vendor_certification" />
|
||||
<field name="sequence">17</field>
|
||||
<field name="title">How many chairs do you think we should aim to sell in a year (not rated)?</field>
|
||||
<field name="question_type">numerical_box</field>
|
||||
<field name="answer_score">0</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
37
data/survey_demo_certification_user_input.xml
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data noupdate="1">
|
||||
|
||||
<record id="survey_vendor_certification_answer_1" model="survey.user_input">
|
||||
<field name="survey_id" ref="survey.vendor_certification" />
|
||||
<field name="partner_id" ref="base.res_partner_address_3"/>
|
||||
<field name="email">douglas.fletcher51@example.com</field>
|
||||
<field name="end_datetime" eval="datetime.now() - timedelta(days=1, hours=3, minutes=10)"/>
|
||||
<field name="start_datetime" eval="datetime.now() - timedelta(days=1, hours=3, minutes=50)"/>
|
||||
<field name="state">done</field>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_2" model="survey.user_input">
|
||||
<field name="survey_id" ref="survey.vendor_certification" />
|
||||
<field name="partner_id" ref="base.res_partner_address_7"/>
|
||||
<field name="email">billy.fox45@example.com</field>
|
||||
<field name="end_datetime" eval="datetime.now() - timedelta(days=0, hours=3, minutes=0)"/>
|
||||
<field name="start_datetime" eval="datetime.now() - timedelta(days=0, hours=3, minutes=50)"/>
|
||||
<field name="state">done</field>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_3" model="survey.user_input">
|
||||
<field name="survey_id" ref="survey.vendor_certification" />
|
||||
<field name="partner_id" ref="base.res_partner_address_15"/>
|
||||
<field name="email">brandon.freeman55@example.com</field>
|
||||
<field name="end_datetime" eval="datetime.now() - timedelta(days=0, hours=3, minutes=30)"/>
|
||||
<field name="start_datetime" eval="datetime.now() - timedelta(days=0, hours=3, minutes=50)"/>
|
||||
<field name="state">done</field>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_4" model="survey.user_input">
|
||||
<field name="survey_id" ref="survey.vendor_certification" />
|
||||
<field name="partner_id" ref="base.res_partner_address_25"/>
|
||||
<field name="email">oscar.morgan11@example.com</field>
|
||||
<field name="end_datetime" eval="datetime.now() - timedelta(days=0, hours=2, minutes=30)"/>
|
||||
<field name="start_datetime" eval="datetime.now() - timedelta(days=0, hours=2, minutes=50)"/>
|
||||
<field name="state">done</field>
|
||||
</record>
|
||||
|
||||
</data></odoo>
|
429
data/survey_demo_certification_user_input_line.xml
Normal file
|
@ -0,0 +1,429 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data noupdate="0">
|
||||
|
||||
<!-- User input 1 -->
|
||||
<!-- page 1 -->
|
||||
<record id="survey_vendor_certification_answer_1_p1_q1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_1"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_1_choice_2"/>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_1_p1_q2_1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_2_choice_1"/>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_1_p1_q2_2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_2_choice_3"/>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_1_p1_q2_3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_2_choice_4"/>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_1_p1_q3_1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_3"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_3_choice_1"/>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_1_p1_q3_2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_3"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_3_choice_3"/>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_1_p1_q3_3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_3"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_3_choice_4"/>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_1_p1_q4" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_4"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_4_choice_2"/>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_1_p1_q5" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_5"/>
|
||||
<field name="answer_type">text_box</field>
|
||||
<field name="value_text_box">I think it misses a product but I don't know what</field>
|
||||
</record>
|
||||
<!-- page 2 -->
|
||||
<record id="survey_vendor_certification_answer_1_p2_q1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_1"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_2_question_1_choice_4"/>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_1_p2_q2_1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_2_question_2_choice_1"/>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_1_p2_q2_2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_2_question_2_choice_2"/>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_1_p2_q2_3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_2_question_2_choice_4"/>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_1_p2_q3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_3"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_2_question_3_choice_3"/>
|
||||
</record>
|
||||
<!-- page 3 -->
|
||||
<record id="survey_vendor_certification_answer_1_p3_q1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_1"/>
|
||||
<field name="answer_type">numerical_box</field>
|
||||
<field name="value_numerical_box">30</field>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_1_p3_q2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_2"/>
|
||||
<field name="answer_type">date</field>
|
||||
<field name="value_date">2020-01-08</field>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_1_p3_q3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_3"/>
|
||||
<field name="answer_type">datetime</field>
|
||||
<field name="value_datetime">2021-01-07 00:00:01</field>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_1_p3_q4" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_4"/>
|
||||
<field name="answer_type">date</field>
|
||||
<field name="value_date">2020-01-01</field>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_1_p3_q5" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_5"/>
|
||||
<field name="answer_type">datetime</field>
|
||||
<field name="value_datetime">2021-01-01 01:00:01</field>
|
||||
</record>
|
||||
<record id="survey_vendor_certification_answer_1_p3_q6" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_1"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_6"/>
|
||||
<field name="answer_type">numerical_box</field>
|
||||
<field name="value_numerical_box">1000</field>
|
||||
</record>
|
||||
|
||||
<!-- User input 2 -->
|
||||
<!-- page 1 -->
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_2_p1_q1">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_2"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_1"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_1_choice_2"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_2_p1_q2_1">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_2"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_2_choice_1"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_2_p1_q2_2">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_2"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_2_choice_3"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_2_p1_q3_1">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_2"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_3"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_3_choice_1"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_2_p1_q3_2">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_2"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_3"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_3_choice_3"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_2_p1_q4">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_2"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_4"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_4_choice_2"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_2_p1_q5">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_2"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_5"/>
|
||||
<field name="skipped" eval="True"/>
|
||||
</record>
|
||||
<!-- page 2 -->
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_2_p2_q1">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_2"/>
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_1"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_2_question_1_choice_4"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_2_p2_q2_1">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_2"/>
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_2_question_2_choice_1"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_2_p2_q2_2">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_2"/>
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_2_question_2_choice_2"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_2_p2_q2_3">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_2"/>
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_2_question_2_choice_4"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_2_p2_q3">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_2"/>
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_3"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_2_question_3_choice_4"/>
|
||||
</record>
|
||||
<!-- page 3 -->
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_2_p3_q1">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_2"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_1"/>
|
||||
<field name="answer_type">numerical_box</field>
|
||||
<field name="value_numerical_box">30</field>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_2_p3_q2">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_2"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_2"/>
|
||||
<field name="answer_type">date</field>
|
||||
<field name="value_date">2020-01-09</field>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_2_p3_q3">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_2"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_3"/>
|
||||
<field name="answer_type">datetime</field>
|
||||
<field name="value_datetime">2021-01-07 00:00:01</field>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_2_p3_q4">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_2"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_4"/>
|
||||
<field name="skipped" eval="True"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_2_p3_q5">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_2"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_5"/>
|
||||
<field name="skipped" eval="True"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_2_p3_q6">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_2"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_6"/>
|
||||
<field name="answer_type">numerical_box</field>
|
||||
<field name="value_numerical_box">0</field>
|
||||
</record>
|
||||
|
||||
<!-- User input 3 -->
|
||||
<!-- page 1 -->
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_3_p1_q1">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_3"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_1"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_1_choice_2"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_3_p1_q2_1">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_3"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_2_choice_1"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_3_p1_q2_2">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_3"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_2_choice_4"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_3_p1_q3_1">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_3"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_3"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_3_choice_1"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_3_p1_q3_2">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_3"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_3"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_3_choice_4"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_3_p1_q4">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_3"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_4"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_4_choice_2"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_3_p1_q5">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_3"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_5"/>
|
||||
<field name="skipped" eval="True"/>
|
||||
</record>
|
||||
<!-- page 2 -->
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_3_p2_q1">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_3"/>
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_1"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_2_question_1_choice_4"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_3_p2_q2_1">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_3"/>
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_2_question_2_choice_1"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_3_p2_q2_2">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_3"/>
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_2_question_2_choice_4"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_3_p2_q3">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_3"/>
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_3"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_2_question_3_choice_2"/>
|
||||
</record>
|
||||
<!-- page 3 -->
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_3_p3_q1">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_3"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_1"/>
|
||||
<field name="answer_type">numerical_box</field>
|
||||
<field name="value_numerical_box">30</field>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_3_p3_q2">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_3"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_2"/>
|
||||
<field name="answer_type">date</field>
|
||||
<field name="value_date">2020-01-08</field>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_3_p3_q3">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_3"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_3"/>
|
||||
<field name="answer_type">datetime</field>
|
||||
<field name="value_datetime">2021-01-06 23:59:59</field>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_3_p3_q4">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_3"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_4"/>
|
||||
<field name="skipped" eval="True"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_3_p3_q5">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_3"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_5"/>
|
||||
<field name="skipped" eval="True"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_3_p3_q6">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_3"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_6"/>
|
||||
<field name="skipped" eval="True"/>
|
||||
</record>
|
||||
|
||||
<!-- User input 4 -->
|
||||
<!-- page 1 -->
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_4_p1_q1">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_4"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_1"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_1_choice_1"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_4_p1_q2_1">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_4"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_2_choice_3"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_4_p1_q3_1">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_4"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_3"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_3_choice_2"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_4_p1_q4">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_4"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_4"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_1_question_4_choice_4"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_4_p1_q5">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_4"/>
|
||||
<field name="question_id" ref="vendor_certification_page_1_question_5"/>
|
||||
<field name="skipped" eval="True"/>
|
||||
</record>
|
||||
<!-- page 2 -->
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_4_p2_q1">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_4"/>
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_1"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_2_question_1_choice_2"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_4_p2_q2_1">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_4"/>
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_2_question_2_choice_4"/>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_4_p2_q3">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_4"/>
|
||||
<field name="question_id" ref="vendor_certification_page_2_question_3"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="vendor_certification_page_2_question_3_choice_5"/>
|
||||
</record>
|
||||
<!-- page 3 -->
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_4_p3_q1">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_4"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_1"/>
|
||||
<field name="answer_type">numerical_box</field>
|
||||
<field name="value_numerical_box">2</field>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_4_p3_q2">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_4"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_2"/>
|
||||
<field name="answer_type">date</field>
|
||||
<field name="value_date">2020-01-08</field>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_4_p3_q3">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_4"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_3"/>
|
||||
<field name="answer_type">datetime</field>
|
||||
<field name="value_datetime">2021-01-07 00:00:01</field>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_4_p3_q4">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_4"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_4"/>
|
||||
<field name="answer_type">date</field>
|
||||
<field name="value_date">2019-12-31</field>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_4_p3_q5">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_4"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_5"/>
|
||||
<field name="answer_type">datetime</field>
|
||||
<field name="value_datetime">2021-01-01 13:00:01</field>
|
||||
</record>
|
||||
<record model="survey.user_input.line" id="survey_vendor_certification_answer_4_p3_q6">
|
||||
<field name="user_input_id" ref="survey_vendor_certification_answer_4"/>
|
||||
<field name="question_id" ref="vendor_certification_page_3_question_6"/>
|
||||
<field name="skipped" eval="True"/>
|
||||
</record>
|
||||
|
||||
</data></odoo>
|
555
data/survey_demo_conditional.xml
Normal file
|
@ -0,0 +1,555 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data noupdate="0">
|
||||
|
||||
<record id="survey_demo_burger_quiz" model="survey.survey">
|
||||
<field name="title">Burger Quiz</field>
|
||||
<field name="access_token">burger00-quiz-1234-abcd-344ca2tgb31e</field>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="access_mode">public</field>
|
||||
<field name="users_can_go_back" eval="True"/>
|
||||
<field name="scoring_type">scoring_with_answers</field>
|
||||
<field name="scoring_success_min">55</field>
|
||||
<field name="is_time_limited" >limited</field>
|
||||
<field name="time_limit" >10.0</field>
|
||||
<field name="questions_layout">page_per_question</field>
|
||||
<field name="description" type="html">
|
||||
<p>Choose your favourite subject and show how good you are. Ready?</p></field>
|
||||
<field name="background_image" type="base64" file="survey/static/src/img/burger_quiz_background.jpg"/>
|
||||
</record>
|
||||
|
||||
<!-- Page 1: Start -->
|
||||
<record id="survey_demo_burger_quiz_p1" model="survey.question">
|
||||
<field name="title">Start</field>
|
||||
<field name="survey_id" ref="survey_demo_burger_quiz"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="question_type" eval="False"/>
|
||||
<field name="is_page" eval="True"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p1_q1" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_burger_quiz"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="title">Pick a subject</field>
|
||||
<field name="question_type">multiple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p1_q1_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p1_q1"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Geography</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p1_q1_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p1_q1"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">History</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p1_q1_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p1_q1"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Sciences</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p1_q1_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p1_q1"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">Art & Culture</field>
|
||||
</record>
|
||||
|
||||
<!-- Page 2 : Geography -->
|
||||
<record id="survey_demo_burger_quiz_p2" model="survey.question">
|
||||
<field name="title">Geography</field>
|
||||
<field name="survey_id" ref="survey_demo_burger_quiz"/>
|
||||
<field name="sequence">100</field>
|
||||
<field name="question_type" eval="False"/>
|
||||
<field name="is_page" eval="True"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p2_q1" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_burger_quiz"/>
|
||||
<field name="sequence">110</field>
|
||||
<field name="title">How long is the White Nile river?</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="triggering_answer_ids" eval="[Command.link(ref('survey.survey_demo_burger_quiz_p1_q1_sug1'))]"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p2_q1_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p2_q1"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">1450 km</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p2_q1_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p2_q1"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">3700 km</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">10</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p2_q1_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p2_q1"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">6650 km</field>
|
||||
</record>
|
||||
<!-- TODO DBE: Add free text pages with corrections. 1450 km is Blue Nile, 6500 km is the total Nile lenght. -->
|
||||
|
||||
<record id="survey_demo_burger_quiz_p2_q2" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_burger_quiz"/>
|
||||
<field name="sequence">120</field>
|
||||
<field name="title">What is the biggest city in the world?</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="triggering_answer_ids" eval="[Command.link(ref('survey.survey_demo_burger_quiz_p1_q1_sug1'))]"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p2_q2_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p2_q2"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Shanghai</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p2_q2_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p2_q2"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Tokyo</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">10</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p2_q2_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p2_q2"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">New York</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p2_q2_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p2_q2"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">Istanbul</field>
|
||||
</record>
|
||||
|
||||
<record id="survey_demo_burger_quiz_p2_q3" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_burger_quiz"/>
|
||||
<field name="sequence">130</field>
|
||||
<field name="title">Which is the highest volcano in Europe?</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="triggering_answer_ids" eval="[Command.link(ref('survey.survey_demo_burger_quiz_p1_q1_sug1'))]"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p2_q3_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p2_q3"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Mount Teide (Spain - Tenerife)</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p2_q3_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p2_q3"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Eyjafjallajökull (Iceland)</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p2_q3_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p2_q3"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Mount Etna (Italy - Sicily)</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p2_q3_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p2_q3"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">Mount Elbrus (Russia)</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">10</field>
|
||||
</record>
|
||||
|
||||
<!-- Page 3 : History -->
|
||||
<record id="survey_demo_burger_quiz_p3" model="survey.question">
|
||||
<field name="title">History</field>
|
||||
<field name="survey_id" ref="survey_demo_burger_quiz"/>
|
||||
<field name="sequence">200</field>
|
||||
<field name="question_type" eval="False"/>
|
||||
<field name="is_page" eval="True"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p3_q1" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_burger_quiz"/>
|
||||
<field name="sequence">210</field>
|
||||
<field name="title">When did Genghis Khan die?</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="triggering_answer_ids" eval="[Command.link(ref('survey.survey_demo_burger_quiz_p1_q1_sug2'))]"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p3_q1_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p3_q1"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">1227</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">10</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p3_q1_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p3_q1"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">1324</field> <!--Marco Polo-->
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p3_q1_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p3_q1"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">1055</field> <!-- Emperor Xingzong (Liao Dynasty) -->
|
||||
</record>
|
||||
|
||||
<record id="survey_demo_burger_quiz_p3_q2" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_burger_quiz"/>
|
||||
<field name="sequence">220</field>
|
||||
<field name="title">Who is the architect of the Great Pyramid of Giza?</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="triggering_answer_ids" eval="[Command.link(ref('survey.survey_demo_burger_quiz_p1_q1_sug2'))]"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p3_q2_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p3_q2"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Imhotep</field> <!-- Djoser's pyramid -->
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p3_q2_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p3_q2"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Amenhotep</field> <!-- Pharaoh -->
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p3_q2_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p3_q2"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Hemiunu</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">10</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p3_q2_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p3_q2"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">Papyrus</field>
|
||||
</record>
|
||||
|
||||
<record id="survey_demo_burger_quiz_p3_q3" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_burger_quiz"/>
|
||||
<field name="sequence">230</field>
|
||||
<field name="title">How many years did the 100 years war last?</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="triggering_answer_ids" eval="[Command.link(ref('survey.survey_demo_burger_quiz_p1_q1_sug2'))]"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p3_q3_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p3_q3"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">99 years</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p3_q3_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p3_q3"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">100 years</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p3_q3_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p3_q3"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">116 years</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">10</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p3_q3_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p3_q3"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">127 years</field>
|
||||
</record>
|
||||
|
||||
<!-- Page 4 : Sciences -->
|
||||
<record id="survey_demo_burger_quiz_p4" model="survey.question">
|
||||
<field name="title">Sciences</field>
|
||||
<field name="survey_id" ref="survey_demo_burger_quiz"/>
|
||||
<field name="sequence">300</field>
|
||||
<field name="question_type" eval="False"/>
|
||||
<field name="is_page" eval="True"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p4_q1" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_burger_quiz"/>
|
||||
<field name="sequence">310</field>
|
||||
<field name="title">Who received a Nobel prize in Physics for the discovery of neutrino oscillations, which shows that neutrinos have mass?</field>
|
||||
<field name="question_type">multiple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="triggering_answer_ids" eval="[Command.link(ref('survey.survey_demo_burger_quiz_p1_q1_sug3'))]"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p4_q1_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p4_q1"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Arthur B. McDonald</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">5</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p4_q1_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p4_q1"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Peter W. Higgs</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p4_q1_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p4_q1"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Takaaki Kajita</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">5</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p4_q1_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p4_q1"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">Willard S. Boyle</field>
|
||||
</record>
|
||||
|
||||
<record id="survey_demo_burger_quiz_p4_q2" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_burger_quiz"/>
|
||||
<field name="sequence">320</field>
|
||||
<field name="title">What is, approximately, the critical mass of plutonium-239?</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="triggering_answer_ids" eval="[Command.link(ref('survey.survey_demo_burger_quiz_p1_q1_sug3'))]"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p4_q2_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p4_q2"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">5.7 kg</field> <!-- Djoser's pyramid -->
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p4_q2_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p4_q2"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">10 kg</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">10</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p4_q2_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p4_q2"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">16.2 kg</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p4_q2_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p4_q2"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">47 kg</field>
|
||||
</record>
|
||||
|
||||
<record id="survey_demo_burger_quiz_p4_q3" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_burger_quiz"/>
|
||||
<field name="sequence">330</field>
|
||||
<field name="title">Can Humans ever directly see a photon?</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="triggering_answer_ids" eval="[Command.link(ref('survey.survey_demo_burger_quiz_p1_q1_sug3'))]"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p4_q3_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p4_q3"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Yes, that's the only thing a human eye can see.</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">10</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p4_q3_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p4_q3"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">No, it's too small for the human eye.</field>
|
||||
</record>
|
||||
|
||||
<!-- Page 5 : Art & Culture -->
|
||||
<record id="survey_demo_burger_quiz_p5" model="survey.question">
|
||||
<field name="title">Art & Culture</field>
|
||||
<field name="survey_id" ref="survey_demo_burger_quiz"/>
|
||||
<field name="sequence">400</field>
|
||||
<field name="question_type" eval="False"/>
|
||||
<field name="is_page" eval="True"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p5_q1" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_burger_quiz"/>
|
||||
<field name="sequence">410</field>
|
||||
<field name="title">Which Musician is not in the 27th Club?</field>
|
||||
<field name="question_type">multiple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="triggering_answer_ids" eval="[Command.link(ref('survey.survey_demo_burger_quiz_p1_q1_sug4'))]"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p5_q1_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p5_q1"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Kurt Cobain</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p5_q1_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p5_q1"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Kim Jong-hyun</field> <!-- To distinguish from the North Korean Leader Kim Jong-un -->
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p5_q1_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p5_q1"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Avicii</field> <!-- Died at 28 -->
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">5</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p5_q1_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p5_q1"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">Cliff Burton</field> <!-- Died at 24 -->
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">5</field>
|
||||
</record>
|
||||
|
||||
<record id="survey_demo_burger_quiz_p5_q2" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_burger_quiz"/>
|
||||
<field name="sequence">420</field>
|
||||
<field name="title">Which painting/drawing was not made by Pablo Picasso?</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="triggering_answer_ids" eval="[Command.link(ref('survey.survey_demo_burger_quiz_p1_q1_sug4'))]"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p5_q2_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p5_q2"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value"> </field>
|
||||
<field name="value_image" type="base64" file="survey/static/src/img/burger_quiz_guernica.jpg"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p5_q2_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p5_q2"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value"> </field>
|
||||
<field name="value_image" type="base64" file="survey/static/src/img/burger_quiz_cubism_klein.jpg"/>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">10</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p5_q2_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p5_q2"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value"> </field>
|
||||
<field name="value_image" type="base64" file="survey/static/src/img/burger_quiz_don_quixote.jpg"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p5_q2_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p5_q2"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value"> </field>
|
||||
<field name="value_image" type="base64" file="survey/static/src/img/burger_quiz_self_portrait.jpg"/>
|
||||
</record>
|
||||
|
||||
<record id="survey_demo_burger_quiz_p5_q3" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_burger_quiz"/>
|
||||
<field name="sequence">430</field>
|
||||
<field name="title">Which quote is from Jean-Claude Van Damme</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="triggering_answer_ids" eval="[Command.link(ref('survey.survey_demo_burger_quiz_p1_q1_sug4'))]"/>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p5_q3_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p5_q3"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">I’ve never really wanted to go to Japan. Simply because I don’t like eating fish. And I know that’s very popular out there in Africa.</field> <!-- Britney Spears -->
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p5_q3_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p5_q3"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">I am fascinated by air. If you remove the air from the sky, all the birds would fall to the ground. And all the planes, too.</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">10</field>
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p5_q3_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p5_q3"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">I've been noticing gravity since I was very young!</field> <!-- Cameron Diaz -->
|
||||
</record>
|
||||
<record id="survey_demo_burger_quiz_p5_q3_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_burger_quiz_p5_q3"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">I actually don't like thinking. I think people think I like to think a lot. And I don't. I do not like to think at all.</field> <!-- Kanye West -->
|
||||
</record>
|
||||
|
||||
<!-- Multiple triggers -->
|
||||
<record id="survey_demo_food_preferences" model="survey.survey">
|
||||
<field name="title">Food Preferences</field>
|
||||
<field name="survey_type">survey</field>
|
||||
<field name="access_token">foodpref-eren-ces1-abcd-344ca2tgb31e</field>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="access_mode">public</field>
|
||||
<field name="questions_layout">one_page</field>
|
||||
<field name="description" type="html">
|
||||
<p>Please give us your preferences for this event's dinner!</p>
|
||||
</field>
|
||||
<field name="description_done" type="html">
|
||||
<p>Got it!</p>
|
||||
<p>See you soon!</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="survey_demo_food_preferences_q1" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_food_preferences"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="title">Are you vegetarian?</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
</record>
|
||||
<record id="survey_demo_food_preferences_q1_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_food_preferences_q1"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Yes</field>
|
||||
</record>
|
||||
<record id="survey_demo_food_preferences_q1_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_food_preferences_q1"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">No</field>
|
||||
</record>
|
||||
<record id="survey_demo_food_preferences_q1_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_food_preferences_q1"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">It depends</field>
|
||||
</record>
|
||||
|
||||
<record id="survey_demo_food_preferences_q2" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_food_preferences"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="title">Would you prefer a veggie meal if possible?</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="triggering_answer_ids" eval="[
|
||||
Command.link(ref('survey.survey_demo_food_preferences_q1_sug3')),
|
||||
]"/>
|
||||
|
||||
</record>
|
||||
<record id="survey_demo_food_preferences_q2_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_food_preferences_q2"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Yes</field>
|
||||
</record>
|
||||
<record id="survey_demo_food_preferences_q2_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_food_preferences_q2"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">No</field>
|
||||
</record>
|
||||
|
||||
<record id="survey_demo_food_preferences_q3" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_food_preferences"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="title">Choose your green meal</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="triggering_answer_ids" eval="[
|
||||
Command.link(ref('survey.survey_demo_food_preferences_q1_sug1')),
|
||||
Command.link(ref('survey.survey_demo_food_preferences_q2_sug1')),
|
||||
]"/>
|
||||
</record>
|
||||
<record id="survey_demo_food_preferences_q3_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_food_preferences_q3"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Vegetarian pizza</field>
|
||||
</record>
|
||||
<record id="survey_demo_food_preferences_q3_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_food_preferences_q3"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Vegetarian burger</field>
|
||||
</record>
|
||||
|
||||
<record id="survey_demo_food_preferences_q4" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_food_preferences"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="title">Choose your meal</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="triggering_answer_ids" eval="[
|
||||
Command.link(ref('survey.survey_demo_food_preferences_q1_sug2')),
|
||||
Command.link(ref('survey.survey_demo_food_preferences_q2_sug2')),
|
||||
]"/>
|
||||
</record>
|
||||
<record id="survey_demo_food_preferences_q4_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_food_preferences_q4"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Steak with french fries</field>
|
||||
</record>
|
||||
<record id="survey_demo_food_preferences_q4_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_food_preferences_q4"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Fish</field>
|
||||
</record>
|
||||
|
||||
</data></odoo>
|
198
data/survey_demo_feedback.xml
Normal file
|
@ -0,0 +1,198 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data noupdate="0">
|
||||
|
||||
<record model="survey.survey" id="survey_feedback">
|
||||
<field name="title">Feedback Form</field>
|
||||
<field name="access_token">b135640d-14d4-4748-9ef6-344ca256531e</field>
|
||||
<field name="user_id" ref="base.user_admin"/>
|
||||
<field name="access_mode">public</field>
|
||||
<field name="users_can_go_back" eval="True" />
|
||||
<field name="questions_layout">page_per_section</field>
|
||||
<field name="description" type="html">
|
||||
<p>This survey allows you to give a feedback about your experience with our products.
|
||||
Filling it helps us improving your experience.</p></field>
|
||||
</record>
|
||||
|
||||
<!-- Page1: general information -->
|
||||
<record model="survey.question" id="survey_feedback_p1">
|
||||
<field name="title">About you</field>
|
||||
<field name="survey_id" ref="survey_feedback" />
|
||||
<field name="sequence">1</field>
|
||||
<field name="question_type" eval="False" />
|
||||
<field name="is_page" eval="True" />
|
||||
<field name="description" type="html">
|
||||
<p>This section is about general information about you. Answering them helps qualifying your answers.</p></field>
|
||||
</record>
|
||||
<record model="survey.question" id="survey_feedback_p1_q1">
|
||||
<field name="survey_id" ref="survey_feedback" />
|
||||
<field name="sequence">2</field>
|
||||
<field name="title">Where do you live?</field>
|
||||
<field name="question_type">char_box</field>
|
||||
<field name="question_placeholder">Brussels</field>
|
||||
<field name="constr_mandatory" eval="False"/>
|
||||
</record>
|
||||
<record model="survey.question" id="survey_feedback_p1_q2">
|
||||
<field name="survey_id" ref="survey_feedback" />
|
||||
<field name="sequence">3</field>
|
||||
<field name="title">When is your date of birth?</field>
|
||||
<field name="question_type">date</field>
|
||||
<field name="constr_mandatory" eval="False"/>
|
||||
</record>
|
||||
<record model="survey.question" id="survey_feedback_p1_q3">
|
||||
<field name="survey_id" ref="survey_feedback" />
|
||||
<field name="sequence">4</field>
|
||||
<field name="title">How frequently do you buy products online?</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="comments_allowed" eval="True"/>
|
||||
<field name="comment_count_as_answer" eval="True"/>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p1_q3_sug1">
|
||||
<field name="question_id" ref="survey_feedback_p1_q3"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Once a day</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p1_q3_sug2">
|
||||
<field name="question_id" ref="survey_feedback_p1_q3"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Once a week</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p1_q3_sug3">
|
||||
<field name="question_id" ref="survey_feedback_p1_q3"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Once a month</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p1_q3_sug4">
|
||||
<field name="question_id" ref="survey_feedback_p1_q3"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">Once a year</field>
|
||||
</record>
|
||||
<record model="survey.question" id="survey_feedback_p1_q4">
|
||||
<field name="survey_id" ref="survey_feedback" />
|
||||
<field name="sequence">5</field>
|
||||
<field name="title">How many times did you order products on our website?</field>
|
||||
<field name="question_type">numerical_box</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
</record>
|
||||
|
||||
<!-- Page 2 -->
|
||||
<record model="survey.question" id="survey_feedback_p2">
|
||||
<field name="title">About our ecommerce</field>
|
||||
<field name="survey_id" ref="survey_feedback" />
|
||||
<field name="sequence">6</field>
|
||||
<field name="is_page" eval="True" />
|
||||
<field name="question_type" eval="False" />
|
||||
<field name="description" type="html">
|
||||
<p>This section is about our eCommerce experience itself.</p></field>
|
||||
</record>
|
||||
<record model="survey.question" id="survey_feedback_p2_q1">
|
||||
<field name="survey_id" ref="survey_feedback" />
|
||||
<field name="sequence">7</field>
|
||||
<field name="title">Which of the following words would you use to describe our products?</field>
|
||||
<field name="question_type">multiple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="comments_allowed" eval="True"/>
|
||||
<field name="comment_count_as_answer" eval="False"/>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p2_q1_sug1">
|
||||
<field name="question_id" ref="survey_feedback_p2_q1"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">High quality</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p2_q1_sug2">
|
||||
<field name="question_id" ref="survey_feedback_p2_q1"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Useful</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p2_q1_sug3">
|
||||
<field name="question_id" ref="survey_feedback_p2_q1"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Unique</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p2_q1_sug4">
|
||||
<field name="question_id" ref="survey_feedback_p2_q1"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">Good value for money</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p2_q1_sug5">
|
||||
<field name="question_id" ref="survey_feedback_p2_q1"/>
|
||||
<field name="sequence">5</field>
|
||||
<field name="value">Overpriced</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p2_q1_sug6">
|
||||
<field name="question_id" ref="survey_feedback_p2_q1"/>
|
||||
<field name="sequence">6</field>
|
||||
<field name="value">Impractical</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p2_q1_sug7">
|
||||
<field name="question_id" ref="survey_feedback_p2_q1"/>
|
||||
<field name="sequence">7</field>
|
||||
<field name="value">Ineffective</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p2_q1_sug8">
|
||||
<field name="question_id" ref="survey_feedback_p2_q1"/>
|
||||
<field name="sequence">8</field>
|
||||
<field name="value">Poor quality</field>
|
||||
</record>
|
||||
<record model="survey.question" id="survey_feedback_p2_q2">
|
||||
<field name="survey_id" ref="survey_feedback" />
|
||||
<field name="sequence">8</field>
|
||||
<field name="title">What do you think about our new eCommerce?</field>
|
||||
<field name="question_type">matrix</field>
|
||||
<field name="matrix_subtype">multiple</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p2_q2_col1">
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Totally disagree</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p2_q2_col2">
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Disagree</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p2_q2_col3">
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Agree</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p2_q2_col4">
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">Totally agree</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p2_q2_row1">
|
||||
<field name="matrix_question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">The new layout and design is fresh and up-to-date</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p2_q2_row2">
|
||||
<field name="matrix_question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">It is easy to find the product that I want</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p2_q2_row3">
|
||||
<field name="matrix_question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">The tool to compare the products is useful to make a choice</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p2_q2_row4">
|
||||
<field name="matrix_question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">The checkout process is clear and secure</field>
|
||||
</record>
|
||||
<record model="survey.question.answer" id="survey_feedback_p2_q2_row5">
|
||||
<field name="matrix_question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="sequence">5</field>
|
||||
<field name="value">I have added products to my wishlist</field>
|
||||
</record>
|
||||
<record model="survey.question" id="survey_feedback_p2_q3">
|
||||
<field name="survey_id" ref="survey_feedback" />
|
||||
<field name="sequence">9</field>
|
||||
<field name="title">Do you have any other comments, questions, or concerns?</field>
|
||||
<field name="question_type">text_box</field>
|
||||
<field name="constr_mandatory" eval="False"/>
|
||||
</record>
|
||||
|
||||
</data></odoo>
|
29
data/survey_demo_feedback_user_input.xml
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data noupdate="0">
|
||||
|
||||
<record id="survey_answer_1" model="survey.user_input">
|
||||
<field name="survey_id" ref="survey.survey_feedback" />
|
||||
<field name="partner_id" ref="base.partner_demo"/>
|
||||
<field name="email">mark.brown23@example.com</field>
|
||||
<field name="state">done</field>
|
||||
</record>
|
||||
<record id="survey_answer_2" model="survey.user_input">
|
||||
<field name="survey_id" ref="survey.survey_feedback" />
|
||||
<field name="partner_id" ref="base.res_partner_address_7"/>
|
||||
<field name="email">billy.fox45@example.com</field>
|
||||
<field name="state">done</field>
|
||||
</record>
|
||||
<record id="survey_answer_3" model="survey.user_input">
|
||||
<field name="survey_id" ref="survey.survey_feedback" />
|
||||
<field name="partner_id" eval="False"/>
|
||||
<field name="email">Evelyne Gargouillis <evelyne@example.com></field>
|
||||
<field name="state">done</field>
|
||||
</record>
|
||||
<record id="survey_answer_4" model="survey.user_input">
|
||||
<field name="survey_id" ref="survey.survey_feedback" />
|
||||
<field name="partner_id" eval="False"/>
|
||||
<field name="email">Martin Tamarre <martin@example.com></field>
|
||||
<field name="state">in_progress</field>
|
||||
</record>
|
||||
|
||||
</data></odoo>
|
246
data/survey_demo_feedback_user_input_line.xml
Normal file
|
@ -0,0 +1,246 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data noupdate="0">
|
||||
|
||||
<!-- User input 1 -->
|
||||
<record id="survey_answer_1_p1_q1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_1"/>
|
||||
<field name="question_id" ref="survey_feedback_p1_q1"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">Brussels</field>
|
||||
</record>
|
||||
<record id="survey_answer_1_p1_q2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_1"/>
|
||||
<field name="question_id" ref="survey_feedback_p1_q2"/>
|
||||
<field name="answer_type">date</field>
|
||||
<field name="value_date">1980-01-11</field>
|
||||
</record>
|
||||
<record id="survey_answer_1_p1_q3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_1"/>
|
||||
<field name="question_id" ref="survey_feedback_p1_q3"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p1_q3_sug3"/>
|
||||
</record>
|
||||
<record id="survey_answer_1_p1_q4" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_1"/>
|
||||
<field name="question_id" ref="survey_feedback_p1_q4"/>
|
||||
<field name="answer_type">numerical_box</field>
|
||||
<field name="value_numerical_box">4</field>
|
||||
</record>
|
||||
<record id="survey_answer_1_p2_q1_1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_1"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q1"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q1_sug1"/>
|
||||
</record>
|
||||
<record id="survey_answer_1_p2_q1_2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_1"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q1"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q1_sug2"/>
|
||||
</record>
|
||||
<record id="survey_answer_1_p2_q2_1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_1"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q2_col3"/>
|
||||
<field name="matrix_row_id" ref="survey_feedback_p2_q2_row1"/>
|
||||
</record>
|
||||
<record id="survey_answer_1_p2_q2_2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_1"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q2_col3"/>
|
||||
<field name="matrix_row_id" ref="survey_feedback_p2_q2_row2"/>
|
||||
</record>
|
||||
<record id="survey_answer_1_p2_q2_3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_1"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q2_col2"/>
|
||||
<field name="matrix_row_id" ref="survey_feedback_p2_q2_row3"/>
|
||||
</record>
|
||||
<record id="survey_answer_1_p2_q2_4" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_1"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q2_col4"/>
|
||||
<field name="matrix_row_id" ref="survey_feedback_p2_q2_row4"/>
|
||||
</record>
|
||||
<record id="survey_answer_1_p2_q2_5" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_1"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q2_col2"/>
|
||||
<field name="matrix_row_id" ref="survey_feedback_p2_q2_row5"/>
|
||||
</record>
|
||||
<record id="survey_answer_1_p2_q3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_1"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q3"/>
|
||||
<field name="answer_type">text_box</field>
|
||||
<field name="value_text_box">Thanks for the good quality of your products</field>
|
||||
</record>
|
||||
|
||||
<!-- User input 2 -->
|
||||
<record id="survey_answer_2_p1_q1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_2"/>
|
||||
<field name="question_id" ref="survey_feedback_p1_q1"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">Paris</field>
|
||||
</record>
|
||||
<record id="survey_answer_2_p1_q2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_2"/>
|
||||
<field name="question_id" ref="survey_feedback_p1_q2"/>
|
||||
<field name="skipped" eval="True"/>
|
||||
</record>
|
||||
<record id="survey_answer_2_p1_q3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_2"/>
|
||||
<field name="question_id" ref="survey_feedback_p1_q3"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p1_q3_sug2"/>
|
||||
</record>
|
||||
<record id="survey_answer_2_p1_q4" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_2"/>
|
||||
<field name="question_id" ref="survey_feedback_p1_q4"/>
|
||||
<field name="answer_type">numerical_box</field>
|
||||
<field name="value_numerical_box">10</field>
|
||||
</record>
|
||||
<record id="survey_answer_2_p2_q1_1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_2"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q1"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q1_sug2"/>
|
||||
</record>
|
||||
<record id="survey_answer_2_p2_q1_2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_2"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q1"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q1_sug3"/>
|
||||
</record>
|
||||
<record id="survey_answer_2_p2_q1_3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_2"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q1"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q1_sug4"/>
|
||||
</record>
|
||||
<record id="survey_answer_2_p2_q2_1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_2"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q2_col4"/>
|
||||
<field name="matrix_row_id" ref="survey_feedback_p2_q2_row1"/>
|
||||
</record>
|
||||
<record id="survey_answer_2_p2_q2_2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_2"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q2_col3"/>
|
||||
<field name="matrix_row_id" ref="survey_feedback_p2_q2_row2"/>
|
||||
</record>
|
||||
<record id="survey_answer_2_p2_q2_3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_2"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q2_col3"/>
|
||||
<field name="matrix_row_id" ref="survey_feedback_p2_q2_row3"/>
|
||||
</record>
|
||||
<record id="survey_answer_2_p2_q2_4" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_2"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q2_col4"/>
|
||||
<field name="matrix_row_id" ref="survey_feedback_p2_q2_row4"/>
|
||||
</record>
|
||||
<record id="survey_answer_2_p2_q2_5" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_2"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q2_col3"/>
|
||||
<field name="matrix_row_id" ref="survey_feedback_p2_q2_row5"/>
|
||||
</record>
|
||||
<record id="survey_answer_2_p2_q3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_2"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q3"/>
|
||||
<field name="answer_type">text_box</field>
|
||||
<field name="value_text_box">I really appreciate your products. They are awesome !!</field>
|
||||
</record>
|
||||
|
||||
<!-- User input 3 -->
|
||||
<record id="survey_answer_3_p1_q1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_3"/>
|
||||
<field name="question_id" ref="survey_feedback_p1_q1"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">New York</field>
|
||||
</record>
|
||||
<record id="survey_answer_3_p1_q2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_3"/>
|
||||
<field name="question_id" ref="survey_feedback_p1_q2"/>
|
||||
<field name="answer_type">date</field>
|
||||
<field name="value_date">1966-06-15</field>
|
||||
</record>
|
||||
<record id="survey_answer_3_p1_q3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_3"/>
|
||||
<field name="question_id" ref="survey_feedback_p1_q3"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p1_q3_sug4"/>
|
||||
</record>
|
||||
<record id="survey_answer_3_p1_q4" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_3"/>
|
||||
<field name="question_id" ref="survey_feedback_p1_q4"/>
|
||||
<field name="answer_type">numerical_box</field>
|
||||
<field name="value_numerical_box">1</field>
|
||||
</record>
|
||||
<record id="survey_answer_3_p2_q1_1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_3"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q1"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q1_sug7"/>
|
||||
</record>
|
||||
<record id="survey_answer_3_p2_q1_2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_3"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q1"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q1_sug8"/>
|
||||
</record>
|
||||
<record id="survey_answer_3_p2_q2_1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_3"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q2_col1"/>
|
||||
<field name="matrix_row_id" ref="survey_feedback_p2_q2_row1"/>
|
||||
</record>
|
||||
<record id="survey_answer_3_p2_q2_2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_3"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q2_col2"/>
|
||||
<field name="matrix_row_id" ref="survey_feedback_p2_q2_row2"/>
|
||||
</record>
|
||||
<record id="survey_answer_3_p2_q2_3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_3"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q2_col1"/>
|
||||
<field name="matrix_row_id" ref="survey_feedback_p2_q2_row3"/>
|
||||
</record>
|
||||
<record id="survey_answer_3_p2_q2_4" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_3"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q2_col3"/>
|
||||
<field name="matrix_row_id" ref="survey_feedback_p2_q2_row4"/>
|
||||
</record>
|
||||
<record id="survey_answer_3_p2_q2_5" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_3"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_feedback_p2_q2_col1"/>
|
||||
<field name="matrix_row_id" ref="survey_feedback_p2_q2_row5"/>
|
||||
</record>
|
||||
<record id="survey_answer_3_p2_q3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_answer_3"/>
|
||||
<field name="question_id" ref="survey_feedback_p2_q3"/>
|
||||
<field name="answer_type">text_box</field>
|
||||
<field name="value_text_box">The customizable desk received is not the one I ordered on your website and the quality is very poor! Really disappointed.</field>
|
||||
</record>
|
||||
|
||||
</data></odoo>
|
576
data/survey_demo_quiz.xml
Normal file
|
@ -0,0 +1,576 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data noupdate="0">
|
||||
|
||||
<record id="survey_demo_quiz" model="survey.survey">
|
||||
<field name="title">Quiz about our Company</field>
|
||||
<field name="access_token">b137640d-9876-1234-abcd-344ca256531e</field>
|
||||
<field name="user_id" ref="base.user_admin"/>
|
||||
<field name="access_mode">public</field>
|
||||
<field name="users_can_go_back" eval="False"/>
|
||||
<field name="scoring_type">scoring_with_answers</field>
|
||||
<field name="scoring_success_min">55</field>
|
||||
<field name="questions_layout">page_per_question</field>
|
||||
<field name="description" type="html">
|
||||
<p>This small quiz will test your knowledge about our Company. Be prepared!</p></field>
|
||||
<field name="background_image" type="base64" file="survey/static/src/img/survey_background.jpg"/>
|
||||
</record>
|
||||
|
||||
<!-- Page 1: general informations -->
|
||||
<record id="survey_demo_quiz_p1" model="survey.question">
|
||||
<field name="title">Who are you?</field>
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="question_type" eval="False"/>
|
||||
<field name="is_page" eval="True"/>
|
||||
<field name="description" type="html">
|
||||
<p>Some general information about you. It will be used internally for statistics only.</p></field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p1_q1" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="title">What is your email?</field>
|
||||
<field name="question_type">char_box</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="validation_email" eval="True"/>
|
||||
<field name="save_as_email" eval="True"/>
|
||||
<field name="question_placeholder">ex@mple.com</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p1_q2" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="title">What is your nickname?</field>
|
||||
<field name="question_type">char_box</field>
|
||||
<field name="question_placeholder">Don't be shy, be wild!</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="save_as_nickname" eval="True"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p1_q3" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="title">Where are you from?</field>
|
||||
<field name="question_type">char_box</field>
|
||||
<field name="question_placeholder">Brussels, Belgium</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p1_q4" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">5</field>
|
||||
<field name="title">How old are you?</field>
|
||||
<field name="description" type="html"><p>Just to categorize your answers, don't worry.</p></field>
|
||||
<field name="question_type">numerical_box</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
</record>
|
||||
|
||||
<!-- Page 2: quiz about company -->
|
||||
<record id="survey_demo_quiz_p2" model="survey.question">
|
||||
<field name="title">Our Company in a few questions ...</field>
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">10</field>
|
||||
<field name="question_type" eval="False"/>
|
||||
<field name="is_page" eval="True"/>
|
||||
<field name="description" type="html">
|
||||
<p>Some questions about our company. Do you really know us?</p></field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p2_q1" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">11</field>
|
||||
<field name="title">When is Mitchell Admin born?</field>
|
||||
<field name="description" type="html"><span>Our famous Leader!</span></field>
|
||||
<field name="question_type">date</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p2_q2" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">12</field>
|
||||
<field name="title">When did precisely Marc Demo crop its first apple tree?</field>
|
||||
<field name="question_type">datetime</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p2_q3" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">13</field>
|
||||
<field name="title">Give the list of all types of wood we sell.</field>
|
||||
<field name="question_type">text_box</field>
|
||||
<field name="constr_mandatory" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- Page 3: quiz about fruits and vegetables -->
|
||||
<record id="survey_demo_quiz_p3" model="survey.question">
|
||||
<field name="title">Fruits and vegetables</field>
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">20</field>
|
||||
<field name="question_type" eval="False"/>
|
||||
<field name="is_page" eval="True"/>
|
||||
<field name="description" type="html">
|
||||
<p>An apple a day keeps the doctor away.</p></field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q1" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">21</field>
|
||||
<field name="title">Which category does a tomato belong to</field>
|
||||
<field name="description" type="html"><span>"Red" is not a category, I know what you are trying to do ;)</span></field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="comments_allowed" eval="True"/>
|
||||
<field name="comment_count_as_answer" eval="True"/>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q1_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q1"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Fruits</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q1_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q1"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Vegetables</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">10</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q1_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q1"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Space stations</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q2" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">22</field>
|
||||
<field name="title">Which of the following would you use to pollinate</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="comments_allowed" eval="True"/>
|
||||
<field name="comment_count_as_answer" eval="False"/>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="is_time_limited" eval="True"/>
|
||||
<field name="time_limit">15</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q2_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q2"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Bees</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q2_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q2"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Dogs</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q2_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q2"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Mooses</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q3" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">23</field>
|
||||
<field name="title">Select trees that made more than 20K sales this year</field>
|
||||
<field name="description" type="html"><span>Our sales people have an advantage, but you can do it!</span></field>
|
||||
<field name="question_type">multiple_choice</field>
|
||||
<field name="constr_mandatory" eval="False"/>
|
||||
<field name="comments_allowed" eval="True"/>
|
||||
<field name="comment_count_as_answer" eval="True"/>
|
||||
<field name="is_time_limited" eval="True"/>
|
||||
<field name="time_limit">20</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q3_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q3"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Apple Trees</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q3_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q3"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Lemon Trees</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">10</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q3_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q3"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Baobab Trees</field>
|
||||
<field name="answer_score">-10</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q3_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q3"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">Cookies</field>
|
||||
<field name="answer_score">-10</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q4" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">24</field>
|
||||
<field name="title">A "Citrus" could give you ...</field>
|
||||
<field name="question_type">multiple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="comments_allowed" eval="True"/>
|
||||
<field name="comment_count_as_answer" eval="False"/>
|
||||
<field name="is_time_limited" eval="True"/>
|
||||
<field name="time_limit">20</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q4_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q4"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Pomelos</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q4_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q4"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Grapefruits</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q4_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q4"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Cosmic rays</field>
|
||||
<field name="answer_score">-10</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q4_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q4"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">Bricks</field>
|
||||
<field name="answer_score">-10</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q5" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">25</field>
|
||||
<field name="title">How often should you water those plants</field>
|
||||
<field name="question_type">matrix</field>
|
||||
<field name="matrix_subtype">simple</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="comments_allowed" eval="True"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q5_row1" model="survey.question.answer">
|
||||
<field name="matrix_question_id" ref="survey_demo_quiz_p3_q5"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Cactus</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q5_row2" model="survey.question.answer">
|
||||
<field name="matrix_question_id" ref="survey_demo_quiz_p3_q5"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Ficus</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q5_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q5"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Once a month</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q5_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q5"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Once a week</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q6" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">26</field>
|
||||
<field name="title">When do you harvest those fruits</field>
|
||||
<field name="description" type="html"><span>Best time to do it, is the right time to do it.</span></field>
|
||||
<field name="question_type">matrix</field>
|
||||
<field name="matrix_subtype">multiple</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="comments_allowed" eval="False"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q6_row1" model="survey.question.answer">
|
||||
<field name="matrix_question_id" ref="survey_demo_quiz_p3_q6"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Apples</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q6_row2" model="survey.question.answer">
|
||||
<field name="matrix_question_id" ref="survey_demo_quiz_p3_q6"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Strawberries</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q6_row3" model="survey.question.answer">
|
||||
<field name="matrix_question_id" ref="survey_demo_quiz_p3_q6"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Clementine</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q6_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q6"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Spring</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q6_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q6"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Summer</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q6_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q6"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Autumn</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p3_q6_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q6"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">Winter</field>
|
||||
</record>
|
||||
|
||||
<!-- Page 4: Trees -->
|
||||
<record id="survey_demo_quiz_p4" model="survey.question">
|
||||
<field name="title">Trees</field>
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">30</field>
|
||||
<field name="is_page" eval="True"/>
|
||||
<field name="question_type" eval="False"/>
|
||||
<field name="description" type="html">
|
||||
<p>
|
||||
We like to say that the apple doesn't fall far from the tree, so here are trees.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="survey_demo_quiz_p4_q1" model="survey.question">
|
||||
<field name="title">Dogwood is from which family of trees?</field>
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">31</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q1_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q1"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Pinaceae</field>
|
||||
<field name="value_image" type="base64" file="survey/static/img/pinaceae.jpg"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q1_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q1"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Ulmaceae</field>
|
||||
<field name="value_image" type="base64" file="survey/static/img/ulmaceae.jpg"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q1_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q1"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Cornaceae</field>
|
||||
<field name="value_image" type="base64" file="survey/static/img/cornaceae.jpg"/>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q1_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q1"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">Salicaceae</field>
|
||||
<field name="value_image" type="base64" file="survey/static/img/salicaceae.jpg"/>
|
||||
</record>
|
||||
|
||||
<record id="survey_demo_quiz_p4_q2" model="survey.question">
|
||||
<field name="title">In which country did the bonsai technique develop?</field>
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">32</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q2_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q2"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Japan</field>
|
||||
<field name="value_image" type="base64" file="survey/static/img/japan.jpg"/>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q2_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q2"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">China</field>
|
||||
<field name="value_image" type="base64" file="survey/static/img/china.jpg"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q2_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q2"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Vietnam</field>
|
||||
<field name="value_image" type="base64" file="survey/static/img/vietnam.jpg"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q2_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q2"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">South Korea</field>
|
||||
<field name="value_image" type="base64" file="survey/static/img/south_korea.jpg"/>
|
||||
</record>
|
||||
|
||||
<record id="survey_demo_quiz_p4_q3" model="survey.question">
|
||||
<field name="title">Is the wood of a coniferous hard or soft?</field>
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">33</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="description" type="html">
|
||||
<p>
|
||||
<img class="img-fluid o_we_custom_image d-block mx-auto"
|
||||
src="/survey/static/img/coniferous.jpg"/><br/>
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q3_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q3"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Hard</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q3_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q3"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Soft</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">10</field>
|
||||
</record>
|
||||
|
||||
<record id="survey_demo_quiz_p4_q4" model="survey.question">
|
||||
<field name="title">From which continent is native the Scots pine (pinus sylvestris)?</field>
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">34</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
<field name="description" type="html">
|
||||
<p>
|
||||
<img class="img-fluid o_we_custom_image d-block mx-auto"
|
||||
src="/survey/static/img/pinus_sylvestris.jpg" style="width: 100%;"/><br/>
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q4_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q4"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Africa</field>
|
||||
<field name="value_image" type="base64" file="survey/static/img/africa.png"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q4_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q4"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Asia</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
<field name="value_image" type="base64" file="survey/static/img/asia.png"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q4_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q4"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Europe</field>
|
||||
<field name="value_image" type="base64" file="survey/static/img/europe.png"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q4_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q4"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">South America</field>
|
||||
<field name="value_image" type="base64" file="survey/static/img/south_america.png"/>
|
||||
</record>
|
||||
|
||||
<record id="survey_demo_quiz_p4_q5" model="survey.question">
|
||||
<field name="title">In the list below, select all the coniferous.</field>
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">35</field>
|
||||
<field name="question_type">multiple_choice</field>
|
||||
<field name="constr_mandatory" eval="True"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q5_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q5"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Douglas Fir</field>
|
||||
<field name="value_image" type="base64" file="survey/static/img/douglas_fir.jpg"/>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">5</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q5_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q5"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Norway Spruce</field>
|
||||
<field name="value_image" type="base64" file="survey/static/img/norway_spruce.jpg"/>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">5</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q5_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q5"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">European Yew</field>
|
||||
<field name="value_image" type="base64" file="survey/static/img/european_yew.jpg"/>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">5</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q5_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q5"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">Mountain Pine</field>
|
||||
<field name="value_image" type="base64" file="survey/static/img/mountain_pine.jpg"/>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">5</field>
|
||||
</record>
|
||||
|
||||
<record id="survey_demo_quiz_p4_q6" model="survey.question">
|
||||
<field name="title">After watching this video, will you swear that you are not going to procrastinate to trim your hedge this year?</field>
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">36</field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="description" type="html">
|
||||
<div class="text-center">
|
||||
<div class="media_iframe_video" data-oe-expression="//www.youtube.com/embed/7y4T6yv5L1k?autoplay=0&rel=0" style="width: 50%;">
|
||||
<div class="css_editable_mode_display"/>
|
||||
<div class="media_iframe_video_size" contenteditable="false"/>
|
||||
<iframe src="//www.youtube.com/embed/7y4T6yv5L1k?autoplay=0&rel=0" frameborder="0" contenteditable="false"></iframe>
|
||||
</div><br/>
|
||||
</div>
|
||||
</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q6_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q6"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Yes</field>
|
||||
<field name="is_correct" eval="True"/>
|
||||
<field name="answer_score">10</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q6_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q6"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">No</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q6_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q6"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Perhaps</field>
|
||||
<field name="answer_score">-10</field>
|
||||
</record>
|
||||
|
||||
<!-- Page 5: Feedback - non scored question -->
|
||||
<record id="survey_demo_quiz_p5" model="survey.question">
|
||||
<field name="title">Your feeling</field>
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">40</field>
|
||||
<field name="question_type" eval="False"/>
|
||||
<field name="is_page" eval="True"/>
|
||||
<field name="description" type="html">
|
||||
<p>We may be interested by your input.</p></field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p5_q1" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">41</field>
|
||||
<field name="title">What do you think about this survey?</field>
|
||||
<field name="description" type="html"><span>If you don't like us, please try to be as objective as possible.</span></field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="comments_allowed" eval="True"/>
|
||||
<field name="comment_count_as_answer" eval="True"/>
|
||||
<field name="constr_mandatory" eval="False"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p5_q1_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p5_q1"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Good</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p5_q1_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p5_q1"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Not Good, Not Bad</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p5_q1_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p5_q1"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Iznogoud</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p5_q1_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p5_q1"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">I have no idea, I'm a dog!</field>
|
||||
</record>
|
||||
</data></odoo>
|
42
data/survey_demo_quiz_user_input.xml
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data noupdate="0">
|
||||
|
||||
<record id="survey_demo_quiz_answer_1" model="survey.user_input">
|
||||
<field name="survey_id" ref="survey.survey_demo_quiz"/>
|
||||
<field name="partner_id" ref="base.partner_demo"/>
|
||||
<field name="email">mark.brown23@example.com</field>
|
||||
<field name="end_datetime" eval="datetime.now() - timedelta(days=1, hours=3, minutes=30)"/>
|
||||
<field name="start_datetime" eval="datetime.now() - timedelta(days=1, hours=4, minutes=50)"/>
|
||||
<field name="state">done</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2" model="survey.user_input">
|
||||
<field name="survey_id" ref="survey.survey_demo_quiz"/>
|
||||
<field name="partner_id" ref="base.partner_admin"/>
|
||||
<field name="email">admin@yourcompany.example.com</field>
|
||||
<field name="end_datetime" eval="datetime.now() - timedelta(days=1, hours=2, minutes=50)"/>
|
||||
<field name="start_datetime" eval="datetime.now() - timedelta(days=1, hours=3, minutes=50)"/>
|
||||
<field name="state">done</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3" model="survey.user_input">
|
||||
<field name="survey_id" ref="survey.survey_demo_quiz"/>
|
||||
<field name="partner_id" ref="base.partner_demo_portal"/>
|
||||
<field name="email">joel.willis63@example.com</field>
|
||||
<field name="end_datetime" eval="datetime.now() - timedelta(days=1, hours=2, minutes=10)"/>
|
||||
<field name="start_datetime" eval="datetime.now() - timedelta(days=1, hours=2, minutes=50)"/>
|
||||
<field name="state">done</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_4" model="survey.user_input">
|
||||
<field name="survey_id" ref="survey.survey_demo_quiz"/>
|
||||
<field name="partner_id" ref="base.res_partner_address_28"/>
|
||||
<field name="email">colleen.diaz83@example.com</field>
|
||||
<field name="start_datetime" eval="datetime.now() - timedelta(days=1, hours=0, minutes=50)"/>
|
||||
<field name="state">in_progress</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_5" model="survey.user_input">
|
||||
<field name="survey_id" ref="survey.survey_demo_quiz"/>
|
||||
<field name="partner_id" ref="base.res_partner_address_34"/>
|
||||
<field name="email">travis.mendoza24@example.com</field>
|
||||
<field name="state">new</field>
|
||||
</record>
|
||||
|
||||
</data></odoo>
|
564
data/survey_demo_quiz_user_input_line.xml
Normal file
|
@ -0,0 +1,564 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data noupdate="0">
|
||||
<!-- Page 1: general informations -->
|
||||
<record id="survey_demo_quiz_answer_1_p1_q1_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p1_q1"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">mark.brown23@example.com</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p1_q2_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p1_q2"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">Mark Brown</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p1_q3_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p1_q3"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">Brussels</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p1_q4_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p1_q4"/>
|
||||
<field name="answer_type">numerical_box</field>
|
||||
<field name="value_numerical_box">36</field>
|
||||
</record>
|
||||
<!-- Page 2: quiz about company -->
|
||||
<record id="survey_demo_quiz_answer_1_p2_q1_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p2_q1"/>
|
||||
<field name="answer_type">date</field>
|
||||
<field name="value_date" eval="DateTime.today() - relativedelta(years=36)"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p2_q2_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p2_q2"/>
|
||||
<field name="answer_type">datetime</field>
|
||||
<field name="value_datetime" eval="DateTime.now().replace(year=2017, month=10, day=2, hour=2, minute=27, second=0)"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p2_q3_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p2_q3"/>
|
||||
<field name="answer_type">text_box</field>
|
||||
<field name="value_text_box">Oak, ash, pine</field>
|
||||
</record>
|
||||
<!-- Page 3: quiz about fruits and vegetables -->
|
||||
<record id="survey_demo_quiz_answer_1_p3_q1_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q1"/>
|
||||
<field name="answer_is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q1_sug1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p3_q2_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q2"/>
|
||||
<field name="answer_is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q2_sug1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p3_q2_l2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q2"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">Mooses?? Really?</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p3_q3_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q3"/>
|
||||
<field name="answer_is_correct" eval="True"/>
|
||||
<field name="answer_score">10</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q3_sug2"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p3_q3_l2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q3"/>
|
||||
<field name="answer_score">-10</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q3_sug3"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p3_q4_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q4"/>
|
||||
<field name="answer_is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q4_sug1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p3_q4_l2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q4"/>
|
||||
<field name="answer_score">-10</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q4_sug3"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p3_q5_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q5"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q5_sug1"/>
|
||||
<field name="matrix_row_id" ref="survey_demo_quiz_p3_q5_row1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p3_q5_l2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q5"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q5_sug1"/>
|
||||
<field name="matrix_row_id" ref="survey_demo_quiz_p3_q5_row2"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p3_q6_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q6"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q6_sug1"/>
|
||||
<field name="matrix_row_id" ref="survey_demo_quiz_p3_q6_row1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p3_q6_l2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q6"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q6_sug1"/>
|
||||
<field name="matrix_row_id" ref="survey_demo_quiz_p3_q6_row2"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p3_q6_l3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q6"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q6_sug1"/>
|
||||
<field name="matrix_row_id" ref="survey_demo_quiz_p3_q6_row3"/>
|
||||
</record>
|
||||
<!-- Page 4: trees -->
|
||||
<record id="survey_demo_quiz_answer_1_p4_q1_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q1"/>
|
||||
<field name="answer_is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q1_sug1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p4_q2_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q2_sug1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p4_q3_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q3"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q3_sug1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p4_q4_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q4"/>
|
||||
<field name="answer_is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q4_sug2"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p4_q5_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q5"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q5_sug1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p4_q5_l2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q5"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q5_sug2"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_1_p4_q6_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_1"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q6"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q6_sug1"/>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- Page 1: general informations -->
|
||||
<record id="survey_demo_quiz_answer_2_p1_q1_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p1_q1"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">admin@yourcompany.example.com</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p1_q2_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p1_q2"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">Mitchell Admin</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p1_q3_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p1_q3"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">Ottawa</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p1_q4_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p1_q4"/>
|
||||
<field name="answer_type">numerical_box</field>
|
||||
<field name="value_numerical_box">48</field>
|
||||
</record>
|
||||
<!-- Page 2: quiz about company -->
|
||||
<record id="survey_demo_quiz_answer_2_p2_q1_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p2_q1"/>
|
||||
<field name="answer_type">date</field>
|
||||
<field name="value_date" eval="DateTime.today() + relativedelta(years=24)"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p2_q2_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p2_q2"/>
|
||||
<field name="answer_type">datetime</field>
|
||||
<field name="value_datetime" eval="DateTime.now().replace(year=2011, month=8, day=21, hour=15, minute=34, second=0)"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p2_q3_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p2_q3"/>
|
||||
<field name="skipped" eval="True"/>
|
||||
</record>
|
||||
<!-- Page 3: quiz about fruits and vegetables -->
|
||||
<record id="survey_demo_quiz_answer_2_p3_q1_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q1"/>
|
||||
<field name="answer_is_correct" eval="True"/>
|
||||
<field name="answer_score">10</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q1_sug2"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p3_q2_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q2_sug3"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p3_q2_l2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q2"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">Mooses are best pollinators of the world!</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p3_q3_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q3"/>
|
||||
<field name="answer_is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q3_sug1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p3_q3_l2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q3"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">I sold a 30K raspberry tree once.</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p3_q4_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q4"/>
|
||||
<field name="answer_is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q4_sug1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p3_q5_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q5"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q5_sug1"/>
|
||||
<field name="matrix_row_id" ref="survey_demo_quiz_p3_q5_row1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p3_q5_l2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q5"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q5_sug2"/>
|
||||
<field name="matrix_row_id" ref="survey_demo_quiz_p3_q5_row2"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p3_q6_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q6"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q6_sug1"/>
|
||||
<field name="matrix_row_id" ref="survey_demo_quiz_p3_q6_row1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p3_q6_l2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q6"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q6_sug2"/>
|
||||
<field name="matrix_row_id" ref="survey_demo_quiz_p3_q6_row2"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p3_q6_l3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q6"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q6_sug3"/>
|
||||
<field name="matrix_row_id" ref="survey_demo_quiz_p3_q6_row3"/>
|
||||
</record>
|
||||
<!-- Page 4: trees -->
|
||||
<record id="survey_demo_quiz_answer_2_p4_q1_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q1"/>
|
||||
<field name="answer_is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q1_sug1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p4_q2_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q2"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q2_sug1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p4_q3_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q3"/>
|
||||
<field name="answer_is_correct" eval="True"/>
|
||||
<field name="answer_score">10</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q3_sug2"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p4_q4_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q4"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q4_sug1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p4_q5_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q5"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q5_sug1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p4_q5_l2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q5"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q5_sug3"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_2_p4_q6_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_2"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q6"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q6_sug1"/>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- Page 1: general informations -->
|
||||
<record id="survey_demo_quiz_answer_3_p1_q1_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p1_q1"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">joel.willis63@example.com</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p1_q2_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p1_q2"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">Joël Willis</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p1_q3_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p1_q3"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">Brussels</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p1_q4_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p1_q4"/>
|
||||
<field name="answer_type">numerical_box</field>
|
||||
<field name="value_numerical_box">28</field>
|
||||
</record>
|
||||
<!-- Page 2: quiz about company -->
|
||||
<record id="survey_demo_quiz_answer_3_p2_q1_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p2_q1"/>
|
||||
<field name="answer_type">date</field>
|
||||
<field name="value_date" eval="DateTime.today() - relativedelta(years=38)"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p2_q2_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p2_q2"/>
|
||||
<field name="answer_type">datetime</field>
|
||||
<field name="value_datetime" eval="DateTime.now().replace(year=2005, month=4, day=18, hour=10, minute=0, second=0)"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p2_q3_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p2_q3"/>
|
||||
<field name="answer_type">text_box</field>
|
||||
<field name="value_text_box">Oak, fur, pine, red pine</field>
|
||||
</record>
|
||||
<!-- Page 3: quiz about fruits and vegetables -->
|
||||
<record id="survey_demo_quiz_answer_3_p3_q1_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q1"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">Both fruit (seeds, par of the plant) and vegetable (culinary use), obviously.</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p3_q2_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q2"/>
|
||||
<field name="answer_is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q2_sug1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p3_q3_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q3"/>
|
||||
<field name="answer_is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q3_sug1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p3_q3_l2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q3"/>
|
||||
<field name="answer_is_correct" eval="True"/>
|
||||
<field name="answer_score">10</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q3_sug2"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p3_q3_l3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q3"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">You forgot the strawberry tree.</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p3_q4_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q4"/>
|
||||
<field name="answer_is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q4_sug1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p3_q4_l2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q4"/>
|
||||
<field name="answer_is_correct" eval="True"/>
|
||||
<field name="answer_score">20</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q4_sug2"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p3_q4_l3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q4"/>
|
||||
<field name="answer_score">-10</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q4_sug3"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p3_q4_l4" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q4"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">Gives the beeest cosmics rays man. So juicy.</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p3_q5_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q5"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q5_sug1"/>
|
||||
<field name="matrix_row_id" ref="survey_demo_quiz_p3_q5_row1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p3_q5_l2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q5"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q5_sug2"/>
|
||||
<field name="matrix_row_id" ref="survey_demo_quiz_p3_q5_row2"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p3_q5_l3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q5"/>
|
||||
<field name="answer_type">char_box</field>
|
||||
<field name="value_char_box">Well sometimes I forget them, they survived. Almost.</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p3_q6_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q6"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q6_sug3"/>
|
||||
<field name="matrix_row_id" ref="survey_demo_quiz_p3_q6_row1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p3_q6_l2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q6"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q6_sug4"/>
|
||||
<field name="matrix_row_id" ref="survey_demo_quiz_p3_q6_row1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p3_q6_l3" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q6"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q6_sug1"/>
|
||||
<field name="matrix_row_id" ref="survey_demo_quiz_p3_q6_row2"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p3_q6_l4" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q6"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q6_sug2"/>
|
||||
<field name="matrix_row_id" ref="survey_demo_quiz_p3_q6_row2"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p3_q6_l5" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p3_q6"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p3_q6_sug4"/>
|
||||
<field name="matrix_row_id" ref="survey_demo_quiz_p3_q6_row3"/>
|
||||
</record>
|
||||
<!-- Page 4: trees -->
|
||||
<record id="survey_demo_quiz_answer_3_p4_q1_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q1"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q1_sug3"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p4_q2_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q2"/>
|
||||
<field name="answer_score">20</field>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q2_sug2"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p4_q3_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q3"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q3_sug1"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p4_q4_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q4"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q4_sug3"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p4_q5_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q5"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q5_sug2"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p4_q5_l2" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q5"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q5_sug3"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_answer_3_p4_q6_l1" model="survey.user_input.line">
|
||||
<field name="user_input_id" ref="survey_demo_quiz_answer_3"/>
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q6"/>
|
||||
<field name="answer_type">suggestion</field>
|
||||
<field name="suggested_answer_id" ref="survey_demo_quiz_p4_q6_sug1"/>
|
||||
</record>
|
||||
|
||||
</data></odoo>
|
5255
i18n/af.po
Normal file
5782
i18n/ar.po
Normal file
5260
i18n/az.po
Normal file
5594
i18n/bg.po
Normal file
5257
i18n/bs.po
Normal file
5774
i18n/ca.po
Normal file
5709
i18n/cs.po
Normal file
5694
i18n/da.po
Normal file
5889
i18n/de.po
Normal file
5261
i18n/el.po
Normal file
5253
i18n/en_GB.po
Normal file
5882
i18n/es.po
Normal file
5883
i18n/es_419.po
Normal file
5253
i18n/es_CL.po
Normal file
5256
i18n/es_CO.po
Normal file
5253
i18n/es_CR.po
Normal file
5253
i18n/es_DO.po
Normal file
5255
i18n/es_EC.po
Normal file
5254
i18n/es_PE.po
Normal file
5253
i18n/es_PY.po
Normal file
5257
i18n/es_VE.po
Normal file
5745
i18n/et.po
Normal file
5653
i18n/fa.po
Normal file
5791
i18n/fi.po
Normal file
5892
i18n/fr.po
Normal file
5253
i18n/gl.po
Normal file
5264
i18n/gu.po
Normal file
5616
i18n/he.po
Normal file
5270
i18n/hr.po
Normal file
5626
i18n/hu.po
Normal file
5865
i18n/id.po
Normal file
5255
i18n/is.po
Normal file
5871
i18n/it.po
Normal file
5718
i18n/ja.po
Normal file
5253
i18n/ka.po
Normal file
5253
i18n/kab.po
Normal file
5258
i18n/km.po
Normal file
5722
i18n/ko.po
Normal file
5251
i18n/lb.po
Normal file
5648
i18n/lt.po
Normal file
5682
i18n/lv.po
Normal file
5258
i18n/mk.po
Normal file
5275
i18n/mn.po
Normal file
5268
i18n/nb.po
Normal file
5862
i18n/nl.po
Normal file
5783
i18n/pl.po
Normal file
5584
i18n/pt.po
Normal file
5849
i18n/pt_BR.po
Normal file
5300
i18n/ro.po
Normal file
5874
i18n/ru.po
Normal file
5600
i18n/sk.po
Normal file
5594
i18n/sl.po
Normal file
5766
i18n/sr.po
Normal file
5257
i18n/sr@latin.po
Normal file
5568
i18n/survey.pot
Normal file
5788
i18n/sv.po
Normal file
5794
i18n/th.po
Normal file
5754
i18n/tr.po
Normal file
5779
i18n/uk.po
Normal file
5852
i18n/vi.po
Normal file
5709
i18n/zh_CN.po
Normal file
5660
i18n/zh_TW.po
Normal file
10
models/__init__.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import survey_survey
|
||||
from . import survey_survey_template
|
||||
from . import survey_question
|
||||
from . import survey_user_input
|
||||
from . import badge
|
||||
from . import challenge
|
||||
from . import res_partner
|
16
models/badge.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class GamificationBadge(models.Model):
|
||||
_inherit = 'gamification.badge'
|
||||
|
||||
survey_ids = fields.One2many('survey.survey', 'certification_badge_id', 'Survey Ids')
|
||||
survey_id = fields.Many2one('survey.survey', 'Survey', compute='_compute_survey_id', store=True)
|
||||
|
||||
@api.depends('survey_ids.certification_badge_id')
|
||||
def _compute_survey_id(self):
|
||||
for badge in self:
|
||||
badge.survey_id = badge.survey_ids[0] if badge.survey_ids else None
|
12
models/challenge.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models, fields
|
||||
|
||||
|
||||
class Challenge(models.Model):
|
||||
_inherit = 'gamification.challenge'
|
||||
|
||||
challenge_category = fields.Selection(selection_add=[
|
||||
('certification', 'Certifications')
|
||||
], ondelete={'certification': 'set default'})
|
32
models/res_partner.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
_inherit = 'res.partner'
|
||||
|
||||
certifications_count = fields.Integer('Certifications Count', compute='_compute_certifications_count')
|
||||
certifications_company_count = fields.Integer('Company Certifications Count', compute='_compute_certifications_company_count')
|
||||
|
||||
@api.depends('is_company')
|
||||
def _compute_certifications_count(self):
|
||||
read_group_res = self.env['survey.user_input'].sudo()._read_group(
|
||||
[('partner_id', 'in', self.ids), ('scoring_success', '=', True)],
|
||||
['partner_id'], ['__count']
|
||||
)
|
||||
data = {partner.id: count for partner, count in read_group_res}
|
||||
for partner in self:
|
||||
partner.certifications_count = data.get(partner.id, 0)
|
||||
|
||||
@api.depends('is_company', 'child_ids.certifications_count')
|
||||
def _compute_certifications_company_count(self):
|
||||
self.certifications_company_count = sum(child.certifications_count for child in self.child_ids)
|
||||
|
||||
def action_view_certifications(self):
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("survey.res_partner_action_certifications")
|
||||
action['view_mode'] = 'tree'
|
||||
action['domain'] = ['|', ('partner_id', 'in', self.ids), ('partner_id', 'in', self.child_ids.ids)]
|
||||
|
||||
return action
|
756
models/survey_question.py
Normal file
|
@ -0,0 +1,756 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import collections
|
||||
import contextlib
|
||||
import itertools
|
||||
import json
|
||||
import operator
|
||||
from textwrap import shorten
|
||||
|
||||
from odoo import api, fields, models, tools, _
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
|
||||
class SurveyQuestion(models.Model):
|
||||
""" Questions that will be asked in a survey.
|
||||
|
||||
Each question can have one of more suggested answers (eg. in case of
|
||||
multi-answer checkboxes, radio buttons...).
|
||||
|
||||
Technical note:
|
||||
|
||||
survey.question is also the model used for the survey's pages (with the "is_page" field set to True).
|
||||
|
||||
A page corresponds to a "section" in the interface, and the fact that it separates the survey in
|
||||
actual pages in the interface depends on the "questions_layout" parameter on the survey.survey model.
|
||||
Pages are also used when randomizing questions. The randomization can happen within a "page".
|
||||
|
||||
Using the same model for questions and pages allows to put all the pages and questions together in a o2m field
|
||||
(see survey.survey.question_and_page_ids) on the view side and easily reorganize your survey by dragging the
|
||||
items around.
|
||||
|
||||
It also removes on level of encoding by directly having 'Add a page' and 'Add a question'
|
||||
links on the tree view of questions, enabling a faster encoding.
|
||||
|
||||
However, this has the downside of making the code reading a little bit more complicated.
|
||||
Efforts were made at the model level to create computed fields so that the use of these models
|
||||
still seems somewhat logical. That means:
|
||||
- A survey still has "page_ids" (question_and_page_ids filtered on is_page = True)
|
||||
- These "page_ids" still have question_ids (questions located between this page and the next)
|
||||
- These "question_ids" still have a "page_id"
|
||||
|
||||
That makes the use and display of these information at view and controller levels easier to understand.
|
||||
"""
|
||||
_name = 'survey.question'
|
||||
_description = 'Survey Question'
|
||||
_rec_name = 'title'
|
||||
_order = 'sequence,id'
|
||||
|
||||
# question generic data
|
||||
title = fields.Char('Title', required=True, translate=True)
|
||||
description = fields.Html(
|
||||
'Description', translate=True, sanitize=True, sanitize_overridable=True,
|
||||
help="Use this field to add additional explanations about your question or to illustrate it with pictures or a video")
|
||||
question_placeholder = fields.Char("Placeholder", translate=True, compute="_compute_question_placeholder", store=True, readonly=False)
|
||||
background_image = fields.Image("Background Image", compute="_compute_background_image", store=True, readonly=False)
|
||||
background_image_url = fields.Char("Background Url", compute="_compute_background_image_url")
|
||||
survey_id = fields.Many2one('survey.survey', string='Survey', ondelete='cascade')
|
||||
scoring_type = fields.Selection(related='survey_id.scoring_type', string='Scoring Type', readonly=True)
|
||||
sequence = fields.Integer('Sequence', default=10)
|
||||
# page specific
|
||||
is_page = fields.Boolean('Is a page?')
|
||||
question_ids = fields.One2many('survey.question', string='Questions', compute="_compute_question_ids")
|
||||
questions_selection = fields.Selection(
|
||||
related='survey_id.questions_selection', readonly=True,
|
||||
help="If randomized is selected, add the number of random questions next to the section.")
|
||||
random_questions_count = fields.Integer(
|
||||
'# Questions Randomly Picked', default=1,
|
||||
help="Used on randomized sections to take X random questions from all the questions of that section.")
|
||||
# question specific
|
||||
page_id = fields.Many2one('survey.question', string='Page', compute="_compute_page_id", store=True)
|
||||
question_type = fields.Selection([
|
||||
('simple_choice', 'Multiple choice: only one answer'),
|
||||
('multiple_choice', 'Multiple choice: multiple answers allowed'),
|
||||
('text_box', 'Multiple Lines Text Box'),
|
||||
('char_box', 'Single Line Text Box'),
|
||||
('numerical_box', 'Numerical Value'),
|
||||
('date', 'Date'),
|
||||
('datetime', 'Datetime'),
|
||||
('matrix', 'Matrix')], string='Question Type',
|
||||
compute='_compute_question_type', readonly=False, store=True)
|
||||
is_scored_question = fields.Boolean(
|
||||
'Scored', compute='_compute_is_scored_question',
|
||||
readonly=False, store=True, copy=True,
|
||||
help="Include this question as part of quiz scoring. Requires an answer and answer score to be taken into account.")
|
||||
# -- scoreable/answerable simple answer_types: numerical_box / date / datetime
|
||||
answer_numerical_box = fields.Float('Correct numerical answer', help="Correct number answer for this question.")
|
||||
answer_date = fields.Date('Correct date answer', help="Correct date answer for this question.")
|
||||
answer_datetime = fields.Datetime('Correct datetime answer', help="Correct date and time answer for this question.")
|
||||
answer_score = fields.Float('Score', help="Score value for a correct answer to this question.")
|
||||
# -- char_box
|
||||
save_as_email = fields.Boolean(
|
||||
"Save as user email", compute='_compute_save_as_email', readonly=False, store=True, copy=True,
|
||||
help="If checked, this option will save the user's answer as its email address.")
|
||||
save_as_nickname = fields.Boolean(
|
||||
"Save as user nickname", compute='_compute_save_as_nickname', readonly=False, store=True, copy=True,
|
||||
help="If checked, this option will save the user's answer as its nickname.")
|
||||
# -- simple choice / multiple choice / matrix
|
||||
suggested_answer_ids = fields.One2many(
|
||||
'survey.question.answer', 'question_id', string='Types of answers', copy=True,
|
||||
help='Labels used for proposed choices: simple choice, multiple choice and columns of matrix')
|
||||
# -- matrix
|
||||
matrix_subtype = fields.Selection([
|
||||
('simple', 'One choice per row'),
|
||||
('multiple', 'Multiple choices per row')], string='Matrix Type', default='simple')
|
||||
matrix_row_ids = fields.One2many(
|
||||
'survey.question.answer', 'matrix_question_id', string='Matrix Rows', copy=True,
|
||||
help='Labels used for proposed choices: rows of matrix')
|
||||
# -- display & timing options
|
||||
is_time_limited = fields.Boolean("The question is limited in time",
|
||||
help="Currently only supported for live sessions.")
|
||||
time_limit = fields.Integer("Time limit (seconds)")
|
||||
# -- comments (simple choice, multiple choice, matrix (without count as an answer))
|
||||
comments_allowed = fields.Boolean('Show Comments Field')
|
||||
comments_message = fields.Char('Comment Message', translate=True)
|
||||
comment_count_as_answer = fields.Boolean('Comment is an answer')
|
||||
# question validation
|
||||
validation_required = fields.Boolean('Validate entry', compute='_compute_validation_required', readonly=False, store=True)
|
||||
validation_email = fields.Boolean('Input must be an email')
|
||||
validation_length_min = fields.Integer('Minimum Text Length', default=0)
|
||||
validation_length_max = fields.Integer('Maximum Text Length', default=0)
|
||||
validation_min_float_value = fields.Float('Minimum value', default=0.0)
|
||||
validation_max_float_value = fields.Float('Maximum value', default=0.0)
|
||||
validation_min_date = fields.Date('Minimum Date')
|
||||
validation_max_date = fields.Date('Maximum Date')
|
||||
validation_min_datetime = fields.Datetime('Minimum Datetime')
|
||||
validation_max_datetime = fields.Datetime('Maximum Datetime')
|
||||
validation_error_msg = fields.Char('Validation Error', translate=True)
|
||||
constr_mandatory = fields.Boolean('Mandatory Answer')
|
||||
constr_error_msg = fields.Char('Error message', translate=True)
|
||||
# answers
|
||||
user_input_line_ids = fields.One2many(
|
||||
'survey.user_input.line', 'question_id', string='Answers',
|
||||
domain=[('skipped', '=', False)], groups='survey.group_survey_user')
|
||||
|
||||
# Not stored, convenient for trigger display computation.
|
||||
triggering_question_ids = fields.Many2many(
|
||||
'survey.question', string="Triggering Questions", compute="_compute_triggering_question_ids",
|
||||
store=False, help="Questions containing the triggering answer(s) to display the current question.")
|
||||
|
||||
allowed_triggering_question_ids = fields.Many2many(
|
||||
'survey.question', string="Allowed Triggering Questions", copy=False, compute="_compute_allowed_triggering_question_ids")
|
||||
is_placed_before_trigger = fields.Boolean(
|
||||
string='Is misplaced?', help="Is this question placed before any of its trigger questions?",
|
||||
compute="_compute_allowed_triggering_question_ids")
|
||||
triggering_answer_ids = fields.Many2many(
|
||||
'survey.question.answer', string="Triggering Answers", copy=False, store=True,
|
||||
readonly=False, help="Picking any of these answers will trigger this question.\n"
|
||||
"Leave the field empty if the question should always be displayed.",
|
||||
domain="""[
|
||||
('question_id.survey_id', '=', survey_id),
|
||||
'&', ('question_id.question_type', 'in', ['simple_choice', 'multiple_choice']),
|
||||
'|',
|
||||
('question_id.sequence', '<', sequence),
|
||||
'&', ('question_id.sequence', '=', sequence), ('question_id.id', '<', id)
|
||||
]"""
|
||||
)
|
||||
|
||||
_sql_constraints = [
|
||||
('positive_len_min', 'CHECK (validation_length_min >= 0)', 'A length must be positive!'),
|
||||
('positive_len_max', 'CHECK (validation_length_max >= 0)', 'A length must be positive!'),
|
||||
('validation_length', 'CHECK (validation_length_min <= validation_length_max)', 'Max length cannot be smaller than min length!'),
|
||||
('validation_float', 'CHECK (validation_min_float_value <= validation_max_float_value)', 'Max value cannot be smaller than min value!'),
|
||||
('validation_date', 'CHECK (validation_min_date <= validation_max_date)', 'Max date cannot be smaller than min date!'),
|
||||
('validation_datetime', 'CHECK (validation_min_datetime <= validation_max_datetime)', 'Max datetime cannot be smaller than min datetime!'),
|
||||
('positive_answer_score', 'CHECK (answer_score >= 0)', 'An answer score for a non-multiple choice question cannot be negative!'),
|
||||
('scored_datetime_have_answers', "CHECK (is_scored_question != True OR question_type != 'datetime' OR answer_datetime is not null)",
|
||||
'All "Is a scored question = True" and "Question Type: Datetime" questions need an answer'),
|
||||
('scored_date_have_answers', "CHECK (is_scored_question != True OR question_type != 'date' OR answer_date is not null)",
|
||||
'All "Is a scored question = True" and "Question Type: Date" questions need an answer'),
|
||||
]
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# CONSTRAINT METHODS
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@api.constrains("is_page")
|
||||
def _check_question_type_for_pages(self):
|
||||
invalid_pages = self.filtered(lambda question: question.is_page and question.question_type)
|
||||
if invalid_pages:
|
||||
raise ValidationError(_("Question type should be empty for these pages: %s", ', '.join(invalid_pages.mapped('title'))))
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# COMPUTE METHODS
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@api.depends('question_type')
|
||||
def _compute_question_placeholder(self):
|
||||
for question in self:
|
||||
if question.question_type in ('simple_choice', 'multiple_choice', 'matrix') \
|
||||
or not question.question_placeholder: # avoid CacheMiss errors
|
||||
question.question_placeholder = False
|
||||
|
||||
@api.depends('is_page')
|
||||
def _compute_background_image(self):
|
||||
""" Background image is only available on sections. """
|
||||
for question in self.filtered(lambda q: not q.is_page):
|
||||
question.background_image = False
|
||||
|
||||
@api.depends('survey_id.access_token', 'background_image', 'page_id', 'survey_id.background_image_url')
|
||||
def _compute_background_image_url(self):
|
||||
""" How the background url is computed:
|
||||
- For a question: it depends on the related section (see below)
|
||||
- For a section:
|
||||
- if a section has a background, then we create the background URL using this section's ID
|
||||
- if not, then we fallback on the survey background url """
|
||||
base_bg_url = "/survey/%s/%s/get_background_image"
|
||||
for question in self:
|
||||
if question.is_page:
|
||||
background_section_id = question.id if question.background_image else False
|
||||
else:
|
||||
background_section_id = question.page_id.id if question.page_id.background_image else False
|
||||
|
||||
if background_section_id:
|
||||
question.background_image_url = base_bg_url % (
|
||||
question.survey_id.access_token,
|
||||
background_section_id
|
||||
)
|
||||
else:
|
||||
question.background_image_url = question.survey_id.background_image_url
|
||||
|
||||
@api.depends('is_page')
|
||||
def _compute_question_type(self):
|
||||
pages = self.filtered(lambda question: question.is_page)
|
||||
pages.question_type = False
|
||||
(self - pages).filtered(lambda question: not question.question_type).question_type = 'simple_choice'
|
||||
|
||||
@api.depends('survey_id.question_and_page_ids.is_page', 'survey_id.question_and_page_ids.sequence')
|
||||
def _compute_question_ids(self):
|
||||
"""Will take all questions of the survey for which the index is higher than the index of this page
|
||||
and lower than the index of the next page."""
|
||||
for question in self:
|
||||
if question.is_page:
|
||||
next_page_index = False
|
||||
for page in question.survey_id.page_ids:
|
||||
if page._index() > question._index():
|
||||
next_page_index = page._index()
|
||||
break
|
||||
|
||||
question.question_ids = question.survey_id.question_ids.filtered(
|
||||
lambda q: q._index() > question._index() and (not next_page_index or q._index() < next_page_index)
|
||||
)
|
||||
else:
|
||||
question.question_ids = self.env['survey.question']
|
||||
|
||||
@api.depends('survey_id.question_and_page_ids.is_page', 'survey_id.question_and_page_ids.sequence')
|
||||
def _compute_page_id(self):
|
||||
"""Will find the page to which this question belongs to by looking inside the corresponding survey"""
|
||||
for question in self:
|
||||
if question.is_page:
|
||||
question.page_id = None
|
||||
else:
|
||||
page = None
|
||||
for q in question.survey_id.question_and_page_ids.sorted():
|
||||
if q == question:
|
||||
break
|
||||
if q.is_page:
|
||||
page = q
|
||||
question.page_id = page
|
||||
|
||||
@api.depends('question_type', 'validation_email')
|
||||
def _compute_save_as_email(self):
|
||||
for question in self:
|
||||
if question.question_type != 'char_box' or not question.validation_email:
|
||||
question.save_as_email = False
|
||||
|
||||
@api.depends('question_type')
|
||||
def _compute_save_as_nickname(self):
|
||||
for question in self:
|
||||
if question.question_type != 'char_box':
|
||||
question.save_as_nickname = False
|
||||
|
||||
@api.depends('question_type')
|
||||
def _compute_validation_required(self):
|
||||
for question in self:
|
||||
if not question.validation_required or question.question_type not in ['char_box', 'numerical_box', 'date', 'datetime']:
|
||||
question.validation_required = False
|
||||
|
||||
@api.depends('survey_id', 'survey_id.question_ids', 'triggering_answer_ids')
|
||||
def _compute_allowed_triggering_question_ids(self):
|
||||
"""Although the question (and possible trigger questions) sequence
|
||||
is used here, we do not add these fields to the dependency list to
|
||||
avoid cascading rpc calls when reordering questions via the webclient.
|
||||
"""
|
||||
possible_trigger_questions = self.search([
|
||||
('is_page', '=', False),
|
||||
('question_type', 'in', ['simple_choice', 'multiple_choice']),
|
||||
('suggested_answer_ids', '!=', False),
|
||||
('survey_id', 'in', self.survey_id.ids)
|
||||
])
|
||||
# Using the sequence stored in db is necessary for existing questions that are passed as
|
||||
# NewIds because the sequence provided by the JS client can be incorrect.
|
||||
(self | possible_trigger_questions).flush_recordset()
|
||||
self.env.cr.execute(
|
||||
"SELECT id, sequence FROM survey_question WHERE id =ANY(%s)",
|
||||
[self.ids]
|
||||
)
|
||||
conditional_questions_sequences = dict(self.env.cr.fetchall()) # id: sequence mapping
|
||||
|
||||
for question in self:
|
||||
question_id = question._origin.id
|
||||
if not question_id: # New question
|
||||
question.allowed_triggering_question_ids = possible_trigger_questions.filtered(
|
||||
lambda q: q.survey_id.id == question.survey_id._origin.id)
|
||||
question.is_placed_before_trigger = False
|
||||
continue
|
||||
|
||||
question_sequence = conditional_questions_sequences[question_id]
|
||||
|
||||
question.allowed_triggering_question_ids = possible_trigger_questions.filtered(
|
||||
lambda q: q.survey_id.id == question.survey_id._origin.id
|
||||
and (q.sequence < question_sequence or q.sequence == question_sequence and q.id < question_id)
|
||||
)
|
||||
question.is_placed_before_trigger = bool(
|
||||
set(question.triggering_answer_ids.question_id.ids)
|
||||
- set(question.allowed_triggering_question_ids.ids) # .ids necessary to match ids with newIds
|
||||
)
|
||||
|
||||
@api.depends('triggering_answer_ids')
|
||||
def _compute_triggering_question_ids(self):
|
||||
for question in self:
|
||||
question.triggering_question_ids = question.triggering_answer_ids.question_id
|
||||
|
||||
@api.depends('question_type', 'scoring_type', 'answer_date', 'answer_datetime', 'answer_numerical_box', 'suggested_answer_ids.is_correct')
|
||||
def _compute_is_scored_question(self):
|
||||
""" Computes whether a question "is scored" or not. Handles following cases:
|
||||
- inconsistent Boolean=None edge case that breaks tests => False
|
||||
- survey is not scored => False
|
||||
- 'date'/'datetime'/'numerical_box' question types w/correct answer => True
|
||||
(implied without user having to activate, except for numerical whose correct value is 0.0)
|
||||
- 'simple_choice / multiple_choice': set to True if any of suggested answers are marked as correct
|
||||
- question_type isn't scoreable (note: choice questions scoring logic handled separately) => False
|
||||
"""
|
||||
for question in self:
|
||||
if question.is_scored_question is None or question.scoring_type == 'no_scoring':
|
||||
question.is_scored_question = False
|
||||
elif question.question_type == 'date':
|
||||
question.is_scored_question = bool(question.answer_date)
|
||||
elif question.question_type == 'datetime':
|
||||
question.is_scored_question = bool(question.answer_datetime)
|
||||
elif question.question_type == 'numerical_box' and question.answer_numerical_box:
|
||||
question.is_scored_question = True
|
||||
elif question.question_type in ['simple_choice', 'multiple_choice']:
|
||||
question.is_scored_question = any(question.suggested_answer_ids.mapped('is_correct'))
|
||||
else:
|
||||
question.is_scored_question = False
|
||||
|
||||
@api.onchange('question_type', 'validation_required')
|
||||
def _onchange_validation_parameters(self):
|
||||
"""Ensure no value stays set but not visible on form,
|
||||
preventing saving (+consistency with question type)."""
|
||||
self.validation_email = False
|
||||
self.validation_length_min = 0
|
||||
self.validation_length_max = 0
|
||||
self.validation_min_date = False
|
||||
self.validation_max_date = False
|
||||
self.validation_min_datetime = False
|
||||
self.validation_max_datetime = False
|
||||
self.validation_min_float_value = 0
|
||||
self.validation_max_float_value = 0
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# CRUD
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@api.returns('self', lambda value: value.id)
|
||||
def copy(self, default=None):
|
||||
self.ensure_one()
|
||||
clone = super().copy(default)
|
||||
if self.triggering_answer_ids:
|
||||
clone.triggering_answer_ids = self.triggering_answer_ids
|
||||
return clone
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# CRUD
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@api.ondelete(at_uninstall=False)
|
||||
def _unlink_except_live_sessions_in_progress(self):
|
||||
running_surveys = self.survey_id.filtered(lambda survey: survey.session_state == 'in_progress')
|
||||
if running_surveys:
|
||||
raise UserError(_(
|
||||
'You cannot delete questions from surveys "%(survey_names)s" while live sessions are in progress.',
|
||||
survey_names=', '.join(running_surveys.mapped('title')),
|
||||
))
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# VALIDATION
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def validate_question(self, answer, comment=None):
|
||||
""" Validate question, depending on question type and parameters
|
||||
for simple choice, text, date and number, answer is simply the answer of the question.
|
||||
For other multiple choices questions, answer is a list of answers (the selected choices
|
||||
or a list of selected answers per question -for matrix type-):
|
||||
- Simple answer : answer = 'example' or 2 or question_answer_id or 2019/10/10
|
||||
- Multiple choice : answer = [question_answer_id1, question_answer_id2, question_answer_id3]
|
||||
- Matrix: answer = { 'rowId1' : [colId1, colId2,...], 'rowId2' : [colId1, colId3, ...] }
|
||||
|
||||
return dict {question.id (int): error (str)} -> empty dict if no validation error.
|
||||
"""
|
||||
self.ensure_one()
|
||||
if isinstance(answer, str):
|
||||
answer = answer.strip()
|
||||
# Empty answer to mandatory question
|
||||
# because in choices question types, comment can count as answer
|
||||
if not answer and self.question_type not in ['simple_choice', 'multiple_choice']:
|
||||
if self.constr_mandatory and not self.survey_id.users_can_go_back:
|
||||
return {self.id: self.constr_error_msg or _('This question requires an answer.')}
|
||||
else:
|
||||
if self.question_type == 'char_box':
|
||||
return self._validate_char_box(answer)
|
||||
elif self.question_type == 'numerical_box':
|
||||
return self._validate_numerical_box(answer)
|
||||
elif self.question_type in ['date', 'datetime']:
|
||||
return self._validate_date(answer)
|
||||
elif self.question_type in ['simple_choice', 'multiple_choice']:
|
||||
return self._validate_choice(answer, comment)
|
||||
elif self.question_type == 'matrix':
|
||||
return self._validate_matrix(answer)
|
||||
return {}
|
||||
|
||||
def _validate_char_box(self, answer):
|
||||
# Email format validation
|
||||
# all the strings of the form "<something>@<anything>.<extension>" will be accepted
|
||||
if self.validation_email:
|
||||
if not tools.email_normalize(answer):
|
||||
return {self.id: _('This answer must be an email address')}
|
||||
|
||||
# Answer validation (if properly defined)
|
||||
# Length of the answer must be in a range
|
||||
if self.validation_required:
|
||||
if not (self.validation_length_min <= len(answer) <= self.validation_length_max):
|
||||
return {self.id: self.validation_error_msg or _('The answer you entered is not valid.')}
|
||||
return {}
|
||||
|
||||
def _validate_numerical_box(self, answer):
|
||||
try:
|
||||
floatanswer = float(answer)
|
||||
except ValueError:
|
||||
return {self.id: _('This is not a number')}
|
||||
|
||||
if self.validation_required:
|
||||
# Answer is not in the right range
|
||||
with contextlib.suppress(Exception):
|
||||
if not (self.validation_min_float_value <= floatanswer <= self.validation_max_float_value):
|
||||
return {self.id: self.validation_error_msg or _('The answer you entered is not valid.')}
|
||||
return {}
|
||||
|
||||
def _validate_date(self, answer):
|
||||
isDatetime = self.question_type == 'datetime'
|
||||
# Checks if user input is a date
|
||||
try:
|
||||
dateanswer = fields.Datetime.from_string(answer) if isDatetime else fields.Date.from_string(answer)
|
||||
except ValueError:
|
||||
return {self.id: _('This is not a date')}
|
||||
if self.validation_required:
|
||||
# Check if answer is in the right range
|
||||
if isDatetime:
|
||||
min_date = fields.Datetime.from_string(self.validation_min_datetime)
|
||||
max_date = fields.Datetime.from_string(self.validation_max_datetime)
|
||||
dateanswer = fields.Datetime.from_string(answer)
|
||||
else:
|
||||
min_date = fields.Date.from_string(self.validation_min_date)
|
||||
max_date = fields.Date.from_string(self.validation_max_date)
|
||||
dateanswer = fields.Date.from_string(answer)
|
||||
|
||||
if (min_date and max_date and not (min_date <= dateanswer <= max_date))\
|
||||
or (min_date and not min_date <= dateanswer)\
|
||||
or (max_date and not dateanswer <= max_date):
|
||||
return {self.id: self.validation_error_msg or _('The answer you entered is not valid.')}
|
||||
return {}
|
||||
|
||||
def _validate_choice(self, answer, comment):
|
||||
# Empty comment
|
||||
if not self.survey_id.users_can_go_back \
|
||||
and self.constr_mandatory \
|
||||
and not answer \
|
||||
and not (self.comments_allowed and self.comment_count_as_answer and comment):
|
||||
return {self.id: self.constr_error_msg or _('This question requires an answer.')}
|
||||
return {}
|
||||
|
||||
def _validate_matrix(self, answers):
|
||||
# Validate that each line has been answered
|
||||
if self.constr_mandatory and len(self.matrix_row_ids) != len(answers):
|
||||
return {self.id: self.constr_error_msg or _('This question requires an answer.')}
|
||||
return {}
|
||||
|
||||
def _index(self):
|
||||
"""We would normally just use the 'sequence' field of questions BUT, if the pages and questions are
|
||||
created without ever moving records around, the sequence field can be set to 0 for all the questions.
|
||||
|
||||
However, the order of the recordset is always correct so we can rely on the index method."""
|
||||
self.ensure_one()
|
||||
return list(self.survey_id.question_and_page_ids).index(self)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# STATISTICS / REPORTING
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def _prepare_statistics(self, user_input_lines):
|
||||
""" Compute statistical data for questions by counting number of vote per choice on basis of filter """
|
||||
all_questions_data = []
|
||||
for question in self:
|
||||
question_data = {'question': question, 'is_page': question.is_page}
|
||||
|
||||
if question.is_page:
|
||||
all_questions_data.append(question_data)
|
||||
continue
|
||||
|
||||
# fetch answer lines, separate comments from real answers
|
||||
all_lines = user_input_lines.filtered(lambda line: line.question_id == question)
|
||||
if question.question_type in ['simple_choice', 'multiple_choice', 'matrix']:
|
||||
answer_lines = all_lines.filtered(
|
||||
lambda line: line.answer_type == 'suggestion' or (
|
||||
line.skipped and not line.answer_type) or (
|
||||
line.answer_type == 'char_box' and question.comment_count_as_answer)
|
||||
)
|
||||
comment_line_ids = all_lines.filtered(lambda line: line.answer_type == 'char_box')
|
||||
else:
|
||||
answer_lines = all_lines
|
||||
comment_line_ids = self.env['survey.user_input.line']
|
||||
skipped_lines = answer_lines.filtered(lambda line: line.skipped)
|
||||
done_lines = answer_lines - skipped_lines
|
||||
question_data.update(
|
||||
answer_line_ids=answer_lines,
|
||||
answer_line_done_ids=done_lines,
|
||||
answer_input_done_ids=done_lines.mapped('user_input_id'),
|
||||
answer_input_skipped_ids=skipped_lines.mapped('user_input_id'),
|
||||
comment_line_ids=comment_line_ids)
|
||||
question_data.update(question._get_stats_summary_data(answer_lines))
|
||||
|
||||
# prepare table and graph data
|
||||
table_data, graph_data = question._get_stats_data(answer_lines)
|
||||
question_data['table_data'] = table_data
|
||||
question_data['graph_data'] = json.dumps(graph_data)
|
||||
|
||||
all_questions_data.append(question_data)
|
||||
return all_questions_data
|
||||
|
||||
def _get_stats_data(self, user_input_lines):
|
||||
if self.question_type == 'simple_choice':
|
||||
return self._get_stats_data_answers(user_input_lines)
|
||||
elif self.question_type == 'multiple_choice':
|
||||
table_data, graph_data = self._get_stats_data_answers(user_input_lines)
|
||||
return table_data, [{'key': self.title, 'values': graph_data}]
|
||||
elif self.question_type == 'matrix':
|
||||
return self._get_stats_graph_data_matrix(user_input_lines)
|
||||
return [line for line in user_input_lines], []
|
||||
|
||||
def _get_stats_data_answers(self, user_input_lines):
|
||||
""" Statistics for question.answer based questions (simple choice, multiple
|
||||
choice.). A corner case with a void record survey.question.answer is added
|
||||
to count comments that should be considered as valid answers. This small hack
|
||||
allow to have everything available in the same standard structure. """
|
||||
suggested_answers = [answer for answer in self.mapped('suggested_answer_ids')]
|
||||
if self.comment_count_as_answer:
|
||||
suggested_answers += [self.env['survey.question.answer']]
|
||||
|
||||
count_data = dict.fromkeys(suggested_answers, 0)
|
||||
for line in user_input_lines:
|
||||
if line.suggested_answer_id in count_data\
|
||||
or (line.value_char_box and self.comment_count_as_answer):
|
||||
count_data[line.suggested_answer_id] += 1
|
||||
|
||||
table_data = [{
|
||||
'value': _('Other (see comments)') if not sug_answer else sug_answer.value,
|
||||
'suggested_answer': sug_answer,
|
||||
'count': count_data[sug_answer]
|
||||
}
|
||||
for sug_answer in suggested_answers]
|
||||
graph_data = [{
|
||||
'text': _('Other (see comments)') if not sug_answer else sug_answer.value,
|
||||
'count': count_data[sug_answer]
|
||||
}
|
||||
for sug_answer in suggested_answers]
|
||||
|
||||
return table_data, graph_data
|
||||
|
||||
def _get_stats_graph_data_matrix(self, user_input_lines):
|
||||
suggested_answers = self.mapped('suggested_answer_ids')
|
||||
matrix_rows = self.mapped('matrix_row_ids')
|
||||
|
||||
count_data = dict.fromkeys(itertools.product(matrix_rows, suggested_answers), 0)
|
||||
for line in user_input_lines:
|
||||
if line.matrix_row_id and line.suggested_answer_id:
|
||||
count_data[(line.matrix_row_id, line.suggested_answer_id)] += 1
|
||||
|
||||
table_data = [{
|
||||
'row': row,
|
||||
'columns': [{
|
||||
'suggested_answer': sug_answer,
|
||||
'count': count_data[(row, sug_answer)]
|
||||
} for sug_answer in suggested_answers],
|
||||
} for row in matrix_rows]
|
||||
graph_data = [{
|
||||
'key': sug_answer.value,
|
||||
'values': [{
|
||||
'text': row.value,
|
||||
'count': count_data[(row, sug_answer)]
|
||||
}
|
||||
for row in matrix_rows
|
||||
]
|
||||
} for sug_answer in suggested_answers]
|
||||
|
||||
return table_data, graph_data
|
||||
|
||||
def _get_stats_summary_data(self, user_input_lines):
|
||||
stats = {}
|
||||
if self.question_type in ['simple_choice', 'multiple_choice']:
|
||||
stats.update(self._get_stats_summary_data_choice(user_input_lines))
|
||||
elif self.question_type == 'numerical_box':
|
||||
stats.update(self._get_stats_summary_data_numerical(user_input_lines))
|
||||
|
||||
if self.question_type in ['numerical_box', 'date', 'datetime']:
|
||||
stats.update(self._get_stats_summary_data_scored(user_input_lines))
|
||||
return stats
|
||||
|
||||
def _get_stats_summary_data_choice(self, user_input_lines):
|
||||
right_inputs, partial_inputs = self.env['survey.user_input'], self.env['survey.user_input']
|
||||
right_answers = self.suggested_answer_ids.filtered(lambda label: label.is_correct)
|
||||
if self.question_type == 'multiple_choice':
|
||||
for user_input, lines in tools.groupby(user_input_lines, operator.itemgetter('user_input_id')):
|
||||
user_input_answers = self.env['survey.user_input.line'].concat(*lines).filtered(lambda l: l.answer_is_correct).mapped('suggested_answer_id')
|
||||
if user_input_answers and user_input_answers < right_answers:
|
||||
partial_inputs += user_input
|
||||
elif user_input_answers:
|
||||
right_inputs += user_input
|
||||
else:
|
||||
right_inputs = user_input_lines.filtered(lambda line: line.answer_is_correct).mapped('user_input_id')
|
||||
return {
|
||||
'right_answers': right_answers,
|
||||
'right_inputs_count': len(right_inputs),
|
||||
'partial_inputs_count': len(partial_inputs),
|
||||
}
|
||||
|
||||
def _get_stats_summary_data_numerical(self, user_input_lines):
|
||||
all_values = user_input_lines.filtered(lambda line: not line.skipped).mapped('value_numerical_box')
|
||||
lines_sum = sum(all_values)
|
||||
return {
|
||||
'numerical_max': max(all_values, default=0),
|
||||
'numerical_min': min(all_values, default=0),
|
||||
'numerical_average': round(lines_sum / (len(all_values) or 1), 2),
|
||||
}
|
||||
|
||||
def _get_stats_summary_data_scored(self, user_input_lines):
|
||||
return {
|
||||
'common_lines': collections.Counter(
|
||||
user_input_lines.filtered(lambda line: not line.skipped).mapped('value_%s' % self.question_type)
|
||||
).most_common(5),
|
||||
'right_inputs_count': len(user_input_lines.filtered(lambda line: line.answer_is_correct).mapped('user_input_id'))
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# OTHERS
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def _get_correct_answers(self):
|
||||
""" Return a dictionary linking the scorable question ids to their correct answers.
|
||||
The questions without correct answers are not considered.
|
||||
"""
|
||||
correct_answers = {}
|
||||
|
||||
# Simple and multiple choice
|
||||
choices_questions = self.filtered(lambda q: q.question_type in ['simple_choice', 'multiple_choice'])
|
||||
if choices_questions:
|
||||
suggested_answers_data = self.env['survey.question.answer'].search_read(
|
||||
[('question_id', 'in', choices_questions.ids), ('is_correct', '=', True)],
|
||||
['question_id', 'id'],
|
||||
load='', # prevent computing display_names
|
||||
)
|
||||
for data in suggested_answers_data:
|
||||
if not data.get('id'):
|
||||
continue
|
||||
correct_answers.setdefault(data['question_id'], []).append(data['id'])
|
||||
|
||||
# Numerical box, date, datetime
|
||||
for question in self - choices_questions:
|
||||
if question.question_type not in ['numerical_box', 'date', 'datetime']:
|
||||
continue
|
||||
answer = question[f'answer_{question.question_type}']
|
||||
if question.question_type == 'date':
|
||||
answer = tools.format_date(self.env, answer)
|
||||
elif question.question_type == 'datetime':
|
||||
answer = tools.format_datetime(self.env, answer, tz='UTC', dt_format=False)
|
||||
correct_answers[question.id] = answer
|
||||
|
||||
return correct_answers
|
||||
|
||||
class SurveyQuestionAnswer(models.Model):
|
||||
""" A preconfigured answer for a question. This model stores values used
|
||||
for
|
||||
|
||||
* simple choice, multiple choice: proposed values for the selection /
|
||||
radio;
|
||||
* matrix: row and column values;
|
||||
|
||||
"""
|
||||
_name = 'survey.question.answer'
|
||||
_rec_name = 'value'
|
||||
_rec_names_search = ['question_id.title', 'value']
|
||||
_order = 'question_id, sequence, id'
|
||||
_description = 'Survey Label'
|
||||
|
||||
MAX_ANSWER_NAME_LENGTH = 90 # empirically tested in client dropdown
|
||||
|
||||
# question and question related fields
|
||||
question_id = fields.Many2one('survey.question', string='Question', ondelete='cascade')
|
||||
matrix_question_id = fields.Many2one('survey.question', string='Question (as matrix row)', ondelete='cascade')
|
||||
question_type = fields.Selection(related='question_id.question_type')
|
||||
sequence = fields.Integer('Label Sequence order', default=10)
|
||||
scoring_type = fields.Selection(related='question_id.scoring_type')
|
||||
# answer related fields
|
||||
value = fields.Char('Suggested value', translate=True, required=True)
|
||||
value_image = fields.Image('Image', max_width=1024, max_height=1024)
|
||||
value_image_filename = fields.Char('Image Filename')
|
||||
is_correct = fields.Boolean('Correct')
|
||||
answer_score = fields.Float('Score', help="A positive score indicates a correct choice; a negative or null score indicates a wrong answer")
|
||||
|
||||
@api.depends('value', 'question_id.question_type', 'question_id.title', 'matrix_question_id')
|
||||
def _compute_display_name(self):
|
||||
"""Render an answer name as "Question title : Answer value", making sure it is not too long.
|
||||
|
||||
Unless the answer is part of a matrix-type question, this implementation makes sure we have
|
||||
at least 30 characters for the question title, then we elide it, leaving the rest of the
|
||||
space for the answer.
|
||||
"""
|
||||
for answer in self:
|
||||
if not answer.question_id or answer.question_id.question_type == 'matrix':
|
||||
answer.display_name = answer.value
|
||||
continue
|
||||
title = answer.question_id.title or _("[Question Title]")
|
||||
n_extra_characters = len(title) + len(answer.value) + 3 - self.MAX_ANSWER_NAME_LENGTH # 3 for `" : "`
|
||||
if n_extra_characters <= 0:
|
||||
answer.display_name = f'{title} : {answer.value}'
|
||||
else:
|
||||
answer.display_name = shorten(
|
||||
f'{shorten(title, max(30, len(title) - n_extra_characters), placeholder="...")} : {answer.value}',
|
||||
self.MAX_ANSWER_NAME_LENGTH,
|
||||
placeholder="..."
|
||||
)
|
||||
|
||||
@api.constrains('question_id', 'matrix_question_id')
|
||||
def _check_question_not_empty(self):
|
||||
"""Ensure that field question_id XOR field matrix_question_id is not null"""
|
||||
for label in self:
|
||||
if not bool(label.question_id) != bool(label.matrix_question_id):
|
||||
raise ValidationError(_("A label must be attached to only one question."))
|
||||
|
||||
def _get_answer_matching_domain(self, row_id=False):
|
||||
self.ensure_one()
|
||||
if self.question_type == "matrix":
|
||||
return ['&', '&', ('question_id', '=', self.question_id.id), ('matrix_row_id', '=', row_id), ('suggested_answer_id', '=', self.id)]
|
||||
elif self.question_type in ('multiple_choice', 'simple_choice'):
|
||||
return ['&', ('question_id', '=', self.question_id.id), ('suggested_answer_id', '=', self.id)]
|
||||
return []
|
1217
models/survey_survey.py
Normal file
266
models/survey_survey_template.py
Normal file
|
@ -0,0 +1,266 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import ast
|
||||
|
||||
from odoo import api, models, _
|
||||
|
||||
|
||||
class SurveyTemplate(models.Model):
|
||||
"""This model defines additional actions on the 'survey.survey' model that
|
||||
can be used to load a survey sample. The model defines a sample for each
|
||||
survey type:
|
||||
(1) survey: A feedback form
|
||||
(2) assessment: A certification
|
||||
(3) live_session: A live presentation
|
||||
(4) custom: An empty survey
|
||||
"""
|
||||
|
||||
_inherit = 'survey.survey'
|
||||
|
||||
@api.model
|
||||
def action_load_sample_survey(self):
|
||||
return self.env['survey.survey'].create({
|
||||
'survey_type': 'survey',
|
||||
'title': _('Feedback Form'),
|
||||
'description': '<br>'.join([
|
||||
_('Please complete this very short survey to let us know how satisfied your are with our products.'),
|
||||
_('Your responses will help us improve our product range to serve you even better.')
|
||||
]),
|
||||
'description_done': _('Thank you very much for your feedback. We highly value your opinion!'),
|
||||
'progression_mode': 'number',
|
||||
'questions_layout': 'page_per_question',
|
||||
'question_and_page_ids': [
|
||||
(0, 0, { # survey.question
|
||||
'title': _('How frequently do you use our products?'),
|
||||
'question_type': 'simple_choice',
|
||||
'constr_mandatory': True,
|
||||
'suggested_answer_ids': [
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('Often (1-3 times per week)')
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('Rarely (1-3 times per month)')
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('Never (less than once a month)')
|
||||
})
|
||||
]
|
||||
}),
|
||||
(0, 0, { # survey.question
|
||||
'title': _('How many orders did you pass during the last 6 months?'),
|
||||
'question_type': 'numerical_box',
|
||||
}),
|
||||
(0, 0, { # survey.question
|
||||
'title': _('How likely are you to recommend the following products to a friend?'),
|
||||
'question_type': 'matrix',
|
||||
'matrix_subtype': 'simple',
|
||||
'suggested_answer_ids': [
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('Unlikely')
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('Neutral')
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('Likely')
|
||||
}),
|
||||
],
|
||||
'matrix_row_ids': [
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('Red Pen')
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('Blue Pen')
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('Yellow Pen')
|
||||
})
|
||||
]
|
||||
})
|
||||
]
|
||||
}).action_show_sample()
|
||||
|
||||
@api.model
|
||||
def action_load_sample_assessment(self):
|
||||
survey_values = {
|
||||
'survey_type': 'assessment',
|
||||
'title': _('Certification'),
|
||||
'certification': True,
|
||||
'access_mode': 'token',
|
||||
'is_time_limited': True,
|
||||
'time_limit': 15, # 15 minutes
|
||||
'is_attempts_limited': True,
|
||||
'attempts_limit': 1,
|
||||
'progression_mode': 'number',
|
||||
'scoring_type': 'scoring_without_answers',
|
||||
'users_can_go_back': True,
|
||||
'description': ''.join([
|
||||
_('Welcome to this Odoo certification. You will receive 2 random questions out of a pool of 3.'),
|
||||
'(<span style="font-style: italic">',
|
||||
_('Cheating on your neighbors will not help!'),
|
||||
'</span> 😁).<br>',
|
||||
_('Good luck!')
|
||||
]),
|
||||
'description_done': _('Thank you. We will contact you soon.'),
|
||||
'questions_layout': 'page_per_section',
|
||||
'questions_selection': 'random',
|
||||
'question_and_page_ids': [
|
||||
(0, 0, { # survey.question
|
||||
'title': _('Odoo Certification'),
|
||||
'is_page': True,
|
||||
'question_type': False,
|
||||
'random_questions_count': 2
|
||||
}),
|
||||
(0, 0, { # survey.question
|
||||
'title': _('What does "ODOO" stand for?'),
|
||||
'question_type': 'simple_choice',
|
||||
'suggested_answer_ids': [
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('It\'s a Belgian word for "Management"')
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('Object-Directed Open Organization')
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('Organizational Development for Operation Officers')
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('It does not mean anything specific'),
|
||||
'is_correct': True,
|
||||
'answer_score': 10
|
||||
}),
|
||||
]
|
||||
}),
|
||||
(0, 0, { # survey.question
|
||||
'title': _('On Survey questions, one can define "placeholders". But what are they for?'),
|
||||
'question_type': 'simple_choice',
|
||||
'suggested_answer_ids': [
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('They are a default answer, used if the participant skips the question')
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('It is a small bit of text, displayed to help participants answer'),
|
||||
'is_correct': True,
|
||||
'answer_score': 10
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('They are technical parameters that guarantees the responsiveness of the page')
|
||||
})
|
||||
]
|
||||
}),
|
||||
(0, 0, { # survey.question
|
||||
'title': _('What does one need to get to pass an Odoo Survey?'),
|
||||
'question_type': 'simple_choice',
|
||||
'suggested_answer_ids': [
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('It is an option that can be different for each Survey'),
|
||||
'is_correct': True,
|
||||
'answer_score': 10
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('One needs to get 50% of the total score')
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('One needs to answer at least half the questions correctly')
|
||||
})
|
||||
]
|
||||
}),
|
||||
]
|
||||
}
|
||||
mail_template = self.env.ref('survey.mail_template_certification', raise_if_not_found=False)
|
||||
if mail_template:
|
||||
survey_values.update({
|
||||
'certification_mail_template_id': mail_template.id
|
||||
})
|
||||
return self.env['survey.survey'].create(survey_values).action_show_sample()
|
||||
|
||||
@api.model
|
||||
def action_load_sample_live_session(self):
|
||||
return self.env['survey.survey'].create({
|
||||
'survey_type': 'live_session',
|
||||
'title': _('Live Session'),
|
||||
'description': '<br>'.join([
|
||||
_('How good of a presenter are you? Let\'s find out!'),
|
||||
_('But first, keep listening to the host.')
|
||||
]),
|
||||
'description_done': _('Thank you for your participation, hope you had a blast!'),
|
||||
'progression_mode': 'number',
|
||||
'scoring_type': 'scoring_with_answers',
|
||||
'questions_layout': 'page_per_question',
|
||||
'session_speed_rating': True,
|
||||
'question_and_page_ids': [
|
||||
(0, 0, { # survey.question
|
||||
'title': _('What is the best way to catch the attention of an audience?'),
|
||||
'question_type': 'simple_choice',
|
||||
'suggested_answer_ids': [
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('Speak softly so that they need to focus to hear you')
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('Use a fun visual support, like a live presentation'),
|
||||
'is_correct': True,
|
||||
'answer_score': 20
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('Show them slides with a ton of text they need to read fast')
|
||||
})
|
||||
]
|
||||
}),
|
||||
(0, 0, { # survey.question
|
||||
'title': _('What is a frequent mistake public speakers do?'),
|
||||
'question_type': 'simple_choice',
|
||||
'suggested_answer_ids': [
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('Practice in front of a mirror')
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('Speak too fast'),
|
||||
'is_correct': True,
|
||||
'answer_score': 20
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('Use humor and make jokes')
|
||||
})
|
||||
]
|
||||
}),
|
||||
(0, 0, { # survey.question
|
||||
'title': _('Why should you consider making your presentation more fun with a small quiz?'),
|
||||
'question_type': 'multiple_choice',
|
||||
'suggested_answer_ids': [
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('It helps attendees focus on what you are saying'),
|
||||
'is_correct': True,
|
||||
'answer_score': 20
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('It is more engaging for your audience'),
|
||||
'is_correct': True,
|
||||
'answer_score': 20
|
||||
}),
|
||||
(0, 0, { # survey.question.answer
|
||||
'value': _('It helps attendees remember the content of your presentation'),
|
||||
'is_correct': True,
|
||||
'answer_score': 20
|
||||
})
|
||||
]
|
||||
}),
|
||||
|
||||
]
|
||||
}).action_show_sample()
|
||||
|
||||
@api.model
|
||||
def action_load_sample_custom(self):
|
||||
return self.env['survey.survey'].create({
|
||||
'survey_type': 'custom',
|
||||
'title': '',
|
||||
}).action_show_sample()
|
||||
|
||||
def action_show_sample(self):
|
||||
action = self.env['ir.actions.act_window']._for_xml_id('survey.action_survey_form')
|
||||
action['views'] = [[self.env.ref('survey.survey_survey_view_form').id, 'form']]
|
||||
action['res_id'] = self.id
|
||||
action['context'] = dict(ast.literal_eval(action.get('context', {})),
|
||||
create=False
|
||||
)
|
||||
return action
|
914
models/survey_user_input.py
Normal file
|
@ -0,0 +1,914 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
import textwrap
|
||||
import uuid
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError, UserError
|
||||
from odoo.tools import float_is_zero
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SurveyUserInput(models.Model):
|
||||
""" Metadata for a set of one user's answers to a particular survey """
|
||||
_name = "survey.user_input"
|
||||
_description = "Survey User Input"
|
||||
_rec_name = "survey_id"
|
||||
_order = "create_date desc"
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
|
||||
# answer description
|
||||
survey_id = fields.Many2one('survey.survey', string='Survey', required=True, readonly=True, ondelete='cascade')
|
||||
scoring_type = fields.Selection(string="Scoring", related="survey_id.scoring_type")
|
||||
start_datetime = fields.Datetime('Start date and time', readonly=True)
|
||||
end_datetime = fields.Datetime('End date and time', readonly=True)
|
||||
deadline = fields.Datetime('Deadline', help="Datetime until customer can open the survey and submit answers")
|
||||
state = fields.Selection([
|
||||
('new', 'Not started yet'),
|
||||
('in_progress', 'In Progress'),
|
||||
('done', 'Completed')], string='Status', default='new', readonly=True)
|
||||
test_entry = fields.Boolean(readonly=True)
|
||||
last_displayed_page_id = fields.Many2one('survey.question', string='Last displayed question/page')
|
||||
# attempts management
|
||||
is_attempts_limited = fields.Boolean("Limited number of attempts", related='survey_id.is_attempts_limited')
|
||||
attempts_limit = fields.Integer("Number of attempts", related='survey_id.attempts_limit')
|
||||
attempts_count = fields.Integer("Attempts Count", compute='_compute_attempts_info')
|
||||
attempts_number = fields.Integer("Attempt n°", compute='_compute_attempts_info')
|
||||
survey_time_limit_reached = fields.Boolean("Survey Time Limit Reached", compute='_compute_survey_time_limit_reached')
|
||||
# identification / access
|
||||
access_token = fields.Char('Identification token', default=lambda self: str(uuid.uuid4()), readonly=True, required=True, copy=False)
|
||||
invite_token = fields.Char('Invite token', readonly=True, copy=False) # no unique constraint, as it identifies a pool of attempts
|
||||
partner_id = fields.Many2one('res.partner', string='Contact', readonly=True)
|
||||
email = fields.Char('Email', readonly=True)
|
||||
nickname = fields.Char('Nickname', help="Attendee nickname, mainly used to identify them in the survey session leaderboard.")
|
||||
# questions / answers
|
||||
user_input_line_ids = fields.One2many('survey.user_input.line', 'user_input_id', string='Answers', copy=True)
|
||||
predefined_question_ids = fields.Many2many('survey.question', string='Predefined Questions', readonly=True)
|
||||
scoring_percentage = fields.Float("Score (%)", compute="_compute_scoring_values", store=True, compute_sudo=True) # stored for perf reasons
|
||||
scoring_total = fields.Float("Total Score", compute="_compute_scoring_values", store=True, compute_sudo=True) # stored for perf reasons
|
||||
scoring_success = fields.Boolean('Quizz Passed', compute='_compute_scoring_success', store=True, compute_sudo=True) # stored for perf reasons
|
||||
survey_first_submitted = fields.Boolean(string='Survey First Submitted')
|
||||
# live sessions
|
||||
is_session_answer = fields.Boolean('Is in a Session', help="Is that user input part of a survey session or not.")
|
||||
question_time_limit_reached = fields.Boolean("Question Time Limit Reached", compute='_compute_question_time_limit_reached')
|
||||
|
||||
_sql_constraints = [
|
||||
('unique_token', 'UNIQUE (access_token)', 'An access token must be unique!'),
|
||||
]
|
||||
|
||||
@api.depends('user_input_line_ids.answer_score', 'user_input_line_ids.question_id', 'predefined_question_ids.answer_score')
|
||||
def _compute_scoring_values(self):
|
||||
for user_input in self:
|
||||
# sum(multi-choice question scores) + sum(simple answer_type scores)
|
||||
total_possible_score = 0
|
||||
for question in user_input.predefined_question_ids:
|
||||
if question.question_type == 'simple_choice':
|
||||
total_possible_score += max([score for score in question.mapped('suggested_answer_ids.answer_score') if score > 0], default=0)
|
||||
elif question.question_type == 'multiple_choice':
|
||||
total_possible_score += sum(score for score in question.mapped('suggested_answer_ids.answer_score') if score > 0)
|
||||
elif question.is_scored_question:
|
||||
total_possible_score += question.answer_score
|
||||
|
||||
if total_possible_score == 0:
|
||||
user_input.scoring_percentage = 0
|
||||
user_input.scoring_total = 0
|
||||
else:
|
||||
score_total = sum(user_input.user_input_line_ids.mapped('answer_score'))
|
||||
user_input.scoring_total = score_total
|
||||
score_percentage = (score_total / total_possible_score) * 100
|
||||
user_input.scoring_percentage = round(score_percentage, 2) if score_percentage > 0 else 0
|
||||
|
||||
@api.depends('scoring_percentage', 'survey_id')
|
||||
def _compute_scoring_success(self):
|
||||
for user_input in self:
|
||||
user_input.scoring_success = user_input.scoring_percentage >= user_input.survey_id.scoring_success_min
|
||||
|
||||
@api.depends(
|
||||
'start_datetime',
|
||||
'survey_id.is_time_limited',
|
||||
'survey_id.time_limit')
|
||||
def _compute_survey_time_limit_reached(self):
|
||||
""" Checks that the user_input is not exceeding the survey's time limit. """
|
||||
for user_input in self:
|
||||
if not user_input.is_session_answer and user_input.start_datetime:
|
||||
start_time = user_input.start_datetime
|
||||
time_limit = user_input.survey_id.time_limit
|
||||
user_input.survey_time_limit_reached = user_input.survey_id.is_time_limited and \
|
||||
fields.Datetime.now() >= start_time + relativedelta(minutes=time_limit)
|
||||
else:
|
||||
user_input.survey_time_limit_reached = False
|
||||
|
||||
@api.depends(
|
||||
'survey_id.session_question_id.time_limit',
|
||||
'survey_id.session_question_id.is_time_limited',
|
||||
'survey_id.session_question_start_time')
|
||||
def _compute_question_time_limit_reached(self):
|
||||
""" Checks that the user_input is not exceeding the question's time limit.
|
||||
Only used in the context of survey sessions. """
|
||||
for user_input in self:
|
||||
if user_input.is_session_answer and user_input.survey_id.session_question_start_time:
|
||||
start_time = user_input.survey_id.session_question_start_time
|
||||
time_limit = user_input.survey_id.session_question_id.time_limit
|
||||
user_input.question_time_limit_reached = user_input.survey_id.session_question_id.is_time_limited and \
|
||||
fields.Datetime.now() >= start_time + relativedelta(seconds=time_limit)
|
||||
else:
|
||||
user_input.question_time_limit_reached = False
|
||||
|
||||
@api.depends('state', 'test_entry', 'survey_id.is_attempts_limited', 'partner_id', 'email', 'invite_token')
|
||||
def _compute_attempts_info(self):
|
||||
attempts_to_compute = self.filtered(
|
||||
lambda user_input: user_input.state == 'done' and not user_input.test_entry and user_input.survey_id.is_attempts_limited
|
||||
)
|
||||
|
||||
for user_input in (self - attempts_to_compute):
|
||||
user_input.attempts_count = 1
|
||||
user_input.attempts_number = 1
|
||||
|
||||
if attempts_to_compute:
|
||||
self.flush_model(['email', 'invite_token', 'partner_id', 'state', 'survey_id', 'test_entry'])
|
||||
|
||||
self.env.cr.execute("""
|
||||
SELECT user_input.id,
|
||||
COUNT(all_attempts_user_input.id) AS attempts_count,
|
||||
COUNT(CASE WHEN all_attempts_user_input.id < user_input.id THEN all_attempts_user_input.id END) + 1 AS attempts_number
|
||||
FROM survey_user_input user_input
|
||||
LEFT OUTER JOIN survey_user_input all_attempts_user_input
|
||||
ON user_input.survey_id = all_attempts_user_input.survey_id
|
||||
AND all_attempts_user_input.state = 'done'
|
||||
AND all_attempts_user_input.test_entry IS NOT TRUE
|
||||
AND (user_input.invite_token IS NULL OR user_input.invite_token = all_attempts_user_input.invite_token)
|
||||
AND (user_input.partner_id = all_attempts_user_input.partner_id OR user_input.email = all_attempts_user_input.email)
|
||||
WHERE user_input.id IN %s
|
||||
GROUP BY user_input.id;
|
||||
""", (tuple(attempts_to_compute.ids),))
|
||||
|
||||
attempts_number_results = self.env.cr.dictfetchall()
|
||||
|
||||
attempts_number_results = {
|
||||
attempts_number_result['id']: {
|
||||
'attempts_number': attempts_number_result['attempts_number'],
|
||||
'attempts_count': attempts_number_result['attempts_count'],
|
||||
}
|
||||
for attempts_number_result in attempts_number_results
|
||||
}
|
||||
|
||||
for user_input in attempts_to_compute:
|
||||
attempts_number_result = attempts_number_results.get(user_input.id, {})
|
||||
user_input.attempts_number = attempts_number_result.get('attempts_number', 1)
|
||||
user_input.attempts_count = attempts_number_result.get('attempts_count', 1)
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
if 'predefined_question_ids' not in vals:
|
||||
suvey_id = vals.get('survey_id', self.env.context.get('default_survey_id'))
|
||||
survey = self.env['survey.survey'].browse(suvey_id)
|
||||
vals['predefined_question_ids'] = [(6, 0, survey._prepare_user_input_predefined_questions().ids)]
|
||||
return super(SurveyUserInput, self).create(vals_list)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# ACTIONS / BUSINESS
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def action_resend(self):
|
||||
partners = self.env['res.partner']
|
||||
emails = []
|
||||
for user_answer in self:
|
||||
if user_answer.partner_id:
|
||||
partners |= user_answer.partner_id
|
||||
elif user_answer.email:
|
||||
emails.append(user_answer.email)
|
||||
|
||||
return self.survey_id.with_context(
|
||||
default_existing_mode='resend',
|
||||
default_partner_ids=partners.ids,
|
||||
default_emails=','.join(emails)
|
||||
).action_send_survey()
|
||||
|
||||
def action_print_answers(self):
|
||||
""" Open the website page with the survey form """
|
||||
self.ensure_one()
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'name': "View Answers",
|
||||
'target': 'self',
|
||||
'url': '/survey/print/%s?answer_token=%s' % (self.survey_id.access_token, self.access_token)
|
||||
}
|
||||
|
||||
def action_redirect_to_attempts(self):
|
||||
self.ensure_one()
|
||||
|
||||
action = self.env['ir.actions.act_window']._for_xml_id('survey.action_survey_user_input')
|
||||
context = dict(self.env.context or {})
|
||||
|
||||
context['create'] = False
|
||||
context['search_default_survey_id'] = self.survey_id.id
|
||||
context['search_default_group_by_survey'] = False
|
||||
if self.partner_id:
|
||||
context['search_default_partner_id'] = self.partner_id.id
|
||||
elif self.email:
|
||||
context['search_default_email'] = self.email
|
||||
|
||||
action['context'] = context
|
||||
return action
|
||||
|
||||
@api.model
|
||||
def _generate_invite_token(self):
|
||||
return str(uuid.uuid4())
|
||||
|
||||
def _mark_in_progress(self):
|
||||
""" marks the state as 'in_progress' and updates the start_datetime accordingly. """
|
||||
self.write({
|
||||
'start_datetime': fields.Datetime.now(),
|
||||
'state': 'in_progress'
|
||||
})
|
||||
|
||||
def _mark_done(self):
|
||||
""" This method will:
|
||||
1. mark the state as 'done'
|
||||
2. send the certification email with attached document if
|
||||
- The survey is a certification
|
||||
- It has a certification_mail_template_id set
|
||||
- The user succeeded the test
|
||||
3. Notify survey subtype subscribers of the newly completed input
|
||||
Will also run challenge Cron to give the certification badge if any."""
|
||||
self.write({
|
||||
'end_datetime': fields.Datetime.now(),
|
||||
'state': 'done',
|
||||
})
|
||||
|
||||
Challenge_sudo = self.env['gamification.challenge'].sudo()
|
||||
badge_ids = []
|
||||
self._notify_new_participation_subscribers()
|
||||
for user_input in self:
|
||||
if user_input.survey_id.certification and user_input.scoring_success:
|
||||
if user_input.survey_id.certification_mail_template_id and not user_input.test_entry:
|
||||
user_input.survey_id.certification_mail_template_id.send_mail(user_input.id, email_layout_xmlid="mail.mail_notification_light")
|
||||
if user_input.survey_id.certification_give_badge:
|
||||
badge_ids.append(user_input.survey_id.certification_badge_id.id)
|
||||
|
||||
# Update predefined_question_id to remove inactive questions
|
||||
user_input.predefined_question_ids -= user_input._get_inactive_conditional_questions()
|
||||
|
||||
if badge_ids:
|
||||
challenges = Challenge_sudo.search([('reward_id', 'in', badge_ids)])
|
||||
if challenges:
|
||||
Challenge_sudo._cron_update(ids=challenges.ids, commit=False)
|
||||
|
||||
def get_start_url(self):
|
||||
self.ensure_one()
|
||||
return '%s?answer_token=%s' % (self.survey_id.get_start_url(), self.access_token)
|
||||
|
||||
def get_print_url(self):
|
||||
self.ensure_one()
|
||||
return '%s?answer_token=%s' % (self.survey_id.get_print_url(), self.access_token)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# CREATE / UPDATE LINES FROM SURVEY FRONTEND INPUT
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def _save_lines(self, question, answer, comment=None, overwrite_existing=True):
|
||||
""" Save answers to questions, depending on question type.
|
||||
|
||||
:param bool overwrite_existing: if an answer already exists for question and user_input_id
|
||||
it will be overwritten (or deleted for 'choice' questions) in order to maintain data consistency.
|
||||
:raises UserError: if line exists and overwrite_existing is False
|
||||
"""
|
||||
old_answers = self.env['survey.user_input.line'].search([
|
||||
('user_input_id', '=', self.id),
|
||||
('question_id', '=', question.id)
|
||||
])
|
||||
if old_answers and not overwrite_existing:
|
||||
raise UserError(_("This answer cannot be overwritten."))
|
||||
|
||||
if question.question_type in ['char_box', 'text_box', 'numerical_box', 'date', 'datetime']:
|
||||
self._save_line_simple_answer(question, old_answers, answer)
|
||||
if question.save_as_email and answer:
|
||||
self.write({'email': answer})
|
||||
if question.save_as_nickname and answer:
|
||||
self.write({'nickname': answer})
|
||||
|
||||
elif question.question_type in ['simple_choice', 'multiple_choice']:
|
||||
self._save_line_choice(question, old_answers, answer, comment)
|
||||
elif question.question_type == 'matrix':
|
||||
self._save_line_matrix(question, old_answers, answer, comment)
|
||||
else:
|
||||
raise AttributeError(question.question_type + ": This type of question has no saving function")
|
||||
|
||||
def _save_line_simple_answer(self, question, old_answers, answer):
|
||||
vals = self._get_line_answer_values(question, answer, question.question_type)
|
||||
if old_answers:
|
||||
old_answers.write(vals)
|
||||
return old_answers
|
||||
else:
|
||||
return self.env['survey.user_input.line'].create(vals)
|
||||
|
||||
def _save_line_choice(self, question, old_answers, answers, comment):
|
||||
if not (isinstance(answers, list)):
|
||||
answers = [answers]
|
||||
|
||||
if not answers:
|
||||
# add a False answer to force saving a skipped line
|
||||
# this will make this question correctly considered as skipped in statistics
|
||||
answers = [False]
|
||||
|
||||
vals_list = []
|
||||
|
||||
if question.question_type == 'simple_choice':
|
||||
if not question.comment_count_as_answer or not question.comments_allowed or not comment:
|
||||
vals_list = [self._get_line_answer_values(question, answer, 'suggestion') for answer in answers]
|
||||
elif question.question_type == 'multiple_choice':
|
||||
vals_list = [self._get_line_answer_values(question, answer, 'suggestion') for answer in answers]
|
||||
|
||||
if comment:
|
||||
vals_list.append(self._get_line_comment_values(question, comment))
|
||||
|
||||
old_answers.sudo().unlink()
|
||||
return self.env['survey.user_input.line'].create(vals_list)
|
||||
|
||||
def _save_line_matrix(self, question, old_answers, answers, comment):
|
||||
vals_list = []
|
||||
|
||||
if not answers and question.matrix_row_ids:
|
||||
# add a False answer to force saving a skipped line
|
||||
# this will make this question correctly considered as skipped in statistics
|
||||
answers = {question.matrix_row_ids[0].id: [False]}
|
||||
|
||||
if answers:
|
||||
for row_key, row_answer in answers.items():
|
||||
for answer in row_answer:
|
||||
vals = self._get_line_answer_values(question, answer, 'suggestion')
|
||||
vals['matrix_row_id'] = int(row_key)
|
||||
vals_list.append(vals.copy())
|
||||
|
||||
if comment:
|
||||
vals_list.append(self._get_line_comment_values(question, comment))
|
||||
|
||||
old_answers.sudo().unlink()
|
||||
return self.env['survey.user_input.line'].create(vals_list)
|
||||
|
||||
def _get_line_answer_values(self, question, answer, answer_type):
|
||||
vals = {
|
||||
'user_input_id': self.id,
|
||||
'question_id': question.id,
|
||||
'skipped': False,
|
||||
'answer_type': answer_type,
|
||||
}
|
||||
if not answer or (isinstance(answer, str) and not answer.strip()):
|
||||
vals.update(answer_type=None, skipped=True)
|
||||
return vals
|
||||
|
||||
if answer_type == 'suggestion':
|
||||
vals['suggested_answer_id'] = int(answer)
|
||||
elif answer_type == 'numerical_box':
|
||||
vals['value_numerical_box'] = float(answer)
|
||||
else:
|
||||
vals['value_%s' % answer_type] = answer
|
||||
return vals
|
||||
|
||||
def _get_line_comment_values(self, question, comment):
|
||||
return {
|
||||
'user_input_id': self.id,
|
||||
'question_id': question.id,
|
||||
'skipped': False,
|
||||
'answer_type': 'char_box',
|
||||
'value_char_box': comment,
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# STATISTICS / RESULTS
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def _prepare_statistics(self):
|
||||
""" Prepares survey.user_input's statistics to display various charts on the frontend.
|
||||
Returns a structure containing answers statistics "by section" and "totals" for every input in self.
|
||||
|
||||
e.g returned structure:
|
||||
{
|
||||
survey.user_input(1,): {
|
||||
'by_section': {
|
||||
'Uncategorized': {
|
||||
'question_count': 2,
|
||||
'correct': 2,
|
||||
'partial': 0,
|
||||
'incorrect': 0,
|
||||
'skipped': 0,
|
||||
},
|
||||
'Mathematics': {
|
||||
'question_count': 3,
|
||||
'correct': 1,
|
||||
'partial': 1,
|
||||
'incorrect': 0,
|
||||
'skipped': 1,
|
||||
},
|
||||
'Geography': {
|
||||
'question_count': 4,
|
||||
'correct': 2,
|
||||
'partial': 0,
|
||||
'incorrect': 2,
|
||||
'skipped': 0,
|
||||
}
|
||||
},
|
||||
'totals' [{
|
||||
'text': 'Correct',
|
||||
'count': 5,
|
||||
}, {
|
||||
'text': 'Partially',
|
||||
'count': 1,
|
||||
}, {
|
||||
'text': 'Incorrect',
|
||||
'count': 2,
|
||||
}, {
|
||||
'text': 'Unanswered',
|
||||
'count': 1,
|
||||
}]
|
||||
}
|
||||
}"""
|
||||
res = dict((user_input, {
|
||||
'by_section': {}
|
||||
}) for user_input in self)
|
||||
|
||||
scored_questions = self.mapped('predefined_question_ids').filtered(lambda question: question.is_scored_question)
|
||||
|
||||
for question in scored_questions:
|
||||
if question.question_type == 'simple_choice':
|
||||
question_incorrect_scored_answers = question.suggested_answer_ids.filtered(lambda answer: not answer.is_correct and answer.answer_score > 0)
|
||||
|
||||
if question.question_type in ['simple_choice', 'multiple_choice']:
|
||||
question_correct_suggested_answers = question.suggested_answer_ids.filtered(lambda answer: answer.is_correct)
|
||||
|
||||
question_section = question.page_id.title or _('Uncategorized')
|
||||
for user_input in self:
|
||||
user_input_lines = user_input.user_input_line_ids.filtered(lambda line: line.question_id == question)
|
||||
if question.question_type == 'simple_choice':
|
||||
answer_result_key = self._simple_choice_question_answer_result(user_input_lines, question_correct_suggested_answers, question_incorrect_scored_answers)
|
||||
elif question.question_type == 'multiple_choice':
|
||||
answer_result_key = self._multiple_choice_question_answer_result(user_input_lines, question_correct_suggested_answers)
|
||||
else:
|
||||
answer_result_key = self._simple_question_answer_result(user_input_lines)
|
||||
|
||||
if question_section not in res[user_input]['by_section']:
|
||||
res[user_input]['by_section'][question_section] = {
|
||||
'question_count': 0,
|
||||
'correct': 0,
|
||||
'partial': 0,
|
||||
'incorrect': 0,
|
||||
'skipped': 0,
|
||||
}
|
||||
|
||||
res[user_input]['by_section'][question_section]['question_count'] += 1
|
||||
res[user_input]['by_section'][question_section][answer_result_key] += 1
|
||||
|
||||
for user_input in self:
|
||||
correct_count = 0
|
||||
partial_count = 0
|
||||
incorrect_count = 0
|
||||
skipped_count = 0
|
||||
|
||||
for section_counts in res[user_input]['by_section'].values():
|
||||
correct_count += section_counts.get('correct', 0)
|
||||
partial_count += section_counts.get('partial', 0)
|
||||
incorrect_count += section_counts.get('incorrect', 0)
|
||||
skipped_count += section_counts.get('skipped', 0)
|
||||
|
||||
res[user_input]['totals'] = [
|
||||
{'text': _("Correct"), 'count': correct_count},
|
||||
{'text': _("Partially"), 'count': partial_count},
|
||||
{'text': _("Incorrect"), 'count': incorrect_count},
|
||||
{'text': _("Unanswered"), 'count': skipped_count}
|
||||
]
|
||||
|
||||
return res
|
||||
|
||||
def _multiple_choice_question_answer_result(self, user_input_lines, question_correct_suggested_answers):
|
||||
correct_user_input_lines = user_input_lines.filtered(lambda line: line.answer_is_correct and not line.skipped).mapped('suggested_answer_id')
|
||||
incorrect_user_input_lines = user_input_lines.filtered(lambda line: not line.answer_is_correct and not line.skipped)
|
||||
if question_correct_suggested_answers and correct_user_input_lines == question_correct_suggested_answers:
|
||||
return 'correct'
|
||||
elif correct_user_input_lines and correct_user_input_lines < question_correct_suggested_answers:
|
||||
return 'partial'
|
||||
elif not correct_user_input_lines and incorrect_user_input_lines:
|
||||
return 'incorrect'
|
||||
else:
|
||||
return 'skipped'
|
||||
|
||||
def _simple_choice_question_answer_result(self, user_input_line, question_correct_suggested_answers, question_incorrect_scored_answers):
|
||||
user_answer = user_input_line.suggested_answer_id if not user_input_line.skipped else self.env['survey.question.answer']
|
||||
if user_answer in question_correct_suggested_answers:
|
||||
return 'correct'
|
||||
elif user_answer in question_incorrect_scored_answers:
|
||||
return 'partial'
|
||||
elif user_answer:
|
||||
return 'incorrect'
|
||||
else:
|
||||
return 'skipped'
|
||||
|
||||
def _simple_question_answer_result(self, user_input_line):
|
||||
if user_input_line.skipped:
|
||||
return 'skipped'
|
||||
elif user_input_line.answer_is_correct:
|
||||
return 'correct'
|
||||
else:
|
||||
return 'incorrect'
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Conditional Questions Management
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def _get_conditional_values(self):
|
||||
""" For survey containing conditional questions, we need a triggered_questions_by_answer map that contains
|
||||
{key: answer, value: the question that the answer triggers, if selected},
|
||||
The idea is to be able to verify, on every answer check, if this answer is triggering the display
|
||||
of another question.
|
||||
If answer is not in the conditional map:
|
||||
- nothing happens.
|
||||
If the answer is in the conditional map:
|
||||
- If we are in ONE PAGE survey : (handled at CLIENT side)
|
||||
-> display immediately the depending question
|
||||
- If we are in PAGE PER SECTION : (handled at CLIENT side)
|
||||
- If related question is on the same page :
|
||||
-> display immediately the depending question
|
||||
- If the related question is not on the same page :
|
||||
-> keep the answers in memory and check at next page load if the depending question is in there and
|
||||
display it, if so.
|
||||
- If we are in PAGE PER QUESTION : (handled at SERVER side)
|
||||
-> During submit, determine which is the next question to display getting the next question
|
||||
that is the next in sequence and that is either not triggered by another question's answer, or that
|
||||
is triggered by an already selected answer.
|
||||
To do all this, we need to return:
|
||||
- triggering_answers_by_question: dict -> for a given question, the answers that triggers it
|
||||
Used mainly to ease template rendering
|
||||
- triggered_questions_by_answer: dict -> for a given answer, list of questions triggered by this answer;
|
||||
Used mainly for dynamic show/hide behaviour at client side
|
||||
- list of all selected answers: [answer_id1, answer_id2, ...] (for survey reloading, otherwise, this list is
|
||||
updated at client side)
|
||||
"""
|
||||
triggering_answers_by_question = {}
|
||||
triggered_questions_by_answer = {}
|
||||
# Ignore conditional configuration if randomised questions selection
|
||||
if self.survey_id.questions_selection != 'random':
|
||||
triggering_answers_by_question, triggered_questions_by_answer = self.survey_id._get_conditional_maps()
|
||||
selected_answers = self._get_selected_suggested_answers()
|
||||
|
||||
return triggering_answers_by_question, triggered_questions_by_answer, selected_answers
|
||||
|
||||
def _get_selected_suggested_answers(self):
|
||||
"""
|
||||
For now, only simple and multiple choices question type are handled by the conditional questions feature.
|
||||
Mapping all the suggested answers selected by the user will also include answers from matrix question type,
|
||||
Those ones won't be used.
|
||||
Maybe someday, conditional questions feature will be extended to work with matrix question.
|
||||
:return: all the suggested answer selected by the user.
|
||||
"""
|
||||
return self.mapped('user_input_line_ids.suggested_answer_id')
|
||||
|
||||
def _clear_inactive_conditional_answers(self):
|
||||
"""
|
||||
Clean eventual answers on conditional questions that should not have been displayed to user.
|
||||
This method is used mainly for page per question survey, a similar method does the same treatment
|
||||
at client side for the other survey layouts.
|
||||
E.g.: if depending answer was uncheck after answering conditional question, we need to clear answers
|
||||
of that conditional question, for two reasons:
|
||||
- ensure correct scoring
|
||||
- if the selected answer triggers another question later in the survey, if the answer is not cleared,
|
||||
a question that should not be displayed to the user will be.
|
||||
|
||||
TODO DBE: Maybe this can be the only cleaning method, even for section_per_page or one_page where
|
||||
conditional questions are, for now, cleared in JS directly. But this can be annoying if user typed a long
|
||||
answer, changed their mind unchecking depending answer and changed again their mind by rechecking the depending
|
||||
answer -> For now, the long answer will be lost. If we use this as the master cleaning method,
|
||||
long answer will be cleared only during submit.
|
||||
"""
|
||||
inactive_questions = self._get_inactive_conditional_questions()
|
||||
|
||||
# delete user.input.line on question that should not be answered.
|
||||
answers_to_delete = self.user_input_line_ids.filtered(lambda answer: answer.question_id in inactive_questions)
|
||||
answers_to_delete.unlink()
|
||||
|
||||
def _get_inactive_conditional_questions(self):
|
||||
triggering_answers_by_question, _, selected_answers = self._get_conditional_values()
|
||||
|
||||
# get questions that should not be answered
|
||||
inactive_questions = self.env['survey.question']
|
||||
for question, triggering_answers in triggering_answers_by_question.items():
|
||||
if triggering_answers and not triggering_answers & selected_answers:
|
||||
inactive_questions |= question
|
||||
return inactive_questions
|
||||
|
||||
def _get_print_questions(self):
|
||||
""" Get the questions to display : the ones that should have been answered = active questions
|
||||
In case of session, active questions are based on most voted answers
|
||||
:return: active survey.question browse records
|
||||
"""
|
||||
survey = self.survey_id
|
||||
if self.is_session_answer:
|
||||
most_voted_answers = survey._get_session_most_voted_answers()
|
||||
inactive_questions = most_voted_answers._get_inactive_conditional_questions()
|
||||
else:
|
||||
inactive_questions = self._get_inactive_conditional_questions()
|
||||
return survey.question_ids - inactive_questions
|
||||
|
||||
def _get_next_skipped_page_or_question(self):
|
||||
"""Get next skipped question or page in case the option 'can_go_back' is set on the survey
|
||||
It loops to the first skipped question or page if 'last_displayed_page_id' is the last
|
||||
skipped question or page."""
|
||||
self.ensure_one()
|
||||
skipped_mandatory_answer_ids = self.user_input_line_ids.filtered(
|
||||
lambda answer: answer.skipped and answer.question_id.constr_mandatory)
|
||||
|
||||
if not skipped_mandatory_answer_ids:
|
||||
return self.env['survey.question']
|
||||
|
||||
page_or_question_key = 'page_id' if self.survey_id.questions_layout == 'page_per_section' else 'question_id'
|
||||
page_or_question_ids = skipped_mandatory_answer_ids.mapped(page_or_question_key).sorted()
|
||||
|
||||
if self.last_displayed_page_id not in page_or_question_ids\
|
||||
or self.last_displayed_page_id == page_or_question_ids[-1]:
|
||||
return page_or_question_ids[0]
|
||||
|
||||
current_page_index = page_or_question_ids.ids.index(self.last_displayed_page_id.id)
|
||||
return page_or_question_ids[current_page_index + 1]
|
||||
|
||||
def _get_skipped_questions(self):
|
||||
self.ensure_one()
|
||||
|
||||
return self.user_input_line_ids.filtered(
|
||||
lambda answer: answer.skipped and answer.question_id.constr_mandatory).question_id
|
||||
|
||||
def _is_last_skipped_page_or_question(self, page_or_question):
|
||||
"""In case of a submitted survey tells if the question or page is the last
|
||||
skipped page or question.
|
||||
|
||||
This is used to :
|
||||
|
||||
- Display a Submit button if the actual question is the last skipped question.
|
||||
- Avoid displaying a Submit button on the last survey question if there are
|
||||
still skipped questions before.
|
||||
- Avoid displaying the next page if submitting the latest skipped question.
|
||||
|
||||
:param page_or_question: page if survey's layout is page_per_section, question if page_per_question.
|
||||
"""
|
||||
if self.survey_id.questions_layout == 'one_page':
|
||||
return True
|
||||
skipped = self._get_skipped_questions()
|
||||
if not skipped:
|
||||
return True
|
||||
if self.survey_id.questions_layout == 'page_per_section':
|
||||
skipped = skipped.page_id
|
||||
return skipped == page_or_question
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# MESSAGING
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def _message_get_suggested_recipients(self):
|
||||
recipients = super()._message_get_suggested_recipients()
|
||||
for user_input in self:
|
||||
if user_input.partner_id:
|
||||
user_input._message_add_suggested_recipient(
|
||||
recipients,
|
||||
partner=user_input.partner_id,
|
||||
reason=_('Survey Participant')
|
||||
)
|
||||
return recipients
|
||||
|
||||
def _notify_new_participation_subscribers(self):
|
||||
subtype_id = self.env.ref('survey.mt_survey_survey_user_input_completed', raise_if_not_found=False)
|
||||
if not self.ids or not subtype_id:
|
||||
return
|
||||
author_id = self.env.ref('base.partner_root').id if self.env.user.is_public else self.env.user.partner_id.id
|
||||
# Only post if there are any followers
|
||||
recipients_data = self.env['mail.followers']._get_recipient_data(self.survey_id, 'notification', subtype_id.id)
|
||||
followed_survey_ids = [survey_id for survey_id, followers in recipients_data.items() if followers]
|
||||
for user_input in self.filtered(lambda user_input_: user_input_.survey_id.id in followed_survey_ids):
|
||||
survey_title = user_input.survey_id.title
|
||||
if user_input.partner_id:
|
||||
body = _(
|
||||
'%(participant) just participated in "%(survey_title)s".',
|
||||
participant=user_input.partner_id.display_name,
|
||||
survey_title=survey_title,
|
||||
)
|
||||
else:
|
||||
body = _('Someone just participated in "%(survey_title)s".', survey_title=survey_title)
|
||||
|
||||
user_input.message_post(author_id=author_id, body=body, subtype_xmlid='survey.mt_survey_user_input_completed')
|
||||
|
||||
|
||||
class SurveyUserInputLine(models.Model):
|
||||
_name = 'survey.user_input.line'
|
||||
_description = 'Survey User Input Line'
|
||||
_rec_name = 'user_input_id'
|
||||
_order = 'question_sequence, id'
|
||||
|
||||
# survey data
|
||||
user_input_id = fields.Many2one('survey.user_input', string='User Input', ondelete='cascade', required=True, index=True)
|
||||
survey_id = fields.Many2one(related='user_input_id.survey_id', string='Survey', store=True, readonly=False)
|
||||
question_id = fields.Many2one('survey.question', string='Question', ondelete='cascade', required=True)
|
||||
page_id = fields.Many2one(related='question_id.page_id', string="Section", readonly=False)
|
||||
question_sequence = fields.Integer('Sequence', related='question_id.sequence', store=True)
|
||||
# answer
|
||||
skipped = fields.Boolean('Skipped')
|
||||
answer_type = fields.Selection([
|
||||
('text_box', 'Free Text'),
|
||||
('char_box', 'Text'),
|
||||
('numerical_box', 'Number'),
|
||||
('date', 'Date'),
|
||||
('datetime', 'Datetime'),
|
||||
('suggestion', 'Suggestion')], string='Answer Type')
|
||||
value_char_box = fields.Char('Text answer')
|
||||
value_numerical_box = fields.Float('Numerical answer')
|
||||
value_date = fields.Date('Date answer')
|
||||
value_datetime = fields.Datetime('Datetime answer')
|
||||
value_text_box = fields.Text('Free Text answer')
|
||||
suggested_answer_id = fields.Many2one('survey.question.answer', string="Suggested answer")
|
||||
matrix_row_id = fields.Many2one('survey.question.answer', string="Row answer")
|
||||
# scoring
|
||||
answer_score = fields.Float('Score')
|
||||
answer_is_correct = fields.Boolean('Correct')
|
||||
|
||||
@api.depends(
|
||||
'answer_type', 'value_text_box', 'value_numerical_box',
|
||||
'value_char_box', 'value_date', 'value_datetime',
|
||||
'suggested_answer_id.value', 'matrix_row_id.value',
|
||||
)
|
||||
def _compute_display_name(self):
|
||||
for line in self:
|
||||
if line.answer_type == 'char_box':
|
||||
line.display_name = line.value_char_box
|
||||
elif line.answer_type == 'text_box' and line.value_text_box:
|
||||
line.display_name = textwrap.shorten(line.value_text_box, width=50, placeholder=" [...]")
|
||||
elif line.answer_type == 'numerical_box':
|
||||
line.display_name = line.value_numerical_box
|
||||
elif line.answer_type == 'date':
|
||||
line.display_name = fields.Date.to_string(line.value_date)
|
||||
elif line.answer_type == 'datetime':
|
||||
line.display_name = fields.Datetime.to_string(line.value_datetime)
|
||||
elif line.answer_type == 'suggestion':
|
||||
if line.matrix_row_id:
|
||||
line.display_name = f'{line.suggested_answer_id.value}: {line.matrix_row_id.value}'
|
||||
else:
|
||||
line.display_name = line.suggested_answer_id.value
|
||||
|
||||
if not line.display_name:
|
||||
line.display_name = _('Skipped')
|
||||
|
||||
@api.constrains('skipped', 'answer_type')
|
||||
def _check_answer_type_skipped(self):
|
||||
for line in self:
|
||||
if (line.skipped == bool(line.answer_type)):
|
||||
raise ValidationError(_('A question can either be skipped or answered, not both.'))
|
||||
|
||||
# allow 0 for numerical box
|
||||
if line.answer_type == 'numerical_box' and float_is_zero(line['value_numerical_box'], precision_digits=6):
|
||||
continue
|
||||
if line.answer_type == 'suggestion':
|
||||
field_name = 'suggested_answer_id'
|
||||
elif line.answer_type:
|
||||
field_name = 'value_%s' % line.answer_type
|
||||
else: # skipped
|
||||
field_name = False
|
||||
|
||||
if field_name and not line[field_name]:
|
||||
raise ValidationError(_('The answer must be in the right type'))
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
if not vals.get('answer_score'):
|
||||
score_vals = self._get_answer_score_values(vals)
|
||||
vals.update(score_vals)
|
||||
return super(SurveyUserInputLine, self).create(vals_list)
|
||||
|
||||
def write(self, vals):
|
||||
res = True
|
||||
for line in self:
|
||||
vals_copy = {**vals}
|
||||
getter_params = {
|
||||
'user_input_id': line.user_input_id.id,
|
||||
'answer_type': line.answer_type,
|
||||
'question_id': line.question_id.id,
|
||||
**vals_copy
|
||||
}
|
||||
if not vals_copy.get('answer_score'):
|
||||
score_vals = self._get_answer_score_values(getter_params, compute_speed_score=False)
|
||||
vals_copy.update(score_vals)
|
||||
res = super(SurveyUserInputLine, line).write(vals_copy) and res
|
||||
return res
|
||||
|
||||
def _get_answer_matching_domain(self):
|
||||
self.ensure_one()
|
||||
if self.answer_type in ('char_box', 'text_box', 'numerical_box', 'date', 'datetime'):
|
||||
value_field = {
|
||||
'char_box': 'value_char_box',
|
||||
'text_box': 'value_text_box',
|
||||
'numerical_box': 'value_numerical_box',
|
||||
'date': 'value_date',
|
||||
'datetime': 'value_datetime',
|
||||
}
|
||||
operators = {
|
||||
'char_box': 'ilike',
|
||||
'text_box': 'ilike',
|
||||
'numerical_box': '=',
|
||||
'date': '=',
|
||||
'datetime': '=',
|
||||
}
|
||||
return ['&', ('question_id', '=', self.question_id.id), (value_field[self.answer_type], operators[self.answer_type], self._get_answer_value())]
|
||||
elif self.answer_type == 'suggestion':
|
||||
return self.suggested_answer_id._get_answer_matching_domain(self.matrix_row_id.id if self.matrix_row_id else False)
|
||||
|
||||
@api.model
|
||||
def _get_answer_score_values(self, vals, compute_speed_score=True):
|
||||
""" Get values for: answer_is_correct and associated answer_score.
|
||||
|
||||
Requires vals to contain 'answer_type', 'question_id', and 'user_input_id'.
|
||||
Depending on 'answer_type' additional value of 'suggested_answer_id' may also be
|
||||
required.
|
||||
|
||||
Calculates whether an answer_is_correct and its score based on 'answer_type' and
|
||||
corresponding question. Handles choice (answer_type == 'suggestion') questions
|
||||
separately from other question types. Each selected choice answer is handled as an
|
||||
individual answer.
|
||||
|
||||
If score depends on the speed of the answer, it is adjusted as follows:
|
||||
- If the user answers in less than 2 seconds, they receive 100% of the possible points.
|
||||
- If user answers after that, they receive 50% of the possible points + the remaining
|
||||
50% scaled by the time limit and time taken to answer [i.e. a minimum of 50% of the
|
||||
possible points is given to all correct answers]
|
||||
|
||||
Example of returned values:
|
||||
* {'answer_is_correct': False, 'answer_score': 0} (default)
|
||||
* {'answer_is_correct': True, 'answer_score': 2.0}
|
||||
"""
|
||||
user_input_id = vals.get('user_input_id')
|
||||
answer_type = vals.get('answer_type')
|
||||
question_id = vals.get('question_id')
|
||||
if not question_id:
|
||||
raise ValueError(_('Computing score requires a question in arguments.'))
|
||||
question = self.env['survey.question'].browse(int(question_id))
|
||||
|
||||
# default and non-scored questions
|
||||
answer_is_correct = False
|
||||
answer_score = 0
|
||||
|
||||
# record selected suggested choice answer_score (can be: pos, neg, or 0)
|
||||
if question.question_type in ['simple_choice', 'multiple_choice']:
|
||||
if answer_type == 'suggestion':
|
||||
suggested_answer_id = vals.get('suggested_answer_id')
|
||||
if suggested_answer_id:
|
||||
question_answer = self.env['survey.question.answer'].browse(int(suggested_answer_id))
|
||||
answer_score = question_answer.answer_score
|
||||
answer_is_correct = question_answer.is_correct
|
||||
# for all other scored question cases, record question answer_score (can be: pos or 0)
|
||||
elif question.question_type in ['date', 'datetime', 'numerical_box']:
|
||||
answer = vals.get('value_%s' % answer_type)
|
||||
if answer_type == 'numerical_box':
|
||||
answer = float(answer)
|
||||
elif answer_type == 'date':
|
||||
answer = fields.Date.from_string(answer)
|
||||
elif answer_type == 'datetime':
|
||||
answer = fields.Datetime.from_string(answer)
|
||||
if answer and answer == question['answer_%s' % answer_type]:
|
||||
answer_is_correct = True
|
||||
answer_score = question.answer_score
|
||||
|
||||
if compute_speed_score and answer_score > 0:
|
||||
user_input = self.env['survey.user_input'].browse(user_input_id)
|
||||
session_speed_rating = user_input.exists() and user_input.is_session_answer and user_input.survey_id.session_speed_rating
|
||||
if session_speed_rating:
|
||||
max_score_delay = 2
|
||||
time_limit = question.time_limit
|
||||
now = fields.Datetime.now()
|
||||
seconds_to_answer = (now - user_input.survey_id.session_question_start_time).total_seconds()
|
||||
question_remaining_time = time_limit - seconds_to_answer
|
||||
# if answered within the max_score_delay => leave score as is
|
||||
if question_remaining_time < 0: # if no time left
|
||||
answer_score /= 2
|
||||
elif seconds_to_answer > max_score_delay:
|
||||
time_limit -= max_score_delay # we remove the max_score_delay to have all possible values
|
||||
score_proportion = (time_limit - seconds_to_answer) / time_limit
|
||||
answer_score = (answer_score / 2) * (1 + score_proportion)
|
||||
|
||||
return {
|
||||
'answer_is_correct': answer_is_correct,
|
||||
'answer_score': answer_score
|
||||
}
|
||||
|
||||
def _get_answer_value(self):
|
||||
self.ensure_one()
|
||||
if self.answer_type == 'char_box':
|
||||
return self.value_char_box
|
||||
elif self.answer_type == 'text_box':
|
||||
return self.value_text_box
|
||||
elif self.answer_type == 'numerical_box':
|
||||
return self.value_numerical_box
|
||||
elif self.answer_type == 'date':
|
||||
return self.value_date
|
||||
elif self.answer_type == 'datetime':
|
||||
return self.value_datetime
|
||||
elif self.answer_type == 'suggestion':
|
||||
return self.suggested_answer_id.value
|
33
report/survey_reports.xml
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- Paper Format -->
|
||||
<record id="paperformat_survey_certification" model="report.paperformat">
|
||||
<field name="name">Survey Certification</field>
|
||||
<field name="default" eval="True"/>
|
||||
<field name="format">A4</field>
|
||||
<field name="orientation">Landscape</field>
|
||||
<field name="margin_top">0</field>
|
||||
<field name="margin_bottom">0</field>
|
||||
<field name="margin_left">0</field>
|
||||
<field name="margin_right">0</field>
|
||||
<field name="header_line" eval="False"/>
|
||||
<field name="header_spacing">0</field>
|
||||
<field name="disable_shrinking" eval="True"/>
|
||||
<field name="dpi">96</field>
|
||||
</record>
|
||||
<!-- QWeb Reports -->
|
||||
<record id="certification_report" model="ir.actions.report">
|
||||
<field name="name">Certifications</field>
|
||||
<field name="model">survey.user_input</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">survey.certification_report_view</field>
|
||||
<field name="report_file">survey.certification_report_view</field>
|
||||
<field name="print_report_name">'Certification - %s' % (object.survey_id.display_name)</field>
|
||||
<field name="attachment">'certification.pdf'</field>
|
||||
<field name="binding_model_id" ref="model_survey_user_input"/>
|
||||
<field name="binding_type">report</field>
|
||||
<field name="paperformat_id" ref="paperformat_survey_certification"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
81
report/survey_templates.xml
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<template id="certification_report_view_general">
|
||||
<!-- Style classes to be applied to '#o_survey_certification': [no class](purple), gold, blue -->
|
||||
<div id="o_survey_certification" t-att-data-oe-model="user_input._name" t-att-data-oe-id="user_input.id"
|
||||
t-attf-class="#{'article certification-wrapper ' + layout_template + ' ' + layout_color}">
|
||||
<div class="certification">
|
||||
<div class="certification-seal" t-if="user_input.scoring_success and layout_template == 'modern'"/>
|
||||
<div class="certification-top">
|
||||
<h1><b>Certificate</b>
|
||||
<br/><span t-if="user_input.scoring_success">of achievement</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="page certification-content">
|
||||
<div class="oe_structure"/>
|
||||
<div t-if="user_input.scoring_success">
|
||||
<div class="oe_structure"/>
|
||||
<p> <span>This certificate is presented to</span>
|
||||
<br/>
|
||||
<t t-set="certif_style" t-value="''"/>
|
||||
<t t-set="certified_name" t-value="user_input.partner_id.name or user_input.email or ''"/>
|
||||
<t t-if="certified_name.isupper()">
|
||||
<t t-set="certif_style" t-value="certif_style + 'font-family: certification-serif;'"/>
|
||||
</t>
|
||||
<t t-if="len(certified_name) > 35 and layout_template == 'classic'">
|
||||
<t t-set="certif_style" t-value="certif_style + 'font-size: 20px; line-height: 4; font-family: certification-serif; '"/>
|
||||
</t>
|
||||
<t t-elif="len(certified_name) > 20">
|
||||
<t t-if="layout_template == 'modern'">
|
||||
<t t-set="certif_style" t-value="certif_style + 'font-size: 40px; line-height: 4;'"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<t t-set="certif_style" t-value="certif_style + 'font-size: 30px; line-height: 4;'"/>
|
||||
</t>
|
||||
</t>
|
||||
<span t-att-style="certif_style" class="user-name" t-out="certified_name">DEMO_CERTIFIED_NAME</span>
|
||||
|
||||
<br/> <span>by</span> <span class="certification-company" t-field="user_input.create_uid.company_id.display_name">Odoo</span> <span>for successfully completing</span>
|
||||
<br/><b><span class="certification-name" t-field="user_input.survey_id.display_name">Functional Training</span></b>
|
||||
</p>
|
||||
<div class="oe_structure"/>
|
||||
</div>
|
||||
<div t-else="" class="certification-failed">
|
||||
<p>Certification Failed</p>
|
||||
<div class="oe_structure"/>
|
||||
</div>
|
||||
<div class="oe_structure"/>
|
||||
</div>
|
||||
|
||||
<div class="certification-bottom">
|
||||
<div class="oe_structure"/>
|
||||
<div class="certification-date-wrapper">
|
||||
<span class="certification-date" t-field="user_input.create_date" t-options='{"widget": "date"}'>2023-08-18</span>
|
||||
<span>Date</span>
|
||||
</div>
|
||||
<div class="certification-company">
|
||||
<span class="certification-company-logo" t-field="user_input.create_uid.company_id.logo" t-options="{'widget': 'image'}" role="img"/>
|
||||
</div>
|
||||
</div>
|
||||
<div t-if="user_input.test_entry" class="test-entry"/>
|
||||
<div class="certification-number" t-if="user_input.scoring_success">
|
||||
Certification n°<span t-out="str(user_input.id).rjust(10, '0')">0000000010</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="certification_report_view">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="user_input">
|
||||
<t t-set="layout_values" t-value="user_input.survey_id.certification_report_layout.split('_') if user_input.survey_id.certification_report_layout else ['modern', 'purple']"/>
|
||||
<t t-set="layout_template" t-value="layout_values[0]"/>
|
||||
<t t-set="layout_color" t-value="layout_values[1]"/>
|
||||
<t t-call="survey.certification_report_view_general"/>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</data>
|
||||
</odoo>
|
23
security/ir.model.access.csv
Normal file
|
@ -0,0 +1,23 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_survey_all,survey.survey.all,model_survey_survey,,0,0,0,0
|
||||
access_survey_user,survey.survey.user,model_survey_survey,base.group_user,0,0,0,0
|
||||
access_survey_survey_user,survey.survey.survey.user,model_survey_survey,group_survey_user,1,1,1,1
|
||||
access_survey_survey_manager,survey.survey.survey.manager,model_survey_survey,group_survey_manager,1,1,1,1
|
||||
access_survey_question_all,survey.question.all,model_survey_question,,0,0,0,0
|
||||
access_survey_question_user,survey.question.user,model_survey_question,base.group_user,0,0,0,0
|
||||
access_survey_question_survey_user,survey.question.survey.user,model_survey_question,group_survey_user,1,1,1,1
|
||||
access_survey_question_survey_manager,survey.question.survey.manager,model_survey_question,group_survey_manager,1,1,1,1
|
||||
access_survey_question_answer_all,survey.question.answer.all,model_survey_question_answer,,0,0,0,0
|
||||
access_survey_question_answer_user,survey.question.answer.user,model_survey_question_answer,base.group_user,0,0,0,0
|
||||
access_survey_question_answer_survey_user,survey.question.answer.survey.user,model_survey_question_answer,group_survey_user,1,1,1,1
|
||||
access_survey_question_answer_survey_manager,survey.question.answer.survey.manager,model_survey_question_answer,group_survey_manager,1,1,1,1
|
||||
access_survey_user_input_all,survey.user_input.all,model_survey_user_input,,0,0,0,0
|
||||
access_survey_user_input_user,survey.user_input.user,model_survey_user_input,base.group_user,0,0,0,0
|
||||
access_survey_user_input_survey_user,survey.user_input.survey.user,model_survey_user_input,group_survey_user,1,1,1,1
|
||||
access_survey_user_input_survey_manager,survey.user_input.survey.manager,model_survey_user_input,group_survey_manager,1,1,1,1
|
||||
access_survey_user_input_line_all,survey.user_input.line.all,model_survey_user_input_line,,0,0,0,0
|
||||
access_survey_user_input_line_user,survey.user_input.line.user,model_survey_user_input_line,base.group_user,0,0,0,0
|
||||
access_survey_user_input_line_survey_user,survey.user_input.line.survey.user,model_survey_user_input_line,group_survey_user,1,0,0,0
|
||||
access_survey_user_input_line_survey_manager,survey.user_input.line.survey.manager,model_survey_user_input_line,group_survey_manager,1,1,1,1
|
||||
access_gamification_badge_survey_user,gamification.badge.survey.user,model_gamification_badge,group_survey_user,1,1,1,1
|
||||
access_survey_invite,access.survey.invite,model_survey_invite,survey.group_survey_user,1,1,1,0
|
|
184
security/survey_security.xml
Normal file
|
@ -0,0 +1,184 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record model="ir.module.category" id="base.module_category_marketing_surveys">
|
||||
<field name="description">Helps you manage your survey for review of different-different users.</field>
|
||||
<field name="sequence">20</field>
|
||||
</record>
|
||||
|
||||
<!-- Survey users -->
|
||||
<record model="res.groups" id="group_survey_user">
|
||||
<field name="name">User</field>
|
||||
<field name="category_id" ref="base.module_category_marketing_surveys"/>
|
||||
</record>
|
||||
|
||||
<!-- Survey managers -->
|
||||
<record model="res.groups" id="group_survey_manager">
|
||||
<field name="name">Administrator</field>
|
||||
<field name="category_id" ref="base.module_category_marketing_surveys"/>
|
||||
<field name="implied_ids" eval="[(4, ref('group_survey_user'))]"/>
|
||||
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="base.default_user" model="res.users">
|
||||
<field name="groups_id" eval="[(4,ref('group_survey_manager'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- SURVEY: SURVEY, PAGE, STAGE, QUESTION, LABEL -->
|
||||
<record id="survey_survey_rule_survey_manager" model="ir.rule">
|
||||
<field name="name">Survey: manager: all</field>
|
||||
<field name="model_id" ref="survey.model_survey_survey"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
<field name="groups" eval="[(4, ref('group_survey_manager'))]"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
</record>
|
||||
<record id="survey_survey_rule_survey_user_read" model="ir.rule">
|
||||
<field name="name">Survey: officer: read all</field>
|
||||
<field name="model_id" ref="survey.model_survey_survey"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
<field name="groups" eval="[(4, ref('group_survey_user'))]"/>
|
||||
<field name="perm_unlink" eval="0"/>
|
||||
<field name="perm_write" eval="0"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
<field name="perm_create" eval="0"/>
|
||||
</record>
|
||||
<record id="survey_survey_rule_survey_user_cwu" model="ir.rule">
|
||||
<field name="name">Survey: officer: create/write/unlink own only</field>
|
||||
<field name="model_id" ref="survey.model_survey_survey"/>
|
||||
<field name="domain_force">[('create_uid', '=', user.id)]</field>
|
||||
<field name="groups" eval="[(4, ref('group_survey_user'))]"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_read" eval="0"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
</record>
|
||||
|
||||
<record id="survey_question_rule_survey_manager" model="ir.rule">
|
||||
<field name="name">Survey question: manager: all</field>
|
||||
<field name="model_id" ref="survey.model_survey_question"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
<field name="groups" eval="[(4, ref('group_survey_manager'))]"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
</record>
|
||||
<record id="survey_question_rule_survey_user_read" model="ir.rule">
|
||||
<field name="name">Survey question: officer: read all</field>
|
||||
<field name="model_id" ref="survey.model_survey_question"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
<field name="groups" eval="[(4, ref('group_survey_user'))]"/>
|
||||
<field name="perm_unlink" eval="0"/>
|
||||
<field name="perm_write" eval="0"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
<field name="perm_create" eval="0"/>
|
||||
</record>
|
||||
<record id="survey_question_rule_survey_user_cw" model="ir.rule">
|
||||
<field name="name">Survey question: officer: create/write/unlink linked to own survey only</field>
|
||||
<field name="model_id" ref="survey.model_survey_question"/>
|
||||
<field name="domain_force">[('survey_id.create_uid', '=', user.id)]</field>
|
||||
<field name="groups" eval="[(4, ref('group_survey_user'))]"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_read" eval="0"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
</record>
|
||||
|
||||
<record id="survey_question_answer_rule_survey_manager" model="ir.rule">
|
||||
<field name="name">Survey question answer: manager: all</field>
|
||||
<field name="model_id" ref="survey.model_survey_question_answer"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
<field name="groups" eval="[(4, ref('group_survey_manager'))]"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
</record>
|
||||
<record id="survey_question_answer_rule_survey_user_read" model="ir.rule">
|
||||
<field name="name">Survey question answer: officer: read all</field>
|
||||
<field name="model_id" ref="survey.model_survey_question_answer"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
<field name="groups" eval="[(4, ref('group_survey_user'))]"/>
|
||||
<field name="perm_unlink" eval="0"/>
|
||||
<field name="perm_write" eval="0"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
<field name="perm_create" eval="0"/>
|
||||
</record>
|
||||
<record id="survey_question_answer_rule_survey_user_cw" model="ir.rule">
|
||||
<field name="name">Survey question answer: officer: create/write/unlink linked to own survey only</field>
|
||||
<field name="model_id" ref="survey.model_survey_question_answer"/>
|
||||
<field name="domain_force">['|', ('question_id.survey_id.create_uid', '=', user.id), ('matrix_question_id.survey_id.create_uid', '=', user.id)]</field>
|
||||
<field name="groups" eval="[(4, ref('group_survey_user'))]"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_read" eval="0"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
</record>
|
||||
|
||||
<!-- SURVEY: USER_INPUT, USER_INPUT_LINE -->
|
||||
<record id="survey_user_input_rule_survey_manager" model="ir.rule">
|
||||
<field name="name">Survey user input: manager: all</field>
|
||||
<field name="model_id" ref="survey.model_survey_user_input"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
<field name="groups" eval="[(4, ref('group_survey_manager'))]"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
</record>
|
||||
<record id="survey_user_input_rule_survey_user_read" model="ir.rule">
|
||||
<field name="name">Survey user input: officer: read all</field>
|
||||
<field name="model_id" ref="survey.model_survey_user_input"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
<field name="groups" eval="[(4, ref('group_survey_user'))]"/>
|
||||
<field name="perm_unlink" eval="0"/>
|
||||
<field name="perm_write" eval="0"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
<field name="perm_create" eval="0"/>
|
||||
</record>
|
||||
<record id="survey_user_input_rule_survey_user_cw" model="ir.rule">
|
||||
<field name="name">Survey user input: officer: create/write/unlink linked to own survey only</field>
|
||||
<field name="model_id" ref="survey.model_survey_user_input"/>
|
||||
<field name="domain_force">[('survey_id.create_uid', '=', user.id)]</field>
|
||||
<field name="groups" eval="[(4, ref('group_survey_user'))]"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_read" eval="0"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
</record>
|
||||
|
||||
<record id="survey_user_input_line_rule_survey_manager" model="ir.rule">
|
||||
<field name="name">Survey user input line: manager: all</field>
|
||||
<field name="model_id" ref="survey.model_survey_user_input_line"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
<field name="groups" eval="[(4, ref('group_survey_manager'))]"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
</record>
|
||||
<record id="survey_user_input_line_rule_survey_user_read" model="ir.rule">
|
||||
<field name="name">Survey user input line: officer: read all</field>
|
||||
<field name="model_id" ref="survey.model_survey_user_input_line"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
<field name="groups" eval="[(4, ref('group_survey_user'))]"/>
|
||||
<field name="perm_unlink" eval="0"/>
|
||||
<field name="perm_write" eval="0"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
<field name="perm_create" eval="0"/>
|
||||
</record>
|
||||
<record id="survey_user_input_line_rule_survey_user_cw" model="ir.rule">
|
||||
<field name="name">Survey user input line: officer: create/write/unlink linked to own survey only</field>
|
||||
<field name="model_id" ref="survey.model_survey_user_input_line"/>
|
||||
<field name="domain_force">[('user_input_id.survey_id.create_uid', '=', user.id)]</field>
|
||||
<field name="groups" eval="[(4, ref('group_survey_user'))]"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_read" eval="0"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
BIN
static/description/icon.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
1
static/description/icon.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="50" height="50" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"><path d="M15 15.5a7.5 7.5 0 1 1-15 0 7.5 7.5 0 0 1 15 0Z" fill="#088BF5"/><path d="M19 12a4 4 0 0 1 4-4h27v11a4 4 0 0 1-4 4H19V12Z" fill="#2EBCFA"/><path d="M0 34.5a7.5 7.5 0 1 1 15 0 7.5 7.5 0 0 1-15 0Z" fill="#F9464C"/><path d="M19 42h19a4 4 0 0 0 4-4V27H23a4 4 0 0 0-4 4v11Z" fill="#FC868B"/></svg>
|
After Width: | Height: | Size: 385 B |
BIN
static/img/africa.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
static/img/asia.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
static/img/china.jpg
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
static/img/coniferous.jpg
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
static/img/cornaceae.jpg
Normal file
After Width: | Height: | Size: 73 KiB |