diff --git a/survey_xlsx/README.rst b/survey_xlsx/README.rst new file mode 100644 index 00000000..c5cb4072 --- /dev/null +++ b/survey_xlsx/README.rst @@ -0,0 +1,76 @@ +=========== +Survey XLSX +=========== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:0647ba4a4a0c3a3dc8f4e7b281d9bc6482d39efc59b2da832241080c29d611ad + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/licence-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/github-OCA%2Fsurvey-lightgray.png?logo=github + :target: https://github.com/OCA/survey/tree/17.0/survey_xlsx + :alt: OCA/survey +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/survey-17-0/survey-17-0-survey_xlsx + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/survey&target_branch=17.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows to print a XLSX report with the survey results. + +**Table of contents** + +.. contents:: + :local: + +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 +------- + +* Creu Blanca + +Contributors +------------ + +- Olga Marco + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/survey `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/survey_xlsx/__init__.py b/survey_xlsx/__init__.py new file mode 100644 index 00000000..59b4cb50 --- /dev/null +++ b/survey_xlsx/__init__.py @@ -0,0 +1,2 @@ +from . import report +from . import models diff --git a/survey_xlsx/__manifest__.py b/survey_xlsx/__manifest__.py new file mode 100644 index 00000000..1b24e5f8 --- /dev/null +++ b/survey_xlsx/__manifest__.py @@ -0,0 +1,16 @@ +# Copyright 2022 CreuBlanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Survey XLSX", + "summary": """ + XLSX Report to show the survey results""", + "version": "17.0.1.0.0", + "license": "AGPL-3", + "installable": True, + "application": False, + "author": "Creu Blanca, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/survey", + "depends": ["survey", "report_xlsx"], + "data": ["report/report_survey_xlsx.xml"], +} diff --git a/survey_xlsx/i18n/it.po b/survey_xlsx/i18n/it.po new file mode 100644 index 00000000..4fa3f865 --- /dev/null +++ b/survey_xlsx/i18n/it.po @@ -0,0 +1,55 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * survey_xlsx +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-10-29 06:42+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: survey_xlsx +#. odoo-python +#: code:addons/survey_xlsx/report/report_survey_xlsx.py:0 +#, python-format +msgid "Created on" +msgstr "Creato il" + +#. module: survey_xlsx +#. odoo-python +#: code:addons/survey_xlsx/report/report_survey_xlsx.py:0 +#, python-format +msgid "Partner" +msgstr "Partner" + +#. module: survey_xlsx +#: model:ir.model,name:survey_xlsx.model_survey_user_input_line +msgid "Survey User Input Line" +msgstr "Riga risposta utente al sondaggio" + +#. module: survey_xlsx +#: model:ir.actions.report,name:survey_xlsx.report_survey_xlsx +msgid "XLSX Report Survey" +msgstr "Resoconto sondaggio XLSX" + +#. module: survey_xlsx +#: model:ir.model,name:survey_xlsx.model_report_survey_xlsx +msgid "XLSX Report to show all the results for the survey" +msgstr "Resoconto XLSX per visualizzare tutti i risultati del sondaggio" + +#~ msgid "Display Name" +#~ msgstr "Nome visualizzato" + +#~ msgid "ID" +#~ msgstr "ID" + +#~ msgid "Last Modified on" +#~ msgstr "Ultima modifica il" diff --git a/survey_xlsx/i18n/survey_xlsx.pot b/survey_xlsx/i18n/survey_xlsx.pot new file mode 100644 index 00000000..20e520a8 --- /dev/null +++ b/survey_xlsx/i18n/survey_xlsx.pot @@ -0,0 +1,43 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * survey_xlsx +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: survey_xlsx +#. odoo-python +#: code:addons/survey_xlsx/report/report_survey_xlsx.py:0 +#, python-format +msgid "Created on" +msgstr "" + +#. module: survey_xlsx +#. odoo-python +#: code:addons/survey_xlsx/report/report_survey_xlsx.py:0 +#, python-format +msgid "Partner" +msgstr "" + +#. module: survey_xlsx +#: model:ir.model,name:survey_xlsx.model_survey_user_input_line +msgid "Survey User Input Line" +msgstr "" + +#. module: survey_xlsx +#: model:ir.actions.report,name:survey_xlsx.report_survey_xlsx +msgid "XLSX Report Survey" +msgstr "" + +#. module: survey_xlsx +#: model:ir.model,name:survey_xlsx.model_report_survey_xlsx +msgid "XLSX Report to show all the results for the survey" +msgstr "" diff --git a/survey_xlsx/models/__init__.py b/survey_xlsx/models/__init__.py new file mode 100644 index 00000000..7212aae9 --- /dev/null +++ b/survey_xlsx/models/__init__.py @@ -0,0 +1 @@ +from . import survey_user_input_line diff --git a/survey_xlsx/models/survey_user_input_line.py b/survey_xlsx/models/survey_user_input_line.py new file mode 100644 index 00000000..2e83a9f2 --- /dev/null +++ b/survey_xlsx/models/survey_user_input_line.py @@ -0,0 +1,14 @@ +# Copyright 2022 CreuBlanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import models + + +class SurveyUserInputLine(models.Model): + _inherit = "survey.user_input.line" + + def _get_xlsx_value(self): + if self.answer_type == "suggestion": + return self.suggested_answer_id.value + if self.answer_type == "date": + return self.value_date.isoformat() + return self[f"value_{self.answer_type}"] diff --git a/survey_xlsx/pyproject.toml b/survey_xlsx/pyproject.toml new file mode 100644 index 00000000..4231d0cc --- /dev/null +++ b/survey_xlsx/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/survey_xlsx/readme/CONTRIBUTORS.md b/survey_xlsx/readme/CONTRIBUTORS.md new file mode 100644 index 00000000..63aaadf3 --- /dev/null +++ b/survey_xlsx/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Olga Marco \ diff --git a/survey_xlsx/readme/DESCRIPTION.md b/survey_xlsx/readme/DESCRIPTION.md new file mode 100644 index 00000000..3c5e3770 --- /dev/null +++ b/survey_xlsx/readme/DESCRIPTION.md @@ -0,0 +1 @@ +This module allows to print a XLSX report with the survey results. diff --git a/survey_xlsx/report/__init__.py b/survey_xlsx/report/__init__.py new file mode 100644 index 00000000..2a41f6fc --- /dev/null +++ b/survey_xlsx/report/__init__.py @@ -0,0 +1 @@ +from . import report_survey_xlsx diff --git a/survey_xlsx/report/report_survey_xlsx.py b/survey_xlsx/report/report_survey_xlsx.py new file mode 100644 index 00000000..d594d285 --- /dev/null +++ b/survey_xlsx/report/report_survey_xlsx.py @@ -0,0 +1,88 @@ +# Copyright 2022 CreuBlanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import datetime +from collections import defaultdict + +from odoo import _, models + + +class Iterator: + def __init__(self, value=0): + self.value = value + + def next(self): + self.value += 1 + return self.value + + +class ReportSurveyXlsx(models.AbstractModel): + _name = "report.survey.xlsx" + _inherit = "report.report_xlsx.abstract" + _description = "XLSX Report to show all the results for the survey" + + def _pre_generate_xlsx_report_header(self, sheet, results, cols, bold): + # Hook for adding some extra headers at the beginning + sheet.write(0, cols["partner_id"], _("Partner"), bold) + sheet.write(0, cols["create_date"], _("Created on"), bold) + + def _post_generate_xlsx_report_header(self, sheet, results, cols, bold): + # Hook for adding some extra headers at the end + pass + + def _add_extra_data(self, user_input_data, user_input, cols): + # Hook for adding extra data if needed + for fieldname, col in cols.items(): + if fieldname in user_input._fields: + if not isinstance(user_input[fieldname], models.Model): + user_input_data[col] = [user_input[fieldname]] + elif user_input[fieldname]: + user_input_data[col] = [user_input[fieldname].display_name] + + def generate_xlsx_report(self, workbook, data, results): + n_cols = Iterator(-1) + sheet = workbook.add_worksheet("Survey Results") + bold = workbook.add_format({"bold": True}) + no_bold = workbook.add_format({"bold": False}) + cols = defaultdict(n_cols.next) + data = defaultdict(lambda: defaultdict(list)) + self._pre_generate_xlsx_report_header(sheet, results, cols, bold) + # One column by question + for question in results.question_ids: + sheet.write(0, cols[f"question_{question.id}"], question.title, bold) + self._post_generate_xlsx_report_header(sheet, results, n_cols, bold) + user_inputs = self.env["survey.user_input"].search( + self._get_input_domain(results) + ) + for user_input in user_inputs: + self._add_extra_data(data[user_input.id], user_input, cols) + for user_answer in user_input.user_input_line_ids: + question_id = f"question_{user_answer.question_id.id}" + if question_id not in cols or user_answer.skipped: + # We should ignore old removed questions + continue + data[user_input.id][cols[question_id]].append( + user_answer._get_xlsx_value() + ) + row = 0 + for answer_data in data.values(): + row += 1 + for col_id, answer_vals in answer_data.items(): + if isinstance(answer_vals[0], datetime.datetime): + date = answer_vals[0].date() + answer_vals[0] = date.isoformat() + result = ( + answer_vals[0] if len(answer_vals) == 1 else ", ".join(answer_vals) + ) + sheet.write( + row, + col_id, + result, + no_bold, + ) + + def _get_input_domain(self, results): + return [ + ("survey_id", "=", results.id), + ("test_entry", "=", False), + ("state", "=", "done"), + ] diff --git a/survey_xlsx/report/report_survey_xlsx.xml b/survey_xlsx/report/report_survey_xlsx.xml new file mode 100644 index 00000000..3099eef2 --- /dev/null +++ b/survey_xlsx/report/report_survey_xlsx.xml @@ -0,0 +1,14 @@ + + + + XLSX Report Survey + survey.survey + ir.actions.report + xlsx + survey.xlsx + survey + False + + report + + diff --git a/survey_xlsx/static/description/icon.png b/survey_xlsx/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/survey_xlsx/static/description/icon.png differ diff --git a/survey_xlsx/static/description/index.html b/survey_xlsx/static/description/index.html new file mode 100644 index 00000000..27ad0a2c --- /dev/null +++ b/survey_xlsx/static/description/index.html @@ -0,0 +1,423 @@ + + + + + +Survey XLSX + + + +
+

