From 07d0af5e7f6985764673e7fa07259f254d644753 Mon Sep 17 00:00:00 2001 From: Quentin Mondot Date: Thu, 2 Jul 2026 12:24:51 +0200 Subject: [PATCH] [ADD] survey_xlsx_expand_multiple_choice, survey_xlsx_extra_fields survey_xlsx_expand_multiple_choice: expand multiple_choice questions into one Oui/Non column per option, and matrix questions into one column per row (value = selected option). Relies on the extension hooks added to survey_xlsx. survey_xlsx_extra_fields: bridge (auto_install) with survey_extra_fields that excludes 'file' question types from the XLSX export. Co-Authored-By: Claude Opus 4.8 --- survey_xlsx_expand_multiple_choice/README.rst | 109 ++++ .../__init__.py | 3 + .../__manifest__.py | 16 + .../readme/CONTRIBUTORS.rst | 3 + .../readme/DESCRIPTION.rst | 23 + .../readme/USAGE.rst | 17 + .../report/__init__.py | 3 + .../report/report_survey_xlsx.py | 70 +++ .../static/description/index.html | 466 ++++++++++++++++++ .../tests/__init__.py | 3 + .../tests/test_report.py | 148 ++++++ survey_xlsx_extra_fields/README.rst | 87 ++++ survey_xlsx_extra_fields/__init__.py | 1 + survey_xlsx_extra_fields/__manifest__.py | 19 + .../readme/CONTRIBUTORS.rst | 3 + .../readme/DESCRIPTION.rst | 22 + survey_xlsx_extra_fields/report/__init__.py | 1 + .../report/report_survey_xlsx.py | 15 + .../static/description/index.html | 442 +++++++++++++++++ survey_xlsx_extra_fields/tests/__init__.py | 1 + survey_xlsx_extra_fields/tests/test_report.py | 55 +++ 21 files changed, 1507 insertions(+) create mode 100644 survey_xlsx_expand_multiple_choice/README.rst create mode 100644 survey_xlsx_expand_multiple_choice/__init__.py create mode 100644 survey_xlsx_expand_multiple_choice/__manifest__.py create mode 100644 survey_xlsx_expand_multiple_choice/readme/CONTRIBUTORS.rst create mode 100644 survey_xlsx_expand_multiple_choice/readme/DESCRIPTION.rst create mode 100644 survey_xlsx_expand_multiple_choice/readme/USAGE.rst create mode 100644 survey_xlsx_expand_multiple_choice/report/__init__.py create mode 100644 survey_xlsx_expand_multiple_choice/report/report_survey_xlsx.py create mode 100644 survey_xlsx_expand_multiple_choice/static/description/index.html create mode 100644 survey_xlsx_expand_multiple_choice/tests/__init__.py create mode 100644 survey_xlsx_expand_multiple_choice/tests/test_report.py create mode 100644 survey_xlsx_extra_fields/README.rst create mode 100644 survey_xlsx_extra_fields/__init__.py create mode 100644 survey_xlsx_extra_fields/__manifest__.py create mode 100644 survey_xlsx_extra_fields/readme/CONTRIBUTORS.rst create mode 100644 survey_xlsx_extra_fields/readme/DESCRIPTION.rst create mode 100644 survey_xlsx_extra_fields/report/__init__.py create mode 100644 survey_xlsx_extra_fields/report/report_survey_xlsx.py create mode 100644 survey_xlsx_extra_fields/static/description/index.html create mode 100644 survey_xlsx_extra_fields/tests/__init__.py create mode 100644 survey_xlsx_extra_fields/tests/test_report.py diff --git a/survey_xlsx_expand_multiple_choice/README.rst b/survey_xlsx_expand_multiple_choice/README.rst new file mode 100644 index 0000000..217bc0b --- /dev/null +++ b/survey_xlsx_expand_multiple_choice/README.rst @@ -0,0 +1,109 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +==================================== +Survey XLSX - Expand Multiple Choice +==================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:2ec53fabb2863ebd536f204a7cb4fa4833a634a5711a9e325ed64f50a4c3c4b6 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/gitea-Elabore%2Fsurvey--tools-lightgray.png + :target: https://git.elabore.coop/Elabore/survey-tools/tree/18.0/survey_xlsx_expand_multiple_choice + :alt: Elabore/survey-tools + +|badge1| |badge2| |badge3| + +This module improves the **Survey Results XLSX export** provided by +``survey_xlsx`` for questions that can hold several answers. + +By default such questions are exported as a single column containing every +selected value joined together, which is hard to analyse in a spreadsheet. +This module splits them into dedicated columns: + +* **Multiple choice** questions (*multiple answers allowed*): one column per + possible answer, with ``Oui`` / ``Non`` as value. +* **Matrix** questions: one column per matrix row, with the selected option + as value. + +.. warning:: + + This module relies on report extension hooks that are **not part of the + standard** ``survey_xlsx`` yet. They are introduced by this pull request: + + https://github.com/elabore-coop/survey/pull/1 + + You must run a ``survey_xlsx`` that includes these hooks (the PR branch, + until it is merged upstream). Installed against a plain ``survey_xlsx``, + this module installs without error but the export **silently falls back** + to the default one-column-per-question behaviour. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +Export the results of a survey as usual: + +#. Go to *Surveys* and open a survey. +#. Print the *Survey Results XLSX* report. + +In the generated spreadsheet: + +* A multiple choice question ``Favorite colors`` with options *Red*, *Green* + and *Blue* produces three columns ``Favorite colors / Red``, + ``Favorite colors / Green`` and ``Favorite colors / Blue``, each containing + ``Oui`` or ``Non``. +* A matrix question ``Satisfaction`` with rows *Dashboards* and *Customer + relationship* produces two columns ``Satisfaction / Dashboards`` and + ``Satisfaction / Customer relationship``, each containing the selected + option (e.g. *Not satisfied at all*). + +Other question types keep their standard single-column export. + +Bug Tracker +=========== + +Bugs are tracked on `Gitea Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Elabore + +Contributors +~~~~~~~~~~~~ + +* `Elabore `_ + + * Quentin Mondot + +Maintainers +~~~~~~~~~~~ + +This module is part of the `Elabore/survey-tools `_ project on git.elabore.coop. + +You are welcome to contribute. diff --git a/survey_xlsx_expand_multiple_choice/__init__.py b/survey_xlsx_expand_multiple_choice/__init__.py new file mode 100644 index 0000000..1806b02 --- /dev/null +++ b/survey_xlsx_expand_multiple_choice/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2025 Elabore +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import report diff --git a/survey_xlsx_expand_multiple_choice/__manifest__.py b/survey_xlsx_expand_multiple_choice/__manifest__.py new file mode 100644 index 0000000..0787694 --- /dev/null +++ b/survey_xlsx_expand_multiple_choice/__manifest__.py @@ -0,0 +1,16 @@ +# Copyright 2025 Elabore +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Survey XLSX - Expand Multiple Choice", + "summary": """ + Expands multiple_choice questions into one Oui/Non column per option, and + matrix questions into one column per row (value = selected option)""", + "version": "18.0.1.0.0", + "license": "AGPL-3", + "installable": True, + "application": False, + "author": "Elabore", + "website": "https://elabore.coop", + "depends": ["survey_xlsx"], # WARNING : besoin des hooks créés dans cette PR pour fonctionner : https://github.com/elabore-coop/survey/pull/1 +} diff --git a/survey_xlsx_expand_multiple_choice/readme/CONTRIBUTORS.rst b/survey_xlsx_expand_multiple_choice/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..5d7b7bd --- /dev/null +++ b/survey_xlsx_expand_multiple_choice/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Elabore `_ + + * Quentin Mondot diff --git a/survey_xlsx_expand_multiple_choice/readme/DESCRIPTION.rst b/survey_xlsx_expand_multiple_choice/readme/DESCRIPTION.rst new file mode 100644 index 0000000..cb4f2fd --- /dev/null +++ b/survey_xlsx_expand_multiple_choice/readme/DESCRIPTION.rst @@ -0,0 +1,23 @@ +This module improves the **Survey Results XLSX export** provided by +``survey_xlsx`` for questions that can hold several answers. + +By default such questions are exported as a single column containing every +selected value joined together, which is hard to analyse in a spreadsheet. +This module splits them into dedicated columns: + +* **Multiple choice** questions (*multiple answers allowed*): one column per + possible answer, with ``Oui`` / ``Non`` as value. +* **Matrix** questions: one column per matrix row, with the selected option + as value. + +.. warning:: + + This module relies on report extension hooks that are **not part of the + standard** ``survey_xlsx`` yet. They are introduced by this pull request: + + https://github.com/elabore-coop/survey/pull/1 + + You must run a ``survey_xlsx`` that includes these hooks (the PR branch, + until it is merged upstream). Installed against a plain ``survey_xlsx``, + this module installs without error but the export **silently falls back** + to the default one-column-per-question behaviour. diff --git a/survey_xlsx_expand_multiple_choice/readme/USAGE.rst b/survey_xlsx_expand_multiple_choice/readme/USAGE.rst new file mode 100644 index 0000000..aca0cc6 --- /dev/null +++ b/survey_xlsx_expand_multiple_choice/readme/USAGE.rst @@ -0,0 +1,17 @@ +Export the results of a survey as usual: + +#. Go to *Surveys* and open a survey. +#. Print the *Survey Results XLSX* report. + +In the generated spreadsheet: + +* A multiple choice question ``Favorite colors`` with options *Red*, *Green* + and *Blue* produces three columns ``Favorite colors / Red``, + ``Favorite colors / Green`` and ``Favorite colors / Blue``, each containing + ``Oui`` or ``Non``. +* A matrix question ``Satisfaction`` with rows *Dashboards* and *Customer + relationship* produces two columns ``Satisfaction / Dashboards`` and + ``Satisfaction / Customer relationship``, each containing the selected + option (e.g. *Not satisfied at all*). + +Other question types keep their standard single-column export. diff --git a/survey_xlsx_expand_multiple_choice/report/__init__.py b/survey_xlsx_expand_multiple_choice/report/__init__.py new file mode 100644 index 0000000..fa0a90c --- /dev/null +++ b/survey_xlsx_expand_multiple_choice/report/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2025 Elabore +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import report_survey_xlsx diff --git a/survey_xlsx_expand_multiple_choice/report/report_survey_xlsx.py b/survey_xlsx_expand_multiple_choice/report/report_survey_xlsx.py new file mode 100644 index 0000000..65b534d --- /dev/null +++ b/survey_xlsx_expand_multiple_choice/report/report_survey_xlsx.py @@ -0,0 +1,70 @@ +# Copyright 2025 Elabore +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import models + + +class ReportSurveyXlsx(models.AbstractModel): + _inherit = "report.survey.xlsx" + + def _write_question_header(self, sheet, question, cols, bold): + if question.question_type == "multiple_choice": + for answer in question.suggested_answer_ids: + col_key = f"question_{question.id}_answer_{answer.id}" + sheet.write( + 0, + cols[col_key], + f"{question.title} / {answer.value}", + bold, + ) + return + if question.question_type == "matrix": + for row in question.matrix_row_ids: + col_key = f"question_{question.id}_row_{row.id}" + sheet.write( + 0, + cols[col_key], + f"{question.title} / {row.value}", + bold, + ) + return + return super()._write_question_header(sheet, question, cols, bold) + + def _process_user_answer(self, data, user_input_id, user_answer, cols): + question = user_answer.question_id + if question.question_type == "multiple_choice": + if user_answer.skipped: + return + col_key = f"question_{question.id}_answer_{user_answer.suggested_answer_id.id}" + if col_key not in cols: + return + data[user_input_id][cols[col_key]] = ["Oui"] + return + if question.question_type == "matrix": + if user_answer.skipped: + return + col_key = f"question_{question.id}_row_{user_answer.matrix_row_id.id}" + if col_key not in cols: + return + data[user_input_id][cols[col_key]].append( + user_answer.suggested_answer_id.value + ) + return + return super()._process_user_answer(data, user_input_id, user_answer, cols) + + def _post_process_user_input(self, data, user_input, cols): + super()._post_process_user_input(data, user_input, cols) + answered_mc = set() + for line in user_input.user_input_line_ids: + if line.question_id.question_type == "multiple_choice" and not line.skipped: + answered_mc.add(line.question_id.id) + for question in user_input.survey_id.question_ids: + if question.question_type != "multiple_choice": + continue + if question.id not in answered_mc: + continue + for answer in question.suggested_answer_ids: + col_key = f"question_{question.id}_answer_{answer.id}" + if col_key in cols: + col_idx = cols[col_key] + if col_idx not in data[user_input.id]: + data[user_input.id][col_idx] = ["Non"] diff --git a/survey_xlsx_expand_multiple_choice/static/description/index.html b/survey_xlsx_expand_multiple_choice/static/description/index.html new file mode 100644 index 0000000..791ee2b --- /dev/null +++ b/survey_xlsx_expand_multiple_choice/static/description/index.html @@ -0,0 +1,466 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Survey XLSX - Expand Multiple Choice

