Merge branch 'develop_support_arch_linux'

- Upgrade odoo 14 and 16
- Improve selenium odoo navigation and recording
- Improve installation ERPLibre and docker build
This commit is contained in:
Mathieu Benoit 2025-04-12 17:12:23 -04:00
commit 4f39f86cc5
30 changed files with 4204 additions and 2808 deletions

1
.erplibre-semver-version Normal file
View file

@ -0,0 +1 @@
1.5.0

View file

@ -30,6 +30,18 @@ else
endif
endif
# Example for update
.PHONY: custom_run_example
custom_run_example:
./run.sh -d example_prod
.PHONY: custom_update_example
custom_update_example:
./script/database/db_restore.py --database example_prod --image image_name_to_restore
./script/addons/update_addons_all.sh example_prod
./script/addons/update_prod_to_dev.sh example_prod
#########
# RUN #
#########
@ -727,6 +739,13 @@ tag_push_all:
open_terminal:
./script/open_terminal_code_generator.sh
##############
# selenium #
##############
.PHONY: open_selenium
open_selenium:
./.venv/bin/python ./script/selenium/web_login.py
############
# format #
############
@ -938,6 +957,7 @@ config_install:
.PHONY: config_update
config_update:
# Need http to configure the file config.conf, or will disable it
./run.sh -c config.conf -s --stop-after-init
.PHONY: config_update_over_proxy

View file

