# -*- coding: utf-8 -*- import random import requests from urllib.parse import urlencode import uuid from odoo import models, fields, api, tools, _ from odoo.exceptions import UserError, ValidationError class SlideChannel(models.Model): _inherit = 'slide.channel' uuid = fields.Char('UUID', required=True, copy=False, readonly=True, default=lambda self: uuid.uuid4(), tracking=True) code = fields.Char('Code', required=True, copy=False, readonly=True, default=lambda self: _('New'), tracking=True) instance_arrangement = fields.Selection( [('share', 'Share'), ('exclude', 'Exclude'), ('both', 'Both'), ('no_need', 'No Need')], required=True, default="no_need", string="Instance Arrangement") shared_instance_count = fields.Integer('Shared Instance Count', default=1) instance_base = fields.Many2one('ospp.instance', string="Instance Base") instance_ids = fields.One2many('channel.instance', 'channel_id', string="Instances") shared_instance_demo_data = fields.Boolean('Shared Instance Demo Data', default=True) excluded_instance_demo_data = fields.Boolean('Excluded Instance Demo Data', default=False) @api.model def update_slide_channel(self): channel_obj = self.env['slide.channel'].sudo() instance_obj = self.env['channel.instance'].sudo() channel_ids = channel_obj.search([('instance_arrangement', 'not in', ['no_need']), ('instance_base', '!=', False)]) for channel_id in channel_ids: channel_id.action_create_instance() self.env.cr.commit() instance_ids = instance_obj.search([('instance_created', '!=', True)]) for instance_id in instance_ids: instance_id.action_create_instance() self.env.cr.commit() instance_ids = instance_obj.search([('instance_created', '=', True), ('is_notified', '!=', True)]) for instance_id in instance_ids: instance_id.action_post_instance() return True def action_create_instance(self): instance_obj = self.env['channel.instance'].sudo() partner_obj = self.env['slide.channel.partner'].sudo() for rec in self: if rec.instance_arrangement not in ['no_need'] and not (rec.instance_arrangement and rec.instance_base): raise ValidationError(_("No Instance Arrangement or Base provided!")) partner_ids = partner_obj.search([('channel_id', '=', rec.id)]) rec.message_unsubscribe(partner_ids=[x.partner_id.id for x in partner_ids]) if not rec.instance_ids: if rec.instance_arrangement in ['share', 'both']: vals = [] for i in range(rec.shared_instance_count): service_name = f"{rec.code}{i}" val = { "name": service_name, "channel_id": rec.id, "instance_id": rec.instance_base and rec.instance_base.id, "service_name": service_name, "service_url": f"{rec.instance_base.service_scheme}://{service_name}.{rec.instance_base.service_base_host}", "admin_account": f"{rec.user_id.email}", "admin_password": f"{random.randint(10000000,99999999)}", "user_scope": [(6, 0, partner_ids.ids)], "type": "share", "demo_data": rec.shared_instance_demo_data } vals.append(val) instance_obj.create(vals) if rec.instance_arrangement in ['exclude', 'both']: vals = [] for partner_id in partner_ids: service_name = f"{rec.code}{partner_id.partner_id.code}" val = { "name": service_name, "channel_id": rec.id, "instance_id": rec.instance_base and rec.instance_base.id, "service_name": service_name, "service_url": f"{rec.instance_base.service_scheme}://{service_name}.{rec.instance_base.service_base_host}", "admin_account": f"{partner_id.partner_email}", "admin_password": f"{random.randint(10000000,99999999)}", "user_scope": [(6, 0, partner_id.ids)], "type": "exclude", "demo_data": rec.excluded_instance_demo_data } vals.append(val) instance_obj.create(vals) else: if rec.instance_arrangement in ['share', 'both']: vals = [] for i in range(rec.shared_instance_count): service_name = f"{rec.code}{i}" instance_id = instance_obj.search([('name', '=', service_name), ('channel_id', '=', rec.id)]) if not instance_id: val = { "name": service_name, "channel_id": rec.id, "instance_id": rec.instance_base and rec.instance_base.id, "service_name": service_name, "service_url": f"{rec.instance_base.service_scheme}://{service_name}.{rec.instance_base.service_base_host}", "admin_account": f"{rec.user_id.email}", "admin_password": f"{random.randint(10000000,99999999)}", "user_scope": [(6, 0, partner_ids.ids)], "type": "share", "demo_data": rec.shared_instance_demo_data } vals.append(val) if vals: instance_obj.create(vals) if rec.instance_arrangement in ['exclude', 'both']: vals = [] for partner_id in partner_ids: service_name = f"{rec.code}{partner_id.partner_id.code}" instance_id = instance_obj.search([('name', '=', service_name), ('channel_id', '=', rec.id)]) if not instance_id: val = { "name": service_name, "channel_id": rec.id, "instance_id": rec.instance_base and rec.instance_base.id, "service_name": service_name, "service_url": f"{rec.instance_base.service_scheme}://{service_name}.{rec.instance_base.service_base_host}", "admin_account": f"{partner_id.partner_email}", "admin_password": f"{random.randint(10000000,99999999)}", "user_scope": [(6, 0, partner_id.ids)], "type": "exclude", "demo_data": rec.excluded_instance_demo_data } vals.append(val) if vals: instance_obj.create(vals) def name_get(self): # Prefetch the fields used by the `name_get`, so `browse` doesn't fetch other fields self.browse(self.ids).read(['name', 'code']) return [(channel.id, '%s%s' % (channel.code and '[%s] ' % channel.code or '', channel.name)) for channel in self] @api.model_create_multi def create(self, vals_list): for vals in vals_list: if 'company_id' in vals: self = self.with_company(vals['company_id']) if vals.get('code', _("New")) == _("New"): seq_date = None vals['code'] = self.env['ir.sequence'].next_by_code('slide.channel', sequence_date=seq_date) or _("New") return super().create(vals_list) class ChannelInstance(models.Model): _name = "channel.instance" _inherit = ['mail.thread', 'mail.activity.mixin'] _description = "Channel Instance" sequence = fields.Integer('Sequence', default=10) uuid = fields.Char( 'UUID', required=True, copy=False, readonly=True, default=lambda self: uuid.uuid4(), tracking=True) code = fields.Char('Code', required=True, copy=False, readonly=True, default=lambda self: _('New'), tracking=True) channel_id = fields.Many2one('slide.channel', required=True, string="Channel") instance_id = fields.Many2one('ospp.instance', required=True, string="Instance") name = fields.Char('Name', required=True) service_name = fields.Char('Service Name', required=True) service_url = fields.Char('Service URL', required=True) user_scope = fields.Many2many('slide.channel.partner', string="User Scope") admin_account = fields.Char('Admin Account') admin_password = fields.Char('Admin Password') active = fields.Boolean('Active', default=True) type = fields.Selection([('share', 'Share'), ('exclude', 'Exclude')], string="Type", required=True, default='share') instance_created = fields.Boolean('Instance Created', default=False) message = fields.Text('Message') demo_data = fields.Boolean('Demo Data', default=True) is_notified = fields.Boolean('Is Notified', default=False) def action_post_instance(self): """ 发送实训环境地址和登录信息给用户... :return: """ partner_obj = self.env['slide.channel.partner'].sudo() for rec in self: subject = f"通知: 《{rec.channel_id.name}》实训环境已经生成" html_body = f"""
@{', '.join([x.partner_id.name for x in rec.user_scope])} 您的实训环境已经创建!\n
环境: {rec.service_url} \n
账号: {rec.admin_account} \n
密码: {rec.admin_password} \n
课程: {self.env['ir.config_parameter'].sudo().get_param("web.base.url")}/slides/{rec.channel_id.id} \n
预祝学习愉快!
""" partner_ids = partner_obj.search([('channel_id', '=', rec.channel_id.id)]) rec.channel_id.message_unsubscribe(partner_ids=[x.partner_id.id for x in partner_ids]) partner_ids = [x.partner_id.id for x in rec.user_scope] kwargs = {} rec.channel_id.with_context(mail_create_nosubscribe=True).message_post( subject=subject, body=html_body, subtype_xmlid='website_slides.mt_channel_slide_published', email_layout_xmlid='mail.mail_notification_light', partner_ids=partner_ids, notified_partner_ids=[], **kwargs, ) rec.is_notified = True return True def action_create_instance(self): for rec in self: if rec.instance_created: return True url = f"{rec.instance_id.management_api}/web/database/create" master_pwd = f"{rec.instance_id.management_secret}" dbname = rec.service_name login = rec.admin_account password = rec.admin_password phone = '' lang = 'zh_CN' country_code = 'cn' demo = 1 if rec.demo_data else 0 headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Cookie': 'frontend_lang=en_US; session_id=645c41edf8f8c7e1e983ba0f5bd9e94d9a3bd4bc' } payload = { 'master_pwd': master_pwd, 'name': dbname, 'login': login, 'password': password, 'phone': phone, 'lang': lang, 'country_code': country_code, 'demo': demo } if demo else { 'master_pwd': master_pwd, 'name': dbname, 'login': login, 'password': password, 'phone': phone, 'lang': lang, 'country_code': country_code } response = requests.request("POST", url, headers=headers, data=urlencode(payload)) rec.message = response.text if response.status_code == 200 and ('error' not in response.text or 'exists' in response.text): rec.instance_created = True return True def action_drop_instance(self): for rec in self: if not rec.instance_created: return True url = f"{rec.instance_id.management_api}/web/database/drop" master_pwd = f"{rec.instance_id.management_secret}" dbname = rec.service_name headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Cookie': 'frontend_lang=en_US; session_id=645c41edf8f8c7e1e983ba0f5bd9e94d9a3bd4bc' } payload = { 'master_pwd': master_pwd, 'name': dbname, } response = requests.request("POST", url, headers=headers, data=urlencode(payload)) rec.message = response.text if response.status_code == 200 and 'error' not in response.text: rec.instance_created = False # rec.active = True return True def name_get(self): # Prefetch the fields used by the `name_get`, so `browse` doesn't fetch other fields self.browse(self.ids).read(['name', 'code']) return [(channel.id, '%s%s' % (channel.code and '[%s] ' % channel.code or '', channel.name)) for channel in self] @api.model_create_multi def create(self, vals_list): for vals in vals_list: if 'company_id' in vals: self = self.with_company(vals['company_id']) if vals.get('code', _("New")) == _("New"): seq_date = None vals['code'] = self.env['ir.sequence'].next_by_code( 'channel.instance', sequence_date=seq_date) or _("New") return super().create(vals_list)