From 16eac18601f4933141a3b48fe5822f6965a623cf Mon Sep 17 00:00:00 2001 From: ArnauCForgeFlow Date: Thu, 22 Feb 2024 13:48:43 +0100 Subject: [PATCH] [ADD] calendar_import_ics --- calendar_import_ics/README.rst | 98 ++++ calendar_import_ics/__init__.py | 2 + calendar_import_ics/__manifest__.py | 17 + calendar_import_ics/models/__init__.py | 1 + calendar_import_ics/models/calendar_event.py | 9 + calendar_import_ics/readme/CONTRIBUTORS.rst | 4 + calendar_import_ics/readme/DESCRIPTION.rst | 6 + calendar_import_ics/readme/USAGE.rst | 10 + .../security/calendar_import_security.xml | 7 + .../security/ir.model.access.csv | 2 + .../static/description/index.html | 443 ++++++++++++++++++ calendar_import_ics/tests/__init__.py | 1 + .../tests/sample_files/test_calendar.ics | 45 ++ .../tests/sample_files/test_calendar_2.ics | 45 ++ .../tests/test_calendar_import_ics.py | 61 +++ calendar_import_ics/wizards/__init__.py | 1 + .../wizards/wizard_import_ics.py | 89 ++++ .../wizards/wizard_import_ics.xml | 37 ++ .../odoo/addons/calendar_import_ics | 1 + setup/calendar_import_ics/setup.py | 6 + 20 files changed, 885 insertions(+) create mode 100644 calendar_import_ics/README.rst create mode 100644 calendar_import_ics/__init__.py create mode 100644 calendar_import_ics/__manifest__.py create mode 100644 calendar_import_ics/models/__init__.py create mode 100644 calendar_import_ics/models/calendar_event.py create mode 100644 calendar_import_ics/readme/CONTRIBUTORS.rst create mode 100644 calendar_import_ics/readme/DESCRIPTION.rst create mode 100644 calendar_import_ics/readme/USAGE.rst create mode 100644 calendar_import_ics/security/calendar_import_security.xml create mode 100644 calendar_import_ics/security/ir.model.access.csv create mode 100644 calendar_import_ics/static/description/index.html create mode 100644 calendar_import_ics/tests/__init__.py create mode 100644 calendar_import_ics/tests/sample_files/test_calendar.ics create mode 100644 calendar_import_ics/tests/sample_files/test_calendar_2.ics create mode 100644 calendar_import_ics/tests/test_calendar_import_ics.py create mode 100644 calendar_import_ics/wizards/__init__.py create mode 100644 calendar_import_ics/wizards/wizard_import_ics.py create mode 100644 calendar_import_ics/wizards/wizard_import_ics.xml create mode 120000 setup/calendar_import_ics/odoo/addons/calendar_import_ics create mode 100644 setup/calendar_import_ics/setup.py diff --git a/calendar_import_ics/README.rst b/calendar_import_ics/README.rst new file mode 100644 index 00000000..092d436a --- /dev/null +++ b/calendar_import_ics/README.rst @@ -0,0 +1,98 @@ +===================== +Calendar - Import ics +===================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:57021ad288c107b8274bc4277bfad98fec642aa8a268cba05193d665d2ee706b + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Fcalendar-lightgray.png?logo=github + :target: https://github.com/OCA/calendar/tree/15.0/calendar_import_ics + :alt: OCA/calendar +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/calendar-15-0/calendar-15-0-calendar_import_ics + :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/calendar&target_branch=15.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds a new wizard that allows you to import .ics files into odoo calendar, importing events and the following attributes: + +* Summary +* Start Date +* End Date +* UID + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To use this module, follow these steps: + +#. Navigate to the Calendar App. +#. Go to Configuration. +#. Select Import ICS File. + +When importing, you have two options: + +* Specify start and end dates to import events occurring within that range. +* Leave the date fields empty to import all events from the entire file. + +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 +~~~~~~~ + +* ForgeFlow S.L. + +Contributors +~~~~~~~~~~~~ + +* Arnau Cruz + + +Do not contact contributors directly about support or help with technical issues. + +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/calendar `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/calendar_import_ics/__init__.py b/calendar_import_ics/__init__.py new file mode 100644 index 00000000..976591c9 --- /dev/null +++ b/calendar_import_ics/__init__.py @@ -0,0 +1,2 @@ +from . import wizards +from . import models diff --git a/calendar_import_ics/__manifest__.py b/calendar_import_ics/__manifest__.py new file mode 100644 index 00000000..9d1441e7 --- /dev/null +++ b/calendar_import_ics/__manifest__.py @@ -0,0 +1,17 @@ +# Copyright (C) 2024 - ForgeFlow S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "Calendar - Import ics", + "summary": "Allow importing an ics file to our calendar", + "version": "15.0.1.0.0", + "author": "ForgeFlow S.L.,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/calendar", + "license": "AGPL-3", + "depends": ["calendar"], + "data": [ + "security/calendar_import_security.xml", + "security/ir.model.access.csv", + "wizards/wizard_import_ics.xml", + ], +} diff --git a/calendar_import_ics/models/__init__.py b/calendar_import_ics/models/__init__.py new file mode 100644 index 00000000..ba757cbe --- /dev/null +++ b/calendar_import_ics/models/__init__.py @@ -0,0 +1 @@ +from . import calendar_event diff --git a/calendar_import_ics/models/calendar_event.py b/calendar_import_ics/models/calendar_event.py new file mode 100644 index 00000000..d66830e1 --- /dev/null +++ b/calendar_import_ics/models/calendar_event.py @@ -0,0 +1,9 @@ +# Copyright (C) 2024 - ForgeFlow S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class CalendarEvent(models.Model): + _inherit = "calendar.event" + import_id = fields.Char("Imported Event Id") diff --git a/calendar_import_ics/readme/CONTRIBUTORS.rst b/calendar_import_ics/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..42206acc --- /dev/null +++ b/calendar_import_ics/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* Arnau Cruz + + +Do not contact contributors directly about support or help with technical issues. diff --git a/calendar_import_ics/readme/DESCRIPTION.rst b/calendar_import_ics/readme/DESCRIPTION.rst new file mode 100644 index 00000000..ebbc7ae0 --- /dev/null +++ b/calendar_import_ics/readme/DESCRIPTION.rst @@ -0,0 +1,6 @@ +This module adds a new wizard that allows you to import .ics files into odoo calendar, importing events and the following attributes: + +* Summary +* Start Date +* End Date +* UID diff --git a/calendar_import_ics/readme/USAGE.rst b/calendar_import_ics/readme/USAGE.rst new file mode 100644 index 00000000..37572f1b --- /dev/null +++ b/calendar_import_ics/readme/USAGE.rst @@ -0,0 +1,10 @@ +To use this module, follow these steps: + +#. Navigate to the Calendar App. +#. Go to Configuration. +#. Select Import ICS File. + +When importing, you have two options: + +* Specify start and end dates to import events occurring within that range. +* Leave the date fields empty to import all events from the entire file. diff --git a/calendar_import_ics/security/calendar_import_security.xml b/calendar_import_ics/security/calendar_import_security.xml new file mode 100644 index 00000000..1ad29a88 --- /dev/null +++ b/calendar_import_ics/security/calendar_import_security.xml @@ -0,0 +1,7 @@ + + + + Calendar Import Ics + + + diff --git a/calendar_import_ics/security/ir.model.access.csv b/calendar_import_ics/security/ir.model.access.csv new file mode 100644 index 00000000..cadc4160 --- /dev/null +++ b/calendar_import_ics/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_wizard_calendar_import_ics,access_wizard_calendar_import_ics,model_calendar_import_ics,calendar_import_ics.group_calendar_import,1,1,1,1 diff --git a/calendar_import_ics/static/description/index.html b/calendar_import_ics/static/description/index.html new file mode 100644 index 00000000..02df9568 --- /dev/null +++ b/calendar_import_ics/static/description/index.html @@ -0,0 +1,443 @@ + + + + + + +Calendar - Import ics + + + +
+

