- erplibre private repertory for user private configuration - move todo_override to private/todo.json - selenium moving script
510 lines
17 KiB
Python
Executable file
510 lines
17 KiB
Python
Executable file
#!/usr/bin/env python
|
|
|
|
import getpass
|
|
import json
|
|
import logging
|
|
import os
|
|
import sys
|
|
import time
|
|
import subprocess
|
|
|
|
try:
|
|
import tkinter as tk
|
|
from tkinter import filedialog
|
|
|
|
import click
|
|
import openai
|
|
from pykeepass import PyKeePass
|
|
except ModuleNotFoundError as e:
|
|
# TODO auto-detect gnome-terminal, or choose another.
|
|
if os.path.exists(".venv"):
|
|
print("You forgot to activate source \nsource ./.venv/bin/activate")
|
|
time.sleep(0.5)
|
|
subprocess.run(
|
|
"gnome-terminal -- bash -c './script/todo/source_todo.sh'",
|
|
shell=True,
|
|
executable="/bin/bash",
|
|
)
|
|
else:
|
|
first_installation_input = (
|
|
input(
|
|
"First installation? This will process system installation"
|
|
" before (Y/N): "
|
|
)
|
|
.strip()
|
|
.upper()
|
|
)
|
|
if first_installation_input == "Y":
|
|
subprocess.run(
|
|
"gnome-terminal -- bash -c"
|
|
" './script/version/update_env_version.py --install;bash'",
|
|
shell=True,
|
|
executable="/bin/bash",
|
|
)
|
|
print("Wait after OS installation before continue.")
|
|
|
|
# First detect pycharm, need to be open before installation and close to increase speed
|
|
result = subprocess.run(
|
|
["which", "pycharm"],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True,
|
|
)
|
|
if result.returncode == 0 and not os.path.exists(".idea"):
|
|
pycharm_configuration_input = (
|
|
input("Open Pycharm? (Y/N): ").strip().upper()
|
|
)
|
|
if pycharm_configuration_input == "Y":
|
|
subprocess.run(
|
|
"gnome-terminal -- bash -c 'pycharm .'",
|
|
shell=True,
|
|
executable="/bin/bash",
|
|
)
|
|
print(
|
|
"Close Pycharm when processing is done before continue"
|
|
" this guide."
|
|
)
|
|
# Propose Odoo installation
|
|
# TODO detect last version supported
|
|
odoo_installation_input = (
|
|
input("Install Odoo 16? (Y/N): ").strip().upper()
|
|
)
|
|
if odoo_installation_input == "Y":
|
|
subprocess.run(
|
|
"gnome-terminal -- bash -c 'make install_odoo_16;bash'",
|
|
shell=True,
|
|
executable="/bin/bash",
|
|
)
|
|
print("Wait after installation and open projects by terminal.")
|
|
print("make open_terminal")
|
|
else:
|
|
print("Nothing to do, you need a fresh installation to continue.")
|
|
sys.exit(0)
|
|
sys.exit(1)
|
|
|
|
# TODO implement urwid to improve text user interface
|
|
# import urwid
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
CONFIG_FILE = "./script/todo/todo.json"
|
|
CONFIG_OVERRIDE_FILE = "./private/todo.json"
|
|
LOGO_ASCII_FILE = "./script/todo/logo_ascii.txt"
|
|
|
|
|
|
class TODO:
|
|
def __init__(self):
|
|
self.kdbx = None
|
|
|
|
def run(self):
|
|
with open(LOGO_ASCII_FILE) as my_file:
|
|
print(my_file.read())
|
|
print("Ouverture de TODO en cours ...")
|
|
print("🤖 => Entre tes directives par son chiffre et fait Entrée!")
|
|
help_info = """Commande :
|
|
[1] Execute
|
|
[2] Question
|
|
[3] Fork - Ouvre TODO 🤖 dans une nouvelle tabulation
|
|
[0] Quitter
|
|
"""
|
|
# [3] install
|
|
while True:
|
|
status = click.prompt(help_info)
|
|
print()
|
|
if status == "0":
|
|
break
|
|
elif status == "1":
|
|
self.prompt_execute()
|
|
elif status == "2":
|
|
self.execute_prompt_ia()
|
|
elif status == "3":
|
|
cmd = (
|
|
f"gnome-terminal --tab -- bash -c 'source"
|
|
f" ./.venv/bin/activate;make todo'"
|
|
)
|
|
self.executer_commande_live(cmd)
|
|
# elif status == "3" or status == "install":
|
|
# print("install")
|
|
else:
|
|
print("Commande non trouvée 🤖!")
|
|
|
|
print(status)
|
|
# manipuler()
|
|
|
|
def get_kdbx(self):
|
|
if self.kdbx:
|
|
return self.kdbx
|
|
# Open file
|
|
chemin_fichier_kdbx = self.get_config(["kdbx", "path"])
|
|
if not chemin_fichier_kdbx:
|
|
root = tk.Tk()
|
|
root.withdraw() # Hide the main window
|
|
chemin_fichier_kdbx = filedialog.askopenfilename(
|
|
title="Select a File",
|
|
filetypes=(("KeepassX files", "*.kdbx"),),
|
|
)
|
|
if not chemin_fichier_kdbx:
|
|
_logger.error(f"KDBX is not configured, please fill {CONFIG_FILE}")
|
|
return
|
|
|
|
mot_de_passe_kdbx = self.get_config(["kdbx", "password"])
|
|
if not mot_de_passe_kdbx:
|
|
mot_de_passe_kdbx = getpass.getpass(
|
|
prompt="Entrez votre mot de passe : "
|
|
)
|
|
|
|
kp = PyKeePass(chemin_fichier_kdbx, password=mot_de_passe_kdbx)
|
|
|
|
if kp:
|
|
self.kdbx = kp
|
|
return kp
|
|
|
|
def get_config(self, lst_params):
|
|
# Open file
|
|
config_file = CONFIG_FILE
|
|
if os.path.exists(CONFIG_OVERRIDE_FILE):
|
|
config_file = CONFIG_OVERRIDE_FILE
|
|
|
|
with open(config_file) as cfg:
|
|
dct_data = json.load(cfg)
|
|
for param in lst_params:
|
|
try:
|
|
dct_data = dct_data[param]
|
|
except KeyError:
|
|
_logger.error(
|
|
f"KeyError on file {config_file} with keys"
|
|
f" {lst_params}"
|
|
)
|
|
return
|
|
return dct_data
|
|
|
|
def execute_prompt_ia(self):
|
|
while True:
|
|
help_info = """Commande :
|
|
[0] Retour
|
|
Écrit moi ta question """
|
|
status = click.prompt(help_info)
|
|
print()
|
|
if status == "0":
|
|
return
|
|
kp = self.get_kdbx()
|
|
if not kp:
|
|
return
|
|
nom_configuration = self.get_config(
|
|
["kdbx_config", "openai", "kdbx_key"]
|
|
)
|
|
entry = kp.find_entries_by_title(nom_configuration, first=True)
|
|
|
|
client = openai.OpenAI(api_key=entry.password)
|
|
prompt_update = status
|
|
completion = client.chat.completions.create(
|
|
model="gpt-4o",
|
|
messages=[{"role": "user", "content": prompt_update}],
|
|
)
|
|
|
|
print(completion.choices[0].message.content)
|
|
print()
|
|
|
|
def prompt_execute(self):
|
|
help_info = """Commande :
|
|
[1] RUN Exécuter et installer une instance
|
|
[2] EXEC Automatisation - Démonstration des fonctions développées
|
|
[3] UPD Mise à jour - Update all developed staging source code
|
|
[4] Code - Outil pour développeur
|
|
[0] Retour
|
|
"""
|
|
while True:
|
|
status = click.prompt(help_info)
|
|
print()
|
|
if status == "0":
|
|
return
|
|
elif status == "1":
|
|
status = self.prompt_execute_instance()
|
|
if status is not False:
|
|
return
|
|
elif status == "2":
|
|
status = self.prompt_execute_fonction()
|
|
if status is not False:
|
|
return
|
|
elif status == "3":
|
|
status = self.prompt_execute_update()
|
|
if status is not False:
|
|
return
|
|
elif status == "4":
|
|
status = self.prompt_execute_code()
|
|
if status is not False:
|
|
return
|
|
else:
|
|
print("Commande non trouvée 🤖!")
|
|
|
|
def execute_from_configuration(
|
|
self, dct_instance, exec_run_db=False, ignore_makefile=False
|
|
):
|
|
# exec_run_db need argument database
|
|
kdbx_key = dct_instance.get("kdbx_key")
|
|
odoo_user = dct_instance.get("user")
|
|
odoo_password = dct_instance.get("password")
|
|
|
|
if kdbx_key:
|
|
extra_cmd_web_login = self.kdbx_get_extra_command_user(kdbx_key)
|
|
elif odoo_user and odoo_password:
|
|
extra_cmd_web_login = (
|
|
f" --default_email_auth {odoo_user} --default_password_auth"
|
|
f" '{odoo_password}'"
|
|
)
|
|
else:
|
|
extra_cmd_web_login = ""
|
|
|
|
makefile_cmd = dct_instance.get("makefile_cmd")
|
|
if makefile_cmd and not ignore_makefile:
|
|
self.executer_commande_live(f"make {makefile_cmd}")
|
|
|
|
if exec_run_db:
|
|
db_name = dct_instance.get("database")
|
|
self.prompt_execute_selenium_and_run_db(
|
|
db_name, extra_cmd_web_login=extra_cmd_web_login
|
|
)
|
|
|
|
command = dct_instance.get("command")
|
|
if command:
|
|
self.prompt_execute_selenium(
|
|
command=command, extra_cmd_web_login=extra_cmd_web_login
|
|
)
|
|
|
|
def fill_help_info(self, lst_instance):
|
|
help_info = "Commande :\n"
|
|
help_end = "[0] Retour\n"
|
|
for i, dct_instance in enumerate(lst_instance):
|
|
|
|
help_info += (
|
|
f"[{i + 1}] " + dct_instance["prompt_description"] + "\n"
|
|
)
|
|
help_info += help_end
|
|
return help_info
|
|
|
|
def prompt_execute_instance(self):
|
|
# TODO proposer le déploiement à distance
|
|
# TODO proposer l'exécution de docker
|
|
# TODO proposer la création de docker
|
|
lst_instance = self.get_config(["instance"])
|
|
help_info = self.fill_help_info(lst_instance)
|
|
|
|
while True:
|
|
status = click.prompt(help_info)
|
|
print()
|
|
if status == "0":
|
|
return False
|
|
else:
|
|
cmd_no_found = True
|
|
try:
|
|
int_cmd = int(status)
|
|
if 0 < int_cmd <= len(lst_instance):
|
|
cmd_no_found = False
|
|
status = click.confirm(
|
|
"Voulez-vous une nouvelle instance?"
|
|
)
|
|
dct_instance = lst_instance[int_cmd - 1]
|
|
self.execute_from_configuration(
|
|
dct_instance,
|
|
exec_run_db=True,
|
|
ignore_makefile=not bool(status),
|
|
)
|
|
except ValueError:
|
|
pass
|
|
if cmd_no_found:
|
|
print("Commande non trouvée 🤖!")
|
|
|
|
def prompt_execute_fonction(self):
|
|
lst_instance = self.get_config(["function"])
|
|
help_info = self.fill_help_info(lst_instance)
|
|
|
|
while True:
|
|
status = click.prompt(help_info)
|
|
print()
|
|
if status == "0":
|
|
return False
|
|
else:
|
|
cmd_no_found = True
|
|
try:
|
|
int_cmd = int(status)
|
|
if 0 < int_cmd <= len(lst_instance):
|
|
cmd_no_found = False
|
|
dct_instance = lst_instance[int_cmd - 1]
|
|
self.execute_from_configuration(dct_instance)
|
|
except ValueError:
|
|
pass
|
|
if cmd_no_found:
|
|
print("Commande non trouvée 🤖!")
|
|
|
|
def prompt_execute_update(self):
|
|
# self.executer_commande_live(f"make {makefile_cmd}")
|
|
print("🤖 Mise à jour du développement")
|
|
# TODO détecter les modules en modification pour faire la mise à jour en cours
|
|
# TODO demander sur quel BD faire la mise à jour
|
|
# TODO proposer les modules manuelles selon la configuration à mettre à jour
|
|
# TODO proposer la mise à jour de l'IDE
|
|
# TODO proposer la mise à jour des git-repo
|
|
|
|
lst_instance = self.get_config(["update_from_makefile"])
|
|
help_info = self.fill_help_info(lst_instance)
|
|
|
|
while True:
|
|
status = click.prompt(help_info)
|
|
print()
|
|
if status == "0":
|
|
return False
|
|
else:
|
|
cmd_no_found = True
|
|
try:
|
|
int_cmd = int(status)
|
|
if 0 < int_cmd <= len(lst_instance):
|
|
cmd_no_found = False
|
|
dct_instance = lst_instance[int_cmd - 1]
|
|
self.execute_from_configuration(dct_instance)
|
|
except ValueError:
|
|
pass
|
|
if cmd_no_found:
|
|
print("Commande non trouvée 🤖!")
|
|
return False
|
|
|
|
def prompt_execute_code(self):
|
|
print("🤖 Qu'avez-vous de besoin pour développer?")
|
|
# help_info = """Commande :
|
|
# [1] Status Git local et distant
|
|
# [2] Démarrer le générateur de code
|
|
# [3] Format - Formatage automatique selon changement [ou manuelle]
|
|
# [4] Qualité - Qualité logiciel, détecter les fichiers qui manquent les licences AGPLv3
|
|
# [0] Retour
|
|
# """
|
|
# help_info = """Commande :
|
|
# [1] Status Git local et distant
|
|
# [0] Retour
|
|
# """
|
|
lst_instance = self.get_config(["code_from_makefile"])
|
|
help_info = self.fill_help_info(lst_instance)
|
|
|
|
while True:
|
|
status = click.prompt(help_info)
|
|
print()
|
|
if status == "0":
|
|
return False
|
|
else:
|
|
cmd_no_found = True
|
|
try:
|
|
int_cmd = int(status)
|
|
if 0 < int_cmd <= len(lst_instance):
|
|
cmd_no_found = False
|
|
dct_instance = lst_instance[int_cmd - 1]
|
|
self.execute_from_configuration(dct_instance)
|
|
except ValueError:
|
|
pass
|
|
if cmd_no_found:
|
|
print("Commande non trouvée 🤖!")
|
|
return False
|
|
|
|
def kdbx_get_extra_command_user(self, kdbx_key):
|
|
lst_value = []
|
|
if kdbx_key:
|
|
kp = self.get_kdbx()
|
|
if not kp:
|
|
return ""
|
|
if type(kdbx_key) is not list:
|
|
lst_kdbx_key = [kdbx_key]
|
|
else:
|
|
lst_kdbx_key = kdbx_key
|
|
for key in lst_kdbx_key:
|
|
entry = kp.find_entries_by_title(key, first=True)
|
|
try:
|
|
odoo_user = entry.username
|
|
except AttributeError:
|
|
_logger.error(f"Cannot find username from keys {key}")
|
|
try:
|
|
odoo_password = entry.password
|
|
except AttributeError:
|
|
_logger.error(f"Cannot find password from keys {key}")
|
|
lst_value.append(
|
|
" --default_email_auth"
|
|
f" {odoo_user} --default_password_auth '{odoo_password}'"
|
|
)
|
|
if len(lst_value) == 0:
|
|
return ""
|
|
elif len(lst_value) == 1:
|
|
return lst_value[0]
|
|
return lst_value
|
|
|
|
def prompt_execute_selenium_and_run_db(self, bd, extra_cmd_web_login=""):
|
|
cmd = (
|
|
f'parallel ::: "./run.sh -d {bd}" "sleep'
|
|
f' 3;./script/selenium/web_login.py{extra_cmd_web_login}"'
|
|
)
|
|
self.executer_commande_live(cmd)
|
|
|
|
def prompt_execute_selenium(self, command=None, extra_cmd_web_login=""):
|
|
lst_cmd = []
|
|
if not command:
|
|
cmd = "./script/selenium/web_login.py"
|
|
else:
|
|
cmd = command
|
|
|
|
if type(extra_cmd_web_login) is list:
|
|
for item in extra_cmd_web_login:
|
|
lst_cmd.append(cmd + item)
|
|
else:
|
|
lst_cmd.append(cmd + extra_cmd_web_login)
|
|
|
|
if len(lst_cmd) == 1:
|
|
self.executer_commande_live(lst_cmd[0])
|
|
elif len(lst_cmd) > 1:
|
|
new_cmd = "parallel ::: "
|
|
for i, cmd in enumerate(lst_cmd):
|
|
new_cmd += f' "sleep {1 * i};{cmd}"'
|
|
self.executer_commande_live(new_cmd)
|
|
|
|
def executer_commande_live(self, commande):
|
|
"""
|
|
Exécute une commande et affiche la sortie en direct.
|
|
|
|
Args:
|
|
commande (str): La commande à exécuter (sous forme de chaîne de caractères).
|
|
"""
|
|
try:
|
|
process = subprocess.Popen(
|
|
commande,
|
|
shell=True,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
text=True,
|
|
bufsize=1, # Désactive la mise en tampon pour la sortie en direct
|
|
universal_newlines=True, # Pour traiter les sauts de lignes correctement
|
|
)
|
|
|
|
while True:
|
|
ligne = process.stdout.readline()
|
|
if not ligne:
|
|
break
|
|
print(ligne, end="")
|
|
|
|
process.wait() # Attendre la fin du process
|
|
|
|
if process.returncode != 0:
|
|
print(
|
|
"La commande a retourné un code d'erreur :"
|
|
f" {process.returncode}"
|
|
)
|
|
|
|
except FileNotFoundError:
|
|
if "password" in commande:
|
|
print(
|
|
f"Erreur : La commande '{commande.split(' ')[0]}'[...] n'a"
|
|
" pas été trouvée."
|
|
)
|
|
else:
|
|
print(
|
|
f"Erreur : La commande '{commande}' n'a pas été trouvée."
|
|
)
|
|
except Exception as e:
|
|
print(f"Une erreur s'est produite : {e}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
todo = TODO()
|
|
todo.run()
|