@ -122,6 +122,36 @@ git reset --hard REF_OF_REMOTES/m
> git reset --hard remotes/origin/rel/8953/zd552kl/7.1.1-11.40.208
```
### fatal: GitCommandError: git command failure
Example of error after an installation with git-repo:
```
raise GitCommandError(
git_command.GitCommandError: GitCommandError: git command failure
Project: manifests
Args: rev-list ^e96a83f03665ecc1b151331776909be32d5e2c7b HEAD --
Stdout:
None
Stderr:
fatal: bad revision 'HEAD'
Skipped fetching project manifests (already have persistent ref)
fatal: GitCommandError: git command failure
Project: manifests
Args: rev-list ^HEAD c8e64956f188f77abcfa7ff0e764af97162d3071 --
Stdout:
None
Stderr:
fatal: bad revision '^HEAD'
```
Solution: Check files into directory `cd .repo/manifests` with commande `git status`. If
you
have some differences in stage, you can try `git reset --hard` and redo the
installation.
If it's not working, re-clone the repo and do a fresh installation.
## OSX installation
### Docker installation

View file

@ -52,7 +52,7 @@ Execute migration with OpenUpgrade.
```bash
make config_gen_migration
./.venv/bin/python ./script/OCA_OpenUpgrade/odoo-bin -c ./config.conf --update all --stop-after-init -d BD
./.venv/bin/python ./script/OCA_OpenUpgrade/odoo-bin -c ./config.conf --no-http --update all --stop-after-init -d BD
```
## Migration Odoo 13 to Odoo 14
@ -61,5 +61,5 @@ Replace BD to your database name.
```bash
make config_gen_migration
./run.sh --upgrade-path=./script/OCA_OpenUpgrade/openupgrade_scripts/scripts --update all --stop-after-init --load=base,web,openupgrade_framework -d BD
./run.sh --upgrade-path=./script/OCA_OpenUpgrade/openupgrade_scripts/scripts --update all --no-http --stop-after-init --load=base,web,openupgrade_framework -d BD
```

View file

@ -112,7 +112,7 @@ database. WARNING, this is not safe for production, you will expose all data.
Update all features.
```bash
./run.sh --limit-time-real 99999 --stop-after-init -u all -d DATABASE
./run.sh --limit-time-real 99999 --no-http --stop-after-init -u all -d DATABASE
```
# Postgresql

View file

@ -49,17 +49,17 @@ Great idea to run it when updating Odoo, it updates each module database.
First execution, install you requirements, choose a new database.
```bash
./run.sh -d [DATABASE] -i [module to test] --test-enable --stop-after-init --log-level=test
./run.sh -d [DATABASE] -i [module to test] --test-enable --no-http --stop-after-init --log-level=test
```
Execute your test on a specific module.
```bash
./run.sh -d [DATABASE] -u [module to test] --test-enable --stop-after-init --log-level=test
./run.sh -d [DATABASE] -u [module to test] --test-enable --no-http --stop-after-init --log-level=test
```
Execute your test on a specific module with tags.
```bash
./run.sh -d [DATABASE] -u [module to test] --test-enable --stop-after-init --log-level=test --test-tags [module_name][tags]
./run.sh -d [DATABASE] -u [module to test] --test-enable --no-http --stop-after-init --log-level=test --test-tags [module_name][tags]
```

View file

@ -45,7 +45,7 @@ RUN cd $ODOO_PREFIX && \
RUN cd $ODOO_PREFIX && \
ls -lha /ERPLibre && \
echo "${ERPLIBRE_VERSION}" && \
./script/version/update_env_version.py --erplibre_version "${ERPLIBRE_VERSION}"
./script/version/update_env_version.py --erplibre_version "${ERPLIBRE_VERSION}" --partial_install
RUN cd $ODOO_PREFIX && ls ./manifest -la && \
ls -lha /ERPLibre && \

View file

@ -112,6 +112,13 @@ You can change the docker directory by editing file `/etc/docker/daemon.json`
```
And restart docker service. You can delete or move all older locations of docker.
Or
Add `--data-root /second_drive/docker` like example following into file `/lib/systemd/system/docker.service` :
```
ExecStart=/usr/bin/dockerd --data-root /second_drive/docker -H fd:// --containerd=/run/containerd/containerd.sock
```
# Update docker
When building your docker with script
> make docker_build_odoo_16

View file

@ -96,7 +96,7 @@
<project name="odoo-product-addons.git" path="addons.odoo14.0/Numigi_odoo-product-addons" remote="Numigi" groups="addons"/>
<project name="odoo-project-addons.git" path="addons.odoo14.0/Numigi_odoo-project-addons" remote="Numigi" groups="addons"/>
<project name="odoo-purchase-addons.git" path="addons.odoo14.0/Numigi_odoo-purchase-addons" remote="Numigi" groups="addons"/>
<project name="odoo-stock-addons.git" path="addons.odoo14.0/Numigi_odoo-stock-addons" remote="Numigi" groups="addons"/>
<project name="odoo-stock-addons.git" path="addons.odoo14.0/Numigi_odoo-stock-addons" remote="ERPLibre" revision="14.0_dev" groups="addons"/>
<project name="odoo-survey-addons.git" path="addons.odoo14.0/Numigi_odoo-survey-addons" remote="Numigi" groups="addons"/>
<project name="odoo-test-docs.git" path="doc/itpp-labs_odoo-test-docs" remote="itpp-labs" revision="master" groups="odoo"/>
<project name="odoo-timesheet-addons.git" path="addons.odoo14.0/Numigi_odoo-timesheet-addons" remote="Numigi" groups="addons"/>
@ -131,7 +131,7 @@
<project name="server-brand.git" path="addons.odoo14.0/OCA_server-brand" remote="OCA" groups="addons"/>
<project name="server-env.git" path="addons.odoo14.0/OCA_server-env" remote="OCA" groups="addons"/>
<project name="server-tools.git" path="addons.odoo14.0/OCA_server-tools" remote="ERPLibre" revision="14.0_dev" groups="addons"/>
<project name="server-ux.git" path="addons.odoo14.0/OCA_server-ux" remote="OCA" groups="addons"/>
<project name="server-ux.git" path="addons.odoo14.0/OCA_server-ux" remote="ERPLibre" revision="14.0_dev" groups="addons"/>
<project name="social.git" path="addons.odoo14.0/OCA_social" remote="ERPLibre" revision="14.0_dev" groups="addons"/>
<project name="survey.git" path="addons.odoo14.0/OCA_survey" remote="OCA" groups="addons"/>
<project name="stock-logistics-warehouse.git" path="addons.odoo14.0/OCA_stock-logistics-warehouse" remote="OCA" groups="addons"/>

View file

@ -28,7 +28,7 @@
<project name="ERPLibre_image_db.git" path="image_db" remote="ERPLibre" clone-depth="1" revision="main" groups="base"/>
<project name="OpenHRMS.git" path="addons.odoo16.0/CybroOdoo_OpenHRMS" remote="CybroOdoo" groups="addons"/>
<project name="CybroAddons.git" path="addons.odoo16.0/CybroOdoo_CybroAddons" remote="ERPLibre" revision="16.0_dev" groups="addons"/>
<project name="CybroAddons.git" path="addons.odoo16.0/CybroOdoo_CybroAddons" remote="ERPLibre" clone-depth="1" revision="16.0_dev" groups="addons"/>
<project name="account-analytic.git" path="addons.odoo16.0/OCA_account-analytic" remote="OCA" groups="addons"/>
<project name="account-budgeting.git" path="addons.odoo16.0/OCA_account-budgeting" remote="OCA" groups="addons"/>
<project name="account-closing.git" path="addons.odoo16.0/OCA_account-closing" remote="OCA" groups="addons"/>
@ -70,6 +70,7 @@
<project name="geospatial.git" path="addons.odoo16.0/OCA_geospatial" remote="OCA" groups="addons"/>
<project name="helpdesk.git" path="addons.odoo16.0/OCA_helpdesk" remote="OCA" groups="addons"/>
<project name="hr.git" path="addons.odoo16.0/OCA_hr" remote="OCA" groups="addons"/>
<project name="hr-holidays.git" path="addons.odoo16.0/OCA_hr-holidays" remote="OCA" groups="addons"/>
<project name="interface-github.git" path="addons.odoo16.0/OCA_interface-github" remote="OCA" groups="addons"/>
<project name="knowledge.git" path="addons.odoo16.0/OCA_knowledge" remote="OCA" groups="addons"/>
<project name="l10n-canada.git" path="addons.odoo16.0/OCA_l10n-canada" remote="OCA" groups="addons"/>
@ -114,11 +115,12 @@
<project name="server-env.git" path="addons.odoo16.0/OCA_server-env" remote="OCA" groups="addons"/>
<project name="server-tools.git" path="addons.odoo16.0/OCA_server-tools" remote="ERPLibre" revision="ERPLibre/16.0" groups="addons"/>
<project name="server-ux.git" path="addons.odoo16.0/OCA_server-ux" remote="OCA" groups="addons"/>
<project name="shift-planning.git" path="addons.odoo16.0/OCA_shift-planning" remote="ERPLibre" revision="16.0_dev" groups="addons"/>
<project name="social.git" path="addons.odoo16.0/OCA_social" remote="OCA" groups="addons"/>
<project name="survey.git" path="addons.odoo16.0/OCA_survey" remote="ERPLibre" revision="16.0_dev" groups="addons"/>
<project name="stock-logistics-warehouse.git" path="addons.odoo16.0/OCA_stock-logistics-warehouse" remote="OCA" groups="addons"/>
<project name="storage.git" path="addons.odoo16.0/OCA_storage" remote="OCA" groups="addons"/>
<project name="timesheet.git" path="addons.odoo16.0/OCA_timesheet" remote="OCA" groups="addons"/>
<project name="timesheet.git" path="addons.odoo16.0/OCA_timesheet" remote="ERPLibre" revision="16.0-refactor-hr_timesheet_begin_end" groups="addons"/>
<project name="vertical-association.git" path="addons.odoo16.0/OCA_vertical-association" remote="OCA" groups="addons"/>
<project name="vertical-hotel.git" path="addons.odoo16.0/OCA_vertical-hotel" remote="OCA" groups="addons"/>
<project name="vertical-isp.git" path="addons.odoo16.0/OCA_vertical-isp" remote="OCA" groups="addons"/>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -19,31 +19,32 @@ authors = [ "Mathieu Benoit <mathben@technolibre.ca>",]
[tool.poetry.dependencies]
python = "^3.8.20"
acme = "^2.11.0"
acme = "2.11.0"
agithub = "^2.2.2"
aiohttp = "^3.10.10"
aiohttp = "3.10.10"
aioshutil = "<1.2"
argcomplete = "^3.5.1"
anyio = ">=3.5"
argcomplete = "^3.6.1"
astor = "0.8.1"
avalara = "^24.10.0"
avalara = "^25.3.0"
azure-identity = "1.13.0"
azure-storage-blob = "12.16.0"
babel = "2.9.1"
beautifulsoup4 = "^4.12.3"
beautifulsoup4 = "^4.13.3"
bokeh = "2.3.1"
boto3 = ">=1.20.41"
cachetools = "^5.5.0"
cachetools = "^5.5.2"
chardet = "5.0.0"
chromedriver-binary = "^132.0.6786.0.0"
click = "^8.1.7"
chromedriver-binary = "^136.0.7088.0.0"
click = "^8.1.8"
cloudflare = "2.20.0"
colorama = "^0.4.6"
contextvars = "^2.4"
coverage = "7.6.1"
cryptography = "39.0.1"
css-html-prettify = "^2.5.5"
cython = "^3.0.11"
dateparser = "^1.2.0"
cython = "^3.0.12"
dateparser = "^1.2.1"
ddt = "1.2.1"
decorator = "4.3.0"
dicttoxml = "^1.7.16"
@ -51,10 +52,10 @@ dnspython = "2.6.1"
docutils = "0.16.0"
ebaysdk = "2.1.5"
email-validator = "^2.2.0"
emoji = "^2.14.0"
endesive = "^2.17.3"
emoji = "^2.14.1"
endesive = "^2.18.5"
extract-msg = "0.36.1"
factur-x = "^3.1"
factur-x = "<=3.1"
fdfgen = "^0.16.1"
feedparser = "6.0.10"
flake8 = "<6.0.0"
@ -65,19 +66,19 @@ geojson = "<3.0.0"
gevent = "20.9.0"
gitpython = "3.1.30"
giturlparse = "^0.12.0"
google-api-python-client = "^2.149.0"
google-api-python-client = "^2.165.0"
google-auth-httplib2 = "^0.2.0"
google-auth-oauthlib = "^1.2.1"
greenlet = "0.4.17"
gst = "^0.1.0"
gst = "^0.1.4"
html2text = "^2024.2.26"
idna = "2.6"
img2pdf = "^0.5.1"
idna = "2.8"
img2pdf = "^0.6.0"
invoice2data = "^0.4.5"
iscompatible = "^0.1.1"
isort = "<5.12.0"
jinja2 = "2.11.3"
josepy = "^1.14.0"
josepy = "1.14.0"
keystoneauth1 = "3.14.0"
libsass = "0.17.0"
lxml = "4.9.1"
@ -91,17 +92,18 @@ numpy = "1.21.1"
numpy-financial = "<=1.0.0"
odoo-test-helper = "^2.1.1"
odoorpc = "^0.10.1"
ofxparse = "0.19"
ofxparse = "0.21"
openai = "^1.68.2"
openpyxl = "^3.1.5"
openupgradelib = "^3.7.1"
openupgradelib = "^3.9.3"
ovh = "1.1.2"
packaging = "21.3"
pandas = "1.3.5"
paramiko = "^3.5.0"
paramiko = "^3.5.1"
passlib = "1.7.1"
pathspec = "0.12.1"
pdf2image = "^1.17.0"
phonenumbers = "^8.13.47"
phonenumbers = "^9.0.1"
pillow = "9.3.0"
plotly = "5.4.0"
polib = "1.1.0"
@ -114,12 +116,13 @@ py3o-formats = "^0.3"
py3o-template = "^0.10.0"
pycountry = "^24.6.1"
pydot = "1.4.1"
pyftpdlib = "^2.0.0"
pyftpdlib = "^2.0.1"
pygithub = "2.3.0"
pygments = "2.7.4"
pygobject = "3.44.1"
pygount = "1.5.1"
pyjwt = "2.5.0"
pylint = "3.1.0"
pyopenssl = "21.0.0"
pypdf = ">=3.1.0,<5.0"
pypdf2 = "1.27.9"
@ -130,7 +133,7 @@ pysftp = "^0.2.9"
python-chess = "^1.999"
python-dateutil = "2.7.3"
python-git = "^2018.2.1"
python-jose = "^3.3.0"
python-jose = "^3.4.0"
python-json-logger = "0.1.5"
python-keystoneclient = "3.22.0"
python-ldap = "3.1.0"
@ -151,17 +154,17 @@ reportlab = "3.6.5"
requests = "2.28.1"
retrying = "^1.3.4"
roulier = "^1.1.1"
selenium = "^4.25.0"
selenium = "4.25.0"
sentry-sdk = "<=1.9.0"
setuptools = "65.5.1"
shapely = "1.8.5"
simplejson = "3.16.0"
sphinx = "4.3.2"
sphinx-intl = "^2.2.0"
sphinx-intl = "2.2.0"
sphinx-rtd-theme = "1.3.0"
statsd = "3.2.1"
toml = "^0.10.2"
tqdm = "^4.66.5"
tqdm = "^4.67.1"
unicodecsv = "^0.14.1"
unidecode = "1.0.23"
unidiff = "^0.7.5"
@ -170,21 +173,21 @@ uvloop = "^0.21.0"
validators = "0.24.0"
vobject = "0.9.6.1"
voicent-python = "^1.0"
webcolors = "^24.8.0"
webcolors = "24.8.0"
websocket-client = "^1.8.0"
werkzeug = "0.16.1"
wheel = "^0.44.0"
wheel = "^0.45.1"
win-unicode-console = "^0.5"
xlrd = "1.1.0"
xlsxwriter = "1.1.2"
xlwt = "1.3"
xmlformatter = "^0.2.6"
xmlformatter = "^0.2.8"
xmlrunner = "^1.7.7"
xmlschema = "^3.4.2"
xmlschema = "^3.4.5"
xmltodict = "^0.14.2"
zeep = "3.2.0"
zipp = ">=3.6.0"
zxcvbn = "^4.4.28"
zxcvbn = "^4.5.0"
[tool.poetry.dev-dependencies]
websocket_client = "^1.1.0"
@ -195,3 +198,4 @@ rev = "22.3.0"
[tool.poetry.dependencies.pylint-odoo]
git = "https://github.com/oca/pylint-odoo.git"
rev = "v9.1.3"

View file

@ -20,30 +20,32 @@ authors = [ "Mathieu Benoit <mathben@technolibre.ca>",]
[tool.poetry.dependencies]
python = "^3.10.14"
agithub = "^2.2.2"
argcomplete = "^3.5.1"
asana = "^5.0.10"
argcomplete = "^3.6.2"
asana = "^5.1.0"
asn1crypto = "^1.5.1"
astor = "^0.8.1"
avalara = "^24.11.2"
avalara = "^25.3.2"
azure-identity = "1.13.0"
azure-storage-blob = "12.16.0"
babel = "2.9.1"
base64io = "^1.0.3"
beautifulsoup4 = "^4.12.3"
beautifulsoup4 = "^4.13.3"
bokeh = "3.1.1"
boto3 = "1.26.7"
bs4 = "^0.0.2"
cachetools = "^5.5.0"
cachetools = "^5.5.2"
cairosvg = "^2.7.1"
cerberus = "^1.3.7"
chardet = "4.0.0"
chromedriver-binary = "^137.0.7118.2.0"
clicksend-client = "^5.0.78"
cloudflare = "^3.1.0"
cmake = "^3.31.1"
cloudflare = "^4.1.0"
cmake = "^4.0.0"
colorama = "^0.4.6"
countryinfo = "^0.1.2"
cryptography = "3.4.8"
cybersource-rest-client-python = "0.0.56"
cython = "^3.0.11"
cython = "^3.0.12"
decorator = "4.4.2"
dlib = "^19.24.6"
docutils = "0.16"
@ -51,44 +53,46 @@ dotty-dict = "^1.3.1"
dropbox = "^12.0.2"
ebaysdk = "2.1.5"
email-validator = "^2.2.0"
endesive = "^2.17.3"
extract-msg = "^0.52.0"
ezdxf = "^1.3.4"
endesive = "^2.18.5"
extract-msg = "^0.54.0"
ezdxf = "^1.4.0"
face-recognition = "^1.3.0"
factur-x = "^3.1"
factur-x = "^3.6"
filetype = "^1.2.0"
formio-data = "^2.1.0"
freezegun = "0.3.15"
fsspec = ">=2024.5.0"
gdown = "^5.2.0"
geocoder = "^1.38.1"
geojson = "^3.1.0"
geojson = "^3.2.0"
geopy = "^2.4.1"
gevent = "21.8.0"
gevent = "21.12.0"
gitpython = "3.1.30"
giturlparse = "^0.12.0"
googletrans-py = "4.0.0"
greenlet = "1.1.2"
gst = "^0.1.2"
gst = "^0.1.4"
httpagentparser = "1.8.1"
hubspot-api-client = "10.0.0"
idna = "2.10"
iscompatible = "^0.1.1"
isort = "^6.0.1"
jinja2 = "2.11.3"
keystoneauth1 = "5.0.0"
libsass = "0.20.1"
linkedin = "^0.1.5"
lottie = "^0.7.1"
lxml = "4.6.5"
lxml-html-clean = "^0.4.1"
mako = "^1.3.6"
lxml-html-clean = "^0.4.2"
mako = "^1.3.9"
markupsafe = "1.1.1"
matplotlib = "^3.9.2"
matplotlib = "^3.10.1"
mechanize = "^0.4.10"
messagebird = "^2.2.0"
mock = "^5.1.0"
meteostat = "^1.6.8"
mock = "^5.2.0"
mpld3 = "0.5.9"
mysqlclient = "^2.2.6"
mysqlclient = "^2.2.7"
nextcloud-api-wrapper = "^0.2.3"
num2words = "0.5.13"
numpy = ">=1.15"
@ -98,21 +102,22 @@ odoo-test-helper = "^2.1.1"
odoorpc = "^0.10.1"
ofxparse = "0.21"
ofxtools = "^0.9.5"
openai = "^1.55.2"
opencv-python = "^4.10.0.84"
openai = "^1.72.0"
opencv-python = "^4.11.0.86"
openpyxl = "^3.1.5"
openupgradelib = "^3.9.4"
pandas = ">=0.25.3"
paramiko = "^3.5.0"
paramiko = "^3.5.1"
passlib = "1.7.4"
pathspec = "^0.12.1"
pay-ccavenue = "^1.0.0"
pdf2image = "^1.17.0"
pdfminer = "^20191125"
phonenumbers = "^8.13.50"
phonenumbers = "^9.0.2"
pillow = "9.0.1"
plotly = "5.13.1"
polib = "1.1.0"
pre-commit = "^4.0.1"
pre-commit = "^4.2.0"
premailer = "^3.10.0"
printnodeapi = "^0.2.0"
prometheus-client = "0.15.0"
@ -124,24 +129,26 @@ py3o-formats = "^0.3"
py3o-template = "^0.10.0"
pyaudio = "^0.2.14"
pycountry = "^24.6.1"
pydevd-odoo = "^1.2.2"
pydifact = "^0.1.8"
pydot = "1.4.2"
pyfcm = "^2.0.7"
pygithub = "^2.5.0"
pyfcm = "^2.0.8"
pygithub = "^2.6.1"
pygobject = "3.44.1"
pyjwt = "^2.10.1"
pymssql = "<=2.2.5"
pykeepass = "^4.1.1.post1"
pymssql = "<=2.2.8"
pyncclient = "^0.7"
pyopenssl = "20.0.1"
pypdf2 = "1.26.0"
pyrfc6266 = "^1.0.2"
pysaml2 = "^7.5.0"
pysaml2 = "^7.5.2"
pyserial = "3.5"
pysftp = "^0.2.9"
pytesseract = "^0.3.13"
python-barcode = "^0.15.1"
python-dateutil = "2.8.1"
python-jose = "^3.3.0"
python-dateutil = ">=2.8.2"
python-jose = "^3.4.0"
python-json-logger = "2.0.4"
python-keystoneclient = "5.0.0"
python-ldap = "3.4.0"
@ -150,43 +157,44 @@ python-resize-image = "^1.1.20"
python-slugify = "^8.0.4"
python-stdnum = "1.16"
python-swiftclient = "4.1.0"
python-telegram-bot = "^21.7"
pytz = "^2024.2"
python-telegram-bot = "^22.0"
pytz = "^2025.2"
pyusb = "1.0.2"
pyyaml = "6.0"
pyzbar = "^0.1.9"
pyzk = "^0.9"
qrcode = "6.1"
redis = "4.3.4"
reportlab = "3.5.59"
reportlab = "3.6.5"
requests = "2.30.0"
requests-toolbelt = "^1.0.0"
responses = "^0.25.3"
responses = "^0.25.7"
retrying = "^1.3.4"
rl-renderpm = "4.0.3"
roulier = "^1.1.1"
schwifty = "2024.4.0"
selenium = "^4.27.1"
selenium = "^4.31.0"
sentry-sdk = "<=1.9.0"
shapely = "^2.0.6"
simplejson = "^3.19.3"
spacy = "^3.8.2"
shapely = "^2.1.0"
simplejson = "^3.20.1"
spacy = "3.8.2"
speechrecognition = "3.10.0"
sqlalchemy = "^2.0.36"
sqlalchemy = "^2.0.40"
statsd = "4.0.1"
telesign = "^2.2.5"
telnyx = "^2.1.3"
telesign = "^2.2.6"
telnyx = "^2.1.5"
todoist-python = "^8.1.4"
toml = "^0.10.2"
tweepy = "^4.14.0"
twilio = "^9.3.7"
tweepy = "^4.15.0"
twilio = "^9.5.2"
unidecode = "^1.3.8"
unidiff = "^0.7.5"
urllib3 = "1.26.5"
uvloop = "^0.21.0"
validate-email = "^1.3"
virtualenv = "20.30.0"
vobject = "0.9.6.1"
vonage = "^4.0.0"
vonage = "^4.4.0"
vonage-verify-v2 = "1.1.4"
werkzeug = "2.0.2"
wheel = "^0.45.1"
@ -202,7 +210,3 @@ websocket_client = "^1.1.0"
[tool.poetry.dependencies.black]
git = "https://github.com/psf/black.git"
rev = "24.8.0"
[tool.poetry.dependencies.openupgradelib]
git = "https://github.com/OCA/openupgradelib"
rev = "master"

View file

@ -35,12 +35,11 @@ python-git
voicent-python
python-swiftclient
python-keystoneclient
coverage
# For logical test
docutils==0.16.0
flake8<6.0.0
git+https://github.com/oca/pylint-odoo.git
git+https://github.com/oca/pylint-odoo.git@v9.1.3
# oca-maintainers-tools
# git+https://github.com/OCA/maintainer-tools.git#egg=oca-maintainers-tools
xmlformatter
@ -98,7 +97,6 @@ geojson<3.0.0
regex==2022.3.2
pygount==1.5.1
pysaml2==7.2.1
gevent==1.5.0
freezegun==0.3.15
pyopenssl==21.0.0
ovh==1.1.2
@ -106,6 +104,16 @@ sphinx-rtd-theme==1.3.0
sphinx_rtd_theme==1.3.0
dnspython==2.6.1
coverage==7.6.1
webcolors==24.8.0
aiohttp==3.10.10
josepy==1.14.0
acme==2.11.0
sphinx-intl==2.2.0
selenium==4.25.0
anyio>=3.5
openai
idna==2.8
pylint==3.1.0
# asyncio
aioshutil<1.2

View file

@ -17,6 +17,8 @@ selenium
# mmg # depend on weasyprint 59.0 and depend on old version pillow 9.0.1
uvloop
python-randomword-fr
isort
pykeepass
# For OSX
cython
@ -51,7 +53,7 @@ pre-commit
# For python 3.10 from odoo/requirements.txt, poetry_update.py cannot support python_version
cryptography==3.4.8
freezegun==0.3.15
gevent==21.8.0
gevent==21.12.0
greenlet==1.1.2
Jinja2==2.11.3
libsass==0.20.1
@ -66,7 +68,7 @@ PyPDF2==1.26.0
python-ldap==3.4.0
pyusb==1.0.2
requests==2.30.0
reportlab==3.5.59
reportlab==3.6.5
urllib3==1.26.5
Werkzeug==2.0.2
xlrd==1.2.0
@ -83,3 +85,18 @@ hubspot-api-client==10.0.0
# Optional Odoo
pdfminer
# Optional PyDev
pydevd-odoo
# For fixing module ocr_data_retrieval check file ocr_data_retrieval/models/ocr_data_template.py
spacy==3.8.2
# Fix update dateutil from odoo
python-dateutil>=2.8.2
# Extra module
meteostat
# Force upgrade package to fix Ubuntu 25.04
virtualenv==20.30.0

View file

@ -5,4 +5,4 @@ if [ $# -lt 1 ]; then
exit 1
fi
source ./.venv/bin/activate
python3 ./odoo/odoo-bin -c ./config.conf --limit-time-real 99999 --limit-time-cpu 99999 --stop-after-init -i user_test,disable_mail_server,disable_auto_backup --dev prod -d $@
python3 ./odoo/odoo-bin -c ./config.conf --limit-time-real 99999 --limit-time-cpu 99999 --no-http --stop-after-init -i user_test,disable_mail_server,disable_auto_backup --dev prod -d $@

View file

@ -67,15 +67,15 @@ echo $POETRY_VERSION
if [ "$IS_RELEASE_ALPHA" == true ]
then
# Add commit hash when release alpha
ERPLIBRE_VERSION="${ERPLIBRE_VERSION}_ALPHA_$(git rev-parse --short HEAD)"
ERPLIBRE_VERSION="${ERPLIBRE_VERSION}_ALPHA_odoo_${ODOO_VERSION}_$(git rev-parse --short HEAD)"
elif [ "$IS_RELEASE_BETA" == true ]
then
# Add commit hash when release beta
ERPLIBRE_VERSION="${ERPLIBRE_VERSION}_BETA_$(git rev-parse --short HEAD)"
ERPLIBRE_VERSION="${ERPLIBRE_VERSION}_BETA_odoo_${ODOO_VERSION}_$(git rev-parse --short HEAD)"
elif [ "$IS_RELEASE" == false ]
then
# Add commit hash when not a release
ERPLIBRE_VERSION="${ERPLIBRE_VERSION}_$(git rev-parse --short HEAD)"
ERPLIBRE_VERSION="${ERPLIBRE_VERSION}_odoo_${ODOO_VERSION}_$(git rev-parse --short HEAD)"
fi

View file

@ -29,6 +29,7 @@ if [[ ${EL_MINIMAL_ADDONS} = "False" ]]; then
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/CybroOdoo_CybroAddons," >> "${EL_CONFIG_FILE}"
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/CybroOdoo_OpenHRMS," >> "${EL_CONFIG_FILE}"
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/ERPLibre_erplibre_addons," >> "${EL_CONFIG_FILE}"
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/MathBenTech_odoo-business-spending-management-quebec-canada," >> "${EL_CONFIG_FILE}"
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/OCA_account-analytic," >> "${EL_CONFIG_FILE}"
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/OCA_account-budgeting," >> "${EL_CONFIG_FILE}"
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/OCA_account-closing," >> "${EL_CONFIG_FILE}"
@ -68,6 +69,7 @@ if [[ ${EL_MINIMAL_ADDONS} = "False" ]]; then
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/OCA_geospatial," >> "${EL_CONFIG_FILE}"
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/OCA_helpdesk," >> "${EL_CONFIG_FILE}"
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/OCA_hr," >> "${EL_CONFIG_FILE}"
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/OCA_hr-holidays," >> "${EL_CONFIG_FILE}"
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/OCA_interface-github," >> "${EL_CONFIG_FILE}"
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/OCA_knowledge," >> "${EL_CONFIG_FILE}"
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/OCA_l10n-canada," >> "${EL_CONFIG_FILE}"
@ -99,6 +101,7 @@ if [[ ${EL_MINIMAL_ADDONS} = "False" ]]; then
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/OCA_server-env," >> "${EL_CONFIG_FILE}"
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/OCA_server-tools," >> "${EL_CONFIG_FILE}"
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/OCA_server-ux," >> "${EL_CONFIG_FILE}"
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/OCA_shift-planning," >> "${EL_CONFIG_FILE}"
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/OCA_social," >> "${EL_CONFIG_FILE}"
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/OCA_stock-logistics-warehouse," >> "${EL_CONFIG_FILE}"
printf "${EL_HOME}/addons.odoo${EL_ODOO_VERSION}/OCA_storage," >> "${EL_CONFIG_FILE}"

View file

@ -0,0 +1,62 @@
#!/usr/bin/env bash
install_package() {
local package_name=$1
# Check package is already installed
if pacman -Qs "$package_name" > /dev/null; then
echo "$package_name is already installed."
else
echo "Installation of package $package_name..."
yes|yay -S "$package_name"
fi
}
# Odoo installation
install_package postgis
install_package postgresql
install_package mariadb
install_package libev
install_package wkhtmltopdf
echo "Need password to create symbolic link, create postgres user and install npm :"
sudo ln -fs /usr/lib/libldap.so /usr/lib/libldap_r.so
sudo su - postgres -c "createuser -s ${EL_USER}" 2>/dev/null || true
echo -e "\n---- Update NPM ----"
install_package npm
sudo npm install npm@latest -g
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "npm install npm lastest installation error."
exit 1
fi
sudo npm install -g rtlcss
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "npm install rtlcss installation error."
exit 1
fi
sudo npm install -g less
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "npm install less installation error."
exit 1
fi
echo -e "\n---- Test tool ----"
npm install
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "npm install prettier + plugin-xml installation error."
exit 1
fi
# TODO install nginx
# ERPLibre installation
install_package cmake
install_package parallel
install_package tk

View file

@ -14,7 +14,7 @@ EL_USER=${USER}
UBUNTU_VERSION=$(lsb_release -rs)
DEBIAN_VERSION=$(lsb_release -cs)
OS=$(lsb_release -si)
if [ "24.04" == "${UBUNTU_VERSION}" ] || [ "24.10" == "${UBUNTU_VERSION}" ] || [ "23.10" == "${UBUNTU_VERSION}" ] || [ "23.04" == "${UBUNTU_VERSION}" ] || [ "22.10" == "${UBUNTU_VERSION}" ] || [ "22.04" == "${UBUNTU_VERSION}" ]; then
if [ "25.04" == "${UBUNTU_VERSION}" ] || [ "24.04" == "${UBUNTU_VERSION}" ] || [ "24.10" == "${UBUNTU_VERSION}" ] || [ "23.10" == "${UBUNTU_VERSION}" ] || [ "23.04" == "${UBUNTU_VERSION}" ] || [ "22.10" == "${UBUNTU_VERSION}" ] || [ "22.04" == "${UBUNTU_VERSION}" ]; then
WKHTMLTOX_X64=https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-3/wkhtmltox_0.12.6.1-3.jammy_amd64.deb
elif [ "20.04" == "${UBUNTU_VERSION}" ]; then
WKHTMLTOX_X64=https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb

View file

@ -1,20 +1,21 @@
#!/usr/bin/env bash
if [[ "${OSTYPE}" == "linux-gnu" ]]; then
OS=$(lsb_release -si)
VERSION=$(cat /etc/issue)
if [[ "${OS}" == *"Ubuntu"* ]]; then
if [[ "${VERSION}" == Ubuntu\ 18.04* || "${VERSION}" == Ubuntu\ 20.04* || "${VERSION}" == Ubuntu\ 22.04* || "${VERSION}" == Ubuntu\ 22.10* || "${VERSION}" == Ubuntu\ 23.04* || "${VERSION}" == Ubuntu\ 23.10* || "${VERSION}" == Ubuntu\ 24.04* ]]; then
source /etc/os-release
if [[ "${ID}" == "ubuntu" ]]; then
if [[ "${VERSION_ID}" == "18.04" || "${VERSION_ID}" == "20.04" || "${VERSION_ID}" == "22.04" || "${VERSION_ID}" == "22.10" || "${VERSION_ID}" == "23.04" || "${VERSION_ID}" == "23.10" || "${VERSION_ID}" == "24.04" || "${VERSION_ID}" == "25.04" ]]; then
echo "\n---- linux-gnu installation process started ----"
./script/install/install_debian_dependency.sh
else
echo "Your version is not supported, only support 18.04, 20.04 and 22.04 - 24.04 : ${VERSION}"
echo "Your version is not supported, only support 18.04, 20.04 and 22.04 - 24.04, 25.04 : ${VERSION_ID}"
fi
elif [[ "${OS}" == *"Debian"* ]]; then
elif [[ "${ID}" == "debian" ]]; then
./script/install/install_debian_dependency.sh
elif [[ "${ID}" == "arch" ]]; then
./script/install/install_arch_linux.sh
else
./script/install/install_debian_dependency.sh
echo "Your Linux system is not supported, only support Ubuntu 18.04 or Ubuntu 20.04 or Ubuntu 22.04 - Ubuntu 23.10 - Ubuntu 24.04 ."
echo "Your Linux system is not supported, only support Ubuntu 18.04 or Ubuntu 20.04 or Ubuntu 22.04 - Ubuntu 23.10 - Ubuntu 24.04, Ubuntu 25.04 ."
fi
elif [[ "${OSTYPE}" == "darwin"* ]]; then
echo "\n---- Darwin installation process started ----"

View file

@ -105,6 +105,10 @@ def combine_requirements(config):
lst_requirements_file = get_lst_requirements_txt()
# lst_requirements_with_condition = set()
# dct_special_condition = defaultdict(list)
if not os.path.isfile(priority_filename_requirement):
_logger.error(f"File {priority_filename_requirement} not found.")
dct_requirements_module_filename = defaultdict(list)
for requirements_filename in lst_requirements_file:
with open(requirements_filename, "r") as f:

View file

@ -0,0 +1,51 @@
#!/usr/bin/env bash
# Get two last .webm file
videos=($(ls -t *.webm | head -n 2))
if [ ${#videos[@]} -ne 2 ]; then
echo "Error : you need two files .webm in root directory"
exit 1
fi
timestamp=$(date +"%Y%m%d_%H%M%S")
output_name="output_${timestamp}.webm"
video1="${videos[0]}"
video2="${videos[1]}"
echo "first video ${video1}"
echo "second video ${video2}"
# Obtenez les durées des vidéos
duration1=$(ffmpeg -i "$video1" 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | tr -d ,)
duration2=$(ffmpeg -i "$video2" 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | tr -d ,)
echo "duration 1 : ${duration1}"
echo "duration 2 : ${duration2}"
# Convertir les durées en secondes
duration1_sec=$(echo "$duration1" | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')
duration2_sec=$(echo "$duration2" | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')
echo "duration 1 sec : ${duration1_sec}"
echo "duration 2 sec : ${duration2_sec}"
# Déterminer la durée la plus courte
min_duration=$(echo "$duration1_sec $duration2_sec" | awk '{print ($1 < $2) ? $1 : $2}')
echo "Min duration : ${min_duration}"
# Créez la sortie
#output_name="output.mp4"
# Utiliser ffmpeg pour couper et empiler les vidéos
#ffmpeg -i "$video1" -i "$video2" -filter_complex "
# [0:v]trim=start=0:end=$min_duration,setpts=PTS-STARTPTS[v0];
# [1:v]trim=start=0:end=$min_duration,setpts=PTS-STARTPTS[v1];
# [v0][v1]hstack=inputs=2" "$output_name"
#echo "Les vidéos ont été combinées en $output_name"
ffmpeg -i "${videos[0]}" -i "${videos[1]}" -filter_complex "hstack=inputs=2" "${output_name}"
# with normalize size video
#ffmpeg -i "${videos[0]}" -i "${videos[1]}" -filter_complex "[0:v]scale=320:240[v0]; [1:v]scale=320:240[v1]; [v0][v1]hstack=inputs=2" "${output_name}"
echo "${output_name}"

View file

@ -2,10 +2,11 @@
# © 2021-2024 TechnoLibre (http://www.technolibre.ca)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
import sys
import argparse
import web_login
import sys
import selenium_lib
import web_login
def run(config, selenium_tool):

View file

@ -2,29 +2,46 @@
# © 2021-2024 TechnoLibre (http://www.technolibre.ca)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from randomwordfr import RandomWordFr
import re
import datetime
import os
import re
import sys
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.firefox.service import Service
from subprocess import getoutput
from randomwordfr import RandomWordFr
from selenium import webdriver
from selenium.common.exceptions import (
ElementClickInterceptedException,
TimeoutException,
)
from selenium.webdriver import ActionChains
from selenium.webdriver.common.actions.wheel_input import ScrollOrigin
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.firefox.service import Service
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
class SeleniumLib(object):
def __init__(self, config):
self.config = config
self.video_recorder = None
self.filename = "video_" + time.strftime("%Y_%m_%d-%H_%M_%S") + ".webm"
if self.config.video_suffix:
self.filename = (
f"video_{self.config.video_suffix}_"
+ time.strftime("%Y_%m_%d-%H_%M_%S")
+ ".webm"
)
else:
self.filename = (
"video_" + time.strftime("%Y_%m_%d-%H_%M_%S") + ".webm"
)
self.driver = None
def configure(self):
def configure(self, ignore_open_web=False):
# Configuration pour lancer Firefox en mode de navigation privée
firefox_options = webdriver.FirefoxOptions()
if not self.config.not_private_mode:
@ -70,6 +87,7 @@ class SeleniumLib(object):
# Ajout de l'enregistrement
if self.config.record_mode:
# Create recording
new_path = os.path.normpath(
os.path.join(os.path.dirname(__file__), "..", "..")
)
@ -121,7 +139,8 @@ class SeleniumLib(object):
)
# Ouvrez la page web
self.driver.get(f"{self.config.url}/web")
if not ignore_open_web:
self.driver.get(f"{self.config.url}/web")
# Close tab opening by DarkReader
if self.config.not_private_mode and not self.config.no_dark_mode:
@ -150,6 +169,7 @@ class SeleniumLib(object):
)
)
button.click()
return button
def get_element(self, by: str = By.ID, value: str = None, timeout=5):
wait = WebDriverWait(self.driver, timeout)
@ -164,6 +184,125 @@ class SeleniumLib(object):
return ele[0]
def get_element_not_visible(
self, by: str = By.ID, value: str = None, timeout=5
):
wait = WebDriverWait(self.driver, timeout)
ele = wait.until(
EC.presence_of_element_located(
(
by,
value,
)
)
)
return ele
def get_elements_not_visible(
self, by: str = By.ID, value: str = None, timeout=5
):
wait = WebDriverWait(self.driver, timeout)
ele = wait.until(
EC.presence_of_all_elements_located(
(
by,
value,
)
)
)
return ele
def get_all_element(self, by: str = By.ID, value: str = None, timeout=5):
wait = WebDriverWait(self.driver, timeout)
ele = wait.until(
EC.visibility_of_all_elements_located(
(
by,
value,
)
)
)
return ele
def wait_new_element_and_click(
self,
by: str = By.ID,
value: str = None,
delay_wait=1,
timeout=5,
inject_cursor=False,
):
new_element = self.wait_new_element(
by=by, value=value, delay_wait=delay_wait, timeout=timeout
)
if new_element:
if inject_cursor:
self.inject_cursor()
self.click_with_mouse_move(element=new_element, no_scroll=True)
return new_element
def wait_new_element(
self,
by: str = By.ID,
value: str = None,
delay_wait=1,
timeout=5,
timeout_wait=None,
):
element = None
# TODO support timeout_wait
while element is None:
time.sleep(delay_wait)
try:
element = self.get_element(by, value, timeout)
except TimeoutException as e:
pass
print(f"Waiting {delay_wait} seconds after value '{value}'")
return element
def wait_add_new_element_and_click(
self, by: str = By.ID, value: str = None, delay_wait=1, timeout=5
):
new_element = self.wait_add_new_element(
by=by, value=value, delay_wait=delay_wait, timeout=timeout
)
self.click_with_mouse_move(element=new_element, no_scroll=True)
return new_element
def wait_add_new_element(
self, by: str = By.ID, value: str = None, delay_wait=1, timeout=5
):
all_element_init = self.get_all_element(by=by, value=value)
len_all_element_init = len(all_element_init)
len_all_element = len_all_element_init
all_element = []
while len_all_element_init == len_all_element:
time.sleep(delay_wait)
all_element = self.get_all_element(by=by, value=value)
len_all_element = len(all_element)
print(f"Waiting {delay_wait} seconds after value '{value}'")
new_element = all_element[-1]
return new_element
def wait_increment_number_text_and_click(
self, by: str = By.ID, value: str = None, delay_wait=1, timeout=5
):
element_init = self.get_element(by=by, value=value, timeout=timeout)
actual_number = int(element_init.text)
number_goal = actual_number + 1
while actual_number < number_goal:
time.sleep(delay_wait)
element_goal = self.get_element(
by=by, value=value, timeout=timeout
)
actual_number = int(element_goal.text)
print(f"Waiting {delay_wait} seconds after value '{value}'")
self.click_with_mouse_move(element=element_init, no_scroll=True)
return element_init
def click_with_mouse_move(
self,
by: str = By.ID,
@ -172,9 +311,15 @@ class SeleniumLib(object):
no_scroll: bool = False,
viewport_ele_by: str = By.ID,
viewport_ele_value: str = None,
element: WebElement = None,
with_index: bool = False,
index_of_list: int = 0,
):
# ele = self.driver.find_element(by, value)
ele = self.get_element(by, value, timeout)
if element:
ele = element
else:
ele = self.get_element(by, value, timeout)
if not no_scroll:
viewport_ele = None
if viewport_ele_value:
@ -182,18 +327,67 @@ class SeleniumLib(object):
viewport_ele_by, viewport_ele_value, timeout
)
self.scrollto_element(ele, viewport_ele=viewport_ele)
# ActionChains(self.driver).move_to_element(ele).click().perform()
ActionChains(self.driver).move_to_element(ele).perform()
time.sleep(self.config.selenium_default_delay)
wait = WebDriverWait(self.driver, timeout)
button = wait.until(
EC.element_to_be_clickable(
(
by,
value,
if element:
button = wait.until(EC.element_to_be_clickable(element))
else:
if with_index:
buttons = wait.until(
EC.presence_of_all_elements_located(
(
by,
value,
)
)
)
)
)
button.click()
button = buttons[index_of_list]
else:
button = wait.until(
EC.element_to_be_clickable(
(
by,
value,
)
)
)
try:
button.click()
except ElementClickInterceptedException as e:
try:
print(e)
self.driver.execute_script(
"arguments[0].scrollIntoView(true);", button
)
button.click()
except ElementClickInterceptedException as e:
print(e)
self.driver.execute_script("arguments[0].click();", button)
return button
def click_canvas_form(
self,
by: str = By.ID,
value: str = None,
timeout: int = 5,
form="carre",
):
# ele = self.driver.find_element(by, value)
ele = self.get_element(by, value, timeout)
actions = ActionChains(self.driver)
actions.move_to_element_with_offset(ele, -50, -50)
actions.click_and_hold()
if form == "carre":
actions.move_by_offset(50, 0)
actions.move_by_offset(0, 50)
actions.move_by_offset(-50, 0)
actions.move_by_offset(0, -50)
actions.release()
actions.perform()
def input_text_with_mouse_move(
self,
@ -216,6 +410,8 @@ class SeleniumLib(object):
)
self.scrollto_element(ele, viewport_ele=viewport_ele)
ActionChains(self.driver).move_to_element(ele).perform()
# Write content
ele.send_keys(text_value)
if delay_after > 0:
time.sleep(delay_after)
@ -346,7 +542,50 @@ class SeleniumLib(object):
cursor.style.top = e.pageY - 5 + 'px';
});
"""
self.driver.execute_script(cursor_script)
try:
try:
self.driver.execute_script(cursor_script)
except Exception as e:
time.sleep(1)
print("Error execute script cursor, wait 1 second")
self.driver.execute_script(cursor_script)
except Exception as e:
time.sleep(3)
print("Error execute script cursor, wait 3 second")
self.driver.execute_script(cursor_script)
def scenario_create_new_account_with_random_user(
self, show_cursor=False, def_action_before_submit=None
):
# Click no account
if show_cursor:
self.inject_cursor()
self.click_with_mouse_move(
By.XPATH, "/html/body/div[1]/main/div/form/div[3]/div[1]/a[1]"
)
# Trouvez les éléments du formulaire
if show_cursor:
self.inject_cursor()
# Remplissez le courriel et le mot de passe
first_name = self.get_french_word_no_space_no_accent()
password = first_name.lower()
second_name = self.get_french_word_no_space_no_accent()
full_name = f"{first_name} {second_name}"
domain = self.get_french_word_no_space_no_accent().lower()
self.input_text_with_mouse_move(
By.NAME, "login", f"{password}@{domain}.com"
)
self.input_text_with_mouse_move(By.NAME, "name", full_name)
self.input_text_with_mouse_move(By.NAME, "password", password)
self.input_text_with_mouse_move(By.NAME, "confirm_password", password)
if def_action_before_submit:
def_action_before_submit()
self.click_with_mouse_move(
By.XPATH, "/html/body/div[1]/main/div/form/div[6]/button"
)
def open_tab(self, url):
# Open a new window
@ -364,9 +603,157 @@ class SeleniumLib(object):
except Exception:
print("Chatbot cannot be found, stop searching it.")
# Website
def odoo_website_menu_click(self, from_text=""):
if not from_text:
raise Exception(
"Cannot click, empty parameter from method"
" odoo_website_menu_click"
)
button = self.click_with_mouse_move(
By.LINK_TEXT, from_text, timeout=30
)
return button
# Web
def odoo_fill_widget_char(self, field_name, text):
# Type input
text_field = WebDriverWait(self.driver, 10).until(
EC.presence_of_element_located((By.NAME, field_name))
)
text_field.send_keys(text)
return text_field
def odoo_web_click_principal_menu(self):
# Click menu button
button = self.click_with_mouse_move(
By.CSS_SELECTOR, "a.full[data-toggle='dropdown']", timeout=30
)
return button
def odoo_web_principal_menu_click_root_menu(self, from_text=""):
if not from_text:
raise Exception(
"Cannot click, empty parameter from method"
" odoo_web_principal_menu_click_root_menu"
)
button = self.click_with_mouse_move(
By.LINK_TEXT, from_text, timeout=30
)
# If not work, force it
# selenium_tool.driver.execute_script("arguments[0].click();", button)
return button
def odoo_web_action_button_new(self):
# From web view kanban, list
return self.click_with_mouse_move(
By.CLASS_NAME, "o-kanban-button-new", timeout=30
)
def odoo_web_kanban_click(self, button_text, btn_class="btn-primary"):
# From web view kanban, list
return self.click_with_mouse_move(
By.XPATH,
f"//button[contains(@class, 'btn {btn_class} o_kanban_edit') and"
f" text()='{button_text}']",
timeout=30,
)
def odoo_web_form_notebook_tab_click(self, tab_text):
return self.click_with_mouse_move(
By.XPATH,
f"//a[text()='{tab_text}']",
timeout=30,
)
def odoo_fill_widget_selection(self, field_name, text):
return self.odoo_fill_widget_many2one(field_name, text)
def odoo_fill_widget_boolean(self, field_name, value):
self.driver.implicitly_wait(10)
checkbox_div = self.driver.find_element(
By.XPATH, f"//div[@name='{field_name}']"
)
checkbox = checkbox_div.find_element(
By.CSS_SELECTOR, 'input[type="checkbox"]'
)
is_selected = checkbox.get_attribute("selected")
# Check if already set
if is_selected == "true" and value:
return checkbox
elif is_selected is None and not value:
return checkbox
parent_element = checkbox.find_element(By.XPATH, "..")
parent_element.click()
return checkbox
def odoo_fill_widget_many2one(self, field_name, text):
# TODO read this file odoo/addons/web/static/src/js/fields/field_registry.js
div_field = WebDriverWait(self.driver, 10).until(
EC.presence_of_element_located((By.NAME, field_name))
)
input_field = div_field.find_element(By.XPATH, ".//input")
input_field.send_keys(text)
# TODO improve speed by observation of comportement
time.sleep(0.5)
input_field.send_keys(Keys.ENTER)
return input_field
def odoo_fill_widget_date(self, field_name, date_to_select):
# Type date
self.driver.implicitly_wait(10)
checkbox_div = self.driver.find_element(
By.XPATH, f"//div[@name='{field_name}']"
)
checkbox_div.click()
# TODO improve speed by observation of comportement
time.sleep(0.5)
checkbox_date_div = self.driver.find_element(
By.CSS_SELECTOR, ".bootstrap-datetimepicker-widget"
)
# time.sleep(0.5)
str_date_today = date_to_select.strftime("%Y-%m-%d")
new_value = checkbox_date_div.find_element(
By.XPATH, f"//td[@data-day='{str_date_today}']"
)
new_value.click()
return new_value
def start_record(self):
# Démarrer l'enregistrement
if self.config.record_mode:
if self.config.record_wait_before_start_time:
time.sleep(int(self.config.record_wait_before_start_time))
# Sync before record
has_sync_error = False
if self.config.sync_file_record_read:
if not self.config.sync_file_record_write:
print(
"Error, cannot sync, missing 'sync_file_record_write'"
)
has_sync_error = True
if self.config.sync_file_record_write:
if not self.config.sync_file_record_read:
print(
"Error, cannot sync, missing 'sync_file_record_read'"
)
has_sync_error = True
if (
not has_sync_error
and self.config.sync_file_record_read
and self.config.sync_file_record_write
):
# Do sync
file_name_1 = f"/tmp/erplibre_sync_temp_file{self.config.sync_file_record_write}"
file_name_2 = f"/tmp/erplibre_sync_temp_file{self.config.sync_file_record_read}"
with open(file_name_1, "w") as file:
file.write(f"Recording {self.filename}\n")
while not os.path.exists(file_name_2):
var_wait_time = 0.001
print(f"File {self.filename} wait {var_wait_time} seconds")
time.sleep(var_wait_time)
self.video_recorder.start()
def stop_record(self):
@ -377,6 +764,7 @@ class SeleniumLib(object):
time.sleep(self.config.selenium_video_time_waiting_end)
self.video_recorder.stop()
print("End of recording video")
print(self.filename)
if self.config.selenium_video_auto_play_video:
print(f"Play video {self.filename}")
@ -402,7 +790,7 @@ def fill_parser(parser):
"--selenium_default_delay",
default=0.4,
type=float,
help="Pause at the end before close video.",
help="Pause at the end of an action.",
)
group_browser = parser.add_argument_group(title="Browser")
@ -434,6 +822,31 @@ def fill_parser(parser):
action="store_true",
help="Start recording (not finish to be implemented).",
)
group_record.add_argument(
"--record_wait_before_start_time",
help="Time to wait in second before start recording.",
)
parser.add_argument(
"--video_suffix",
default="",
help="Will modify video name.",
)
parser.add_argument(
"--sync_file_record_read",
default="",
help=(
"Will sync and check file from tmp before record, write before"
" read."
),
)
parser.add_argument(
"--sync_file_record_write",
default="",
help=(
"Will sync and check file from tmp before record, write before"
" read."
),
)
group_record.add_argument(
"--selenium_video_time_waiting_end",
default=5,

View file

@ -26,7 +26,7 @@ class VideoRecorder(object):
self,
driver,
filename="video.webm",
framerate="25/1",
framerate="33/1",
width=None,
height=None,
):

View file

@ -8,7 +8,6 @@ import sys
import time
import selenium_lib
from selenium.webdriver.common.by import By
@ -35,18 +34,24 @@ def run(config, selenium_tool):
# Trouvez les éléments du formulaire
courriel_input = selenium_tool.driver.find_element(By.NAME, "login")
mot_de_passe_input = selenium_tool.driver.find_element(By.NAME, "password")
try:
connexion_button = selenium_tool.driver.find_element(
By.XPATH,
"/html/body/div/div/div/form/div[3]/button",
# '//button[contains(text(), "Log in")]'
)
except Exception:
connexion_button = selenium_tool.driver.find_element(
By.XPATH,
"/html/body/div/main/div/form/div[3]/button",
# '//button[contains(text(), "Connexion")]'
)
div_connexion_button = selenium_tool.driver.find_element(
By.CLASS_NAME, "oe_login_buttons"
)
connexion_button = div_connexion_button.find_element(
By.CLASS_NAME, "btn-primary"
)
# try:
# connexion_button = selenium_tool.driver.find_element(
# By.XPATH,
# "/html/body/div/div/div/form/div[3]/button",
# # '//button[contains(text(), "Log in")]'
# )
# except Exception:
# connexion_button = selenium_tool.driver.find_element(
# By.XPATH,
# "/html/body/div/main/div/form/div[3]/button",
# # '//button[contains(text(), "Connexion")]'
# )
# Remplissez le courriel et le mot de passe
courriel_input.send_keys(config.default_email_auth)
@ -105,6 +110,7 @@ def run(config, selenium_tool):
if config.open_dashboard:
selenium_tool.click(
"/html/body/header/nav/div/div[1]/div[2]/div/div/div/ul/li[3]/a",
timeout=15,
)
# Open conversation chat

View file

@ -4,13 +4,13 @@
# This script need only basic importation
import argparse
import logging
import json
import logging
import os
import shutil
import subprocess
import sys
import time
import subprocess
logging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO"))
@ -100,6 +100,11 @@ def get_config():
action="store_true",
help="Install developer environment.",
)
parser.add_argument(
"--partial_install",
action="store_true",
help="Preparation environment file, without installation. Docker need this",
)
parser.add_argument(
"--force_install",
action="store_true",
@ -457,7 +462,15 @@ class Update:
# Force create addons link
if os.path.isdir(ADDONS_PATH):
os.remove(ADDONS_PATH)
if os.path.islink(ADDONS_PATH):
os.remove(ADDONS_PATH)
else:
os.rename(
ADDONS_PATH,
ADDONS_PATH
+ "_"
+ time.strftime('%Yy%mm%dd-%Hh%Mm%Ss'),
)
os.symlink(addons_path_with_version, ADDONS_PATH)
return status
@ -688,13 +701,14 @@ def main():
_logger.info("Validate environment")
status = 0
if update.config.install_dev or update.config.is_in_switch:
if update.config.install_dev or update.config.partial_install or update.config.is_in_switch:
status = update.validate_environment()
if update.config.install:
status = update.install_system()
if (
update.config.force_install
or update.config.install_dev
or update.config.partial_install
or update.config.is_in_switch
) and not status:
update.update_environment()

View file

@ -14,7 +14,7 @@ if [ ! -f "${CONFIG_PATH}" ]; then
fi
fi
coverage run -p ./odoo/odoo-bin -c "${CONFIG_PATH}" --limit-time-real 99999 --limit-time-cpu 99999 --log-level=test --test-enable --stop-after-init "$@"
coverage run -p ./odoo/odoo-bin -c "${CONFIG_PATH}" --limit-time-real 99999 --limit-time-cpu 99999 --log-level=test --test-enable --no-http --stop-after-init "$@"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "${Red}Error${Color_Off} run.sh"