webp support
This commit is contained in:
263
add_products.py
263
add_products.py
@@ -4,66 +4,249 @@ from app import db, SuggestedProduct, app
|
||||
|
||||
CATEGORIES = {
|
||||
"Przyprawa": [
|
||||
"przyprawa", "pieprz", "sól", "bazylia", "oregano", "papryka", "majeranek", "czosnek",
|
||||
"tymianek", "rozmaryn", "kolendra", "curry", "imbir", "goździki", "chili", "koper",
|
||||
"kminek", "liść laurowy", "ziele angielskie", "kurkuma", "musztarda", "chrzan"
|
||||
"przyprawa",
|
||||
"pieprz",
|
||||
"sól",
|
||||
"bazylia",
|
||||
"oregano",
|
||||
"papryka",
|
||||
"majeranek",
|
||||
"czosnek",
|
||||
"tymianek",
|
||||
"rozmaryn",
|
||||
"kolendra",
|
||||
"curry",
|
||||
"imbir",
|
||||
"goździki",
|
||||
"chili",
|
||||
"koper",
|
||||
"kminek",
|
||||
"liść laurowy",
|
||||
"ziele angielskie",
|
||||
"kurkuma",
|
||||
"musztarda",
|
||||
"chrzan",
|
||||
],
|
||||
"Mięso": [
|
||||
"kurczak", "piersi z kurczaka", "udka z kurczaka", "wołowina", "mielona wołowina",
|
||||
"wieprzowina", "schab", "łopatka", "szynka", "boczek", "indyk", "filet z indyka",
|
||||
"gulasz", "pasztet", "karkówka", "żeberka", "kiełbasa", "parówki", "salami", "kabanos"
|
||||
"kurczak",
|
||||
"piersi z kurczaka",
|
||||
"udka z kurczaka",
|
||||
"wołowina",
|
||||
"mielona wołowina",
|
||||
"wieprzowina",
|
||||
"schab",
|
||||
"łopatka",
|
||||
"szynka",
|
||||
"boczek",
|
||||
"indyk",
|
||||
"filet z indyka",
|
||||
"gulasz",
|
||||
"pasztet",
|
||||
"karkówka",
|
||||
"żeberka",
|
||||
"kiełbasa",
|
||||
"parówki",
|
||||
"salami",
|
||||
"kabanos",
|
||||
],
|
||||
"Ryba i owoce morza": [
|
||||
"łosoś", "dorsz", "mintaj", "makrela", "pstrąg", "karp", "śledź", "tuńczyk",
|
||||
"morszczuk", "sardynka", "szproty", "anchois", "tilapia", "sandacz", "halibut",
|
||||
"sum", "flądra", "ostrobok", "paluszki rybne", "konserwa rybna"
|
||||
"łosoś",
|
||||
"dorsz",
|
||||
"mintaj",
|
||||
"makrela",
|
||||
"pstrąg",
|
||||
"karp",
|
||||
"śledź",
|
||||
"tuńczyk",
|
||||
"morszczuk",
|
||||
"sardynka",
|
||||
"szproty",
|
||||
"anchois",
|
||||
"tilapia",
|
||||
"sandacz",
|
||||
"halibut",
|
||||
"sum",
|
||||
"flądra",
|
||||
"ostrobok",
|
||||
"paluszki rybne",
|
||||
"konserwa rybna",
|
||||
],
|
||||
"Nabiał": [
|
||||
"mleko", "jogurt", "ser żółty", "ser biały", "twaróg", "śmietana", "masło",
|
||||
"kefir", "maślanka", "serek wiejski", "serek topiony", "mozzarella", "feta",
|
||||
"parmezan", "gouda", "emmental", "ser pleśniowy", "ser homogenizowany",
|
||||
"serek mascarpone", "ser ricotta"
|
||||
"mleko",
|
||||
"jogurt",
|
||||
"ser żółty",
|
||||
"ser biały",
|
||||
"twaróg",
|
||||
"śmietana",
|
||||
"masło",
|
||||
"kefir",
|
||||
"maślanka",
|
||||
"serek wiejski",
|
||||
"serek topiony",
|
||||
"mozzarella",
|
||||
"feta",
|
||||
"parmezan",
|
||||
"gouda",
|
||||
"emmental",
|
||||
"ser pleśniowy",
|
||||
"ser homogenizowany",
|
||||
"serek mascarpone",
|
||||
"ser ricotta",
|
||||
],
|
||||
"Warzywo": [
|
||||
"pomidor", "ogórek", "marchew", "cebula", "sałata", "papryka", "ziemniak",
|
||||
"kapusta", "brokuł", "kalafior", "cukinia", "bakłażan", "szpinak", "rukola",
|
||||
"seler", "por", "burak", "dynia", "rzodkiewka", "fasola"
|
||||
"pomidor",
|
||||
"ogórek",
|
||||
"marchew",
|
||||
"cebula",
|
||||
"sałata",
|
||||
"papryka",
|
||||
"ziemniak",
|
||||
"kapusta",
|
||||
"brokuł",
|
||||
"kalafior",
|
||||
"cukinia",
|
||||
"bakłażan",
|
||||
"szpinak",
|
||||
"rukola",
|
||||
"seler",
|
||||
"por",
|
||||
"burak",
|
||||
"dynia",
|
||||
"rzodkiewka",
|
||||
"fasola",
|
||||
],
|
||||
"Owoc": [
|
||||
"jabłko", "banan", "gruszka", "truskawka", "winogrono", "malina", "borówka",
|
||||
"czereśnia", "wiśnia", "brzoskwinia", "nektaryna", "śliwka", "ananas",
|
||||
"mango", "kiwi", "cytryna", "limonka", "pomarańcza", "mandarynka", "grejpfrut"
|
||||
"jabłko",
|
||||
"banan",
|
||||
"gruszka",
|
||||
"truskawka",
|
||||
"winogrono",
|
||||
"malina",
|
||||
"borówka",
|
||||
"czereśnia",
|
||||
"wiśnia",
|
||||
"brzoskwinia",
|
||||
"nektaryna",
|
||||
"śliwka",
|
||||
"ananas",
|
||||
"mango",
|
||||
"kiwi",
|
||||
"cytryna",
|
||||
"limonka",
|
||||
"pomarańcza",
|
||||
"mandarynka",
|
||||
"grejpfrut",
|
||||
],
|
||||
"Pieczywo i zboża": [
|
||||
"chleb", "bułka", "bagietka", "kajzerka", "pumpernikiel", "chleb razowy",
|
||||
"chleb żytni", "tost", "grahamka", "croissant", "tortilla", "pizza",
|
||||
"pierogi", "ryż", "makaron", "kasza jaglana", "kasza gryczana", "owsianka",
|
||||
"płatki kukurydziane", "musli"
|
||||
"chleb",
|
||||
"bułka",
|
||||
"bagietka",
|
||||
"kajzerka",
|
||||
"pumpernikiel",
|
||||
"chleb razowy",
|
||||
"chleb żytni",
|
||||
"tost",
|
||||
"grahamka",
|
||||
"croissant",
|
||||
"tortilla",
|
||||
"pizza",
|
||||
"pierogi",
|
||||
"ryż",
|
||||
"makaron",
|
||||
"kasza jaglana",
|
||||
"kasza gryczana",
|
||||
"owsianka",
|
||||
"płatki kukurydziane",
|
||||
"musli",
|
||||
],
|
||||
"Słodycze i przekąski": [
|
||||
"czekolada", "baton", "ciastko", "wafel", "lody", "cukierek", "żelki",
|
||||
"herbatnik", "paluszki", "chipsy", "orzeszki", "popcorn", "krakersy",
|
||||
"ciasto", "muffin", "pączek", "drożdżówka", "babeczka", "piernik", "beza"
|
||||
"czekolada",
|
||||
"baton",
|
||||
"ciastko",
|
||||
"wafel",
|
||||
"lody",
|
||||
"cukierek",
|
||||
"żelki",
|
||||
"herbatnik",
|
||||
"paluszki",
|
||||
"chipsy",
|
||||
"orzeszki",
|
||||
"popcorn",
|
||||
"krakersy",
|
||||
"ciasto",
|
||||
"muffin",
|
||||
"pączek",
|
||||
"drożdżówka",
|
||||
"babeczka",
|
||||
"piernik",
|
||||
"beza",
|
||||
],
|
||||
"Napoje": [
|
||||
"woda", "sok jabłkowy", "sok pomarańczowy", "sok multiwitamina", "cola",
|
||||
"pepsi", "napój gazowany", "kawa", "herbata", "piwo", "wino czerwone",
|
||||
"wino białe", "tonik", "lemoniada", "napój izotoniczny", "kompot",
|
||||
"napój mleczny", "maślanka pitna", "koktajl owocowy", "nektar"
|
||||
"woda",
|
||||
"sok jabłkowy",
|
||||
"sok pomarańczowy",
|
||||
"sok multiwitamina",
|
||||
"cola",
|
||||
"pepsi",
|
||||
"napój gazowany",
|
||||
"kawa",
|
||||
"herbata",
|
||||
"piwo",
|
||||
"wino czerwone",
|
||||
"wino białe",
|
||||
"tonik",
|
||||
"lemoniada",
|
||||
"napój izotoniczny",
|
||||
"kompot",
|
||||
"napój mleczny",
|
||||
"maślanka pitna",
|
||||
"koktajl owocowy",
|
||||
"nektar",
|
||||
],
|
||||
"Tłuszcze i oleje": [
|
||||
"oliwa", "olej rzepakowy", "olej słonecznikowy", "masło klarowane",
|
||||
"margaryna", "smalec", "masło orzechowe", "tłuszcz kokosowy",
|
||||
"olej lniany", "olej z pestek winogron", "olej sezamowy",
|
||||
"olej ryżowy", "olej z awokado", "olej kukurydziany", "olej arachidowy",
|
||||
"olej palmowy", "olej konopny", "olej sojowy", "olej dyniowy", "olej z orzechów włoskich"
|
||||
"oliwa",
|
||||
"olej rzepakowy",
|
||||
"olej słonecznikowy",
|
||||
"masło klarowane",
|
||||
"margaryna",
|
||||
"smalec",
|
||||
"masło orzechowe",
|
||||
"tłuszcz kokosowy",
|
||||
"olej lniany",
|
||||
"olej z pestek winogron",
|
||||
"olej sezamowy",
|
||||
"olej ryżowy",
|
||||
"olej z awokado",
|
||||
"olej kukurydziany",
|
||||
"olej arachidowy",
|
||||
"olej palmowy",
|
||||
"olej konopny",
|
||||
"olej sojowy",
|
||||
"olej dyniowy",
|
||||
"olej z orzechów włoskich",
|
||||
],
|
||||
"Dania gotowe": [
|
||||
"pizza", "hamburger", "hot dog", "zupa", "gulasz", "pierogi ruskie",
|
||||
"pierogi z mięsem", "lasagne", "sałatka warzywna", "kanapka",
|
||||
"wrap", "tortilla", "zapiekanka", "sushi", "falafel", "kebab",
|
||||
"pyzy", "kluski śląskie", "kotlet schabowy", "gołąbki"
|
||||
]
|
||||
"pizza",
|
||||
"hamburger",
|
||||
"hot dog",
|
||||
"zupa",
|
||||
"gulasz",
|
||||
"pierogi ruskie",
|
||||
"pierogi z mięsem",
|
||||
"lasagne",
|
||||
"sałatka warzywna",
|
||||
"kanapka",
|
||||
"wrap",
|
||||
"tortilla",
|
||||
"zapiekanka",
|
||||
"sushi",
|
||||
"falafel",
|
||||
"kebab",
|
||||
"pyzy",
|
||||
"kluski śląskie",
|
||||
"kotlet schabowy",
|
||||
"gołąbki",
|
||||
],
|
||||
}
|
||||
|
||||
produkty = []
|
||||
|
@@ -2,6 +2,7 @@ import os
|
||||
from datetime import datetime
|
||||
from app import db, app, Receipt
|
||||
|
||||
|
||||
def extract_list_id(filename):
|
||||
if filename.startswith("list_"):
|
||||
parts = filename.split("_", 2)
|
||||
@@ -9,6 +10,7 @@ def extract_list_id(filename):
|
||||
return int(parts[1])
|
||||
return None
|
||||
|
||||
|
||||
def migrate_missing_receipts():
|
||||
with app.app_context():
|
||||
folder = app.config["UPLOAD_FOLDER"]
|
||||
@@ -30,7 +32,9 @@ def migrate_missing_receipts():
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
new_receipt = Receipt(list_id=list_id, filename=file, uploaded_at=datetime.utcnow())
|
||||
new_receipt = Receipt(
|
||||
list_id=list_id, filename=file, uploaded_at=datetime.utcnow()
|
||||
)
|
||||
db.session.add(new_receipt)
|
||||
added += 1
|
||||
print(f"📄 {file} dodany do Receipt (list_id={list_id})")
|
||||
@@ -38,5 +42,6 @@ def migrate_missing_receipts():
|
||||
db.session.commit()
|
||||
print(f"\n✅ Dodano: {added}, pominięto (już były): {skipped}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
migrate_missing_receipts()
|
||||
|
22
app.py
22
app.py
@@ -48,7 +48,7 @@ from functools import wraps
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(Config)
|
||||
register_heif_opener() # pillow_heif dla HEIC
|
||||
register_heif_opener() # pillow_heif dla HEIC
|
||||
|
||||
ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "gif", "webp", "heic"}
|
||||
SQLALCHEMY_ECHO = True
|
||||
@@ -142,6 +142,7 @@ class Expense(db.Model):
|
||||
receipt_filename = db.Column(db.String(255), nullable=True)
|
||||
list = db.relationship("ShoppingList", backref="expenses", lazy=True)
|
||||
|
||||
|
||||
class Receipt(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
list_id = db.Column(db.Integer, db.ForeignKey("shopping_list.id"), nullable=False)
|
||||
@@ -150,6 +151,7 @@ class Receipt(db.Model):
|
||||
shopping_list = db.relationship("ShoppingList", backref="receipts", lazy=True)
|
||||
filesize = db.Column(db.Integer, nullable=True)
|
||||
|
||||
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
from werkzeug.security import generate_password_hash
|
||||
@@ -297,7 +299,6 @@ def delete_receipts_for_list(list_id):
|
||||
print(f"Nie udało się usunąć pliku {filename}: {e}")
|
||||
|
||||
|
||||
|
||||
# zabezpieczenie logowani do systemu - błędne hasła
|
||||
def is_ip_blocked(ip):
|
||||
now = time.time()
|
||||
@@ -937,6 +938,7 @@ def upload_receipt(list_id):
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
@app.route("/upload_receipt/<int:list_id>", methods=["POST"])
|
||||
def upload_receipt(list_id):
|
||||
if "receipt" not in request.files:
|
||||
@@ -961,12 +963,15 @@ def upload_receipt(list_id):
|
||||
list_id=list_id,
|
||||
filename=webp_filename,
|
||||
filesize=filesize,
|
||||
uploaded_at=uploaded_at
|
||||
uploaded_at=uploaded_at,
|
||||
)
|
||||
db.session.add(new_receipt)
|
||||
db.session.commit()
|
||||
|
||||
if request.is_json or request.headers.get("X-Requested-With") == "XMLHttpRequest":
|
||||
if (
|
||||
request.is_json
|
||||
or request.headers.get("X-Requested-With") == "XMLHttpRequest"
|
||||
):
|
||||
url = url_for("uploaded_file", filename=webp_filename)
|
||||
socketio.emit("receipt_added", {"url": url}, to=str(list_id))
|
||||
return jsonify({"success": True, "url": url})
|
||||
@@ -977,7 +982,6 @@ def upload_receipt(list_id):
|
||||
return _receipt_error("Niedozwolony format pliku")
|
||||
|
||||
|
||||
|
||||
@app.route("/uploads/<filename>")
|
||||
def uploaded_file(filename):
|
||||
response = send_from_directory(app.config["UPLOAD_FOLDER"], filename)
|
||||
@@ -1198,6 +1202,7 @@ def delete_user(user_id):
|
||||
flash("Użytkownik usunięty", "success")
|
||||
return redirect(url_for("list_users"))
|
||||
|
||||
|
||||
@app.route("/admin/receipts/<id>")
|
||||
@login_required
|
||||
@admin_required
|
||||
@@ -1250,7 +1255,6 @@ def delete_receipt(filename):
|
||||
return redirect(next_url or url_for("admin_receipts", id="all"))
|
||||
|
||||
|
||||
|
||||
@app.route("/admin/delete_selected_lists", methods=["POST"])
|
||||
@login_required
|
||||
@admin_required
|
||||
@@ -1286,7 +1290,11 @@ def edit_list(list_id):
|
||||
db.session.query(Item).filter_by(list_id=list_id).order_by(Item.id.desc()).all()
|
||||
)
|
||||
|
||||
receipts = Receipt.query.filter_by(list_id=list_id).order_by(Receipt.uploaded_at.desc()).all()
|
||||
receipts = (
|
||||
Receipt.query.filter_by(list_id=list_id)
|
||||
.order_by(Receipt.uploaded_at.desc())
|
||||
.all()
|
||||
)
|
||||
|
||||
if request.method == "POST":
|
||||
action = request.form.get("action")
|
||||
|
21
config.py
21
config.py
@@ -1,14 +1,15 @@
|
||||
import os
|
||||
|
||||
|
||||
class Config:
|
||||
SECRET_KEY = os.environ.get('SECRET_KEY', 'D8pceNZ8q%YR7^7F&9wAC2')
|
||||
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL', 'sqlite:///shopping.db')
|
||||
SECRET_KEY = os.environ.get("SECRET_KEY", "D8pceNZ8q%YR7^7F&9wAC2")
|
||||
SQLALCHEMY_DATABASE_URI = os.environ.get("DATABASE_URL", "sqlite:///shopping.db")
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
SYSTEM_PASSWORD = os.environ.get('SYSTEM_PASSWORD', 'admin')
|
||||
DEFAULT_ADMIN_USERNAME = os.environ.get('DEFAULT_ADMIN_USERNAME', 'admin')
|
||||
DEFAULT_ADMIN_PASSWORD = os.environ.get('DEFAULT_ADMIN_PASSWORD', 'admin123')
|
||||
UPLOAD_FOLDER = os.environ.get('UPLOAD_FOLDER', 'uploads')
|
||||
AUTHORIZED_COOKIE_VALUE = os.environ.get('AUTHORIZED_COOKIE_VALUE', 'cookievalue')
|
||||
AUTH_COOKIE_MAX_AGE = int(os.environ.get('AUTH_COOKIE_MAX_AGE', 86400))
|
||||
HEALTHCHECK_TOKEN = os.environ.get('HEALTHCHECK_TOKEN', 'alamapsaikota1234')
|
||||
SESSION_TIMEOUT_MINUTES = int(os.environ.get('SESSION_TIMEOUT_MINUTES', 10080))
|
||||
SYSTEM_PASSWORD = os.environ.get("SYSTEM_PASSWORD", "admin")
|
||||
DEFAULT_ADMIN_USERNAME = os.environ.get("DEFAULT_ADMIN_USERNAME", "admin")
|
||||
DEFAULT_ADMIN_PASSWORD = os.environ.get("DEFAULT_ADMIN_PASSWORD", "admin123")
|
||||
UPLOAD_FOLDER = os.environ.get("UPLOAD_FOLDER", "uploads")
|
||||
AUTHORIZED_COOKIE_VALUE = os.environ.get("AUTHORIZED_COOKIE_VALUE", "cookievalue")
|
||||
AUTH_COOKIE_MAX_AGE = int(os.environ.get("AUTH_COOKIE_MAX_AGE", 86400))
|
||||
HEALTHCHECK_TOKEN = os.environ.get("HEALTHCHECK_TOKEN", "alamapsaikota1234")
|
||||
SESSION_TIMEOUT_MINUTES = int(os.environ.get("SESSION_TIMEOUT_MINUTES", 10080))
|
||||
|
@@ -6,6 +6,7 @@ from app import db, app, Receipt
|
||||
ALLOWED_EXTS = ("jpg", "jpeg", "png", "gif", "heic")
|
||||
UPLOAD_FOLDER = None
|
||||
|
||||
|
||||
def convert_to_webp(input_path, output_path):
|
||||
try:
|
||||
image = Image.open(input_path).convert("RGB")
|
||||
@@ -15,6 +16,7 @@ def convert_to_webp(input_path, output_path):
|
||||
print(f"Błąd konwersji {input_path}: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def extract_list_id(filename):
|
||||
if filename.startswith("list_"):
|
||||
parts = filename.split("_", 2)
|
||||
@@ -22,6 +24,7 @@ def extract_list_id(filename):
|
||||
return int(parts[1])
|
||||
return None
|
||||
|
||||
|
||||
def migrate():
|
||||
global UPLOAD_FOLDER
|
||||
with app.app_context():
|
||||
@@ -53,13 +56,19 @@ def migrate():
|
||||
|
||||
if convert_to_webp(src_path, dst_path):
|
||||
os.remove(src_path)
|
||||
r = Receipt.query.filter_by(list_id=list_id, filename=webp_filename).first()
|
||||
r = Receipt.query.filter_by(
|
||||
list_id=list_id, filename=webp_filename
|
||||
).first()
|
||||
if r:
|
||||
print(f"Już istnieje w Receipt: {webp_filename}")
|
||||
existing += 1
|
||||
continue
|
||||
|
||||
new_receipt = Receipt(list_id=list_id, filename=webp_filename, uploaded_at=datetime.utcnow())
|
||||
new_receipt = Receipt(
|
||||
list_id=list_id,
|
||||
filename=webp_filename,
|
||||
uploaded_at=datetime.utcnow(),
|
||||
)
|
||||
db.session.add(new_receipt)
|
||||
created += 1
|
||||
print(f"{file} → {webp_filename} + zapis do Receipt")
|
||||
@@ -69,5 +78,6 @@ def migrate():
|
||||
print(f"Pominięte (webp istniało): {skipped}")
|
||||
print(f"Duplikaty w bazie: {existing}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
migrate()
|
||||
|
@@ -2,13 +2,16 @@ import os
|
||||
from datetime import datetime
|
||||
from app import app, db, Receipt
|
||||
|
||||
|
||||
def update_missing_receipt_fields():
|
||||
with app.app_context():
|
||||
folder = app.config["UPLOAD_FOLDER"]
|
||||
updated = 0
|
||||
|
||||
receipts = Receipt.query.filter(
|
||||
(Receipt.filesize == None) | (Receipt.filesize == 0) | (Receipt.uploaded_at == None)
|
||||
(Receipt.filesize == None)
|
||||
| (Receipt.filesize == 0)
|
||||
| (Receipt.uploaded_at == None)
|
||||
).all()
|
||||
|
||||
for r in receipts:
|
||||
@@ -36,5 +39,6 @@ def update_missing_receipt_fields():
|
||||
db.session.commit()
|
||||
print(f"\nZaktualizowano {updated} rekordów.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
update_missing_receipt_fields()
|
||||
|
Reference in New Issue
Block a user