diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..351bb72 --- /dev/null +++ b/.flake8 @@ -0,0 +1,13 @@ +[flake8] +max-line-length = 80 +max-complexity = 16 +# B = bugbear +# B9 = bugbear opinionated (incl line length) +select = C,E,F,W,B,B9 +# E203: whitespace before ':' (black behaviour) +# E501: flake8 line length (covered by bugbear B950) +# W503: line break before binary operator (black behaviour) +ignore = E203,E501,W503 +exclude = + ./.git + .eggs/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cfd41e..50fd137 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,13 +9,42 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added -- Code generator for Odoo module +- Adapt script to give an execution status +- Multilingual markdown +- Guide to use Cloudflare with DDNS +- Script to check git diff and ignore date +- Repo with ERPLibre image +- Improve git repo usage, filter repo by use case + +#### Makefile +- Format code +- Code generator test +- Addons installation +- OS installation +- Restore database +- Docker execution + +#### Code generator +- Code generator for Odoo module, depending of ERPLibre - Support map geoengine +- Support i18n +- Script to transform Python and XML to Python code writer script to regenerate themselves ### Changed -- Downgrade postgresql to 11 to support postgis -- Travis CI +- Update Python dependency with Poetry +- Format all Python code with black + +#### Docker + +- Use buster python 3.7.7 image to remove pyenv +- Update Postgresql to support Postgis +- Support volume addons /ERPLibre/addons/addons + +### Fixed + +- Ubuntu installation +- Poetry installation ## [1.1.1] - 2020-12-11 diff --git a/Makefile b/Makefile index d240c9e..44dcce4 100644 --- a/Makefile +++ b/Makefile @@ -46,11 +46,15 @@ run_test: .PHONY: run_code_generator run_code_generator: echo http://localhost:8069 + # -$(BROWSER) http://localhost:8069 ./run.sh --database code_generator ############# # INSTALL # ############# +.PHONY: install +install:install_os install_dev + .PHONY: install_dev install_dev: ./script/install_locally_dev.sh @@ -87,86 +91,193 @@ db_version: db_drop_db_test: ./.venv/bin/python3 ./odoo/odoo-bin db --drop --database test +.PHONY: db_drop_db_test2 +db_drop_db_test2: + ./.venv/bin/python3 ./odoo/odoo-bin db --drop --database test2 + +.PHONY: db_drop_db_test3 +db_drop_db_test3: + ./.venv/bin/python3 ./odoo/odoo-bin db --drop --database test3 + .PHONY: db_drop_db_code_generator db_drop_db_code_generator: ./.venv/bin/python3 ./odoo/odoo-bin db --drop --database code_generator +.PHONY: db_drop_db_template +db_drop_db_template: + ./.venv/bin/python3 ./odoo/odoo-bin db --drop --database template + .PHONY: db_restore_erplibre_base_db_test db_restore_erplibre_base_db_test: - ./.venv/bin/python3 ./odoo/odoo-bin db --restore --restore_image erplibre_base --database test + ./script/db_restore.py --database test + +.PHONY: db_restore_erplibre_base_db_test_module_test +db_restore_erplibre_base_db_test_module_test: db_restore_erplibre_base_db_test + ./script/addons/install_addons.sh test test + +.PHONY: db_restore_erplibre_base_db_test2 +db_restore_erplibre_base_db_test2: + ./script/db_restore.py --database test2 + +.PHONY: db_restore_erplibre_base_db_test3 +db_restore_erplibre_base_db_test3: + ./script/db_restore.py --database test3 .PHONY: db_restore_erplibre_website_db_test db_restore_erplibre_website_db_test: - ./.venv/bin/python3 ./odoo/odoo-bin db --restore --restore_image erplibre_website --database test + ./script/db_restore.py --database test --image erplibre_website .PHONY: db_restore_erplibre_website_chat_crm_db_test db_restore_erplibre_website_chat_crm_db_test: - ./.venv/bin/python3 ./odoo/odoo-bin db --restore --restore_image erplibre_website_chat_crm --database test + ./script/db_restore.py --database test --image erplibre_website_chat_crm + +.PHONY: db_restore_erplibre_ecommerce_base_db_test +db_restore_erplibre_ecommerce_base_db_test: + ./script/db_restore.py --database test --image erplibre_ecommerce_base .PHONY: db_restore_erplibre_base_db_code_generator db_restore_erplibre_base_db_code_generator: - ./.venv/bin/python3 ./odoo/odoo-bin db --restore --restore_image erplibre_base --database code_generator + ./script/db_restore.py --database code_generator + +.PHONY: db_restore_erplibre_base_db_template +db_restore_erplibre_base_db_template: + ./script/db_restore.py --database template ######################### # Addons installation # ######################### +.PHONY: addons_install_code_generator_basic +addons_install_code_generator_basic: db_restore_erplibre_base_db_code_generator + ./script/addons/install_addons_dev.sh code_generator code_generator + +.PHONY: addons_install_code_generator_featured +addons_install_code_generator_featured: db_restore_erplibre_base_db_code_generator + ./script/addons/install_addons_dev.sh code_generator code_generator_cron,code_generator_hook,code_generator_portal + +.PHONY: addons_install_code_generator_full +addons_install_code_generator_full: db_restore_erplibre_base_db_code_generator + ./script/addons/install_addons_dev.sh code_generator code_generator_cron,code_generator_hook,code_generator_portal,code_generator_db_servers,code_generator_website_snippet,code_generator_geoengine,code_generator_theme_website,code_generator_website_leaflet + .PHONY: addons_install_code_generator_demo addons_install_code_generator_demo: - ./run.sh --no-http --stop-after-init -d code_generator -i code_generator_demo -u code_generator_demo + ./script/addons/install_addons_dev.sh code_generator code_generator_demo .PHONY: addons_uninstall_code_generator_demo addons_uninstall_code_generator_demo: - ./run.sh --no-http --stop-after-init -d code_generator --uninstall code_generator_demo + ./script/addons/uninstall_addons.sh code_generator code_generator_demo .PHONY: addons_reinstall_code_generator_demo addons_reinstall_code_generator_demo: addons_uninstall_code_generator_demo addons_install_code_generator_demo -.PHONY: addons_install_code_generator_demo_portal -addons_install_code_generator_demo_portal: - ./run.sh --no-http --stop-after-init -d code_generator -i code_generator_demo_portal -u code_generator_demo_portal - -.PHONY: addons_uninstall_code_generator_demo_portal -addons_uninstall_code_generator_demo_portal: - ./run.sh --no-http --stop-after-init -d code_generator --uninstall code_generator_demo_portal - ./run.sh --no-http --stop-after-init -d code_generator --uninstall code_generator_demo - -.PHONY: addons_reinstall_code_generator_demo_portal -addons_reinstall_code_generator_demo_portal: addons_uninstall_code_generator_demo_portal addons_install_code_generator_demo_portal - -.PHONY: addons_install_demo_portal_on_code_generator -addons_install_demo_portal_on_code_generator: - ./run.sh --no-http --stop-after-init -d code_generator -i demo_portal -u demo_portal - -.PHONY: addons_uninstall_demo_portal_on_code_generator -addons_uninstall_demo_portal_on_code_generator: - ./run.sh --no-http --stop-after-init -d code_generator --uninstall demo_portal - -.PHONY: addons_reinstall_demo_portal_on_code_generator -addons_reinstall_demo_portal_on_code_generator: addons_uninstall_demo_portal_on_code_generator addons_install_demo_portal_on_code_generator - -.PHONY: addons_install_demo_portal_on_test -addons_install_demo_portal_on_test: - ./run.sh --no-http --stop-after-init -d test -i demo_portal -u demo_portal - -.PHONY: addons_uninstall_demo_portal_on_test -addons_uninstall_demo_portal_on_test: - ./run.sh --no-http --stop-after-init -d test --uninstall demo_portal - -.PHONY: addons_reinstall_demo_portal_on_test -addons_reinstall_demo_portal_on_test: addons_uninstall_demo_portal_on_test addons_install_demo_portal_on_test - .PHONY: addons_install_all_code_generator_demo -addons_install_all_code_generator_demo: - ./run.sh --no-http --stop-after-init -d code_generator -i code_generator_demo -u code_generator_demo - ./run.sh --no-http --stop-after-init -d code_generator -i code_generator_demo_export_helpdesk -u code_generator_demo_export_helpdesk - ./run.sh --no-http --stop-after-init -d code_generator -i code_generator_demo_internal -u code_generator_demo_internal - ./run.sh --no-http --stop-after-init -d code_generator -i code_generator_demo_portal -u code_generator_demo_portal - ./run.sh --no-http --stop-after-init -d code_generator -i code_generator_demo_theme_website -u code_generator_demo_theme_website - ./run.sh --no-http --stop-after-init -d code_generator -i code_generator_demo_website_leaflet -u code_generator_demo_website_leaflet +addons_install_all_code_generator_demo: db_restore_erplibre_base_db_code_generator + ./script/addons/install_addons_dev.sh code_generator code_generator_demo,code_generator_demo_export_helpdesk,code_generator_demo_internal,code_generator_demo_portal,code_generator_demo_theme_website,code_generator_demo_website_leaflet,code_generator_demo_website_snippet +# ./script/addons/install_addons_dev.sh code_generator code_generator_demo +# ./script/addons/install_addons_dev.sh code_generator code_generator_demo_export_helpdesk +# ./script/addons/install_addons_dev.sh code_generator code_generator_demo_internal +# ./script/addons/install_addons_dev.sh code_generator code_generator_demo_portal +# ./script/addons/install_addons_dev.sh code_generator code_generator_demo_theme_website +# ./script/addons/install_addons_dev.sh code_generator code_generator_demo_website_leaflet +# ./script/addons/install_addons_dev.sh code_generator code_generator_demo_website_snippet -.PHONY: addons_uninstall_all_code_generator_demo -addons_uninstall_all_code_generator_demo: - ./run.sh --no-http --stop-after-init -d code_generator --uninstall code_generator_demo,code_generator_demo_export_helpdesk,code_generator_demo_internal,code_generator_demo_portal,code_generator_demo_theme_website,code_generator_demo_website_leaflet +.PHONY: addons_install_all_code_generator_template +addons_install_all_code_generator_template: + ./script/addons/install_addons_dev.sh template demo_portal,auto_backup + ./script/addons/install_addons_dev.sh template code_generator_template_demo_portal code_generator_template_demo_sysadmin_cron + +.PHONY: addons_install_all_generated_demo +addons_install_all_generated_demo: + ./script/addons/install_addons_dev.sh template demo_export_helpdesk,demo_internal,demo_portal,demo_website_leaflet,demo_website_snippet + # TODO support installation theme with cli + #./script/addons/install_addons_dev.sh template theme_website_demo_code_generator + +.PHONY: addons_install_all_code_generator +addons_install_all_code_generator: + ./script/addons/install_addons_dev.sh code_generator code_generator_auto_backup + +########## +# test # +########## +.PHONY: test +test: test_format test_code_generator_generation test_code_generator_generation_extra test_code_generator_code_i18n test_code_generator_code_i18n_extra + +.PHONY: test_format +test_format: + ./script/maintenance/black.sh --check ./addons/TechnoLibre_odoo-code-generator/ + ./script/maintenance/black.sh --check ./addons/TechnoLibre_odoo-code-generator-template/ + +.PHONY: test_code_generator_generation +test_code_generator_generation: + ./script/code_generator/check_git_change_code_generator.sh ./addons/TechnoLibre_odoo-code-generator-template + ./script/db_restore.py --database code_generator + ./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_demo,code_generator_demo_export_helpdesk,code_generator_demo_internal,code_generator_demo_portal,code_generator_demo_theme_website,code_generator_demo_website_leaflet,code_generator_demo_website_snippet ./addons/TechnoLibre_odoo-code-generator-template +# ./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_demo ./addons/TechnoLibre_odoo-code-generator-template +# ./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_demo_export_helpdesk ./addons/TechnoLibre_odoo-code-generator-template +# ./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_demo_internal ./addons/TechnoLibre_odoo-code-generator-template +# ./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_demo_portal ./addons/TechnoLibre_odoo-code-generator-template +# ./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_demo_theme_website ./addons/TechnoLibre_odoo-code-generator-template +# ./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_demo_website_leaflet ./addons/TechnoLibre_odoo-code-generator-template +# ./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_demo_website_snippet ./addons/TechnoLibre_odoo-code-generator-template + +.PHONY: test_code_generator_generation_extra +test_code_generator_generation_extra: + ./script/code_generator/check_git_change_code_generator.sh ./addons/OCA_server-tools/auto_backup + ./script/db_restore.py --database code_generator + ./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_auto_backup ./addons/OCA_server-tools/auto_backup + +.PHONY: test_code_generator_code_i18n +test_code_generator_code_i18n: + ./script/code_generator/check_git_change_code_generator.sh ./addons/TechnoLibre_odoo-code-generator-template + ./script/db_restore.py --database template + ./script/addons/install_addons_dev.sh template demo_portal + ./script/code_generator/install_and_test_code_generator.sh template code_generator_template_demo_portal ./addons/TechnoLibre_odoo-code-generator-template + +.PHONY: test_code_generator_code_i18n_extra +test_code_generator_code_i18n_extra: + ./script/code_generator/check_git_change_code_generator.sh ./addons/OCA_server-tools/auto_backup + ./script/db_restore.py --database template + ./script/addons/install_addons_dev.sh template auto_backup + ./script/code_generator/install_and_test_code_generator.sh template code_generator_template_demo_sysadmin_cron ./addons/TechnoLibre_odoo-code-generator-template + # To support i18n in auto_backup + ./script/code_generator/check_git_change_code_generator.sh ./addons/OCA_server-tools/auto_backup + +############## +# terminal # +############## +.PHONY: open_terminal +open_terminal: + ./script/open_terminal_code_generator.sh + +############ +# format # +############ +.PHONY: format +format: format_code_generator format_code_generator_template format_script + +.PHONY: format_code_generator +format_code_generator: + ./script/maintenance/black.sh ./addons/TechnoLibre_odoo-code-generator/ + #./script/maintenance/prettier_xml.sh ./addons/TechnoLibre_odoo-code-generator/ + +.PHONY: format_code_generator_template +format_code_generator_template: + ./script/maintenance/black.sh ./addons/TechnoLibre_odoo-code-generator-template/ + #./script/maintenance/prettier_xml.sh ./addons/TechnoLibre_odoo-code-generator-template/ + +.PHONY: format_script +format_script: + ./script/maintenance/black.sh ./script/ + +########### +# clean # +########### +.PHONY: clean_code_generator_template +clean_code_generator_template: + ./script/repo_revert_git_diff_date_from_code_generator.py + +.PHONY: clean_test +clean_test: + cd addons/OCA_server-tools; git stash; git clean -fd ############ # docker # @@ -219,6 +330,11 @@ docker_clean_all: repo_clear_all: ./script/clean_repo_manifest.sh +# configure all repo +.PHONY: repo_configure_all +repo_configure_all: + ./script/update_manifest_local_dev.sh + # configure only group code_generator .PHONY: repo_configure_group_code_generator repo_configure_group_code_generator: @@ -251,9 +367,18 @@ config_gen_all: # generate config repo code_generator .PHONY: config_gen_code_generator config_gen_code_generator: - ./script/git_repo_update_group.py --group base,code_generator + ./script/git_repo_update_group.py --group base,code_generator ./script/install_locally.sh +########## +# I18n # +########## + +# i18n generation demo_portal +.PHONY: i18n_generate_demo_portal +i18n_generate_demo_portal: + ./.venv/bin/python3 ./odoo/odoo-bin i18n --database code_generator --module demo_portal --addons_path addons/TechnoLibre_odoo-code-generator + ################### # Documentation # ################### @@ -304,3 +429,8 @@ doc_user: .PHONY: doc_clean_user doc_clean_user: make -C doc/odoo_documentation-user clean + +# documentation markdown +.PHONY: doc_markdown +doc_markdown: + ./.venv/multilang_md.py $(shell pwd)/doc/CODE_GENERATOR.base.md diff --git a/README.md b/README.md index 8f0ea32..747d535 100644 --- a/README.md +++ b/README.md @@ -7,19 +7,18 @@ Clone the project git clone https://github.com/ERPLibre/ERPLibre.git cd ERPLibre ``` -Support Ubuntu 18.04 and OSX +Support Ubuntu 18.04, 20.04 and OSX. The installation duration is more than 30 minutes. ```bash -./script/install_dev.sh -./script/install_locally_prod.sh +make install ``` -Update your configuration if you need to run somehere than 127.0.0.1, file `config.conf` +Update your configuration if you need to run from another interface than 127.0.0.1, file `config.conf` ``` #xmlrpc_interface = 127.0.0.1 #netrpc_interface = 127.0.0.1 ``` -Run +Ready to execute ```bash -./run.sh +make run ``` ## Discover guide @@ -36,3 +35,9 @@ Run # git-repo To change repository like addons, see [GIT_REPO.md](doc/GIT_REPO.md) + +# Test +Execute ERPLibre test with his code generator. +```bash +time make test +``` diff --git a/doc/CODE_GENERATOR.base.md b/doc/CODE_GENERATOR.base.md new file mode 100644 index 0000000..95ea584 --- /dev/null +++ b/doc/CODE_GENERATOR.base.md @@ -0,0 +1,932 @@ + + + + + + +# How to generate code + + +# Comment générer du code + + +Never run this on production environment, this create circular dependencies and will cause frustration to clean damage. + +Ne jamais exécuter le générateur de code dans un environnement de production, il y a création de dépendance circulaire pouvant causer de la frustration à nettoyer tous les dommages. D'ailleurs, il est nécessaire d'exécuter en mode développement, avec l'argument `--dev all`. + +L'objectif du générateur de code est de : +- utilisation du générateur via l'interface web; +- créer un nouveau module; +- modifier un module existant; +- exécuter les tests. + +Ce générateur de code, pour ERPLibre, a tout avantage d'être sous licence AGPLv3 et nécessite d'être dans une communauté active de logiciels libres puisqu'il sera plus efficace d'accéder à du code pour l'auto-apprentissage. Le libre permet : +- (Utiliser) L'utilisation de module sans restriction; +- (Copier) Copier des modules pour faciliter leur maintenance et leur pérennité; +- (Étudier) De comprendre le comportement des fonctionnalités; +- (Modifier) Modifier un module existant pour l'améliorer et pouvoir le redistribuer à la communauté; + + +``` +Modules +############ ################## ########## +# Template # -> # Code_Generator # -> # Module # +############ ################## ########## +``` + + +1. [A Template generates a Template or a Code_Generator.](#create-your-first-code_generator) +2. A Code_Generator generates a Module. +3. A Template reads a Module to generate a Code_Generator. + + +Il y a 3 types de module dans le contexte du générateur de code : +1. [A Template generates a Template or a Code_Generator.](#crer-votre-premier-code_generator), le chef d'orchestre qui permet de gérer plusieurs générateur de code. +2. Un Code_Generator génère un module, c'est un moule à module généré. +3. Un Template lit un module pour générer un Code_Generator. + + +Warning, be careful to your code, always commit after a manipulation, because the mode enable_sync_code erase data, only the git will save you! + + +Attention pour ne pas écraser votre code, toujours commiter après une manipulation, l'utilisation du mode enable_sync_code peut effacer des données. Seul git vous sauvera! + + +## Manual generator with web interface + + +## Générateur de module avec l'interface web + + +TODO + + +At root path of ERPLibre git project, run: + + +Niveau intermédiaire. + +Pour utiliser le générateur de code, il faut ouvrir l'interface web qui permet la configuration du logiciel à générer. Un des choix suivants : + +Générateur de code de base : + +```bash +make addons_install_code_generator_basic +make run_code_generator +``` + + +Générateur de code avec fonctionnalités avancées : + +```bash +make addons_install_code_generator_featured +make run_code_generator +``` + + +Générateur de code avec fonctionnalités complètes : + +```bash +make addons_install_code_generator_full +make run_code_generator +``` + +Ouvrir le navigateur sur [http://localhost:8069](http://localhost:8069). Utilisateur `test` et mot de passe `test`. Une fois connecté, ouvrir sur [http://localhost:8069/web?debug=](http://localhost:8069/web?debug=) pour activer le déverminage. + +Ouvrir l'application `Code Generator` et créer un `Module`. Remplir les champs requis et générer avec `Action/Generate code`. + + +### Create model-view module + + +### Créer un module modèle avec vue + + +```bash +make addons_install_code_generator_basic +make run_code_generator +``` + + +TODO + + +Pour des références techniques, voir module : +- code_generator_template_demo_portal +- code_generator_demo_portal +- code_generator_demo_internal +- demo_portal +- demo_internal + + +TODO + + +Aller dans le module «Code generator» et créer un module «test». +- Sur l'onglet «Information», activer «Enable Sync Code» +- Sur l'onglet «Technical Data», activer «Application». +- Sur l'onglet «Elements»/«Models», ajouter un modèle et sauvegarder. +- Appuyer sur le bouton «Views» + - Appuyer «Generate» + +Générer le module avec «Action/Générer code». + +Tester sur : + +```bash +make db_restore_erplibre_base_db_test_module_test +make run_test +``` + + +### Create portal module + + +### Créer un module avec la vue portail + + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_portal +make run_code_generator +``` + + +TODO + + +Pour des références techniques, voir module : +- code_generator_template_demo_portal +- code_generator_demo_portal +- code_generator_demo_internal +- demo_portal +- demo_internal + + +TODO + + +Aller dans le module «Code generator» et créer un module «test». +- Sur l'onglet «Information», activer «Enable Sync Code» +- Sur l'onglet «Technical Data», activer «Application». +- Sur l'onglet «Elements»/«Models», ajouter un modèle et sauvegarder. +- Appuyer sur le bouton «Views» + - Désactiver «Enable all feature» + - Dans l'onglet «Portal» + - Activer «Enable portal feature» + - Appuyer «Generate» + +Générer le module avec «Action/Générer code». + +Tester sur : + +```bash +make db_restore_erplibre_base_db_test_module_test +make run_test +``` + + +TODO + + +Ajouter des données dans votre nouveau module «test». + +Aller sur le portail pour visualiser les données, à l'adresse `/my`. + + +### Create hook module for installation + + +### Créer un module crochet pour l'installation + + +TODO + + +Le crochet nommé «hook» permet d'exécuter du code en +- Pré-initialisation du module +- Post-initialisation du module +- Désinstallation du module + +Pour des références techniques, voir module : +- code_generator_demo + + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_hook +make run_code_generator +``` + + +TODO + + +Aller dans le module «Code generator» et créer un module «test». +- Sur l'onglet «Information», activer «Enable Sync Code» +- Sur l'onglet «Hook» + - Activer «Show post_init_hook» + - Ajouter le code suivant dans la fenêtre qui est apparu : + + +```python +with api.Environment.manage(): + env = api.Environment(cr, SUPERUSER_ID, {}) + print("Hello World") +``` + + +TODO + + +Générer le module avec «Action/Générer code». + +Tester et chercher dans la console "Hello World" : + +```bash +make db_restore_erplibre_base_db_test_module_test +``` + + +### Create cron module + + +### Créer un module d'exécution de code sur une plage horaire + + +TODO + + +Permettre d'exécuter du code basé sur des séquences de temps ou des moments spécifiques à répétition. + +Pour des références techniques, voir module : +- code_generator_template_demo_sysadmin_cron +- code_generator_auto_backup +- auto_backup + + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_cron +make run_code_generator +``` + + +TODO + + +Aller dans le module «Code generator» et créer un module «test». +- Sur l'onglet «Information», activer «Enable Sync Code» +- Sur l'onglet «Cron» + - Ajouter un cron + - Choisir le «Modèle» : «Contact» + - Choisir unité de temps en «Minutes» dans «Exécuter tous les» + - Modifier «Nombre d'appel» à -1 pour exécution sans arrêt. + - Activer «Force nextcall» pour permettre que l'interval soit basé sur le moment d'installation du module + - Ajouter le code : + + +```python +log("Coucou") +``` + + +TODO + + +Générer le module avec «Action/Générer code». + +Tester : + +```bash +make db_restore_erplibre_base_db_test_module_test +make run_test +``` + + +TODO + + +Avec les outils développeur, aller regarder les logs dans l'application «Configuration»/Technique/«Structure de base de donnée»/Historisation. Des informations dans le temps apparaîtront avec le mot «Coucou». + + +### Create website snippet module + + +### Créer un module de snippet pour le site web + + +In progress + + +En progression, non terminé + + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_website_snippet +make run_code_generator +``` + + +TODO + + +Pour des références techniques, voir module : +- code_generator_template_demo_website_snippet +- code_generator_demo_website_snippet +- demo_website_snippet + +Puis dans le repo addons/ERPLibre_erplibre_theme_addons, branche code_generator_erplibre_website_snippets +- code_generator_erplibre_website_snippets + + +TODO + + +Aller dans le module «Code generator» et créer un module «test». +- Sur l'onglet «Information», activer «Enable Sync Code» + +TODO Incomplet + + +### Create geoengine with Leaflet module + + +### Créer un module de gestion de coordonnée géospatial avec Leaflet + + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_website_leaflet +make run_code_generator +``` + + +TODO + + +Pour des références techniques, voir module : +- code_generator_demo_website_leaflet +- demo_website_leaflet + + +TODO + + +Aller dans le module «Code generator» et créer un module «test». +- Sur l'onglet «Information», activer «Enable Sync Code» +- Sur l'onglet «Technical Data» + - Activer «Application» + - Ajouter la dépendance du geo_engine «Geospatial support for System» +- Sur l'onglet «Elements»/«Models» + - Ajouter un modèle «test» + - Ajouter un champs de type «geo_». Le «geo_point» fonctionne bien. Si vous ajoutez plusieurs géométrie, tel que «geo_point», «geo_line» et «geo_polygon», il faut ajouter le champs «type» avec les valeurs du nom des champs choisis en option de sélection, exemple `[('geo_point', 'Geo Point'),('geo_polygon', 'Geo Polygon')]` + - Ajouter le champs optionnel «html_text» de type «html» pour pouvoir afficher du texte dessus. +- Appuyer sur le bouton «Controllers» + - Ajouter le modèle du module actuel, «test» + - Appuyer «Generate» +- Appuyer sur le bouton «Views» + - Désactiver «Enable all feature» + - Dans l'onglet «Website» + - Activer «Enable website leaflet feature» + - Activer «Enable geoengine feature» + - Appuyer «Generate» + +Générer le module avec «Action/Générer code». + +Tester sur : + +```bash +make db_restore_erplibre_base_db_test_module_test +make run_test +``` + + +TODO + + +Ajouter les permissions d'utilisation, dans l'application «Configuration», «Gérer les droits d'accès». Sur votre utilisateur, ajouter «Geoengine Admin». + +Aller sur l'application «test», créer un «geo_point». + +Aller sur la page du site web, ajouter le snippet «Leaflet». Chercher le point qui a été ajouté sur la carte du monde. + + +### Create theme module for website + + +### Créer un module thème pour site web + + +TODO + + +Pour des références techniques, voir module : +- code_generator_demo_theme_website + + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_theme_website +make run_code_generator +``` + +TODO + + +Aller dans le module «Code generator» et créer un module «test». +- Sur l'onglet «Information», activer «Enable Sync Code» +- Sur l'onglet «Technical Data», activer «Website theme». +- Appuyer sur le bouton «Views» + - Aller à l'onglet «theme_website» + - Mettre les couleurs désirés + - Appuyer «Generate» + +Générer le module avec «Action/Générer code». + +Ce module doit être installé manuellement pour l'instant. + + +```bash +make db_restore_erplibre_base_db_test +./script/addons/install_addons.sh test website +make run_test +``` + + +TODO + + +Aller dans l'interface web, Application «Configuration», «Paramètres Généraux»/«Site Web»/«Choisissez un thème». Installer le thème «TEST». Puis aller la page du site web, les couleurs sont dans «Personnaliser»/«Personnaliser le thème». + + +### Extract data to module + + +### Extraire les données vers un module + + +TODO + + +L'exemple est avec le helpdesk. Installer le module helpdesk_mgmt : + +Pour des références techniques, voir module : +- code_generator_demo_export_helpdesk +- code_generator_demo_export_website +- demo_website_data +- demo_helpdesk_data + + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator helpdesk_mgmt +make run_code_generator +``` + +TODO + + +Créer un ticket dans l'application «Helpdesk» dans l'interface web. + +Aller dans le module «Code generator» et créer un module «test». +- Sur l'onglet «Information», activer «Enable Sync Code» +- Sur l'onglet «Technical Data», activer «Only export data». +- Appuyer sur le bouton «Models» + - Choisissez dans «Models» : + - «helpdesk.ticket» + - Activer «Clear field blacklisted» + - Enlever les éléments suivant de «Fields» : + - name + - description + - number + - Appuyer «Generate» + +Générer le module avec «Action/Générer code». + +```bash +make db_restore_erplibre_base_db_test_module_test +``` + + +### Migrate external database into module of migration + + +### Migrer une base de donnée externe en module de migration + + +TODO + + +En progression. + +Pour des références techniques, voir module : +- ?? + + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_db_servers +make run_code_generator +``` + +TODO + + +Aller dans le module «Code generator», menu «Databases»/«Databases» et créer un connecteur vers une base de donnée. + +À compléter... + + +### Migrate from website to model with PDF data + + +### Migrer d'un site web vers un modèle de donnée avec des données format PDF + + +TODO + + +En progression, ce n'est pas encore supporté via l'interface web. + +Cette technique permet d'aller lire du Javascript sur un site web pour lire ensuite le HTML, c'est à dire la vue, et en comprendre l'information pour créer un modèle. + +Pour des références techniques, voir module : +- code_generator_demo_converter_js +- business_plan_import_pdf + +Attention, il faut mettre à jour les variables, dans les fichiers hook.py qui permettront d'extraire des données sur le site web à copier. Aucun exemple public n'est accessible pour le moment. + + +```bash +make db_restore_erplibre_base_db_code_generator +./script/addons/install_addons_dev.sh code_generator code_generator_demo_converter_js +make db_restore_erplibre_base_db_test +./script/addons/install_addons_dev.sh test business_plan_import_pdf +``` + + +## Prepare a DB + + +## Préparer une BD + + +This will destroy and create database named `code_generator`. + + +Ceci va détruire et créer une base de donnée nommé `code_generator`. + + +```bash +make db_restore_erplibre_base_db_code_generator +``` + + +TODO + + +Pour de meilleur performance d'exécution, réduire la quantité de «repo addons» permet d'accélérer l'installation, exécuter : + + +```bash +make config_gen_code_generator +``` + + +TODO + + +Pour revenir à la configuration normal, en production, exécuter : + + +```bash +make config_gen_all +``` + + +## Create your first `Code_Generator` + + +## Créer votre premier `Code_Generator` + + +Edit [Code Generator Demo](./../addons/TechnoLibre_odoo-code-generator/code_generator_demo/hooks.py) and update `# TODO HUMAN:` + + +Modifier [Code Generator Demo](./../addons/TechnoLibre_odoo-code-generator/code_generator_demo/hooks.py) et mettre à jour `# TODO HUMAN:` + + +### Generate a `Template` + + +### Générer un `Template` + + +By default, `code_generator_demo` generate itself, `code_generator_demo`. + +Name your module begin with `MODULE_NAME = "code_generator_template_"` + +The value "enable_template_code_generator_demo" at `False` to create a new template module, else this will recreate `code_generator_demo`. + + +Par défaut, installer le module code_generator_demo va générer le module `code_generator_demo`. + +Nommer votre module en commençant par `MODULE_NAME = "code_generator_template_"` + + +Example, to generate the module `code_generator_template_demo_website_snippet`, change value: + + +Un exemple, générer le module `code_generator_template_demo_website_snippet`, changer la variable `value`: + + +```python +MODULE_NAME = "code_generator_template_demo_website_snippet" +[...] +value["enable_template_website_snippet_view"] = True +[...] +lst_depend = [ + "code_generator", + "code_generator_website_snippet", +] +``` + + +Generate new module, this will overwrite `code_generator_template_demo_website_snippet`: + + +Générer un nouveau module, ceci va écraser `code_generator_template_demo_website_snippet` : + + +```bash +make db_restore_erplibre_base_db_code_generator +make addons_install_code_generator_demo +``` + + +### Generate a Code_Generator (continue template) + + +### Générer un Code_Generator (suite de template) + + +Name your module begin with `MODULE_NAME = "code_generator_"` + + +Au besoin, renommé votre module qui débute par `MODULE_NAME = "code_generator_"` + + +Continue the example, you generated the template `code_generator_template_demo_website_snippet`, this will overwrite `code_generator_demo_website_snippet`: + + +Pour continuer avec l'exemple, généré le template `code_generator_template_demo_website_snippet`, ceci va écraser `code_generator_demo_website_snippet`: + + +```bash +make db_restore_erplibre_base_db_template +./script/addons/install_addons_dev.sh template code_generator_template_demo_website_snippet +``` + + +Go to section [Create your first module](#create-your-first-module) + + +Aller à la section [Créer votre premier module](#crer-votre-premier-module) + + +### Generate a Code_Generator_Demo + + +### Générer un Code_Generator_Demo + + +TODO validate this + + +Name your module begin with `MODULE_NAME = "code_generator_demo_"` + +Disable `enable_template_code_generator_demo` + + +Nommé votre module qui débute par `MODULE_NAME = "code_generator_demo_"` + +Désactiver la variable `enable_template_code_generator_demo` + + +```python +value["enable_template_code_generator_demo"] = False +``` + + +Continue the example, you generated the template `code_generator_template_demo_website_snippet`, this will overwrite `code_generator_demo_website_snippet`: + + +Pour continuer avec l'exemple, généré le template `code_generator_template_demo_website_snippet`, ceci va écraser `code_generator_demo_website_snippet`: + + +```bash +./script/addons/install_addons_dev.sh code_generator code_generator_template_demo_website_snippet +``` + + +## Create your first Module + + +## Créer votre premier module + + +Continue example of code_generator_demo_website_snippet to generate your first module. Update next value: + + +Continuer avec l'exemple du `code_generator_demo_website_snippet` pour générer votre premier module. Mettre à jour les valeurs suivantes : + + +```python +"application": True, +"category_id": env.ref("base.module_category_website").id, +[...] +# Add dependencies +lst_depend = [ + "website", +] +``` + + +Generate your module: + + +Générer votre module : + + +```bash +make db_restore_erplibre_base_db_code_generator +./script/addons/install_addons_dev.sh code_generator code_generator_demo_website_snippet +``` + + +Now, you can test it! Note, you cannot install a generated module with is code_generator associated, because duplicated +models! + + +Maintenant, vous pouvez le tester! Pour info, on ne peut pas installer un module généré avec son générateur de code associé, puisqu'il y a une duplication des modèles! + + + + +Il est maintenant possible de modifier les paramètres via l'interface manuelle pour ce module, puis le regénérer en effaçant la BD. Ajouter un modèle et une vue. + + +```bash +make db_restore_erplibre_base_db_code_generator +./script/addons/install_addons_dev.sh code_generator code_generator_demo_website_snippet +``` + + + + +Faire la boucle d'amélioration, commiter le code pour avoir une traçabilité des changements. + +Ensuite, modifier la valeur `enable_sync_template` à `True`, `enable_template_wizard_view` à `True`, `template_model_name` avec un modèle existant, dans le fichier template, de l'exemple `code_generator_template_demo_website_snippet`. + + +```python +value["enable_sync_template"] = True +value["enable_template_wizard_view"] = True +value["template_model_name"] = "test;test2" +``` + + +TODO + + +Exécuter l'installation du template pour qu'il se synchronise sur le module généré. + + +```bash +make db_restore_erplibre_base_db_template +./script/addons/install_addons_dev.sh template demo_website_snippet +./script/addons/install_addons_dev.sh template code_generator_template_demo_website_snippet +``` + + +Ready to test, generate your module: + + +Prêt à tester, générer le module : + + +```bash +# Optional, reset test database +make db_restore_erplibre_base_db_test +./script/addons/install_addons_dev.sh test demo_website_snippet +``` + + +# How to generate i18n translation + + +# Comment générer les traductions i18n + + +You need a template and a module. Be sure you have this line in template: + + +Vous avez besoin d'un template et d'un module. Soyez certain que vous avez cette ligne dans le template : + + +```python +new_module_path = os.path.join(path_module_generate, new_module_name) +code_generator_writer.set_module_translator(new_module_name, new_module_path) +``` + + +First, install the module, example `demo_portal`, and after the template, example `code_generator_template_demo_portal`. + + +Premièrement, installer le module, par exemple `demo_portal`, et ensuite le template, par exemple `code_generator_template_demo_portal`. + + +```bash +make db_restore_erplibre_base_db_code_generator +./script/addons/install_addons_dev.sh test demo_portal +./script/addons/install_addons_dev.sh test code_generator_template_demo_portal +``` + + +# Sync Code_Generator fields from Module + + +# Synchroniser les champs du `Code_Generator` du module + + +This reduce time of programming a `Code_Generator` with sync module field with `Template`. + +You need a `Template`, a `Code_Generator` with his `Module`. Install the `Module` first in clean database and install `Template`, +this will generate the `Code_Generator`, use `enable_sync_template` at True. + +Example: + + +Ceci va permettre de réduire le temps de programmation d'un `Code_Generator` avec la synchronisation des champs de module avec `Template`. + +On a besoin d'un `Template`, un `Code_Generator` accompagné de son module. Installer le module en premier dans une base de donnée nettoyé et installer le `Template`, ceci va générer le `Code_Generator`, utiliser `enable_sync_template` à la valeur `True`. + +Exemple : + + +```bash +make db_restore_erplibre_base_db_code_generator +./script/addons/install_addons_dev.sh test demo_portal +./script/addons/install_addons_dev.sh test code_generator_template_demo_portal +``` + + +# Test the code generator + + +# Tester le générateur de code + + +TODO + + +Une bonne manière de tester un générateur de code est de valider qu'il génère le même code, il ne devient pas nécessaire de tester les fonctionnalités, puisque le comportement est la génération. + + +```bash +make test +``` + + +TODO + + +Tester les générations simples : + + +```bash +make test_code_generator_generation +``` + + +TODO + + +Tester les générations extra : + + +```bash +make test_code_generator_generation_extra +``` + +TODO + + +Tester les générations des templates : + + +```bash +make test_code_generator_template +``` diff --git a/doc/CODE_GENERATOR.fr.md b/doc/CODE_GENERATOR.fr.md new file mode 100644 index 0000000..c7d9a03 --- /dev/null +++ b/doc/CODE_GENERATOR.fr.md @@ -0,0 +1,556 @@ +# Comment générer du code + +Ne jamais exécuter le générateur de code dans un environnement de production, il y a création de dépendance circulaire pouvant causer de la frustration à nettoyer tous les dommages. D'ailleurs, il est nécessaire d'exécuter en mode développement, avec l'argument `--dev all`. + +L'objectif du générateur de code est de : +- utilisation du générateur via l'interface web; +- créer un nouveau module; +- modifier un module existant; +- exécuter les tests. + +Ce générateur de code, pour ERPLibre, a tout avantage d'être sous licence AGPLv3 et nécessite d'être dans une communauté active de logiciels libres puisqu'il sera plus efficace d'accéder à du code pour l'auto-apprentissage. Le libre permet : +- (Utiliser) L'utilisation de module sans restriction; +- (Copier) Copier des modules pour faciliter leur maintenance et leur pérennité; +- (Étudier) De comprendre le comportement des fonctionnalités; +- (Modifier) Modifier un module existant pour l'améliorer et pouvoir le redistribuer à la communauté; + +``` +Modules +############ ################## ########## +# Template # -> # Code_Generator # -> # Module # +############ ################## ########## +``` + +Il y a 3 types de module dans le contexte du générateur de code : +1. [A Template generates a Template or a Code_Generator.](#crer-votre-premier-code_generator), le chef d'orchestre qui permet de gérer plusieurs générateur de code. +2. Un Code_Generator génère un module, c'est un moule à module généré. +3. Un Template lit un module pour générer un Code_Generator. + +Attention pour ne pas écraser votre code, toujours commiter après une manipulation, l'utilisation du mode enable_sync_code peut effacer des données. Seul git vous sauvera! + +## Générateur de module avec l'interface web + +Niveau intermédiaire. + +Pour utiliser le générateur de code, il faut ouvrir l'interface web qui permet la configuration du logiciel à générer. Un des choix suivants : + +Générateur de code de base : +```bash +make addons_install_code_generator_basic +make run_code_generator +``` + +Générateur de code avec fonctionnalités avancées : +```bash +make addons_install_code_generator_featured +make run_code_generator +``` + +Générateur de code avec fonctionnalités complètes : +```bash +make addons_install_code_generator_full +make run_code_generator +``` + +Ouvrir le navigateur sur [http://localhost:8069](http://localhost:8069). Utilisateur `test` et mot de passe `test`. Une fois connecté, ouvrir sur [http://localhost:8069/web?debug=](http://localhost:8069/web?debug=) pour activer le déverminage. + +Ouvrir l'application `Code Generator` et créer un `Module`. Remplir les champs requis et générer avec `Action/Generate code`. + +### Créer un module modèle avec vue + +```bash +make addons_install_code_generator_basic +make run_code_generator +``` + +Pour des références techniques, voir module : +- code_generator_template_demo_portal +- code_generator_demo_portal +- code_generator_demo_internal +- demo_portal +- demo_internal + +Aller dans le module «Code generator» et créer un module «test». +- Sur l'onglet «Information», activer «Enable Sync Code» +- Sur l'onglet «Technical Data», activer «Application». +- Sur l'onglet «Elements»/«Models», ajouter un modèle et sauvegarder. +- Appuyer sur le bouton «Views» + - Appuyer «Generate» + +Générer le module avec «Action/Générer code». + +Tester sur : +```bash +make db_restore_erplibre_base_db_test_module_test +make run_test +``` + +### Créer un module avec la vue portail + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_portal +make run_code_generator +``` + +Pour des références techniques, voir module : +- code_generator_template_demo_portal +- code_generator_demo_portal +- code_generator_demo_internal +- demo_portal +- demo_internal + +Aller dans le module «Code generator» et créer un module «test». +- Sur l'onglet «Information», activer «Enable Sync Code» +- Sur l'onglet «Technical Data», activer «Application». +- Sur l'onglet «Elements»/«Models», ajouter un modèle et sauvegarder. +- Appuyer sur le bouton «Views» + - Désactiver «Enable all feature» + - Dans l'onglet «Portal» + - Activer «Enable portal feature» + - Appuyer «Generate» + +Générer le module avec «Action/Générer code». + +Tester sur : +```bash +make db_restore_erplibre_base_db_test_module_test +make run_test +``` + +Ajouter des données dans votre nouveau module «test». + +Aller sur le portail pour visualiser les données, à l'adresse `/my`. + +### Créer un module crochet pour l'installation + +Le crochet nommé «hook» permet d'exécuter du code en +- Pré-initialisation du module +- Post-initialisation du module +- Désinstallation du module + +Pour des références techniques, voir module : +- code_generator_demo + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_hook +make run_code_generator +``` + +Aller dans le module «Code generator» et créer un module «test». +- Sur l'onglet «Information», activer «Enable Sync Code» +- Sur l'onglet «Hook» + - Activer «Show post_init_hook» + - Ajouter le code suivant dans la fenêtre qui est apparu : + +```python +with api.Environment.manage(): + env = api.Environment(cr, SUPERUSER_ID, {}) + print("Hello World") +``` + +Générer le module avec «Action/Générer code». + +Tester et chercher dans la console "Hello World" : +```bash +make db_restore_erplibre_base_db_test_module_test +``` + +### Créer un module d'exécution de code sur une plage horaire + +Permettre d'exécuter du code basé sur des séquences de temps ou des moments spécifiques à répétition. + +Pour des références techniques, voir module : +- code_generator_template_demo_sysadmin_cron +- code_generator_auto_backup +- auto_backup + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_cron +make run_code_generator +``` + +Aller dans le module «Code generator» et créer un module «test». +- Sur l'onglet «Information», activer «Enable Sync Code» +- Sur l'onglet «Cron» + - Ajouter un cron + - Choisir le «Modèle» : «Contact» + - Choisir unité de temps en «Minutes» dans «Exécuter tous les» + - Modifier «Nombre d'appel» à -1 pour exécution sans arrêt. + - Activer «Force nextcall» pour permettre que l'interval soit basé sur le moment d'installation du module + - Ajouter le code : + +```python +log("Coucou") +``` + +Générer le module avec «Action/Générer code». + +Tester : +```bash +make db_restore_erplibre_base_db_test_module_test +make run_test +``` + +Avec les outils développeur, aller regarder les logs dans l'application «Configuration»/Technique/«Structure de base de donnée»/Historisation. Des informations dans le temps apparaîtront avec le mot «Coucou». + +### Créer un module de snippet pour le site web + +En progression, non terminé + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_website_snippet +make run_code_generator +``` + +Pour des références techniques, voir module : +- code_generator_template_demo_website_snippet +- code_generator_demo_website_snippet +- demo_website_snippet + +Puis dans le repo addons/ERPLibre_erplibre_theme_addons, branche code_generator_erplibre_website_snippets +- code_generator_erplibre_website_snippets + +Aller dans le module «Code generator» et créer un module «test». +- Sur l'onglet «Information», activer «Enable Sync Code» + +TODO Incomplet + +### Créer un module de gestion de coordonnée géospatial avec Leaflet + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_website_leaflet +make run_code_generator +``` + +Pour des références techniques, voir module : +- code_generator_demo_website_leaflet +- demo_website_leaflet + +Aller dans le module «Code generator» et créer un module «test». +- Sur l'onglet «Information», activer «Enable Sync Code» +- Sur l'onglet «Technical Data» + - Activer «Application» + - Ajouter la dépendance du geo_engine «Geospatial support for System» +- Sur l'onglet «Elements»/«Models» + - Ajouter un modèle «test» + - Ajouter un champs de type «geo_». Le «geo_point» fonctionne bien. Si vous ajoutez plusieurs géométrie, tel que «geo_point», «geo_line» et «geo_polygon», il faut ajouter le champs «type» avec les valeurs du nom des champs choisis en option de sélection, exemple `[('geo_point', 'Geo Point'),('geo_polygon', 'Geo Polygon')]` + - Ajouter le champs optionnel «html_text» de type «html» pour pouvoir afficher du texte dessus. +- Appuyer sur le bouton «Controllers» + - Ajouter le modèle du module actuel, «test» + - Appuyer «Generate» +- Appuyer sur le bouton «Views» + - Désactiver «Enable all feature» + - Dans l'onglet «Website» + - Activer «Enable website leaflet feature» + - Activer «Enable geoengine feature» + - Appuyer «Generate» + +Générer le module avec «Action/Générer code». + +Tester sur : +```bash +make db_restore_erplibre_base_db_test_module_test +make run_test +``` + +Ajouter les permissions d'utilisation, dans l'application «Configuration», «Gérer les droits d'accès». Sur votre utilisateur, ajouter «Geoengine Admin». + +Aller sur l'application «test», créer un «geo_point». + +Aller sur la page du site web, ajouter le snippet «Leaflet». Chercher le point qui a été ajouté sur la carte du monde. + +### Créer un module thème pour site web + +Pour des références techniques, voir module : +- code_generator_demo_theme_website + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_theme_website +make run_code_generator +``` +Aller dans le module «Code generator» et créer un module «test». +- Sur l'onglet «Information», activer «Enable Sync Code» +- Sur l'onglet «Technical Data», activer «Website theme». +- Appuyer sur le bouton «Views» + - Aller à l'onglet «theme_website» + - Mettre les couleurs désirés + - Appuyer «Generate» + +Générer le module avec «Action/Générer code». + +Ce module doit être installé manuellement pour l'instant. + +```bash +make db_restore_erplibre_base_db_test +./script/addons/install_addons.sh test website +make run_test +``` + +Aller dans l'interface web, Application «Configuration», «Paramètres Généraux»/«Site Web»/«Choisissez un thème». Installer le thème «TEST». Puis aller la page du site web, les couleurs sont dans «Personnaliser»/«Personnaliser le thème». + +### Extraire les données vers un module + +L'exemple est avec le helpdesk. Installer le module helpdesk_mgmt : + +Pour des références techniques, voir module : +- code_generator_demo_export_helpdesk +- code_generator_demo_export_website +- demo_website_data +- demo_helpdesk_data + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator helpdesk_mgmt +make run_code_generator +``` +Créer un ticket dans l'application «Helpdesk» dans l'interface web. + +Aller dans le module «Code generator» et créer un module «test». +- Sur l'onglet «Information», activer «Enable Sync Code» +- Sur l'onglet «Technical Data», activer «Only export data». +- Appuyer sur le bouton «Models» + - Choisissez dans «Models» : + - «helpdesk.ticket» + - Activer «Clear field blacklisted» + - Enlever les éléments suivant de «Fields» : + - name + - description + - number + - Appuyer «Generate» + +Générer le module avec «Action/Générer code». +```bash +make db_restore_erplibre_base_db_test_module_test +``` + +### Migrer une base de donnée externe en module de migration + +En progression. + +Pour des références techniques, voir module : +- ?? + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_db_servers +make run_code_generator +``` +Aller dans le module «Code generator», menu «Databases»/«Databases» et créer un connecteur vers une base de donnée. + +À compléter... + +### Migrer d'un site web vers un modèle de donnée avec des données format PDF + +En progression, ce n'est pas encore supporté via l'interface web. + +Cette technique permet d'aller lire du Javascript sur un site web pour lire ensuite le HTML, c'est à dire la vue, et en comprendre l'information pour créer un modèle. + +Pour des références techniques, voir module : +- code_generator_demo_converter_js +- business_plan_import_pdf + +Attention, il faut mettre à jour les variables, dans les fichiers hook.py qui permettront d'extraire des données sur le site web à copier. Aucun exemple public n'est accessible pour le moment. + +```bash +make db_restore_erplibre_base_db_code_generator +./script/addons/install_addons_dev.sh code_generator code_generator_demo_converter_js +make db_restore_erplibre_base_db_test +./script/addons/install_addons_dev.sh test business_plan_import_pdf +``` + +## Préparer une BD + +Ceci va détruire et créer une base de donnée nommé `code_generator`. + +```bash +make db_restore_erplibre_base_db_code_generator +``` + +Pour de meilleur performance d'exécution, réduire la quantité de «repo addons» permet d'accélérer l'installation, exécuter : + +```bash +make config_gen_code_generator +``` + +Pour revenir à la configuration normal, en production, exécuter : + +```bash +make config_gen_all +``` + +## Créer votre premier `Code_Generator` + +Modifier [Code Generator Demo](./../addons/TechnoLibre_odoo-code-generator/code_generator_demo/hooks.py) et mettre à jour `# TODO HUMAN:` + +### Générer un `Template` + +Par défaut, installer le module code_generator_demo va générer le module `code_generator_demo`. + +Nommer votre module en commençant par `MODULE_NAME = "code_generator_template_"` + +Un exemple, générer le module `code_generator_template_demo_website_snippet`, changer la variable `value`: + +```python +MODULE_NAME = "code_generator_template_demo_website_snippet" +[...] +value["enable_template_website_snippet_view"] = True +[...] +lst_depend = [ + "code_generator", + "code_generator_website_snippet", +] +``` + +Générer un nouveau module, ceci va écraser `code_generator_template_demo_website_snippet` : + +```bash +make db_restore_erplibre_base_db_code_generator +make addons_install_code_generator_demo +``` + +### Générer un Code_Generator (suite de template) + +Au besoin, renommé votre module qui débute par `MODULE_NAME = "code_generator_"` + +Pour continuer avec l'exemple, généré le template `code_generator_template_demo_website_snippet`, ceci va écraser `code_generator_demo_website_snippet`: + +```bash +make db_restore_erplibre_base_db_template +./script/addons/install_addons_dev.sh template code_generator_template_demo_website_snippet +``` + +Aller à la section [Créer votre premier module](#crer-votre-premier-module) + +### Générer un Code_Generator_Demo + +Nommé votre module qui débute par `MODULE_NAME = "code_generator_demo_"` + +Désactiver la variable `enable_template_code_generator_demo` + +```python +value["enable_template_code_generator_demo"] = False +``` + +Pour continuer avec l'exemple, généré le template `code_generator_template_demo_website_snippet`, ceci va écraser `code_generator_demo_website_snippet`: + +```bash +./script/addons/install_addons_dev.sh code_generator code_generator_template_demo_website_snippet +``` + +## Créer votre premier module + +Continuer avec l'exemple du `code_generator_demo_website_snippet` pour générer votre premier module. Mettre à jour les valeurs suivantes : + +```python +"application": True, +"category_id": env.ref("base.module_category_website").id, +[...] +# Add dependencies +lst_depend = [ + "website", +] +``` + +Générer votre module : + +```bash +make db_restore_erplibre_base_db_code_generator +./script/addons/install_addons_dev.sh code_generator code_generator_demo_website_snippet +``` + +Maintenant, vous pouvez le tester! Pour info, on ne peut pas installer un module généré avec son générateur de code associé, puisqu'il y a une duplication des modèles! + +Il est maintenant possible de modifier les paramètres via l'interface manuelle pour ce module, puis le regénérer en effaçant la BD. Ajouter un modèle et une vue. + +```bash +make db_restore_erplibre_base_db_code_generator +./script/addons/install_addons_dev.sh code_generator code_generator_demo_website_snippet +``` + +Faire la boucle d'amélioration, commiter le code pour avoir une traçabilité des changements. + +Ensuite, modifier la valeur `enable_sync_template` à `True`, `enable_template_wizard_view` à `True`, `template_model_name` avec un modèle existant, dans le fichier template, de l'exemple `code_generator_template_demo_website_snippet`. + +```python +value["enable_sync_template"] = True +value["enable_template_wizard_view"] = True +value["template_model_name"] = "test;test2" +``` + +Exécuter l'installation du template pour qu'il se synchronise sur le module généré. + +```bash +make db_restore_erplibre_base_db_template +./script/addons/install_addons_dev.sh template demo_website_snippet +./script/addons/install_addons_dev.sh template code_generator_template_demo_website_snippet +``` + +Prêt à tester, générer le module : + +```bash +# Optional, reset test database +make db_restore_erplibre_base_db_test +./script/addons/install_addons_dev.sh test demo_website_snippet +``` + +# Comment générer les traductions i18n + +Vous avez besoin d'un template et d'un module. Soyez certain que vous avez cette ligne dans le template : + +```python +new_module_path = os.path.join(path_module_generate, new_module_name) +code_generator_writer.set_module_translator(new_module_name, new_module_path) +``` + +Premièrement, installer le module, par exemple `demo_portal`, et ensuite le template, par exemple `code_generator_template_demo_portal`. + +```bash +make db_restore_erplibre_base_db_code_generator +./script/addons/install_addons_dev.sh test demo_portal +./script/addons/install_addons_dev.sh test code_generator_template_demo_portal +``` + +# Synchroniser les champs du `Code_Generator` du module + +Ceci va permettre de réduire le temps de programmation d'un `Code_Generator` avec la synchronisation des champs de module avec `Template`. + +On a besoin d'un `Template`, un `Code_Generator` accompagné de son module. Installer le module en premier dans une base de donnée nettoyé et installer le `Template`, ceci va générer le `Code_Generator`, utiliser `enable_sync_template` à la valeur `True`. + +Exemple : + +```bash +make db_restore_erplibre_base_db_code_generator +./script/addons/install_addons_dev.sh test demo_portal +./script/addons/install_addons_dev.sh test code_generator_template_demo_portal +``` + +# Tester le générateur de code + +Une bonne manière de tester un générateur de code est de valider qu'il génère le même code, il ne devient pas nécessaire de tester les fonctionnalités, puisque le comportement est la génération. + +```bash +make test +``` + +Tester les générations simples : + +```bash +make test_code_generator_generation +``` + +Tester les générations extra : + +```bash +make test_code_generator_generation_extra +``` +Tester les générations des templates : + +```bash +make test_code_generator_template +``` diff --git a/doc/CODE_GENERATOR.md b/doc/CODE_GENERATOR.md new file mode 100644 index 0000000..6f5dc4c --- /dev/null +++ b/doc/CODE_GENERATOR.md @@ -0,0 +1,403 @@ +# How to generate code + +Never run this on production environment, this create circular dependencies and will cause frustration to clean damage. + +``` +Modules +############ ################## ########## +# Template # -> # Code_Generator # -> # Module # +############ ################## ########## +``` + +1. [A Template generates a Template or a Code_Generator.](#create-your-first-code_generator) +2. A Code_Generator generates a Module. +3. A Template reads a Module to generate a Code_Generator. + +Warning, be careful to your code, always commit after a manipulation, because the mode enable_sync_code erase data, only the git will save you! + +## Manual generator with web interface + +TODO + +```bash +make addons_install_code_generator_basic +make run_code_generator +``` + +```bash +make addons_install_code_generator_featured +make run_code_generator +``` + +```bash +make addons_install_code_generator_full +make run_code_generator +``` + +Ouvrir le navigateur sur [http://localhost:8069](http://localhost:8069). Utilisateur `test` et mot de passe `test`. Une fois connecté, ouvrir sur [http://localhost:8069/web?debug=](http://localhost:8069/web?debug=) pour activer le déverminage. + +Ouvrir l'application `Code Generator` et créer un `Module`. Remplir les champs requis et générer avec `Action/Generate code`. + +### Create model-view module + +```bash +make addons_install_code_generator_basic +make run_code_generator +``` + +TODO + +TODO + +```bash +make db_restore_erplibre_base_db_test_module_test +make run_test +``` + +### Create portal module + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_portal +make run_code_generator +``` + +TODO + +TODO + +```bash +make db_restore_erplibre_base_db_test_module_test +make run_test +``` + +TODO + +### Create hook module for installation + +TODO + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_hook +make run_code_generator +``` + +TODO + +```python +with api.Environment.manage(): + env = api.Environment(cr, SUPERUSER_ID, {}) + print("Hello World") +``` + +TODO + +```bash +make db_restore_erplibre_base_db_test_module_test +``` + +### Create cron module + +TODO + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_cron +make run_code_generator +``` + +TODO + +```python +log("Coucou") +``` + +TODO + +```bash +make db_restore_erplibre_base_db_test_module_test +make run_test +``` + +TODO + +### Create website snippet module + +In progress + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_website_snippet +make run_code_generator +``` + +TODO + +TODO + +### Create geoengine with Leaflet module + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_website_leaflet +make run_code_generator +``` + +TODO + +TODO + +```bash +make db_restore_erplibre_base_db_test_module_test +make run_test +``` + +TODO + +### Create theme module for website + +TODO + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_theme_website +make run_code_generator +``` +TODO + +```bash +make db_restore_erplibre_base_db_test +./script/addons/install_addons.sh test website +make run_test +``` + +TODO + +### Extract data to module + +TODO + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator helpdesk_mgmt +make run_code_generator +``` +TODO + +```bash +make db_restore_erplibre_base_db_test_module_test +``` + +### Migrate external database into module of migration + +TODO + +```bash +make addons_install_code_generator_basic +./script/addons/install_addons.sh code_generator code_generator_db_servers +make run_code_generator +``` +TODO + +### Migrate from website to model with PDF data + +TODO + +```bash +make db_restore_erplibre_base_db_code_generator +./script/addons/install_addons_dev.sh code_generator code_generator_demo_converter_js +make db_restore_erplibre_base_db_test +./script/addons/install_addons_dev.sh test business_plan_import_pdf +``` + +## Prepare a DB + +This will destroy and create database named `code_generator`. + +```bash +make db_restore_erplibre_base_db_code_generator +``` + +TODO + +```bash +make config_gen_code_generator +``` + +TODO + +```bash +make config_gen_all +``` + +## Create your first `Code_Generator` + +Edit [Code Generator Demo](./../addons/TechnoLibre_odoo-code-generator/code_generator_demo/hooks.py) and update `# TODO HUMAN:` + +### Generate a `Template` + +By default, `code_generator_demo` generate itself, `code_generator_demo`. + +Name your module begin with `MODULE_NAME = "code_generator_template_"` + +The value "enable_template_code_generator_demo" at `False` to create a new template module, else this will recreate `code_generator_demo`. + +Example, to generate the module `code_generator_template_demo_website_snippet`, change value: + +```python +MODULE_NAME = "code_generator_template_demo_website_snippet" +[...] +value["enable_template_website_snippet_view"] = True +[...] +lst_depend = [ + "code_generator", + "code_generator_website_snippet", +] +``` + +Generate new module, this will overwrite `code_generator_template_demo_website_snippet`: + +```bash +make db_restore_erplibre_base_db_code_generator +make addons_install_code_generator_demo +``` + +### Generate a Code_Generator (continue template) + +Name your module begin with `MODULE_NAME = "code_generator_"` + +Continue the example, you generated the template `code_generator_template_demo_website_snippet`, this will overwrite `code_generator_demo_website_snippet`: + +```bash +make db_restore_erplibre_base_db_template +./script/addons/install_addons_dev.sh template code_generator_template_demo_website_snippet +``` + +Go to section [Create your first module](#create-your-first-module) + +### Generate a Code_Generator_Demo + +Name your module begin with `MODULE_NAME = "code_generator_demo_"` + +Disable `enable_template_code_generator_demo` + +```python +value["enable_template_code_generator_demo"] = False +``` + +Continue the example, you generated the template `code_generator_template_demo_website_snippet`, this will overwrite `code_generator_demo_website_snippet`: + +```bash +./script/addons/install_addons_dev.sh code_generator code_generator_template_demo_website_snippet +``` + +## Create your first Module + +Continue example of code_generator_demo_website_snippet to generate your first module. Update next value: + +```python +"application": True, +"category_id": env.ref("base.module_category_website").id, +[...] +# Add dependencies +lst_depend = [ + "website", +] +``` + +Generate your module: + +```bash +make db_restore_erplibre_base_db_code_generator +./script/addons/install_addons_dev.sh code_generator code_generator_demo_website_snippet +``` + +Now, you can test it! Note, you cannot install a generated module with is code_generator associated, because duplicated +models! + + +```bash +make db_restore_erplibre_base_db_code_generator +./script/addons/install_addons_dev.sh code_generator code_generator_demo_website_snippet +``` + + +```python +value["enable_sync_template"] = True +value["enable_template_wizard_view"] = True +value["template_model_name"] = "test;test2" +``` + +TODO + +```bash +make db_restore_erplibre_base_db_template +./script/addons/install_addons_dev.sh template demo_website_snippet +./script/addons/install_addons_dev.sh template code_generator_template_demo_website_snippet +``` + +Ready to test, generate your module: + +```bash +# Optional, reset test database +make db_restore_erplibre_base_db_test +./script/addons/install_addons_dev.sh test demo_website_snippet +``` + +# How to generate i18n translation + +You need a template and a module. Be sure you have this line in template: + +```python +new_module_path = os.path.join(path_module_generate, new_module_name) +code_generator_writer.set_module_translator(new_module_name, new_module_path) +``` + +First, install the module, example `demo_portal`, and after the template, example `code_generator_template_demo_portal`. + +```bash +make db_restore_erplibre_base_db_code_generator +./script/addons/install_addons_dev.sh test demo_portal +./script/addons/install_addons_dev.sh test code_generator_template_demo_portal +``` + +# Sync Code_Generator fields from Module + +This reduce time of programming a `Code_Generator` with sync module field with `Template`. + +You need a `Template`, a `Code_Generator` with his `Module`. Install the `Module` first in clean database and install `Template`, +this will generate the `Code_Generator`, use `enable_sync_template` at True. + +Example: + +```bash +make db_restore_erplibre_base_db_code_generator +./script/addons/install_addons_dev.sh test demo_portal +./script/addons/install_addons_dev.sh test code_generator_template_demo_portal +``` + +# Test the code generator + +TODO + +```bash +make test +``` + +TODO + +```bash +make test_code_generator_generation +``` + +TODO + +```bash +make test_code_generator_generation_extra +``` +TODO + +```bash +make test_code_generator_template +``` diff --git a/doc/DEVELOPMENT.md b/doc/DEVELOPMENT.md index 3de1dda..fcc92e3 100644 --- a/doc/DEVELOPMENT.md +++ b/doc/DEVELOPMENT.md @@ -139,3 +139,27 @@ Use this commit format: ```bash git commit -am "[#ticket] subject: short sentence" ``` + +# Format code +## Python +Use [black](https://github.com/psf/black) +```bash +./script/maintenance/black.sh ./addons/TechnoLibre_odoo-code-generator +``` + +Or if you prefer [oca-autopep8](https://github.com/psf/black) +```bash +./script/maintenance/autopep8.sh ./addons/TechnoLibre_odoo-code-generator +``` + +## HTML and css +Use [prettier](https://github.com/prettier/prettier) +```bash +./script/maintenance/prettier.sh ./addons/TechnoLibre_odoo-code-generator +``` + +## Javascript +Use [prettier](https://github.com/prettier/prettier) +```bash +./script/maintenance/prettier.sh --tab-width 4 ./addons/TechnoLibre_odoo-code-generator +``` diff --git a/doc/POETRY.md b/doc/POETRY.md index 60629dc..df94ae7 100644 --- a/doc/POETRY.md +++ b/doc/POETRY.md @@ -6,6 +6,8 @@ Add your dependencies in file [requirements.txt](../requirements.txt) and run sc ``` This will search all `requirements.txt` files and update `pyproject.toml` and it will update poetry +Priority dependencies in ./requirements.txt, after it's ./odoo/requirements.txt, after it's highest version values. + TODO add option to only add missing dependencies and ignore update. ## Add manually dependencies diff --git a/doc/PRODUCTION.md b/doc/PRODUCTION.md index b2480e8..c5971a3 100644 --- a/doc/PRODUCTION.md +++ b/doc/PRODUCTION.md @@ -62,9 +62,10 @@ cd /[EL_USER]/erplibre ## Move prod database to dev When moving prod database to your dev environment, you want to remove email servers and install user test to test the database. +Warning, this is not safe for production, you expose all data. Run: ```bash -./run.sh --stop-after-init -i user_test,disable_mail_server --dev all -d DATABASE +./script/migrate_prod_to_test.sh DATABASE ``` ## Update production @@ -85,3 +86,28 @@ Caution, this delete user's home, it's irrevocable. ```bash ./script/delete_production.sh ``` + +# Update ip when public ip change with CloudFlare and crontab + +```bash +mkdir ~/.cloudflare +``` + +Edit ~/.cloudflare/cloudflare.cfg +``` +[PROFILE_NAME] +email=EMAIL +token=TOKEN +``` + +Add your cron +```bash +vim /etc/crontab +# Add +*/5 * * * * USER cd PATH && ./script/deployment/update_dns_cloudflare.py --profile PROFILE_NAME --zone_name CLOUDFLARE_ZONE_NAME --dns_name DNS_NAME --auto_sync +``` + +Check log with +```bash +sudo journalctl -feu cron +``` \ No newline at end of file diff --git a/doc/RELEASE.md b/doc/RELEASE.md index 10fde8a..9fef491 100644 --- a/doc/RELEASE.md +++ b/doc/RELEASE.md @@ -80,7 +80,7 @@ When building your docker with script > ./script/docker_build.sh --release List your docker version -> docker image +> docker images You need to push your docker image and update your tag, like 1.0.1: > docker push technolibre/erplibre:VERSION diff --git a/docker/Dockerfile b/docker/Dockerfile index 5fe10c7..aa13444 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -15,7 +15,7 @@ RUN apt-get update \ fonts-noto-cjk \ gnupg \ libssl-dev \ - node-less \ + node-less \ npm \ python3-num2words \ python3-pip \ @@ -38,6 +38,8 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends ./wkhtmltox.deb \ && rm -rf /var/lib/apt/lists/* wkhtmltox.deb +RUN npm i prettier + # install latest postgresql-client RUN echo 'deb http://apt.postgresql.org/pub/repos/apt/ buster-pgdg main' > /etc/apt/sources.list.d/pgdg.list \ && GNUPGHOME="$(mktemp -d)" \ diff --git a/docker/Dockerfile.base b/docker/Dockerfile.base index 3f992b8..f6e408a 100644 --- a/docker/Dockerfile.base +++ b/docker/Dockerfile.base @@ -1,4 +1,4 @@ -FROM debian:buster-slim +FROM python:3.7.7-slim-buster MAINTAINER TechnoLibre SHELL ["/bin/bash", "-xo", "pipefail", "-c"] @@ -25,29 +25,19 @@ RUN apt-get update \ gnupg \ libssl-dev \ npm \ - python3-num2words \ - python3-pip \ - python3-phonenumbers \ - python3-pyldap \ - python3-qrcode \ - python3-renderpm \ - python3-setuptools \ - python3-slugify \ - python3-vobject \ - python3-watchdog \ - python3-xlrd \ - python3-xlwt \ - python3-babel \ - python3-psycopg2 \ xz-utils \ git \ wget \ + make \ + libssl-dev \ + zlib1g-dev \ + llvm \ + tk-dev \ libxslt-dev \ libzip-dev \ libldap2-dev \ libsasl2-dev \ gdebi-core \ - libffi-dev \ iproute2 \ libmariadbd-dev \ inetutils-ping \ @@ -85,21 +75,21 @@ RUN echo 'deb http://apt.postgresql.org/pub/repos/apt/ buster-pgdg main' > /etc/ && rm -rf /var/lib/apt/lists/* # Install npm dependencies -RUN npm install -g rtlcss less \ +RUN npm install -g rtlcss less prettier @prettier/plugin-xml \ && ln -fs /usr/local/bin/lessc /usr/bin/lessc # Install python with pyenv # Reference https://github.com/bopen/docker-ubuntu-pyenv -COPY pyenv-version.txt python-versions.txt / +#COPY pyenv-version.txt python-versions.txt / -RUN git clone -b `cat /pyenv-version.txt` --single-branch --depth 1 https://github.com/pyenv/pyenv.git $PYENV_ROOT \ - && for version in `cat /python-versions.txt`; do pyenv install $version; done \ - && pyenv global `cat /python-versions.txt` \ - && find $PYENV_ROOT/versions -type d '(' -name '__pycache__' -o -name 'test' -o -name 'tests' ')' -exec rm -rf '{}' + \ - && find $PYENV_ROOT/versions -type f '(' -name '*.pyo' -o -name '*.exe' ')' -exec rm -f '{}' + \ - && rm -rf /tmp/* \ - && python --version \ - && python3 --version +#RUN git clone -b `cat /pyenv-version.txt` --single-branch --depth 1 https://github.com/pyenv/pyenv.git $PYENV_ROOT \ +# && for version in `cat /python-versions.txt`; do pyenv install $version; done \ +# && pyenv global `cat /python-versions.txt` \ +# && find $PYENV_ROOT/versions -type d '(' -name '__pycache__' -o -name 'test' -o -name 'tests' ')' -exec rm -rf '{}' + \ +# && find $PYENV_ROOT/versions -type f '(' -name '*.pyo' -o -name '*.exe' ')' -exec rm -f '{}' + \ +# && rm -rf /tmp/* \ +# && python --version \ +# && python3 --version # Install git-repo RUN cd ; mkdir -p .bin/ && \ @@ -112,11 +102,6 @@ RUN cd ; mkdir -p .bin/ && \ RUN groupadd --gid 101 --force odoo && \ useradd --non-unique --create-home --uid 101 --gid 101 odoo -# TODO delete poetry installation -# Install Poetry -RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python - -y --preview --version 1.0.10 -#curl -fsS -o get-poetry.py https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py - # Copy entrypoint script and Odoo configuration file COPY ./entrypoint.sh / RUN chmod +x /entrypoint.sh diff --git a/docker/Dockerfile.prod.pkg b/docker/Dockerfile.prod.pkg index f9a3c30..9706f67 100644 --- a/docker/Dockerfile.prod.pkg +++ b/docker/Dockerfile.prod.pkg @@ -34,9 +34,22 @@ RUN cd $ODOO_PREFIX && \ RUN cd $ODOO_PREFIX && \ ./script/update_manifest_dev.sh +# TODO delete poetry installation +# Install Poetry RUN cd $ODOO_PREFIX && \ - source /root/.poetry/env && \ - poetry install + curl -sSL -o get-poetry.py https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py && \ + ./.venv/bin/python get-poetry.py -y --preview --version 1.0.10 + +RUN cd $ODOO_PREFIX && \ + ./.venv/bin/pip install --upgrade pip && \ + ./.venv/bin/python ~/.poetry/bin/poetry install && \ + rm -rf artifacts + +RUN cd $ODOO_PREFIX/.venv && \ + curl https://raw.githubusercontent.com/ERPLibre/multilingual-markdown/master/multilang_md.py > ./multilang_md.py && \ + chmod +x ./multilang_md.py && \ + sed -i 1d ./multilang_md.py && \ + sed -i "1 i #!./.venv/bin/python" ./multilang_md.py RUN cd $ODOO_PREFIX && \ head /etc/odoo/odoo.conf && \ diff --git a/docker/repo_manifest_gen_org_prefix_path.py b/docker/repo_manifest_gen_org_prefix_path.py index 7838e11..01b323a 100755 --- a/docker/repo_manifest_gen_org_prefix_path.py +++ b/docker/repo_manifest_gen_org_prefix_path.py @@ -25,7 +25,7 @@ addonsDirs = [abspath(join(addonsBaseDir, f)) for f in listdir(addonsBaseDir) if # addonsDirs.insert(0, "/usr/lib/python3/dist-packages/odoo/addons/") # TODO path addons/addons is not exist in docker, so force to add it -addonsDirs.insert(0, "/ERPLibre/addons/addons") +# addonsDirs.insert(0, "/ERPLibre/addons/addons") addonsDirs.insert(0, "/ERPLibre/odoo/addons/") config = configparser.ConfigParser() diff --git a/ignore_requirements.txt b/ignore_requirements.txt index 601982d..2c61fea 100644 --- a/ignore_requirements.txt +++ b/ignore_requirements.txt @@ -13,3 +13,4 @@ xlsxwriter ldap pythonjsonlogger python-alipay-sdk +pyrfc diff --git a/manifest/default.dev.xml b/manifest/default.dev.xml index a9b4c1d..0013b55 100644 --- a/manifest/default.dev.xml +++ b/manifest/default.dev.xml @@ -51,7 +51,7 @@ - + @@ -65,7 +65,7 @@ - + @@ -75,6 +75,7 @@ + @@ -86,14 +87,15 @@ - + - + + - + @@ -104,7 +106,6 @@ - @@ -129,6 +130,7 @@ + diff --git a/poetry.lock b/poetry.lock index 86e99a8..982ad3a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -47,12 +47,12 @@ description = "Bash tab completion for argparse" name = "argcomplete" optional = false python-versions = "*" -version = "1.12.2" +version = "1.12.3" [package.dependencies] [package.dependencies.importlib-metadata] python = ">=3.7,<3.8" -version = ">=0.23,<4" +version = ">=0.23,<5" [package.extras] test = ["coverage", "flake8", "pexpect", "wheel"] @@ -84,6 +84,22 @@ version = "0.0.8" [package.dependencies] requests = "*" +[[package]] +category = "main" +description = "An abstract syntax tree for Python with inference support." +name = "astroid" +optional = false +python-versions = ">=3.6" +version = "2.5" + +[package.dependencies] +lazy-object-proxy = ">=1.4.0" +wrapt = ">=1.11,<1.13" + +[package.dependencies.typed-ast] +python = "<3.8" +version = ">=1.4.0,<1.5" + [[package]] category = "main" description = "Classes Without Boilerplate" @@ -130,15 +146,51 @@ description = "Screen-scraping library" name = "beautifulsoup4" optional = false python-versions = "*" -version = "4.9.1" +version = "4.9.3" [package.dependencies] -soupsieve = [">1.2", "<2.0"] +[package.dependencies.soupsieve] +python = ">=3.0" +version = ">1.2" [package.extras] html5lib = ["html5lib"] lxml = ["lxml"] +[[package]] +category = "main" +description = "The uncompromising code formatter." +name = "black" +optional = false +python-versions = ">=3.6.2" +version = "21.6b0" + +[package.dependencies] +appdirs = "*" +click = ">=7.1.2" +mypy_extensions = ">=0.4.3" +pathspec = ">=0.8.1,<1" +regex = ">=2020.1.8" +toml = ">=0.10.1" + +[package.dependencies.typed-ast] +python = "<3.8" +version = ">=1.4.2" + +[package.dependencies.typing_extensions] +python = "<3.8" +version = ">=3.7.4" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.6.0)", "aiohttp-cors (>=0.4.0)"] +python2 = ["typed-ast (>=1.4.2)"] +uvloop = ["uvloop (>=0.15.2)"] + +[package.source] +reference = "93c10bf9ebccf8d7cc686b0b9579f2e5e41c5328" +type = "git" +url = "https://github.com/psf/black.git" [[package]] category = "main" description = "Interactive plots and applications in the browser from Python" @@ -157,14 +209,6 @@ python-dateutil = ">=2.1" six = ">=1.5.2" tornado = ">=4.3" -[[package]] -category = "main" -description = "Amazon Web Services Library" -name = "boto" -optional = false -python-versions = "*" -version = "2.49.0" - [[package]] category = "main" description = "The AWS SDK for Python" @@ -184,10 +228,10 @@ description = "Low-level, data-driven core of boto 3." name = "botocore" optional = false python-versions = "*" -version = "1.12.253" +version = "1.12.191" [package.dependencies] -docutils = ">=0.10,<0.16" +docutils = ">=0.10" jmespath = ">=0.7.1,<1.0.0" [package.dependencies.python-dateutil] @@ -212,7 +256,7 @@ description = "Extensible memoizing collections and decorators" name = "cachetools" optional = false python-versions = "~=3.5" -version = "4.2.0" +version = "4.2.2" [[package]] category = "main" @@ -220,7 +264,7 @@ description = "Lightweight, extensible schema and data validation tool for Pytho name = "cerberus" optional = false python-versions = ">=2.7" -version = "1.3.2" +version = "1.3.4" [package.dependencies] setuptools = "*" @@ -257,8 +301,29 @@ category = "main" description = "Composable command line interface toolkit" name = "click" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "7.1.2" +python-versions = ">=3.6" +version = "8.0.1" + +[package.dependencies] +colorama = "*" + +[package.dependencies.importlib-metadata] +python = "<3.8" +version = "*" + +[[package]] +category = "main" +description = "Python wrapper for the Cloudflare v4 API" +name = "cloudflare" +optional = false +python-versions = "*" +version = "2.8.15" + +[package.dependencies] +beautifulsoup4 = "*" +jsonlines = "*" +pyyaml = "*" +requests = "*" [[package]] category = "main" @@ -276,20 +341,6 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "0.4.4" -[[package]] -category = "main" -description = "Colored terminal output for Python's logging module" -name = "coloredlogs" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "14.0" - -[package.dependencies] -humanfriendly = ">=7.1" - -[package.extras] -cron = ["capturer (>=2.4)"] - [[package]] category = "main" description = "Compressed Rich Text Format (RTF) compression and decompression package" @@ -318,6 +369,17 @@ idna = ["idna (>=2.1)"] pep8test = ["flake8", "flake8-import-order", "pep8-naming"] test = ["pytest (>=3.6.0,<3.9.0 || >3.9.0,<3.9.1 || >3.9.1,<3.9.2 || >3.9.2)", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,<3.79.2 || >3.79.2)"] +[[package]] +category = "main" +description = "StandAlone Async cross-platform Prettifier Beautifier for the Web." +name = "css-html-prettify" +optional = false +python-versions = ">=3.6" +version = "2.5.5" + +[package.dependencies] +beautifulsoup4 = "*" + [[package]] category = "main" description = "cssselect parses CSS3 Selectors and translates them to XPath 1.0" @@ -351,7 +413,7 @@ description = "The Cython compiler for writing C extensions for the Python langu name = "cython" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "0.29.21" +version = "0.29.23" [[package]] category = "main" @@ -447,8 +509,8 @@ category = "main" description = "Docutils -- Python Documentation Utilities" name = "docutils" optional = false -python-versions = "*" -version = "0.12" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.16" [[package]] category = "main" @@ -462,18 +524,37 @@ version = "2.1.5" lxml = "*" requests = "*" +[[package]] +category = "main" +description = "Additional EBCDIC codecs" +name = "ebcdic" +optional = false +python-versions = "*" +version = "1.1.1" + [[package]] category = "main" description = "A robust email syntax and deliverability validation library for Python 2.x/3.x." name = "email-validator" optional = false -python-versions = "*" -version = "1.1.2" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +version = "1.1.3" [package.dependencies] dnspython = ">=1.15.0" idna = ">=2.0.0" +[[package]] +category = "main" +description = "Emoji for Python" +name = "emoji" +optional = false +python-versions = "*" +version = "1.2.0" + +[package.extras] +dev = ["pytest", "coverage", "coveralls"] + [[package]] category = "main" description = "Discover and load entry points from installed packages." @@ -496,7 +577,7 @@ description = "Python library to manipulate ESC/POS Printers" name = "escpos" optional = false python-versions = ">=3.5" -version = "1.8" +version = "1.9" [package.dependencies] Pillow = ">=2.0" @@ -521,59 +602,33 @@ optional = false python-versions = "*" version = "1.0.1" -[[package]] -category = "main" -description = "Programmer friendly subprocess wrapper" -name = "executor" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "23.1" - -[package.dependencies] -coloredlogs = ">=3.5" -fasteners = ">=0.14.1" -humanfriendly = ">=8.0" -property-manager = ">=3.0" -six = ">=1.9.0" - [[package]] category = "main" description = "Extracts emails and attachments saved in Microsoft Outlook's .msg files" name = "extract-msg" optional = false python-versions = "*" -version = "0.28.0" +version = "0.28.7" [package.dependencies] compressed-rtf = ">=1.0.6" +ebcdic = ">=1.1.1" imapclient = "2.1.0" olefile = ">=0.46" tzlocal = ">=2.1" [[package]] category = "main" -description = "Factur-X: electronic invoicing standard for Germany & France" +description = "Factur-X and Order-X: electronic invoicing and ordering standards" name = "factur-x" optional = false python-versions = "*" -version = "1.12" +version = "2.3" [package.dependencies] PyPDF4 = "*" lxml = "*" -[[package]] -category = "main" -description = "A python package that provides useful locks." -name = "fasteners" -optional = false -python-versions = "*" -version = "0.15" - -[package.dependencies] -monotonic = ">=0.1" -six = "*" - [[package]] category = "main" description = "Universal feed parser, handles RSS 0.9x, RSS 1.0, RSS 2.0, CDF, Atom 0.3, and Atom 1.0 feeds" @@ -582,6 +637,23 @@ optional = false python-versions = "*" version = "5.2.1" +[[package]] +category = "main" +description = "the modular source code checker: pep8 pyflakes and co" +name = "flake8" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +version = "3.9.2" + +[package.dependencies] +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.7.0,<2.8.0" +pyflakes = ">=2.3.0,<2.4.0" + +[package.dependencies.importlib-metadata] +python = "<3.8" +version = "*" + [[package]] category = "main" description = "Let your Python tests travel through time" @@ -688,14 +760,38 @@ version = "2016.9.19" [[package]] category = "main" -description = "Human friendly output for text interfaces using Python" -name = "humanfriendly" +description = "HTML parser based on the WHATWG HTML specification" +name = "html5lib" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "8.2" +version = "1.1" [package.dependencies] -pyreadline = "*" +six = ">=1.9" +webencodings = "*" + +[package.extras] +all = ["genshi", "chardet (>=2.2)", "lxml"] +chardet = ["chardet (>=2.2)"] +genshi = ["genshi"] +lxml = ["lxml"] + +[[package]] +category = "main" +description = "HTML5, CSS, Javascript Pretty Print" +name = "html5print" +optional = false +python-versions = "*" +version = "0.1.2" + +[package.dependencies] +beautifulsoup4 = ">=4.3.2" +chardet = ">=2.2.1" +html5lib = ">=0.999" +ply = "3.4" +requests = ">=2.3.5" +slimit = ">=0.8.1" +tinycss2 = ">=0.4" [[package]] category = "main" @@ -761,7 +857,7 @@ description = "Python parser to extract data from pdf invoice" name = "invoice2data" optional = false python-versions = "*" -version = "0.3.5" +version = "0.3.6" [package.dependencies] chardet = "*" @@ -770,6 +866,9 @@ pillow = "*" pyyaml = "*" unidecode = "*" +[package.extras] +test = ["pytest", "pytest-cov", "flake8", "pdfminer", "tox"] + [[package]] category = "main" description = "Python versioning with requirements.txt syntax" @@ -799,11 +898,17 @@ six = "*" [[package]] category = "main" -description = "Julian dates from proleptic Gregorian and Julian calendars." -name = "jdcal" +description = "A Python utility / library to sort Python imports." +name = "isort" optional = false -python-versions = "*" -version = "1.4.1" +python-versions = ">=3.6.1,<4.0" +version = "5.9.1" + +[package.extras] +colors = ["colorama (>=0.4.3,<0.5.0)"] +pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +plugins = ["setuptools"] +requirements_deprecated_finder = ["pipreqs", "pip-api"] [[package]] category = "main" @@ -853,6 +958,27 @@ optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" version = "0.10.0" +[[package]] +category = "main" +description = "JavaScript to Python Translator & JavaScript interpreter written in 100% pure Python." +name = "js2py" +optional = false +python-versions = "*" +version = "0.71" + +[package.dependencies] +pyjsparser = ">=2.5.1" +six = ">=1.10" +tzlocal = ">=1.2" + +[[package]] +category = "main" +description = "Library with helpers for the jsonlines file format" +name = "jsonlines" +optional = false +python-versions = ">=3.6" +version = "2.0.0" + [[package]] category = "main" description = "An implementation of JSON Schema validation for Python" @@ -930,6 +1056,14 @@ version = "0.0.5" delorean = ">=0.4.4" schema = ">=0.3.1,<0.4.0" +[[package]] +category = "main" +description = "A fast and thorough lazy object proxy." +name = "lazy-object-proxy" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +version = "1.6.0" + [[package]] category = "main" description = "Levenshtein edit distance library" @@ -1004,6 +1138,14 @@ numpy = ">=1.11" pyparsing = ">=2.0.1,<2.0.4 || >2.0.4,<2.1.2 || >2.1.2,<2.1.6 || >2.1.6" python-dateutil = ">=2.1" +[[package]] +category = "main" +description = "McCabe checker, plugin for flake8" +name = "mccabe" +optional = false +python-versions = "*" +version = "0.6.1" + [[package]] category = "main" description = "Rolling backport of unittest.mock for all Pythons" @@ -1020,14 +1162,6 @@ six = ">=1.9" docs = ["sphinx", "Pygments (<2)", "jinja2 (<2.7)", "sphinx (<1.3)"] test = ["unittest2 (>=1.1.0)"] -[[package]] -category = "main" -description = "An implementation of time.monotonic() for Python 2 & < 3.3" -name = "monotonic" -optional = false -python-versions = "*" -version = "1.5" - [[package]] category = "main" description = "D3 Viewer for Matplotlib" @@ -1053,6 +1187,14 @@ optional = false python-versions = ">=3.5" version = "4.7.6" +[[package]] +category = "main" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +name = "mypy-extensions" +optional = false +python-versions = "*" +version = "0.4.3" + [[package]] category = "main" description = "Python interface to MySQL" @@ -1061,14 +1203,6 @@ optional = false python-versions = ">=3.5" version = "2.0.3" -[[package]] -category = "main" -description = "Simple natural order sorting API for Python" -name = "naturalsort" -optional = false -python-versions = "*" -version = "1.5.1" - [[package]] category = "main" description = "A network address manipulation library for Python" @@ -1106,8 +1240,8 @@ category = "main" description = "NumPy is the fundamental package for array computing with Python." name = "numpy" optional = false -python-versions = ">=3.6" -version = "1.19.5" +python-versions = ">=3.7" +version = "1.21.0" [[package]] category = "main" @@ -1161,11 +1295,10 @@ description = "A Python library to read/write Excel 2010 xlsx/xlsm files" name = "openpyxl" optional = false python-versions = ">=3.6," -version = "3.0.5" +version = "3.0.7" [package.dependencies] et-xmlfile = "*" -jdcal = "*" [[package]] category = "main" @@ -1316,6 +1449,14 @@ optional = false python-versions = "*" version = "1.6.5" +[[package]] +category = "main" +description = "Utility library for gitignore style pattern matching of file paths." +name = "pathspec" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.8.1" + [[package]] category = "main" description = "Python Build Reasonableness" @@ -1330,11 +1471,22 @@ description = "A wrapper around the pdftoppm and pdftocairo command line tools t name = "pdf2image" optional = false python-versions = "*" -version = "1.14.0" +version = "1.16.0" [package.dependencies] pillow = "*" +[[package]] +category = "main" +description = "PDF parser and analyzer" +name = "pdfminer" +optional = false +python-versions = ">=3.6" +version = "20191125" + +[package.dependencies] +pycryptodome = "*" + [[package]] category = "main" description = "Pexpect allows easy control of interactive console applications." @@ -1352,7 +1504,7 @@ description = "Python version of Google's common library for parsing, formatting name = "phonenumbers" optional = false python-versions = "*" -version = "8.12.15" +version = "8.12.26" [[package]] category = "main" @@ -1374,13 +1526,29 @@ version = "4.1.0" retrying = ">=1.3.3" six = "*" +[[package]] +category = "main" +description = "Python Lex & Yacc" +name = "ply" +optional = false +python-versions = "*" +version = "3.4" + +[[package]] +category = "main" +description = "A library to manipulate gettext files (po and mo files)." +name = "polib" +optional = false +python-versions = "*" +version = "1.1.0" + [[package]] category = "main" description = "Turns CSS blocks into style attributes" name = "premailer" optional = false python-versions = "*" -version = "3.7.0" +version = "3.9.0" [package.dependencies] cachetools = "*" @@ -1393,18 +1561,6 @@ requests = "*" dev = ["tox", "twine", "therapist", "black", "flake8", "wheel"] test = ["nose", "mock"] -[[package]] -category = "main" -description = "Useful property variants for Python programming (required properties, writable properties, cached properties, etc)" -name = "property-manager" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "3.0" - -[package.dependencies] -humanfriendly = ">=8.0" -verboselogs = ">=1.1" - [[package]] category = "main" description = "psutil is a cross-platform library for retrieving information onrunning processes and system utilization (CPU, memory, disks, network)in Python." @@ -1465,6 +1621,14 @@ lxml = "*" setuptools = "*" six = ">=1.9" +[[package]] +category = "main" +description = "Python style guide checker" +name = "pycodestyle" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.7.0" + [[package]] category = "main" description = "ISO country, subdivision, language, currency and script definitions and their translations" @@ -1481,6 +1645,14 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "2.20" +[[package]] +category = "main" +description = "Cryptographic library for Python" +name = "pycryptodome" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "3.10.1" + [[package]] category = "main" description = "Python interface to Graphviz's Dot" @@ -1492,6 +1664,14 @@ version = "1.2.3" [package.dependencies] pyparsing = ">=2.1.4" +[[package]] +category = "main" +description = "passive checker of Python programs" +name = "pyflakes" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.3.1" + [[package]] category = "main" description = "Pygments is a syntax highlighting package written in Python." @@ -1522,6 +1702,14 @@ version = "0.7" [package.dependencies] setuptools = "*" +[[package]] +category = "main" +description = "Fast javascript parser (based on esprima.js)" +name = "pyjsparser" +optional = false +python-versions = "*" +version = "2.7.1" + [[package]] category = "main" description = "JSON Web Token implementation in Python" @@ -1546,13 +1734,64 @@ version = "2.4.28" [package.dependencies] setuptools = "*" +[[package]] +category = "main" +description = "python code static checker" +name = "pylint" +optional = false +python-versions = ">=3.5.*" +version = "2.6.0" + +[package.dependencies] +astroid = ">=2.4.0,<=2.5" +colorama = "*" +isort = ">=4.2.5,<6" +mccabe = ">=0.6,<0.7" +toml = ">=0.7.1" + +[[package]] +category = "main" +description = "Pylint plugin for Odoo" +name = "pylint-odoo" +optional = false +python-versions = ">=2.7" +version = "3.8.0" + +[package.dependencies] +Pygments = "2.6.1" +docutils = "0.16" +lxml = ">=4.2.3" +pbr = "*" +polib = "1.1.0" +pylint = "2.6.0" +pylint-plugin-utils = "0.6" +restructuredtext_lint = "1.3.1" +rfc3986 = "*" +six = "*" +whichcraft = "*" + +[package.source] +reference = "455ac91d2d6d651bf67ec8eae7dae516f11b1dec" +type = "git" +url = "https://github.com/oca/pylint-odoo.git" +[[package]] +category = "main" +description = "Utilities and helpers for writing Pylint plugins" +name = "pylint-plugin-utils" +optional = false +python-versions = "*" +version = "0.6" + +[package.dependencies] +pylint = ">=1.7" + [[package]] category = "main" description = "DB-API interface to Microsoft SQL Server for Python. (new Cython-based version)" name = "pymssql" optional = false python-versions = "*" -version = "2.1.5" +version = "2.2.1" [[package]] category = "main" @@ -1588,7 +1827,7 @@ description = "Python One Time Password Library" name = "pyotp" optional = false python-versions = "*" -version = "2.4.1" +version = "2.6.0" [[package]] category = "main" @@ -1619,29 +1858,12 @@ category = "main" description = "Python interface to PROJ (cartographic projections and coordinate transformations library)" name = "pyproj" optional = false -python-versions = ">=3.6" -version = "3.0.0.post1" +python-versions = ">=3.7" +version = "3.1.0" [package.dependencies] certifi = "*" -[[package]] -category = "main" -description = "A python implmementation of GNU readline." -marker = "sys_platform == \"win32\"" -name = "pyreadline" -optional = false -python-versions = "*" -version = "2.1" - -[[package]] -category = "main" -description = "Implementation of some RFC standard algorithms" -name = "pyrfc" -optional = false -python-versions = "*" -version = "0.1.2" - [[package]] category = "main" description = "Persistent/Functional/Immutable data structures" @@ -1771,8 +1993,8 @@ category = "main" description = "A Python Slugify application that handles Unicode" name = "python-slugify" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "4.0.1" +python-versions = ">=3.6" +version = "5.0.2" [package.dependencies] text-unidecode = ">=1.3" @@ -1786,7 +2008,7 @@ description = "Python module to handle standardized numbers and codes" name = "python-stdnum" optional = false python-versions = "*" -version = "1.15" +version = "1.16" [package.extras] soap = ["zeep"] @@ -1943,7 +2165,7 @@ description = "Mock out responses from the requests package" name = "requests-mock" optional = false python-versions = "*" -version = "1.8.0" +version = "1.9.3" [package.dependencies] requests = ">=2.3,<3" @@ -1979,6 +2201,17 @@ version = "0.9.1" [package.dependencies] requests = ">=2.0.1,<3.0.0" +[[package]] +category = "main" +description = "reStructuredText linter" +name = "restructuredtext-lint" +optional = false +python-versions = "*" +version = "1.3.1" + +[package.dependencies] +docutils = ">=0.11,<1.0" + [[package]] category = "main" description = "Retrying" @@ -2001,37 +2234,6 @@ version = "1.4.0" [package.extras] idna2008 = ["idna"] -[[package]] -category = "main" -description = "Simple command line interface for backup rotation" -name = "rotate-backups" -optional = false -python-versions = "*" -version = "4.3" - -[package.dependencies] -coloredlogs = ">=5.0" -executor = ">=13.0" -humanfriendly = ">=1.44.5" -naturalsort = ">=1.4" -property-manager = ">=2.0" -python-dateutil = ">=2.2" -simpleeval = ">=0.8.7" -six = ">=1.9.0" -verboselogs = ">=1.4" - -[[package]] -category = "main" -description = "Simple command line interface for S3 backup rotation" -name = "rotate-backups-s3" -optional = false -python-versions = "*" -version = "0.3" - -[package.dependencies] -boto = "*" -rotate-backups = "4.3" - [[package]] category = "main" description = "An Amazon S3 Transfer Manager" @@ -2085,14 +2287,6 @@ all = ["pytest", "pytest-cov", "numpy"] test = ["pytest", "pytest-cov"] vectorized = ["numpy"] -[[package]] -category = "main" -description = "A simple, safe single expression evaluator library." -name = "simpleeval" -optional = false -python-versions = "*" -version = "0.9.10" - [[package]] category = "main" description = "Simple, fast, extensible JSON encoder/decoder for Python" @@ -2109,6 +2303,17 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" version = "1.15.0" +[[package]] +category = "main" +description = "SlimIt - JavaScript minifier" +name = "slimit" +optional = false +python-versions = "*" +version = "0.8.1" + +[package.dependencies] +ply = ">=3.4" + [[package]] category = "main" description = "A generic slugifier." @@ -2148,6 +2353,7 @@ wstools = "*" [[package]] category = "main" description = "A modern CSS selector implementation for Beautiful Soup." +marker = "python_version >= \"3.0\"" name = "soupsieve" optional = false python-versions = "*" @@ -2158,8 +2364,8 @@ category = "main" description = "Python documentation generator" name = "sphinx" optional = false -python-versions = ">=3.5" -version = "3.4.3" +python-versions = ">=3.6" +version = "4.0.2" [package.dependencies] Jinja2 = ">=2.3" @@ -2167,7 +2373,7 @@ Pygments = ">=2.0" alabaster = ">=0.7,<0.8" babel = ">=1.3" colorama = ">=0.3.5" -docutils = ">=0.12" +docutils = ">=0.14,<0.18" imagesize = "*" packaging = "*" requests = ">=2.5.0" @@ -2182,7 +2388,7 @@ sphinxcontrib-serializinghtml = "*" [package.extras] docs = ["sphinxcontrib-websupport"] -lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.790)", "docutils-stubs"] +lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.800)", "docutils-stubs"] test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"] [[package]] @@ -2221,9 +2427,10 @@ description = "Read the Docs theme for Sphinx" name = "sphinx-rtd-theme" optional = false python-versions = "*" -version = "0.5.1" +version = "0.5.2" [package.dependencies] +docutils = "<0.17" sphinx = "*" [package.extras] @@ -2305,20 +2512,37 @@ category = "main" description = "Database Abstraction Library" name = "sqlalchemy" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.3.22" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +version = "1.4.19" + +[package.dependencies] +[package.dependencies.greenlet] +python = ">=3" +version = "!=0.4.17" + +[package.dependencies.importlib-metadata] +python = "<3.8" +version = "*" [package.extras] +aiomysql = ["greenlet (!=0.4.17)", "aiomysql"] +aiosqlite = ["greenlet (!=0.4.17)", "aiosqlite"] +asyncio = ["greenlet (!=0.4.17)"] +mariadb_connector = ["mariadb (>=1.0.1)"] mssql = ["pyodbc"] mssql_pymssql = ["pymssql"] mssql_pyodbc = ["pyodbc"] -mysql = ["mysqlclient"] -oracle = ["cx-oracle"] -postgresql = ["psycopg2"] -postgresql_pg8000 = ["pg8000"] +mypy = ["sqlalchemy2-stubs", "mypy (>=0.800)"] +mysql = ["mysqlclient (>=1.4.0,<2)", "mysqlclient (>=1.4.0)"] +mysql_connector = ["mysqlconnector"] +oracle = ["cx_oracle (>=7,<8)", "cx_oracle (>=7)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql_asyncpg = ["greenlet (!=0.4.17)", "asyncpg"] +postgresql_pg8000 = ["pg8000 (>=1.16.6)"] postgresql_psycopg2binary = ["psycopg2-binary"] postgresql_psycopg2cffi = ["psycopg2cffi"] -pymysql = ["pymysql"] +pymysql = ["pymysql (<1)", "pymysql"] +sqlcipher = ["sqlcipher3-binary"] [[package]] category = "main" @@ -2359,6 +2583,21 @@ optional = false python-versions = "*" version = "1.3" +[[package]] +category = "main" +description = "tinycss2" +name = "tinycss2" +optional = false +python-versions = ">=3.6" +version = "1.1.0" + +[package.dependencies] +webencodings = ">=0.4" + +[package.extras] +doc = ["sphinx", "sphinx-rtd-theme"] +test = ["pytest", "pytest-cov", "pytest-flake8", "pytest-isort", "coverage"] + [[package]] category = "main" description = "Python Library for Tom's Obvious, Minimal Language" @@ -2395,10 +2634,19 @@ version = "3.8.2" requests = ">=2.1.0" requests-oauthlib = ">=0.4.0" +[[package]] +category = "main" +description = "a fork of Python 2 and 3 ast modules with type comment support" +marker = "implementation_name == \"cpython\" and python_version < \"3.8\" or python_version < \"3.8\"" +name = "typed-ast" +optional = false +python-versions = "*" +version = "1.4.3" + [[package]] category = "main" description = "Backported and Experimental Type Hints for Python 3.5+" -marker = "python_version >= \"3.6\" and python_version < \"3.8\"" +marker = "python_version >= \"3.6\" and python_version < \"3.8\" or python_version < \"3.8\"" name = "typing-extensions" optional = false python-versions = "*" @@ -2485,14 +2733,6 @@ version = "0.1.7" [package.dependencies] vcrpy = "*" -[[package]] -category = "main" -description = "Verbose logging level for Python's logging module" -name = "verboselogs" -optional = false -python-versions = "*" -version = "1.7" - [[package]] category = "main" description = "Create standard barcodes with Python. No external modules needed (optional PIL support included)." @@ -2536,14 +2776,19 @@ six = "*" [[package]] category = "main" -description = "WebSocket client for Python. hybi13 is supported." +description = "Character encoding aliases for legacy web content" +name = "webencodings" +optional = false +python-versions = "*" +version = "0.5.1" + +[[package]] +category = "main" +description = "WebSocket client for Python with low level API options" name = "websocket-client" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.57.0" - -[package.dependencies] -six = "*" +python-versions = ">=3.6" +version = "1.1.0" [[package]] category = "main" @@ -2551,7 +2796,7 @@ description = "WeChat SDK for Python" name = "wechatpy" optional = false python-versions = "*" -version = "1.8.14" +version = "1.8.15" [package.dependencies] optionaldict = ">=0.1.0" @@ -2572,6 +2817,14 @@ optional = false python-versions = "*" version = "0.11.15" +[[package]] +category = "main" +description = "pure python download utility" +name = "wget" +optional = false +python-versions = "*" +version = "3.2" + [[package]] category = "main" description = "A built-package format for Python" @@ -2583,6 +2836,14 @@ version = "0.36.2" [package.extras] test = ["pytest (>=3.0.0)", "pytest-cov"] +[[package]] +category = "main" +description = "This package provides cross-platform cross-python shutil.which functionality." +name = "whichcraft" +optional = false +python-versions = "*" +version = "0.6.1" + [[package]] category = "main" description = "Module for decorators, wrappers and monkey patching." @@ -2628,6 +2889,14 @@ optional = false python-versions = "*" version = "1.3.0" +[[package]] +category = "main" +description = "Format and compress XML documents" +name = "xmlformatter" +optional = false +python-versions = "*" +version = "0.2.2" + [[package]] category = "main" description = "Makes working with XML feel like you are working with JSON" @@ -2701,7 +2970,7 @@ python-versions = "*" version = "4.4.28" [metadata] -content-hash = "88ae1fc45a0fb59a5ede3d1a1e0220c8f5cde8722a7bf01435a65298eaa66599" +content-hash = "8f717dd4d3138e704720a02bc6ca7efb301108825e0cfc0dcd16c2a8d3822b6c" lock-version = "1.0" python-versions = "^3.7.7" @@ -2723,8 +2992,8 @@ appdirs = [ {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, ] argcomplete = [ - {file = "argcomplete-1.12.2-py2.py3-none-any.whl", hash = "sha256:17f01a9b9b9ece3e6b07058eae737ad6e10de8b4e149105f84614783913aba71"}, - {file = "argcomplete-1.12.2.tar.gz", hash = "sha256:de0e1282330940d52ea92a80fea2e4b9e0da1932aaa570f84d268939d1897b04"}, + {file = "argcomplete-1.12.3-py2.py3-none-any.whl", hash = "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81"}, + {file = "argcomplete-1.12.3.tar.gz", hash = "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445"}, ] argparse = [ {file = "argparse-1.4.0-py2.py3-none-any.whl", hash = "sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314"}, @@ -2737,6 +3006,10 @@ asn1crypto = [ asterisk = [ {file = "asterisk-0.0.8.tar.gz", hash = "sha256:a7813896f355f8ed0ca1bdea05938a10c2788255332fc826cf87a4cb34308f37"}, ] +astroid = [ + {file = "astroid-2.5-py3-none-any.whl", hash = "sha256:87ae7f2398b8a0ae5638ddecf9987f081b756e0e9fc071aeebdca525671fc4dc"}, + {file = "astroid-2.5.tar.gz", hash = "sha256:b31c92f545517dcc452f284bc9c044050862fbe6d93d2b3de4a215a6b384bf0d"}, +] attrs = [ {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, @@ -2767,35 +3040,32 @@ bcrypt = [ {file = "bcrypt-3.1.7.tar.gz", hash = "sha256:0b0069c752ec14172c5f78208f1863d7ad6755a6fae6fe76ec2c80d13be41e42"}, ] beautifulsoup4 = [ - {file = "beautifulsoup4-4.9.1-py2-none-any.whl", hash = "sha256:e718f2342e2e099b640a34ab782407b7b676f47ee272d6739e60b8ea23829f2c"}, - {file = "beautifulsoup4-4.9.1-py3-none-any.whl", hash = "sha256:a6237df3c32ccfaee4fd201c8f5f9d9df619b93121d01353a64a73ce8c6ef9a8"}, - {file = "beautifulsoup4-4.9.1.tar.gz", hash = "sha256:73cc4d115b96f79c7d77c1c7f7a0a8d4c57860d1041df407dd1aae7f07a77fd7"}, + {file = "beautifulsoup4-4.9.3-py2-none-any.whl", hash = "sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35"}, + {file = "beautifulsoup4-4.9.3-py3-none-any.whl", hash = "sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666"}, + {file = "beautifulsoup4-4.9.3.tar.gz", hash = "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25"}, ] +black = [] bokeh = [ {file = "bokeh-1.1.0.tar.gz", hash = "sha256:70c944ccb99faea9c1bc2b77caba4dbcd884e083fc32511efbd6f895d7cb6832"}, ] -boto = [ - {file = "boto-2.49.0-py2.py3-none-any.whl", hash = "sha256:147758d41ae7240dc989f0039f27da8ca0d53734be0eb869ef16e3adcfa462e8"}, - {file = "boto-2.49.0.tar.gz", hash = "sha256:ea0d3b40a2d852767be77ca343b58a9e3a4b00d9db440efb8da74b4e58025e5a"}, -] boto3 = [ {file = "boto3-1.9.102-py2.py3-none-any.whl", hash = "sha256:4605a0707e857f316a7b01766fb49350e7b3d922458aa9c870f6393ec6fa22ca"}, {file = "boto3-1.9.102.tar.gz", hash = "sha256:1f43d7a4865b75c4628a5084c2c32b00f356a9fd09f97574a8903c574356890a"}, ] botocore = [ - {file = "botocore-1.12.253-py2.py3-none-any.whl", hash = "sha256:dc080aed4f9b220a9e916ca29ca97a9d37e8e1d296fe89cbaeef929bf0c8066b"}, - {file = "botocore-1.12.253.tar.gz", hash = "sha256:3baf129118575602ada9926f5166d82d02273c250d0feb313fc270944b27c48b"}, + {file = "botocore-1.12.191-py2.py3-none-any.whl", hash = "sha256:6f609214e358d602a927af375e851af8edff0fb7dcdc0f9790416936eded0442"}, + {file = "botocore-1.12.191.tar.gz", hash = "sha256:971bfcce68b0c92cb8ff6c43a080a2baa07f0f6809582d758da19c14fc039da0"}, ] cached-property = [ {file = "cached-property-1.5.1.tar.gz", hash = "sha256:9217a59f14a5682da7c4b8829deadbfc194ac22e9908ccf7c8820234e80a1504"}, {file = "cached_property-1.5.1-py2.py3-none-any.whl", hash = "sha256:3a026f1a54135677e7da5ce819b0c690f156f37976f3e30c5430740725203d7f"}, ] cachetools = [ - {file = "cachetools-4.2.0-py3-none-any.whl", hash = "sha256:c6b07a6ded8c78bf36730b3dc452dfff7d95f2a12a2fed856b1a0cb13ca78c61"}, - {file = "cachetools-4.2.0.tar.gz", hash = "sha256:3796e1de094f0eaca982441c92ce96c68c89cced4cd97721ab297ea4b16db90e"}, + {file = "cachetools-4.2.2-py3-none-any.whl", hash = "sha256:2cc0b89715337ab6dbba85b5b50effe2b0c74e035d83ee8ed637cf52f12ae001"}, + {file = "cachetools-4.2.2.tar.gz", hash = "sha256:61b5ed1e22a0924aed1d23b478f37e8d52549ff8a961de2909c69bf950020cff"}, ] cerberus = [ - {file = "Cerberus-1.3.2.tar.gz", hash = "sha256:302e6694f206dd85cb63f13fd5025b31ab6d38c99c50c6d769f8fa0b0f299589"}, + {file = "Cerberus-1.3.4.tar.gz", hash = "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c"}, ] certifi = [ {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"}, @@ -2836,8 +3106,11 @@ chardet = [ {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, ] click = [ - {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, - {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, + {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"}, + {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"}, +] +cloudflare = [ + {file = "cloudflare-2.8.15.tar.gz", hash = "sha256:1f47bd324f80e91487dea2c79be934b1dc612bcfa63e784dcf74c6a2f52a41cc"}, ] code-writer = [ {file = "code-writer-1.1.1.tar.gz", hash = "sha256:485260d0444707876596c499bd9b1ef9a11dc720d741ac57c884216de9fa3f40"}, @@ -2847,10 +3120,6 @@ colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] -coloredlogs = [ - {file = "coloredlogs-14.0-py2.py3-none-any.whl", hash = "sha256:346f58aad6afd48444c2468618623638dadab76e4e70d5e10822676f2d32226a"}, - {file = "coloredlogs-14.0.tar.gz", hash = "sha256:a1fab193d2053aa6c0a97608c4342d031f1f93a3d1218432c59322441d31a505"}, -] compressed-rtf = [ {file = "compressed_rtf-1.0.6.tar.gz", hash = "sha256:c1c827f1d124d24608981a56e8b8691eb1f2a69a78ccad6440e7d92fde1781dd"}, ] @@ -2875,6 +3144,11 @@ cryptography = [ {file = "cryptography-2.6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:2afb83308dc5c5255149ff7d3fb9964f7c9ee3d59b603ec18ccf5b0a8852e2b1"}, {file = "cryptography-2.6.1.tar.gz", hash = "sha256:26c821cbeb683facb966045e2064303029d572a87ee69ca5a1bf54bf55f93ca6"}, ] +css-html-prettify = [ + {file = "css-html-prettify-2.5.5.zip", hash = "sha256:4fbdd6d42256fc9573fea57874852b8df58bef469b0a3e2f154000b85bcbbc44"}, + {file = "css_html_prettify-2.5.5-py2.py3-none-any.whl", hash = "sha256:992040e47cd81511c8ca8f632febda380b7d93908418703a29d310c0b56077a6"}, + {file = "css_html_prettify-2.5.5-py3.6.egg", hash = "sha256:d1df7cf0830298a5dd532dfefa240291021d66097b4f8446cbbf3b3050f04b61"}, +] cssselect = [ {file = "cssselect-1.1.0-py2.py3-none-any.whl", hash = "sha256:f612ee47b749c877ebae5bb77035d8f4202c6ad0f0fc1271b3c18ad6c4468ecf"}, {file = "cssselect-1.1.0.tar.gz", hash = "sha256:f95f8dedd925fd8f54edb3d2dfb44c190d9d18512377d3c1e2388d16126879bc"}, @@ -2888,41 +3162,41 @@ cycler = [ {file = "cycler-0.10.0.tar.gz", hash = "sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"}, ] cython = [ - {file = "Cython-0.29.21-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c541b2b49c6638f2b5beb9316726db84a8d1c132bf31b942dae1f9c7f6ad3b92"}, - {file = "Cython-0.29.21-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b8d8497091c1dc8705d1575c71e908a93b1f127a174b2d472020f3d84263ac28"}, - {file = "Cython-0.29.21-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:695a6bcaf9e12b1e471dfce96bbecf22a1487adc2ac6106b15960a2b51b97f5d"}, - {file = "Cython-0.29.21-cp27-cp27m-win32.whl", hash = "sha256:171b9f70ceafcec5852089d0f9c1e75b0d554f46c882cd4e2e4acaba9bd7d148"}, - {file = "Cython-0.29.21-cp27-cp27m-win_amd64.whl", hash = "sha256:539e59949aab4955c143a468810123bf22d3e8556421e1ce2531ed4893914ca0"}, - {file = "Cython-0.29.21-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:e93acd1f603a0c1786e0841f066ae7cef014cf4750e3cd06fd03cfdf46361419"}, - {file = "Cython-0.29.21-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:2922e3031ba9ebbe7cb9200b585cc33b71d66023d78450dcb883f824f4969371"}, - {file = "Cython-0.29.21-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:497841897942f734b0abc2dead2d4009795ee992267a70a23485fd0e937edc0b"}, - {file = "Cython-0.29.21-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:0ac10bf476476a9f7ef61ec6e44c280ef434473124ad31d3132b720f7b0e8d2a"}, - {file = "Cython-0.29.21-cp34-cp34m-win32.whl", hash = "sha256:31c71a615f38401b0dc1f2a5a9a6c421ffd8908c4cd5bbedc4014c1b876488e8"}, - {file = "Cython-0.29.21-cp34-cp34m-win_amd64.whl", hash = "sha256:c4b78356074fcaac04ecb4de289f11d506e438859877670992ece11f9c90f37b"}, - {file = "Cython-0.29.21-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:b2f9172e4d6358f33ecce6a4339b5960f9f83eab67ea244baa812737793826b7"}, - {file = "Cython-0.29.21-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:856c7fb31d247ce713d60116375e1f8153d0291ab5e92cca7d8833a524ba9991"}, - {file = "Cython-0.29.21-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:715294cd2246b39a8edca464a8366eb635f17213e4a6b9e74e52d8b877a8cb63"}, - {file = "Cython-0.29.21-cp35-cp35m-win32.whl", hash = "sha256:23f3a00b843a19de8bb4468b087db5b413a903213f67188729782488d67040e0"}, - {file = "Cython-0.29.21-cp35-cp35m-win_amd64.whl", hash = "sha256:ccb77faeaad99e99c6c444d04862c6cf604204fe0a07d4c8f9cbf2c9012d7d5a"}, - {file = "Cython-0.29.21-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e272ed97d20b026f4f25a012b25d7d7672a60e4f72b9ca385239d693cd91b2d5"}, - {file = "Cython-0.29.21-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:8c6e25e9cc4961bb2abb1777c6fa9d0fa2d9b014beb3276cebe69996ff162b78"}, - {file = "Cython-0.29.21-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:57ead89128dee9609119c93d3926c7a2add451453063147900408a50144598c6"}, - {file = "Cython-0.29.21-cp36-cp36m-win32.whl", hash = "sha256:0e25c209c75df8785480dcef85db3d36c165dbc0f4c503168e8763eb735704f2"}, - {file = "Cython-0.29.21-cp36-cp36m-win_amd64.whl", hash = "sha256:a0674f246ad5e1571ef29d4c5ec1d6ecabe9e6c424ad0d6fee46b914d5d24d69"}, - {file = "Cython-0.29.21-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5da187bebe38030325e1c0b5b8a804d489410be2d384c0ef3ba39493c67eb51e"}, - {file = "Cython-0.29.21-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:9ce5e5209f8406ffc2b058b1293cce7a954911bb7991e623564d489197c9ba30"}, - {file = "Cython-0.29.21-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5e545a48f919e40079b0efe7b0e081c74b96f9ef25b9c1ff4cdbd95764426b58"}, - {file = "Cython-0.29.21-cp37-cp37m-win32.whl", hash = "sha256:c8435959321cf8aec867bbad54b83b7fb8343204b530d85d9ea7a1f5329d5ac2"}, - {file = "Cython-0.29.21-cp37-cp37m-win_amd64.whl", hash = "sha256:540b3bee0711aac2e99bda4fa0a46dbcd8c74941666bfc1ef9236b1a64eeffd9"}, - {file = "Cython-0.29.21-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:93f5fed1c9445fb7afe20450cdaf94b0e0356d47cc75008105be89c6a2e417b1"}, - {file = "Cython-0.29.21-cp38-cp38-manylinux1_i686.whl", hash = "sha256:9207fdedc7e789a3dcaca628176b80c82fbed9ae0997210738cbb12536a56699"}, - {file = "Cython-0.29.21-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:603b9f1b8e93e8b494d3e89320c410679e21018e48b6cbc77280f5db71f17dc0"}, - {file = "Cython-0.29.21-cp38-cp38-win32.whl", hash = "sha256:473df5d5e400444a36ed81c6596f56a5b52a3481312d0a48d68b777790f730ae"}, - {file = "Cython-0.29.21-cp38-cp38-win_amd64.whl", hash = "sha256:b8a8a31b9e8860634adbca30fea1d0c7f08e208b3d7611f3e580e5f20992e5d7"}, - {file = "Cython-0.29.21-cp39-cp39-manylinux1_i686.whl", hash = "sha256:7ebaa8800c376bcdae596fb1372cb4232a5ef957619d35839520d2786f2debb9"}, - {file = "Cython-0.29.21-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:c111ac9abdf715762e4fb87395e59d61c0fbb6ce79eb2e24167700b6cfa8ba79"}, - {file = "Cython-0.29.21-py2.py3-none-any.whl", hash = "sha256:5c4276fdcbccdf1e3c1756c7aeb8395e9a36874fa4d30860e7694f43d325ae13"}, - {file = "Cython-0.29.21.tar.gz", hash = "sha256:e57acb89bd55943c8d8bf813763d20b9099cc7165c0f16b707631a7654be9cad"}, + {file = "Cython-0.29.23-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ff885f18d169759b57f116d3956e45cd2b9cba989fde348bba091544c668dc11"}, + {file = "Cython-0.29.23-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:3b29224eb62309a10819d923dc6262f769e4f3facfee3cd06372c355e5b38b33"}, + {file = "Cython-0.29.23-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:355a6e768d91e21fbf477b61881bab64b7a2da386a166898997bccefd532cf5d"}, + {file = "Cython-0.29.23-cp27-cp27m-win32.whl", hash = "sha256:2af52d312e96b38ded38b34d06e22685c226b1b0e58278bd27209f5d2385d115"}, + {file = "Cython-0.29.23-cp27-cp27m-win_amd64.whl", hash = "sha256:519fccf526d26b377e1db22f22aa44889b28bc5833ec106588cb13557e8ba2da"}, + {file = "Cython-0.29.23-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:625a16103770fd92b487b701fb0c07e5790b080f40fa11ce572a2d56d9e9fcca"}, + {file = "Cython-0.29.23-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7b7a766726d207d7cd57aff0fcb4b35ce042d3cc88a421fcdb45eeb61a5b9d12"}, + {file = "Cython-0.29.23-cp34-cp34m-win32.whl", hash = "sha256:474c1a29ab43e29d990df279e2cf6aa96baa9208f5cd4bc76ac87ffcdf1e2945"}, + {file = "Cython-0.29.23-cp34-cp34m-win_amd64.whl", hash = "sha256:f4aca6bffb1c1c3c4ada3347d0b162a699c18a66e097ee08b63b3a35118fdfcc"}, + {file = "Cython-0.29.23-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:58dc06871bfdb0592542d779714fe9f918e11ba20ac07757dd63b198bdc704fe"}, + {file = "Cython-0.29.23-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:2a3bbce689a2fddb85aa66712d93875c99bf7f64ac82b1d149ecce522a7a4e0c"}, + {file = "Cython-0.29.23-cp35-cp35m-win32.whl", hash = "sha256:3ef530f975e3a760e7282fce2a25f900fa63f96d17321b4aa5f5542eb9859cdf"}, + {file = "Cython-0.29.23-cp35-cp35m-win_amd64.whl", hash = "sha256:ef21c51350462160456eb71df31b0869e5141e940f22c61c358bdb6e3ebc3388"}, + {file = "Cython-0.29.23-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:20402ef316393168909926ab21848aa6e08e39bed5003b657139774e66166cd0"}, + {file = "Cython-0.29.23-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4858043ac5f96a8f0277cf63760bb39b9521c1f897678cf1d22423f3e758f4ed"}, + {file = "Cython-0.29.23-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4d7c3b0882d8757c601eaf288fc0d321d5c7ac6c3afb8c42eddf9325a3419cf5"}, + {file = "Cython-0.29.23-cp36-cp36m-win32.whl", hash = "sha256:37ff66039e3d138ec968ee1d1e12441fa5fb4e6a9c5458bc3c3a232f01be4a7d"}, + {file = "Cython-0.29.23-cp36-cp36m-win_amd64.whl", hash = "sha256:5be3ae3189cf7d0e9bbeafb854496dc7030c6f6a5602d809435fab8223543a41"}, + {file = "Cython-0.29.23-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b0699f0dc90181f2458fdb8170455e7798a309e18f41379eda7a2dc8c7aadee0"}, + {file = "Cython-0.29.23-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41cd0dd2ff5d78466e73409db509887a84449b400074d4f217980cedbb18e4be"}, + {file = "Cython-0.29.23-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:aa3bb0928fb2aa3a8828801eb8b29af2261c199f805ae835467489e2bdd00372"}, + {file = "Cython-0.29.23-cp37-cp37m-win32.whl", hash = "sha256:20cb50d9fede8029bdb50875458f07a27f909289aeed4cdb9c19544dd9a9bc45"}, + {file = "Cython-0.29.23-cp37-cp37m-win_amd64.whl", hash = "sha256:c4b82461edbbcf90f19b319006345b77474a2d7514e1476d49a14bbd55d6b797"}, + {file = "Cython-0.29.23-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:794e3df0b57e16bce7583ac909126f4cb381fe566adadb20484d89095855eedb"}, + {file = "Cython-0.29.23-cp38-cp38-manylinux1_i686.whl", hash = "sha256:0c4b9f7e3aa004cf3f364e3e772f55fec5740485bafea99d1f13bdc9bbd8a545"}, + {file = "Cython-0.29.23-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ceccc03b633113ede1f14ad914a6db5c278ce108c8ddb308a5c01c1567d8a02a"}, + {file = "Cython-0.29.23-cp38-cp38-win32.whl", hash = "sha256:4b0bcf2e06a9063fc78c3243ed4003228375d532ef13b9e5d7183be8f0a52cf5"}, + {file = "Cython-0.29.23-cp38-cp38-win_amd64.whl", hash = "sha256:5a6792153b728a0240e55bbb5b643f4f7e45c76319e03abf15bf367471ea1d1a"}, + {file = "Cython-0.29.23-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a8eed9c82e8fe07b8a8ffbd36018871a17458903fc25c9d015f37b54513a3efd"}, + {file = "Cython-0.29.23-cp39-cp39-manylinux1_i686.whl", hash = "sha256:266459c7e48fe3c6c492b297e4033e42d4c6863cc1a1ff7cc4034949fc574fa6"}, + {file = "Cython-0.29.23-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4b6824b58d4373224fc76ee8bee6b35c2d17c91a1ed0fa67b88440f63daebe50"}, + {file = "Cython-0.29.23-cp39-cp39-win32.whl", hash = "sha256:7d6a33c8a11f05f698e215bfdb837f32c27f63c20f3af863557ed91c748dc2be"}, + {file = "Cython-0.29.23-cp39-cp39-win_amd64.whl", hash = "sha256:2365f3b5e6451b6bc6dcd262230656f4ade1d862ec2f6c22154deebef37c08b6"}, + {file = "Cython-0.29.23-py2.py3-none-any.whl", hash = "sha256:282263628c5d601b313d5920f7b6d7e08c7fedbddacd080c4858aa04d86b6b4b"}, + {file = "Cython-0.29.23.tar.gz", hash = "sha256:6a0d31452f0245daacb14c979c77e093eb1a546c760816b5eed0047686baad8e"}, ] dateparser = [ {file = "dateparser-0.7.6-py2.py3-none-any.whl", hash = "sha256:7552c994f893b5cb8fcf103b4cd2ff7f57aab9bfd2619fdf0cf571c0740fd90b"}, @@ -2955,16 +3229,23 @@ dnspython = [ {file = "dnspython-2.0.0.zip", hash = "sha256:044af09374469c3a39eeea1a146e8cac27daec951f1f1f157b1962fc7cb9d1b7"}, ] docutils = [ - {file = "docutils-0.12-py3-none-any.whl", hash = "sha256:dcebd4928112631626f4c4d0df59787c748404e66dda952110030ea883d3b8cd"}, - {file = "docutils-0.12.tar.gz", hash = "sha256:c7db717810ab6965f66c8cf0398a98c9d8df982da39b4cd7f162911eb89596fa"}, + {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, + {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"}, ] ebaysdk = [ {file = "ebaysdk-2.1.5-py3.7.egg", hash = "sha256:55360edc239037c3a0aaaf3f39889ddea666b8b1d97c893b2c3fd2269d868b45"}, {file = "ebaysdk-2.1.5.tar.gz", hash = "sha256:78458e1ea4a0fc7d693c26de363069696393767e4aa9689fd38efbfb08409acd"}, ] +ebcdic = [ + {file = "ebcdic-1.1.1-py2.py3-none-any.whl", hash = "sha256:33b4cb729bc2d0bf46cc1847b0e5946897cb8d3f53520c5b9aa5fa98d7e735f1"}, +] email-validator = [ - {file = "email-validator-1.1.2.tar.gz", hash = "sha256:1a13bd6050d1db4475f13e444e169b6fe872434922d38968c67cea9568cce2f0"}, - {file = "email_validator-1.1.2-py2.py3-none-any.whl", hash = "sha256:094b1d1c60d790649989d38d34f69e1ef07792366277a2cf88684d03495d018f"}, + {file = "email_validator-1.1.3-py2.py3-none-any.whl", hash = "sha256:5675c8ceb7106a37e40e2698a57c056756bf3f272cfa8682a4f87ebd95d8440b"}, + {file = "email_validator-1.1.3.tar.gz", hash = "sha256:aa237a65f6f4da067119b7df3f13e89c25c051327b2b5b66dc075f33d62480d7"}, +] +emoji = [ + {file = "emoji-1.2.0-py3-none-any.whl", hash = "sha256:6b19b65da8d6f30551eead1705539cc0eadcd9e33a6ecbc421a29b87f96287eb"}, + {file = "emoji-1.2.0.tar.gz", hash = "sha256:496f432058567985838c13d67dde84ca081614a8286c0b9cdc7d63dfa89d51a3"}, ] entrypoints = [ {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"}, @@ -2976,31 +3257,28 @@ enum34 = [ {file = "enum34-1.1.10.tar.gz", hash = "sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248"}, ] escpos = [ - {file = "escpos-1.8.tar.gz", hash = "sha256:54760134065b99c2ce9fd7fc3da1b5cff04580791e8a1849e56c3671b2d391be"}, + {file = "escpos-1.9-py3.8.egg", hash = "sha256:df5f7bad9ab904a3a41f3456e0bbecc436951ecce9c34825b7173507efa7c08e"}, + {file = "escpos-1.9.tar.gz", hash = "sha256:5acc126fa5ddd6ec395ccd9c7729545e91d85ebb2f5b17e9ab88e33d6925f501"}, ] et-xmlfile = [ {file = "et_xmlfile-1.0.1.tar.gz", hash = "sha256:614d9722d572f6246302c4491846d2c393c199cfa4edc9af593437691683335b"}, ] -executor = [ - {file = "executor-23.1-py2.py3-none-any.whl", hash = "sha256:0104bb9d7f5a3bd00642ad031ad2241d0663988bcb3da1c976e763db29b0d6ae"}, - {file = "executor-23.1.tar.gz", hash = "sha256:c8d4bba81d80c16612fcbcefd45439f45f82daa1d6e24c44b1832f3bb4649d5f"}, -] extract-msg = [ - {file = "extract_msg-0.28.0-py2.py3-none-any.whl", hash = "sha256:694ea75633e88d942b29a47c10dc9b6b9f84a2c42ebcf3d0c4b8ee0aed72755e"}, - {file = "extract_msg-0.28.0.tar.gz", hash = "sha256:72eace1f0c2dd772f3fd6745f736d5681eb7ee3e9d8102a56520bb2d6e9d3bfb"}, + {file = "extract_msg-0.28.7-py2.py3-none-any.whl", hash = "sha256:6ad2702bef86e6c1b8505e2993c7f3d37a1f3d140903138ee2df4a299dd2a29c"}, + {file = "extract_msg-0.28.7.tar.gz", hash = "sha256:7ebdbd7863a3699080a69f71ec0cd30ed9bfee70bad9acc6a8e6abe9523c78c0"}, ] factur-x = [ - {file = "factur-x-1.12.tar.gz", hash = "sha256:423d29ab0f8816cdb93bab9ebef4b6d7841c7b2ee747397d5b009ef5afce44e3"}, -] -fasteners = [ - {file = "fasteners-0.15-py2.py3-none-any.whl", hash = "sha256:007e4d2b2d4a10093f67e932e5166722d2eab83b77724156e92ad013c6226574"}, - {file = "fasteners-0.15.tar.gz", hash = "sha256:3a176da6b70df9bb88498e1a18a9e4a8579ed5b9141207762368a1017bf8f5ef"}, + {file = "factur-x-2.3.tar.gz", hash = "sha256:1b930a09ae6df3c9ed7bdf5d7088e7160304310840a7f29847c4e7296d5b1f0e"}, ] feedparser = [ {file = "feedparser-5.2.1.tar.bz2", hash = "sha256:ce875495c90ebd74b179855449040003a1beb40cd13d5f037a0654251e260b02"}, {file = "feedparser-5.2.1.tar.gz", hash = "sha256:bd030652c2d08532c034c27fcd7c85868e7fa3cb2b17f230a44a6bbc92519bf9"}, {file = "feedparser-5.2.1.zip", hash = "sha256:cd2485472e41471632ed3029d44033ee420ad0b57111db95c240c9160a85831c"}, ] +flake8 = [ + {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, + {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, +] freezegun = [ {file = "freezegun-0.3.11-py2.py3-none-any.whl", hash = "sha256:6cb82b276f83f2acce67f121dc2656f4df26c71e32238334eb071170b892a278"}, {file = "freezegun-0.3.11.tar.gz", hash = "sha256:e839b43bfbe8158b4d62bb97e6313d39f3586daf48e1314fb1083d2ef17700da"}, @@ -3075,9 +3353,12 @@ greenlet = [ html2text = [ {file = "html2text-2016.9.19.tar.gz", hash = "sha256:554ef5fd6c6cf6e3e4f725a62a3e9ec86a0e4d33cd0928136d1c79dbeb7b2d55"}, ] -humanfriendly = [ - {file = "humanfriendly-8.2-py2.py3-none-any.whl", hash = "sha256:e78960b31198511f45fd455534ae7645a6207d33e512d2e842c766d15d9c8080"}, - {file = "humanfriendly-8.2.tar.gz", hash = "sha256:bf52ec91244819c780341a3438d5d7b09f431d3f113a475147ac9b7b167a3d12"}, +html5lib = [ + {file = "html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d"}, + {file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"}, +] +html5print = [ + {file = "html5print-0.1.2.tar.gz", hash = "sha256:1ff645698a7192a61f8a5963f23dc894d16fa3346804a2d1ac462683f6e44d4b"}, ] humanize = [ {file = "humanize-2.5.0-py3-none-any.whl", hash = "sha256:89062c6db8601693b7d223443d0d7529aa9577df43a1387ddd4b9c273abb4a51"}, @@ -3100,8 +3381,8 @@ importlib-metadata = [ {file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"}, ] invoice2data = [ - {file = "invoice2data-0.3.5-py3.6.egg", hash = "sha256:5668b09cc6bcbc1c9acd03a699b0ddb5a2d481a8550977abb3313616752988cb"}, - {file = "invoice2data-0.3.5.tar.gz", hash = "sha256:c58ec7e46224dc3ffae72b17c05a5347fee1a13d9c17f6ee95f8bd16d1a2e791"}, + {file = "invoice2data-0.3.6-py3.9.egg", hash = "sha256:ff3b767056dfac473037d6caa1165e6a2b1c2683913d6df0a83a381f81ca48fa"}, + {file = "invoice2data-0.3.6.tar.gz", hash = "sha256:59d4c9e72cfb1577a74f25dd004c8d5d222bd3f82457489293c093bbec099761"}, ] iscompatible = [ {file = "iscompatible-0.1.1.tar.gz", hash = "sha256:0ef45bd23569df4ba01d66e4ab7a25f07db934bba235e706a85d26417d676f83"}, @@ -3115,9 +3396,9 @@ isodate = [ {file = "isodate-0.6.0-py2.py3-none-any.whl", hash = "sha256:aa4d33c06640f5352aca96e4b81afd8ab3b47337cc12089822d6f322ac772c81"}, {file = "isodate-0.6.0.tar.gz", hash = "sha256:2e364a3d5759479cdb2d37cce6b9376ea504db2ff90252a2e5b7cc89cc9ff2d8"}, ] -jdcal = [ - {file = "jdcal-1.4.1-py2.py3-none-any.whl", hash = "sha256:1abf1305fce18b4e8aa248cf8fe0c56ce2032392bc64bbd61b5dff2a19ec8bba"}, - {file = "jdcal-1.4.1.tar.gz", hash = "sha256:472872e096eb8df219c23f2689fc336668bdb43d194094b5cc1707e1640acfc8"}, +isort = [ + {file = "isort-5.9.1-py3-none-any.whl", hash = "sha256:8e2c107091cfec7286bc0f68a547d0ba4c094d460b732075b6fba674f1035c0c"}, + {file = "isort-5.9.1.tar.gz", hash = "sha256:83510593e07e433b77bd5bff0f6f607dbafa06d1a89022616f02d8b699cfcd56"}, ] jinja2 = [ {file = "Jinja2-2.10.1-py2.py3-none-any.whl", hash = "sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"}, @@ -3131,6 +3412,14 @@ jmespath = [ {file = "jmespath-0.10.0-py2.py3-none-any.whl", hash = "sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f"}, {file = "jmespath-0.10.0.tar.gz", hash = "sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9"}, ] +js2py = [ + {file = "Js2Py-0.71-py3-none-any.whl", hash = "sha256:7f3dfa30c4b8e3ac3edd64c361c950a8c2587a30a86854aeb8c1ffb6f4dd24b4"}, + {file = "Js2Py-0.71.tar.gz", hash = "sha256:a41b1009dd1498ae7d436bfa5ac952a08ca92a4bb9e31dca6e8bb966b49f7fce"}, +] +jsonlines = [ + {file = "jsonlines-2.0.0-py3-none-any.whl", hash = "sha256:bfb043d4e25fd894dca67b1f2adf014e493cb65d0f18b3a74a98bfcd97c3d983"}, + {file = "jsonlines-2.0.0.tar.gz", hash = "sha256:6fdd03104c9a421a1ba587a121aaac743bf02d8f87fa9cdaa3b852249a241fe8"}, +] jsonschema = [ {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"}, {file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"}, @@ -3166,6 +3455,30 @@ kiwisolver = [ lasso = [ {file = "lasso-0.0.5.tar.gz", hash = "sha256:e64abaeef6f3e3e5f669cc44ca1423e69ad59360625269f50ee6ecb2172aa824"}, ] +lazy-object-proxy = [ + {file = "lazy-object-proxy-1.6.0.tar.gz", hash = "sha256:489000d368377571c6f982fba6497f2aa13c6d1facc40660963da62f5c379726"}, + {file = "lazy_object_proxy-1.6.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:c6938967f8528b3668622a9ed3b31d145fab161a32f5891ea7b84f6b790be05b"}, + {file = "lazy_object_proxy-1.6.0-cp27-cp27m-win32.whl", hash = "sha256:ebfd274dcd5133e0afae738e6d9da4323c3eb021b3e13052d8cbd0e457b1256e"}, + {file = "lazy_object_proxy-1.6.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93"}, + {file = "lazy_object_proxy-1.6.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d900d949b707778696fdf01036f58c9876a0d8bfe116e8d220cfd4b15f14e741"}, + {file = "lazy_object_proxy-1.6.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5743a5ab42ae40caa8421b320ebf3a998f89c85cdc8376d6b2e00bd12bd1b587"}, + {file = "lazy_object_proxy-1.6.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:bf34e368e8dd976423396555078def5cfc3039ebc6fc06d1ae2c5a65eebbcde4"}, + {file = "lazy_object_proxy-1.6.0-cp36-cp36m-win32.whl", hash = "sha256:b579f8acbf2bdd9ea200b1d5dea36abd93cabf56cf626ab9c744a432e15c815f"}, + {file = "lazy_object_proxy-1.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:4f60460e9f1eb632584c9685bccea152f4ac2130e299784dbaf9fae9f49891b3"}, + {file = "lazy_object_proxy-1.6.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d7124f52f3bd259f510651450e18e0fd081ed82f3c08541dffc7b94b883aa981"}, + {file = "lazy_object_proxy-1.6.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:22ddd618cefe54305df49e4c069fa65715be4ad0e78e8d252a33debf00f6ede2"}, + {file = "lazy_object_proxy-1.6.0-cp37-cp37m-win32.whl", hash = "sha256:9d397bf41caad3f489e10774667310d73cb9c4258e9aed94b9ec734b34b495fd"}, + {file = "lazy_object_proxy-1.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:24a5045889cc2729033b3e604d496c2b6f588c754f7a62027ad4437a7ecc4837"}, + {file = "lazy_object_proxy-1.6.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:17e0967ba374fc24141738c69736da90e94419338fd4c7c7bef01ee26b339653"}, + {file = "lazy_object_proxy-1.6.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:410283732af311b51b837894fa2f24f2c0039aa7f220135192b38fcc42bd43d3"}, + {file = "lazy_object_proxy-1.6.0-cp38-cp38-win32.whl", hash = "sha256:85fb7608121fd5621cc4377a8961d0b32ccf84a7285b4f1d21988b2eae2868e8"}, + {file = "lazy_object_proxy-1.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:d1c2676e3d840852a2de7c7d5d76407c772927addff8d742b9808fe0afccebdf"}, + {file = "lazy_object_proxy-1.6.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b865b01a2e7f96db0c5d12cfea590f98d8c5ba64ad222300d93ce6ff9138bcad"}, + {file = "lazy_object_proxy-1.6.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4732c765372bd78a2d6b2150a6e99d00a78ec963375f236979c0626b97ed8e43"}, + {file = "lazy_object_proxy-1.6.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9698110e36e2df951c7c36b6729e96429c9c32b3331989ef19976592c5f3c77a"}, + {file = "lazy_object_proxy-1.6.0-cp39-cp39-win32.whl", hash = "sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61"}, + {file = "lazy_object_proxy-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b"}, +] leven = [ {file = "leven-1.0.4.tar.gz", hash = "sha256:732474a3b9e94003aab61fd57eb2cbdaf0ed32f9a85b63073bcc4fa1ed5d0aff"}, ] @@ -3241,14 +3554,14 @@ matplotlib = [ {file = "matplotlib-3.2.2-pp36-pypy36_pp73-win32.whl", hash = "sha256:67065d938df34478451af62fbd0670d2b51c4d859fb66673064eb5de8660dd7c"}, {file = "matplotlib-3.2.2.tar.gz", hash = "sha256:3d77a6630d093d74cbbfebaa0571d00790966be1ed204e4a8239f5cbd6835c5d"}, ] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] mock = [ {file = "mock-2.0.0-py2.py3-none-any.whl", hash = "sha256:5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1"}, {file = "mock-2.0.0.tar.gz", hash = "sha256:b158b6df76edd239b8208d481dc46b6afd45a846b7812ff0ce58971cf5bc8bba"}, ] -monotonic = [ - {file = "monotonic-1.5-py2.py3-none-any.whl", hash = "sha256:552a91f381532e33cbd07c6a2655a21908088962bb8fa7239ecbcc6ad1140cc7"}, - {file = "monotonic-1.5.tar.gz", hash = "sha256:23953d55076df038541e648a53676fb24980f7a1be290cdda21300b3bc21dfb0"}, -] mpld3 = [ {file = "mpld3-0.3.tar.gz", hash = "sha256:4d455884a211bf99b37ecc760759435c7bb6a5955de47d8daf4967e301878ab7"}, ] @@ -3291,6 +3604,10 @@ multidict = [ {file = "multidict-4.7.6-cp38-cp38-win_amd64.whl", hash = "sha256:7388d2ef3c55a8ba80da62ecfafa06a1c097c18032a501ffd4cabbc52d7f2b19"}, {file = "multidict-4.7.6.tar.gz", hash = "sha256:fbb77a75e529021e7c4a8d4e823d88ef4d23674a202be4f5addffc72cbb91430"}, ] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] mysqlclient = [ {file = "mysqlclient-2.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:3381ca1a4f37ff1155fcfde20836b46416d66531add8843f6aa6d968982731c3"}, {file = "mysqlclient-2.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:0ac0dd759c4ca02c35a9fedc24bc982cf75171651e8187c2495ec957a87dfff7"}, @@ -3298,9 +3615,6 @@ mysqlclient = [ {file = "mysqlclient-2.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:fc575093cf81b6605bed84653e48b277318b880dc9becf42dd47fa11ffd3e2b6"}, {file = "mysqlclient-2.0.3.tar.gz", hash = "sha256:f6ebea7c008f155baeefe16c56cd3ee6239f7a5a9ae42396c2f1860f08a7c432"}, ] -naturalsort = [ - {file = "naturalsort-1.5.1.tar.gz", hash = "sha256:172b5ed6e7ba3420c08ba2ded1c52fe74878f5d769dc5d9dec2b6a53e7117356"}, -] netaddr = [ {file = "netaddr-0.8.0-py2.py3-none-any.whl", hash = "sha256:9666d0232c32d2656e5e5f8d735f58fd6c7457ce52fc21c98d45f2af78f990ac"}, {file = "netaddr-0.8.0.tar.gz", hash = "sha256:d6cc57c7a07b1d9d2e917aa8b36ae8ce61c35ba3fcd1b83ca31c5a0ee2b5a243"}, @@ -3339,40 +3653,34 @@ num2words = [ {file = "num2words-0.5.6.tar.gz", hash = "sha256:aea26c2d11d636f0e9da094f2bf55ac94cb1c380ff1f86e8db22c210e5a6a05f"}, ] numpy = [ - {file = "numpy-1.19.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc6bd4fd593cb261332568485e20a0712883cf631f6f5e8e86a52caa8b2b50ff"}, - {file = "numpy-1.19.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:aeb9ed923be74e659984e321f609b9ba54a48354bfd168d21a2b072ed1e833ea"}, - {file = "numpy-1.19.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8b5e972b43c8fc27d56550b4120fe6257fdc15f9301914380b27f74856299fea"}, - {file = "numpy-1.19.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:43d4c81d5ffdff6bae58d66a3cd7f54a7acd9a0e7b18d97abb255defc09e3140"}, - {file = "numpy-1.19.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:a4646724fba402aa7504cd48b4b50e783296b5e10a524c7a6da62e4a8ac9698d"}, - {file = "numpy-1.19.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:2e55195bc1c6b705bfd8ad6f288b38b11b1af32f3c8289d6c50d47f950c12e76"}, - {file = "numpy-1.19.5-cp36-cp36m-win32.whl", hash = "sha256:39b70c19ec771805081578cc936bbe95336798b7edf4732ed102e7a43ec5c07a"}, - {file = "numpy-1.19.5-cp36-cp36m-win_amd64.whl", hash = "sha256:dbd18bcf4889b720ba13a27ec2f2aac1981bd41203b3a3b27ba7a33f88ae4827"}, - {file = "numpy-1.19.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:603aa0706be710eea8884af807b1b3bc9fb2e49b9f4da439e76000f3b3c6ff0f"}, - {file = "numpy-1.19.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cae865b1cae1ec2663d8ea56ef6ff185bad091a5e33ebbadd98de2cfa3fa668f"}, - {file = "numpy-1.19.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:36674959eed6957e61f11c912f71e78857a8d0604171dfd9ce9ad5cbf41c511c"}, - {file = "numpy-1.19.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:06fab248a088e439402141ea04f0fffb203723148f6ee791e9c75b3e9e82f080"}, - {file = "numpy-1.19.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6149a185cece5ee78d1d196938b2a8f9d09f5a5ebfbba66969302a778d5ddd1d"}, - {file = "numpy-1.19.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:50a4a0ad0111cc1b71fa32dedd05fa239f7fb5a43a40663269bb5dc7877cfd28"}, - {file = "numpy-1.19.5-cp37-cp37m-win32.whl", hash = "sha256:d051ec1c64b85ecc69531e1137bb9751c6830772ee5c1c426dbcfe98ef5788d7"}, - {file = "numpy-1.19.5-cp37-cp37m-win_amd64.whl", hash = "sha256:a12ff4c8ddfee61f90a1633a4c4afd3f7bcb32b11c52026c92a12e1325922d0d"}, - {file = "numpy-1.19.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cf2402002d3d9f91c8b01e66fbb436a4ed01c6498fffed0e4c7566da1d40ee1e"}, - {file = "numpy-1.19.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1ded4fce9cfaaf24e7a0ab51b7a87be9038ea1ace7f34b841fe3b6894c721d1c"}, - {file = "numpy-1.19.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:012426a41bc9ab63bb158635aecccc7610e3eff5d31d1eb43bc099debc979d94"}, - {file = "numpy-1.19.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:759e4095edc3c1b3ac031f34d9459fa781777a93ccc633a472a5468587a190ff"}, - {file = "numpy-1.19.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:a9d17f2be3b427fbb2bce61e596cf555d6f8a56c222bd2ca148baeeb5e5c783c"}, - {file = "numpy-1.19.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:99abf4f353c3d1a0c7a5f27699482c987cf663b1eac20db59b8c7b061eabd7fc"}, - {file = "numpy-1.19.5-cp38-cp38-win32.whl", hash = "sha256:384ec0463d1c2671170901994aeb6dce126de0a95ccc3976c43b0038a37329c2"}, - {file = "numpy-1.19.5-cp38-cp38-win_amd64.whl", hash = "sha256:811daee36a58dc79cf3d8bdd4a490e4277d0e4b7d103a001a4e73ddb48e7e6aa"}, - {file = "numpy-1.19.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c843b3f50d1ab7361ca4f0b3639bf691569493a56808a0b0c54a051d260b7dbd"}, - {file = "numpy-1.19.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d6631f2e867676b13026e2846180e2c13c1e11289d67da08d71cacb2cd93d4aa"}, - {file = "numpy-1.19.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7fb43004bce0ca31d8f13a6eb5e943fa73371381e53f7074ed21a4cb786c32f8"}, - {file = "numpy-1.19.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2ea52bd92ab9f768cc64a4c3ef8f4b2580a17af0a5436f6126b08efbd1838371"}, - {file = "numpy-1.19.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:400580cbd3cff6ffa6293df2278c75aef2d58d8d93d3c5614cd67981dae68ceb"}, - {file = "numpy-1.19.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:df609c82f18c5b9f6cb97271f03315ff0dbe481a2a02e56aeb1b1a985ce38e60"}, - {file = "numpy-1.19.5-cp39-cp39-win32.whl", hash = "sha256:ab83f24d5c52d60dbc8cd0528759532736b56db58adaa7b5f1f76ad551416a1e"}, - {file = "numpy-1.19.5-cp39-cp39-win_amd64.whl", hash = "sha256:0eef32ca3132a48e43f6a0f5a82cb508f22ce5a3d6f67a8329c81c8e226d3f6e"}, - {file = "numpy-1.19.5-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:a0d53e51a6cb6f0d9082decb7a4cb6dfb33055308c4c44f53103c073f649af73"}, - {file = "numpy-1.19.5.zip", hash = "sha256:a76f502430dd98d7546e1ea2250a7360c065a5fdea52b2dffe8ae7180909b6f4"}, + {file = "numpy-1.21.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d5caa946a9f55511e76446e170bdad1d12d6b54e17a2afe7b189112ed4412bb8"}, + {file = "numpy-1.21.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ac4fd578322842dbda8d968e3962e9f22e862b6ec6e3378e7415625915e2da4d"}, + {file = "numpy-1.21.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:598fe100b2948465cf3ed64b1a326424b5e4be2670552066e17dfaa67246011d"}, + {file = "numpy-1.21.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c55407f739f0bfcec67d0df49103f9333edc870061358ac8a8c9e37ea02fcd2"}, + {file = "numpy-1.21.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:75579acbadbf74e3afd1153da6177f846212ea2a0cc77de53523ae02c9256513"}, + {file = "numpy-1.21.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cc367c86eb87e5b7c9592935620f22d13b090c609f1b27e49600cd033b529f54"}, + {file = "numpy-1.21.0-cp37-cp37m-win32.whl", hash = "sha256:d89b0dc7f005090e32bb4f9bf796e1dcca6b52243caf1803fdd2b748d8561f63"}, + {file = "numpy-1.21.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eda2829af498946c59d8585a9fd74da3f810866e05f8df03a86f70079c7531dd"}, + {file = "numpy-1.21.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1a784e8ff7ea2a32e393cc53eb0003eca1597c7ca628227e34ce34eb11645a0e"}, + {file = "numpy-1.21.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bba474a87496d96e61461f7306fba2ebba127bed7836212c360f144d1e72ac54"}, + {file = "numpy-1.21.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fd0a359c1c17f00cb37de2969984a74320970e0ceef4808c32e00773b06649d9"}, + {file = "numpy-1.21.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e4d5a86a5257843a18fb1220c5f1c199532bc5d24e849ed4b0289fb59fbd4d8f"}, + {file = "numpy-1.21.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:620732f42259eb2c4642761bd324462a01cdd13dd111740ce3d344992dd8492f"}, + {file = "numpy-1.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9205711e5440954f861ceeea8f1b415d7dd15214add2e878b4d1cf2bcb1a914"}, + {file = "numpy-1.21.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ad09f55cc95ed8d80d8ab2052f78cc21cb231764de73e229140d81ff49d8145e"}, + {file = "numpy-1.21.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a1f2fb2da242568af0271455b89aee0f71e4e032086ee2b4c5098945d0e11cf6"}, + {file = "numpy-1.21.0-cp38-cp38-win32.whl", hash = "sha256:e58ddb53a7b4959932f5582ac455ff90dcb05fac3f8dcc8079498d43afbbde6c"}, + {file = "numpy-1.21.0-cp38-cp38-win_amd64.whl", hash = "sha256:d2910d0a075caed95de1a605df00ee03b599de5419d0b95d55342e9a33ad1fb3"}, + {file = "numpy-1.21.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a290989cd671cd0605e9c91a70e6df660f73ae87484218e8285c6522d29f6e38"}, + {file = "numpy-1.21.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3537b967b350ad17633b35c2f4b1a1bbd258c018910b518c30b48c8e41272717"}, + {file = "numpy-1.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccc6c650f8700ce1e3a77668bb7c43e45c20ac06ae00d22bdf6760b38958c883"}, + {file = "numpy-1.21.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:709884863def34d72b183d074d8ba5cfe042bc3ff8898f1ffad0209161caaa99"}, + {file = "numpy-1.21.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bebab3eaf0641bba26039fb0b2c5bf9b99407924b53b1ea86e03c32c64ef5aef"}, + {file = "numpy-1.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf680682ad0a3bef56dae200dbcbac2d57294a73e5b0f9864955e7dd7c2c2491"}, + {file = "numpy-1.21.0-cp39-cp39-win32.whl", hash = "sha256:d95d16204cd51ff1a1c8d5f9958ce90ae190be81d348b514f9be39f878b8044a"}, + {file = "numpy-1.21.0-cp39-cp39-win_amd64.whl", hash = "sha256:2ba579dde0563f47021dcd652253103d6fd66165b18011dce1a0609215b2791e"}, + {file = "numpy-1.21.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3c40e6b860220ed862e8097b8f81c9af6d7405b723f4a7af24a267b46f90e461"}, + {file = "numpy-1.21.0.zip", hash = "sha256:e80fe25cba41c124d04c662f33f6364909b985f2eb5998aaa5ae4b9587242cce"}, ] oauthlib = [ {file = "oauthlib-2.1.0-py2.py3-none-any.whl", hash = "sha256:d883b36b21a6ad813953803edfa563b1b579d79ca758fe950d1bc9e8b326025b"}, @@ -3390,8 +3698,8 @@ olefile = [ {file = "olefile-0.46.zip", hash = "sha256:133b031eaf8fd2c9399b78b8bc5b8fcbe4c31e85295749bb17a87cba8f3c3964"}, ] openpyxl = [ - {file = "openpyxl-3.0.5-py2.py3-none-any.whl", hash = "sha256:f7d666b569f729257082cf7ddc56262431878f602dcc2bc3980775c59439cdab"}, - {file = "openpyxl-3.0.5.tar.gz", hash = "sha256:18e11f9a650128a12580a58e3daba14e00a11d9e907c554a17ea016bf1a2c71b"}, + {file = "openpyxl-3.0.7-py2.py3-none-any.whl", hash = "sha256:46af4eaf201a89b610fcca177eed957635f88770a5462fb6aae4a2a52b0ff516"}, + {file = "openpyxl-3.0.7.tar.gz", hash = "sha256:6456a3b472e1ef0facb1129f3c6ef00713cebf62e736cd7a75bcc3247432f251"}, ] openupgradelib = [ {file = "openupgradelib-2.0.0.tar.gz", hash = "sha256:acb826ed5965db388d086a6585afb63aee89719082ea7ab8142fc170c02ff257"}, @@ -3452,21 +3760,28 @@ passlib = [ {file = "passlib-1.6.5-py2.py3-none-any.whl", hash = "sha256:ad631a58dc8abeb0f48016c13f4b3b0f3a7b1045a8cb3c61dd15e2d95b45c472"}, {file = "passlib-1.6.5.tar.gz", hash = "sha256:a83d34f53dc9b17aa42c9a35c3fbcc5120f3fcb07f7f8721ec45e6a27be347fc"}, ] +pathspec = [ + {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, + {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, +] pbr = [ {file = "pbr-5.4.5-py2.py3-none-any.whl", hash = "sha256:579170e23f8e0c2f24b0de612f71f648eccb79fb1322c814ae6b3c07b5ba23e8"}, {file = "pbr-5.4.5.tar.gz", hash = "sha256:07f558fece33b05caf857474a366dfcc00562bca13dd8b47b2b3e22d9f9bf55c"}, ] pdf2image = [ - {file = "pdf2image-1.14.0-py3-none-any.whl", hash = "sha256:cf1b2dc77cf3e050cb06078cd7373469c8dca93d4cea1af2a2e1bbe0ed4a8800"}, - {file = "pdf2image-1.14.0.tar.gz", hash = "sha256:066527e1bf954762fb4369c677ae3bc15f2ce8707eee830cccef8471fde736d7"}, + {file = "pdf2image-1.16.0-py3-none-any.whl", hash = "sha256:84f79f2b8fad943e36323ea4e937fcb05f26ded0caa0a01181df66049e42fb65"}, + {file = "pdf2image-1.16.0.tar.gz", hash = "sha256:d58ed94d978a70c73c2bb7fdf8acbaf2a7089c29ff8141be5f45433c0c4293bb"}, +] +pdfminer = [ + {file = "pdfminer-20191125.tar.gz", hash = "sha256:9e700bc731300ed5c8936343c1dd4529638184198e54e91dd2b59b64a755dc01"}, ] pexpect = [ {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, ] phonenumbers = [ - {file = "phonenumbers-8.12.15-py2.py3-none-any.whl", hash = "sha256:13d499f7114c4b37c54ee844b188d5e7441951a7da41de5fc1a25ff8fdceef80"}, - {file = "phonenumbers-8.12.15.tar.gz", hash = "sha256:b734bfcf33e87ddae72196a40b3d1af35abd0beb263816ae18e1bff612926406"}, + {file = "phonenumbers-8.12.26-py2.py3-none-any.whl", hash = "sha256:37299bddabd81ce34fee97e1c95da49f2742b8240710d88649cfcfde3dc73ff2"}, + {file = "phonenumbers-8.12.26.tar.gz", hash = "sha256:65bab6ebdbe0ec5196c74626949748db5337d23895aabc1ef8f5d7284787998a"}, ] pillow = [ {file = "Pillow-6.1.0-cp27-cp27m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:76638865c83b1bb33bcac2a61ce4d13c17dba2204969dedb9ab60ef62bede686"}, @@ -3517,13 +3832,16 @@ plotly = [ {file = "plotly-4.1.0-py2.py3-none-any.whl", hash = "sha256:c5effc51364490b1a20fe7bb8443cfe2451004fca727c7f3fedbc30f4cef5aab"}, {file = "plotly-4.1.0.tar.gz", hash = "sha256:9333f28feacaa0751df04cccfb266463bf5029dcb106d3f81d7af5942fd3d998"}, ] -premailer = [ - {file = "premailer-3.7.0-py2.py3-none-any.whl", hash = "sha256:c7ac48986984a810afea5147bc8410a8fe0659bf52f357e78b28a1b949209b91"}, - {file = "premailer-3.7.0.tar.gz", hash = "sha256:5eec9603e84cee583a390de69c75192e50d76e38ef0292b027bd64923766aca7"}, +ply = [ + {file = "ply-3.4.tar.gz", hash = "sha256:af435f11b7bdd69da5ffbc3fecb8d70a7073ec952e101764c88720cdefb2546b"}, ] -property-manager = [ - {file = "property-manager-3.0.tar.gz", hash = "sha256:93e76da9ae7af13cccc2cd1e3b47219950c56c125cd962aaed693894af267c54"}, - {file = "property_manager-3.0-py2.py3-none-any.whl", hash = "sha256:d5d648053e669cf9cb2f157e4a41ef46174eaa8ee13bfa49e1530c17d65bfe45"}, +polib = [ + {file = "polib-1.1.0-py2.py3-none-any.whl", hash = "sha256:93b730477c16380c9a96726c54016822ff81acfa553977fdd131f2b90ba858d7"}, + {file = "polib-1.1.0.tar.gz", hash = "sha256:fad87d13696127ffb27ea0882d6182f1a9cf8a5e2b37a587751166c51e5a332a"}, +] +premailer = [ + {file = "premailer-3.9.0-py2.py3-none-any.whl", hash = "sha256:d674826981be58d2eaa51db25f31bfe02f2859760aa1ff9879de9110f48e6476"}, + {file = "premailer-3.9.0.tar.gz", hash = "sha256:da18b9e8cb908893b67ab9b7451276fef7c0ab179f40189378545f6bb0ab3695"}, ] psutil = [ {file = "psutil-4.3.1-cp27-none-win32.whl", hash = "sha256:b0c5bf0d2a29a6f18ac22e2d24210730dca458c9f961914289c9e027ccb5ae43"}, @@ -3594,6 +3912,10 @@ py-asterisk = [ {file = "py3o.template-0.10.0-py3-none-any.whl", hash = "sha256:b83c43d1ff89ec9688e0c9c5297b0afdb0ecb123c6b2311e4d173aa36920b4e2"}, {file = "py3o.template-0.10.0.tar.gz", hash = "sha256:ea544e76ad519172fc49a621e999a1438fc8aa8869762db913fbc336518606d3"}, ] +pycodestyle = [ + {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, + {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, +] pycountry = [ {file = "pycountry-20.7.3.tar.gz", hash = "sha256:81084a53d3454344c0292deebc20fcd0a1488c136d4900312cbd465cf552cb42"}, ] @@ -3601,9 +3923,45 @@ pycparser = [ {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, ] +pycryptodome = [ + {file = "pycryptodome-3.10.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1c5e1ca507de2ad93474be5cfe2bfa76b7cf039a1a32fc196f40935944871a06"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:6260e24d41149268122dd39d4ebd5941e9d107f49463f7e071fd397e29923b0c"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:3f840c49d38986f6e17dbc0673d37947c88bc9d2d9dba1c01b979b36f8447db1"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:2dea65df54349cdfa43d6b2e8edb83f5f8d6861e5cf7b1fbc3e34c5694c85e27"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:e61e363d9a5d7916f3a4ce984a929514c0df3daf3b1b2eb5e6edbb131ee771cf"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:2603c98ae04aac675fefcf71a6c87dc4bb74a75e9071ae3923bbc91a59f08d35"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-win32.whl", hash = "sha256:38661348ecb71476037f1e1f553159b80d256c00f6c0b00502acac891f7116d9"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-win_amd64.whl", hash = "sha256:1723ebee5561628ce96748501cdaa7afaa67329d753933296321f0be55358dce"}, + {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:77997519d8eb8a4adcd9a47b9cec18f9b323e296986528186c0e9a7a15d6a07e"}, + {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:99b2f3fc51d308286071d0953f92055504a6ffe829a832a9fc7a04318a7683dd"}, + {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:e0a4d5933a88a2c98bbe19c0c722f5483dc628d7a38338ac2cb64a7dbd34064b"}, + {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d3d6958d53ad307df5e8469cc44474a75393a434addf20ecd451f38a72fe29b8"}, + {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:a8eb8b6ea09ec1c2535bf39914377bc8abcab2c7d30fa9225eb4fe412024e427"}, + {file = "pycryptodome-3.10.1-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:31c1df17b3dc5f39600a4057d7db53ac372f492c955b9b75dd439f5d8b460129"}, + {file = "pycryptodome-3.10.1-cp35-abi3-manylinux1_i686.whl", hash = "sha256:a3105a0eb63eacf98c2ecb0eb4aa03f77f40fbac2bdde22020bb8a536b226bb8"}, + {file = "pycryptodome-3.10.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:a92d5c414e8ee1249e850789052608f582416e82422502dc0ac8c577808a9067"}, + {file = "pycryptodome-3.10.1-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:60386d1d4cfaad299803b45a5bc2089696eaf6cdd56f9fc17479a6f89595cfc8"}, + {file = "pycryptodome-3.10.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:501ab36aae360e31d0ec370cf5ce8ace6cb4112060d099b993bc02b36ac83fb6"}, + {file = "pycryptodome-3.10.1-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:fc7489a50323a0df02378bc2fff86eb69d94cc5639914346c736be981c6a02e7"}, + {file = "pycryptodome-3.10.1-cp35-abi3-win32.whl", hash = "sha256:9b6f711b25e01931f1c61ce0115245a23cdc8b80bf8539ac0363bdcf27d649b6"}, + {file = "pycryptodome-3.10.1-cp35-abi3-win_amd64.whl", hash = "sha256:7fd519b89585abf57bf47d90166903ec7b43af4fe23c92273ea09e6336af5c07"}, + {file = "pycryptodome-3.10.1-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:09c1555a3fa450e7eaca41ea11cd00afe7c91fef52353488e65663777d8524e0"}, + {file = "pycryptodome-3.10.1-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:758949ca62690b1540dfb24ad773c6da9cd0e425189e83e39c038bbd52b8e438"}, + {file = "pycryptodome-3.10.1-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:e3bf558c6aeb49afa9f0c06cee7fb5947ee5a1ff3bd794b653d39926b49077fa"}, + {file = "pycryptodome-3.10.1-pp27-pypy_73-win32.whl", hash = "sha256:f977cdf725b20f6b8229b0c87acb98c7717e742ef9f46b113985303ae12a99da"}, + {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6d2df5223b12437e644ce0a3be7809471ffa71de44ccd28b02180401982594a6"}, + {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:98213ac2b18dc1969a47bc65a79a8fca02a414249d0c8635abb081c7f38c91b6"}, + {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:12222a5edc9ca4a29de15fbd5339099c4c26c56e13c2ceddf0b920794f26165d"}, + {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-win32.whl", hash = "sha256:6bbf7fee7b7948b29d7e71fcacf48bac0c57fb41332007061a933f2d996f9713"}, + {file = "pycryptodome-3.10.1.tar.gz", hash = "sha256:3e2e3a06580c5f190df843cdb90ea28d61099cf4924334d5297a995de68e4673"}, +] pydot = [ {file = "pydot-1.2.3.tar.gz", hash = "sha256:edb5d3f249f97fbd9c4bb16959e61bc32ecf40eee1a9f6d27abe8d01c0a73502"}, ] +pyflakes = [ + {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, + {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, +] pygments = [ {file = "Pygments-2.6.1-py3-none-any.whl", hash = "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"}, {file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"}, @@ -3619,6 +3977,10 @@ pygount = [ {file = "pyjon.utils-0.7.tar.gz", hash = "sha256:c2759ea361e00ef272ab65eb7af353398361d122f956efbf6a0241018f702d89"}, {file = "pyjon.utils-0.7.zip", hash = "sha256:d360fb393c2cf87ded962480895c83bc8c4f05f5b2ce7788bc5f16a67452bc2d"}, ] +pyjsparser = [ + {file = "pyjsparser-2.7.1-py2-none-any.whl", hash = "sha256:2b12842df98d83f65934e0772fa4a5d8b123b3bc79f1af1789172ac70265dd21"}, + {file = "pyjsparser-2.7.1.tar.gz", hash = "sha256:be60da6b778cc5a5296a69d8e7d614f1f870faf94e1b1b6ac591f2ad5d729579"}, +] pyjwt = [ {file = "PyJWT-1.7.1-py2.py3-none-any.whl", hash = "sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e"}, {file = "PyJWT-1.7.1.tar.gz", hash = "sha256:8d59a976fb773f3e6a39c85636357c4f0e242707394cadadd9814f5cbaa20e96"}, @@ -3626,36 +3988,58 @@ pyjwt = [ pyldap = [ {file = "pyldap-2.4.28.tar.gz", hash = "sha256:d38f31018f0c15925f50aec39f7255c15463f98797af53931e0e2a9ac21f6661"}, ] +pylint = [ + {file = "pylint-2.6.0-py3-none-any.whl", hash = "sha256:bfe68f020f8a0fece830a22dd4d5dddb4ecc6137db04face4c3420a46a52239f"}, + {file = "pylint-2.6.0.tar.gz", hash = "sha256:bb4a908c9dadbc3aac18860550e870f58e1a02c9f2c204fdf5693d73be061210"}, +] +pylint-odoo = [] +pylint-plugin-utils = [ + {file = "pylint-plugin-utils-0.6.tar.gz", hash = "sha256:57625dcca20140f43731311cd8fd879318bf45a8b0fd17020717a8781714a25a"}, + {file = "pylint_plugin_utils-0.6-py3-none-any.whl", hash = "sha256:2f30510e1c46edf268d3a195b2849bd98a1b9433229bb2ba63b8d776e1fc4d0a"}, +] pymssql = [ - {file = "pymssql-2.1.5-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:cff8e775fb6294effeb716735bfd7707e79a2a79b617d0f1984bd574f26bda65"}, - {file = "pymssql-2.1.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:c47c093cc4dc60e3356458c8e2935bab3834cea1f94a66c8ca62a5af2f060d64"}, - {file = "pymssql-2.1.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:658b4ea09050c85c6be09e1371335198b9441d2b5b08ef4f0b250ee4e5e8afc3"}, - {file = "pymssql-2.1.5-cp27-cp27m-win32.whl", hash = "sha256:f36392e1874445d7cb67b928686ad424b0b3980282512b21f640828ad3adf968"}, - {file = "pymssql-2.1.5-cp27-cp27m-win_amd64.whl", hash = "sha256:4fd4991eee848a4fd7d0b19a24fe49b508633881e221004652ab15a7e4cfe041"}, - {file = "pymssql-2.1.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cfd9ae0484056e46b981b7c3893ddb620ccd52f48349bada78cb141192dfbfbe"}, - {file = "pymssql-2.1.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:36539e42e8bb33018a05f9bd524b5a76286132ab7c82bfe9b60c4169d460fdf5"}, - {file = "pymssql-2.1.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:18b6550a02b34e88134b4b70eedcc6982036e459b0c91c7dd248bb1926287264"}, - {file = "pymssql-2.1.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c7a715c0b2b3a37462a9cf7972ed9ef0be98b2c64aebd549359f08af7f53b9a9"}, - {file = "pymssql-2.1.5-cp35-cp35m-win32.whl", hash = "sha256:e4741c6ec0483dcadb8a63077a7ceb17f263d9815ea842fed6663508c8852d7f"}, - {file = "pymssql-2.1.5-cp35-cp35m-win_amd64.whl", hash = "sha256:3e077455a11fcb4cb8705cb3ae83236b8e130df9fd4186c707d638e8e43f9646"}, - {file = "pymssql-2.1.5-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:0ff55a944ee7506a5e9aef7b40f0cddabc0b61f9ba13d716bff5a308923b8111"}, - {file = "pymssql-2.1.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3bdbeca64af7856923b7f84ed3355e2fd00bb1b897877b0bd4a74ec638801d52"}, - {file = "pymssql-2.1.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:dfc764a5a07197d742da34a593578295e9f8b64bb035c07e0981961672e18c85"}, - {file = "pymssql-2.1.5-cp36-cp36m-win32.whl", hash = "sha256:4f6d4434c29b846f491f5236daf06175f1652953d1d162be0f1b2b037bcf9a8d"}, - {file = "pymssql-2.1.5-cp36-cp36m-win_amd64.whl", hash = "sha256:aad5a1218691f83a16bab6dcfa24abf9da796abf5bf168a41972fe1cf93b3e37"}, - {file = "pymssql-2.1.5-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:1682ead549dcec31f3b8cc47da429572ea1c4b106cb4fa91df884f968123af93"}, - {file = "pymssql-2.1.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ec28c73afde96def469c581208903cf035923dc6313b6916f80cbcc71f9413d1"}, - {file = "pymssql-2.1.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fcf98e2c7cf18fa2fa09cdb7220849cd02e7b9481cb81ccdd8940da438f58d85"}, - {file = "pymssql-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:1e8d8abab391559b70f5df97fb22fc1d9ea627edcb943e558bdc7d7f455f93e2"}, - {file = "pymssql-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:e19a59eb8115418c3debcc9b685b2138d0abe6c9cb8c00bc2e738eb744bc6bda"}, - {file = "pymssql-2.1.5-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:2108114e4cc34ebbb8031df3e5579320e7569d51cd5094c5ddc333bf749d09a0"}, - {file = "pymssql-2.1.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:3977b056c5db8d01e74d88417cbb48e3e8bf03ab09ca6ef53790d025eae543df"}, - {file = "pymssql-2.1.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:d0f8094330523b8e4763a6903151bc35069309ccb57c61f87eeaa910a34f5a35"}, - {file = "pymssql-2.1.5-cp38-cp38-win32.whl", hash = "sha256:10f9b5b033eb30a38f4b36144eb4583fd478fd30afa9d64cd9a1965d22740446"}, - {file = "pymssql-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:557719b3ebc4617543de52eaadcdb6779f0c850e95b07be5f9775a2ef6a6c61f"}, - {file = "pymssql-2.1.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:04aab92d5a1a5d4e01a0797a939f103f02c0ef777bc8dcf1e952ed30dd1d43d4"}, - {file = "pymssql-2.1.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:70a5c67759254e982368c5b9ccfe076447a7fd545b8376eb62d60c3b85e3b94d"}, - {file = "pymssql-2.1.5.tar.gz", hash = "sha256:d60f5f90337399668e10ab6a23a1657f190c9585401eb96a5456261f7c414864"}, + {file = "pymssql-2.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:6fc382f253365f22286372a32cfeb5d380ad955045ae6f88cf372f758635557a"}, + {file = "pymssql-2.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1032cc2bafb9edcb309ac526c359387b5d1e4c06c62a92872f8c28681883fe25"}, + {file = "pymssql-2.2.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:ecb95a2301375b4a48ba73fcfd664981d511904e552cfd2c17530ec03a5f502d"}, + {file = "pymssql-2.2.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:0dafb4b2cf4a1deace503656b917cc5f4dad7d600f7cfc5e16f10918727e369f"}, + {file = "pymssql-2.2.1-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:f293d4cb9c03e6d859c8f3afb6fa7a99b30fe8f42eafedfa21358b1d8a420165"}, + {file = "pymssql-2.2.1-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9cbad4cd57ef67a32e55ead549da73ad5730ae5da0ace2a2206451897716ccfa"}, + {file = "pymssql-2.2.1-cp36-cp36m-manylinux_2_24_i686.whl", hash = "sha256:8ec909a71b239243d55fe59cbf8e25c8ef5dce3e3bac58c3aa0eaddaeffcd790"}, + {file = "pymssql-2.2.1-cp36-cp36m-manylinux_2_24_x86_64.whl", hash = "sha256:5a23654683698a26a8d45b239e19820ab9e4239aa52293e81a9ba9ddddf3006d"}, + {file = "pymssql-2.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:8bc05f632641e51d5782761622eee2538444fdbb39c9c0750ad76d8f650e6898"}, + {file = "pymssql-2.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:e8deccee711d1cdc8b6010622ced9d064bc943e5a0540616348768d4adc6e95e"}, + {file = "pymssql-2.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:379fcd412cf34a493b49cefd689f5f9f0878af57ce130dc47bd5dead8bb67f80"}, + {file = "pymssql-2.2.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:a579ae07dc7528385bf339261308e396bd483e68e770f84feb31032e18c72d2b"}, + {file = "pymssql-2.2.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d4ff41cc76c3851e0410feaf96f7760c2eeb9fb84597baf47d7431124e66d6f8"}, + {file = "pymssql-2.2.1-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:f3201b11dafd073183b6237667d2224a3a1f1d1ef41c4f351b48a0779127ac40"}, + {file = "pymssql-2.2.1-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:7301d2da53b436e07f23f847172d9da8c1f506390b0ad4d32ad73527b67728ce"}, + {file = "pymssql-2.2.1-cp37-cp37m-manylinux_2_24_i686.whl", hash = "sha256:39c61b8104fbd4d9b1e74c7f2c90c3871d0f1ef82e1be177d544b207e44f1b4c"}, + {file = "pymssql-2.2.1-cp37-cp37m-manylinux_2_24_x86_64.whl", hash = "sha256:40d355603dd31258dd6de28351f504fcc4d70b715637c01ffb384fd9ba0189df"}, + {file = "pymssql-2.2.1-cp37-cp37m-win32.whl", hash = "sha256:3638ca82915d663acc7f898290bcc3e94a236ba56530b6959f821334977bf56e"}, + {file = "pymssql-2.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:42cbfc756c9b29f3b974a3352c744c90c280c905f7cadda2dfcb6e46ca96b3f6"}, + {file = "pymssql-2.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8319bbbcaa8b3ca89f897e808ca5b48d9c64d3231408b1f0e1ce4400bcd7d6ae"}, + {file = "pymssql-2.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:dbfc4b97e6e6ce776461570d79c738116c20e2c2c01ed5f3afe03ec6740dacce"}, + {file = "pymssql-2.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:c841921a1729035f8fe056c76b1202985f203b4ff589904d26cb33885271cdad"}, + {file = "pymssql-2.2.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:2b865ba3644e08d2ce10e2b06256dadb64677e1c81651224c4b1cabea2be2fd8"}, + {file = "pymssql-2.2.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4465d1776d3cf36d17b3058d155ec8a581b1bc9ecf29740ea07254ea72248b73"}, + {file = "pymssql-2.2.1-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:0b4c2b4b3b13f866a037c905220c6b3691c20d19a18ff6f6457351f15620db46"}, + {file = "pymssql-2.2.1-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:cead77d3ac65c1e2910c3c082760148fd275815d5df4b13549e485fc305f4768"}, + {file = "pymssql-2.2.1-cp38-cp38-manylinux_2_24_i686.whl", hash = "sha256:c956c93f57d69c4551b442dec2b6eed1e4a0e185ae913ec52305e4b66d4903b7"}, + {file = "pymssql-2.2.1-cp38-cp38-manylinux_2_24_x86_64.whl", hash = "sha256:fd2c537c798d2e0166cdebd2dd29f1a676fc508d664cf1fefe69ae51c3004a82"}, + {file = "pymssql-2.2.1-cp38-cp38-win32.whl", hash = "sha256:a82ba1f66f6537a42c54704d0da171a1541a3d88225d6e9ba6a17712968b0980"}, + {file = "pymssql-2.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:a9adf3ca9f143dace3d4d52bc056e8a70e5f956c4555a9e204ddf4937d98ba66"}, + {file = "pymssql-2.2.1-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:724432260900d1a4dff51da52f276741e143756712fbf285d8b228b2340403a9"}, + {file = "pymssql-2.2.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:0acfb137c385b14a7b27c6e9c6c2a047e5575c45b85239503086895d8d71819e"}, + {file = "pymssql-2.2.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:a67b45a91203a35251d427cd35a316e878a27d275f20a6ae815a0db6bb0769de"}, + {file = "pymssql-2.2.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:844883d3543d8da5037ef8a925a75d0c1de3972d2f9754f063c87c7d7f580b68"}, + {file = "pymssql-2.2.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:d28cec093e53f7a7e6242683f46418a2c0348eb6d22c5008d442028b6207e204"}, + {file = "pymssql-2.2.1-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:fe53b55ef9572ac3d5f34f2acd3adf1ec9ee7d1f637bc59bdd58dc06c4ef4ed7"}, + {file = "pymssql-2.2.1-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:6ef017731bd6e1ffaed4637e767c1983750f8b63de6f8e6ed1e700578a73b4a3"}, + {file = "pymssql-2.2.1-cp39-cp39-manylinux_2_24_i686.whl", hash = "sha256:c788534ff389d31742f71a8fae2922b6a498bcde8e3c6c650f15495f33e50e44"}, + {file = "pymssql-2.2.1-cp39-cp39-manylinux_2_24_x86_64.whl", hash = "sha256:6b845d1c940521d3ec344906208fbc001bef4913e780ae799ad06f4d9e197f8b"}, + {file = "pymssql-2.2.1-cp39-cp39-win32.whl", hash = "sha256:13d0cce2716d34e8486cdd83602f72e13a1261de1f50a98ddf161d207e0f3ec0"}, + {file = "pymssql-2.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:637432b07f9b6e5f0b1d77cec8b32b6d5ee182033c6c7b47ef2d5a3d9233ee4c"}, + {file = "pymssql-2.2.1.tar.gz", hash = "sha256:7238530b45955f793d235664cc6f104d8b8fbcdeb6fa394dd22d8fe43748bab0"}, ] pymysql = [ {file = "PyMySQL-1.0.2-py3-none-any.whl", hash = "sha256:41fc3a0c5013d5f039639442321185532e3e2c8924687abe6537de157d403641"}, @@ -3682,8 +4066,8 @@ pynacl = [ {file = "PyNaCl-1.4.0.tar.gz", hash = "sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505"}, ] pyotp = [ - {file = "pyotp-2.4.1-py2.py3-none-any.whl", hash = "sha256:ef07c393660529261e66902e788b32e46260d2c29eb740978df778260a1c2b4c"}, - {file = "pyotp-2.4.1.tar.gz", hash = "sha256:038a3f70b34eaad3f72459e8b411662ef8dfcdd95f7d9203fa489e987a75584b"}, + {file = "pyotp-2.6.0-py2.py3-none-any.whl", hash = "sha256:9d144de0f8a601d6869abe1409f4a3f75f097c37b50a36a3bf165810a6e23f28"}, + {file = "pyotp-2.6.0.tar.gz", hash = "sha256:d28ddfd40e0c1b6a6b9da961c7d47a10261fb58f378cb00f05ce88b26df9c432"}, ] pyparsing = [ {file = "pyparsing-2.1.10-py2.py3-none-any.whl", hash = "sha256:67101d7acee692962f33dd30b5dce079ff532dd9aa99ff48d52a3dad51d2fe84"}, @@ -3702,40 +4086,25 @@ pypdf4 = [ {file = "PyPDF4-1.27.0.tar.gz", hash = "sha256:7c932441146d205572f96254d53c79ea2c30c9e11df55a5cf87e056c7b3d7f89"}, ] pyproj = [ - {file = "pyproj-3.0.0.post1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c5c2f3a42d7ec4315207756fa22d94c2f1d44974a897d95fd0ae50c6275b10f"}, - {file = "pyproj-3.0.0.post1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:4098c6245af35bc08c94d0569e3f4072520735c8fb6d484da26ac12de90d1461"}, - {file = "pyproj-3.0.0.post1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:ff0ab697e710772ace753b6d72f017e013dd9d5aa0878406cc25d02c75c12963"}, - {file = "pyproj-3.0.0.post1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:b845c6fbeae886ae4f06460ee35f9ae8526e5dc92c5db71bc575f0f836e2c11c"}, - {file = "pyproj-3.0.0.post1-cp36-cp36m-win32.whl", hash = "sha256:0e9c7a56ceb3a65d23a99b839ee846817d367c2f3c066b7687e78d9642331b3a"}, - {file = "pyproj-3.0.0.post1-cp36-cp36m-win_amd64.whl", hash = "sha256:57f35a708e10e5f24fec6f02643559bdcfcbeb757d5d1dc03049b818525002d3"}, - {file = "pyproj-3.0.0.post1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cc0bac8f6fb46441f8b472cdad6c4809898c7f90f4f043420eee27e44f49b4e9"}, - {file = "pyproj-3.0.0.post1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:1b09aa6a75c66fc7eaa0b48fadaaa5f831f4621b2c8d4c692cf1f21597200467"}, - {file = "pyproj-3.0.0.post1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6d76793b507341b29ca8d17da76c3f047e936f65728a7a5a549fb8e55b42a21b"}, - {file = "pyproj-3.0.0.post1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:f9bfbadd28d258801853b2ea309f2d37c289a476a7eb08b4212f83627da52d60"}, - {file = "pyproj-3.0.0.post1-cp37-cp37m-win32.whl", hash = "sha256:657df773781d80d122e7d040464c17a004cdd7338739b8b24321ae3dbd872427"}, - {file = "pyproj-3.0.0.post1-cp37-cp37m-win_amd64.whl", hash = "sha256:73d495eca7ba83d9ea32b08cf86f65cbc2292cd2d889a2229bac4a56a32f2d5b"}, - {file = "pyproj-3.0.0.post1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a917e1d22e7a195edb06a7345c65e608b401fceead8dc96c49a00d06ff2f5e12"}, - {file = "pyproj-3.0.0.post1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1974506d59f3cf57bdb05aabace43ed8dcbb9822373bfc7b4e85b9458ea22007"}, - {file = "pyproj-3.0.0.post1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:c3388bd9b23a5dab4d81331a3e9cde409c9f2bf2ce3f2c125a797000215c0062"}, - {file = "pyproj-3.0.0.post1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8d3e61c59e624e76d9f26909ff6b50b04d0328ff7431ff7ec123d8b762d51998"}, - {file = "pyproj-3.0.0.post1-cp38-cp38-win32.whl", hash = "sha256:cdd6afc4a96aa69c605cd26d139714f3d0d51f2e50de308a4cfa04d8df9791d4"}, - {file = "pyproj-3.0.0.post1-cp38-cp38-win_amd64.whl", hash = "sha256:5f99e5f9303fcd30ee958a2dc5c50ead33523fc2e3d1b789f244047f7e348d8e"}, - {file = "pyproj-3.0.0.post1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4b49aad9ef82cb9d1768af3381a6691e7d011cacfea7914cab15a029f3d48197"}, - {file = "pyproj-3.0.0.post1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2efc593fb610654ff002c6e48dde11a430d997c2c7f2e5a26793e93b58a2f137"}, - {file = "pyproj-3.0.0.post1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:608a09a4b739742ebe10a2c6f3d33f1e22a26d7763c1005ebd63cb00169b5112"}, - {file = "pyproj-3.0.0.post1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:1c4002dc1d06f750da80a63528195e0f4c18b466470ee8b3911bfadec0689036"}, - {file = "pyproj-3.0.0.post1-cp39-cp39-win32.whl", hash = "sha256:ab1b3af7ba2d06e45486d80967ad83028a252b15566cc326ae40ed067e0167cc"}, - {file = "pyproj-3.0.0.post1-cp39-cp39-win_amd64.whl", hash = "sha256:839c7c4478073c1e5109674d61edf0450e61e4317aabcd4f12e349be14eeeff9"}, - {file = "pyproj-3.0.0.post1-pp37-pypy37_pp73-win32.whl", hash = "sha256:8ba718f552e69073105021fd7b5ceb2189d4376e66d519c3b4cf0f1aed6a54d3"}, - {file = "pyproj-3.0.0.post1.tar.gz", hash = "sha256:a49581629cadd29e61fc061d153a4d62ff28b4063c71fe8ca881eeb98cd22017"}, -] -pyreadline = [ - {file = "pyreadline-2.1.win-amd64.exe", hash = "sha256:9ce5fa65b8992dfa373bddc5b6e0864ead8f291c94fbfec05fbd5c836162e67b"}, - {file = "pyreadline-2.1.win32.exe", hash = "sha256:65540c21bfe14405a3a77e4c085ecfce88724743a4ead47c66b84defcf82c32e"}, - {file = "pyreadline-2.1.zip", hash = "sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1"}, -] -pyrfc = [ - {file = "pyrfc-0.1.2.tar.gz", hash = "sha256:101ab6ac5c99025caf8d2f1e728dac1650d9b8a8c584120c670bc9ff32977578"}, + {file = "pyproj-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8eda240225971b5cd0bac2d399ed6222068f0598ee92d5f6e847bd2019d2c8b0"}, + {file = "pyproj-3.1.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:ae237492767e0225f99b53a0fd7110fde2b7e7cabc105bbc243c151a7497de88"}, + {file = "pyproj-3.1.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b635e7e21fea5af74e90fc9e54d1a4c27078efdce6f214101c98dd93afae599a"}, + {file = "pyproj-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa87df0982aa0f4477478899d9c930cc0f97cd6d8a4ce84c43ac88ccf86d1da7"}, + {file = "pyproj-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:10dad599b9f7ce2194996dc25f1000e0aa15754ecef9db46b624713959c67957"}, + {file = "pyproj-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:a162ed199cd2ec392cffe20b2fa3381b68e7a166d55f3f060eceb8d517e4f46d"}, + {file = "pyproj-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1e88ebc4e08e661e9011b5c1ebfb32f0d311963a9824a6effb4168c7e07918b1"}, + {file = "pyproj-3.1.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:da88abc5e2f6a8fb07533855a57ca2a31845f58901a87f821b68b0db6b023978"}, + {file = "pyproj-3.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:911d773da9fa4d4f3f7580173858c391e3ee0b61acaf0be303baab323d2eae78"}, + {file = "pyproj-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f8a8d982bde211e65dc2de1f8f36cf162f9cc7fcd8a7625046ea265284e5e65"}, + {file = "pyproj-3.1.0-cp38-cp38-win32.whl", hash = "sha256:c4193e1069d165476b2d0f7d882b7712b3eab6e2e6fe2a0a78ef40de825a1f28"}, + {file = "pyproj-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:b6c74bbec679199746a3e02c0e0fad093c3652df96dd63e086a2fbf2afe9dc0e"}, + {file = "pyproj-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:04c185102e659439c5bd428ac5473d36ef795fca8e225bbbe78e20643d804ec0"}, + {file = "pyproj-3.1.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ebbba7707fe83a01e54bce8e3e7342feb0b3e0d74ff8c28df12f8bc59b76827c"}, + {file = "pyproj-3.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9cc464a1c51baad28ffb7a233116e8d4ce4c560b32039fa986d0f992ac3c431f"}, + {file = "pyproj-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f3ad09cf3352bf5664794042b28d98781362ec8d9774ad73f28a1a0101a27f1"}, + {file = "pyproj-3.1.0-cp39-cp39-win32.whl", hash = "sha256:ae5534fa7a3b74f20534694d297fce6f7483890ff6ca404394ecf372f3c589d4"}, + {file = "pyproj-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:808f5992320e9631b2e45444028a65cd6ba3ee40229292934178ef07020a5ffd"}, + {file = "pyproj-3.1.0.tar.gz", hash = "sha256:67b94f4e694ae33fc90dfb7da0e6b5ed5f671dd0acc2f6cf46e9c39d56e16e1a"}, ] pyrsistent = [ {file = "pyrsistent-0.16.0.tar.gz", hash = "sha256:28669905fe725965daa16184933676547c5bb40a5153055a8dee2a4bd7933ad3"}, @@ -3773,11 +4142,12 @@ python-keystoneclient = [ {file = "python_keystoneclient-3.22.0-py2.py3-none-any.whl", hash = "sha256:b9f9e74219a44be7c9955c836ab9a7ae16bde7325db6db15b6e5fe140c11bc3a"}, ] python-slugify = [ - {file = "python-slugify-4.0.1.tar.gz", hash = "sha256:69a517766e00c1268e5bbfc0d010a0a8508de0b18d30ad5a1ff357f8ae724270"}, + {file = "python-slugify-5.0.2.tar.gz", hash = "sha256:f13383a0b9fcbe649a1892b9c8eb4f8eab1d6d84b84bb7a624317afa98159cab"}, + {file = "python_slugify-5.0.2-py2.py3-none-any.whl", hash = "sha256:6d8c5df75cd4a7c3a2d21e257633de53f52ab0265cd2d1dc62a730e8194a7380"}, ] python-stdnum = [ - {file = "python-stdnum-1.15.tar.gz", hash = "sha256:ff0e4d2b8731711cf2a73d22964139208088b4bd1a3e3b0c4f78d6e823dd8923"}, - {file = "python_stdnum-1.15-py2.py3-none-any.whl", hash = "sha256:46c05dff2d5ff89b12cc63ab54cca1e4788e4e3087c163a1b7434a53fa926f28"}, + {file = "python-stdnum-1.16.tar.gz", hash = "sha256:4248d898042a801fc4eff96fbfe4bf63a43324854efe3b5534718c1c195c6f43"}, + {file = "python_stdnum-1.16-py2.py3-none-any.whl", hash = "sha256:2e2c56c548ca166b95547a8d748f4d71320a5b4896960717c8e6380e08d993a5"}, ] python-swiftclient = [ {file = "python-swiftclient-3.9.0.tar.gz", hash = "sha256:4f2097492e4c76e948882fc859bfa033ade09bed72f8e6b328e34a3467d9a377"}, @@ -3871,8 +4241,8 @@ requests-file = [ {file = "requests_file-1.5.1-py2.py3-none-any.whl", hash = "sha256:dfe5dae75c12481f68ba353183c53a65e6044c923e64c24b2209f6c7570ca953"}, ] requests-mock = [ - {file = "requests-mock-1.8.0.tar.gz", hash = "sha256:e68f46844e4cee9d447150343c9ae875f99fa8037c6dcf5f15bf1fe9ab43d226"}, - {file = "requests_mock-1.8.0-py2.py3-none-any.whl", hash = "sha256:11215c6f4df72702aa357f205cf1e537cffd7392b3e787b58239bde5fb3db53b"}, + {file = "requests-mock-1.9.3.tar.gz", hash = "sha256:8d72abe54546c1fc9696fa1516672f1031d72a55a1d66c85184f972a24ba0eba"}, + {file = "requests_mock-1.9.3-py2.py3-none-any.whl", hash = "sha256:0a2d38a117c08bb78939ec163522976ad59a6b7fdd82b709e23bb98004a44970"}, ] requests-oauthlib = [ {file = "requests-oauthlib-1.1.0.tar.gz", hash = "sha256:eabd8eb700ebed81ba080c6ead96d39d6bdc39996094bd23000204f6965786b0"}, @@ -3883,6 +4253,9 @@ requests-toolbelt = [ {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, ] +restructuredtext-lint = [ + {file = "restructuredtext_lint-1.3.1.tar.gz", hash = "sha256:470e53b64817211a42805c3a104d2216f6f5834b22fe7adb637d1de4d6501fb8"}, +] retrying = [ {file = "retrying-1.3.3.tar.gz", hash = "sha256:08c039560a6da2fe4f2c426d0766e284d3b736e355f8dd24b37367b0bb41973b"}, ] @@ -3890,13 +4263,6 @@ rfc3986 = [ {file = "rfc3986-1.4.0-py2.py3-none-any.whl", hash = "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"}, {file = "rfc3986-1.4.0.tar.gz", hash = "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d"}, ] -rotate-backups = [ - {file = "rotate-backups-4.3.tar.gz", hash = "sha256:8824f97f3e928f46728749669b039d994e88cd7c583ebb7f83f4325b892ab794"}, - {file = "rotate_backups-4.3-py2.py3-none-any.whl", hash = "sha256:c71170fa2c5cb40214cb63cef37cc3b696582f6676918517eec97654ea858a20"}, -] -rotate-backups-s3 = [ - {file = "rotate-backups-s3-0.3.tar.gz", hash = "sha256:b5be4294a82d9b8619a60d17feca41142c617510fc13ea2e9ba2800c347d02ef"}, -] s3transfer = [ {file = "s3transfer-0.2.1-py2.py3-none-any.whl", hash = "sha256:b780f2411b824cb541dbcd2c713d0cb61c7d1bcadae204cdddda2b35cef493ba"}, {file = "s3transfer-0.2.1.tar.gz", hash = "sha256:6efc926738a3cd576c2a79725fed9afde92378aa5c6a957e3af010cb019fac9d"}, @@ -3926,9 +4292,6 @@ shapely = [ {file = "Shapely-1.6.4.post2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3ef28e3f20a1c37f5b99ea8cf8dcb58e2f1a8762d65ed2d21fd92bf1d4811182"}, {file = "Shapely-1.6.4.post2.tar.gz", hash = "sha256:c4b87bb61fc3de59fc1f85e71a79b0c709dc68364d9584473697aad4aa13240f"}, ] -simpleeval = [ - {file = "simpleeval-0.9.10.tar.gz", hash = "sha256:692055488c2864637f6c2edb5fa48175978a2a07318009e7cf03c9790ca17bea"}, -] simplejson = [ {file = "simplejson-3.17.2-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:2d3eab2c3fe52007d703a26f71cf649a8c771fcdd949a3ae73041ba6797cfcf8"}, {file = "simplejson-3.17.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:813846738277729d7db71b82176204abc7fdae2f566e2d9fcf874f9b6472e3e6"}, @@ -3980,6 +4343,9 @@ six = [ {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, ] +slimit = [ + {file = "slimit-0.8.1.zip", hash = "sha256:f433dcef899f166b207b67d91d3f7344659cb33b8259818f084167244e17720b"}, +] slugify = [ {file = "slugify-0.0.1.tar.gz", hash = "sha256:c5703cc11c1a6947536f3ce8bb306766b8bb5a84a53717f5a703ce0f18235e4c"}, ] @@ -3999,8 +4365,8 @@ soupsieve = [ {file = "soupsieve-1.9.6.tar.gz", hash = "sha256:7985bacc98c34923a439967c1a602dc4f1e15f923b6fcf02344184f86cc7efaa"}, ] sphinx = [ - {file = "Sphinx-3.4.3-py3-none-any.whl", hash = "sha256:c314c857e7cd47c856d2c5adff514ac2e6495f8b8e0f886a8a37e9305dfea0d8"}, - {file = "Sphinx-3.4.3.tar.gz", hash = "sha256:41cad293f954f7d37f803d97eb184158cfd90f51195131e94875bc07cd08b93c"}, + {file = "Sphinx-4.0.2-py3-none-any.whl", hash = "sha256:d1cb10bee9c4231f1700ec2e24a91be3f3a3aba066ea4ca9f3bbe47e59d5a1d4"}, + {file = "Sphinx-4.0.2.tar.gz", hash = "sha256:b5c2ae4120bf00c799ba9b3699bc895816d272d120080fbc967292f29b52b48c"}, ] sphinx-intl = [ {file = "sphinx-intl-2.0.1.tar.gz", hash = "sha256:b25a6ec169347909e8d983eefe2d8adecb3edc2f27760db79b965c69950638b4"}, @@ -4010,8 +4376,8 @@ sphinx-patchqueue = [ {file = "sphinx-patchqueue-1.0.4.tar.gz", hash = "sha256:00362a5985c79eb038ab4a564f9944cfd097bd94350caab4fdebaabfb9eb3bd3"}, ] sphinx-rtd-theme = [ - {file = "sphinx_rtd_theme-0.5.1-py2.py3-none-any.whl", hash = "sha256:fa6bebd5ab9a73da8e102509a86f3fcc36dec04a0b52ea80e5a033b2aba00113"}, - {file = "sphinx_rtd_theme-0.5.1.tar.gz", hash = "sha256:eda689eda0c7301a80cf122dad28b1861e5605cbf455558f3775e1e8200e83a5"}, + {file = "sphinx_rtd_theme-0.5.2-py2.py3-none-any.whl", hash = "sha256:4a05bdbe8b1446d77a01e20a23ebc6777c74f43237035e76be89699308987d6f"}, + {file = "sphinx_rtd_theme-0.5.2.tar.gz", hash = "sha256:32bd3b5d13dc8186d7a42fc816a23d32e83a4827d7d9882948e7b837c232da5a"}, ] sphinxcontrib-applehelp = [ {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, @@ -4038,44 +4404,36 @@ sphinxcontrib-serializinghtml = [ {file = "sphinxcontrib_serializinghtml-1.1.4-py2.py3-none-any.whl", hash = "sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a"}, ] sqlalchemy = [ - {file = "SQLAlchemy-1.3.22-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:61628715931f4962e0cdb2a7c87ff39eea320d2aa96bd471a3c293d146f90394"}, - {file = "SQLAlchemy-1.3.22-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:81d8d099a49f83111cce55ec03cc87eef45eec0d90f9842b4fc674f860b857b0"}, - {file = "SQLAlchemy-1.3.22-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:d055ff750fcab69ca4e57b656d9c6ad33682e9b8d564f2fbe667ab95c63591b0"}, - {file = "SQLAlchemy-1.3.22-cp27-cp27m-win32.whl", hash = "sha256:9bf572e4f5aa23f88dd902f10bb103cb5979022a38eec684bfa6d61851173fec"}, - {file = "SQLAlchemy-1.3.22-cp27-cp27m-win_amd64.whl", hash = "sha256:7d4b8de6bb0bc736161cb0bbd95366b11b3eb24dd6b814a143d8375e75af9990"}, - {file = "SQLAlchemy-1.3.22-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:4a84c7c7658dd22a33dab2e2aa2d17c18cb004a42388246f2e87cb4085ef2811"}, - {file = "SQLAlchemy-1.3.22-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:f1e88b30da8163215eab643962ae9d9252e47b4ea53404f2c4f10f24e70ddc62"}, - {file = "SQLAlchemy-1.3.22-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:f115150cc4361dd46153302a640c7fa1804ac207f9cc356228248e351a8b4676"}, - {file = "SQLAlchemy-1.3.22-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6aaa13ee40c4552d5f3a59f543f0db6e31712cc4009ec7385407be4627259d41"}, - {file = "SQLAlchemy-1.3.22-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3ab5b44a07b8c562c6dcb7433c6a6c6e03266d19d64f87b3333eda34e3b9936b"}, - {file = "SQLAlchemy-1.3.22-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:426ece890153ccc52cc5151a1a0ed540a5a7825414139bb4c95a868d8da54a52"}, - {file = "SQLAlchemy-1.3.22-cp35-cp35m-win32.whl", hash = "sha256:bd4b1af45fd322dcd1fb2a9195b4f93f570d1a5902a842e3e6051385fac88f9c"}, - {file = "SQLAlchemy-1.3.22-cp35-cp35m-win_amd64.whl", hash = "sha256:62285607a5264d1f91590abd874d6a498e229d5840669bd7d9f654cfaa599bd0"}, - {file = "SQLAlchemy-1.3.22-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:314f5042c0b047438e19401d5f29757a511cfc2f0c40d28047ca0e4c95eabb5b"}, - {file = "SQLAlchemy-1.3.22-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:62fb881ba51dbacba9af9b779211cf9acff3442d4f2993142015b22b3cd1f92a"}, - {file = "SQLAlchemy-1.3.22-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:bde677047305fe76c7ee3e4492b545e0018918e44141cc154fe39e124e433991"}, - {file = "SQLAlchemy-1.3.22-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:0c6406a78a714a540d980a680b86654feadb81c8d0eecb59f3d6c554a4c69f19"}, - {file = "SQLAlchemy-1.3.22-cp36-cp36m-win32.whl", hash = "sha256:95bde07d19c146d608bccb9b16e144ec8f139bcfe7fd72331858698a71c9b4f5"}, - {file = "SQLAlchemy-1.3.22-cp36-cp36m-win_amd64.whl", hash = "sha256:888d5b4b5aeed0d3449de93ea80173653e939e916cc95fe8527079e50235c1d2"}, - {file = "SQLAlchemy-1.3.22-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:d53f59744b01f1440a1b0973ed2c3a7de204135c593299ee997828aad5191693"}, - {file = "SQLAlchemy-1.3.22-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:70121f0ae48b25ef3e56e477b88cd0b0af0e1f3a53b5554071aa6a93ef378a03"}, - {file = "SQLAlchemy-1.3.22-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:54da615e5b92c339e339fe8536cce99fe823b6ed505d4ea344852aefa1c205fb"}, - {file = "SQLAlchemy-1.3.22-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:68428818cf80c60dc04aa0f38da20ad39b28aba4d4d199f949e7d6e04444ea86"}, - {file = "SQLAlchemy-1.3.22-cp37-cp37m-win32.whl", hash = "sha256:17610d573e698bf395afbbff946544fbce7c5f4ee77b5bcb1f821b36345fae7a"}, - {file = "SQLAlchemy-1.3.22-cp37-cp37m-win_amd64.whl", hash = "sha256:216ba5b4299c95ed179b58f298bda885a476b16288ab7243e89f29f6aeced7e0"}, - {file = "SQLAlchemy-1.3.22-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:0c72b90988be749e04eff0342dcc98c18a14461eb4b2ad59d611b57b31120f90"}, - {file = "SQLAlchemy-1.3.22-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:491fe48adc07d13e020a8b07ef82eefc227003a046809c121bea81d3dbf1832d"}, - {file = "SQLAlchemy-1.3.22-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:f8191fef303025879e6c3548ecd8a95aafc0728c764ab72ec51a0bdf0c91a341"}, - {file = "SQLAlchemy-1.3.22-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:108580808803c7732f34798eb4a329d45b04c562ed83ee90f09f6a184a42b766"}, - {file = "SQLAlchemy-1.3.22-cp38-cp38-win32.whl", hash = "sha256:bab5a1e15b9466a25c96cda19139f3beb3e669794373b9ce28c4cf158c6e841d"}, - {file = "SQLAlchemy-1.3.22-cp38-cp38-win_amd64.whl", hash = "sha256:318b5b727e00662e5fc4b4cd2bf58a5116d7c1b4dd56ffaa7d68f43458a8d1ed"}, - {file = "SQLAlchemy-1.3.22-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:1418f5e71d6081aa1095a1d6b567a562d2761996710bdce9b6e6ba20a03d0864"}, - {file = "SQLAlchemy-1.3.22-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:5a7f224cdb7233182cec2a45d4c633951268d6a9bcedac37abbf79dd07012aea"}, - {file = "SQLAlchemy-1.3.22-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:715b34578cc740b743361f7c3e5f584b04b0f1344f45afc4e87fbac4802eb0a0"}, - {file = "SQLAlchemy-1.3.22-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:2ff132a379838b1abf83c065be54cef32b47c987aedd06b82fc76476c85225eb"}, - {file = "SQLAlchemy-1.3.22-cp39-cp39-win32.whl", hash = "sha256:c389d7cc2b821853fb018c85457da3e7941db64f4387720a329bc7ff06a27963"}, - {file = "SQLAlchemy-1.3.22-cp39-cp39-win_amd64.whl", hash = "sha256:04f995fcbf54e46cddeb4f75ce9dfc17075d6ae04ac23b2bacb44b3bc6f6bf11"}, - {file = "SQLAlchemy-1.3.22.tar.gz", hash = "sha256:758fc8c4d6c0336e617f9f6919f9daea3ab6bb9b07005eda9a1a682e24a6cacc"}, + {file = "SQLAlchemy-1.4.19-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:ddbce8fe4d0190db21db602e38aaf4c158c540b49f1ef7475323ec682a9fbf2d"}, + {file = "SQLAlchemy-1.4.19-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:942ca49b7ec7449d2473a6587825c55ad99534ddfc4eee249dd42be3cc1aa8c9"}, + {file = "SQLAlchemy-1.4.19-cp27-cp27m-win32.whl", hash = "sha256:9c0945c79cbe507b49524e31a4bb8700060bbccb60bb553df6432e176baff3d5"}, + {file = "SQLAlchemy-1.4.19-cp27-cp27m-win_amd64.whl", hash = "sha256:6fd1b745ade2020a1a7bf1e22536d8afe86287882c81ca5d860bdf231d5854e9"}, + {file = "SQLAlchemy-1.4.19-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0fb3f73e5009f5a4c9b24469939d3d57cc3ad8099a09c0cfefc47fe45ab7ffbe"}, + {file = "SQLAlchemy-1.4.19-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:64eab458619ef759f16f0f82242813d3289e829f8557fbc7c212ca4eadf96472"}, + {file = "SQLAlchemy-1.4.19-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:311051c06f905774427b4a92dcb3924d6ee563dea3a88176da02fdfc572d0d1d"}, + {file = "SQLAlchemy-1.4.19-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a34a7fd3353ee61a1dca72fc0c3e38d4e56bdc2c343e712f60a8c70acd4ef5bf"}, + {file = "SQLAlchemy-1.4.19-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ace9ab2af9d7d7b0e2ff2178809941c56ab8921e38128278192a73a8a1c08a2"}, + {file = "SQLAlchemy-1.4.19-cp36-cp36m-win32.whl", hash = "sha256:96d3d4a7ead376d738775a1fa9786dc17a31975ec664cea284e53735c79a5686"}, + {file = "SQLAlchemy-1.4.19-cp36-cp36m-win_amd64.whl", hash = "sha256:20f4bf1459548a74aade997cb045015e4d72f0fde1789b09b3bb380be28f6511"}, + {file = "SQLAlchemy-1.4.19-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:8cba69545246d16c6d2a12ce45865947cbdd814bacddf2e532fdd4512e70728c"}, + {file = "SQLAlchemy-1.4.19-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57ba8a96b6d058c7dcf44de8ac0955b7a787f7177a0221dd4b8016e0191268f5"}, + {file = "SQLAlchemy-1.4.19-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8f1e7f4de05c15d6b46af12f3cf0c2552f2940d201a49926703249a62402d851"}, + {file = "SQLAlchemy-1.4.19-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c92d9ebf4b38c22c0c9e4f203a80e101910a50dc555b4578816932015b97d7f"}, + {file = "SQLAlchemy-1.4.19-cp37-cp37m-win32.whl", hash = "sha256:c6efc7477551ba9ce632d5c3b448b7de0277c86005eec190a1068fcc7115fd0e"}, + {file = "SQLAlchemy-1.4.19-cp37-cp37m-win_amd64.whl", hash = "sha256:e2761b925fda550debfd5a8bc3cef9debc9a23c6a280429c4ec3a07c35c6b4b3"}, + {file = "SQLAlchemy-1.4.19-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:58d4f79d119010fdced6e7fd7e4b9f2230dbf55a8235d7c58b1c8207ef74791b"}, + {file = "SQLAlchemy-1.4.19-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cefd44faca7c57534503261f6fab49bd47eb9c2945ee0bab09faaa8cb047c24f"}, + {file = "SQLAlchemy-1.4.19-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9133635edcec1e7fbfc16eba5dc2b5b3b11818d25b7a57cfcbfa8d3b3e9594fd"}, + {file = "SQLAlchemy-1.4.19-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3cf5f543d048a7c8da500133068c5c90c97a2c4bf0c027928a85028a519f33d"}, + {file = "SQLAlchemy-1.4.19-cp38-cp38-win32.whl", hash = "sha256:d04160462f874eaa4d88721a0d5ecca8ebf433616801efe779f252ef87b0e216"}, + {file = "SQLAlchemy-1.4.19-cp38-cp38-win_amd64.whl", hash = "sha256:45b0f773e195d8d51e2fd67cb5b5fb32f5a1f5e7f0752016207091bed108909a"}, + {file = "SQLAlchemy-1.4.19-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:93ba458b3c279581288a10a55df2aa6ac3509882228fcbad9d9d88069f899337"}, + {file = "SQLAlchemy-1.4.19-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6317701c06a829b066c794545512bb70b1a10a74574cfa5658a0aaf49f31aa93"}, + {file = "SQLAlchemy-1.4.19-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:95a9fd0a11f89a80d8815418eccba034f3fec8ea1f04c41b6b8decc5c95852e9"}, + {file = "SQLAlchemy-1.4.19-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9014fd1d8aebcb4eb6bc69a382dd149200e1d5924412b1d08b4443f6c1ce526f"}, + {file = "SQLAlchemy-1.4.19-cp39-cp39-win32.whl", hash = "sha256:fa05a77662c23226c9ec031638fd90ae767009e05cd092b948740f09d10645f0"}, + {file = "SQLAlchemy-1.4.19-cp39-cp39-win_amd64.whl", hash = "sha256:d7b21a4b62921cf6dca97e8f9dea1fbe2432aebbb09895a2bd4f527105af41a4"}, + {file = "SQLAlchemy-1.4.19.tar.gz", hash = "sha256:89a5a13dcf33b7e47c7a9404a297c836965a247c7f076a0fe0910cae2bee5ce2"}, ] statsd = [ {file = "statsd-3.2.1-py2.py3-none-any.whl", hash = "sha256:7aff40c6cdda703193fac6231410c6c0ad5939ce053a168016501e982c755c84"}, @@ -4093,6 +4451,10 @@ text-unidecode = [ {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, ] +tinycss2 = [ + {file = "tinycss2-1.1.0-py3-none-any.whl", hash = "sha256:0353b5234bcaee7b1ac7ca3dea7e02cd338a9f8dcbb8f2dcd32a5795ec1e5f9a"}, + {file = "tinycss2-1.1.0.tar.gz", hash = "sha256:fbdcac3044d60eb85fdb2aa840ece43cf7dbe798e373e6ee0be545d4d134e18a"}, +] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, @@ -4115,6 +4477,38 @@ twython = [ {file = "twython-3.8.2-py3-none-any.whl", hash = "sha256:a469d673fdd20d1c346e9b9f784212db521aa611bbdfc4912229ab701b36002b"}, {file = "twython-3.8.2.tar.gz", hash = "sha256:c6ca64309260e0ab47267f76217c80812f591991437f376fc61498816384f9e7"}, ] +typed-ast = [ + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"}, + {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"}, + {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"}, + {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"}, + {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"}, + {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"}, + {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"}, + {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"}, + {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"}, + {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"}, + {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"}, + {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"}, + {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"}, + {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"}, + {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"}, + {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, +] typing-extensions = [ {file = "typing_extensions-3.7.4.2-py2-none-any.whl", hash = "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"}, {file = "typing_extensions-3.7.4.2-py3-none-any.whl", hash = "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5"}, @@ -4147,10 +4541,6 @@ vcrpy-unittest = [ {file = "vcrpy-unittest-0.1.7.tar.gz", hash = "sha256:a508d8848e678243f1c0d4b152a645c18403a8da517b8a44dfcfc35a675ec981"}, {file = "vcrpy_unittest-0.1.7-py2.py3-none-any.whl", hash = "sha256:b5e2d78bf4c13bc04dca04061dda934abba5f2c9d5201f148747800ff80a762b"}, ] -verboselogs = [ - {file = "verboselogs-1.7-py2.py3-none-any.whl", hash = "sha256:d63f23bf568295b95d3530c6864a0b580cec70e7ff974177dead1e4ffbc6ff49"}, - {file = "verboselogs-1.7.tar.gz", hash = "sha256:e33ddedcdfdafcb3a174701150430b11b46ceb64c2a9a26198c76a156568e427"}, -] viivakoodi = [ {file = "viivakoodi-0.8.0.tar.gz", hash = "sha256:670a35774559b25abdb089d58fb8e67e4ee9618f385b8418e5027ab860290e39"}, ] @@ -4165,22 +4555,33 @@ webcolors = [ {file = "webcolors-1.10-py2.py3-none-any.whl", hash = "sha256:2559b4f4694bfcd61c87ee06acad921bd2fdbf0c844bee65fc844c6910c413dd"}, {file = "webcolors-1.10.tar.gz", hash = "sha256:7b11194c414dcf4b9bd8fb5ceaafc9da183b27430883c62f620589eb79b91b6e"}, ] +webencodings = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] websocket-client = [ - {file = "websocket_client-0.57.0-py2.py3-none-any.whl", hash = "sha256:0fc45c961324d79c781bab301359d5a1b00b13ad1b10415a4780229ef71a5549"}, - {file = "websocket_client-0.57.0.tar.gz", hash = "sha256:d735b91d6d1692a6a181f2a8c9e0238e5f6373356f561bb9dc4c7af36f452010"}, + {file = "websocket-client-1.1.0.tar.gz", hash = "sha256:b68e4959d704768fa20e35c9d508c8dc2bbc041fd8d267c0d7345cffe2824568"}, + {file = "websocket_client-1.1.0-py2.py3-none-any.whl", hash = "sha256:e5c333bfa9fa739538b652b6f8c8fc2559f1d364243c8a689d7c0e1d41c2e611"}, ] wechatpy = [ - {file = "wechatpy-1.8.14-py2.py3-none-any.whl", hash = "sha256:37ec2d46decedbb2a2174f9f7d620c3b5cec53a5185bad01257ff718ac7a6907"}, - {file = "wechatpy-1.8.14.tar.gz", hash = "sha256:5d26c2da51747bcefa3cee11f4c140d1ffe5cfff26fbbf92742be89b30b92141"}, + {file = "wechatpy-1.8.15-py2.py3-none-any.whl", hash = "sha256:0ac7bd23725cc6cbcfcc45cc4864c2afb9e9b630a2fa6763b0fbed9fe714d5f6"}, + {file = "wechatpy-1.8.15.tar.gz", hash = "sha256:0724347ea9ede4f14b59acefd6e41c9baae25e822df204ece3eeba3d9e1dfd03"}, ] werkzeug = [ {file = "Werkzeug-0.11.15-py2.py3-none-any.whl", hash = "sha256:c6f6f89124df0514d886782c658c3e12f2caaa94da34cee3fd82eebf4ebf052b"}, {file = "Werkzeug-0.11.15.tar.gz", hash = "sha256:455d7798ac263266dbd38d4841f7534dd35ca9c3da4a8df303f8488f38f3bcc0"}, ] +wget = [ + {file = "wget-3.2.zip", hash = "sha256:35e630eca2aa50ce998b9b1a127bb26b30dfee573702782aa982f875e3f16061"}, +] wheel = [ {file = "wheel-0.36.2-py2.py3-none-any.whl", hash = "sha256:78b5b185f0e5763c26ca1e324373aadd49182ca90e825f7853f4b2509215dc0e"}, {file = "wheel-0.36.2.tar.gz", hash = "sha256:e11eefd162658ea59a60a0f6c7d493a7190ea4b9a85e335b33489d9f17e0245e"}, ] +whichcraft = [ + {file = "whichcraft-0.6.1-py2.py3-none-any.whl", hash = "sha256:deda9266fbb22b8c64fd3ee45c050d61139cd87419765f588e37c8d23e236dd9"}, + {file = "whichcraft-0.6.1.tar.gz", hash = "sha256:acdbb91b63d6a15efbd6430d1d7b2d36e44a71697e93e19b7ded477afd9fce87"}, +] wrapt = [ {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"}, ] @@ -4200,6 +4601,9 @@ xlwt = [ {file = "xlwt-1.3.0-py2.py3-none-any.whl", hash = "sha256:a082260524678ba48a297d922cc385f58278b8aa68741596a87de01a9c628b2e"}, {file = "xlwt-1.3.0.tar.gz", hash = "sha256:c59912717a9b28f1a3c2a98fd60741014b06b043936dcecbc113eaaada156c88"}, ] +xmlformatter = [ + {file = "xmlformatter-0.2.2.tar.gz", hash = "sha256:3135aaf08ecc33b0efedbe61be8b58fc5e5a878c2d342f6fb1d6fd5e2b60d545"}, +] xmltodict = [ {file = "xmltodict-0.12.0-py2.py3-none-any.whl", hash = "sha256:8bbcb45cc982f48b2ca8fe7e7827c5d792f217ecf1792626f808bf41c3b86051"}, {file = "xmltodict-0.12.0.tar.gz", hash = "sha256:50d8c638ed7ecb88d90561beedbf720c9b4e851a9fa6c47ebd64e99d166d8a21"}, diff --git a/pyproject.toml b/pyproject.toml index 653ec71..8072ce1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,8 @@ authors = [ "Mathieu Benoit ",] python = "^3.7.7" Asterisk = "^0.0.8" Babel = "2.3.4" -GitPython = "^3.1.12" +BeautifulSoup4 = "^4.9.3" +GitPython = "3.1.12" Jinja2 = "2.10.1" Mako = "1.0.4" MarkupSafe = "0.23" @@ -28,37 +29,42 @@ Werkzeug = "0.11.15" XlsxWriter = "0.9.3" agithub = "^2.2.2" altair = "^4.1.0" -argcomplete = "^1.12.2" +argcomplete = "^1.12.3" bokeh = "1.1.0" -boto = "^2.49.0" boto3 = "1.9.102" cachetools = ">=2.0.1" -cerberus = "^1.3.2" +cerberus = "^1.3.4" chardet = "3.0.4" +click = "^8.0.1" +cloudflare = "^2.8.15" code_writer = "^1.1.1" colorama = "^0.4.4" cryptography = "2.6.1" -cython = "^0.29.21" +css-html-prettify = "^2.5.5" +cython = "^0.29.23" ddt = "1.2.0" decorator = "4.0.10" -docutils = "0.12" +docutils = "0.16" ebaysdk = "2.1.5" -email_validator = "^1.1.2" -escpos = "^1.8" -extract-msg = "^0.28.0" -extract_msg = "^0.28.0" -factur-x = "^1.12" +email_validator = "^1.1.3" +emoji = "^1.2.0" +escpos = "^1.9" +extract-msg = "^0.28.7" +extract_msg = "^0.28.7" +factur-x = "^2.3" feedparser = "5.2.1" +flake8 = "^3.9.2" freezegun = "0.3.11" geojson = "2.4.1" gevent = "1.3.4" -gitpython = "^3.1.12" giturlparse = "^0.10.0" greenlet = "0.4.13" html2text = "2016.9.19" -invoice2data = "^0.3.5" +html5print = "^0.1.2" +invoice2data = "^0.3.6" iscompatible = "^0.1.1" jira = "2.0.0" +js2py = "^0.71" keystoneauth1 = "3.14.0" lasso = "^0.0.5" libsass = "0.12.3" @@ -68,20 +74,21 @@ mock = "2.0.0" mpld3 = "0.3" mysqlclient = "^2.0.3" num2words = "0.5.6" -numpy = "^1.19.5" +numpy = "^1.21.0" oauthlib = "2.1.0" "oca.decorators" = "^0.0.1" ofxparse = "0.16" -openpyxl = "^3.0.5" +openpyxl = "^3.0.7" openupgradelib = "^2.0.0" pandas = "1.2.0" paramiko = "^2.7.2" passlib = "1.6.5" -pdf2image = "^1.14.0" +pdf2image = "^1.16.0" +pdfminer = "^20191125" pexpect = "^4.8.0" -phonenumbers = "^8.12.15" +phonenumbers = "^8.12.26" plotly = "4.1.0" -premailer = "^3.7.0" +premailer = "^3.9.0" psutil = "4.3.1" psycopg2 = "2.7.3.1" py-Asterisk = "^0.5.18" @@ -91,12 +98,11 @@ pycountry = "^20.7.3" pydot = "1.2.3" pygount = "<1.2.0" pyldap = "2.4.28" -pymssql = "^2.1.5" +pymssql = "^2.2.1" pymysql = "^1.0.2" -pyotp = "^2.4.1" +pyotp = "^2.6.0" pyparsing = "2.1.10" -pyproj = "^3.0.0" -pyrfc = "^0.1.2" +pyproj = "^3.1.0" pyserial = "3.1.1" pysftp = "^0.2.9" python-chess = "<0.24" @@ -106,7 +112,7 @@ python-git = "^2018.2.1" python-json-logger = "0.1.5" python-keystoneclient = "3.22.0" python-slugify = ">=3.0.2" -python-stdnum = "^1.15" +python-stdnum = "^1.16" python-swiftclient = "3.9.0" python-u2flib-server = "^5.0.1" python-u2flib_server = "^5.0.1" @@ -118,11 +124,10 @@ raven = "^6.10.0" redis = "2.10.5" reportlab = "3.3.0" requests = "2.20.0" -requests-mock = "^1.8.0" +requests-mock = "^1.9.3" requests-oauthlib = "1.1.0" requests-toolbelt = "0.9.1" retrying = "^1.3.3" -rotate_backups_s3 = "^0.3" serial = "^0.0.97" shapely = "1.6.4.post2" simplejson = "^3.17.2" @@ -130,27 +135,37 @@ slugify = "^0.0.1" sphinx = ">=1.6.7" sphinx-intl = "^2.0.1" sphinx-patchqueue = ">=1.0" -sphinx_rtd_theme = "^0.5.1" -sqlalchemy = "^1.3.22" +sphinx_rtd_theme = "^0.5.2" +sqlalchemy = "^1.4.19" statsd = "3.2.1" suds-jurko = "0.6" toml = "^0.10.2" twython = "^3.8.2" unidecode = "1.0.22" +unidiff = "^0.6.0" vatnumber = "1.2" vcrpy = ">=2.1.1" vcrpy-unittest = "^0.1.7" vobject = "0.9.3" voicent-python = "^1.0" webcolors = "1.10" -websocket-client = "^0.57.0" -wechatpy = "^1.8.14" +websocket-client = "^1.1.0" +wechatpy = "^1.8.15" +wget = "^3.2" wheel = "^0.36.2" xlrd = "1.0.0" xlwt = "1.3" +xmlformatter = "^0.2.2" xmltodict = "^0.12.0" zeep = "^4.0.0" zxcvbn = "^4.4.28" [tool.poetry.dev-dependencies] -websocket_client = "^0.57.0" +websocket_client = "^1.1.0" + +[tool.poetry.dependencies.black] +git = "https://github.com/psf/black.git" +rev = "21.6b0" + +[tool.poetry.dependencies.pylint-odoo] +git = "https://github.com/oca/pylint-odoo.git" diff --git a/requirements.txt b/requirements.txt index 6f619c9..f847d19 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,12 @@ # For script giturlparse agithub -gitpython +GitPython==3.1.12 PyYAML retrying xmltodict openupgradelib +unidiff # For OSX cython @@ -13,6 +14,7 @@ cython # For Odoo wheel phonenumbers +pytz==2017.3 # For OCA factur-x @@ -29,7 +31,26 @@ voicent-python python-swiftclient python-keystoneclient +# For logical test +docutils==0.16 +flake8 +git+https://github.com/oca/pylint-odoo.git +# oca-maintainers-tools +# git+https://github.com/OCA/maintainer-tools.git#egg=oca-maintainers-tools +xmlformatter +html5print +css-html-prettify +BeautifulSoup4 +git+https://github.com/psf/black.git@21.6b0 + # For updating poetry toml iscompatible pandas==1.2.0 + +# For deployment +cloudflare + +# For multilang_md.py +click +emoji diff --git a/run.sh b/run.sh index 962ef3e..67d8bb3 100755 --- a/run.sh +++ b/run.sh @@ -1,3 +1,8 @@ #!/usr/bin/env bash source ./.venv/bin/activate python3 ./odoo/odoo-bin -c ./config.conf --limit-time-real 99999 --limit-time-cpu 99999 $@ +retVal=$? +if [[ $retVal -ne 0 ]]; then + echo "Error run.sh" + exit 1 +fi diff --git a/script/addons/install_addons.sh b/script/addons/install_addons.sh new file mode 100755 index 0000000..5c684c2 --- /dev/null +++ b/script/addons/install_addons.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +./run.sh --no-http --stop-after-init -d $1 -i $2 -u $2 +retVal=$? +if [[ $retVal -ne 0 ]]; then + echo "Error install_addons.sh" + exit 1 +fi diff --git a/script/addons/install_addons_dev.sh b/script/addons/install_addons_dev.sh new file mode 100755 index 0000000..ba82bb8 --- /dev/null +++ b/script/addons/install_addons_dev.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +./run.sh --no-http --stop-after-init --dev qweb -d $1 -i $2 -u $2 +retVal=$? +if [[ $retVal -ne 0 ]]; then + echo "Error install_addons_dev.sh" + exit 1 +fi diff --git a/script/addons/reinstall_addons.sh b/script/addons/reinstall_addons.sh new file mode 100755 index 0000000..a32fb6e --- /dev/null +++ b/script/addons/reinstall_addons.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +./script/addons/uninstall_addons.sh $1 $2 +./script/addons/install_addons_dev.sh $1 $2 +retVal=$? +if [[ $retVal -ne 0 ]]; then + echo "Error reinstall_addons.sh" + exit 1 +fi diff --git a/script/addons/uninstall_addons.sh b/script/addons/uninstall_addons.sh new file mode 100755 index 0000000..65c3ce9 --- /dev/null +++ b/script/addons/uninstall_addons.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +./run.sh --no-http --stop-after-init -d $1 --uninstall $2 diff --git a/script/code_generator/check_git_change_code_generator.sh b/script/code_generator/check_git_change_code_generator.sh new file mode 100755 index 0000000..f0c5e69 --- /dev/null +++ b/script/code_generator/check_git_change_code_generator.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +# REF: https://stackoverflow.com/a/28938235 +# Reset +Color_Off='\033[0m' # Text Reset + +# Regular Colors +Black='\033[0;30m' # Black +Red='\033[0;31m' # Red +Green='\033[0;32m' # Green +Yellow='\033[0;33m' # Yellow +Blue='\033[0;34m' # Blue +Purple='\033[0;35m' # Purple +Cyan='\033[0;36m' # Cyan +White='\033[0;37m' # White + +# Bold +BBlack='\033[1;30m' # Black +BRed='\033[1;31m' # Red +BGreen='\033[1;32m' # Green +BYellow='\033[1;33m' # Yellow +BBlue='\033[1;34m' # Blue +BPurple='\033[1;35m' # Purple +BCyan='\033[1;36m' # Cyan +BWhite='\033[1;37m' # White + +# Underline +UBlack='\033[4;30m' # Black +URed='\033[4;31m' # Red +UGreen='\033[4;32m' # Green +UYellow='\033[4;33m' # Yellow +UBlue='\033[4;34m' # Blue +UPurple='\033[4;35m' # Purple +UCyan='\033[4;36m' # Cyan +UWhite='\033[4;37m' # White + +# Background +On_Black='\033[40m' # Black +On_Red='\033[41m' # Red +On_Green='\033[42m' # Green +On_Yellow='\033[43m' # Yellow +On_Blue='\033[44m' # Blue +On_Purple='\033[45m' # Purple +On_Cyan='\033[46m' # Cyan +On_White='\033[47m' # White + +# High Intensity +IBlack='\033[0;90m' # Black +IRed='\033[0;91m' # Red +IGreen='\033[0;92m' # Green +IYellow='\033[0;93m' # Yellow +IBlue='\033[0;94m' # Blue +IPurple='\033[0;95m' # Purple +ICyan='\033[0;96m' # Cyan +IWhite='\033[0;97m' # White + +# Bold High Intensity +BIBlack='\033[1;90m' # Black +BIRed='\033[1;91m' # Red +BIGreen='\033[1;92m' # Green +BIYellow='\033[1;93m' # Yellow +BIBlue='\033[1;94m' # Blue +BIPurple='\033[1;95m' # Purple +BICyan='\033[1;96m' # Cyan +BIWhite='\033[1;97m' # White + +# High Intensity backgrounds +On_IBlack='\033[0;100m' # Black +On_IRed='\033[0;101m' # Red +On_IGreen='\033[0;102m' # Green +On_IYellow='\033[0;103m' # Yellow +On_IBlue='\033[0;104m' # Blue +On_IPurple='\033[0;105m' # Purple +On_ICyan='\033[0;106m' # Cyan +On_IWhite='\033[0;107m' # White + +# TODO support argument to test only specified path + +check_git() { + REP=$1 + cd ${REP} + + output=$(git status --porcelain) + if [ -z "$output" ]; then + echo "PASS - ${REP}" + else + echo -e "${Red}FAIL - ${REP}${Color_Off}" + echo -e "${BRed}${output}${Color_Off}" + cd - >/dev/null + exit 1 + fi + + cd - >/dev/null +} + +if [ $# -gt 0 ]; then + check_git "$1" +else + # check_git "./addons/TechnoLibre_odoo-code-generator" + check_git "./addons/TechnoLibre_odoo-code-generator-template" +fi diff --git a/script/code_generator/install_and_test_code_generator.sh b/script/code_generator/install_and_test_code_generator.sh new file mode 100755 index 0000000..58f66bb --- /dev/null +++ b/script/code_generator/install_and_test_code_generator.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# $1 is database name +# $2 is module name +# $3 is directory path to check + +./script/addons/install_addons_dev.sh $1 $2 +retVal=$? +if [[ $retVal -ne 0 ]]; then + echo "Error ./script/addons/install_addons_dev.sh ${1} ${2}" + exit 1 +fi +./script/repo_revert_git_diff_date_from_code_generator.py +# Remove pot and po diff +cd $3 +BRANCH=$(git branch --show-current) +git restore --source="${BRANCH}" "*.po*" +cd - +./script/maintenance/black.sh $3 +echo "TEST ${2}" +./script/code_generator/check_git_change_code_generator.sh $3 +retVal=$? +if [[ $retVal -ne 0 ]]; then + echo "Error ./script/code_generator/check_git_change_code_generator.sh" + exit 1 +fi diff --git a/script/code_generator/transform_python_to_code_writer.py b/script/code_generator/transform_python_to_code_writer.py index 6030507..fae2585 100644 --- a/script/code_generator/transform_python_to_code_writer.py +++ b/script/code_generator/transform_python_to_code_writer.py @@ -5,6 +5,7 @@ import argparse import logging import subprocess from code_writer import CodeWriter + # import tokenize from script.git_tool import GitTool @@ -21,16 +22,22 @@ def get_config(): # TODO update description parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description='''\ + description="""\ Transform a python file in code writer format python file. -''', - epilog='''\ -''' +""", + epilog="""\ +""", + ) + parser.add_argument( + "-f", + "--file", + dest="file", + required=True, + help="Path of file to transform to code_writer.", + ) + parser.add_argument( + "-o", "--output", dest="output", help="The output file." ) - parser.add_argument('-f', '--file', dest="file", required=True, - help="Path of file to transform to code_writer.") - parser.add_argument('-o', '--output', dest="output", - help="The output file.") args = parser.parse_args() return args @@ -68,7 +75,11 @@ def main(): no_tab = 0 # Validate file format - out = subprocess.check_output(f"python -m tabnanny {config.file}", stderr=subprocess.STDOUT, shell=True) + out = subprocess.check_output( + f"python -m tabnanny {config.file}", + stderr=subprocess.STDOUT, + shell=True, + ) if out: print(out) sys.exit(1) @@ -82,14 +93,16 @@ def main(): cw.emit("cw = CodeWriter()") no_line = 1 - with open(config.file, 'r') as file: + with open(config.file, "r") as file: for line in file.readlines(): nb_tab, nb_space = count_space_tab(line) diff_tab = nb_tab - last_nb_tab new_line = line.strip() - new_line = new_line.replace("\"", "\\\"") + new_line = new_line.replace('"', '\\"') - status_no_tab = add_line(cw, new_line, no_line, nb_tab, no_tab, no_tab, nb_space) + status_no_tab = add_line( + cw, new_line, no_line, nb_tab, no_tab, no_tab, nb_space + ) if status_no_tab >= 0: no_tab = status_no_tab @@ -99,13 +112,15 @@ def main(): output = cw.render() if config.output: - with open(config.output, 'w') as file: + with open(config.output, "w") as file: file.write(output) else: print(output) -def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): +def add_line( + cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space +): """ Recursive check indent and write line :param cw: code_writer module @@ -123,25 +138,27 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): sys.exit(-1) if nb_indent == -1: - cw.emit("cw.emit(\"\")") + cw.emit('cw.emit("")') return 0 elif nb_indent == no_indent: if nb_indent == 0: - cw.emit(f"cw.emit(\"{line}\")") + cw.emit(f'cw.emit("{line}")') return 0 elif nb_indent == 1: if no_indent != init_no_intend: cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit(f'cw.emit("{line}")') elif nb_indent == 2: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") with cw.indent(): if no_indent != init_no_intend: - cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") + cw.emit( + f"with cw.indent({4 + nb_space if nb_space else ''}):" + ) with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit(f'cw.emit("{line}")') elif nb_indent == 3: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") @@ -150,9 +167,12 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): cw.emit(f"with cw.indent():") with cw.indent(): if no_indent != init_no_intend: - cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") + cw.emit( + "with" + f" cw.indent({4 + nb_space if nb_space else ''}):" + ) with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit(f'cw.emit("{line}")') elif nb_indent == 4: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") @@ -164,9 +184,12 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): cw.emit(f"with cw.indent():") with cw.indent(): if no_indent != init_no_intend: - cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") + cw.emit( + "with" + f" cw.indent({4 + nb_space if nb_space else ''}):" + ) with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit(f'cw.emit("{line}")') elif nb_indent == 5: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") @@ -181,9 +204,12 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): cw.emit(f"with cw.indent():") with cw.indent(): if no_indent != init_no_intend: - cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") + cw.emit( + "with" + f" cw.indent({4 + nb_space if nb_space else ''}):" + ) with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit(f'cw.emit("{line}")') elif nb_indent == 6: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") @@ -201,9 +227,12 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): cw.emit(f"with cw.indent():") with cw.indent(): if no_indent != init_no_intend: - cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") + cw.emit( + "with" + f" cw.indent({4 + nb_space if nb_space else ''}):" + ) with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit(f'cw.emit("{line}")') elif nb_indent == 7: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") @@ -224,9 +253,12 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): cw.emit(f"with cw.indent():") with cw.indent(): if no_indent != init_no_intend: - cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") + cw.emit( + "with" + f" cw.indent({4 + nb_space if nb_space else ''}):" + ) with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit(f'cw.emit("{line}")') elif nb_indent == 8: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") @@ -250,9 +282,12 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): cw.emit(f"with cw.indent():") with cw.indent(): if no_indent != init_no_intend: - cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") + cw.emit( + "with" + f" cw.indent({4 + nb_space if nb_space else ''}):" + ) with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit(f'cw.emit("{line}")') elif nb_indent == 9: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") @@ -279,9 +314,12 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): cw.emit(f"with cw.indent():") with cw.indent(): if no_indent != init_no_intend: - cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") + cw.emit( + "with" + f" cw.indent({4 + nb_space if nb_space else ''}):" + ) with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit(f'cw.emit("{line}")') elif nb_indent == 10: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") @@ -311,9 +349,14 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): cw.emit(f"with cw.indent():") with cw.indent(): if no_indent != init_no_intend: - cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") + cw.emit( + "with" + f" cw.indent({4 + nb_space if nb_space else ''}):" + ) with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit( + f'cw.emit("{line}")' + ) elif nb_indent == 11: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") @@ -342,13 +385,26 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with cw.indent():" + ) with cw.indent(): - if no_indent != init_no_intend: - cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") + if ( + no_indent + != init_no_intend + ): + cw.emit( + "with" + f" cw.indent({4 + nb_space if nb_space else ''}):" + ) with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit( + f'cw.emit("{line}")' + ) elif nb_indent == 12: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") @@ -377,16 +433,35 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if no_indent != init_no_intend: - cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") + if ( + no_indent + != init_no_intend + ): + cw.emit( + "with" + f" cw.indent({4 + nb_space if nb_space else ''}):" + ) with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit( + f'cw.emit("{line}")' + ) elif nb_indent == 13: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") @@ -415,19 +490,44 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if no_indent != init_no_intend: - cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") + if ( + no_indent + != init_no_intend + ): + cw.emit( + "with" + f" cw.indent({4 + nb_space if nb_space else ''}):" + ) with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit( + f'cw.emit("{line}")' + ) elif nb_indent == 14: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") @@ -458,22 +558,53 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if no_indent != init_no_intend: - cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") + if ( + no_indent + != init_no_intend + ): + cw.emit( + "with" + f" cw.indent({4 + nb_space if nb_space else ''}):" + ) with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit( + f'cw.emit("{line}")' + ) elif nb_indent == 15: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") @@ -504,25 +635,63 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent + - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if no_indent != init_no_intend: - cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") + if ( + no_indent + != init_no_intend + ): + cw.emit( + "with" + f" cw.indent({4 + nb_space if nb_space else ''}):" + ) with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit( + f'cw.emit("{line}")' + ) elif nb_indent == 16: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") @@ -551,28 +720,72 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent + - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent + - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if no_indent != init_no_intend: - cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") + if ( + no_indent + != init_no_intend + ): + cw.emit( + f"with cw.indent({4 + nb_space if nb_space else ''}):" + ) with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit( + f'cw.emit("{line}")' + ) elif nb_indent == 17: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") @@ -601,31 +814,81 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent + - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent + - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent + - 1 + > init_no_intend + ): + cw.emit( + f"with cw.indent():" + ) with cw.indent(): - if no_indent != init_no_intend: - cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") + if ( + no_indent + != init_no_intend + ): + cw.emit( + f"with cw.indent({4 + nb_space if nb_space else ''}):" + ) with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit( + f'cw.emit("{line}")' + ) elif nb_indent == 18: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") @@ -656,34 +919,90 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent + - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent + - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent + - 1 + > init_no_intend + ): + cw.emit( + f"with cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent + - 1 + > init_no_intend + ): + cw.emit( + f"with cw.indent():" + ) with cw.indent(): - if no_indent != init_no_intend: - cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") + if ( + no_indent + != init_no_intend + ): + cw.emit( + f"with cw.indent({4 + nb_space if nb_space else ''}):" + ) with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit( + f'cw.emit("{line}")' + ) elif nb_indent == 19: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") @@ -716,48 +1035,126 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent + - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent + - 1 + > init_no_intend + ): + cw.emit( + f"with" + f" cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent + - 1 + > init_no_intend + ): + cw.emit( + f"with cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent + - 1 + > init_no_intend + ): + cw.emit( + f"with cw.indent():" + ) with cw.indent(): - if nb_indent - 1 > init_no_intend: - cw.emit(f"with cw.indent():") + if ( + nb_indent + - 1 + > init_no_intend + ): + cw.emit( + f"with cw.indent():" + ) with cw.indent(): - if no_indent != init_no_intend: - cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") + if ( + no_indent + != init_no_intend + ): + cw.emit( + f"with cw.indent({4 + nb_space if nb_space else ''}):" + ) with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit( + f'cw.emit("{line}")' + ) return nb_indent else: if no_indent > nb_indent: # deindent - return add_line(cw, line, no_line, nb_indent, no_indent - 1, init_no_intend, nb_space) + return add_line( + cw, + line, + no_line, + nb_indent, + no_indent - 1, + init_no_intend, + nb_space, + ) else: # indent - return add_line(cw, line, no_line, nb_indent, no_indent + 1, init_no_intend, nb_space) + return add_line( + cw, + line, + no_line, + nb_indent, + no_indent + 1, + init_no_intend, + nb_space, + ) print("BUG") return -1 -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/script/code_generator/transform_xml_to_code_writer.py b/script/code_generator/transform_xml_to_code_writer.py index 46c8e3b..aa90052 100644 --- a/script/code_generator/transform_xml_to_code_writer.py +++ b/script/code_generator/transform_xml_to_code_writer.py @@ -20,16 +20,22 @@ def get_config(): # TODO update description parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description='''\ + description="""\ Transform a xml file in code writer format xml file. -''', - epilog='''\ -''' +""", + epilog="""\ +""", + ) + parser.add_argument( + "-f", + "--file", + dest="file", + required=True, + help="Path of file to transform to code_writer.", + ) + parser.add_argument( + "-o", "--output", dest="output", help="The output file." ) - parser.add_argument('-f', '--file', dest="file", required=True, - help="Path of file to transform to code_writer.") - parser.add_argument('-o', '--output', dest="output", - help="The output file.") args = parser.parse_args() return args @@ -55,7 +61,10 @@ def code_writer_deep_xml(nodes): if lst_comment: comment = f"# {lst_comment[0]}" - code_line = (f"E.{node.tagName}({str(dict(node.attributes.items()))})", value) + code_line = ( + f"E.{node.tagName}({str(dict(node.attributes.items()))})", + value, + ) lst_result.append((comment, code_line)) elif node.nodeType == Node.TEXT_NODE: if size_nodes == 1: @@ -102,31 +111,36 @@ def main(): print(f"Error, cannot parse {config.file}") sys.exit(1) - cw.emit('from lxml.builder import E') - cw.emit('from lxml import etree as ET') - cw.emit('from code_writer import CodeWriter') - cw.emit('') + cw.emit("from lxml.builder import E") + cw.emit("from lxml import etree as ET") + cw.emit("from code_writer import CodeWriter") + cw.emit("") cw.emit('print(\'\')') cw.emit('print("")') lst_function = [] comment_for_next_group = None - for odoo in mydoc.getElementsByTagName('odoo'): + for odoo in mydoc.getElementsByTagName("odoo"): for ir_view_item in odoo.childNodes: if ir_view_item.nodeType == Node.ELEMENT_NODE: # Show part of xml fct_name = "ma_fonction" lst_function.append(fct_name) - cw.emit(f'def {fct_name}():') + cw.emit(f"def {fct_name}():") with cw.indent(): cw.emit('"""') - for line in transform_string_to_list(ir_view_item.toprettyxml()): + for line in transform_string_to_list( + ir_view_item.toprettyxml() + ): cw.emit(line) cw.emit('"""') # Show comment if comment_for_next_group: - cw.emit(f'print(\'\')') + cw.emit( + "print('')" + ) comment_for_next_group = None attributes_root = dict(ir_view_item.attributes.items()) @@ -136,15 +150,21 @@ def main(): generate_code.generate_code(lst_out) child_root = generate_code.result - code = f'root = E.{ir_view_item.tagName}({str(attributes_root)}, {child_root})' + code = ( + "root =" + f" E.{ir_view_item.tagName}({str(attributes_root)}," + f" {child_root})" + ) for line in code.split("\n"): cw.emit(line) - cw.emit('content = ET.tostring(root, pretty_print=True)') + cw.emit("content = ET.tostring(root, pretty_print=True)") cw.emit() cw.emit("cw = CodeWriter()") - cw.emit('for line in content.decode("utf-8").split("\\n"):') + cw.emit( + 'for line in content.decode("utf-8").split("\\n"):' + ) with cw.indent(): cw.emit("with cw.indent():") with cw.indent(): @@ -161,13 +181,15 @@ def main(): output = cw.render() if config.output: - with open(config.output, 'w') as file: + with open(config.output, "w") as file: file.write(output) else: print(output) -def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): +def add_line( + cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space +): """ Recursive check indent and write line :param cw: code_writer module @@ -185,21 +207,21 @@ def add_line(cw, line, no_line, nb_indent, no_indent, init_no_intend, nb_space): sys.exit(-1) if nb_indent == -1: - cw.emit("cw.emit(\"\")") + cw.emit('cw.emit("")') return 0 elif nb_indent == no_indent: if nb_indent == 0: - cw.emit(f"cw.emit(\"{line}\")") + cw.emit(f'cw.emit("{line}")') return 0 elif nb_indent == 1: if no_indent != init_no_intend: cw.emit(f"with cw.indent({4 + nb_space if nb_space else ''}):") with cw.indent(): - cw.emit(f"cw.emit(\"{line}\")") + cw.emit(f'cw.emit("{line}")') elif nb_indent == 2: if nb_indent - 1 > init_no_intend: cw.emit(f"with cw.indent():") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/script/db_restore.py b/script/db_restore.py new file mode 100755 index 0000000..bb49972 --- /dev/null +++ b/script/db_restore.py @@ -0,0 +1,107 @@ +#!./.venv/bin/python +import os +import sys +import argparse +import logging +from subprocess import check_output + +new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) +sys.path.append(new_path) + +logging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO")) + +_logger = logging.getLogger(__name__) + + +def get_config(): + """Parse command line arguments, extracting the config file name, + returning the union of config file and command line arguments + + :return: dict of config file settings and command line arguments + """ + # TODO update description + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description="""\ + Restore database, use cache to clone to improve speed. +""", + epilog="""\ +""", + ) + # parser.add_argument('-d', '--dir', dest="dir", default="./", + # help="Path of repo to change remote, including submodule.") + parser.add_argument("--database", help="Database to manipulate.") + parser.add_argument( + "--image", + default="erplibre_base", + help=( + "Image name to restore, from directory image_db, filename without" + " '.zip'. Example, use erplibre_base to use image" + " erplibre_base.zip." + ), + ) + parser.add_argument( + "--clean_cache", + action="store_true", + help="Delete all database cache to clone, begin by _cache_.", + ) + args = parser.parse_args() + return args + + +def main(): + config = get_config() + + # Get list of database + arg = "./.venv/bin/python3 ./odoo/odoo-bin db --list" + out = check_output(arg.split(" ")).decode() + lst_db = out.strip().split("\n") + lst_db_cache = [a for a in lst_db if a.startswith("_cache_")] + + if config.clean_cache: + for db in lst_db_cache: + _logger.info(f"## Delete {db} ##") + arg = ( + "./.venv/bin/python3 ./odoo/odoo-bin db --drop --database" + f" {db}" + ) + out = check_output(arg.split(" ")).decode() + print(out) + + if config.database: + cache_database = f"_cache_{config.image}" + # Drop db + if config.database in lst_db: + _logger.info(f"## Drop {config.database} ##") + arg = ( + "./.venv/bin/python3 ./odoo/odoo-bin db --drop --database" + f" {config.database}" + ) + out = check_output(arg.split(" ")).decode() + print(out) + # Check cache exist + if cache_database not in lst_db_cache: + _logger.info( + f"## Create cache {cache_database} from image" + f" {config.image} ##" + ) + arg = ( + "./.venv/bin/python3 ./odoo/odoo-bin db --restore" + f" --restore_image {config.image} --database {cache_database}" + ) + out = check_output(arg.split(" ")).decode() + print(out) + # Clone database + _logger.info( + f"## Clone cache {cache_database} to database {config.database} ##" + ) + arg = ( + "./.venv/bin/python3 ./odoo/odoo-bin db --clone --from_database" + f" {cache_database} --database {config.database}" + ) + out = check_output(arg.split(" ")).decode() + print(out) + + +if __name__ == "__main__": + main() diff --git a/script/deployment/get_public_ip.py b/script/deployment/get_public_ip.py new file mode 100755 index 0000000..dcc3232 --- /dev/null +++ b/script/deployment/get_public_ip.py @@ -0,0 +1,6 @@ +#!./.venv/bin/python +import requests + +r = requests.get(r"https://api.ipify.org") +ip = r.text +print(ip) diff --git a/script/deployment/update_dns_cloudflare.py b/script/deployment/update_dns_cloudflare.py new file mode 100755 index 0000000..b87a7a4 --- /dev/null +++ b/script/deployment/update_dns_cloudflare.py @@ -0,0 +1,145 @@ +#!./.venv/bin/python +import argparse +import logging +import CloudFlare +import requests + +logging.basicConfig() +logging.getLogger().setLevel(logging.INFO) +_logger = logging.getLogger(__name__) + + +def get_config(): + """Parse command line arguments, extracting the config file name, + returning the union of config file and command line arguments + + :return: dict of config file settings and command line arguments + """ + + # TODO update description + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description="""\ + Can update all old_ip to new_ip. + When auto_sync is enable: + Check zone and name on cloudflare and compare with public ip, update all old ip with new ip if different. + You need your profile in file ~/.cloudflare/cloudflare.cfg +""", + epilog="""\ +""", + ) + parser.add_argument( + "--profile", + required=True, + help="The profile of CloudFlare, check ~/.cloudflare/cloudflare.cfg.", + ) + parser.add_argument( + "--old_ip", required=False, help="Ip to search to update." + ) + parser.add_argument( + "--new_ip", required=False, help="Ip to replace the old ip." + ) + parser.add_argument( + "--dns_name", + required=False, + help="DNS name to output his ip and sync.", + ) + parser.add_argument( + "--zone_name", + required=False, + help="The CloudFlare zone name to check.", + ) + parser.add_argument("--delete", action="store_true") + parser.add_argument("--debug", action="store_true") + parser.add_argument("--raw", action="store_true") + parser.add_argument( + "--auto_sync", + action="store_true", + help=( + "Need this to use DNS name and zone name to sync public ip to" + " CloudFlare." + ), + ) + args = parser.parse_args() + return args + + +class ManageCloudFlare: + def __init__(self, profile, debug=False, raw=False): + self.cf = CloudFlare.CloudFlare(profile=profile, debug=debug, raw=raw) + self.raw = raw + + @staticmethod + def get_public_ip(): + r = requests.get(r"https://api.ipify.org") + ip = r.text + return ip + + def get_ip_cloudflare(self, zone_name, name): + """Return ip from a zone and domain""" + zone = self.cf.zones.get(params={"name": zone_name}) + lst_zone = [a.get("id") for a in zone] + for zone_id in lst_zone: + lst_result_dns = self.cf.zones.dns_records.get(zone_id) + for result_dns in lst_result_dns: + if ( + result_dns.get("type") == "A" + and result_dns.get("name") == name + ): + return result_dns.get("content") + + def edit_cloudflare(self, old_ip, delete=False, new_ip=""): + """Change ip of zone and name""" + + # TODO support smaller range + params = {"per_page": 500} + zones = self.cf.zones.get(params=params) + result = zones.get("result") if self.raw else zones + lst_zone = [a.get("id") for a in result] + for zone_id in lst_zone: + # dns_records = cf.zones.dns_records.export.get(zone_id) + result_dns = self.cf.zones.dns_records.get(zone_id) + dns_records = result_dns.get("result") if self.raw else result_dns + for dns in dns_records: + if dns.get("content") == old_ip: + if delete: + _logger.info(f"Delete {dns.get('name')}") + r = self.cf.zones.dns_records.delete( + zone_id, dns.get("id") + ) + elif new_ip: + _logger.info( + f"Update {dns.get('name')} with ip {new_ip}, old" + f" ip was {old_ip}" + ) + dns_record = { + "name": dns.get("name"), + "type": dns.get("type"), + "content": new_ip, + } + r = self.cf.zones.dns_records.post( + zone_id, data=dns_record + ) + r = self.cf.zones.dns_records.delete( + zone_id, dns.get("id") + ) + + +def main(): + config = get_config() + cl = ManageCloudFlare(config.profile, debug=config.debug) + if config.auto_sync: + configured_ip = cl.get_ip_cloudflare(config.zone_name, config.dns_name) + actual_ip = cl.get_public_ip() + if configured_ip != actual_ip: + cl.edit_cloudflare(configured_ip, new_ip=actual_ip) + else: + _logger.info(f"Nothing to do, same ip {actual_ip}") + else: + cl.edit_cloudflare( + config.old_ip, delete=config.delete, new_ip=config.new_ip + ) + + +if __name__ == "__main__": + main() diff --git a/script/docker_update_version.py b/script/docker_update_version.py index 8d5838a..56bb974 100755 --- a/script/docker_update_version.py +++ b/script/docker_update_version.py @@ -5,7 +5,7 @@ import argparse import logging import yaml -new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) +new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) sys.path.append(new_path) from script.git_tool import GitTool @@ -24,17 +24,27 @@ def get_config(): # TODO update description parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description='''\ + description="""\ Update version of docker ready to commit. -''', - epilog='''\ -''' +""", + epilog="""\ +""", + ) + parser.add_argument( + "--version", required=True, help="Version of ERPLibre." + ) + parser.add_argument("--base", required=True, help="Docker base name.") + parser.add_argument("--prod", required=True, help="Docker prod name.") + parser.add_argument( + "--docker_compose_file", + default="./docker-compose.yml", + help="Docker compose file to update.", + ) + parser.add_argument( + "--docker_prod", + default="./docker/Dockerfile.prod.pkg", + help="Docker prod file to update.", ) - parser.add_argument('--version', required=True, help="Version of ERPLibre.") - parser.add_argument('--base', required=True, help="Docker base name.") - parser.add_argument('--prod', required=True, help="Docker prod name.") - parser.add_argument('--docker_compose_file', default="./docker-compose.yml", help="Docker compose file to update.") - parser.add_argument('--docker_prod', default="./docker/Dockerfile.prod.pkg", help="Docker prod file to update.") args = parser.parse_args() args.base_version = f"{args.base}:{args.version}" args.prod_version = f"{args.prod}:{args.version}" @@ -57,7 +67,7 @@ def get_config(): def edit_text(config): - with open(config.docker_compose_file, 'r') as f: + with open(config.docker_compose_file, "r") as f: lst_docker_info = f.readlines() if not lst_docker_info: @@ -70,18 +80,20 @@ def edit_text(config): if is_find: key = "image:" value = lst_docker_info[i] - lst_docker_info[i] = f"{value[:value.find(key) + len(key)]} {config.prod_version}\n" + lst_docker_info[ + i + ] = f"{value[:value.find(key) + len(key)]} {config.prod_version}\n" break if "ERPLibre" in docker_info: is_find = True i += 1 - with open(config.docker_compose_file, 'w') as f: + with open(config.docker_compose_file, "w") as f: f.writelines(lst_docker_info) def edit_docker_prod(config): - with open(config.docker_prod, 'r') as f: + with open(config.docker_prod, "r") as f: lst_docker_info = f.readlines() if not lst_docker_info: @@ -94,7 +106,7 @@ def edit_docker_prod(config): lst_docker_info[i] = f"FROM {config.base_version}\n" i += 1 - with open(config.docker_prod, 'w') as f: + with open(config.docker_prod, "w") as f: f.writelines(lst_docker_info) @@ -104,5 +116,5 @@ def main(): edit_docker_prod(config) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/script/fork_github_repo/__init__.py b/script/fork_github_repo/__init__.py index 1d7d081..5b209ca 100644 --- a/script/fork_github_repo/__init__.py +++ b/script/fork_github_repo/__init__.py @@ -8,7 +8,7 @@ from giturlparse import parse # pip install giturlparse from retrying import retry # pip install retrying import yaml # pip install PyYAML -DEFAULT_CONFIG_FILENAME = '~/.github/fork_github_repo.yaml' +DEFAULT_CONFIG_FILENAME = "~/.github/fork_github_repo.yaml" def github_url_argument(url): @@ -20,11 +20,11 @@ def github_url_argument(url): """ parsed_url = parse(url) if not parsed_url.valid: - raise argparse.ArgumentTypeError('%s is not a valid git URL' - % url) + raise argparse.ArgumentTypeError("%s is not a valid git URL" % url) if not parsed_url.github: - raise argparse.ArgumentTypeError('%s is not a GitHub repo' - % parsed_url.url) + raise argparse.ArgumentTypeError( + "%s is not a GitHub repo" % parsed_url.url + ) return url @@ -41,10 +41,10 @@ def get_config(): """ parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description='''\ + description="""\ Fork a GitHub repo, clone that repo to a local directory, add the upstream -remote, create an optional feature branch and checkout that branch''', - epilog='''\ +remote, create an optional feature branch and checkout that branch""", + epilog="""\ The config file with a default location of ~/.github/fork_github_repo.yaml contains the following settings: @@ -59,27 +59,40 @@ The file is YAML formatted and the contents look like this : github_token: 0123456789abcdef0123456789abcdef01234567 repo_dir: ~/Documents/github.com/example/ organization: -''' +""", ) parser.add_argument( - '-c', '--config', - help='Filename of the yaml config file (default : %s)' - % DEFAULT_CONFIG_FILENAME, + "-c", + "--config", + help="Filename of the yaml config file (default : %s)" + % DEFAULT_CONFIG_FILENAME, default=filename_argument(DEFAULT_CONFIG_FILENAME), - type=filename_argument) - parser.add_argument('url', help="GitHub URL of the upstream repo to fork", - type=github_url_argument) - parser.add_argument('branch', nargs='?', default=None, - help="Name of the feature branch to create") + type=filename_argument, + ) + parser.add_argument( + "url", + help="GitHub URL of the upstream repo to fork", + type=github_url_argument, + ) + parser.add_argument( + "branch", + nargs="?", + default=None, + help="Name of the feature branch to create", + ) args = parser.parse_args() if (args.config == filename_argument(DEFAULT_CONFIG_FILENAME)) and ( - not os.path.isfile(args.config)): - parser.error('Please create a config file at %s or point to one with ' - 'the --config option.' % DEFAULT_CONFIG_FILENAME) + not os.path.isfile(args.config) + ): + parser.error( + "Please create a config file at %s or point to one with " + "the --config option." % DEFAULT_CONFIG_FILENAME + ) if not os.path.isfile(args.config): raise argparse.ArgumentTypeError( - 'Could not find config file %s.' % args.config) - with open(args.config, 'r') as f: + "Could not find config file %s." % args.config + ) + with open(args.config, "r") as f: try: config = yaml.safe_load(f) if isinstance(config, dict): @@ -88,10 +101,12 @@ organization: else: raise argparse.ArgumentTypeError( 'Config contains %s of "%s" but it should be a dict' - % (type(config), config)) + % (type(config), config) + ) except yaml.YAMLError: raise argparse.ArgumentTypeError( - 'Could not parse YAML in %s' % args.config) + "Could not parse YAML in %s" % args.config + ) def get_list_fork_repo(upstream_url, github_token): @@ -105,9 +120,15 @@ def get_list_fork_repo(upstream_url, github_token): def fork_and_clone_repo( - upstream_url, github_token, repo_dir_root, branch_name=None, - upstream_name='upstream', organization_name=None, fork_only=False, - repo_root=None): + upstream_url, + github_token, + repo_dir_root, + branch_name=None, + upstream_name="upstream", + organization_name=None, + fork_only=False, + repo_root=None, +): """Fork a GitHub repo, clone that repo to a local directory, add the upstream remote, create an optional feature branch and checkout that branch @@ -130,26 +151,28 @@ def fork_and_clone_repo( # Fork the repo status, user = gh.user.get() - user_name = user['login'] if not organization_name else organization_name + user_name = user["login"] if not organization_name else organization_name status, forked_repo = gh.repos[user_name][parsed_url.repo].get() if status == 404: - status, upstream_repo = ( - gh.repos[parsed_url.owner][parsed_url.repo].get()) + status, upstream_repo = gh.repos[parsed_url.owner][ + parsed_url.repo + ].get() if status == 404: print("Unable to find repo %s" % upstream_url) exit(1) args = {} if organization_name: args["organization"] = organization_name - status, forked_repo = ( - gh.repos[parsed_url.owner][parsed_url.repo].forks.post(**args)) + status, forked_repo = gh.repos[parsed_url.owner][ + parsed_url.repo + ].forks.post(**args) if status == 404: print("Error when forking repo %s" % forked_repo) exit(1) else: - print("Forked %s to %s" % (upstream_url, forked_repo['html_url'])) + print("Forked %s to %s" % (upstream_url, forked_repo["html_url"])) elif status == 202: - print("Forked repo %s already exists" % forked_repo['full_name']) + print("Forked repo %s already exists" % forked_repo["full_name"]) elif status != 200: print("Status not supported: %s - %s" % (status, forked_repo)) exit(1) @@ -166,24 +189,30 @@ def fork_and_clone_repo( try: if branch_name: submodule_repo = retry( - wait_exponential_multiplier=1000, - stop_max_delay=15000 - )(repo_root.create_submodule)(repo_dir_root, repo_dir_root, - url=http_url, - branch=branch_name) + wait_exponential_multiplier=1000, stop_max_delay=15000 + )(repo_root.create_submodule)( + repo_dir_root, + repo_dir_root, + url=http_url, + branch=branch_name, + ) else: submodule_repo = retry( - wait_exponential_multiplier=1000, - stop_max_delay=15000 - )(repo_root.create_submodule)(repo_dir_root, repo_dir_root, - url=http_url) + wait_exponential_multiplier=1000, stop_max_delay=15000 + )(repo_root.create_submodule)( + repo_dir_root, repo_dir_root, url=http_url + ) except KeyError as e: if os.path.isdir(repo_dir_root): - print(f"Warning, submodule {repo_dir_root} already exist, " - f"you need to add it in stage.") + print( + f"Warning, submodule {repo_dir_root} already exist, " + "you need to add it in stage." + ) else: - print(f"\nERROR Cannot create submodule {repo_dir_root}." - f"Maybe you need to delete .git/modules/{repo_dir_root}\n") + print( + f"\nERROR Cannot create submodule {repo_dir_root}." + f"Maybe you need to delete .git/modules/{repo_dir_root}\n" + ) return # # Delete appropriate submodule and recreate_submodule # shutil.rmtree(f".git/modules/{repo_dir_root}", ignore_errors=True) @@ -219,14 +248,15 @@ def fork_and_clone_repo( # url=bare_repo.git_dir, branch='master') else: if os.path.isdir(repo_dir): - print("Directory %s already exists, assuming it's a clone" % repo_dir) + print( + "Directory %s already exists, assuming it's a clone" % repo_dir + ) cloned_repo = Repo(repo_dir) else: cloned_repo = retry( - wait_exponential_multiplier=1000, - stop_max_delay=15000 - )(Repo.clone_from)(forked_repo['ssh_url'], repo_dir) - print("Cloned %s to %s" % (forked_repo['ssh_url'], repo_dir)) + wait_exponential_multiplier=1000, stop_max_delay=15000 + )(Repo.clone_from)(forked_repo["ssh_url"], repo_dir) + print("Cloned %s to %s" % (forked_repo["ssh_url"], repo_dir)) # Create the remote upstream try: @@ -234,14 +264,14 @@ def fork_and_clone_repo( print('Remote "%s" already exists in %s' % (upstream_name, repo_dir)) except ValueError: upstream_remote = retry( - wait_exponential_multiplier=1000, - stop_max_delay=15000 + wait_exponential_multiplier=1000, stop_max_delay=15000 )(cloned_repo.create_remote)(upstream_name, upstream_url) print('Remote "%s" created for %s' % (upstream_name, upstream_url)) # Fetch the remote upstream retry(wait_exponential_multiplier=1000, stop_max_delay=15000)( - upstream_remote.fetch)() + upstream_remote.fetch + )() print('Remote "%s" fetched' % upstream_name) # Create and checkout the branch @@ -255,19 +285,25 @@ def fork_and_clone_repo( branch = cloned_repo.heads[branch_name] print('Branch "%s" already exists' % branch_name) if branch_name not in cloned_repo.remotes.origin.refs: - cloned_repo.remotes.origin.push(refspec='{}:{}'.format( - branch.path, branch.path)) + cloned_repo.remotes.origin.push( + refspec="{}:{}".format(branch.path, branch.path) + ) print('Branch "%s" pushed to origin' % branch_name) else: print('Branch "%s" already exists in remote origin' % branch_name) if branch.tracking_branch() is None: branch.set_tracking_branch( - cloned_repo.remotes.origin.refs[branch_name]) - print('Tracking branch "%s" setup for branch "%s"' % ( - cloned_repo.remotes.origin.refs[branch_name], branch_name)) + cloned_repo.remotes.origin.refs[branch_name] + ) + print( + 'Tracking branch "%s" setup for branch "%s"' + % (cloned_repo.remotes.origin.refs[branch_name], branch_name) + ) else: - print('Branch "%s" already setup to track "%s"' % ( - branch_name, cloned_repo.remotes.origin.refs[branch_name])) + print( + 'Branch "%s" already setup to track "%s"' + % (branch_name, cloned_repo.remotes.origin.refs[branch_name]) + ) branch.checkout() print('Branch "%s" checked out' % branch_name) return branch diff --git a/script/fork_project.py b/script/fork_project.py index 3837d58..3dc9541 100644 --- a/script/fork_project.py +++ b/script/fork_project.py @@ -6,7 +6,7 @@ import logging from git import Repo from retrying import retry # pip install retrying -new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) +new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) sys.path.append(new_path) from script import fork_github_repo @@ -27,18 +27,30 @@ def get_config(): # TODO update description parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description='''\ -''', - epilog='''\ -''' + description="""\ +""", + epilog="""\ +""", + ) + parser.add_argument( + "-d", + "--dir", + dest="dir", + default="./", + help="Path of repo to change remote, including submodule.", + ) + parser.add_argument( + "--organization", + dest="organization", + default="ERPLibre", + help="Choose organization to fork and change all repo.", + ) + parser.add_argument( + "--github_token", + dest="github_token", + default=config.get(CST_EL_GITHUB_TOKEN), + help="GitHub token generated by user", ) - parser.add_argument('-d', '--dir', dest="dir", default="./", - help="Path of repo to change remote, including submodule.") - parser.add_argument('--organization', dest="organization", default="ERPLibre", - help="Choose organization to fork and change all repo.") - parser.add_argument('--github_token', dest="github_token", - default=config.get(CST_EL_GITHUB_TOKEN), - help="GitHub token generated by user") args = parser.parse_args() return args @@ -76,21 +88,28 @@ def main(): # for remote in working_repo.remotes: if remote_origin: repo_origin_info = GitTool.get_transformed_repo_info_from_url( - remote_origin.url, repo_path=repo.get("relative_path")) + remote_origin.url, repo_path=repo.get("relative_path") + ) if repo_origin_info.organization not in dct_remote_name.keys(): # Add remote upstream_remote = retry( - wait_exponential_multiplier=1000, - stop_max_delay=15000 - )(working_repo.create_remote)(repo_origin_info.organization, - repo_origin_info.url_https) - print('Remote "%s" created for %s' % ( - repo_origin_info.organization, repo_origin_info.url_https)) + wait_exponential_multiplier=1000, stop_max_delay=15000 + )(working_repo.create_remote)( + repo_origin_info.organization, repo_origin_info.url_https + ) + print( + 'Remote "%s" created for %s' + % ( + repo_origin_info.organization, + repo_origin_info.url_https, + ) + ) # Fetch the remote retry(wait_exponential_multiplier=1000, stop_max_delay=15000)( - upstream_remote.fetch)() + upstream_remote.fetch + )() print('Remote "%s" fetched' % repo_origin_info.organization) # working_repo.git.remote.remove("origin") upstream_name = organization_name @@ -99,7 +118,9 @@ def main(): # [, , ] _logger.info( - f"Fork {url} on dir {repo_dir_root} for organization {organization_name}") + f"Fork {url} on dir {repo_dir_root} for organization" + f" {organization_name}" + ) fork_github_repo.fork_and_clone_repo( url, @@ -117,5 +138,5 @@ def main(): GitTool().generate_install_locally() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/script/fork_project_ERPLibre.py b/script/fork_project_ERPLibre.py index dbd198e..10f51e3 100644 --- a/script/fork_project_ERPLibre.py +++ b/script/fork_project_ERPLibre.py @@ -6,7 +6,7 @@ import logging from git import Repo import git -new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) +new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) sys.path.append(new_path) from script.git_tool import GitTool @@ -26,24 +26,41 @@ def get_config(): # TODO update description parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description='''\ -''', - epilog='''\ -''' + description="""\ +""", + epilog="""\ +""", + ) + parser.add_argument( + "-d", + "--dir", + dest="dir", + default="./", + help="Path of repo to change remote, including submodule.", + ) + parser.add_argument( + "--organization", + dest="organization", + default="ERPLibre", + help="Choose organization to fork and change all repo.", + ) + parser.add_argument( + "--skip_fork", + action="store_true", + help="Ignore fork to generate only manifest.", + ) + parser.add_argument( + "-f", "--force", action="store_true", help="Force rewrite remote." + ) + parser.add_argument( + "--fast", action="store_true", help="Ignore if repo already exist." + ) + parser.add_argument( + "--github_token", + dest="github_token", + default=config.get(CST_EL_GITHUB_TOKEN), + help="GitHub token generated by user", ) - parser.add_argument('-d', '--dir', dest="dir", default="./", - help="Path of repo to change remote, including submodule.") - parser.add_argument('--organization', dest="organization", default="ERPLibre", - help="Choose organization to fork and change all repo.") - parser.add_argument("--skip_fork", action="store_true", - help="Ignore fork to generate only manifest.") - parser.add_argument('-f', "--force", action="store_true", - help="Force rewrite remote.") - parser.add_argument("--fast", action="store_true", - help="Ignore if repo already exist.") - parser.add_argument('--github_token', dest="github_token", - default=config.get(CST_EL_GITHUB_TOKEN), - help="GitHub token generated by user") args = parser.parse_args() return args @@ -58,12 +75,21 @@ def main(): raise ValueError("Missing github_token") organization_name = config.organization - lst_repo = git_tool.get_source_repo_addons(repo_path=config.dir, add_repo_root=True) - lst_repo_organization = [git_tool.get_transformed_repo_info_from_url( - a.get("url"), repo_path=config.dir, organization_force=organization_name, - is_submodule=a.get("is_submodule"), sub_path=a.get("sub_path"), - revision=a.get("revision"), clone_depth=a.get("clone_depth")) - for a in lst_repo] + lst_repo = git_tool.get_source_repo_addons( + repo_path=config.dir, add_repo_root=True + ) + lst_repo_organization = [ + git_tool.get_transformed_repo_info_from_url( + a.get("url"), + repo_path=config.dir, + organization_force=organization_name, + is_submodule=a.get("is_submodule"), + sub_path=a.get("sub_path"), + revision=a.get("revision"), + clone_depth=a.get("clone_depth"), + ) + for a in lst_repo + ] if not config.skip_fork: i = 0 @@ -71,7 +97,11 @@ def main(): for repo in lst_repo: i += 1 print(f"Nb element {i}/{total} - {repo.get('project_name')}") - if config.fast and repo.get("is_submodule") and os.path.isdir(repo.get("path")): + if ( + config.fast + and repo.get("is_submodule") + and os.path.isdir(repo.get("path")) + ): continue url = repo.get("url") @@ -83,16 +113,22 @@ def main(): # url, organization_force="ERPLibre", # is_submodule=repo.get("is_submodule"), # sub_path=repo.get("sub_path")) - git_tool.fork_repo(upstream_url=url, - github_token=github_token, - organization_name="ERPLibre") + git_tool.fork_repo( + upstream_url=url, + github_token=github_token, + organization_name="ERPLibre", + ) repo_info = git_tool.get_transformed_repo_info_from_url( - url, organization_force=organization_name, + url, + organization_force=organization_name, is_submodule=repo.get("is_submodule"), - sub_path=repo.get("sub_path")) - git_tool.fork_repo(upstream_url=url, - github_token=github_token, - organization_name=organization_name) + sub_path=repo.get("sub_path"), + ) + git_tool.fork_repo( + upstream_url=url, + github_token=github_token, + organization_name=organization_name, + ) # git_tool.add_and_fetch_remote(repo_info, root_repo=root_repo) continue @@ -104,18 +140,25 @@ def main(): if not remote_erplibre: repo_info = git_tool.get_transformed_repo_info_from_url( - url, organization_force="ERPLibre", + url, + organization_force="ERPLibre", is_submodule=repo.get("is_submodule"), - sub_path=repo.get("sub_path")) + sub_path=repo.get("sub_path"), + ) # git_tool.add_and_fetch_remote(repo_info) - git_tool.fork_repo(url, github_token, - organization_name=organization_name, - ) + git_tool.fork_repo( + url, + github_token, + organization_name=organization_name, + ) repo_info = git_tool.get_transformed_repo_info_from_url( - url, organization_force=organization_name, - is_submodule=repo.get("is_submodule"), sub_path=repo.get("sub_path")) + url, + organization_force=organization_name, + is_submodule=repo.get("is_submodule"), + sub_path=repo.get("sub_path"), + ) if remote_origin: working_repo.git.remote("remove", "origin") repo_info.organization = "origin" @@ -131,10 +174,11 @@ def main(): # Update origin to new repo # git_tool.generate_git_modules(lst_repo_organization, repo_path=config.dir) - git_tool.generate_repo_manifest(lst_repo_organization, - output=f"{config.dir}manifest/default.dev.xml") + git_tool.generate_repo_manifest( + lst_repo_organization, output=f"{config.dir}manifest/default.dev.xml" + ) git_tool.generate_install_locally() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/script/git_change_remote.py b/script/git_change_remote.py index c66bb2e..972b37f 100755 --- a/script/git_change_remote.py +++ b/script/git_change_remote.py @@ -7,7 +7,7 @@ from git import Repo from retrying import retry # pip install retrying -new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) +new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) sys.path.append(new_path) from script import git_tool @@ -27,28 +27,51 @@ def get_config(): # TODO update description parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description='''\ -''', - epilog='''\ -''' + description="""\ +""", + epilog="""\ +""", + ) + parser.add_argument( + "-d", + "--dir", + dest="dir", + default="./", + help="Path of repo to change remote, including submodule.", + ) + parser.add_argument( + "--github_token", + dest="github_token", + default=config.get(CST_EL_GITHUB_TOKEN), + help="GitHub token generated by user", + ) + parser.add_argument( + "--open_web_browser", + action="store_true", + help="Open web browser for each repo.", + ) + parser.add_argument( + "--generate_only_install_locally", + action="store_true", + help="Only generate script install_locally.sh.", + ) + parser.add_argument( + "--sync_to", + dest="sync_to", + help="Only synchronize matching repo, use path compare to repo.", + ) + parser.add_argument( + "--dry_sync", + dest="dry_sync", + action="store_true", + help="Don't apply modification when sync_to.", + ) + parser.add_argument( + "--sync_with_submodule", + dest="sync_with_submodule", + action="store_true", + help="Remote directory use submodule to sync.", ) - parser.add_argument('-d', '--dir', dest="dir", default="./", - help="Path of repo to change remote, including submodule.") - parser.add_argument('--github_token', dest="github_token", - default=config.get(CST_EL_GITHUB_TOKEN), - help="GitHub token generated by user") - parser.add_argument("--open_web_browser", action="store_true", - help="Open web browser for each repo.") - parser.add_argument("--generate_only_install_locally", action="store_true", - help="Only generate script install_locally.sh.") - parser.add_argument("--sync_to", dest="sync_to", - help="Only synchronize matching repo, use path compare to " - "repo.") - parser.add_argument("--dry_sync", dest="dry_sync", action="store_true", - help="Don't apply modification when sync_to.") - parser.add_argument("--sync_with_submodule", dest="sync_with_submodule", - action="store_true", - help="Remote directory use submodule to sync.") args = parser.parse_args() return args @@ -68,9 +91,11 @@ def main(): if config.sync_to[-1] != "/": config.sync_to += "/" gt = git_tool.GitTool() - result = gt.get_matching_repo(repo_compare_to=config.sync_to, - force_normalize_compare=True, - sync_with_submodule=config.sync_with_submodule) + result = gt.get_matching_repo( + repo_compare_to=config.sync_to, + force_normalize_compare=True, + sync_with_submodule=config.sync_with_submodule, + ) gt.sync_to(result, checkout_when_diff=not config.dry_sync) return @@ -109,28 +134,34 @@ def main(): # # print(f"ERROR, missing branch 12.0 for {repo_dir_root}") _logger.info( - f"Fork {url} on dir {repo_dir_root} for organization {organization_name}") + f"Fork {url} on dir {repo_dir_root} for organization" + f" {organization_name}" + ) try: upstream_remote = cloned_repo.remote(upstream_name) - print('Remote "%s" already exists in %s' % - (upstream_name, repo_dir_root)) + print( + 'Remote "%s" already exists in %s' + % (upstream_name, repo_dir_root) + ) except ValueError: upstream_remote = retry( - wait_exponential_multiplier=1000, - stop_max_delay=15000 + wait_exponential_multiplier=1000, stop_max_delay=15000 )(cloned_repo.create_remote)(upstream_name, upstream_url) print('Remote "%s" created for %s' % (upstream_name, upstream_url)) try: # Fetch the remote upstream retry(wait_exponential_multiplier=1000, stop_max_delay=15000)( - upstream_remote.fetch)() + upstream_remote.fetch + )() print('Remote "%s" fetched' % upstream_name) except Exception: - print(f"ERROR git {repo_dir_root} remote {upstream_name} not exist.") + print( + f"ERROR git {repo_dir_root} remote {upstream_name} not exist." + ) upstream_remote.remove(cloned_repo, upstream_name) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/script/git_change_remote_https_to_git.py b/script/git_change_remote_https_to_git.py index a77b50a..c304167 100755 --- a/script/git_change_remote_https_to_git.py +++ b/script/git_change_remote_https_to_git.py @@ -9,7 +9,7 @@ import sys from git import Repo -new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) +new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) sys.path.append(new_path) from script.git_tool import GitTool @@ -27,18 +27,32 @@ def get_config(): # TODO update description parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description='''\ -''', - epilog='''\ -''' + description="""\ +""", + epilog="""\ +""", + ) + parser.add_argument( + "-d", + "--dir", + dest="dir", + default="./", + help="Path of repo to change remote, including submodule.", + ) + parser.add_argument( + "-f", + "--upstream", + dest="upstream", + help=( + "Upstream name to change address https to git. " + "When empty, all upstream is updated." + ), + ) + parser.add_argument( + "--git_to_https", + action="store_true", + help="Replace all repo git to https.", ) - parser.add_argument('-d', '--dir', dest="dir", default="./", - help="Path of repo to change remote, including submodule.") - parser.add_argument('-f', '--upstream', dest="upstream", - help="Upstream name to change address https to git. " - "When empty, all upstream is updated.") - parser.add_argument("--git_to_https", action="store_true", - help="Replace all repo git to https.") args = parser.parse_args() return args @@ -54,10 +68,16 @@ def main(): for repo in lst_repo: i += 1 print(f"Nb element {i}/{total}") - repo_sm = Repo(repo.get("name")) + repo_name = repo.get("name") + relative_path = os.path.join(new_path, repo_name) + if not os.path.isdir(relative_path): + print(f"Ignore repo {relative_path}") + continue + repo_sm = Repo(repo_name) if upstream_name: - remote_upstream_name = [a for a in repo_sm.remotes - if upstream_name == a.name] + remote_upstream_name = [ + a for a in repo_sm.remotes if upstream_name == a.name + ] else: remote_upstream_name = [a for a in repo_sm.remotes] @@ -68,5 +88,5 @@ def main(): print(f'Remote "{remote.name}" update for {new_url}') -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/script/git_diff_repo_manifest.py b/script/git_diff_repo_manifest.py index 9e92e1d..35a5064 100755 --- a/script/git_diff_repo_manifest.py +++ b/script/git_diff_repo_manifest.py @@ -5,7 +5,7 @@ import argparse import logging from git import Repo -new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) +new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) sys.path.append(new_path) from script.git_tool import GitTool @@ -24,13 +24,19 @@ def get_config(): formatter_class=argparse.RawDescriptionHelpFormatter, description="""Get git diff between manifest repo revision, diff revision input1 to input2 """, - epilog='''\ -''' + epilog="""\ +""", + ) + parser.add_argument( + "--input1", + required=True, + help="Compare input1 to input2. Input1 is older config.", + ) + parser.add_argument( + "--input2", + required=True, + help="Compare input1 to input2. Input2 is newer config.", ) - parser.add_argument('--input1', required=True, - help="Compare input1 to input2. Input1 is older config.") - parser.add_argument('--input2', required=True, - help="Compare input1 to input2. Input2 is newer config.") # parser.add_argument('--clear', action="store_true", # help="Create a new manifest and clear old configuration.") args = parser.parse_args() @@ -41,10 +47,16 @@ def main(): config = get_config() git_tool = GitTool() - dct_remote_1, dct_project_1, default_remote_1 = git_tool.get_manifest_xml_info( - filename=config.input1, add_root=True) - dct_remote_2, dct_project_2, default_remote_2 = git_tool.get_manifest_xml_info( - filename=config.input2, add_root=True) + ( + dct_remote_1, + dct_project_1, + default_remote_1, + ) = git_tool.get_manifest_xml_info(filename=config.input1, add_root=True) + ( + dct_remote_2, + dct_project_2, + default_remote_2, + ) = git_tool.get_manifest_xml_info(filename=config.input2, add_root=True) set_project_1 = set(dct_project_1.keys()) set_project_2 = set(dct_project_2.keys()) @@ -75,14 +87,18 @@ def main(): path1 = value1.get("@path") path2 = value2.get("@path") if path1 != path2: - print(f"WARNING id {i}, path of git are different. " - f"Input1 {path1}, input2 {path2}") + print( + f"WARNING id {i}, path of git are different. " + f"Input1 {path1}, input2 {path2}" + ) continue i += 1 result = "same" if old_revision == new_revision else "diff" - print(f"{i}/{total} - {result} - " - f"{path1} {key} old {old_revision} new {new_revision}") + print( + f"{i}/{total} - {result} - " + f"{path1} {key} old {old_revision} new {new_revision}" + ) default_arg = [f"{old_revision}..{new_revision}"] if old_revision != new_revision: # get git diff @@ -91,5 +107,5 @@ def main(): print(status) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/script/git_merge_repo_manifest.py b/script/git_merge_repo_manifest.py index 7530044..865c366 100755 --- a/script/git_merge_repo_manifest.py +++ b/script/git_merge_repo_manifest.py @@ -5,7 +5,7 @@ import argparse import logging import copy -new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) +new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) sys.path.append(new_path) from script.git_tool import GitTool @@ -23,15 +23,18 @@ def get_config(): parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description="""Replace revision field in input2 from input1 if existing, create an output of new manifest.""", - epilog='''\ -''' + epilog="""\ +""", + ) + parser.add_argument( + "--input1", required=True, help="First manifest to merge into input2." + ) + parser.add_argument( + "--input2", required=True, help="Second manifest, overwrite by input1." + ) + parser.add_argument( + "--output", required=True, help="Output of new manifest" ) - parser.add_argument('--input1', required=True, - help="First manifest to merge into input2.") - parser.add_argument('--input2', required=True, - help="Second manifest, overwrite by input1.") - parser.add_argument('--output', required=True, - help="Output of new manifest") # parser.add_argument('--clear', action="store_true", # help="Create a new manifest and clear old configuration.") args = parser.parse_args() @@ -42,10 +45,16 @@ def main(): config = get_config() git_tool = GitTool() - dct_remote_1, dct_project_1, default_remote_1 = git_tool.get_manifest_xml_info( - filename=config.input1, add_root=True) - dct_remote_2, dct_project_2, default_remote_2 = git_tool.get_manifest_xml_info( - filename=config.input2, add_root=True) + ( + dct_remote_1, + dct_project_1, + default_remote_1, + ) = git_tool.get_manifest_xml_info(filename=config.input1, add_root=True) + ( + dct_remote_2, + dct_project_2, + default_remote_2, + ) = git_tool.get_manifest_xml_info(filename=config.input2, add_root=True) dct_remote_3 = copy.deepcopy(dct_remote_2) dct_project_3 = copy.deepcopy(dct_project_2) @@ -59,10 +68,13 @@ def main(): dct_project_3[key]["@dest-branch"] = "12.0" # Update origin to new repo - git_tool.generate_repo_manifest(dct_remote=dct_remote_3, dct_project=dct_project_3, - output=config.output, - default_remote=default_remote_2) + git_tool.generate_repo_manifest( + dct_remote=dct_remote_3, + dct_project=dct_project_3, + output=config.output, + default_remote=default_remote_2, + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/script/git_repo_manifest.py b/script/git_repo_manifest.py index cf6b390..913d006 100755 --- a/script/git_repo_manifest.py +++ b/script/git_repo_manifest.py @@ -4,7 +4,7 @@ import sys import argparse import logging -new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) +new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) sys.path.append(new_path) from script.git_tool import GitTool @@ -21,17 +21,29 @@ def get_config(): # TODO update description parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description='''\ -''', - epilog='''\ -''' + description="""\ +""", + epilog="""\ +""", + ) + parser.add_argument( + "-d", + "--dir", + dest="dir", + default="./", + help="Path of repo to change remote, including submodule.", + ) + parser.add_argument( + "--clear", + action="store_true", + help="Create a new manifest and clear old configuration.", + ) + parser.add_argument( + "-m", + "--manifest", + default="manifest/default.dev.xml", + help="The manifest file path to generate.", ) - parser.add_argument('-d', '--dir', dest="dir", default="./", - help="Path of repo to change remote, including submodule.") - parser.add_argument('--clear', action="store_true", - help="Create a new manifest and clear old configuration.") - parser.add_argument('-m', '--manifest', default="manifest/default.dev.xml", - help="The manifest file path to generate.") args = parser.parse_args() return args @@ -40,26 +52,39 @@ def main(): config = get_config() git_tool = GitTool() - lst_repo = git_tool.get_source_repo_addons(repo_path=config.dir, add_repo_root=True) - lst_repo_organization = [git_tool.get_transformed_repo_info_from_url( - a.get("url"), repo_path=config.dir, get_obj=True, - is_submodule=a.get("is_submodule"), sub_path=a.get("sub_path"), - revision=a.get("revision"), clone_depth=a.get("clone_depth")) - for a in lst_repo] + lst_repo = git_tool.get_source_repo_addons( + repo_path=config.dir, add_repo_root=True + ) + lst_repo_organization = [ + git_tool.get_transformed_repo_info_from_url( + a.get("url"), + repo_path=config.dir, + get_obj=True, + is_submodule=a.get("is_submodule"), + sub_path=a.get("sub_path"), + revision=a.get("revision"), + clone_depth=a.get("clone_depth"), + ) + for a in lst_repo + ] # Update origin to new repo if not config.clear: dct_remote, dct_project, _ = git_tool.get_manifest_xml_info( - repo_path=config.dir, add_root=True) + repo_path=config.dir, add_root=True + ) else: dct_remote = {} dct_project = {} - git_tool.generate_repo_manifest(lst_repo_organization, - output=f"{config.dir}{config.manifest}", - dct_remote=dct_remote, dct_project=dct_project, - keep_original=True) + git_tool.generate_repo_manifest( + lst_repo_organization, + output=f"{config.dir}{config.manifest}", + dct_remote=dct_remote, + dct_project=dct_project, + keep_original=True, + ) git_tool.generate_install_locally() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/script/git_repo_update_group.py b/script/git_repo_update_group.py index 5c160f5..63614f2 100755 --- a/script/git_repo_update_group.py +++ b/script/git_repo_update_group.py @@ -6,7 +6,7 @@ import logging from git import Repo import git -new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) +new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) sys.path.append(new_path) from script.git_tool import GitTool @@ -25,14 +25,17 @@ def get_config(): # TODO update description parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description='''\ + description="""\ Update config.conf file with group specified in manifest file. -''', - epilog='''\ -''' +""", + epilog="""\ +""", + ) + parser.add_argument( + "--group", + default="", + help="Prod by default, use 'dev' for manifest/default.dev.xml", ) - parser.add_argument('--group', default="", - help="Prod by default, use 'dev' for manifest/default.dev.xml") args = parser.parse_args() return args @@ -46,5 +49,5 @@ def main(): git_tool.generate_install_locally(filter_group=filter_group) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/script/git_show_code_diff_repo_manifest.py b/script/git_show_code_diff_repo_manifest.py index d8e4741..429c186 100755 --- a/script/git_show_code_diff_repo_manifest.py +++ b/script/git_show_code_diff_repo_manifest.py @@ -6,7 +6,7 @@ import logging from git import Repo from git.exc import GitCommandError -new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) +new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) sys.path.append(new_path) from script.git_tool import GitTool @@ -24,11 +24,15 @@ def get_config(): parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description="""Compare actual code with a manifest.""", - epilog='''\ -''' + epilog="""\ +""", + ) + parser.add_argument( + "-m", + "--manifest", + required=True, + help="The manifest to compare with actual code.", ) - parser.add_argument('-m', '--manifest', required=True, - help="The manifest to compare with actual code.") args = parser.parse_args() return args @@ -37,8 +41,12 @@ def main(): config = get_config() git_tool = GitTool() - dct_remote, dct_project, default_remote = git_tool.get_manifest_xml_info(filename=config.manifest, add_root=True) - default_branch_name = default_remote.get("@revision", git_tool.default_branch) + dct_remote, dct_project, default_remote = git_tool.get_manifest_xml_info( + filename=config.manifest, add_root=True + ) + default_branch_name = default_remote.get( + "@revision", git_tool.default_branch + ) i = 0 total = len(dct_project) for name, project in dct_project.items(): @@ -57,7 +65,9 @@ def main(): # TODO maybe need to check divergence with local branch and not remote branch commit_head = git_repo.git.rev_parse("HEAD") try: - commit_branch = git_repo.git.rev_parse(f"{organization}/{branch_name}") + commit_branch = git_repo.git.rev_parse( + f"{organization}/{branch_name}" + ) except GitCommandError: print("ERROR Something wrong with this repo.") continue @@ -66,10 +76,13 @@ def main(): else: print("PASS Not on specified branch, no divergence.") elif branch_name != value: - print(f"ERROR, manifest revision is {branch_name} and actual revision is {value}.") + print( + f"ERROR, manifest revision is {branch_name} and actual" + f" revision is {value}." + ) else: print("PASS") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/script/git_tool.py b/script/git_tool.py index 327d865..296e25d 100644 --- a/script/git_tool.py +++ b/script/git_tool.py @@ -59,13 +59,17 @@ class GitTool: return url, url_https, url_git - def get_transformed_repo_info_from_url(self, url: str, repo_path: str = "./", - get_obj: bool = True, - is_submodule: bool = True, - organization_force: str = None, - sub_path: str = "addons", - revision: str = "", - clone_depth: str = "") -> object: + def get_transformed_repo_info_from_url( + self, + url: str, + repo_path: str = "./", + get_obj: bool = True, + is_submodule: bool = True, + organization_force: str = None, + sub_path: str = "addons", + revision: str = "", + clone_depth: str = "", + ) -> object: """ :param url: @@ -99,8 +103,8 @@ class GitTool: relative_path = os.path.normpath(relative_path) original_organization = organization - url_https_original_organization = url_https[:url_https.rfind("/")] - project_name = url_https[url_https.rfind("/") + 1:] + url_https_original_organization = url_https[: url_https.rfind("/")] + project_name = url_https[url_https.rfind("/") + 1 :] # begin_original = url_git[url_git.find(":") + 1:] # original_organization = begin_original[:begin_original.find("/")] if organization_force: @@ -109,7 +113,7 @@ class GitTool: url_split[3] = organization url_https = "/".join(url_split) url, _, url_git = self.get_url(url_https) - url_https_organization = url_https[:url_https.rfind("/")] + url_https_organization = url_https[: url_https.rfind("/")] d = { "url": url, @@ -132,15 +136,26 @@ class GitTool: return Struct(**d) return d - def get_repo_info(self, repo_path: str = "./", - add_root: bool = False, is_manifest: bool = True, filter_group=None): + def get_repo_info( + self, + repo_path: str = "./", + add_root: bool = False, + is_manifest: bool = True, + filter_group=None, + ): if is_manifest: - return self.get_repo_info_manifest_xml(repo_path=repo_path, - add_root=add_root, filter_group=filter_group) - return self.get_repo_info_submodule(repo_path=repo_path, add_root=add_root) + return self.get_repo_info_manifest_xml( + repo_path=repo_path, + add_root=add_root, + filter_group=filter_group, + ) + return self.get_repo_info_submodule( + repo_path=repo_path, add_root=add_root + ) - def get_repo_info_submodule(self, repo_path: str = "./", - add_root: bool = False) -> list: + def get_repo_info_submodule( + self, repo_path: str = "./", add_root: bool = False + ) -> list: """ Get information about submodule from repo_path :param repo_path: path of repo to get information about submodule @@ -166,7 +181,7 @@ class GitTool: first_execution = True for line in txt: no_line += 1 - if line[:12] == "[submodule \"": + if line[:12] == '[submodule "': if not first_execution: data = { "url": url, @@ -220,8 +235,9 @@ class GitTool: lst_repo = sorted(lst_repo, key=lambda k: k.get("name")) return lst_repo - def get_repo_info_manifest_xml(self, repo_path: str = "./", - add_root: bool = False, filter_group=None) -> list: + def get_repo_info_manifest_xml( + self, repo_path: str = "./", add_root: bool = False, filter_group=None + ) -> list: """ Get information about manifest of Repo from repo_path :param repo_path: path of repo to get information about submodule @@ -255,7 +271,7 @@ class GitTool: dct_remote = {a.get("@name"): a.get("@fetch") for a in lst_remote} for project in lst_project: groups = project.get("@groups") - lst_group = groups.split(',') if groups else [] + lst_group = groups.split(",") if groups else [] # Continue if lst_filter exist and group in filter for group in lst_group: if lst_filter_group and group not in lst_filter_group: @@ -290,9 +306,11 @@ class GitTool: try: url = repo_root.git.remote("get-url", "origin") except Exception as e: - print(f"WARNING: Missing origin remote, use default url " - f"{DEFAULT_REMOTE_URL}. Suggest to add a remote origin: \n" - f"> git remote add origin {DEFAULT_REMOTE_URL}") + print( + "WARNING: Missing origin remote, use default url " + f"{DEFAULT_REMOTE_URL}. Suggest to add a remote origin: \n" + f"> git remote add origin {DEFAULT_REMOTE_URL}" + ) url = DEFAULT_REMOTE_URL url, url_https, url_git = self.get_url(url) @@ -308,8 +326,9 @@ class GitTool: lst_repo = sorted(lst_repo, key=lambda k: k.get("name")) return lst_repo - def get_manifest_xml_info(self, repo_path: str = "./", filename=None, - add_root: bool = False) -> list: + def get_manifest_xml_info( + self, repo_path: str = "./", filename=None, add_root: bool = False + ) -> list: """ Get contain of manifest :param repo_path: path of repo to get information about submodule @@ -366,15 +385,22 @@ class GitTool: def generate_install_locally(self, repo_path="./", filter_group=None): filename_locally = f"{repo_path}script/install_locally.sh" - lst_repo = self.get_repo_info(repo_path=repo_path, filter_group=filter_group) + lst_repo = self.get_repo_info( + repo_path=repo_path, filter_group=filter_group + ) lst_result = [] for repo in lst_repo: # Exception, ignore addons/OCA_web and root - if "addons/OCA_web" == repo.get("path") or \ - "odoo" == repo.get("path"): + if ( + "addons/OCA_web" == repo.get("path") + or "odoo" == repo.get("path") + or "ERPLibre_image_db" == repo.get("path") + ): continue - str_repo = f' printf "${{EL_HOME}}/{repo.get("path")}," >> ' \ - f'${{EL_CONFIG_FILE}}\n' + str_repo = ( + f' printf "${{EL_HOME}}/{repo.get("path")}," >> ' + "${EL_CONFIG_FILE}\n" + ) lst_result.append(str_repo) with open(filename_locally) as file: all_lines = file.readlines() @@ -383,8 +409,10 @@ class GitTool: find_index = False index_find = 0 for line in all_lines: - if not find_index and \ - "if [[ ${EL_MINIMAL_ADDONS} = \"False\" ]]; then\n" == line: + if ( + not find_index + and 'if [[ ${EL_MINIMAL_ADDONS} = "False" ]]; then\n' == line + ): index_find = index + 1 for insert_line in lst_result: all_lines.insert(index_find, insert_line) @@ -398,8 +426,10 @@ class GitTool: index += 1 if not find_index: - print(f"ERROR cannot regenerate file {filename_locally}, " - f"did you change the header?") + print( + f"ERROR cannot regenerate file {filename_locally}, " + "did you change the header?" + ) # create file with open(filename_locally, mode="w") as file: @@ -409,9 +439,15 @@ class GitTool: def str_insert(source_str, insert_str, pos): return source_str[:pos] + insert_str + source_str[pos:] - def generate_repo_manifest(self, lst_repo: List[Struct] = [], output: str = "", - dct_remote={}, dct_project={}, default_remote=None, - keep_original=False): + def generate_repo_manifest( + self, + lst_repo: List[Struct] = [], + output: str = "", + dct_remote={}, + dct_project={}, + default_remote=None, + keep_original=False, + ): """ Generate repo manifest :param lst_repo: optional, update manifest with list_repo @@ -424,7 +460,9 @@ class GitTool: :return: """ if not output: - raise Exception("Cannot generate manifest with missing output filename.") + raise Exception( + "Cannot generate manifest with missing output filename." + ) lst_remote = [] lst_remote_name = [] lst_project = [] @@ -433,28 +471,40 @@ class GitTool: # Fill with configuration for dct_value in dct_remote.values(): - lst_remote.append(OrderedDict( - [('@name', dct_value.get("@name")), - ('@fetch', dct_value.get("@fetch"))] - )) + lst_remote.append( + OrderedDict( + [ + ("@name", dct_value.get("@name")), + ("@fetch", dct_value.get("@fetch")), + ] + ) + ) lst_remote_name.append(dct_value.get("@name")) for dct_value in dct_project.values(): lst_project_info = [ - ('@name', dct_value.get("@name")), - ('@path', dct_value.get("@path")), + ("@name", dct_value.get("@name")), + ("@path", dct_value.get("@path")), ] if "@remote" in dct_value.keys(): - lst_project_info.append(('@remote', dct_value.get("@remote"))) + lst_project_info.append(("@remote", dct_value.get("@remote"))) if "@revision" in dct_value.keys(): - lst_project_info.append(('@revision', dct_value.get("@revision"))) + lst_project_info.append( + ("@revision", dct_value.get("@revision")) + ) if "@clone-depth" in dct_value.keys(): - lst_project_info.append(('@clone-depth', dct_value.get("@clone-depth"))) + lst_project_info.append( + ("@clone-depth", dct_value.get("@clone-depth")) + ) if "@groups" in dct_value.keys(): - lst_project_info.append(('@groups', dct_value.get("@groups"))) + lst_project_info.append(("@groups", dct_value.get("@groups"))) if "@upstream" in dct_value.keys(): - lst_project_info.append(('@upstream', dct_value.get("@upstream"))) + lst_project_info.append( + ("@upstream", dct_value.get("@upstream")) + ) if "@dest-branch" in dct_value.keys(): - lst_project_info.append(('@dest-branch', dct_value.get("@dest-branch"))) + lst_project_info.append( + ("@dest-branch", dct_value.get("@dest-branch")) + ) lst_project.append(OrderedDict(lst_project_info)) lst_project_name.append(dct_value.get("@name")) @@ -463,64 +513,97 @@ class GitTool: if not repo.is_submodule: # Default if lst_default: - raise Exception("Cannot have many root repo. " - "Validate why 2 or more is not submodule.") - lst_default.append(OrderedDict([ - ('@remote', repo.original_organization), - ('@revision', DEFAULT_BRANCH), - ('@sync-j', "4"), - ('@sync-c', "true"), - ])) + raise Exception( + "Cannot have many root repo. " + "Validate why 2 or more is not submodule." + ) + lst_default.append( + OrderedDict( + [ + ("@remote", repo.original_organization), + ("@revision", DEFAULT_BRANCH), + ("@sync-j", "4"), + ("@sync-c", "true"), + ] + ) + ) else: - if keep_original and repo.project_name not in dct_project.keys(): + if ( + keep_original + and repo.project_name not in dct_project.keys() + ): # Exception, create a new remote to keep tracking on original - original_organization = f"{repo.original_organization}_origin" + original_organization = ( + f"{repo.original_organization}_origin" + ) else: original_organization = repo.original_organization # Add remote, only unique remote if original_organization not in lst_remote_name: - lst_remote.append(OrderedDict( - [('@name', original_organization), - ('@fetch', repo.url_https_organization + "/")] - )) + lst_remote.append( + OrderedDict( + [ + ("@name", original_organization), + ("@fetch", repo.url_https_organization + "/"), + ] + ) + ) lst_remote_name.append(repo.original_organization) # Add project, only unique project if repo.project_name not in lst_project_name: lst_project_name.append(repo.project_name) lst_project_info = [ - ('@name', repo.project_name), - ('@path', repo.path), - ('@remote', original_organization), + ("@name", repo.project_name), + ("@path", repo.path), + ("@remote", original_organization), ] if repo.revision: - lst_project_info.append(('@revision', repo.revision)) + lst_project_info.append(("@revision", repo.revision)) if repo.clone_depth: - lst_project_info.append(('@clone-depth', repo.clone_depth)) + lst_project_info.append( + ("@clone-depth", repo.clone_depth) + ) if repo.sub_path == "addons": - lst_project_info.append(('@groups', "addons")) + lst_project_info.append(("@groups", "addons")) else: - lst_project_info.append(('@groups', "odoo")) + lst_project_info.append(("@groups", "odoo")) lst_project.append(OrderedDict(lst_project_info)) if default_remote and not lst_default: - lst_default.append(OrderedDict([ - ('@remote', default_remote.get("@remote")), - ('@revision', DEFAULT_BRANCH), - ('@sync-j', "4"), - ('@sync-c', "true"), - ])) + lst_default.append( + OrderedDict( + [ + ("@remote", default_remote.get("@remote")), + ("@revision", DEFAULT_BRANCH), + ("@sync-j", "4"), + ("@sync-c", "true"), + ] + ) + ) # Order in alphabetic lst_order_remote = sorted(lst_remote, key=lambda key: key.get("@name")) - lst_order_default = sorted(lst_default, key=lambda key: key.get("@remote")) - lst_order_project = sorted(lst_project, key=lambda key: key.get("@name")) + lst_order_default = sorted( + lst_default, key=lambda key: key.get("@remote") + ) + lst_order_project = sorted( + lst_project, key=lambda key: key.get("@name") + ) dct_repo = OrderedDict( - [('manifest', OrderedDict([ - ('remote', lst_order_remote), - ('default', lst_order_default), - ('project', lst_order_project), - ]))]) + [ + ( + "manifest", + OrderedDict( + [ + ("remote", lst_order_remote), + ("default", lst_order_default), + ("project", lst_order_project), + ] + ), + ) + ] + ) str_xml_text = xmltodict.unparse(dct_repo, pretty=True) pos_insert = str_xml_text.rfind("") @@ -541,20 +624,26 @@ class GitTool: str_xml_text = str_xml_text.replace("> 5 and url[0:4] == "http" and url[-4:] != ".git": url = f"{url}.git" - repo_info = self.get_transformed_repo_info_from_url(url, - repo_path=repo_path, - get_obj=False, - sub_path=path, - revision=revision, - clone_depth=clone_depth) + repo_info = self.get_transformed_repo_info_from_url( + url, + repo_path=repo_path, + get_obj=False, + sub_path=path, + revision=revision, + clone_depth=clone_depth, + ) lst_result.append(repo_info) return lst_result @@ -634,11 +726,18 @@ class GitTool: with open(file) as xml: xml_as_string = xml.read() xml_dict = xmltodict.parse(xml_as_string, dict_constructor=dict) - manifest_filename = xml_dict.get("manifest").get("include").get("@name") + manifest_filename = ( + xml_dict.get("manifest").get("include").get("@name") + ) return manifest_filename - def get_matching_repo(self, actual_repo="./", repo_compare_to="./", - force_normalize_compare=False, sync_with_submodule=False): + def get_matching_repo( + self, + actual_repo="./", + repo_compare_to="./", + force_normalize_compare=False, + sync_with_submodule=False, + ): """ Compare repo with .gitmodules files :param actual_repo: @@ -653,12 +752,15 @@ class GitTool: # set_actual_repo = set( # [a[a.find("_") + 1:] for a in dct_repo_info_actual.keys()]) - dct_repo_info_actual_adapted = {key[key.find("_") + 1:]: item for key, item in - dct_repo_info_actual.items()} + dct_repo_info_actual_adapted = { + key[key.find("_") + 1 :]: item + for key, item in dct_repo_info_actual.items() + } set_actual_repo = set(dct_repo_info_actual_adapted.keys()) - lst_repo_info_compare = self.get_repo_info(repo_compare_to, - is_manifest=not sync_with_submodule) + lst_repo_info_compare = self.get_repo_info( + repo_compare_to, is_manifest=not sync_with_submodule + ) if force_normalize_compare: for repo_info in lst_repo_info_compare: url_https = repo_info.get("url_https") @@ -671,7 +773,9 @@ class GitTool: name = f"{repo_name}" repo_info["name"] = name - dct_repo_info_compare = {a.get("name"): a for a in lst_repo_info_compare} + dct_repo_info_compare = { + a.get("name"): a for a in lst_repo_info_compare + } set_compare = set(dct_repo_info_compare.keys()) # TODO finish the match @@ -681,16 +785,17 @@ class GitTool: lst_same_name_normalize = set_actual_repo.intersection(set_compare) lst_missing_name_normalize = set_compare.difference(set_actual_repo) lst_over_name_normalize = set_actual_repo.difference(set_compare) - print(f"Has {len(lst_same_name_normalize)} sames, " - f"{len(lst_missing_name_normalize)} missing, " - f"{len(lst_over_name_normalize)} more.") + print( + f"Has {len(lst_same_name_normalize)} sames, " + f"{len(lst_missing_name_normalize)} missing, " + f"{len(lst_over_name_normalize)} more." + ) lst_match = [] for key in lst_same_name_normalize: - lst_match.append(( - dct_repo_info_actual_adapted[key], - dct_repo_info_compare[key] - )) + lst_match.append( + (dct_repo_info_actual_adapted[key], dct_repo_info_compare[key]) + ) return lst_match, lst_missing_name_normalize, lst_over_name_normalize @@ -728,14 +833,18 @@ class GitTool: repo_compare = Repo(compare_to.get("relative_path")) commit_compare = repo_compare.head.object.hexsha if commit_original != commit_compare: - print(f"DIFF - {original.get('name')} - O {commit_original} - " - f"R {commit_compare}") + print( + f"DIFF - {original.get('name')} - O {commit_original} - " + f"R {commit_compare}" + ) lst_diff.append((original, compare_to)) if checkout_when_diff: # Update all remote for remote in repo_original.remotes: - retry(wait_exponential_multiplier=1000, stop_max_delay=15000)( - remote.fetch)() + retry( + wait_exponential_multiplier=1000, + stop_max_delay=15000, + )(remote.fetch)() repo_original.git.checkout(commit_compare) else: print(f"SAME - {original.get('name')}") @@ -743,8 +852,9 @@ class GitTool: print(f"finish same {len(lst_same)}, diff {len(lst_diff)}") @staticmethod - def add_and_fetch_remote(repo_info: Struct, root_repo: Repo = None, - branch_name: str = ""): + def add_and_fetch_remote( + repo_info: Struct, root_repo: Repo = None, branch_name: str = "" + ): """ Deprecated function, not use anymore git submodule :param repo_info: @@ -754,42 +864,57 @@ class GitTool: """ try: working_repo = Repo(repo_info.relative_path) - if repo_info.organization in [a.name for a in working_repo.remotes]: - print(f"Remote \"{repo_info.organization}\" already exist " - f"in {repo_info.relative_path}") + if repo_info.organization in [ + a.name for a in working_repo.remotes + ]: + print( + f'Remote "{repo_info.organization}" already exist ' + f"in {repo_info.relative_path}" + ) return except git.NoSuchPathError: print(f"New repo {repo_info.relative_path}") if not root_repo: - print(f"Missing git repository to root for repo {repo_info.path}") + print( + f"Missing git repository to root for repo {repo_info.path}" + ) return if branch_name: submodule_repo = retry( - wait_exponential_multiplier=1000, - stop_max_delay=15000 - )(root_repo.create_submodule)(repo_info.path, repo_info.path, - url=repo_info.url_https, - branch=branch_name) + wait_exponential_multiplier=1000, stop_max_delay=15000 + )(root_repo.create_submodule)( + repo_info.path, + repo_info.path, + url=repo_info.url_https, + branch=branch_name, + ) else: submodule_repo = retry( - wait_exponential_multiplier=1000, - stop_max_delay=15000 - )(root_repo.create_submodule)(repo_info.path, repo_info.path, - url=repo_info.url_https) + wait_exponential_multiplier=1000, stop_max_delay=15000 + )(root_repo.create_submodule)( + repo_info.path, repo_info.path, url=repo_info.url_https + ) return # Add remote - upstream_remote = retry(wait_exponential_multiplier=1000, stop_max_delay=15000)( - working_repo.create_remote)(repo_info.organization, repo_info.url_https) - print('Remote "%s" created for %s' % ( - repo_info.organization, repo_info.url_https)) + upstream_remote = retry( + wait_exponential_multiplier=1000, stop_max_delay=15000 + )(working_repo.create_remote)( + repo_info.organization, repo_info.url_https + ) + print( + 'Remote "%s" created for %s' + % (repo_info.organization, repo_info.url_https) + ) # Fetch the remote retry(wait_exponential_multiplier=1000, stop_max_delay=15000)( - upstream_remote.fetch)() + upstream_remote.fetch + )() print('Remote "%s" fetched' % repo_info.organization) - def get_pull_request_repo(self, upstream_url: str, github_token: str, - organization_name: str = ""): + def get_pull_request_repo( + self, upstream_url: str, github_token: str, organization_name: str = "" + ): """ :param upstream_url: @@ -802,7 +927,9 @@ class GitTool: # Fork the repo status, user = gh.user.get() - user_name = user['login'] if not organization_name else organization_name + user_name = ( + user["login"] if not organization_name else organization_name + ) status, lst_pull = gh.repos[user_name][parsed_url.repo].pulls.get() if type(lst_pull) is dict: print(f"For url {upstream_url}, got {lst_pull.get('message')}") @@ -812,34 +939,41 @@ class GitTool: print(pull.get("html_url")) return lst_pull - def fork_repo(self, upstream_url: str, github_token: str, - organization_name: str = ""): + def fork_repo( + self, upstream_url: str, github_token: str, organization_name: str = "" + ): # https://developer.github.com/apps/building-integrations/setting-up-and-registering-oauth-apps/about-scopes-for-oauth-apps/ gh = GitHub(token=github_token) parsed_url = parse(upstream_url) # Fork the repo status, user = gh.user.get() - user_name = user['login'] if not organization_name else organization_name + user_name = ( + user["login"] if not organization_name else organization_name + ) status, forked_repo = gh.repos[user_name][parsed_url.repo].get() if status == 404: - status, upstream_repo = ( - gh.repos[parsed_url.owner][parsed_url.repo].get()) + status, upstream_repo = gh.repos[parsed_url.owner][ + parsed_url.repo + ].get() if status == 404: print("Unable to find repo %s" % upstream_url) exit(1) args = {} if organization_name: args["organization"] = organization_name - status, forked_repo = ( - gh.repos[parsed_url.owner][parsed_url.repo].forks.post(**args)) + status, forked_repo = gh.repos[parsed_url.owner][ + parsed_url.repo + ].forks.post(**args) if status == 404: print("Error when forking repo %s" % forked_repo) exit(1) else: - print("Forked %s to %s" % (upstream_url, forked_repo['html_url'])) + print( + "Forked %s to %s" % (upstream_url, forked_repo["html_url"]) + ) elif status == 202: - print("Forked repo %s already exists" % forked_repo['full_name']) + print("Forked repo %s already exists" % forked_repo["full_name"]) elif status != 200: print("Status not supported: %s - %s" % (status, forked_repo)) exit(1) diff --git a/script/git_update_repo.py b/script/git_update_repo.py index 8ceb1cb..fef6edd 100755 --- a/script/git_update_repo.py +++ b/script/git_update_repo.py @@ -6,7 +6,7 @@ import logging from git import Repo # pip install gitpython from retrying import retry # pip install retrying -new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) +new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) sys.path.append(new_path) from script.git_tool import GitTool @@ -23,13 +23,18 @@ def get_config(): # TODO update description parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description='''\ -''', - epilog='''\ -''' + description="""\ +""", + epilog="""\ +""", + ) + parser.add_argument( + "-d", + "--dir", + dest="dir", + default="./", + help="Path of repo to change remote, including submodule.", ) - parser.add_argument('-d', '--dir', dest="dir", default="./", - help="Path of repo to change remote, including submodule.") args = parser.parse_args() return args @@ -38,13 +43,21 @@ def main(): config = get_config() git_tool = GitTool() - lst_repo = git_tool.get_source_repo_addons(repo_path=config.dir, - add_repo_root=False) - lst_repo_organization = [git_tool.get_transformed_repo_info_from_url( - a.get("url"), repo_path=config.dir, get_obj=True, - is_submodule=a.get("is_submodule"), sub_path=a.get("sub_path"), - revision=a.get("revision"), clone_depth=a.get("clone_depth")) - for a in lst_repo] + lst_repo = git_tool.get_source_repo_addons( + repo_path=config.dir, add_repo_root=False + ) + lst_repo_organization = [ + git_tool.get_transformed_repo_info_from_url( + a.get("url"), + repo_path=config.dir, + get_obj=True, + is_submodule=a.get("is_submodule"), + sub_path=a.get("sub_path"), + revision=a.get("revision"), + clone_depth=a.get("clone_depth"), + ) + for a in lst_repo + ] i = 0 total = len(lst_repo) @@ -61,17 +74,22 @@ def main(): # 1. Add remote if not exist try: upstream_remote = git_repo.remote(upstream_name) - print(f'Remote "{upstream_name}" already exists in {repo.relative_path}') + print( + f'Remote "{upstream_name}" already exists in' + f" {repo.relative_path}" + ) except ValueError: upstream_remote = retry( - wait_exponential_multiplier=1000, - stop_max_delay=15000 + wait_exponential_multiplier=1000, stop_max_delay=15000 )(git_repo.create_remote)(upstream_name, repo.url_https) - print('Remote "%s" created for %s' % (upstream_name, repo.url_https)) + print( + 'Remote "%s" created for %s' % (upstream_name, repo.url_https) + ) # 2. Fetch the remote source retry(wait_exponential_multiplier=1000, stop_max_delay=15000)( - upstream_remote.fetch)() + upstream_remote.fetch + )() print('Remote "%s" fetched' % upstream_name) # 3. Rebase actual branch with new branch @@ -99,15 +117,19 @@ def main(): # push try: retry(wait_exponential_multiplier=1000, stop_max_delay=15000)( - git_repo.git.push)(repo.organization, rev) + git_repo.git.push + )(repo.organization, rev) except: - print("Cannot push, maybe need to push force or resolv rebase conflict", - file=sys.stderr) + print( + "Cannot push, maybe need to push force or resolv rebase" + " conflict", + file=sys.stderr, + ) print(f"cd {repo.path}") print(f"git diff ERPLibre/v#.#.#..HEAD") print(f"git push --force {repo.organization} {rev}") print(f"cd -") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/script/install_OSX_dependency.sh b/script/install_OSX_dependency.sh index 3866f97..61756db 100755 --- a/script/install_OSX_dependency.sh +++ b/script/install_OSX_dependency.sh @@ -35,6 +35,8 @@ echo "\n---- Installing nodeJS NPM and rtlcss for LTR support ----" brew install nodejs npm openssl sudo npm install -g rtlcss sudo npm install -g less +sudo npm install -g prettier +sudo npm install -g prettier @prettier/plugin-xml yes n|pyenv install 3.7.7 pyenv local 3.7.7 diff --git a/script/install_debian_dependency.sh b/script/install_debian_dependency.sh index 373dda7..0a29e00 100755 --- a/script/install_debian_dependency.sh +++ b/script/install_debian_dependency.sh @@ -49,12 +49,18 @@ sudo su - postgres -c "createuser -s ${EL_USER}" 2> /dev/null || true echo -e "\n--- Installing debian dependency --" sudo apt-get install git build-essential wget libxslt-dev libzip-dev libldap2-dev libsasl2-dev libpng12-0 gdebi-core libffi-dev libbz2-dev -y sudo apt-get install libmariadbd-dev -y +# Dependencies for pyenv +sudo apt-get install make libssl-dev zlib1g-dev libreadline-dev libsqlite3-dev curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev liblzma-dev -y echo -e "\n---- Installing nodeJS NPM and rtlcss for LTR support ----" sudo apt-get install nodejs npm -y sudo npm install -g rtlcss sudo npm install -g less +echo -e "\n---- Test tool ----" +sudo npm install -g prettier +sudo npm install -g prettier @prettier/plugin-xml + sudo ln -fs /usr/local/bin/lessc /usr/bin/lessc if [ ${EL_INSTALL_NGINX} = "True" ]; then diff --git a/script/install_locally.sh b/script/install_locally.sh index 9a5387b..3b20639 100755 --- a/script/install_locally.sh +++ b/script/install_locally.sh @@ -129,9 +129,10 @@ if [[ ${EL_MINIMAL_ADDONS} = "False" ]]; then printf "${EL_HOME}/addons/OCA_wms," >> ${EL_CONFIG_FILE} printf "${EL_HOME}/addons/Smile-SA_odoo_addons," >> ${EL_CONFIG_FILE} printf "${EL_HOME}/addons/TechnoLibre_odoo-code-generator," >> ${EL_CONFIG_FILE} + printf "${EL_HOME}/addons/TechnoLibre_odoo-code-generator-template," >> ${EL_CONFIG_FILE} printf "${EL_HOME}/addons/camptocamp_odoo-cloud-platform," >> ${EL_CONFIG_FILE} printf "${EL_HOME}/addons/dhongu_deltatech," >> ${EL_CONFIG_FILE} - printf "${EL_HOME}/addons/it-projects-llc_odoo-saas-tools," >> ${EL_CONFIG_FILE} + printf "${EL_HOME}/addons/it-projects-llc_saas-addons," >> ${EL_CONFIG_FILE} printf "${EL_HOME}/addons/itpp-labs_access-addons," >> ${EL_CONFIG_FILE} printf "${EL_HOME}/addons/itpp-labs_pos-addons," >> ${EL_CONFIG_FILE} printf "${EL_HOME}/addons/itpp-labs_website-addons," >> ${EL_CONFIG_FILE} @@ -154,7 +155,7 @@ if [[ ${EL_MINIMAL_ADDONS} = "False" ]]; then printf "${EL_HOME}/doc/itpp-labs_odoo-port-docs," >> ${EL_CONFIG_FILE} printf "${EL_HOME}/doc/itpp-labs_odoo-test-docs," >> ${EL_CONFIG_FILE} printf "${EL_HOME}/doc/odoo_documentation-user," >> ${EL_CONFIG_FILE} - printf "${EL_HOME}/image_db," >> ${EL_CONFIG_FILE} + printf "${EL_HOME}/script/OCA_maintainer-tools," >> ${EL_CONFIG_FILE} printf "${EL_HOME}/script/OCA_odoo-module-migrator," >> ${EL_CONFIG_FILE} fi printf "\n" >> ${EL_CONFIG_FILE} @@ -177,13 +178,21 @@ if [[ ! -d "./addons/addons" ]]; then fi PYENV_PATH=~/.pyenv -PYENV_VERSION_PATH=${PYENV_PATH}/versions/3.7.7 +PYTHON_VERSION=3.7.7 +PYENV_VERSION_PATH=${PYENV_PATH}/versions/${PYTHON_VERSION} PYTHON_EXEC=${PYENV_VERSION_PATH}/bin/python POETRY_PATH=~/.poetry VENV_PATH=./.venv +LOCAL_PYTHON_EXEC=${VENV_PATH}/bin/python VENV_REPO_PATH=${VENV_PATH}/repo +VENV_MULTILINGUAL_MARKDOWN_PATH=${VENV_PATH}/multilang_md.py POETRY_VERSION=1.0.10 +echo "Python path version home" +echo ${PYENV_VERSION_PATH} +echo "Python path version local" +echo ${LOCAL_PYTHON_EXEC} + if [[ ! -d "${PYENV_PATH}" ]]; then echo -e "\n---- Installing pyenv in ${PYENV_PATH} ----" curl -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash @@ -195,28 +204,37 @@ eval "$(pyenv init -)" eval "$(pyenv virtualenv-init -)" if [[ ! -d "${PYENV_VERSION_PATH}" ]]; then - echo -e "\n---- Installing python 3.7.7 with pyenv in ${PYENV_VERSION_PATH} ----" - yes n|pyenv install 3.7.7 + echo -e "\n---- Installing python ${PYTHON_VERSION} with pyenv in ${PYENV_VERSION_PATH} ----" + yes n|pyenv install ${PYTHON_VERSION} + if [[ $retVal -ne 0 ]]; then + echo "Error when installing pyenv" + exit 1 + fi fi -pyenv local 3.7.7 +pyenv local ${PYTHON_VERSION} + +if [[ ! -d ${VENV_PATH} ]]; then + echo -e "\n---- Create Virtual environment Python ----" + if [[ -e ${PYTHON_EXEC} ]]; then + ${PYTHON_EXEC} -m venv .venv + retVal=$? + if [[ $retVal -ne 0 ]]; then + echo "Virtual environment, error when creating .venv" + exit 1 + fi + else + echo "Missing pyenv, please refer installation guide." + exit 1 + fi +fi if [[ ! -d "${POETRY_PATH}" ]]; then # Delete directory ~/.poetry and .venv to force update to new version echo -e "\n---- Installing poetry for reliable python package ----" # curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | ${PYTHON_EXEC} curl -fsS -o get-poetry.py https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py - python get-poetry.py -y --preview --version ${POETRY_VERSION} -fi - -if [[ ! -d ${VENV_PATH} ]]; then - echo -e "\n---- Create Virtual environment Python ----" - if [[ -e ${PYTHON_EXEC} ]]; then - ${PYTHON_EXEC} -m venv .venv - else - echo "Missing pyenv, please refer installation guide." - exit 1 - fi + ${LOCAL_PYTHON_EXEC} get-poetry.py -y --preview --version ${POETRY_VERSION} fi # Install git-repo if missing @@ -229,11 +247,27 @@ if [[ ! -f ${VENV_REPO_PATH} ]]; then sed -i "1 i ${PYTHON_HASHBANG}" ${VENV_REPO_PATH} fi +# Install Multilingual Markdown Generator if missing +if [[ ! -f ${VENV_MULTILINGUAL_MARKDOWN_PATH} ]]; then + echo "\n---- Install Multilingual Markdown Generator ----" + curl https://raw.githubusercontent.com/ERPLibre/multilingual-markdown/master/multilang_md.py > ${VENV_MULTILINGUAL_MARKDOWN_PATH} + chmod +x ${VENV_MULTILINGUAL_MARKDOWN_PATH} + sed -i 1d ${VENV_MULTILINGUAL_MARKDOWN_PATH} + PYTHON_HASHBANG="#!./.venv/bin/python" + sed -i "1 i ${PYTHON_HASHBANG}" ${VENV_MULTILINGUAL_MARKDOWN_PATH} +fi + echo -e "\n---- Installing poetry dependency ----" ${VENV_PATH}/bin/pip install --upgrade pip -#/home/"${USER}"/.poetry/bin/poetry env use ${PYTHON_EXEC} -source $HOME/.poetry/env -poetry install +# Force python instead of changing env +#/home/"${USER}"/.poetry/bin/poetry env use ${LOCAL_PYTHON_EXEC} +# source $HOME/.poetry/env +${LOCAL_PYTHON_EXEC} ~/.poetry/bin/poetry install +retVal=$? +if [[ $retVal -ne 0 ]]; then + echo "Poetry installation error." + exit 1 +fi # Delete artifacts created by pip, cause error in next "poetry install" rm -rf artifacts diff --git a/script/install_locally_dev.sh b/script/install_locally_dev.sh index 39afb4c..94e39dc 100755 --- a/script/install_locally_dev.sh +++ b/script/install_locally_dev.sh @@ -3,6 +3,26 @@ . ./env_var.sh ./script/install_locally.sh +retVal=$? +if [[ $retVal -ne 0 ]]; then + echo "Error ./script/install_locally.sh" + exit 1 +fi # Update git-repo ./script/update_manifest_local_dev.sh +if [[ $retVal -ne 0 ]]; then + echo "Error manifest update, check git-repo." + exit 1 +fi + +# Install maintainer-tools +cd script/OCA_maintainer-tools +# virtualenv is not installed by default +#virtualenv env +../../.venv/bin/python -m venv env +. env/bin/activate +pip install setuptools-rust +python setup.py install +#${VENV_PATH}/bin/pip install ./script/OCA_maintainer-tools/ +cd - diff --git a/script/install_locally_prod.sh b/script/install_locally_prod.sh index f6898d5..f3d31c5 100755 --- a/script/install_locally_prod.sh +++ b/script/install_locally_prod.sh @@ -3,6 +3,15 @@ . ./env_var.sh ./script/install_locally.sh +retVal=$? +if [[ $retVal -ne 0 ]]; then + echo "Error ./script/install_locally.sh" + exit 1 +fi # Update git-repo ./script/update_manifest_prod.sh +if [[ $retVal -ne 0 ]]; then + echo "Error manifest update, check git-repo." + exit 1 +fi diff --git a/script/maintenance/autopep8.sh b/script/maintenance/autopep8.sh new file mode 100755 index 0000000..7042148 --- /dev/null +++ b/script/maintenance/autopep8.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# This will format all python file +# argument 1: directory or file to format +NPROC=$(nproc) +source ./script/OCA_maintainer-tools/env/bin/activate +oca-autopep8 -j ${NPROC} --max-line-length 79 -ari $@ +retVal=$? +if [[ $retVal -ne 0 ]]; then + echo "Error oca-autopep8 format" + exit 1 +fi diff --git a/script/maintenance/black.sh b/script/maintenance/black.sh new file mode 100755 index 0000000..357531b --- /dev/null +++ b/script/maintenance/black.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# This will format all python file +# argument 1: directory or file to format +source ./.venv/bin/activate +black -l 79 --experimental-string-processing -t py37 $@ +retVal=$? +if [[ $retVal -ne 0 ]]; then + echo "Error black format" + exit 1 +fi diff --git a/script/maintenance/prettier.sh b/script/maintenance/prettier.sh new file mode 100755 index 0000000..b7f22c3 --- /dev/null +++ b/script/maintenance/prettier.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# This will format all js,css,html +# argument 1: directory or file to format +prettier --write $@ +retVal=$? +if [[ $retVal -ne 0 ]]; then + echo "Error prettier format" + exit 1 +fi diff --git a/script/maintenance/prettier_xml.sh b/script/maintenance/prettier_xml.sh new file mode 100755 index 0000000..85237e8 --- /dev/null +++ b/script/maintenance/prettier_xml.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# This will format all xml +# argument 1: directory or file to format +prettier --xml-whitespace-sensitivity "ignore" --prose-wrap always --tab-width 4 --no-bracket-spacing --print-width 120 --write $@ +retVal=$? +if [[ $retVal -ne 0 ]]; then + echo "Error prettier format" + exit 1 +fi diff --git a/script/make.sh b/script/make.sh new file mode 100755 index 0000000..c95d7cb --- /dev/null +++ b/script/make.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +echo "==$&%== Begin ${@}" +time make $@ +echo "==$&%== End ${@}" diff --git a/script/migrate_prod_to_test.sh b/script/migrate_prod_to_test.sh new file mode 100755 index 0000000..21c1f63 --- /dev/null +++ b/script/migrate_prod_to_test.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +if [ $# -lt 1 ]; then + # TODO: print usage + echo "Missing database name" + 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 all -d $@ diff --git a/script/open_terminal_code_generator.sh b/script/open_terminal_code_generator.sh new file mode 100755 index 0000000..45a80f0 --- /dev/null +++ b/script/open_terminal_code_generator.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# Open a new gnome-terminal with different path on new tab +working_path=`readlink -f .` +paths=( +"${working_path}/" +"${working_path}/addons/TechnoLibre_odoo-code-generator" +"${working_path}/addons/TechnoLibre_odoo-code-generator-template" + +) +cmd_before="cd " +# when change directory, open a new tab with command to execute +cmd_after=";gnome-terminal --tab -- bash -c 'git status;bash';" +#cmd_after=";gnome-terminal --tab;" + +LONGCMD="" +for t in ${paths[@]}; do + LONGCMD+=${cmd_before}${t}${cmd_after} +done + +#echo $LONGCMD + +# Open all terminal from paths list +#echo "gnome-terminal --window -- bash -c \"${LONGCMD}\"" +gnome-terminal --window -- bash -c "${LONGCMD}" diff --git a/script/poetry_add_build_dependency.sh b/script/poetry_add_build_dependency.sh index ed6c928..163820a 100755 --- a/script/poetry_add_build_dependency.sh +++ b/script/poetry_add_build_dependency.sh @@ -3,3 +3,9 @@ source $HOME/.poetry/env poetry add -vv $(grep -v ";" ./.venv/build_dependency.txt | grep -v "*" ) # poetry add -vv $(grep -v ";" ./.venv/build_dependency.txt | grep -v "*" | sed 's/==/@^/' ) # poetry export -f ./.venv/build_dependency.txt --dev | poetry run -- pip install -r /dev/stdin +retVal=$? +if [[ $retVal -ne 0 ]]; then + echo "Error ./script/poetry_add_build_dependency.sh" + cat ./.venv/build_dependency.txt + exit 1 +fi diff --git a/script/poetry_update.py b/script/poetry_update.py index ed661f4..5352e36 100755 --- a/script/poetry_update.py +++ b/script/poetry_update.py @@ -9,7 +9,7 @@ from collections import OrderedDict, defaultdict from pathlib import Path import iscompatible -new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) +new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) sys.path.append(new_path) from script import git_tool @@ -27,26 +27,46 @@ def get_config(): parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description='''\ + description="""\ Update pip dependency in Poetry, clear pyproject.toml, search all dependencies from requirements.txt, search conflict version and generate a new list. Launch Poetry installation. -''', - epilog='''\ -''' +""", + epilog="""\ +""", + ) + parser.add_argument( + "-d", + "--dir", + dest="dir", + default="./", + help="Path of repo to change remote, including submodule.", + ) + parser.add_argument( + "-f", + "--force", + action="store_true", + help="Force create new list of dependency, ignore poetry.lock.", + ) + parser.add_argument( + "-v", + "--verbose", + action="store_true", + help="More information in execution, show stats.", ) - parser.add_argument('-d', '--dir', dest="dir", default="./", - help="Path of repo to change remote, including submodule.") - parser.add_argument('-f', '--force', action="store_true", - help="Force create new list of dependency, ignore poetry.lock.") - parser.add_argument('-v', '--verbose', action="store_true", - help="More information in execution, show stats.") args = parser.parse_args() return args def get_lst_requirements_txt(): - return list(Path(".").rglob("requirements.[tT][xX][tT]")) + # Ignore some item in list + lst = [ + a + for a in Path(".").rglob("requirements.[tT][xX][tT]") + if not os.path.dirname(a).startswith(".repo/") + and not os.path.dirname(a).startswith(".venv/") + ] + return lst def get_lst_manifest_py(): @@ -61,7 +81,8 @@ def combine_requirements(config): :param config: :return: """ - priority_filename_requirement = "odoo/requirements.txt" + priority_filename_requirement = "requirements.txt" + second_priority_filename_requirement = "odoo/requirements.txt" lst_sign = ("==", "!=", ">=", "<=", "<", ">", ";") lst_requirements = [] ignore_requirements = ["sys_platform == 'win32'", "python_version < '3.7'"] @@ -71,7 +92,7 @@ def combine_requirements(config): dct_special_condition = defaultdict(list) dct_requirements_module_filename = defaultdict(list) for requirements_filename in lst_requirements_file: - with open(requirements_filename, 'r') as f: + with open(requirements_filename, "r") as f: for a in f.readlines(): b = a.strip() if not b or b[0] == "#": @@ -81,10 +102,14 @@ def combine_requirements(config): for sign in lst_sign: if sign in b: # Exception, some time the sign ; can be first, just check - if sign != ";" and ";" in b and b.find(sign) > b.find(";"): - module_name = b[:b.find(";")] + if ( + sign != ";" + and ";" in b + and b.find(sign) > b.find(";") + ): + module_name = b[: b.find(";")] else: - module_name = b[:b.find(sign)] + module_name = b[: b.find(sign)] module_name = module_name.strip() # Special condition for ";", ignore it if ";" in b: @@ -94,13 +119,15 @@ def combine_requirements(config): if ignore_string in b: break lst_requirements_with_condition.add(module_name) - value = b[:b.find(";")].strip() + value = b[: b.find(";")].strip() dct_special_condition[module_name].append(b) else: value = b dct_requirements[module_name].add(value) filename = str(requirements_filename) - dct_requirements_module_filename[value].append(filename) + dct_requirements_module_filename[value].append( + filename + ) break else: dct_requirements[b].add(b) @@ -110,16 +137,19 @@ def combine_requirements(config): dct_requirements = get_manifest_external_dependencies(dct_requirements) - dct_requirements_diff_version = {k: v for k, v in dct_requirements.items() if - len(v) > 1} + dct_requirements_diff_version = { + k: v for k, v in dct_requirements.items() if len(v) > 1 + } # dct_requirements_same_version = {k: v for k, v in dct_requirements.items() if # len(v) == 1} if config.verbose: # TODO show total repo to compare lst requirements - print(f"Total requirements.txt {len(lst_requirements_file)}, " - f"total module {len(lst_requirements)}, " - f"unique module {len(dct_requirements)}, " - f"module with different version {len(dct_requirements_diff_version)}.") + print( + f"Total requirements.txt {len(lst_requirements_file)}, total" + f" module {len(lst_requirements)}, unique module" + f" {len(dct_requirements)}, module with different version" + f" {len(dct_requirements_diff_version)}." + ) if dct_requirements_diff_version: # Validate compatibility @@ -138,44 +168,71 @@ def combine_requirements(config): # TODO support me in iscompatible lib no_version = result_number[0][1] if "b" in no_version: - result_number[0] = result_number[0][0], no_version[ - :no_version.find("b")] - elif not no_version[no_version.rfind(".") + 1:].isnumeric(): - result_number[0] = result_number[0][0], no_version[ - :no_version.rfind(".")] - result_number = iscompatible.string_to_tuple(result_number[0][1]) + result_number[0] = ( + result_number[0][0], + no_version[: no_version.find("b")], + ) + elif not no_version[no_version.rfind(".") + 1 :].isnumeric(): + result_number[0] = ( + result_number[0][0], + no_version[: no_version.rfind(".")], + ) + result_number = iscompatible.string_to_tuple( + result_number[0][1] + ) lst_version_requirement.append((requirement, result_number)) # Check compatibility with all possibility is_compatible = True if len(lst_version_requirement) > 1: - highest_value = sorted(lst_version_requirement, key=lambda tup: tup[1])[-1] + highest_value = sorted( + lst_version_requirement, key=lambda tup: tup[1] + )[-1] for version_requirement in lst_version_requirement: - is_compatible &= iscompatible.iscompatible(version_requirement[0], - highest_value[1]) + is_compatible &= iscompatible.iscompatible( + version_requirement[0], highest_value[1] + ) if is_compatible: result = highest_value[0] else: # Find the requirements file and print the conflict # Take the version from Odoo by default, else take the more recent odoo_value = None + erplibre_value = None for version_requirement in lst_version_requirement: filename_1 = dct_requirements_module_filename.get( - version_requirement[0]) + version_requirement[0] + ) if priority_filename_requirement in filename_1: + erplibre_value = version_requirement[0] + elif ( + second_priority_filename_requirement in filename_1 + ): odoo_value = version_requirement[0] - break - if odoo_value: - str_result_choose = f"Select {odoo_value} because from Odoo" + if erplibre_value: + str_result_choose = ( + f"Select {erplibre_value} because from ERPLibre" + ) + result = erplibre_value + elif odoo_value: + str_result_choose = ( + f"Select {odoo_value} because from Odoo" + ) result = odoo_value else: result = highest_value[0] str_result_choose = f"Select highest value {result}" str_versions = " VS ".join( - [f"{a[0]} from {dct_requirements_module_filename.get(a[0])}" for - a in lst_version_requirement]) - print(f"WARNING - Not compatible {str_versions} - " - f"{str_result_choose}.") + [ + f"{a[0]} from" + f" {dct_requirements_module_filename.get(a[0])}" + for a in lst_version_requirement + ] + ) + print( + f"WARNING - Not compatible {str_versions} - " + f"{str_result_choose}." + ) elif len(lst_version_requirement) == 1: result = lst_version_requirement[0][0] else: @@ -196,13 +253,13 @@ def combine_requirements(config): for key in lst_ignored_key: del dct_requirements[key] - with open("./.venv/build_dependency.txt", 'w') as f: + with open("./.venv/build_dependency.txt", "w") as f: f.writelines([f"{list(a)[0]}\n" for a in dct_requirements.values()]) def sorted_dependency_poetry(pyproject_filename): # Open pyproject.toml - with open(pyproject_filename, 'r') as f: + with open(pyproject_filename, "r") as f: dct_pyproject = toml.load(f) # Get dependencies and update list, sorted @@ -212,19 +269,23 @@ def sorted_dependency_poetry(pyproject_filename): poetry = tool.get("poetry") if poetry: dependencies = poetry.get("dependencies") - python_dependency = ("python", dependencies.get("python", '')) - lst_dependency = [(k, v) for k, v in dependencies.items() if k != "python"] + python_dependency = ("python", dependencies.get("python", "")) + lst_dependency = [ + (k, v) for k, v in dependencies.items() if k != "python" + ] lst_dependency = sorted(lst_dependency, key=lambda tup: tup[0]) - poetry["dependencies"] = OrderedDict([python_dependency] + lst_dependency) + poetry["dependencies"] = OrderedDict( + [python_dependency] + lst_dependency + ) # Rewrite pyproject.toml - with open(pyproject_filename, 'w') as f: + with open(pyproject_filename, "w") as f: toml.dump(dct_pyproject, f) def delete_dependency_poetry(pyproject_filename): # Open pyproject.toml - with open(pyproject_filename, 'r') as f: + with open(pyproject_filename, "r") as f: dct_pyproject = toml.load(f) # Get dependencies and update list, sorted @@ -234,11 +295,11 @@ def delete_dependency_poetry(pyproject_filename): poetry = tool.get("poetry") if poetry: dependencies = poetry.get("dependencies") - python_dependency = ("python", dependencies.get("python", '')) + python_dependency = ("python", dependencies.get("python", "")) poetry["dependencies"] = OrderedDict([python_dependency]) # Rewrite pyproject.toml - with open(pyproject_filename, 'w') as f: + with open(pyproject_filename, "w") as f: toml.dump(dct_pyproject, f) @@ -246,7 +307,7 @@ def get_manifest_external_dependencies(dct_requirements): lst_manifest_file = get_lst_manifest_py() lst_dct_ext_depend = [] for manifest_file in lst_manifest_file: - with open(manifest_file, 'r') as f: + with open(manifest_file, "r") as f: contents = f.read() try: dct = ast.literal_eval(contents) @@ -270,11 +331,16 @@ def get_manifest_external_dependencies(dct_requirements): def call_poetry_add_build_dependency(): - os.system("./script/poetry_add_build_dependency.sh") + """ + + :return: True if success + """ + status = os.system("./script/poetry_add_build_dependency.sh") + return status == 0 def get_list_ignored(): - with open("./ignore_requirements.txt", 'r') as f: + with open("./ignore_requirements.txt", "r") as f: lst_ignore_requirements = [a.strip() for a in f.readlines()] return lst_ignore_requirements @@ -283,15 +349,16 @@ def main(): # repo = Repo(root_path) # lst_repo = get_all_repo() config = get_config() - pyproject_toml_filename = f'{config.dir}pyproject.toml' + pyproject_toml_filename = f"{config.dir}pyproject.toml" delete_dependency_poetry(pyproject_toml_filename) combine_requirements(config) if config.force and os.path.isfile("./poetry.lock"): os.remove("./poetry.lock") - call_poetry_add_build_dependency() - sorted_dependency_poetry(pyproject_toml_filename) + status = call_poetry_add_build_dependency() + if status: + sorted_dependency_poetry(pyproject_toml_filename) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/script/pull_request_ERPLibre.py b/script/pull_request_ERPLibre.py index 055e73b..eee235b 100755 --- a/script/pull_request_ERPLibre.py +++ b/script/pull_request_ERPLibre.py @@ -4,7 +4,7 @@ import sys import argparse import logging -new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) +new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) sys.path.append(new_path) from script.git_tool import GitTool @@ -24,18 +24,30 @@ def get_config(): # TODO update description parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description='''\ -''', - epilog='''\ -''' + description="""\ +""", + epilog="""\ +""", + ) + parser.add_argument( + "-d", + "--dir", + dest="dir", + default="./", + help="Path of repo to change remote, including submodule.", + ) + parser.add_argument( + "--organization", + dest="organization", + default="ERPLibre", + help="Choose organization to fork and change all repo.", + ) + parser.add_argument( + "--github_token", + dest="github_token", + default=config.get(CST_EL_GITHUB_TOKEN), + help="GitHub token generated by user", ) - parser.add_argument('-d', '--dir', dest="dir", default="./", - help="Path of repo to change remote, including submodule.") - parser.add_argument('--organization', dest="organization", default="ERPLibre", - help="Choose organization to fork and change all repo.") - parser.add_argument('--github_token', dest="github_token", - default=config.get(CST_EL_GITHUB_TOKEN), - help="GitHub token generated by user") args = parser.parse_args() return args @@ -49,12 +61,21 @@ def main(): raise ValueError("Missing github_token") organization_name = config.organization - lst_repo = git_tool.get_source_repo_addons(repo_path=config.dir, add_repo_root=True) - lst_repo_organization = [git_tool.get_transformed_repo_info_from_url( - a.get("url"), repo_path=config.dir, organization_force=organization_name, - is_submodule=a.get("is_submodule"), sub_path=a.get("sub_path"), - revision=a.get("revision"), clone_depth=a.get("clone_depth")) - for a in lst_repo] + lst_repo = git_tool.get_source_repo_addons( + repo_path=config.dir, add_repo_root=True + ) + lst_repo_organization = [ + git_tool.get_transformed_repo_info_from_url( + a.get("url"), + repo_path=config.dir, + organization_force=organization_name, + is_submodule=a.get("is_submodule"), + sub_path=a.get("sub_path"), + revision=a.get("revision"), + clone_depth=a.get("clone_depth"), + ) + for a in lst_repo + ] url_not_found_count = 0 url_found_count = 0 @@ -66,9 +87,11 @@ def main(): print(f"Nb element {i}/{total} - {repo.project_name}") url = repo.url - status = git_tool.get_pull_request_repo(upstream_url=url, - github_token=github_token, - organization_name=organization_name) + status = git_tool.get_pull_request_repo( + upstream_url=url, + github_token=github_token, + organization_name=organization_name, + ) if status is False: url_not_found_count += 1 else: @@ -78,5 +101,5 @@ def main(): print(f"URL found: {url_found_count}") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/script/repo_remove_auto_install.py b/script/repo_remove_auto_install.py index 0c5ef88..4e40a88 100755 --- a/script/repo_remove_auto_install.py +++ b/script/repo_remove_auto_install.py @@ -7,7 +7,7 @@ from pathlib import Path from git import Repo # pip install gitpython from git.exc import GitCommandError -new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) +new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) sys.path.append(new_path) from script.git_tool import GitTool @@ -24,13 +24,18 @@ def get_config(): # TODO update description parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description='''\ -''', - epilog='''\ -''' + description="""\ +""", + epilog="""\ +""", + ) + parser.add_argument( + "-d", + "--dir", + dest="dir", + default="./", + help="Path of repo to change remote, including submodule.", ) - parser.add_argument('-d', '--dir', dest="dir", default="./", - help="Path of repo to change remote, including submodule.") args = parser.parse_args() return args @@ -39,13 +44,21 @@ def main(): config = get_config() git_tool = GitTool() - lst_repo = git_tool.get_source_repo_addons(repo_path=config.dir, - add_repo_root=False) - lst_repo_organization = [git_tool.get_transformed_repo_info_from_url( - a.get("url"), repo_path=config.dir, get_obj=True, - is_submodule=a.get("is_submodule"), sub_path=a.get("sub_path"), - revision=a.get("revision"), clone_depth=a.get("clone_depth")) - for a in lst_repo] + lst_repo = git_tool.get_source_repo_addons( + repo_path=config.dir, add_repo_root=False + ) + lst_repo_organization = [ + git_tool.get_transformed_repo_info_from_url( + a.get("url"), + repo_path=config.dir, + get_obj=True, + is_submodule=a.get("is_submodule"), + sub_path=a.get("sub_path"), + revision=a.get("revision"), + clone_depth=a.get("clone_depth"), + ) + for a in lst_repo + ] lst_ignore_repo = ["odoo"] @@ -68,7 +81,9 @@ def main(): is_checkout_branch = True except GitCommandError: try: - git_repo.git.checkout("-t", f"{repo.organization}/{branch_name}") + git_repo.git.checkout( + "-t", f"{repo.organization}/{branch_name}" + ) is_checkout_branch = True except GitCommandError: pass @@ -93,7 +108,7 @@ def get_manifest_external_dependencies(repo): lst_manifest_file = get_lst_manifest_py(repo.relative_path) for manifest_file in lst_manifest_file: has_change_manifest = False - with open(manifest_file, 'r') as f: + with open(manifest_file, "r") as f: lst_content = f.readlines() i = 0 for content in lst_content: @@ -102,16 +117,18 @@ def get_manifest_external_dependencies(repo): has_change = True first_char_index = content.find("auto_install") index = content.find("True", first_char_index) - lst_content[i] = content[:index] + "False" + content[index + 4:] + lst_content[i] = ( + content[:index] + "False" + content[index + 4 :] + ) i += 1 if has_change_manifest: print(f"Update file {manifest_file}") - with open(manifest_file, 'w') as f: + with open(manifest_file, "w") as f: f.writelines(lst_content) return has_change -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/script/repo_revert_git_diff_date_from_code_generator.py b/script/repo_revert_git_diff_date_from_code_generator.py new file mode 100755 index 0000000..85c892a --- /dev/null +++ b/script/repo_revert_git_diff_date_from_code_generator.py @@ -0,0 +1,120 @@ +#!./.venv/bin/python +import os +import sys +import argparse +import logging +import git +from unidiff import PatchSet +import re + +new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) +sys.path.append(new_path) + +_logger = logging.getLogger(__name__) + + +def get_config(): + """Parse command line arguments, extracting the config file name, + returning the union of config file and command line arguments + + :return: dict of config file settings and command line arguments + """ + # TODO update description + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description="""\ +""", + epilog="""\ +""", + ) + args = parser.parse_args() + return args + + +def main(): + config = get_config() + # rex = r"\s+(?=\d{2}(?:\d{2})?-\d{1,2}-\d{1,2}\b)" + rex = ( + r"[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])" + r" (2[0-3]|[01][0-9]):[0-5][0-9]" + ) + # TODO support argument instead of hardcoded values + lst_path = [ + "./addons/TechnoLibre_odoo-code-generator-template", + "./addons/OCA_server-tools", + ] + for path in lst_path: + repo = git.Repo(path) + supported_ext = [".xml", ".pot", ".po"] + translation_ext = [".pot", ".po"] + for diff in repo.index.diff(None, create_patch=True): + # if diff.change_type == "M": + file_path = diff.a_path + filename, file_extension = os.path.splitext(file_path) + if file_extension in supported_ext: + file_real_path = os.path.join(path, diff.a_path) + if not os.path.isfile(file_real_path): + continue + if file_extension in translation_ext: + is_modified = False + lst_write_data = [] + # Delete code expression path caused by ERPLibre architecture + with open(file_real_path, "r") as file: + lst_data = file.readlines() + for data in lst_data: + if data.startswith("#: code:addons/addons/"): + is_modified = True + else: + lst_write_data.append(data) + if is_modified: + with open(file_real_path, "w") as file: + file.writelines(lst_write_data) + + str_diff = repo.git.diff(diff.a_path) + patch = PatchSet(str_diff) + for modified_file in patch.modified_files: + lst_to_write = [] + for hunk in modified_file: + nb_line_source = len(hunk.source) + nb_line_target = len(hunk.target) + if nb_line_source != nb_line_target: + # TODO support different line + _logger.warning( + "Source nb line different of target nb line" + f" for file {path}/{file_path}." + ) + continue + # try: + # assert nb_line_source == nb_line_target, f"Not the same line of diff:\n{str_diff}" + # except Exception as e: + # print(e) + for i in range(nb_line_source): + result_source = re.split(rex, hunk.source[i]) + if i > len(hunk.target): + result_target = re.split(rex, hunk.target[i]) + else: + result_target = "" + if ( + len(result_source) > 1 + or len(result_target) > 1 + ): + line_to_change = hunk.target_start + i - 1 + lst_to_write.append( + (line_to_change, hunk.source[i][1:]) + ) + # rewrite + rewrite(file_real_path, lst_to_write) + + +def rewrite(file_path, lst_to_write): + # TODO not optimal for big file + with open(file_path, "r") as file: + data = file.readlines() + for line in lst_to_write: + data[line[0]] = line[1] + with open(file_path, "w") as file: + file.writelines(data) + + +if __name__ == "__main__": + main() diff --git a/script/update_manifest_local_dev_code_generator.sh b/script/update_manifest_local_dev_code_generator.sh index 8469bd0..907d45f 100755 --- a/script/update_manifest_local_dev_code_generator.sh +++ b/script/update_manifest_local_dev_code_generator.sh @@ -9,7 +9,7 @@ git daemon --base-path=. --export-all --reuseaddr --informative-errors --verbose & DAEMON_PID=$! -./.venv/repo init -u git://127.0.0.1:9418/ -b $(git rev-parse --verify HEAD) -m ${EL_MANIFEST_DEV} -g code_generator +./.venv/repo init -u git://127.0.0.1:9418/ -b $(git rev-parse --verify HEAD) -m ${EL_MANIFEST_DEV} -g base,code_generator ./.venv/repo sync -v --force-sync -m ${EL_MANIFEST_DEV} kill ${DAEMON_PID} diff --git a/source_repo_addons.csv b/source_repo_addons.csv index efa6147..1364175 100644 --- a/source_repo_addons.csv +++ b/source_repo_addons.csv @@ -115,7 +115,7 @@ https://github.com/dhongu/deltatech.git,addons,, https://github.com/kinjal-sorathiya/Property-Management_odoo.git,addons,master, https://github.com/tegin/medical-fhir.git,addons,, https://github.com/odoomates/odooapps.git,addons,, -https://github.com/it-projects-llc/odoo-saas-tools.git,addons,, +https://github.com/it-projects-llc/saas-addons.git,addons,, https://github.com/itpp-labs/website-addons.git,addons,, https://github.com/itpp-labs/access-addons.git,addons,, https://github.com/itpp-labs/pos-addons.git,addons,, @@ -125,4 +125,7 @@ https://github.com/itpp-labs/odoo-port-docs.git,doc,master, https://github.com/itpp-labs/odoo-test-docs.git,doc,master, https://github.com/odoo/documentation-user.git,doc,, https://github.com/OCA/odoo-module-migrator.git,script,master, +https://github.com/OCA/maintainer-tools.git,script,master, https://github.com/TechnoLibre/odoo-code-generator.git,addons,, +https://github.com/TechnoLibre/odoo-code-generator-template.git,addons,, +https://github.com/ERPLibre/ERPLibre_image_db.git,,,