+ +

Beta License: AGPL-3 elabore-coop/survey-tools

+

This module improves the Survey Results XLSX export provided by +survey_xlsx for questions that can hold several answers.

+

By default such questions are exported as a single column containing every +selected value joined together, which is hard to analyse in a spreadsheet. +This module splits them into dedicated columns:

+
    +
  • Multiple choice questions (multiple answers allowed): one column per +possible answer, with Oui / Non as value.
  • +
  • Matrix questions: one column per matrix row, with the selected option +as value.
  • +
+
+

Warning

+

This module relies on report extension hooks that are not part of the +standard survey_xlsx yet. They are introduced by this pull request:

+

https://github.com/elabore-coop/survey/pull/1

+

You must run a survey_xlsx that includes these hooks (the PR branch, +until it is merged upstream). Installed against a plain survey_xlsx, +this module installs without error but the export silently falls back +to the default one-column-per-question behaviour.

+
+

Table of contents

+ +
+

Usage

+

Export the results of a survey as usual:

+
    +
  1. Go to Surveys and open a survey.
  2. +
  3. Print the Survey Results XLSX report.
  4. +
+

In the generated spreadsheet:

+
    +
  • A multiple choice question Favorite colors with options Red, Green +and Blue produces three columns Favorite colors / Red, +Favorite colors / Green and Favorite colors / Blue, each containing +Oui or Non.
  • +
  • A matrix question Satisfaction with rows Dashboards and Customer +relationship produces two columns Satisfaction / Dashboards and +Satisfaction / Customer relationship, each containing the selected +option (e.g. Not satisfied at all).
  • +
