Files
encoach_backend_new_v2/odoo/addons/web_tour/models/tour.py
Yamen Ahmad 3e83d8d7d5 feat: add complete EnCoach backend — Odoo 19 + all addons
Includes:
- Odoo 19 framework (odoo/)
- 27 custom EnCoach addons (new_project/custom_addons/)
  - encoach_core, encoach_api, encoach_lms_api, encoach_adaptive_api
  - encoach_exam, encoach_taxonomy, encoach_adaptive, encoach_assignment
  - encoach_ai, encoach_ai_grading, encoach_ai_generation, encoach_ai_media
  - encoach_courseware, encoach_communication, encoach_subscription
  - encoach_notification, encoach_approval, encoach_branding
  - encoach_classroom, encoach_registration, encoach_stats
  - encoach_faq, encoach_ticket, encoach_training, encoach_resources
  - encoach_adaptive_ai, encoach_sis
- 21 OpenEduCat Enterprise modules (new_project/enterprise-19/)
- 14 OpenEduCat Community modules (new_project/openeducat_erp-19.0/)
- Configuration: odoo.conf, requirements.txt, scripts
- 200+ REST API endpoints with JWT authentication
- SRS and test documentation

Made-with: Cursor
2026-04-01 17:10:04 +04:00

113 lines
3.7 KiB
Python

from odoo import api, fields, models, Command
import json
import base64
class Web_TourTour(models.Model):
_name = 'web_tour.tour'
_description = "Tours"
_order = "sequence, name, id"
name = fields.Char(required=True)
step_ids = fields.One2many("web_tour.tour.step", "tour_id")
url = fields.Char(string="Starting URL", default="/odoo")
sharing_url = fields.Char(compute="_compute_sharing_url", string="Sharing URL")
rainbow_man_message = fields.Html(default="<b>Good job!</b> You went through all steps of this tour.", translate=True)
sequence = fields.Integer(default=1000)
custom = fields.Boolean(string="Custom")
user_consumed_ids = fields.Many2many("res.users")
_uniq_name = models.Constraint(
'unique(name)',
"A tour already exists with this name . Tour's name must be unique!",
)
@api.depends("name")
def _compute_sharing_url(self):
for tour in self:
tour.sharing_url = f"{tour.get_base_url()}/odoo?tour={tour.name}"
@api.model
def consume(self, tourName):
if self.env.user and self.env.user._is_internal():
tour_id = self.search([("name", "=", tourName)])
if tour_id:
tour_id.sudo().user_consumed_ids = [Command.link(self.env.user.id)]
return self.get_current_tour()
@api.model
def get_current_tour(self):
if self.env.user and self.env.user.tour_enabled and self.env.user._is_internal():
tours_to_run = self.search([("custom", "=", False), ("user_consumed_ids", "not in", self.env.user.id)])
return bool(tours_to_run[:1]) and tours_to_run[:1]._get_tour_json()
@api.model
def get_tour_json_by_name(self, tour_name):
tour_id = self.search([("name", "=", tour_name)])
return tour_id._get_tour_json()
def _get_tour_json(self):
tour_json = self.read(fields={
"name",
"url",
"custom"
})[0]
del tour_json["id"]
tour_json["steps"] = self.step_ids.get_steps_json()
tour_json["rainbowManMessage"] = self.rainbow_man_message
return tour_json
def export_js_file(self):
js_content = f"""import {{ registry }} from '@web/core/registry';
registry.category("web_tour.tours").add("{self.name}", {{
url: "{self.url}",
steps: () => {json.dumps(self.step_ids.get_steps_json(), indent=4)}
}})"""
attachment_id = self.env["ir.attachment"].create({
"datas": base64.b64encode(bytes(js_content, 'utf-8')),
"name": f"{self.name}.js",
"mimetype": "application/javascript",
"res_model": "web_tour.tour",
"res_id": self.id,
})
return {
"type": "ir.actions.act_url",
"url": f"/web/content/{attachment_id.id}?download=true",
}
class Web_TourTourStep(models.Model):
_name = 'web_tour.tour.step'
_description = "Tour's step"
_order = "sequence, id"
trigger = fields.Char(required=True)
content = fields.Char()
tooltip_position = fields.Selection(selection=[
["bottom", "Bottom"],
["top", "Top"],
["right", "Right"],
["left", "left"],
], default="bottom")
tour_id = fields.Many2one("web_tour.tour", required=True, index=True, ondelete="cascade")
run = fields.Char()
sequence = fields.Integer()
def get_steps_json(self):
steps = []
for step in self.read(fields=["trigger", "content", "run", "tooltip_position"]):
del step["id"]
step["tooltipPosition"] = step["tooltip_position"]
del step["tooltip_position"]
if not step["content"]:
del step["content"]
steps.append(step)
return steps