Calendar - Import ics

+ + +

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

+

This module adds a new wizard that allows you to import .ics files into odoo calendar, importing events and the following attributes:

+
    +
  • Summary
  • +
  • Start Date
  • +
  • End Date
  • +
  • UID
  • +
+

Table of contents

+ +
+

Usage

+

To use this module, follow these steps:

+
    +
  1. Navigate to the Calendar App.
  2. +
  3. Go to Configuration.
  4. +
  5. Select Import ICS File.
  6. +
+

When importing, you have two options:

+
    +
  • Specify start and end dates to import events occurring within that range.
  • +
  • Leave the date fields empty to import all events from the entire file.
  • +
+
+
+

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

+
    +
  • ForgeFlow S.L.
  • +
+
+
+

Contributors

+ +

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

+
+
+

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/calendar project on GitHub.

+

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

+
+
+
+ + diff --git a/calendar_import_ics/tests/__init__.py b/calendar_import_ics/tests/__init__.py new file mode 100644 index 00000000..6bf23ec2 --- /dev/null +++ b/calendar_import_ics/tests/__init__.py @@ -0,0 +1 @@ +from . import test_calendar_import_ics diff --git a/calendar_import_ics/tests/sample_files/test_calendar.ics b/calendar_import_ics/tests/sample_files/test_calendar.ics new file mode 100644 index 00000000..5d888fa2 --- /dev/null +++ b/calendar_import_ics/tests/sample_files/test_calendar.ics @@ -0,0 +1,45 @@ +BEGIN:VCALENDAR +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +PRODID:-//Fastmail/2020.5/EN +X-APPLE-CALENDAR-COLOR:#3A429C +X-WR-CALNAME:_CE +X-WR-TIMEZONE:US/Pacific +BEGIN:VEVENT +DTEND:20061010T210000Z +DTSTAMP:20240221T094129Z +DTSTART:20061010T200000Z +SEQUENCE:0 +SUMMARY:Event 1 Test +TRANSP:OPAQUE +UID:ed27f2b89f945c7692547a2903c20cbe80de6cc6 +END:VEVENT +BEGIN:VEVENT +DTEND:20061011T190000Z +DTSTAMP:20240221T094129Z +DTSTART:20061011T160000Z +SEQUENCE:0 +SUMMARY:Unavailable +TRANSP:OPAQUE +UID:4b1a0cb2081f6b3243bfdf191c985fe86237344c +END:VEVENT +BEGIN:VEVENT +DTEND:20061012T020000Z +DTSTAMP:20240221T094129Z +DTSTART:20061012T010000Z +SEQUENCE:0 +SUMMARY:Unavailable +TRANSP:OPAQUE +UID:06b1c17713eca80e7219e8278330a4e9604f12e3 +END:VEVENT +BEGIN:VEVENT +DTEND:20061014T000000Z +DTSTAMP:20240221T094129Z +DTSTART:20061013T150000Z +SEQUENCE:0 +SUMMARY:Unavailable +TRANSP:OPAQUE +UID:3275dc6275325a10b715008ee2713808de24e516 +END:VEVENT +END:VCALENDAR diff --git a/calendar_import_ics/tests/sample_files/test_calendar_2.ics b/calendar_import_ics/tests/sample_files/test_calendar_2.ics new file mode 100644 index 00000000..cb06394d --- /dev/null +++ b/calendar_import_ics/tests/sample_files/test_calendar_2.ics @@ -0,0 +1,45 @@ +BEGIN:VCALENDAR +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +PRODID:-//Fastmail/2020.5/EN +X-APPLE-CALENDAR-COLOR:#3A429C +X-WR-CALNAME:_CE +X-WR-TIMEZONE:US/Pacific +BEGIN:VEVENT +DTEND:20061010T220000Z +DTSTAMP:20240221T094129Z +DTSTART:20061010T210000Z +SEQUENCE:0 +SUMMARY:Event 1 Test Renamed +TRANSP:OPAQUE +UID:ed27f2b89f945c7692547a2903c20cbe80de6cc6 +END:VEVENT +BEGIN:VEVENT +DTEND:20061011T190000Z +DTSTAMP:20240221T094129Z +DTSTART:20061011T160000Z +SEQUENCE:0 +SUMMARY:Unavailable +TRANSP:OPAQUE +UID:4b1a0cb2081f6b3243bfdf191c985fe86237344c +END:VEVENT +BEGIN:VEVENT +DTEND:20061012T020000Z +DTSTAMP:20240221T094129Z +DTSTART:20061012T010000Z +SEQUENCE:0 +SUMMARY:Unavailable +TRANSP:OPAQUE +UID:06b1c17713eca80e7219e8278330a4e9604f12e3 +END:VEVENT +BEGIN:VEVENT +DTEND:20061014T000000Z +DTSTAMP:20240221T094129Z +DTSTART:20061013T150000Z +SEQUENCE:0 +SUMMARY:Unavailable +TRANSP:OPAQUE +UID:3275dc6275325a10b715008ee2713808de24e516 +END:VEVENT +END:VCALENDAR diff --git a/calendar_import_ics/tests/test_calendar_import_ics.py b/calendar_import_ics/tests/test_calendar_import_ics.py new file mode 100644 index 00000000..2dde982b --- /dev/null +++ b/calendar_import_ics/tests/test_calendar_import_ics.py @@ -0,0 +1,61 @@ +# Copyright (C) 2024 - ForgeFlow S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import base64 +from datetime import datetime + +from odoo.modules.module import get_module_resource +from odoo.tests.common import TransactionCase + + +class TestImportIcs(TransactionCase): + def setUp(self): + super(TestImportIcs, self).setUp() + self.event_model = self.env["calendar.event"] + self.import_wiz = self.env["calendar.import.ics"] + + @classmethod + def _get_test_file(cls, file_name): + file_path = get_module_resource( + "calendar_import_ics", "tests/sample_files", file_name + ) + with open(file_path, "rb") as file: + return base64.encodebytes(file.read()) + + def test_no_date_range_import_ics(self): + events_before_imp = self.event_model.search([]) + filename = "test_calendar.ics" + wiz = self.import_wiz.create( + { + "import_ics_file": self._get_test_file(filename), + "import_ics_filename": filename, + } + ) + wiz.button_import() + events_after_imp = self.event_model.search([]) + self.assertEqual(len(events_after_imp) - len(events_before_imp), 4) + uid = "ed27f2b89f945c7692547a2903c20cbe80de6cc6" + start_date = datetime(2006, 10, 10, 20, 00, 00) + end_date = datetime(2006, 10, 10, 21, 00, 00) + name = "Event 1 Test" + event_1 = self.event_model.search([("import_id", "=", uid)]) + self.assertEqual(event_1.import_id, uid) + self.assertEqual(event_1.start, start_date) + self.assertEqual(event_1.stop, end_date) + self.assertEqual(event_1.name, name) + filename = "test_calendar_2.ics" + wiz = self.import_wiz.create( + { + "import_ics_file": self._get_test_file(filename), + "import_ics_filename": filename, + } + ) + wiz.button_import() + event_1 = self.event_model.search([("import_id", "=", uid)]) + start_date = datetime(2006, 10, 10, 21, 00, 00) + end_date = datetime(2006, 10, 10, 22, 00, 00) + name = "Event 1 Test Renamed" + self.assertEqual(event_1.import_id, uid) + self.assertEqual(event_1.start, start_date) + self.assertEqual(event_1.stop, end_date) + self.assertEqual(event_1.name, name) diff --git a/calendar_import_ics/wizards/__init__.py b/calendar_import_ics/wizards/__init__.py new file mode 100644 index 00000000..9f9358b9 --- /dev/null +++ b/calendar_import_ics/wizards/__init__.py @@ -0,0 +1 @@ +from . import wizard_import_ics diff --git a/calendar_import_ics/wizards/wizard_import_ics.py b/calendar_import_ics/wizards/wizard_import_ics.py new file mode 100644 index 00000000..bc806a43 --- /dev/null +++ b/calendar_import_ics/wizards/wizard_import_ics.py @@ -0,0 +1,89 @@ +# Copyright (C) 2024 - ForgeFlow S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import base64 +from datetime import datetime + +from odoo import _, fields, models +from odoo.exceptions import ValidationError + + +class CalendarImportIcs(models.TransientModel): + """ + This wizard is used to import ics files to calendar + """ + + _name = "calendar.import.ics" + _description = "Calendar Import Ics" + + import_ics_file = fields.Binary(required=True) + import_ics_filename = fields.Char() + import_start_date = fields.Date("Start Import Date") + import_end_date = fields.Date("End Import Date") + partner_id = fields.Many2one("res.partner", string="Partner") + + def button_import(self): + self.ensure_one() + assert self.import_ics_file + extension = self.import_ics_filename.split(".")[1] + if extension != "ics": + raise ValidationError(_("Only ics files are supported")) + if self.env.user and not self.partner_id: + self.partner_id = self.env.user.partner_id.id + file_decoded = base64.b64decode(self.import_ics_file) + file_str = file_decoded.decode("utf-8") + lines = file_str.split("\n") + ics_event = {} + for line in lines: + if line.startswith("BEGIN:VEVENT"): + ics_event = {} + elif line.startswith("END:VEVENT"): + self._process_event(ics_event) + else: + if ":" in line: + key, value = line.strip().split(":", 1) + ics_event[key] = value + + def _process_event(self, ics_event): + if "DTSTART" in ics_event and "DTEND" in ics_event: + event_start_date = self._parse_date(ics_event["DTSTART"]) + event_end_date = self._parse_date(ics_event["DTEND"]) + if (not self.import_start_date or not self.import_end_date) or ( + self.import_start_date <= event_start_date.date() + and self.import_end_date >= event_end_date.date() + ): + event = self.env["calendar.event"].search( + [("import_id", "=", ics_event["UID"])] + ) + if event: + self._update_event( + event, ics_event, event_start_date, event_end_date + ) + else: + self._create_event(ics_event, event_start_date, event_end_date) + + def _parse_date(self, date_str): + return datetime.strptime(date_str, "%Y%m%dT%H%M%SZ") + + def _update_event(self, event, ics_event, event_start_date, event_end_date): + vals = {} + if event.start != event_start_date: + vals["start"] = event_start_date + if event.stop != event_end_date: + vals["stop"] = event_end_date + if event.name != ics_event["SUMMARY"]: + vals["name"] = ics_event["SUMMARY"] + if self.partner_id not in event.partner_ids: + vals["partner_ids"] = [(4, self.partner_id.id, 0)] + event.write(vals) + + def _create_event(self, ics_event, event_start_date, event_end_date): + self.env["calendar.event"].create( + { + "start": event_start_date, + "stop": event_end_date, + "name": ics_event["SUMMARY"], + "import_id": ics_event["UID"], + "partner_ids": [(4, self.partner_id.id)], + } + ) diff --git a/calendar_import_ics/wizards/wizard_import_ics.xml b/calendar_import_ics/wizards/wizard_import_ics.xml new file mode 100644 index 00000000..66fa350c --- /dev/null +++ b/calendar_import_ics/wizards/wizard_import_ics.xml @@ -0,0 +1,37 @@ + + + + calendar.import.ics.form + calendar.import.ics + +
+ + + + + +
+
+
+
+
+ + Import Ics File + calendar.import.ics + form + new + + +
diff --git a/setup/calendar_import_ics/odoo/addons/calendar_import_ics b/setup/calendar_import_ics/odoo/addons/calendar_import_ics new file mode 120000 index 00000000..e95c8678 --- /dev/null +++ b/setup/calendar_import_ics/odoo/addons/calendar_import_ics @@ -0,0 +1 @@ +../../../../calendar_import_ics \ No newline at end of file diff --git a/setup/calendar_import_ics/setup.py b/setup/calendar_import_ics/setup.py new file mode 100644 index 00000000..28c57bb6 --- /dev/null +++ b/setup/calendar_import_ics/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)