+

Other question types keep their standard single-column export.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Elabore
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is part of the elabore-coop/survey-tools project on GitHub.

+

You are welcome to contribute.

+
+
+
+
+ + diff --git a/survey_xlsx_expand_multiple_choice/tests/__init__.py b/survey_xlsx_expand_multiple_choice/tests/__init__.py new file mode 100644 index 0000000..5c5c97a --- /dev/null +++ b/survey_xlsx_expand_multiple_choice/tests/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2025 Elabore +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import test_report diff --git a/survey_xlsx_expand_multiple_choice/tests/test_report.py b/survey_xlsx_expand_multiple_choice/tests/test_report.py new file mode 100644 index 0000000..512a874 --- /dev/null +++ b/survey_xlsx_expand_multiple_choice/tests/test_report.py @@ -0,0 +1,148 @@ +# Copyright 2025 Elabore +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import io + +import openpyxl + +from odoo.addons.survey.tests import common + + +class TestExpandMultipleChoice(common.TestSurveyCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.mc_question = cls._add_question( + cls, + page=cls.page_0, + name="Favorite colors", + qtype="multiple_choice", + labels=[ + {"value": "Red"}, + {"value": "Green"}, + {"value": "Blue"}, + ], + survey_id=cls.survey.id, + sequence=10, + ) + cls.answer_red = cls.mc_question.suggested_answer_ids[0] + cls.answer_green = cls.mc_question.suggested_answer_ids[1] + cls.answer_blue = cls.mc_question.suggested_answer_ids[2] + + # Input 1: selects Red and Green + input1 = cls._add_answer(cls, cls.survey, cls.survey_manager.partner_id) + cls._add_answer_line(cls, cls.mc_question, input1, cls.answer_red.id) + cls._add_answer_line(cls, cls.mc_question, input1, cls.answer_green.id) + input1._mark_done() + + # Input 2: selects Blue only + input2 = cls._add_answer(cls, cls.survey, False, email="test2@example.com") + cls._add_answer_line(cls, cls.mc_question, input2, cls.answer_blue.id) + input2._mark_done() + + # Matrix question (one choice per row): satisfaction grid + cls.matrix_question = cls._add_question( + cls, + page=cls.page_0, + name="Satisfaction", + qtype="matrix", + matrix_subtype="simple", + labels=[ + {"value": "Pas du tout satisfait"}, + {"value": "Satisfait"}, + ], + labels_2=[ + {"value": "Tableaux de bord"}, + {"value": "Relation client"}, + ], + survey_id=cls.survey.id, + sequence=20, + ) + cls.col_unhappy = cls.matrix_question.suggested_answer_ids[0] + cls.col_happy = cls.matrix_question.suggested_answer_ids[1] + cls.row_dashboard = cls.matrix_question.matrix_row_ids[0] + cls.row_client = cls.matrix_question.matrix_row_ids[1] + + # input1 answers the matrix: dashboard -> unhappy, client -> happy + cls._add_answer_line( + cls, + cls.matrix_question, + input1, + cls.col_unhappy.id, + answer_value_row=cls.row_dashboard.id, + ) + cls._add_answer_line( + cls, + cls.matrix_question, + input1, + cls.col_happy.id, + answer_value_row=cls.row_client.id, + ) + + def _get_sheet(self): + report = self.env.ref("survey_xlsx.report_survey_xlsx") + rep = self.env["ir.actions.report"]._render(report, self.survey.ids, {}) + wb = openpyxl.load_workbook(io.BytesIO(rep[0])) + return wb.worksheets[0] + + def _find_col(self, sheet, header): + for col in range(1, sheet.max_column + 1): + if sheet.cell(1, col).value == header: + return col + return None + + def test_headers(self): + sheet = self._get_sheet() + self.assertIsNotNone( + self._find_col(sheet, "Favorite colors / Red") + ) + self.assertIsNotNone( + self._find_col(sheet, "Favorite colors / Green") + ) + self.assertIsNotNone( + self._find_col(sheet, "Favorite colors / Blue") + ) + + def test_values(self): + sheet = self._get_sheet() + col_red = self._find_col(sheet, "Favorite colors / Red") + col_green = self._find_col(sheet, "Favorite colors / Green") + col_blue = self._find_col(sheet, "Favorite colors / Blue") + + # Collect rows by (red, green, blue) values + rows = set() + for row in range(2, sheet.max_row + 1): + rows.add(( + sheet.cell(row, col_red).value, + sheet.cell(row, col_green).value, + sheet.cell(row, col_blue).value, + )) + + self.assertIn(("Oui", "Oui", "Non"), rows) + self.assertIn(("Non", "Non", "Oui"), rows) + + def test_matrix_headers(self): + sheet = self._get_sheet() + self.assertIsNotNone( + self._find_col(sheet, "Satisfaction / Tableaux de bord") + ) + self.assertIsNotNone( + self._find_col(sheet, "Satisfaction / Relation client") + ) + # No per-column expansion for matrices + self.assertIsNone( + self._find_col(sheet, "Satisfaction / Pas du tout satisfait") + ) + + def test_matrix_values(self): + sheet = self._get_sheet() + col_dashboard = self._find_col(sheet, "Satisfaction / Tableaux de bord") + col_client = self._find_col(sheet, "Satisfaction / Relation client") + + values = set() + for row in range(2, sheet.max_row + 1): + values.add(( + sheet.cell(row, col_dashboard).value, + sheet.cell(row, col_client).value, + )) + + self.assertIn(("Pas du tout satisfait", "Satisfait"), values) diff --git a/survey_xlsx_extra_fields/README.rst b/survey_xlsx_extra_fields/README.rst new file mode 100644 index 0000000..ccb43f8 --- /dev/null +++ b/survey_xlsx_extra_fields/README.rst @@ -0,0 +1,87 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +================================= +Survey XLSX - Extra Fields Bridge +================================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:8baa47af03e5b4b4e70ca6db224a5ac3e73aa66f287c42053a9a8f631efd10c2 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/gitea-Elabore%2Fsurvey--tools-lightgray.png + :target: https://git.elabore.coop/Elabore/survey-tools/tree/18.0/survey_xlsx_extra_fields + :alt: Elabore/survey-tools + +|badge1| |badge2| |badge3| + +This is a **bridge module** between ``survey_xlsx_expand_multiple_choice`` +and ``survey_extra_fields``. + +``survey_extra_fields`` adds a *File* question type, whose answers are +uploaded attachments that cannot be represented in a spreadsheet cell. This +module excludes those *File* questions from the **Survey Results XLSX +export**: they get no column at all, instead of an unusable one. + +It installs automatically (``auto_install``) as soon as both +``survey_xlsx_expand_multiple_choice`` and ``survey_extra_fields`` are +installed, and is uninstalled when either of them is removed. There is +nothing to configure. + +.. warning:: + + The exclusion is implemented through the report extension hooks provided + by ``survey_xlsx_expand_multiple_choice``, which itself relies on hooks + added to ``survey_xlsx`` by this pull request: + + https://github.com/elabore-coop/survey/pull/1 + + Without those hooks, *File* questions are not filtered out of the export. + +**Table of contents** + +.. contents:: + :local: + +Bug Tracker +=========== + +Bugs are tracked on `Gitea Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Elabore + +Contributors +~~~~~~~~~~~~ + +* `Elabore `_ + + * Quentin Mondot + +Maintainers +~~~~~~~~~~~ + +This module is part of the `Elabore/survey-tools `_ project on git.elabore.coop. + +You are welcome to contribute. diff --git a/survey_xlsx_extra_fields/__init__.py b/survey_xlsx_extra_fields/__init__.py new file mode 100644 index 0000000..4c4f242 --- /dev/null +++ b/survey_xlsx_extra_fields/__init__.py @@ -0,0 +1 @@ +from . import report diff --git a/survey_xlsx_extra_fields/__manifest__.py b/survey_xlsx_extra_fields/__manifest__.py new file mode 100644 index 0000000..9c7bcea --- /dev/null +++ b/survey_xlsx_extra_fields/__manifest__.py @@ -0,0 +1,19 @@ +# Copyright 2025 Elabore +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Survey XLSX - Extra Fields Bridge", + "summary": """ + Excludes 'file' question types from the survey XLSX export""", + "version": "18.0.1.0.0", + "license": "AGPL-3", + "installable": True, + "application": False, + "auto_install": True, + "author": "Elabore", + "website": "https://elabore.coop", + "depends": [ + "survey_xlsx_expand_multiple_choice", + "survey_extra_fields", + ], +} diff --git a/survey_xlsx_extra_fields/readme/CONTRIBUTORS.rst b/survey_xlsx_extra_fields/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..5d7b7bd --- /dev/null +++ b/survey_xlsx_extra_fields/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Elabore `_ + + * Quentin Mondot diff --git a/survey_xlsx_extra_fields/readme/DESCRIPTION.rst b/survey_xlsx_extra_fields/readme/DESCRIPTION.rst new file mode 100644 index 0000000..e286d23 --- /dev/null +++ b/survey_xlsx_extra_fields/readme/DESCRIPTION.rst @@ -0,0 +1,22 @@ +This is a **bridge module** between ``survey_xlsx_expand_multiple_choice`` +and ``survey_extra_fields``. + +``survey_extra_fields`` adds a *File* question type, whose answers are +uploaded attachments that cannot be represented in a spreadsheet cell. This +module excludes those *File* questions from the **Survey Results XLSX +export**: they get no column at all, instead of an unusable one. + +It installs automatically (``auto_install``) as soon as both +``survey_xlsx_expand_multiple_choice`` and ``survey_extra_fields`` are +installed, and is uninstalled when either of them is removed. There is +nothing to configure. + +.. warning:: + + The exclusion is implemented through the report extension hooks provided + by ``survey_xlsx_expand_multiple_choice``, which itself relies on hooks + added to ``survey_xlsx`` by this pull request: + + https://github.com/elabore-coop/survey/pull/1 + + Without those hooks, *File* questions are not filtered out of the export. diff --git a/survey_xlsx_extra_fields/report/__init__.py b/survey_xlsx_extra_fields/report/__init__.py new file mode 100644 index 0000000..2a41f6f --- /dev/null +++ b/survey_xlsx_extra_fields/report/__init__.py @@ -0,0 +1 @@ +from . import report_survey_xlsx diff --git a/survey_xlsx_extra_fields/report/report_survey_xlsx.py b/survey_xlsx_extra_fields/report/report_survey_xlsx.py new file mode 100644 index 0000000..5c99bae --- /dev/null +++ b/survey_xlsx_extra_fields/report/report_survey_xlsx.py @@ -0,0 +1,15 @@ +# Copyright 2025 Elabore +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import models + + +class ReportSurveyXlsx(models.AbstractModel): + _inherit = "report.survey.xlsx" + + def _write_question_header(self, sheet, question, cols, bold): + # "file" questions store uploaded attachments that cannot be rendered + # in a spreadsheet cell: skip them so no column is created. Without a + # column, _process_user_answer ignores their answers automatically. + if question.question_type == "file": + return + return super()._write_question_header(sheet, question, cols, bold) diff --git a/survey_xlsx_extra_fields/static/description/index.html b/survey_xlsx_extra_fields/static/description/index.html new file mode 100644 index 0000000..01b4b98 --- /dev/null +++ b/survey_xlsx_extra_fields/static/description/index.html @@ -0,0 +1,442 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Survey XLSX - Extra Fields Bridge

