diff --git a/Pipfile b/Pipfile index 401b1cfaeb676fa6b71ad8d12838838916bdb783..5d69684776bfb6e8f2b4b4ffb685b4c7bb02edf9 100644 --- a/Pipfile +++ b/Pipfile @@ -7,6 +7,7 @@ name = "pypi" psycopg2 = "*" flask = "*" flask-sqlalchemy = "*" +psycopg2-binary = "*" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index 04bb92b0bea7d0b45b3673fb741ec55683113b84..afe936f2b8efba17b39149da415122c45ef64d4a 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "c612c7907d78fe41a6fc3bf13abc06b9f056ceab75165823254a6bbb96271bf3" + "sha256": "ae4f3899e2a50fe730bcc093c082ce793842d0c8239b150422ef7ff0c622a308" }, "pipfile-spec": 6, "requires": { @@ -229,6 +229,81 @@ "markers": "python_version >= '3.8'", "version": "==2.9.10" }, + "psycopg2-binary": { + "hashes": [ + "sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff", + "sha256:056470c3dc57904bbf63d6f534988bafc4e970ffd50f6271fc4ee7daad9498a5", + "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f", + "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", + "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", + "sha256:19721ac03892001ee8fdd11507e6a2e01f4e37014def96379411ca99d78aeb2c", + "sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c", + "sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341", + "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", + "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", + "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", + "sha256:270934a475a0e4b6925b5f804e3809dd5f90f8613621d062848dd82f9cd62007", + "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", + "sha256:2ad26b467a405c798aaa1458ba09d7e2b6e5f96b1ce0ac15d82fd9f95dc38a92", + "sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb", + "sha256:2ce3e21dc3437b1d960521eca599d57408a695a0d3c26797ea0f72e834c7ffe5", + "sha256:30e34c4e97964805f715206c7b789d54a78b70f3ff19fbe590104b71c45600e5", + "sha256:3216ccf953b3f267691c90c6fe742e45d890d8272326b4a8b20850a03d05b7b8", + "sha256:32581b3020c72d7a421009ee1c6bf4a131ef5f0a968fab2e2de0c9d2bb4577f1", + "sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68", + "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", + "sha256:3c18f74eb4386bf35e92ab2354a12c17e5eb4d9798e4c0ad3a00783eae7cd9f1", + "sha256:3c4745a90b78e51d9ba06e2088a2fe0c693ae19cc8cb051ccda44e8df8a6eb53", + "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", + "sha256:3e9c76f0ac6f92ecfc79516a8034a544926430f7b080ec5a0537bca389ee0906", + "sha256:48b338f08d93e7be4ab2b5f1dbe69dc5e9ef07170fe1f86514422076d9c010d0", + "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", + "sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a", + "sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b", + "sha256:5c370b1e4975df846b0277b4deba86419ca77dbc25047f535b0bb03d1a544d44", + "sha256:6b269105e59ac96aba877c1707c600ae55711d9dcd3fc4b5012e4af68e30c648", + "sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7", + "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", + "sha256:73aa0e31fa4bb82578f3a6c74a73c273367727de397a7a0f07bd83cbea696baa", + "sha256:7559bce4b505762d737172556a4e6ea8a9998ecac1e39b5233465093e8cee697", + "sha256:79625966e176dc97ddabc142351e0409e28acf4660b88d1cf6adb876d20c490d", + "sha256:7a813c8bdbaaaab1f078014b9b0b13f5de757e2b5d9be6403639b298a04d218b", + "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", + "sha256:7f4152f8f76d2023aac16285576a9ecd2b11a9895373a1f10fd9db54b3ff06b4", + "sha256:7f5d859928e635fa3ce3477704acee0f667b3a3d3e4bb109f2b18d4005f38287", + "sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e", + "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", + "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", + "sha256:8aabf1c1a04584c168984ac678a668094d831f152859d06e055288fa515e4d30", + "sha256:8aecc5e80c63f7459a1a2ab2c64df952051df196294d9f739933a9f6687e86b3", + "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", + "sha256:8de718c0e1c4b982a54b41779667242bc630b2197948405b7bd8ce16bcecac92", + "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", + "sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c", + "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8", + "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", + "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", + "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864", + "sha256:d00924255d7fc916ef66e4bf22f354a940c67179ad3fd7067d7a0a9c84d2fbfc", + "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", + "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", + "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", + "sha256:e5720a5d25e3b99cd0dc5c8a440570469ff82659bb09431c1439b92caf184d3b", + "sha256:e8b58f0a96e7a1e341fc894f62c1177a7c83febebb5ff9123b579418fdc8a481", + "sha256:e984839e75e0b60cfe75e351db53d6db750b00de45644c5d1f7ee5d1f34a1ce5", + "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4", + "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", + "sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392", + "sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4", + "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", + "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", + "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", + "sha256:ffe8ed017e4ed70f68b7b371d84b7d4a790368db9203dfc2d222febd3a9c8863" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2.9.10" + }, "sqlalchemy": { "hashes": [ "sha256:03f0528c53ca0b67094c4764523c1451ea15959bbf0a8a8a3096900014db0278", diff --git a/README.md b/README.md index fc207724f30695e96d40a20e31133749f7c3877d..1dca971e75686a68418f2b03b7afc5d4c07fe8ed 100644 --- a/README.md +++ b/README.md @@ -30,10 +30,24 @@ sudo apt install pipenv Une fois cela fait, on entrer dans l'environnement virtuel. ``` +cd web_app +pipenv install pipenv shell -pipenv install psycopg2 flask flask-sqlalchemy ``` +#Création de la base de données + + +''' +sudo -u postgres psql +ALTER USER postgres WITH PASSWORD 'nouveau_mot_de_passe'; +''' +## Run the app + +Dans le terminal, se placer au niveau de app.py +``` +flask app.py +``` ## Sitographie diff --git a/__pycache__/app.cpython-312.pyc b/__pycache__/app.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cec1efd5e27450dfbf6ea081afd97562cb24c4cb Binary files /dev/null and b/__pycache__/app.cpython-312.pyc differ diff --git a/app.py b/app.py index 36a68a2d052e0096e659c001418fc8d818da2a32..fea1cbcd62649647af759e2033ed863c6be0bec1 100644 --- a/app.py +++ b/app.py @@ -1,11 +1,114 @@ -from flask import Flask, render_template +from flask import Flask, render_template, request, redirect, url_for, jsonify +from flask_sqlalchemy import SQLAlchemy +import psycopg2 +from psycopg2 import sql + +# Informations de connexion à PostgreSQL +host = "localhost" # ou l'adresse de votre serveur +port = 5432 # port par défaut de PostgreSQL +user = "postgres" # utilisateur PostgreSQL +password = "mysecretpassword" # mot de passe PostgreSQL +database_name = "mydb" # nom de la base de données à créer + +try: + # Connexion au serveur PostgreSQL + conn = psycopg2.connect( + dbname="postgres", # On se connecte à la base "postgres" par défaut + user=user, + password=password, + host=host, + port=port + ) + conn.autocommit = True # Active le mode autocommit pour exécuter CREATE DATABASE + + # Créer un curseur pour exécuter les requêtes + cur = conn.cursor() + + # Création de la base de données + cur.execute(sql.SQL("CREATE DATABASE {}").format(sql.Identifier(database_name))) + + print(f"La base de données '{database_name}' a été créée avec succès.") + + # Fermer le curseur et la connexion + cur.close() + conn.close() +except psycopg2.Error as e: + print(f"Erreur lors de la création de la base de données : {e}") + + +# Créatipon de la base données app = Flask(__name__, template_folder ="templates") + +# Configuration de la base de données PostgreSQL +app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:mysecretpassword@localhost:5432/mydb' +app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + +db = SQLAlchemy(app) + +#Modèle utilisateur +class User(db.Model): + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(80), nullable=False) + email = db.Column(db.String(120), unique=True, nullable=False) + +# Modèle Article +class Article(db.Model): + id = db.Column(db.Integer, primary_key=True) + title = db.Column(db.String(120), nullable=False) + description = db.Column(db.Text, nullable=False) + price = db.Column(db.Float, nullable=False) + +# Créer les tables dans la base de données +with app.app_context(): + db.create_all() + + @app.route('/') def index(): return render_template("index.html") + # return "Hello, world" + + +@app.route('/admin', methods=['GET', 'POST']) +def admin(): + if request.method == 'POST': + title = request.form['title'] + description = request.form['description'] + price = request.form['price'] + + new_article = Article(title=title, description=description, price=price) + db.session.add(new_article) + db.session.commit() + + return redirect(url_for('admin')) + + articles = Article.query.all() + return render_template('admin.html', articles=articles) + +@app.route('/articles') +def articles(): + articles = Article.query.all() + return render_template('articles.html', articles=articles) + +@app.route('/articles/<int:article_id>') +def article_details(article_id): + article = Article.query.get_or_404(article_id) + return render_template('article_details.html', article=article) + +@app.route('/buy/<int:article_id>', methods=['POST']) +def buy(article_id): + article = Article.query.get_or_404(article_id) + user_email = request.form['email'] # Récupère l'email de l'utilisateur + + # Simulation d'envoi de confirmation (ajoutez Flask-Mail pour le rendre réel) + print(f"Email de confirmation envoyé à {user_email} pour l'article {article.title} au prix de {article.price}€.") + + return jsonify({"message": f"Confirmation envoyée à {user_email} pour l'achat de {article.title}."}) + if __name__ == '__main__': - app.run(debug=True) \ No newline at end of file + app.run(debug=True) + diff --git a/templates/admin.html b/templates/admin.html new file mode 100644 index 0000000000000000000000000000000000000000..db5df41e9039bf4e4c195f47599a1921ae419eab --- /dev/null +++ b/templates/admin.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <title>Admin - Ajouter des articles</title> +</head> +<body> + <h1>Ajouter un article</h1> + <form method="POST"> + <input type="text" name="title" placeholder="Titre" required> + <textarea name="description" placeholder="Description" required></textarea> + <input type="number" step="0.01" name="price" placeholder="Prix (€)" required> + <button type="submit">Ajouter</button> + </form> + + <h2>Articles disponibles</h2> + <ul> + {% for article in articles %} + <li>{{ article.title }} - {{ article.price }}€</li> + {% endfor %} + </ul> +</body> +</html> diff --git a/templates/article_details.html b/templates/article_details.html new file mode 100644 index 0000000000000000000000000000000000000000..9ed79208642aecba16c54148043339d2006535ee --- /dev/null +++ b/templates/article_details.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <title>{{ article.title }}</title> +</head> +<body> + <h1>{{ article.title }}</h1> + <p>{{ article.description }}</p> + <p>Prix : {{ article.price }}€</p> + <form method="POST" action="{{ url_for('buy', article_id=article.id) }}"> + <input type="email" name="email" placeholder="Votre email" required> + <button type="submit">Acheter</button> + </form> +</body> +</html> diff --git a/templates/articles.html b/templates/articles.html new file mode 100644 index 0000000000000000000000000000000000000000..a58c339052961b9ed2c663b3ce59feb0148f10ab --- /dev/null +++ b/templates/articles.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <title>Articles</title> +</head> +<body> + <h1>Articles disponibles</h1> + <ul> + {% for article in articles %} + <li> + <a href="{{ url_for('article_details', article_id=article.id) }}"> + {{ article.title }} - {{ article.price }}€ + </a> + </li> + {% endfor %} + </ul> +</body> +</html> diff --git a/templates/index.html b/templates/index.html index c5d9d6e14d06694d43fde86dc239a6d1888b6e24..ab04df295fdc7f953065b3976f815ebef2d7497a 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,82 +1,43 @@ <!DOCTYPE html> -<html lang="fr"> +<html lang="en"> <head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - <title>Accueil - Style Apple</title> - <style> - /* Réinitialisation de quelques styles de base */ - body, h1, p, nav, footer { - margin: 0; - padding: 0; - } - body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; - background: #fff; - color: #333; - line-height: 1.6; - } - header { - padding: 20px; - text-align: center; - border-bottom: 1px solid #eaeaea; - } - nav { - display: flex; - justify-content: center; - gap: 20px; - font-size: 0.9em; - } - nav a { - text-decoration: none; - color: #333; - padding: 10px; - } - nav a:hover { - text-decoration: underline; - } - .hero { - text-align: center; - padding: 100px 20px; - background: #f9f9f9; - } - .hero h1 { - font-size: 3em; - margin-bottom: 20px; - } - .hero p { - font-size: 1.2em; - color: #555; - } - footer { - text-align: center; - padding: 20px; - font-size: 0.8em; - color: #777; - border-top: 1px solid #eaeaea; - } - </style> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Accueil</title> + <style> + body { + font-family: Arial, sans-serif; + text-align: center; + margin: 50px; + } + h1 { + color: #333; + } + .links { + margin: 20px 0; + } + .link { + display: inline-block; + margin: 10px; + padding: 15px 25px; + background-color: #007bff; + color: white; + text-decoration: none; + border-radius: 5px; + transition: background-color 0.3s; + } + .link:hover { + background-color: #0056b3; + } + </style> </head> <body> - <header> - <nav> - <a href="#">Mac</a> - <a href="#">iPhone</a> - <a href="#">iPad</a> - <a href="#">Watch</a> - <a href="#">TV</a> - <a href="#">Music</a> - <a href="#">Support</a> - </nav> - </header> + <h1>Bienvenue sur l'application</h1> + <p>Choisissez une action pour accéder à vos pages :</p> - <section class="hero"> - <h1>Bienvenue chez Apple</h1> - <p>Découvrez nos derniers produits et innovations.</p> - </section> - - <footer> - © 2025 Apple Inc. Tous droits réservés. - </footer> + <div class="links"> + <a href="/admin" class="link">Page Administrateur</a> + <a href="/articles" class="link">Articles Disponibles</a> + </div> </body> </html> diff --git a/templates/index_op.html b/templates/index_op.html new file mode 100644 index 0000000000000000000000000000000000000000..de4e203d2e395fae0ce3cda2ab90f0e2fc7fe8b3 --- /dev/null +++ b/templates/index_op.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>One Piece - Marine HQ</title> + <style> + body { + font-family: Arial, sans-serif; + background: #0f2027; /* Marine-themed gradient */ + background: -webkit-linear-gradient(to right, #2c5364, #203a43, #0f2027); + background: linear-gradient(to right, #2c5364, #203a43, #0f2027); + color: #f0f0f0; + text-align: center; + padding: 50px; + } + h1 { + font-size: 3rem; + margin-bottom: 10px; + color: #00d4ff; + } + p { + font-size: 1.2rem; + margin-bottom: 30px; + } + .links { + display: flex; + justify-content: center; + gap: 20px; + margin-top: 30px; + flex-wrap: wrap; + } + .link { + text-decoration: none; + background: #00d4ff; + color: #0f2027; + padding: 15px 30px; + border-radius: 5px; + font-size: 1.2rem; + font-weight: bold; + transition: 0.3s ease; + } + .link:hover { + background: #f0f0f0; + color: #00d4ff; + } + footer { + margin-top: 50px; + font-size: 0.9rem; + color: #a0a0a0; + } + </style> +</head> +<body> + <h1>Marine HQ</h1> + <p>Bienvenue à la base de la marine ! Surveillez les pirates, cataloguez les fruits du démon et maintenez l'ordre.</p> + + <div class="links"> + <a href="/api/pirates" class="link">Voir les Pirates</a> + <a href="/api/marines" class="link">Voir les Marines</a> + <a href="/api/devil-fruits" class="link">Voir les Fruits