seo-dev-env 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {seo_dev_env-0.1.0/seo_dev_env.egg-info → seo_dev_env-0.1.2}/PKG-INFO +2 -2
- seo_dev_env-0.1.2/seo/__init__.py +3 -0
- seo_dev_env-0.1.2/seo/cli.py +165 -0
- seo_dev_env-0.1.2/seo/commandes.py +93 -0
- seo_dev_env-0.1.2/seo/generators.py +342 -0
- seo_dev_env-0.1.2/seo/templates/debutant/app.py +16 -0
- seo_dev_env-0.1.2/seo/templates/debutant/index.html +46 -0
- seo_dev_env-0.1.2/seo/templates/debutant/style.css +110 -0
- seo_dev_env-0.1.2/seo/templates/intermediaire/app/__init__.py +38 -0
- seo_dev_env-0.1.2/seo/templates/intermediaire/app/core/__init__.py +0 -0
- seo_dev_env-0.1.2/seo/templates/intermediaire/app/core/config.py +23 -0
- seo_dev_env-0.1.2/seo/templates/intermediaire/app/taches/__init__.py +0 -0
- seo_dev_env-0.1.2/seo/templates/intermediaire/app/taches/models.py +18 -0
- seo_dev_env-0.1.2/seo/templates/intermediaire/app/taches/routes.py +44 -0
- seo_dev_env-0.1.2/seo/templates/intermediaire/app/utilisateurs/__init__.py +0 -0
- seo_dev_env-0.1.2/seo/templates/intermediaire/app/utilisateurs/models.py +29 -0
- seo_dev_env-0.1.2/seo/templates/intermediaire/app/utilisateurs/routes.py +34 -0
- seo_dev_env-0.1.2/seo/templates/intermediaire/config.py +10 -0
- seo_dev_env-0.1.2/seo/templates/intermediaire/docker-compose.yml +39 -0
- seo_dev_env-0.1.2/seo/templates/intermediaire/run.py +17 -0
- seo_dev_env-0.1.2/seo/templates/pro/Dockerfile +26 -0
- seo_dev_env-0.1.2/seo/templates/pro/docker-compose.yml +39 -0
- {seo_dev_env-0.1.0 → seo_dev_env-0.1.2}/seo/utils.py +32 -24
- {seo_dev_env-0.1.0 → seo_dev_env-0.1.2/seo_dev_env.egg-info}/PKG-INFO +2 -2
- {seo_dev_env-0.1.0 → seo_dev_env-0.1.2}/seo_dev_env.egg-info/SOURCES.txt +12 -0
- seo_dev_env-0.1.2/seo_dev_env.egg-info/entry_points.txt +3 -0
- {seo_dev_env-0.1.0 → seo_dev_env-0.1.2}/setup.py +16 -15
- seo_dev_env-0.1.0/seo/__init__.py +0 -3
- seo_dev_env-0.1.0/seo/generators.py +0 -161
- seo_dev_env-0.1.0/seo/templates/debutant/app.py +0 -0
- seo_dev_env-0.1.0/seo/templates/debutant/index.html +0 -0
- seo_dev_env-0.1.0/seo/templates/debutant/style.css +0 -0
- seo_dev_env-0.1.0/seo/templates/intermediaire/run.py +0 -0
- seo_dev_env-0.1.0/seo/templates/pro/app/api/v1/__init__.py +0 -0
- seo_dev_env-0.1.0/seo/templates/pro/app/core/config.py +0 -0
- seo_dev_env-0.1.0/seo/templates/pro/docker-compose.yml +0 -0
- seo_dev_env-0.1.0/seo_dev_env.egg-info/entry_points.txt +0 -2
- {seo_dev_env-0.1.0 → seo_dev_env-0.1.2}/LICENSE.ls +0 -0
- {seo_dev_env-0.1.0 → seo_dev_env-0.1.2}/README.md +0 -0
- {seo_dev_env-0.1.0 → seo_dev_env-0.1.2}/seo/templates/intermediaire/app/models.py +0 -0
- {seo_dev_env-0.1.0 → seo_dev_env-0.1.2}/seo/templates/intermediaire/app/routes.py +0 -0
- {seo_dev_env-0.1.0/seo/templates/intermediaire → seo_dev_env-0.1.2/seo/templates/pro}/app/__init__.py +0 -0
- {seo_dev_env-0.1.0/seo/templates/pro/app → seo_dev_env-0.1.2/seo/templates/pro/app/api}/__init__.py +0 -0
- {seo_dev_env-0.1.0/seo/templates/pro/app/api → seo_dev_env-0.1.2/seo/templates/pro/app/api/v1}/__init__.py +0 -0
- {seo_dev_env-0.1.0 → seo_dev_env-0.1.2}/seo/templates/pro/app/api/v1/endpoints.py +0 -0
- {seo_dev_env-0.1.0/seo/templates/intermediaire → seo_dev_env-0.1.2/seo/templates/pro/app/core}/config.py +0 -0
- {seo_dev_env-0.1.0 → seo_dev_env-0.1.2}/seo/templates/pro/app/core/security.py +0 -0
- {seo_dev_env-0.1.0 → seo_dev_env-0.1.2}/seo/templates/pro/app/db/base.py +0 -0
- {seo_dev_env-0.1.0 → seo_dev_env-0.1.2}/seo/templates/pro/app/db/models.py +0 -0
- {seo_dev_env-0.1.0 → seo_dev_env-0.1.2}/seo_dev_env.egg-info/dependency_links.txt +0 -0
- {seo_dev_env-0.1.0 → seo_dev_env-0.1.2}/seo_dev_env.egg-info/requires.txt +0 -0
- {seo_dev_env-0.1.0 → seo_dev_env-0.1.2}/seo_dev_env.egg-info/top_level.txt +0 -0
- {seo_dev_env-0.1.0 → seo_dev_env-0.1.2}/setup.cfg +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: seo-dev-env
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Générateur d'environnements de développement pour tous niveaux
|
|
5
|
-
Home-page: https://github.com/
|
|
5
|
+
Home-page: https://github.com/elkast/seo-dev-env
|
|
6
6
|
Author: Votre Nom
|
|
7
7
|
Author-email: orsinimelchisedek@gmail.com
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""CLI interactif pour SEO Dev Env"""
|
|
2
|
+
import sys
|
|
3
|
+
from typing import Dict, Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def afficher_titre():
|
|
7
|
+
print("\n" + "="*60)
|
|
8
|
+
print(" SEO Dev Env - Créateur d'architecture Flask")
|
|
9
|
+
print(" pour développeurs francophones")
|
|
10
|
+
print("="*60 + "\n")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def poser_question(question: str, options: list, defaut: int = 1) -> int:
|
|
14
|
+
print(f"\n{question}")
|
|
15
|
+
for i, option in enumerate(options, 1):
|
|
16
|
+
marqueur = "" if i == defaut else " "
|
|
17
|
+
print(f" {marqueur} {i}. {option}")
|
|
18
|
+
while True:
|
|
19
|
+
try:
|
|
20
|
+
choix = input(f"\nVotre choix [1-{len(options)}] (défaut: {defaut}): ").strip()
|
|
21
|
+
if not choix:
|
|
22
|
+
return defaut - 1
|
|
23
|
+
choix_int = int(choix)
|
|
24
|
+
if 1 <= choix_int <= len(options):
|
|
25
|
+
return choix_int - 1
|
|
26
|
+
else:
|
|
27
|
+
print(f" Veuillez choisir entre 1 et {len(options)}")
|
|
28
|
+
except ValueError:
|
|
29
|
+
print(" Veuillez entrer un nombre valide")
|
|
30
|
+
except KeyboardInterrupt:
|
|
31
|
+
print("\n\n Annulé")
|
|
32
|
+
sys.exit(0)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def poser_question_texte(question: str, defaut: str = "") -> str:
|
|
36
|
+
prompt = f"{question} (défaut: {defaut}): " if defaut else f"{question}: "
|
|
37
|
+
try:
|
|
38
|
+
reponse = input(prompt).strip()
|
|
39
|
+
return reponse if reponse else defaut
|
|
40
|
+
except KeyboardInterrupt:
|
|
41
|
+
print("\n\n Annulé")
|
|
42
|
+
sys.exit(0)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def confirmer(question: str, defaut: bool = True) -> bool:
|
|
46
|
+
suffixe = "[O/n]" if defaut else "[o/N]"
|
|
47
|
+
try:
|
|
48
|
+
reponse = input(f"{question} {suffixe}: ").strip().lower()
|
|
49
|
+
if not reponse:
|
|
50
|
+
return defaut
|
|
51
|
+
return reponse in ["o", "oui", "y", "yes"]
|
|
52
|
+
except KeyboardInterrupt:
|
|
53
|
+
print("\n\n Annulé")
|
|
54
|
+
sys.exit(0)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def collecter_preferences() -> Dict[str, Any]:
|
|
58
|
+
afficher_titre()
|
|
59
|
+
preferences = {}
|
|
60
|
+
|
|
61
|
+
print(" Configuration du projet")
|
|
62
|
+
preferences["nom_projet"] = poser_question_texte("Nom de votre projet", "mon-projet")
|
|
63
|
+
|
|
64
|
+
type_options = [
|
|
65
|
+
"Apprentissage (simple, pour débuter)",
|
|
66
|
+
"Application web (structure MVC complète)",
|
|
67
|
+
"API professionnelle (production-ready)",
|
|
68
|
+
"Startup SaaS (auth, paiement, dashboard)"
|
|
69
|
+
]
|
|
70
|
+
type_choix = poser_question(" Quel type de projet ?", type_options)
|
|
71
|
+
types_mapping = {0: "apprentissage", 1: "application", 2: "api", 3: "saas"}
|
|
72
|
+
preferences["type_projet"] = types_mapping[type_choix]
|
|
73
|
+
|
|
74
|
+
if preferences["type_projet"] in ["application", "api", "saas"]:
|
|
75
|
+
db_options = [
|
|
76
|
+
"SQLite (simple, fichier local)",
|
|
77
|
+
"PostgreSQL (recommandé pour production)",
|
|
78
|
+
"MySQL (compatible, largement utilisé)"
|
|
79
|
+
]
|
|
80
|
+
db_choix = poser_question(" Quelle base de données ?", db_options,
|
|
81
|
+
defaut=2 if preferences["type_projet"] in ["api", "saas"] else 1)
|
|
82
|
+
db_mapping = {0: "sqlite", 1: "postgresql", 2: "mysql"}
|
|
83
|
+
preferences["base_donnees"] = db_mapping[db_choix]
|
|
84
|
+
|
|
85
|
+
if preferences["base_donnees"] == "sqlite" and preferences["type_projet"] in ["api", "saas"]:
|
|
86
|
+
print("\n ATTENTION: SQLite non recommandé pour production.")
|
|
87
|
+
if not confirmer("\n Continuer avec SQLite ?", defaut=False):
|
|
88
|
+
print("\n Passage à PostgreSQL")
|
|
89
|
+
preferences["base_donnees"] = "postgresql"
|
|
90
|
+
|
|
91
|
+
auth_options = [
|
|
92
|
+
"Session classique (cookies Flask)",
|
|
93
|
+
"JWT (tokens, pour API)",
|
|
94
|
+
"OAuth2 (Google, GitHub, etc.)"
|
|
95
|
+
]
|
|
96
|
+
auth_choix = poser_question(" Type d'authentification ?", auth_options,
|
|
97
|
+
defaut=2 if preferences["type_projet"] == "api" else 1)
|
|
98
|
+
auth_mapping = {0: "session", 1: "jwt", 2: "oauth"}
|
|
99
|
+
preferences["auth"] = auth_mapping[auth_choix]
|
|
100
|
+
|
|
101
|
+
if preferences["type_projet"] in ["api", "saas"]:
|
|
102
|
+
preferences["docker"] = True
|
|
103
|
+
print("\n Docker inclus automatiquement")
|
|
104
|
+
else:
|
|
105
|
+
preferences["docker"] = confirmer("\n Inclure Docker ?", defaut=True)
|
|
106
|
+
else:
|
|
107
|
+
preferences["base_donnees"] = "sqlite"
|
|
108
|
+
preferences["auth"] = "session"
|
|
109
|
+
preferences["docker"] = False
|
|
110
|
+
|
|
111
|
+
if preferences["type_projet"] == "saas":
|
|
112
|
+
preferences["stripe"] = confirmer("\n Inclure Stripe ?", defaut=True)
|
|
113
|
+
preferences["email"] = confirmer(" Inclure Flask-Mail ?", defaut=True)
|
|
114
|
+
preferences["celery"] = confirmer(" Inclure Celery ?", defaut=True)
|
|
115
|
+
|
|
116
|
+
preferences["git"] = confirmer("\n Initialiser Git ?", defaut=True)
|
|
117
|
+
return preferences
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def afficher_resume(preferences: Dict[str, Any]):
|
|
121
|
+
print("\n" + "="*60)
|
|
122
|
+
print(" Résumé")
|
|
123
|
+
print("="*60)
|
|
124
|
+
print(f"\n Projet: {preferences['nom_projet']}")
|
|
125
|
+
print(f" Type: {preferences['type_projet']}")
|
|
126
|
+
if "base_donnees" in preferences:
|
|
127
|
+
print(f" Base: {preferences['base_donnees']}")
|
|
128
|
+
if "auth" in preferences:
|
|
129
|
+
print(f" Auth: {preferences['auth']}")
|
|
130
|
+
if preferences.get("docker"):
|
|
131
|
+
print(" Docker: ")
|
|
132
|
+
if preferences.get("stripe"):
|
|
133
|
+
print(" Stripe: ")
|
|
134
|
+
if preferences.get("email"):
|
|
135
|
+
print(" Email: ")
|
|
136
|
+
if preferences.get("celery"):
|
|
137
|
+
print(" Celery: ")
|
|
138
|
+
if preferences.get("git"):
|
|
139
|
+
print(" Git: ")
|
|
140
|
+
print("\n" + "="*60 + "\n")
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def afficher_prochaines_etapes(nom_projet: str, type_projet: str, docker: bool = False):
|
|
144
|
+
print("\n" + "="*60)
|
|
145
|
+
print(" Projet créé !")
|
|
146
|
+
print("="*60)
|
|
147
|
+
print(f"\n Prochaines étapes:\n")
|
|
148
|
+
print(f" 1. cd {nom_projet}")
|
|
149
|
+
if docker:
|
|
150
|
+
print(" 2. docker-compose up --build")
|
|
151
|
+
else:
|
|
152
|
+
if type_projet == "apprentissage":
|
|
153
|
+
print(" 2. python app.py")
|
|
154
|
+
else:
|
|
155
|
+
print(" 2. python -m venv venv")
|
|
156
|
+
print(" 3. venv\\Scripts\\activate")
|
|
157
|
+
print(" 4. pip install -r requirements.txt")
|
|
158
|
+
print(" 5. seo db init")
|
|
159
|
+
print(" 6. seo user create")
|
|
160
|
+
print(" 7. seo run")
|
|
161
|
+
print("\n Commandes:")
|
|
162
|
+
print(" seo db init - Initialiser la base")
|
|
163
|
+
print(" seo user create - Créer un admin")
|
|
164
|
+
print(" seo run - Lancer l'app")
|
|
165
|
+
print("\n" + "="*60 + "\n")
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Commandes internes pour gérer le projet Flask
|
|
3
|
+
"""
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import subprocess
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def commande_db(action: str):
|
|
11
|
+
"""Gestion de la base de données"""
|
|
12
|
+
if action == "init":
|
|
13
|
+
print(" Initialisation de la base...")
|
|
14
|
+
subprocess.run([sys.executable, "-m", "flask", "db", "init"])
|
|
15
|
+
print(" Base initialisée")
|
|
16
|
+
elif action == "migrate":
|
|
17
|
+
message = input("Message (optionnel): ").strip()
|
|
18
|
+
if message:
|
|
19
|
+
subprocess.run([sys.executable, "-m", "flask", "db", "migrate", "-m", message])
|
|
20
|
+
else:
|
|
21
|
+
subprocess.run([sys.executable, "-m", "flask", "db", "migrate"])
|
|
22
|
+
print(" Migration créée")
|
|
23
|
+
elif action == "upgrade":
|
|
24
|
+
subprocess.run([sys.executable, "-m", "flask", "db", "upgrade"])
|
|
25
|
+
print(" Migrations appliquées")
|
|
26
|
+
else:
|
|
27
|
+
print(f" Action '{action}' inconnue")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def commande_user(action: str):
|
|
31
|
+
"""Gestion des utilisateurs"""
|
|
32
|
+
if action == "create":
|
|
33
|
+
print(" Création admin\n")
|
|
34
|
+
username = input("Username: ").strip()
|
|
35
|
+
email = input("Email: ").strip()
|
|
36
|
+
import getpass
|
|
37
|
+
password = getpass.getpass("Password: ")
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
from app import create_app, db
|
|
41
|
+
from app.models import User
|
|
42
|
+
app = create_app()
|
|
43
|
+
with app.app_context():
|
|
44
|
+
user = User(username=username, email=email, is_admin=True)
|
|
45
|
+
user.set_password(password)
|
|
46
|
+
db.session.add(user)
|
|
47
|
+
db.session.commit()
|
|
48
|
+
print(f" Utilisateur '{username}' créé")
|
|
49
|
+
except ImportError as e:
|
|
50
|
+
print(f" Erreur: {e}")
|
|
51
|
+
elif action == "list":
|
|
52
|
+
try:
|
|
53
|
+
from app import create_app, db
|
|
54
|
+
from app.models import User
|
|
55
|
+
app = create_app()
|
|
56
|
+
with app.app_context():
|
|
57
|
+
users = User.query.all()
|
|
58
|
+
print("\n Utilisateurs:\n")
|
|
59
|
+
for user in users:
|
|
60
|
+
print(f" {user.username}")
|
|
61
|
+
except ImportError:
|
|
62
|
+
print(" Impossible de charger les utilisateurs")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def commande_run(mode: str = "dev"):
|
|
66
|
+
"""Lance l'application"""
|
|
67
|
+
if mode == "dev":
|
|
68
|
+
print(" Mode développement...\n")
|
|
69
|
+
os.environ["FLASK_ENV"] = "development"
|
|
70
|
+
os.environ["FLASK_DEBUG"] = "1"
|
|
71
|
+
if Path("run.py").exists():
|
|
72
|
+
subprocess.run([sys.executable, "run.py"])
|
|
73
|
+
elif Path("app.py").exists():
|
|
74
|
+
subprocess.run([sys.executable, "app.py"])
|
|
75
|
+
else:
|
|
76
|
+
subprocess.run([sys.executable, "-m", "flask", "run", "--debug"])
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def afficher_aide():
|
|
80
|
+
"""Affiche l'aide"""
|
|
81
|
+
print("\n" + "="*60)
|
|
82
|
+
print(" Commandes SEO")
|
|
83
|
+
print("="*60 + "\n")
|
|
84
|
+
print(" Base de données:")
|
|
85
|
+
print(" seo db init - Initialiser")
|
|
86
|
+
print(" seo db migrate - Créer migration")
|
|
87
|
+
print(" seo db upgrade - Appliquer")
|
|
88
|
+
print("\n Utilisateurs:")
|
|
89
|
+
print(" seo user create - Créer admin")
|
|
90
|
+
print(" seo user list - Lister")
|
|
91
|
+
print("\n Lancement:")
|
|
92
|
+
print(" seo run - Mode dev")
|
|
93
|
+
print("\n" + "="*60 + "\n")
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
import shutil
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Dict, Any
|
|
7
|
+
from .utils import creer_fichier, copier_dossier
|
|
8
|
+
from .cli import collecter_preferences, afficher_resume, afficher_prochaines_etapes
|
|
9
|
+
|
|
10
|
+
class EnvironnementGenerator:
|
|
11
|
+
"""Classe de base pour générer des environnements"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, niveau: str, type_app: str, chemin: str):
|
|
14
|
+
self.niveau = niveau
|
|
15
|
+
self.type_app = type_app
|
|
16
|
+
self.chemin_projet = Path(chemin).resolve()
|
|
17
|
+
self.packages = []
|
|
18
|
+
self.template_dir = Path(__file__).parent / 'templates' / niveau
|
|
19
|
+
|
|
20
|
+
def _installer_dependances(self):
|
|
21
|
+
"""Installe les packages nécessaires"""
|
|
22
|
+
if self.packages:
|
|
23
|
+
print(f"🔧 Installation des packages: {', '.join(self.packages)}")
|
|
24
|
+
subprocess.run([sys.executable, '-m', 'pip', 'install'] + self.packages)
|
|
25
|
+
|
|
26
|
+
def _copier_template(self):
|
|
27
|
+
"""Copie les fichiers du template"""
|
|
28
|
+
if self.template_dir.exists():
|
|
29
|
+
copier_dossier(self.template_dir, self.chemin_projet)
|
|
30
|
+
else:
|
|
31
|
+
print(f"⚠️ Avertissement: Template {self.niveau} non trouvé, création de base")
|
|
32
|
+
self._creer_structure_base()
|
|
33
|
+
|
|
34
|
+
def _creer_structure_base(self):
|
|
35
|
+
"""Crée une structure de base si le template est manquant"""
|
|
36
|
+
creer_fichier(self.chemin_projet / 'app.py', "# Votre application Flask")
|
|
37
|
+
(self.chemin_projet / 'templates').mkdir(exist_ok=True)
|
|
38
|
+
creer_fichier(self.chemin_projet / 'templates/index.html', "<h1>Bienvenue</h1>")
|
|
39
|
+
|
|
40
|
+
def _post_creation(self):
|
|
41
|
+
"""Actions supplémentaires après création"""
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
def generer(self):
|
|
45
|
+
"""Méthode principale pour générer l'environnement"""
|
|
46
|
+
self.chemin_projet.mkdir(exist_ok=True, parents=True)
|
|
47
|
+
print(f"🏗️ Création de l'environnement {self.niveau} ({self.type_app})...")
|
|
48
|
+
|
|
49
|
+
self._copier_template()
|
|
50
|
+
self._creer_structure()
|
|
51
|
+
self._installer_dependances()
|
|
52
|
+
self._post_creation()
|
|
53
|
+
|
|
54
|
+
print(f"✅ Environnement créé avec succès dans {self.chemin_projet}")
|
|
55
|
+
print("👉 Pour démarrer: cd " + str(self.chemin_projet))
|
|
56
|
+
|
|
57
|
+
class DebutantWebGenerator(EnvironnementGenerator):
|
|
58
|
+
"""Générateur pour débutants - Site web simple"""
|
|
59
|
+
|
|
60
|
+
def __init__(self, chemin):
|
|
61
|
+
super().__init__('debutant', 'web', chemin)
|
|
62
|
+
self.packages = ['flask', 'python-dotenv']
|
|
63
|
+
|
|
64
|
+
def _creer_structure(self):
|
|
65
|
+
# Personnalisation supplémentaire
|
|
66
|
+
creer_fichier(
|
|
67
|
+
self.chemin_projet / 'README.md',
|
|
68
|
+
f"# Mon Premier Projet Flask\n\nCe projet a été créé avec SEO pour les débutants!"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
class IntermediaireWebGenerator(EnvironnementGenerator):
|
|
72
|
+
"""Générateur intermédiaire - Applications web complètes"""
|
|
73
|
+
|
|
74
|
+
def __init__(self, chemin):
|
|
75
|
+
super().__init__('intermediaire', 'web', chemin)
|
|
76
|
+
self.packages = [
|
|
77
|
+
'flask',
|
|
78
|
+
'flask-sqlalchemy',
|
|
79
|
+
'flask-wtf',
|
|
80
|
+
'flask-login',
|
|
81
|
+
'flask-migrate',
|
|
82
|
+
'python-dotenv'
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
def _creer_structure(self):
|
|
86
|
+
# Création de la base de données
|
|
87
|
+
db_path = self.chemin_projet / 'app.db'
|
|
88
|
+
if not db_path.exists():
|
|
89
|
+
with open(db_path, 'w') as f:
|
|
90
|
+
f.write("")
|
|
91
|
+
|
|
92
|
+
# Création du fichier requirements
|
|
93
|
+
creer_fichier(
|
|
94
|
+
self.chemin_projet / 'requirements.txt',
|
|
95
|
+
"\n".join(self.packages)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
class ProWebGenerator(EnvironnementGenerator):
|
|
99
|
+
"""Générateur pro - Applications professionnelles"""
|
|
100
|
+
|
|
101
|
+
def __init__(self, chemin):
|
|
102
|
+
super().__init__('pro', 'web', chemin)
|
|
103
|
+
self.packages = [
|
|
104
|
+
'flask',
|
|
105
|
+
'flask-restx',
|
|
106
|
+
'flask-cors',
|
|
107
|
+
'flask-sqlalchemy',
|
|
108
|
+
'flask-migrate',
|
|
109
|
+
'flask-jwt-extended',
|
|
110
|
+
'python-dotenv',
|
|
111
|
+
'gunicorn',
|
|
112
|
+
'psycopg2-binary'
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
def _creer_structure(self):
|
|
116
|
+
# Création du fichier requirements
|
|
117
|
+
creer_fichier(
|
|
118
|
+
self.chemin_projet / 'requirements.txt',
|
|
119
|
+
"\n".join(self.packages)
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
def _post_creation(self):
|
|
123
|
+
# Initialiser un dépôt Git
|
|
124
|
+
try:
|
|
125
|
+
subprocess.run(['git', 'init', str(self.chemin_projet)], check=True)
|
|
126
|
+
print("📦 Dépôt Git initialisé")
|
|
127
|
+
except:
|
|
128
|
+
print("⚠️ Git non installé, ignore l'initialisation du dépôt")
|
|
129
|
+
|
|
130
|
+
# Interface simplifiée
|
|
131
|
+
def creer_environnement(niveau, type_app='web', chemin='.'):
|
|
132
|
+
"""
|
|
133
|
+
Crée un environnement de développement adapté
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
niveau: 'debutant', 'intermediaire' ou 'pro'
|
|
137
|
+
type_app: 'web' ou 'mobile' (défaut: 'web')
|
|
138
|
+
chemin: Chemin où créer le projet (défaut: dossier actuel)
|
|
139
|
+
"""
|
|
140
|
+
generators = {
|
|
141
|
+
'debutant': DebutantWebGenerator,
|
|
142
|
+
'intermediaire': IntermediaireWebGenerator,
|
|
143
|
+
'pro': ProWebGenerator
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if niveau not in generators:
|
|
147
|
+
raise ValueError(f"🚫 Niveau non supporté: {niveau}")
|
|
148
|
+
|
|
149
|
+
generator = generators[niveau](chemin)
|
|
150
|
+
generator.generer()
|
|
151
|
+
|
|
152
|
+
# Ajout des instructions spécifiques
|
|
153
|
+
if niveau == 'debutant':
|
|
154
|
+
print("\n🚀 Pour démarrer votre application:")
|
|
155
|
+
print(f"cd {chemin}")
|
|
156
|
+
print("python app.py")
|
|
157
|
+
elif niveau == 'intermediaire':
|
|
158
|
+
print("\n🚀 Pour initialiser la base de données:")
|
|
159
|
+
print(f"cd {chemin}")
|
|
160
|
+
print("flask db init")
|
|
161
|
+
print("flask db migrate")
|
|
162
|
+
print("flask db upgrade")
|
|
163
|
+
print("flask run")
|
|
164
|
+
else:
|
|
165
|
+
print("\n🚀 Pour démarrer avec Docker:")
|
|
166
|
+
print(f"cd {chemin}")
|
|
167
|
+
print("docker-compose up --build")
|
|
168
|
+
|
|
169
|
+
# Alias pour une utilisation plus simple
|
|
170
|
+
creer_projet = creer_environnement
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def main():
|
|
174
|
+
"""Point d'entrée principal pour le CLI"""
|
|
175
|
+
import argparse
|
|
176
|
+
|
|
177
|
+
parser = argparse.ArgumentParser(
|
|
178
|
+
description='SEO Dev Env - Générateur de projets Flask pour francophones'
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
subparsers = parser.add_subparsers(dest='commande', help='Commandes disponibles')
|
|
182
|
+
|
|
183
|
+
# Commande create (mode interactif)
|
|
184
|
+
parser_create = subparsers.add_parser('create', help='Créer un nouveau projet (mode interactif)')
|
|
185
|
+
parser_create.add_argument('nom', nargs='?', help='Nom du projet (optionnel, sera demandé si non fourni)')
|
|
186
|
+
|
|
187
|
+
# Commandes db
|
|
188
|
+
parser_db = subparsers.add_parser('db', help='Gestion de la base de données')
|
|
189
|
+
parser_db.add_argument('action', choices=['init', 'migrate', 'upgrade', 'downgrade'])
|
|
190
|
+
|
|
191
|
+
# Commandes user
|
|
192
|
+
parser_user = subparsers.add_parser('user', help='Gestion des utilisateurs')
|
|
193
|
+
parser_user.add_argument('action', choices=['create', 'list'])
|
|
194
|
+
|
|
195
|
+
# Commande run
|
|
196
|
+
parser_run = subparsers.add_parser('run', help='Lancer l\'application')
|
|
197
|
+
parser_run.add_argument('mode', nargs='?', default='dev', choices=['dev', 'prod'])
|
|
198
|
+
|
|
199
|
+
# Commande help
|
|
200
|
+
parser_help = subparsers.add_parser('help', help='Afficher l\'aide')
|
|
201
|
+
|
|
202
|
+
args = parser.parse_args()
|
|
203
|
+
|
|
204
|
+
# Si aucune commande, afficher l'aide
|
|
205
|
+
if not args.commande:
|
|
206
|
+
from .commandes import afficher_aide
|
|
207
|
+
afficher_aide()
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
# Traiter les commandes
|
|
211
|
+
if args.commande == 'create':
|
|
212
|
+
creer_projet_interactif(args.nom)
|
|
213
|
+
|
|
214
|
+
elif args.commande == 'db':
|
|
215
|
+
from .commandes import commande_db
|
|
216
|
+
commande_db(args.action)
|
|
217
|
+
|
|
218
|
+
elif args.commande == 'user':
|
|
219
|
+
from .commandes import commande_user
|
|
220
|
+
commande_user(args.action)
|
|
221
|
+
|
|
222
|
+
elif args.commande == 'run':
|
|
223
|
+
from .commandes import commande_run
|
|
224
|
+
commande_run(args.mode)
|
|
225
|
+
|
|
226
|
+
elif args.commande == 'help':
|
|
227
|
+
from .commandes import afficher_aide
|
|
228
|
+
afficher_aide()
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def creer_projet_interactif(nom_fourni: str = None):
|
|
232
|
+
"""Crée un projet en mode interactif"""
|
|
233
|
+
# Collecter les préférences
|
|
234
|
+
preferences = collecter_preferences()
|
|
235
|
+
|
|
236
|
+
# Utiliser le nom fourni en argument si disponible
|
|
237
|
+
if nom_fourni:
|
|
238
|
+
preferences['nom_projet'] = nom_fourni
|
|
239
|
+
|
|
240
|
+
# Afficher le résumé
|
|
241
|
+
afficher_resume(preferences)
|
|
242
|
+
|
|
243
|
+
# Confirmer
|
|
244
|
+
from .cli import confirmer
|
|
245
|
+
if not confirmer("Créer le projet avec cette configuration ?"):
|
|
246
|
+
print("❌ Annulé")
|
|
247
|
+
return
|
|
248
|
+
|
|
249
|
+
# Générer le projet selon le type
|
|
250
|
+
generer_selon_preferences(preferences)
|
|
251
|
+
|
|
252
|
+
# Afficher les prochaines étapes
|
|
253
|
+
afficher_prochaines_etapes(
|
|
254
|
+
preferences['nom_projet'],
|
|
255
|
+
preferences['type_projet'],
|
|
256
|
+
preferences.get('docker', False)
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def generer_selon_preferences(preferences: Dict[str, Any]):
|
|
261
|
+
"""Génère le projet selon les préférences"""
|
|
262
|
+
type_projet = preferences['type_projet']
|
|
263
|
+
nom_projet = preferences['nom_projet']
|
|
264
|
+
|
|
265
|
+
if type_projet == 'apprentissage':
|
|
266
|
+
generator = ApprentissageGenerator(nom_projet, preferences)
|
|
267
|
+
elif type_projet == 'application':
|
|
268
|
+
generator = ApplicationGenerator(nom_projet, preferences)
|
|
269
|
+
elif type_projet == 'api':
|
|
270
|
+
generator = APIGenerator(nom_projet, preferences)
|
|
271
|
+
elif type_projet == 'saas':
|
|
272
|
+
generator = SaaSGenerator(nom_projet, preferences)
|
|
273
|
+
else:
|
|
274
|
+
raise ValueError(f"Type de projet inconnu: {type_projet}")
|
|
275
|
+
|
|
276
|
+
generator.generer()
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class ApprentissageGenerator(EnvironnementGenerator):
|
|
280
|
+
"""Générateur pour débutants"""
|
|
281
|
+
def __init__(self, nom: str, preferences: Dict[str, Any]):
|
|
282
|
+
super().__init__('debutant', 'web', nom)
|
|
283
|
+
self.preferences = preferences
|
|
284
|
+
self.packages = ['flask', 'python-dotenv']
|
|
285
|
+
|
|
286
|
+
def _creer_structure(self):
|
|
287
|
+
creer_fichier(self.chemin_projet / 'README.md',
|
|
288
|
+
f"# {self.preferences['nom_projet']}\n\nProjet Flask pour apprentissage")
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
class ApplicationGenerator(EnvironnementGenerator):
|
|
292
|
+
"""Générateur pour applications web (architecture par feature)"""
|
|
293
|
+
def __init__(self, nom: str, preferences: Dict[str, Any]):
|
|
294
|
+
super().__init__('intermediaire', 'web', nom)
|
|
295
|
+
self.preferences = preferences
|
|
296
|
+
self.packages = [
|
|
297
|
+
'flask', 'flask-sqlalchemy', 'flask-migrate',
|
|
298
|
+
'flask-wtf', 'flask-login', 'python-dotenv'
|
|
299
|
+
]
|
|
300
|
+
|
|
301
|
+
def _creer_structure(self):
|
|
302
|
+
creer_fichier(self.chemin_projet / 'requirements.txt', "\n".join(self.packages))
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
class APIGenerator(EnvironnementGenerator):
|
|
306
|
+
"""Générateur pour API professionnelles"""
|
|
307
|
+
def __init__(self, nom: str, preferences: Dict[str, Any]):
|
|
308
|
+
super().__init__('pro', 'api', nom)
|
|
309
|
+
self.preferences = preferences
|
|
310
|
+
self.packages = [
|
|
311
|
+
'flask', 'flask-restx', 'flask-cors', 'flask-sqlalchemy',
|
|
312
|
+
'flask-migrate', 'flask-jwt-extended', 'python-dotenv', 'gunicorn'
|
|
313
|
+
]
|
|
314
|
+
if preferences.get('base_donnees') == 'postgresql':
|
|
315
|
+
self.packages.append('psycopg2-binary')
|
|
316
|
+
elif preferences.get('base_donnees') == 'mysql':
|
|
317
|
+
self.packages.append('pymysql')
|
|
318
|
+
|
|
319
|
+
def _creer_structure(self):
|
|
320
|
+
creer_fichier(self.chemin_projet / 'requirements.txt', "\n".join(self.packages))
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
class SaaSGenerator(EnvironnementGenerator):
|
|
324
|
+
"""Générateur pour startup SaaS"""
|
|
325
|
+
def __init__(self, nom: str, preferences: Dict[str, Any]):
|
|
326
|
+
super().__init__('pro', 'saas', nom)
|
|
327
|
+
self.preferences = preferences
|
|
328
|
+
self.packages = [
|
|
329
|
+
'flask', 'flask-restx', 'flask-cors', 'flask-sqlalchemy',
|
|
330
|
+
'flask-migrate', 'flask-jwt-extended', 'python-dotenv', 'gunicorn'
|
|
331
|
+
]
|
|
332
|
+
if preferences.get('stripe'):
|
|
333
|
+
self.packages.append('stripe')
|
|
334
|
+
if preferences.get('email'):
|
|
335
|
+
self.packages.append('flask-mail')
|
|
336
|
+
if preferences.get('celery'):
|
|
337
|
+
self.packages.extend(['celery', 'redis'])
|
|
338
|
+
if preferences.get('base_donnees') == 'postgresql':
|
|
339
|
+
self.packages.append('psycopg2-binary')
|
|
340
|
+
|
|
341
|
+
def _creer_structure(self):
|
|
342
|
+
creer_fichier(self.chemin_projet / 'requirements.txt', "\n".join(self.packages))
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from flask import Flask, render_template
|
|
2
|
+
|
|
3
|
+
app = Flask(__name__)
|
|
4
|
+
app.config["SECRET_KEY"] = "votre-cle-secrete-changez-moi"
|
|
5
|
+
|
|
6
|
+
@app.route("/")
|
|
7
|
+
def accueil():
|
|
8
|
+
return render_template("index.html", titre="Bienvenue")
|
|
9
|
+
|
|
10
|
+
@app.route("/a-propos")
|
|
11
|
+
def a_propos():
|
|
12
|
+
return render_template("a_propos.html", titre="À propos")
|
|
13
|
+
|
|
14
|
+
if __name__ == "__main__":
|
|
15
|
+
print(" Application Flask démarrée sur http://localhost:5000")
|
|
16
|
+
app.run(debug=True)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="fr">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{ titre }} - Mon Projet Flask</title>
|
|
7
|
+
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<nav>
|
|
11
|
+
<div class="container">
|
|
12
|
+
<h1> Mon Projet Flask</h1>
|
|
13
|
+
<ul>
|
|
14
|
+
<li><a href="/">Accueil</a></li>
|
|
15
|
+
<li><a href="/a-propos">À propos</a></li>
|
|
16
|
+
</ul>
|
|
17
|
+
</div>
|
|
18
|
+
</nav>
|
|
19
|
+
|
|
20
|
+
<main class="container">
|
|
21
|
+
<h2>Bienvenue sur votre première application Flask !</h2>
|
|
22
|
+
<p>Cette application a été générée avec <strong>SEO Dev Env</strong>.</p>
|
|
23
|
+
|
|
24
|
+
<div class="card">
|
|
25
|
+
<h3> Prochaines étapes</h3>
|
|
26
|
+
<ul>
|
|
27
|
+
<li>Personnalisez ce template dans <code>templates/index.html</code></li>
|
|
28
|
+
<li>Modifiez le style dans <code>static/style.css</code></li>
|
|
29
|
+
<li>Ajoutez de nouvelles routes dans <code>app.py</code></li>
|
|
30
|
+
</ul>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="card">
|
|
34
|
+
<h3> Ressources utiles</h3>
|
|
35
|
+
<ul>
|
|
36
|
+
<li><a href="https://flask.palletsprojects.com/" target="_blank">Documentation Flask</a></li>
|
|
37
|
+
<li><a href="https://jinja.palletsprojects.com/" target="_blank">Documentation Jinja2</a></li>
|
|
38
|
+
</ul>
|
|
39
|
+
</div>
|
|
40
|
+
</main>
|
|
41
|
+
|
|
42
|
+
<footer>
|
|
43
|
+
<p>Créé avec en utilisant Flask</p>
|
|
44
|
+
</footer>
|
|
45
|
+
</body>
|
|
46
|
+
</html>
|