Search code examples
pythonseleniumodooselenium-firefoxdriver

How to pass an instance driver generated by selenium object into a wizard in odoo


Thanks to everyone who saw this issue. I click a button to trigger the execution of a function that uses selenium in this function to create a driver.

With this driver, I can drag and drop pictures, click buttons, send SMS captcha to the phone without opening the Firefox browser window.

When that's all over, I need to jump a wizard to get my users to enter the captcha sent to the phone.But after we load the wizard from the odoo interface and enter the captcha and click confirm button, I need to enter the captcha value into the page that I just opened with the driver.

How can I get the previous driver? What good solutions are there, thanks.

This is my function to use selenium

# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
import selenium
from selenium.webdriver.firefox.options import Options
import time
from selenium.webdriver import ActionChains

class SaleOrderSend(models.Model)
    _name='sale.order.send'

    @api.multi
    def handel_web_send_captcha(self):

        options = Options()
        options.add_argument('--headless')


        driver = selenium.webdriver.Firefox(options=options)
        driver.get(sign_url)  
        time.sleep(15)

        ac1 = driver.find_elements_by_class_name("es-drag-seal")[0]
        ac2 = driver.find_element_by_class_name('es-sign-date-field')
        time.sleep(1)
        ActionChains(driver).drag_and_drop(ac1, ac2).perform()

        time.sleep(2)
        submit_button = driver.find_element_by_xpath("//button//span[text()='Submit']")
        submit_button.click()

        time.sleep(3)
        driver.switch_to.frame(0)
        short_message = driver.find_element_by_xpath("//a[text()='Sign']")
        short_message.click()

        time.sleep(3)
        send_code = driver.find_element_by_xpath("//div[@class='el-form-item__content']//button//span[text()='Get_Code']")
        send_code.click()

        time.sleep(2)
        driver.close()

        action = {
            'name': _("Please enter the verification code"),
            'type': 'ir.actions.act_window',
            'res_model': 'signature.code',
            'view_type': 'form',
            'view_mode': 'form',
            'target': 'new'
        }
        return action

And this is my py file and xml file in wizard

# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
import selenium
from selenium.webdriver.firefox.options import Options
import time
from selenium.webdriver import ActionChains

class SignatureCode(models.Model):
    _name = 'signature.code'
    _description = 'Signature Code'

    sign_code = fields.Char('Sign Code')
    sign_url = fields.Char('Sign Url')

    @api.multi
    def confirm_sign_code(self):
        """
        Enter the user's captcha code into the browser page you just opened using selenium.
        :return:
        """
        sign_code = self.sign_code

        # How do I get to the previous driver?  driver = selenium.webdriver.Firefox(options=options)
<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <record id="signature_code_form_view" model="ir.ui.view">
        <field name="name">Signature Code Form View</field>
        <field name="model">signature.code</field>
        <field name="arch" type="xml">
            <form string="Signature Code Form View">
                <sheet>
                    <group>
                        <group>
                            <field name="sign_code" required="1" readonly="0"/>
                        </group>
                    </group>
                </sheet>
                <footer>
                    <button string="Confirm" name="confirm_sign_code" type="object" default_focus="1"
                            class="btn-primary"/>
                    <button string="Cancel" class="btn-default" special="cancel"/>
                </footer>
            </form>
        </field>
    </record>
</odoo>

