Private
Public Access
1
0

moved to server

Update readme.md

Signed-off-by: rasmus <rsbendtsen@gmail.com>

Update readme.md

Update Dockerfile

Update Dockerfile
This commit is contained in:
2025-12-29 06:19:31 +01:00
commit dd2c15ba67
15 changed files with 1022 additions and 0 deletions

207
app.py Normal file
View File

@@ -0,0 +1,207 @@
import os
from dotenv import load_dotenv
from flask import Flask, render_template, redirect, url_for, request, flash
from flask_sqlalchemy import SQLAlchemy
from flask_login import (
LoginManager,
UserMixin,
login_user,
login_required,
logout_user,
current_user,
)
from werkzeug.security import generate_password_hash, check_password_hash
from openai import OpenAI
load_dotenv()
OPENROUTER_API_KEY=os.environ.get("OPENROUTER_API_KEY")
OPENROUTER_MODEL=os.environ.get("OPENROUTER_MODEL")
app = Flask(__name__)
app.config["SECRET_KEY"] = os.environ.get("SECRET_KEY")
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///mealplan.db"
db = SQLAlchemy(app)
login_manager = LoginManager(app)
login_manager.login_view = "login"
# Anvender Open-router
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=OPENROUTER_API_KEY,
default_headers={
"HTTP-Referer": "http://localhost:5000",
"X-Title": "ChefGPT Meal Planner",
},
)
# --- Models ---
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(150), unique=True, nullable=False)
password = db.Column(db.String(150), nullable=False)
plans = db.relationship("MealPlan", backref="creator", lazy=True)
class MealPlan(db.Model):
id = db.Column(db.Integer, primary_key=True)
summary = db.Column(db.Text, nullable=False)
shopping = db.Column(db.Text, nullable=False)
recipes = db.Column(db.Text, nullable=False)
description = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
@app.route("/")
def index():
return render_template("index.html",source_link=os.environ.get("SOURCECODE_LINK"))
@app.route("/about")
def about():
return render_template("about.html")
@app.route("/logout")
@login_required
def logout():
logout_user()
return redirect(url_for("index"))
@app.route("/dashboard")
@login_required
def dashboard():
# Show the user's previous plans
plans = (
MealPlan.query.filter_by(user_id=current_user.id)
.order_by(MealPlan.id.desc())
.limit(3)
.all()
)
return render_template("dashboard.html", plans=plans)
@app.route("/plan/<int:plan_id>")
@login_required
def view_plan(plan_id):
plan = MealPlan.query.get_or_404(plan_id)
if plan.user_id != current_user.id:
flash("You do not have permission to view this plan.")
return redirect(url_for("dashboard"))
return render_template("plan_result.html", plan=plan)
@app.route("/generate", methods=["POST"])
@login_required
def generate():
data = {
"people_count": request.form.get("people_count"),
"budget_level": request.form.get("budget_level"),
"max_cooking_time": request.form.get("max_cooking_time"),
"skill_level": request.form.get("skill_level"),
"dietary_focus": request.form.get("dietary_focus"),
"dietary_preference": request.form.get("dietary_preference"),
"meal_strategy": request.form.get("meal_strategy"),
"fridge_items": request.form.get("fridge_items") or "None (start from scratch)",
"output_language": request.form.get("output_language", "English"),
}
try:
with open("system_prompt.txt", "r", encoding="utf-8") as f:
prompt_template = f.read()
# indsætter formular parametre i systemprompt
final_prompt = prompt_template.format(**data)
except FileNotFoundError:
flash("System prompt file missing. Please contact admin.", "error")
return redirect(url_for("dashboard"))
# Anvender open-router free modeller
try:
response = client.chat.completions.create(
model=OPENROUTER_MODEL,
messages=[{"role": "system", "content": final_prompt}],
temperature=0.7,
)
ai_output = response.choices[0].message.content
except Exception as e:
flash(f"AI Service Error: {str(e)}", "error")
return redirect(url_for("dashboard"))
# begynder parsing af AI svaret. Svaret skal leve op til formatkravet for at det kan indlæses pænt
def extract_section(text, start_tag, end_tag):
start = text.find(start_tag)
end = text.find(end_tag)
if start == -1 or end == -1 or end <= start:
return None
return text[start + len(start_tag) : end].strip()
desc = extract_section(ai_output, "[[DESCRIPTION_START]]", "[[DESCRIPTION_END]]")
summ = extract_section(ai_output, "[[SUMMARY_START]]", "[[SUMMARY_END]]")
shop = extract_section(ai_output, "[[SHOPPING_START]]", "[[SHOPPING_END]]")
reci = extract_section(ai_output, "[[RECIPES_START]]", "[[RECIPES_END]]")
if not all([desc, summ, shop, reci]):
flash("The plan could not be generated. Please try generating again.", "error")
return redirect(url_for("dashboard"))
# hvis AI svaret levede op til format kravet, gemmes det i db
try:
new_plan = MealPlan(
description=desc,
summary=summ,
shopping=shop,
recipes=reci,
user_id=current_user.id,
)
db.session.add(new_plan)
db.session.commit()
return redirect(url_for("view_plan", plan_id=new_plan.id))
except Exception as e:
db.session.rollback()
flash(f"Database Error: {str(e)}", "error")
return redirect(url_for("dashboard"))
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
user = User.query.filter_by(username=request.form.get("username")).first()
if user and check_password_hash(user.password, request.form.get("password")):
login_user(user)
return redirect(url_for("dashboard"))
flash("Invalid credentials")
return render_template("login.html")
@app.route("/register", methods=["GET", "POST"])
def register():
if request.method == "POST":
new_user = User(
username=request.form.get("username"),
password=generate_password_hash(
request.form.get("password"), method="pbkdf2:sha256"
),
)
db.session.add(new_user)
db.session.commit()
return redirect(url_for("/"))
return render_template("register.html")
if __name__ == "__main__":
with app.app_context():
db.create_all()
app.run()