+ +

Beta License: AGPL-3 elabore-coop/survey-tools

+

This is a bridge module between survey_xlsx_expand_multiple_choice +and survey_extra_fields.

+

survey_extra_fields adds a File question type, whose answers are +uploaded attachments that cannot be represented in a spreadsheet cell. This +module excludes those File questions from the Survey Results XLSX +export: they get no column at all, instead of an unusable one.

+

It installs automatically (auto_install) as soon as both +survey_xlsx_expand_multiple_choice and survey_extra_fields are +installed, and is uninstalled when either of them is removed. There is +nothing to configure.

+
+

Warning

+

The exclusion is implemented through the report extension hooks provided +by survey_xlsx_expand_multiple_choice, which itself relies on hooks +added to survey_xlsx by this pull request:

+

https://github.com/elabore-coop/survey/pull/1

+

Without those hooks, File questions are not filtered out of the export.

+
+

Table of contents

+ +
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Elabore
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is part of the elabore-coop/survey-tools project on GitHub.

+

You are welcome to contribute.

+
+
+
+
+ + diff --git a/survey_xlsx_extra_fields/tests/__init__.py b/survey_xlsx_extra_fields/tests/__init__.py new file mode 100644 index 0000000..32ae3c2 --- /dev/null +++ b/survey_xlsx_extra_fields/tests/__init__.py @@ -0,0 +1 @@ +from . import test_report diff --git a/survey_xlsx_extra_fields/tests/test_report.py b/survey_xlsx_extra_fields/tests/test_report.py new file mode 100644 index 0000000..187e704 --- /dev/null +++ b/survey_xlsx_extra_fields/tests/test_report.py @@ -0,0 +1,55 @@ +# Copyright 2025 Elabore +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import io + +import openpyxl + +from odoo.addons.survey.tests import common + + +class TestExcludeFileQuestion(common.TestSurveyCommon): + def setUp(self): + super().setUp() + self.text_question = self._add_question( + self.page_0, + "Your name", + "char_box", + survey_id=self.survey.id, + ) + self.file_question = self._add_question( + self.page_0, + "Upload your document", + "file", + constr_mandatory=False, + survey_id=self.survey.id, + ) + answer = self._add_answer(self.survey, False, email="test@example.com") + self._add_answer_line(self.text_question, answer, "Alice") + self.env["survey.user_input.line"].create({ + "user_input_id": answer.id, + "question_id": self.file_question.id, + "answer_type": "file", + "skipped": False, + "value_file": "ZmFrZQ==", + "value_file_fname": "doc.pdf", + }) + answer._mark_done() + + def _get_sheet(self): + report = self.env.ref("survey_xlsx.report_survey_xlsx") + rep = self.env["ir.actions.report"]._render(report, self.survey.ids, {}) + wb = openpyxl.load_workbook(io.BytesIO(rep[0])) + return wb.worksheets[0] + + def _find_col(self, sheet, header): + for col in range(1, sheet.max_column + 1): + if sheet.cell(1, col).value == header: + return col + return None + + def test_file_question_excluded(self): + sheet = self._get_sheet() + # Regular questions are still exported + self.assertIsNotNone(self._find_col(sheet, "Your name")) + # File questions get no column at all + self.assertIsNone(self._find_col(sheet, "Upload your document"))