duzo zmian, funkcji
This commit is contained in:
183
app.py
183
app.py
@ -12,7 +12,7 @@ from config import Config
|
||||
from PIL import Image
|
||||
from werkzeug.utils import secure_filename
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy import func, extract
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(Config)
|
||||
@ -64,6 +64,13 @@ class SuggestedProduct(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(150), unique=True, nullable=False)
|
||||
|
||||
class Expense(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
list_id = db.Column(db.Integer, db.ForeignKey('shopping_list.id'))
|
||||
amount = db.Column(db.Float, nullable=False)
|
||||
added_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
receipt_filename = db.Column(db.String(255), nullable=True)
|
||||
|
||||
@static_bp.route('/static/js/live.js')
|
||||
def serve_live_js():
|
||||
response = send_from_directory('static/js', 'live.js')
|
||||
@ -161,11 +168,20 @@ def system_auth():
|
||||
|
||||
@app.route('/')
|
||||
def index_guest():
|
||||
lists = ShoppingList.query.all()
|
||||
now = datetime.utcnow()
|
||||
lists = ShoppingList.query.filter(
|
||||
((ShoppingList.expires_at == None) | (ShoppingList.expires_at > now)),
|
||||
ShoppingList.is_archived == False
|
||||
).order_by(ShoppingList.created_at.desc()).all()
|
||||
|
||||
for l in lists:
|
||||
items = Item.query.filter_by(list_id=l.id).all()
|
||||
l.total_count = len(items)
|
||||
l.purchased_count = len([i for i in items if i.purchased])
|
||||
|
||||
expenses = Expense.query.filter_by(list_id=l.id).all()
|
||||
l.total_expense = sum(e.amount for e in expenses)
|
||||
|
||||
return render_template('index.html', lists=lists)
|
||||
|
||||
@app.errorhandler(404)
|
||||
@ -217,11 +233,11 @@ def create_list():
|
||||
def view_list(list_id):
|
||||
shopping_list = ShoppingList.query.get_or_404(list_id)
|
||||
items = Item.query.filter_by(list_id=list_id).all()
|
||||
|
||||
total_count = len(items)
|
||||
purchased_count = len([i for i in items if i.purchased])
|
||||
percent = (purchased_count / total_count * 100) if total_count > 0 else 0
|
||||
|
||||
expenses = Expense.query.filter_by(list_id=list_id).all()
|
||||
total_expense = sum(e.amount for e in expenses)
|
||||
receipt_pattern = f"list_{list_id}"
|
||||
all_files = os.listdir(app.config['UPLOAD_FOLDER'])
|
||||
receipt_files = [f for f in all_files if receipt_pattern in f]
|
||||
@ -233,10 +249,11 @@ def view_list(list_id):
|
||||
receipt_files=receipt_files,
|
||||
total_count=total_count,
|
||||
purchased_count=purchased_count,
|
||||
percent=percent
|
||||
percent=percent,
|
||||
expenses=expenses,
|
||||
total_expense=total_expense
|
||||
)
|
||||
|
||||
|
||||
@app.route('/share/<token>')
|
||||
def share_list(token):
|
||||
shopping_list = ShoppingList.query.filter_by(share_token=token).first_or_404()
|
||||
@ -246,21 +263,36 @@ def share_list(token):
|
||||
all_files = os.listdir(app.config['UPLOAD_FOLDER'])
|
||||
receipt_files = [f for f in all_files if receipt_pattern in f]
|
||||
|
||||
return render_template('list_guest.html', list=shopping_list, items=items, receipt_files=receipt_files)
|
||||
expenses = Expense.query.filter_by(list_id=shopping_list.id).all()
|
||||
total_expense = sum(e.amount for e in expenses)
|
||||
|
||||
return render_template(
|
||||
'list_guest.html',
|
||||
list=shopping_list,
|
||||
items=items,
|
||||
receipt_files=receipt_files,
|
||||
expenses=expenses,
|
||||
total_expense=total_expense
|
||||
)
|
||||
|
||||
|
||||
@app.route('/guest-list/<int:list_id>')
|
||||
def guest_list(list_id):
|
||||
shopping_list = ShoppingList.query.get_or_404(list_id)
|
||||
items = Item.query.filter_by(list_id=list_id).all()
|
||||
|
||||
receipt_pattern = f"list_{list_id}"
|
||||
all_files = os.listdir(app.config['UPLOAD_FOLDER'])
|
||||
|
||||
receipt_files = [f for f in all_files if receipt_pattern in f]
|
||||
|
||||
return render_template('list_guest.html', list=shopping_list, items=items, receipt_files=receipt_files)
|
||||
|
||||
expenses = Expense.query.filter_by(list_id=list_id).all()
|
||||
total_expense = sum(e.amount for e in expenses)
|
||||
return render_template(
|
||||
'list_guest.html',
|
||||
list=shopping_list,
|
||||
items=items,
|
||||
receipt_files=receipt_files,
|
||||
expenses=expenses,
|
||||
total_expense=total_expense
|
||||
)
|
||||
|
||||
@app.route('/copy/<int:list_id>')
|
||||
@login_required
|
||||
@ -349,9 +381,8 @@ def admin_panel():
|
||||
purchased_count = len([i for i in items if i.purchased])
|
||||
percent = (purchased_count / total_count * 100) if total_count > 0 else 0
|
||||
comments_count = len([i for i in items if i.note and i.note.strip() != ''])
|
||||
purchased_items_count = Item.query.filter_by(purchased=True).count()
|
||||
|
||||
|
||||
expenses = Expense.query.filter_by(list_id=l.id).all()
|
||||
total_expense = sum(e.amount for e in expenses)
|
||||
receipt_pattern = f"list_{l.id}"
|
||||
receipt_files = [f for f in all_files if receipt_pattern in f]
|
||||
|
||||
@ -361,28 +392,53 @@ def admin_panel():
|
||||
'purchased_count': purchased_count,
|
||||
'percent': round(percent),
|
||||
'comments_count': comments_count,
|
||||
'receipts_count': len(receipt_files)
|
||||
'receipts_count': len(receipt_files),
|
||||
'total_expense': total_expense
|
||||
})
|
||||
|
||||
# Najczęściej kupowane produkty
|
||||
top_products = (
|
||||
db.session.query(Item.name, func.count(Item.id).label('count'))
|
||||
.filter(Item.purchased == True)
|
||||
.group_by(Item.name)
|
||||
.order_by(func.count(Item.id).desc())
|
||||
.limit(5)
|
||||
.all()
|
||||
)
|
||||
|
||||
top_products = (
|
||||
db.session.query(Item.name, func.count(Item.id).label('count'))
|
||||
.filter(Item.purchased == True)
|
||||
.group_by(Item.name)
|
||||
.order_by(func.count(Item.id).desc())
|
||||
.limit(5)
|
||||
.all()
|
||||
)
|
||||
purchased_items_count = Item.query.filter_by(purchased=True).count()
|
||||
|
||||
# Podsumowanie wydatków
|
||||
total_expense_sum = db.session.query(func.sum(Expense.amount)).scalar() or 0
|
||||
|
||||
current_year = datetime.utcnow().year
|
||||
year_expense_sum = (
|
||||
db.session.query(func.sum(Expense.amount))
|
||||
.filter(extract('year', Expense.added_at) == current_year)
|
||||
.scalar() or 0
|
||||
)
|
||||
|
||||
current_month = datetime.utcnow().month
|
||||
month_expense_sum = (
|
||||
db.session.query(func.sum(Expense.amount))
|
||||
.filter(extract('year', Expense.added_at) == current_year)
|
||||
.filter(extract('month', Expense.added_at) == current_month)
|
||||
.scalar() or 0
|
||||
)
|
||||
|
||||
return render_template(
|
||||
'admin/admin_panel.html',
|
||||
user_count=user_count,
|
||||
list_count=list_count,
|
||||
item_count=item_count,
|
||||
purchased_items_count=purchased_items_count,
|
||||
enriched_lists=enriched_lists,
|
||||
top_products=top_products,
|
||||
total_expense_sum=total_expense_sum,
|
||||
year_expense_sum=year_expense_sum,
|
||||
month_expense_sum=month_expense_sum,
|
||||
)
|
||||
|
||||
return render_template(
|
||||
'admin/admin_panel.html',
|
||||
user_count=user_count,
|
||||
list_count=list_count,
|
||||
item_count=item_count,
|
||||
purchased_items_count=purchased_items_count,
|
||||
enriched_lists=enriched_lists,
|
||||
top_products=top_products,
|
||||
)
|
||||
|
||||
@app.route('/admin/delete_list/<int:list_id>')
|
||||
@login_required
|
||||
@ -491,15 +547,46 @@ def delete_receipt(filename):
|
||||
def edit_list(list_id):
|
||||
if not current_user.is_admin:
|
||||
return redirect(url_for('index_guest'))
|
||||
|
||||
l = ShoppingList.query.get_or_404(list_id)
|
||||
expenses = Expense.query.filter_by(list_id=list_id).all()
|
||||
total_expense = sum(e.amount for e in expenses)
|
||||
|
||||
if request.method == 'POST':
|
||||
new_title = request.form.get('title')
|
||||
if new_title:
|
||||
l.title = new_title
|
||||
new_amount_str = request.form.get('amount')
|
||||
is_archived = 'archived' in request.form
|
||||
|
||||
if new_title and new_title.strip():
|
||||
l.title = new_title.strip()
|
||||
|
||||
l.is_archived = is_archived
|
||||
|
||||
if new_amount_str:
|
||||
try:
|
||||
new_amount = float(new_amount_str)
|
||||
|
||||
|
||||
if expenses:
|
||||
for expense in expenses:
|
||||
db.session.delete(expense)
|
||||
db.session.commit()
|
||||
|
||||
new_expense = Expense(list_id=list_id, amount=new_amount)
|
||||
db.session.add(new_expense)
|
||||
db.session.commit()
|
||||
flash('Zaktualizowano tytuł, archiwizację i/lub kwotę wydatku', 'success')
|
||||
except ValueError:
|
||||
flash('Niepoprawna kwota', 'danger')
|
||||
return redirect(url_for('edit_list', list_id=list_id))
|
||||
else:
|
||||
db.session.commit()
|
||||
flash('Zaktualizowano tytuł listy', 'success')
|
||||
flash('Zaktualizowano tytuł i/lub archiwizację', 'success')
|
||||
|
||||
return redirect(url_for('admin_panel'))
|
||||
return render_template('admin/edit_list.html', list=l)
|
||||
|
||||
return render_template('admin/edit_list.html', list=l, total_expense=total_expense)
|
||||
|
||||
|
||||
@app.route('/admin/delete_selected_lists', methods=['POST'])
|
||||
@login_required
|
||||
@ -537,6 +624,11 @@ def delete_all_items():
|
||||
flash('Usunięto wszystkie produkty', 'success')
|
||||
return redirect(url_for('admin_panel'))
|
||||
|
||||
|
||||
# =========================================================================================
|
||||
# SOCKET.IO
|
||||
# =========================================================================================
|
||||
|
||||
@socketio.on('delete_item')
|
||||
def handle_delete_item(data):
|
||||
item = Item.query.get(data['item_id'])
|
||||
@ -627,6 +719,23 @@ def handle_update_note(data):
|
||||
db.session.commit()
|
||||
emit('note_updated', {'item_id': item_id, 'note': note}, to=str(item.list_id))
|
||||
|
||||
@socketio.on('add_expense')
|
||||
def handle_add_expense(data):
|
||||
list_id = data['list_id']
|
||||
amount = data['amount']
|
||||
|
||||
new_expense = Expense(list_id=list_id, amount=amount)
|
||||
db.session.add(new_expense)
|
||||
db.session.commit()
|
||||
|
||||
total = db.session.query(func.sum(Expense.amount)).filter_by(list_id=list_id).scalar() or 0
|
||||
|
||||
emit('expense_added', {
|
||||
'amount': amount,
|
||||
'total': total
|
||||
}, to=str(list_id))
|
||||
|
||||
|
||||
@app.cli.command('create_db')
|
||||
def create_db():
|
||||
db.create_all()
|
||||
|
Reference in New Issue
Block a user