Survey XLSX

+ + +

Beta License: AGPL-3 OCA/survey Translate me on Weblate Try me on Runboat

+

This module allows to print a XLSX report with the survey results.

+

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

+
    +
  • Creu Blanca
  • +
+
+ +
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/survey project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/survey_xlsx/tests/__init__.py b/survey_xlsx/tests/__init__.py new file mode 100644 index 00000000..32ae3c2c --- /dev/null +++ b/survey_xlsx/tests/__init__.py @@ -0,0 +1 @@ +from . import test_report diff --git a/survey_xlsx/tests/test_report.py b/survey_xlsx/tests/test_report.py new file mode 100644 index 00000000..dfb62a26 --- /dev/null +++ b/survey_xlsx/tests/test_report.py @@ -0,0 +1,88 @@ +# Copyright 2022 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import io +import logging +from datetime import date + +import freezegun +import openpyxl + +from odoo.addons.survey.tests import common + +_logger = logging.getLogger(__name__) + + + + +@freezegun.freeze_time("2022-04-26") +class TestReport(common.TestSurveyCommon): + def setUp(self): + super().setUp() + self.question_date = ( + self.env["survey.question"] + .with_user(self.survey_manager) + .create( + { + "title": "Test Date", + "survey_id": self.survey.id, + "sequence": 4, + "question_type": "date", + } + ) + ) + self.suggested_question = self._add_question( + page=self.question_date.page_id, + name="Test Suggested", + qtype="simple_choice", + labels=[ + {"value": "FIRST", "suggested_answer_id": True}, + {"value": "SECOND"}, + ], + survey_id=self.survey.id, + sequence=5, + ) + + answer = self._add_answer( + self.survey, + self.survey_manager.partner_id, + email=self.survey_manager.partner_id.email, + ) + self._add_answer_line(self.question_ft, answer, "FIRST ANSWER") + self._add_answer_line(self.question_num, answer, 1) + self._add_answer_line(self.question_date, answer, date.today()) + self._add_answer_line( + self.suggested_question, + answer, + self.suggested_question.suggested_answer_ids[0]["id"], + ) + answer._mark_done() + answer2 = self._add_answer(self.survey, False, email="public2@example.com") + self._add_answer_line(self.question_ft, answer2, "SECOND ANSWER") + self._add_answer_line(self.question_num, answer2, 2) + self._add_answer_line( + self.question_date, answer2, False, skipped=True, answer_type=False + ) + self._add_answer_line( + self.suggested_question, + answer2, + self.suggested_question.suggested_answer_ids[1]["id"], + ) + answer2._mark_done() + + def test_report(self): + report = self.env.ref("survey_xlsx.report_survey_xlsx") + self.assertEqual(report.report_type, "xlsx") + rep = self.env["ir.actions.report"]._render(report, self.survey.ids, {}) + + report_file = io.BytesIO(rep[0]) + wb = openpyxl.load_workbook(report_file) + sheet = wb.worksheets[0] + self.assertEqual(sheet.cell(2, 3).value, "FIRST ANSWER") + self.assertEqual(sheet.cell(3, 3).value, "SECOND ANSWER") + self.assertEqual(sheet.cell(2, 4).value, 1) + self.assertEqual(sheet.cell(3, 4).value, 2) + self.assertEqual(sheet.cell(2, 6).value, "2022-04-26") + self.assertFalse(sheet.cell(3, 6).value) + self.assertEqual(sheet.cell(2, 7).value, "FIRST") + self.assertEqual(sheet.cell(3, 7).value, "SECOND")