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>
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>