seo-dev-env 0.1.0__py3-none-any.whl
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/__init__.py +3 -0
- seo/generators.py +161 -0
- seo/templates/debutant/app.py +0 -0
- seo/templates/debutant/index.html +0 -0
- seo/templates/debutant/style.css +0 -0
- seo/templates/intermediaire/app/__init__.py +0 -0
- seo/templates/intermediaire/app/models.py +0 -0
- seo/templates/intermediaire/app/routes.py +0 -0
- seo/templates/intermediaire/config.py +0 -0
- seo/templates/intermediaire/run.py +0 -0
- seo/templates/pro/app/__init__.py +0 -0
- seo/templates/pro/app/api/__init__.py +0 -0
- seo/templates/pro/app/api/v1/__init__.py +0 -0
- seo/templates/pro/app/api/v1/endpoints.py +0 -0
- seo/templates/pro/app/core/config.py +0 -0
- seo/templates/pro/app/core/security.py +0 -0
- seo/templates/pro/app/db/base.py +0 -0
- seo/templates/pro/app/db/models.py +0 -0
- seo/templates/pro/docker-compose.yml +0 -0
- seo/utils.py +24 -0
- seo_dev_env-0.1.0.dist-info/METADATA +538 -0
- seo_dev_env-0.1.0.dist-info/RECORD +26 -0
- seo_dev_env-0.1.0.dist-info/WHEEL +5 -0
- seo_dev_env-0.1.0.dist-info/entry_points.txt +2 -0
- seo_dev_env-0.1.0.dist-info/licenses/LICENSE.ls +0 -0
- seo_dev_env-0.1.0.dist-info/top_level.txt +1 -0
seo/__init__.py
ADDED
seo/generators.py
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
import shutil
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from .utils import creer_fichier, copier_dossier
|
|
7
|
+
|
|
8
|
+
class EnvironnementGenerator:
|
|
9
|
+
"""Classe de base pour générer des environnements"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, niveau: str, type_app: str, chemin: str):
|
|
12
|
+
self.niveau = niveau
|
|
13
|
+
self.type_app = type_app
|
|
14
|
+
self.chemin_projet = Path(chemin).resolve()
|
|
15
|
+
self.packages = []
|
|
16
|
+
self.template_dir = Path(__file__).parent / 'templates' / niveau
|
|
17
|
+
|
|
18
|
+
def _installer_dependances(self):
|
|
19
|
+
"""Installe les packages nécessaires"""
|
|
20
|
+
if self.packages:
|
|
21
|
+
print(f"🔧 Installation des packages: {', '.join(self.packages)}")
|
|
22
|
+
subprocess.run([sys.executable, '-m', 'pip', 'install'] + self.packages)
|
|
23
|
+
|
|
24
|
+
def _copier_template(self):
|
|
25
|
+
"""Copie les fichiers du template"""
|
|
26
|
+
if self.template_dir.exists():
|
|
27
|
+
copier_dossier(self.template_dir, self.chemin_projet)
|
|
28
|
+
else:
|
|
29
|
+
print(f"⚠️ Avertissement: Template {self.niveau} non trouvé, création de base")
|
|
30
|
+
self._creer_structure_base()
|
|
31
|
+
|
|
32
|
+
def _creer_structure_base(self):
|
|
33
|
+
"""Crée une structure de base si le template est manquant"""
|
|
34
|
+
creer_fichier(self.chemin_projet / 'app.py', "# Votre application Flask")
|
|
35
|
+
(self.chemin_projet / 'templates').mkdir(exist_ok=True)
|
|
36
|
+
creer_fichier(self.chemin_projet / 'templates/index.html', "<h1>Bienvenue</h1>")
|
|
37
|
+
|
|
38
|
+
def _post_creation(self):
|
|
39
|
+
"""Actions supplémentaires après création"""
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
def generer(self):
|
|
43
|
+
"""Méthode principale pour générer l'environnement"""
|
|
44
|
+
self.chemin_projet.mkdir(exist_ok=True, parents=True)
|
|
45
|
+
print(f"🏗️ Création de l'environnement {self.niveau} ({self.type_app})...")
|
|
46
|
+
|
|
47
|
+
self._copier_template()
|
|
48
|
+
self._creer_structure()
|
|
49
|
+
self._installer_dependances()
|
|
50
|
+
self._post_creation()
|
|
51
|
+
|
|
52
|
+
print(f"✅ Environnement créé avec succès dans {self.chemin_projet}")
|
|
53
|
+
print("👉 Pour démarrer: cd " + str(self.chemin_projet))
|
|
54
|
+
|
|
55
|
+
class DebutantWebGenerator(EnvironnementGenerator):
|
|
56
|
+
"""Générateur pour débutants - Site web simple"""
|
|
57
|
+
|
|
58
|
+
def __init__(self, chemin):
|
|
59
|
+
super().__init__('debutant', 'web', chemin)
|
|
60
|
+
self.packages = ['flask', 'python-dotenv']
|
|
61
|
+
|
|
62
|
+
def _creer_structure(self):
|
|
63
|
+
# Personnalisation supplémentaire
|
|
64
|
+
creer_fichier(
|
|
65
|
+
self.chemin_projet / 'README.md',
|
|
66
|
+
f"# Mon Premier Projet Flask\n\nCe projet a été créé avec SEO pour les débutants!"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
class IntermediaireWebGenerator(EnvironnementGenerator):
|
|
70
|
+
"""Générateur intermédiaire - Applications web complètes"""
|
|
71
|
+
|
|
72
|
+
def __init__(self, chemin):
|
|
73
|
+
super().__init__('intermediaire', 'web', chemin)
|
|
74
|
+
self.packages = [
|
|
75
|
+
'flask',
|
|
76
|
+
'flask-sqlalchemy',
|
|
77
|
+
'flask-wtf',
|
|
78
|
+
'flask-login',
|
|
79
|
+
'flask-migrate',
|
|
80
|
+
'python-dotenv'
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
def _creer_structure(self):
|
|
84
|
+
# Création de la base de données
|
|
85
|
+
db_path = self.chemin_projet / 'app.db'
|
|
86
|
+
if not db_path.exists():
|
|
87
|
+
with open(db_path, 'w') as f:
|
|
88
|
+
f.write("")
|
|
89
|
+
|
|
90
|
+
# Création du fichier requirements
|
|
91
|
+
creer_fichier(
|
|
92
|
+
self.chemin_projet / 'requirements.txt',
|
|
93
|
+
"\n".join(self.packages)
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
class ProWebGenerator(EnvironnementGenerator):
|
|
97
|
+
"""Générateur pro - Applications professionnelles"""
|
|
98
|
+
|
|
99
|
+
def __init__(self, chemin):
|
|
100
|
+
super().__init__('pro', 'web', chemin)
|
|
101
|
+
self.packages = [
|
|
102
|
+
'flask',
|
|
103
|
+
'flask-restx',
|
|
104
|
+
'flask-cors',
|
|
105
|
+
'flask-sqlalchemy',
|
|
106
|
+
'flask-migrate',
|
|
107
|
+
'flask-jwt-extended',
|
|
108
|
+
'python-dotenv',
|
|
109
|
+
'gunicorn',
|
|
110
|
+
'psycopg2-binary'
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
def _post_creation(self):
|
|
114
|
+
# Initialiser un dépôt Git
|
|
115
|
+
try:
|
|
116
|
+
subprocess.run(['git', 'init', str(self.chemin_projet)], check=True)
|
|
117
|
+
print("📦 Dépôt Git initialisé")
|
|
118
|
+
except:
|
|
119
|
+
print("⚠️ Git non installé, ignore l'initialisation du dépôt")
|
|
120
|
+
|
|
121
|
+
# Interface simplifiée
|
|
122
|
+
def creer_environnement(niveau, type_app='web', chemin='.'):
|
|
123
|
+
"""
|
|
124
|
+
Crée un environnement de développement adapté
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
niveau: 'debutant', 'intermediaire' ou 'pro'
|
|
128
|
+
type_app: 'web' ou 'mobile' (défaut: 'web')
|
|
129
|
+
chemin: Chemin où créer le projet (défaut: dossier actuel)
|
|
130
|
+
"""
|
|
131
|
+
generators = {
|
|
132
|
+
'debutant': DebutantWebGenerator,
|
|
133
|
+
'intermediaire': IntermediaireWebGenerator,
|
|
134
|
+
'pro': ProWebGenerator
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if niveau not in generators:
|
|
138
|
+
raise ValueError(f"🚫 Niveau non supporté: {niveau}")
|
|
139
|
+
|
|
140
|
+
generator = generators[niveau](chemin)
|
|
141
|
+
generator.generer()
|
|
142
|
+
|
|
143
|
+
# Ajout des instructions spécifiques
|
|
144
|
+
if niveau == 'debutant':
|
|
145
|
+
print("\n🚀 Pour démarrer votre application:")
|
|
146
|
+
print(f"cd {chemin}")
|
|
147
|
+
print("python app.py")
|
|
148
|
+
elif niveau == 'intermediaire':
|
|
149
|
+
print("\n🚀 Pour initialiser la base de données:")
|
|
150
|
+
print(f"cd {chemin}")
|
|
151
|
+
print("flask db init")
|
|
152
|
+
print("flask db migrate")
|
|
153
|
+
print("flask db upgrade")
|
|
154
|
+
print("flask run")
|
|
155
|
+
else:
|
|
156
|
+
print("\n🚀 Pour démarrer avec Docker:")
|
|
157
|
+
print(f"cd {chemin}")
|
|
158
|
+
print("docker-compose up --build")
|
|
159
|
+
|
|
160
|
+
# Alias pour une utilisation plus simple
|
|
161
|
+
creer_projet = creer_environnement
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
seo/utils.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
def creer_fichier(chemin, contenu):
|
|
5
|
+
"""Crée un fichier avec le contenu spécifié"""
|
|
6
|
+
chemin = Path(chemin)
|
|
7
|
+
chemin.parent.mkdir(parents=True, exist_ok=True)
|
|
8
|
+
with open(chemin, 'w', encoding='utf-8') as f:
|
|
9
|
+
f.write(contenu)
|
|
10
|
+
|
|
11
|
+
def copier_dossier(source, destination):
|
|
12
|
+
"""Copie récursivement un dossier"""
|
|
13
|
+
source = Path(source)
|
|
14
|
+
destination = Path(destination)
|
|
15
|
+
|
|
16
|
+
destination.mkdir(parents=True, exist_ok=True)
|
|
17
|
+
|
|
18
|
+
for item in source.iterdir():
|
|
19
|
+
dest_item = destination / item.name
|
|
20
|
+
if item.is_dir():
|
|
21
|
+
copier_dossier(item, dest_item)
|
|
22
|
+
else:
|
|
23
|
+
with open(item, 'r', encoding='utf-8') as src_file:
|
|
24
|
+
creer_fichier(dest_item, src_file.read())
|
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: seo-dev-env
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Générateur d'environnements de développement pour tous niveaux
|
|
5
|
+
Home-page: https://github.com/https://github.com/elkast/seo-dev-env
|
|
6
|
+
Author: Votre Nom
|
|
7
|
+
Author-email: orsinimelchisedek@gmail.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.6
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE.ls
|
|
14
|
+
Requires-Dist: flask>=2.0.0
|
|
15
|
+
Requires-Dist: python-dotenv>=0.19.0
|
|
16
|
+
Dynamic: author
|
|
17
|
+
Dynamic: author-email
|
|
18
|
+
Dynamic: classifier
|
|
19
|
+
Dynamic: description
|
|
20
|
+
Dynamic: description-content-type
|
|
21
|
+
Dynamic: home-page
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
Dynamic: requires-dist
|
|
24
|
+
Dynamic: requires-python
|
|
25
|
+
Dynamic: summary
|
|
26
|
+
|
|
27
|
+
Très bien ! Voici une explication claire, complète et structurée de l'utilisation de **`seo-dev-env`**, un générateur d'environnements de développement Flask. L’objectif est que **même un débutant puisse comprendre** ce que chaque niveau fait, **comment les fichiers interagissent** et **ce qu’on peut construire avec**.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 🔧 Qu’est-ce que `seo-dev-env` ?
|
|
32
|
+
|
|
33
|
+
C’est un **outil Python** qui te permet de **créer un projet Flask prêt à l’emploi** avec une seule commande. Selon ton **niveau** (débutant, intermédiaire ou pro), il te génère automatiquement une structure de projet adaptée, propre et bien organisée.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 🚀 Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install seo-dev-env
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Tu peux maintenant utiliser la commande `seo-create` ou l'importer dans un script Python.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## ⚙️ Utilisation
|
|
48
|
+
|
|
49
|
+
### ➤ En ligne de commande (CLI)
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
seo-create debutant mon-projet
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Cela crée un projet nommé `mon-projet` avec une structure simple (pour débutant).
|
|
56
|
+
|
|
57
|
+
### ➤ En Python
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from seo import creer_projet
|
|
61
|
+
|
|
62
|
+
creer_projet('pro', 'mon-projet-élite')
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Cela génère un projet "pro" avec Docker et une API robuste.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 🧱 Niveaux disponibles et structure de projet
|
|
70
|
+
|
|
71
|
+
### 🐣 1. Débutant : Structure Simple
|
|
72
|
+
|
|
73
|
+
Idéal pour ceux qui apprennent Flask ou font un petit projet/test.
|
|
74
|
+
|
|
75
|
+
#### 🔹 Structure du dossier :
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
mon-projet/
|
|
79
|
+
├── app.py
|
|
80
|
+
├── templates/
|
|
81
|
+
│ └── index.html
|
|
82
|
+
├── static/
|
|
83
|
+
│ └── style.css
|
|
84
|
+
└── requirements.txt
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### 🔹 À quoi ça sert :
|
|
88
|
+
|
|
89
|
+
* `app.py` : ton fichier principal Flask
|
|
90
|
+
* `templates/index.html` : ta page HTML (Jinja2)
|
|
91
|
+
* `static/style.css` : ton style CSS
|
|
92
|
+
* `requirements.txt` : liste des bibliothèques Python à installer
|
|
93
|
+
|
|
94
|
+
#### ✅ Ce que tu peux construire :
|
|
95
|
+
|
|
96
|
+
* Une landing page
|
|
97
|
+
* Un formulaire de contact
|
|
98
|
+
* Une démo rapide pour un concept
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
### ⚡ 2. Intermédiaire : Architecture MVC
|
|
103
|
+
|
|
104
|
+
Architecture propre, réutilisable, bien structurée. Idéal pour un **site web dynamique** ou une **application complète**.
|
|
105
|
+
|
|
106
|
+
#### 🔹 Structure du dossier :
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
mon-projet/
|
|
110
|
+
├── app/
|
|
111
|
+
│ ├── __init__.py
|
|
112
|
+
│ ├── routes.py
|
|
113
|
+
│ ├── models.py
|
|
114
|
+
│ └── forms.py
|
|
115
|
+
├── templates/
|
|
116
|
+
│ └── base.html
|
|
117
|
+
│ └── home.html
|
|
118
|
+
├── static/
|
|
119
|
+
│ └── css/
|
|
120
|
+
├── config.py
|
|
121
|
+
├── run.py
|
|
122
|
+
└── requirements.txt
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### 🔹 Détail des fichiers :
|
|
126
|
+
|
|
127
|
+
* `app/__init__.py` : initialise l'app Flask
|
|
128
|
+
* `routes.py` : contient les pages et leurs comportements
|
|
129
|
+
* `models.py` : les objets liés à la base de données (SQLAlchemy)
|
|
130
|
+
* `forms.py` : formulaires (WTForms)
|
|
131
|
+
* `config.py` : configuration générale (dev, prod, clés secrètes)
|
|
132
|
+
* `run.py` : fichier pour lancer l’app
|
|
133
|
+
* `templates/` : toutes les pages HTML avec héritage de `base.html`
|
|
134
|
+
|
|
135
|
+
#### ✅ Ce que tu peux construire :
|
|
136
|
+
|
|
137
|
+
* Blog personnel
|
|
138
|
+
* Dashboard interne
|
|
139
|
+
* Site avec base de données (utilisateurs, commentaires, etc.)
|
|
140
|
+
* Application avec authentification/login
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
### 🚀 3. Pro : Docker + API Pro
|
|
145
|
+
|
|
146
|
+
Projet conçu pour des applications **robustes, déployables en production**, avec Docker, API REST, structure scalable.
|
|
147
|
+
|
|
148
|
+
#### 🔹 Structure du dossier :
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
mon-projet-élite/
|
|
152
|
+
├── api/
|
|
153
|
+
│ ├── __init__.py
|
|
154
|
+
│ ├── routes/
|
|
155
|
+
│ │ └── user_routes.py
|
|
156
|
+
│ ├── models/
|
|
157
|
+
│ │ └── user_model.py
|
|
158
|
+
│ └── services/
|
|
159
|
+
│ └── auth_service.py
|
|
160
|
+
├── config/
|
|
161
|
+
│ └── config.py
|
|
162
|
+
├── docker/
|
|
163
|
+
│ └── Dockerfile
|
|
164
|
+
├── docker-compose.yml
|
|
165
|
+
├── requirements.txt
|
|
166
|
+
├── .env
|
|
167
|
+
└── run.py
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
#### 🔹 Détail des fichiers :
|
|
171
|
+
|
|
172
|
+
* `api/` : ton app Flask sous forme d’API modulaire
|
|
173
|
+
|
|
174
|
+
* `routes/` : routes REST (GET, POST, etc.)
|
|
175
|
+
* `models/` : objets de base de données (SQLAlchemy ou autre ORM)
|
|
176
|
+
* `services/` : logique métier (auth, traitement, etc.)
|
|
177
|
+
* `config/` : configuration centralisée
|
|
178
|
+
* `Dockerfile` & `docker-compose.yml` : pour l’environnement Docker
|
|
179
|
+
* `.env` : variables d’environnement sensibles
|
|
180
|
+
* `run.py` : point d’entrée de l’application
|
|
181
|
+
|
|
182
|
+
#### ✅ Ce que tu peux construire :
|
|
183
|
+
|
|
184
|
+
* Backend d’une app mobile
|
|
185
|
+
* API REST sécurisée
|
|
186
|
+
* Projet SaaS ou e-commerce scalable
|
|
187
|
+
* Application avec CI/CD, PostgreSQL, Redis, etc.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## 🔁 Interaction des fichiers (exemples)
|
|
192
|
+
|
|
193
|
+
### Exemple pour le niveau **intermédiaire** :
|
|
194
|
+
|
|
195
|
+
1. `run.py` lance l'application
|
|
196
|
+
2. Il appelle `app/__init__.py` pour créer l’objet Flask
|
|
197
|
+
3. `init.py` charge les routes via `routes.py`
|
|
198
|
+
4. Les routes retournent des templates comme `home.html`
|
|
199
|
+
5. Les données viennent de `models.py`
|
|
200
|
+
6. Si besoin de formulaire, `forms.py` gère la validation
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## 💡 Que peut-on faire de A à Z ?
|
|
205
|
+
|
|
206
|
+
Exemple : construire un **portail de gestion de projets**
|
|
207
|
+
|
|
208
|
+
1. Utilise `seo-create pro gestion-projets`
|
|
209
|
+
2. Ajoute une base PostgreSQL dans `docker-compose.yml`
|
|
210
|
+
3. Développe des routes pour :
|
|
211
|
+
|
|
212
|
+
* Créer un projet (`POST /projets`)
|
|
213
|
+
* Lister les projets (`GET /projets`)
|
|
214
|
+
* Ajouter des tâches à un projet (`POST /projets/<id>/taches`)
|
|
215
|
+
4. Gère l’authentification via JWT dans `services/auth_service.py`
|
|
216
|
+
5. Déploie avec Docker sur un VPS
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## 🧠 Résumé des avantages
|
|
221
|
+
|
|
222
|
+
| Niveau | Idéal pour | Points forts |
|
|
223
|
+
| ------------- | ---------------------------- | ----------------------------------------- |
|
|
224
|
+
| Débutant | Apprentissage, mini-projets | Ultra simple, immédiat |
|
|
225
|
+
| Intermédiaire | Sites complets avec logique | MVC propre, bonne base pour la croissance |
|
|
226
|
+
| Pro | APIs, apps déployables, SaaS | Docker, structure modulaire, scalable |
|
|
227
|
+
|
|
228
|
+
un petit exmple😁👌;
|
|
229
|
+
|
|
230
|
+
Parfait ! Je vais te montrer un exemple complet avec le **niveau Intermédiaire (⚡ Architecture MVC)**, qui est un bon équilibre entre simplicité et puissance. Nous allons créer un **mini tableau de bord de gestion de tâches** (type Todo App multi-utilisateur).
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## 🎯 Objectif du projet : Gestion de tâches (Task Manager)
|
|
235
|
+
|
|
236
|
+
Chaque utilisateur peut :
|
|
237
|
+
|
|
238
|
+
* s’inscrire et se connecter,
|
|
239
|
+
* créer, modifier et supprimer ses tâches,
|
|
240
|
+
* voir ses tâches dans un tableau de bord.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## 🧰 Étape 1 : Génération du projet
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
seo-create intermediaire task-manager
|
|
248
|
+
cd task-manager
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## 🗂️ Structure du projet (générée automatiquement)
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
task-manager/
|
|
257
|
+
├── app/
|
|
258
|
+
│ ├── __init__.py
|
|
259
|
+
│ ├── routes.py
|
|
260
|
+
│ ├── models.py
|
|
261
|
+
│ └── forms.py
|
|
262
|
+
├── templates/
|
|
263
|
+
│ ├── base.html
|
|
264
|
+
│ ├── home.html
|
|
265
|
+
│ ├── login.html
|
|
266
|
+
│ └── dashboard.html
|
|
267
|
+
├── static/
|
|
268
|
+
│ └── style.css
|
|
269
|
+
├── config.py
|
|
270
|
+
├── run.py
|
|
271
|
+
└── requirements.txt
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## 🔧 Étape 2 : Configurer la base
|
|
277
|
+
|
|
278
|
+
**config.py**
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
import os
|
|
282
|
+
|
|
283
|
+
class Config:
|
|
284
|
+
SECRET_KEY = 'cle-super-secrete'
|
|
285
|
+
SQLALCHEMY_DATABASE_URI = 'sqlite:///db.sqlite3'
|
|
286
|
+
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## 🧱 Étape 3 : Initialiser l’app
|
|
292
|
+
|
|
293
|
+
**app/**init**.py**
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
from flask import Flask
|
|
297
|
+
from flask_sqlalchemy import SQLAlchemy
|
|
298
|
+
from flask_login import LoginManager
|
|
299
|
+
from config import Config
|
|
300
|
+
|
|
301
|
+
db = SQLAlchemy()
|
|
302
|
+
login = LoginManager()
|
|
303
|
+
login.login_view = 'login'
|
|
304
|
+
|
|
305
|
+
def create_app():
|
|
306
|
+
app = Flask(__name__)
|
|
307
|
+
app.config.from_object(Config)
|
|
308
|
+
|
|
309
|
+
db.init_app(app)
|
|
310
|
+
login.init_app(app)
|
|
311
|
+
|
|
312
|
+
from app.routes import bp
|
|
313
|
+
app.register_blueprint(bp)
|
|
314
|
+
|
|
315
|
+
return app
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## 👤 Étape 4 : Modèle utilisateur + tâche
|
|
321
|
+
|
|
322
|
+
**app/models.py**
|
|
323
|
+
|
|
324
|
+
```python
|
|
325
|
+
from app import db
|
|
326
|
+
from flask_login import UserMixin
|
|
327
|
+
from werkzeug.security import generate_password_hash, check_password_hash
|
|
328
|
+
from app import login
|
|
329
|
+
|
|
330
|
+
class User(UserMixin, db.Model):
|
|
331
|
+
id = db.Column(db.Integer, primary_key=True)
|
|
332
|
+
username = db.Column(db.String(64), unique=True)
|
|
333
|
+
password_hash = db.Column(db.String(128))
|
|
334
|
+
tasks = db.relationship('Task', backref='owner', lazy='dynamic')
|
|
335
|
+
|
|
336
|
+
def set_password(self, password):
|
|
337
|
+
self.password_hash = generate_password_hash(password)
|
|
338
|
+
|
|
339
|
+
def check_password(self, password):
|
|
340
|
+
return check_password_hash(self.password_hash, password)
|
|
341
|
+
|
|
342
|
+
@login.user_loader
|
|
343
|
+
def load_user(id):
|
|
344
|
+
return User.query.get(int(id))
|
|
345
|
+
|
|
346
|
+
class Task(db.Model):
|
|
347
|
+
id = db.Column(db.Integer, primary_key=True)
|
|
348
|
+
title = db.Column(db.String(128))
|
|
349
|
+
done = db.Column(db.Boolean, default=False)
|
|
350
|
+
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## 🧾 Étape 5 : Les formulaires
|
|
356
|
+
|
|
357
|
+
**app/forms.py**
|
|
358
|
+
|
|
359
|
+
```python
|
|
360
|
+
from flask_wtf import FlaskForm
|
|
361
|
+
from wtforms import StringField, PasswordField, SubmitField, BooleanField
|
|
362
|
+
from wtforms.validators import DataRequired, EqualTo
|
|
363
|
+
|
|
364
|
+
class LoginForm(FlaskForm):
|
|
365
|
+
username = StringField('Nom d\'utilisateur', validators=[DataRequired()])
|
|
366
|
+
password = PasswordField('Mot de passe', validators=[DataRequired()])
|
|
367
|
+
submit = SubmitField('Connexion')
|
|
368
|
+
|
|
369
|
+
class TaskForm(FlaskForm):
|
|
370
|
+
title = StringField('Titre de la tâche', validators=[DataRequired()])
|
|
371
|
+
submit = SubmitField('Ajouter')
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## 🌐 Étape 6 : Les routes principales
|
|
377
|
+
|
|
378
|
+
**app/routes.py**
|
|
379
|
+
|
|
380
|
+
```python
|
|
381
|
+
from flask import Blueprint, render_template, redirect, url_for, request, flash
|
|
382
|
+
from flask_login import login_user, logout_user, login_required, current_user
|
|
383
|
+
from app.models import User, Task
|
|
384
|
+
from app.forms import LoginForm, TaskForm
|
|
385
|
+
from app import db
|
|
386
|
+
|
|
387
|
+
bp = Blueprint('main', __name__)
|
|
388
|
+
|
|
389
|
+
@bp.route('/', methods=['GET', 'POST'])
|
|
390
|
+
def login():
|
|
391
|
+
form = LoginForm()
|
|
392
|
+
if form.validate_on_submit():
|
|
393
|
+
user = User.query.filter_by(username=form.username.data).first()
|
|
394
|
+
if user and user.check_password(form.password.data):
|
|
395
|
+
login_user(user)
|
|
396
|
+
return redirect(url_for('main.dashboard'))
|
|
397
|
+
flash('Identifiants invalides.')
|
|
398
|
+
return render_template('login.html', form=form)
|
|
399
|
+
|
|
400
|
+
@bp.route('/dashboard', methods=['GET', 'POST'])
|
|
401
|
+
@login_required
|
|
402
|
+
def dashboard():
|
|
403
|
+
form = TaskForm()
|
|
404
|
+
if form.validate_on_submit():
|
|
405
|
+
task = Task(title=form.title.data, owner=current_user)
|
|
406
|
+
db.session.add(task)
|
|
407
|
+
db.session.commit()
|
|
408
|
+
return redirect(url_for('main.dashboard'))
|
|
409
|
+
tasks = current_user.tasks.all()
|
|
410
|
+
return render_template('dashboard.html', tasks=tasks, form=form)
|
|
411
|
+
|
|
412
|
+
@bp.route('/logout')
|
|
413
|
+
def logout():
|
|
414
|
+
logout_user()
|
|
415
|
+
return redirect(url_for('main.login'))
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
## 🧪 Étape 7 : Initialisation de la base
|
|
421
|
+
|
|
422
|
+
Dans un terminal Python (depuis le dossier du projet) :
|
|
423
|
+
|
|
424
|
+
```python
|
|
425
|
+
from app import create_app, db
|
|
426
|
+
from app.models import User
|
|
427
|
+
|
|
428
|
+
app = create_app()
|
|
429
|
+
app.app_context().push()
|
|
430
|
+
db.create_all()
|
|
431
|
+
|
|
432
|
+
# Création d'un utilisateur test
|
|
433
|
+
u = User(username='admin')
|
|
434
|
+
u.set_password('admin')
|
|
435
|
+
db.session.add(u)
|
|
436
|
+
db.session.commit()
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## 🖥️ Étape 8 : Templates HTML simplifiés
|
|
442
|
+
|
|
443
|
+
**templates/base.html**
|
|
444
|
+
|
|
445
|
+
```html
|
|
446
|
+
<!DOCTYPE html>
|
|
447
|
+
<html>
|
|
448
|
+
<head>
|
|
449
|
+
<title>{% block title %}Task Manager{% endblock %}</title>
|
|
450
|
+
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
|
451
|
+
</head>
|
|
452
|
+
<body>
|
|
453
|
+
{% block content %}{% endblock %}
|
|
454
|
+
</body>
|
|
455
|
+
</html>
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
**templates/login.html**
|
|
459
|
+
|
|
460
|
+
```html
|
|
461
|
+
{% extends "base.html" %}
|
|
462
|
+
{% block content %}
|
|
463
|
+
<h2>Connexion</h2>
|
|
464
|
+
<form method="post">
|
|
465
|
+
{{ form.hidden_tag() }}
|
|
466
|
+
{{ form.username.label }} {{ form.username() }}<br>
|
|
467
|
+
{{ form.password.label }} {{ form.password() }}<br>
|
|
468
|
+
{{ form.submit() }}
|
|
469
|
+
</form>
|
|
470
|
+
{% endblock %}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
**templates/dashboard.html**
|
|
474
|
+
|
|
475
|
+
```html
|
|
476
|
+
{% extends "base.html" %}
|
|
477
|
+
{% block content %}
|
|
478
|
+
<h2>Bienvenue {{ current_user.username }}</h2>
|
|
479
|
+
<a href="{{ url_for('main.logout') }}">Déconnexion</a>
|
|
480
|
+
<form method="post">
|
|
481
|
+
{{ form.hidden_tag() }}
|
|
482
|
+
{{ form.title.label }} {{ form.title() }}
|
|
483
|
+
{{ form.submit() }}
|
|
484
|
+
</form>
|
|
485
|
+
<ul>
|
|
486
|
+
{% for task in tasks %}
|
|
487
|
+
<li>{{ task.title }} {% if task.done %}(fait){% endif %}</li>
|
|
488
|
+
{% endfor %}
|
|
489
|
+
</ul>
|
|
490
|
+
{% endblock %}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## 🌍 Étape 9 : Lancer l’application
|
|
496
|
+
|
|
497
|
+
**run.py**
|
|
498
|
+
|
|
499
|
+
```python
|
|
500
|
+
from app import create_app
|
|
501
|
+
|
|
502
|
+
app = create_app()
|
|
503
|
+
|
|
504
|
+
if __name__ == '__main__':
|
|
505
|
+
app.run(debug=True)
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
Puis lance :
|
|
509
|
+
|
|
510
|
+
```bash
|
|
511
|
+
python run.py
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## ☁️ Étape 10 : Déploiement (plusieurs options)
|
|
517
|
+
|
|
518
|
+
### Option simple : [Render](https://render.com)
|
|
519
|
+
|
|
520
|
+
1. Crée un repo GitHub et pousse ton code
|
|
521
|
+
2. Va sur render.com et connecte ton GitHub
|
|
522
|
+
3. Clique sur "New Web Service"
|
|
523
|
+
4. Paramètres :
|
|
524
|
+
|
|
525
|
+
* **Build Command** : `pip install -r requirements.txt`
|
|
526
|
+
* **Start Command** : `python run.py`
|
|
527
|
+
|
|
528
|
+
---
|
|
529
|
+
|
|
530
|
+
## ✅ Résultat final
|
|
531
|
+
|
|
532
|
+
Tu as :
|
|
533
|
+
|
|
534
|
+
* Un système d'authentification
|
|
535
|
+
* Un tableau de bord utilisateur
|
|
536
|
+
* Un modèle MVC clair et extensible
|
|
537
|
+
* Une app Flask prête pour production (avec ajout facile de tests, Bootstrap, API, etc.)
|
|
538
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
seo/__init__.py,sha256=UZS5pTtyPZ4-3JGGZSgfEKl8aTTFY9IsiM6gvwKRMzA,110
|
|
2
|
+
seo/generators.py,sha256=E2lTwkZNvtJiT4FyC3tMt_wDJaPGBxjVmvXTaxORwuo,5755
|
|
3
|
+
seo/utils.py,sha256=ocTb_CotkmmB_5aZSZXGCG_gJNaC9GdvZFb2ayWHaLE,794
|
|
4
|
+
seo/templates/debutant/app.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
seo/templates/debutant/index.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
seo/templates/debutant/style.css,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
seo/templates/intermediaire/config.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
seo/templates/intermediaire/run.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
seo/templates/intermediaire/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
seo/templates/intermediaire/app/models.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
seo/templates/intermediaire/app/routes.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
seo/templates/pro/docker-compose.yml,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
seo/templates/pro/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
seo/templates/pro/app/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
seo/templates/pro/app/api/v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
seo/templates/pro/app/api/v1/endpoints.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
seo/templates/pro/app/core/config.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
seo/templates/pro/app/core/security.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
+
seo/templates/pro/app/db/base.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
seo/templates/pro/app/db/models.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
+
seo_dev_env-0.1.0.dist-info/licenses/LICENSE.ls,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
+
seo_dev_env-0.1.0.dist-info/METADATA,sha256=bKC9Xe9jcyCuwYf93L9W3o0-JAPJp268E_48UuIFWK8,13961
|
|
23
|
+
seo_dev_env-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
24
|
+
seo_dev_env-0.1.0.dist-info/entry_points.txt,sha256=lrqV5FhVM9pbEiYK-U9rtiWP1ZaUditqz9hpnLxy9E4,51
|
|
25
|
+
seo_dev_env-0.1.0.dist-info/top_level.txt,sha256=5WsA3DhwhBGXTOKiKgDtiTUjXlpLk4oeUvrSKrtRFoQ,4
|
|
26
|
+
seo_dev_env-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
seo
|