Solution

  • Uses Python's class properties to store the already generated driver.

    In the operation, we first click one button to jump to the wizard, in which we have two buttons.

    First click the first button to trigger method send_code,in which we modify the class property like SignatureCode.driver = driver.

    In the second step, we click confirm button to trigger method confirm_sign_code,in which we use driver = self.driver to call back the previously saved driver in the class property.

    Now we have successfully gotten the previously opened browser interface driver and can close it in a later operation.

    # -*- coding: utf-8 -*-
    from odoo import models, fields, api, _
    import selenium
    from selenium.webdriver.firefox.options import Options
    import time
    from selenium.webdriver import ActionChains
    from odoo.exceptions import UserError
    import os
    
    class SignatureCode(models.TransientModel):
        """
        This class is used to receive the captcha when signing
        """
        _name = 'signature.code'
        _description = 'Signature Code'
        driver = None # Define an initialized class property
    
    
        sign_code = fields.Char('Sign Code')
        sign_url = fields.Char('Sign Url')
        have_sent = fields.Boolean('Have Sent') # Whether to send an authentication code
        manual_sign_id = fields.Many2one('manual.sign',string='Manual Sign')
    
        @api.multi
        def send_code(self):
            """
            Send a code
            :return:
            """
            # No interface operation
            if self.driver:
                try:
                    self.driver.close()
                except:
                    pass
                finally:
                    SignatureCode.driver = None
            options = Options()
            options.add_argument('--headless')
    
            # Open the browser, no interface, and don't let selenium generate logs
            driver = selenium.webdriver.Firefox(options=options,service_log_path=os.devnull)
    
    # This step is key, once the property has been generated, we need to use the class
    # property modification method to save this property to the class for the next call
            SignatureCode.driver = driver
    
            self._cr.commit()
            driver.get(self.sign_url)
            time.sleep(15)
    
    
            if not self.manual_sign_id.have_got:
                first_confirm_button = driver.find_element_by_xpath("/html/body/div/div/div[10]/div/div[3]/span/button/span")
                first_confirm_button.click()
                self.manual_sign_id.have_got = True
                self._cr.commit()
                time.sleep(2)
    
            bottom = driver.find_elements_by_xpath("/html/body/div/div/div[2]/div/div[9]/span/img[@class='toBottom']")
            if bottom:
                bottom[0].click()
                time.sleep(3)
    
            ac1 = driver.find_elements_by_class_name("es-drag-seal")[0]
            ac2 = driver.find_element_by_xpath("/html/body/div[1]/div/div[2]/div/div[2]/div/div[5]/div/div/span")
            time.sleep(1)
            ActionChains(driver).drag_and_drop(ac1, ac2).perform()
    
            time.sleep(2)
            submit_button = driver.find_element_by_xpath("//button//span[text()='submit']")
            submit_button.click()
    
            time.sleep(3)
            driver.switch_to.frame(0)
            short_message = driver.find_element_by_xpath("//a[text()='sign']")
            short_message.click()
    
            time.sleep(3)
            send_code = driver.find_element_by_xpath("//div[@class='el-form-item__content']//button//span[text()='get code']")
            send_code.click()
            time.sleep(2)
            self.have_sent = True
            self._cr.commit()
            raise UserError(_('The authentication code has been sent to the phone, please check the receipt'))
    
    
        @api.multi
        def confirm_sign_code(self):
            """
            validation code
            :return:
            """
            if not self.have_sent:
                raise UserError(_('Please send the verification code first'))
            if not self.sign_code:
                raise UserError(_('Please fill sign code'))
            sign_code = self.sign_code
    
            # Since we have previously modified class properties with classes, as long as we
            # don't close the wizard, the class properties that have been changed will not be   
            # released.
            driver = self.driver
    
            # Enter the verification code
            input_code = driver.find_element_by_xpath("//div[@class='el-input']//input[@class='el-input__inner' and @placeholder='Enter the verification code']")
            input_code.send_keys(sign_code) # Enter the verification code
            time.sleep(2)
    
            # confirm
            confirm_button = driver.find_element_by_xpath("//p[@class='realname-form-opt']//button//span[text()='confirm']")
            confirm_button.click()
            time.sleep(4)
    
            # Here you have to judge whether the captcha is correct or not, based on the web
            # domain changes
            fail_tip = driver.find_elements_by_xpath("//div[@class='van-toast van-toast--middle van-toast--fail']")
            if fail_tip: #failed
                fail_msg = driver.find_elements_by_xpath("//div[@class='van-toast__text' and text()='failed']")
                if fail_msg:
                    raise UserError(_('Verification code is incorrect, please re-enter'))
                else:
                    raise UserError(_('Too many verifications, please review again'))
    
            # close
            time.sleep(5)
            driver.close()
    
            return {
                'type':'ir.actions.client',
                'tag':'reload'
            }
    
    <?xml version="1.0" encoding="utf-8"?>
    <odoo>
        <record id="signature_code_form_view" model="ir.ui.view">
            <field name="name">Signature Code Form View</field>
            <field name="model">signature.code</field>
            <field name="arch" type="xml">
                <form string="Signature Code Form View">
                    <sheet>
                        <group>
                            <group>
                                <field name="have_sent" invisible="1"/>
                                <field name="sign_code" readonly="0"/>
                            </group>
                            <group>
                                <button name="send_code" string="Send Code"
                                        attrs="{'invisible':[('have_sent','=',True)]}"
                                        type="object" default_focus="1" class="btn-primary"/>
                            </group>
                        </group>
                    </sheet>
                    <footer>
                        <button string="Confirm" name="confirm_sign_code" type="object" default_focus="1"
                                class="btn-primary"/>
                        <button string="Cancel" class="btn-default" special="cancel"/>
                    </footer>
                </form>
            </field>
        </record>
    </odoo>