2026-01-01 21:07:34 +01:00
|
|
|
from flask_sqlalchemy import SQLAlchemy
|
|
|
|
|
from flask_login import UserMixin
|
|
|
|
|
from werkzeug.security import generate_password_hash, check_password_hash
|
|
|
|
|
from datetime import datetime
|
2026-05-14 21:16:51 +02:00
|
|
|
from sqlalchemy import func
|
2026-01-01 21:07:34 +01:00
|
|
|
|
|
|
|
|
db = SQLAlchemy() # Create the extension object
|
|
|
|
|
|
|
|
|
|
class User(UserMixin, db.Model):
|
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
2026-07-19 11:29:10 +02:00
|
|
|
updated_at = db.Column(db.TIMESTAMP, default=datetime.now, onupdate=datetime.now, nullable=False)
|
2026-05-14 14:13:41 +02:00
|
|
|
group_id = db.Column(db.Integer, default=0, nullable=False)
|
2026-01-01 21:07:34 +01:00
|
|
|
name = db.Column(db.String(40), unique=True, nullable=False)
|
|
|
|
|
password_hash = db.Column(db.String(256))
|
|
|
|
|
is_admin = db.Column(db.Boolean, default=False)
|
|
|
|
|
is_approved = db.Column(db.Boolean, default=False)
|
|
|
|
|
is_private = db.Column(db.Boolean, default=False)
|
|
|
|
|
is_guest = db.Column(db.Boolean, default=False)
|
|
|
|
|
|
2026-01-18 08:22:41 +01:00
|
|
|
def as_dict(self):
|
|
|
|
|
result = {}
|
|
|
|
|
for p in ['id','name','is_admin','is_approved','is_private','is_guest']:
|
|
|
|
|
result[p] = getattr(self, p)
|
|
|
|
|
return result
|
|
|
|
|
|
2026-01-01 21:07:34 +01:00
|
|
|
def set_password(self, password):
|
|
|
|
|
self.password_hash = generate_password_hash(password)
|
|
|
|
|
|
|
|
|
|
def check_password(self, password):
|
|
|
|
|
return check_password_hash(self.password_hash, password)
|
|
|
|
|
|
|
|
|
|
def my_owned_lists(self, with_inactive=False):
|
|
|
|
|
if with_inactive:
|
|
|
|
|
return ListOfItems.query.filter_by(owner_user_id=self.id).all()
|
|
|
|
|
return ListOfItems.query.filter_by(owner_user_id=self.id, is_active=True).all()
|
|
|
|
|
|
|
|
|
|
def all_my_lists(self):
|
|
|
|
|
result = self.my_owned_lists()
|
|
|
|
|
for s in Shared.query.filter_by(user_id=self.id).all():
|
|
|
|
|
l = ListOfItems.query.filter_by(id=s.listofitem_id, is_active=True).first()
|
|
|
|
|
if l:
|
|
|
|
|
result.append(l)
|
|
|
|
|
return result
|
|
|
|
|
|
2026-01-18 08:22:41 +01:00
|
|
|
def all_my_lists_as_dict(self):
|
|
|
|
|
result = []
|
|
|
|
|
for l in self.all_my_lists():
|
|
|
|
|
result.append(l.as_dict())
|
|
|
|
|
return result
|
|
|
|
|
|
2026-01-01 21:07:34 +01:00
|
|
|
def shares_in_list(self, listofitem_id):
|
|
|
|
|
shared = Shared.query.filter_by(user_id=self.id, listofitem_id=listofitem_id).first()
|
|
|
|
|
return True if shared else False
|
|
|
|
|
|
2026-05-14 14:30:23 +02:00
|
|
|
def owns_list(self, listofitem_id):
|
|
|
|
|
return ListOfItems.query.filter_by(id=listofitem_id, owner_user_id=self.id).first() is not None
|
|
|
|
|
|
|
|
|
|
def can_access_list(self, listofitem_id):
|
|
|
|
|
listofitem = ListOfItems.query.filter_by(id=listofitem_id, is_active=True).first()
|
|
|
|
|
if not listofitem:
|
|
|
|
|
return False
|
|
|
|
|
return listofitem.owner_user_id == self.id or self.shares_in_list(listofitem_id)
|
|
|
|
|
|
|
|
|
|
def can_access_item(self, item_id):
|
|
|
|
|
item = Item.query.get(item_id)
|
|
|
|
|
return item is not None and self.can_access_list(item.listofitems_id)
|
|
|
|
|
|
2026-05-14 14:13:41 +02:00
|
|
|
def set_group_id(self, secret):
|
|
|
|
|
group = Group.query.filter_by(secret=secret).first()
|
|
|
|
|
if group:
|
|
|
|
|
return group.id
|
|
|
|
|
else:
|
|
|
|
|
return 0
|
2026-01-18 08:22:41 +01:00
|
|
|
|
2026-01-01 21:07:34 +01:00
|
|
|
class ListOfItems(db.Model):
|
2026-05-14 21:16:51 +02:00
|
|
|
__table_args__ = (
|
|
|
|
|
db.UniqueConstraint("owner_user_id", "name", name="uq_list_owner_name"),
|
|
|
|
|
)
|
|
|
|
|
|
2026-01-01 21:07:34 +01:00
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
2026-07-19 11:29:10 +02:00
|
|
|
updated_at = db.Column(db.TIMESTAMP, default=datetime.now, onupdate=datetime.now, nullable=False)
|
2026-01-01 21:07:34 +01:00
|
|
|
owner_user_id = db.Column(db.Integer, nullable=False)
|
2026-05-14 21:16:51 +02:00
|
|
|
name = db.Column(db.String(40), nullable=False)
|
2026-01-01 21:07:34 +01:00
|
|
|
is_active = db.Column(db.Boolean, default=False)
|
|
|
|
|
|
2026-05-14 21:16:51 +02:00
|
|
|
@classmethod
|
|
|
|
|
def name_exists_for_owner(cls, owner_user_id, name, exclude_id=None):
|
|
|
|
|
query = cls.query.filter(
|
|
|
|
|
cls.owner_user_id == owner_user_id,
|
|
|
|
|
func.lower(cls.name) == name.strip().lower(),
|
|
|
|
|
)
|
|
|
|
|
if exclude_id is not None:
|
|
|
|
|
query = query.filter(cls.id != exclude_id)
|
|
|
|
|
return query.first() is not None
|
|
|
|
|
|
2026-01-18 08:22:41 +01:00
|
|
|
def as_dict(self):
|
|
|
|
|
result = {}
|
|
|
|
|
for p in ['id','name','owner_user_id','is_active']:
|
|
|
|
|
result[p] = getattr(self, p)
|
|
|
|
|
result["pending"] = self.items_left()
|
|
|
|
|
result["total"] = self.items_total()
|
|
|
|
|
return result
|
|
|
|
|
|
2026-01-01 21:07:34 +01:00
|
|
|
def items_left(self):
|
|
|
|
|
return len(Item.query.filter_by(listofitems_id=self.id, is_checked=False, is_suggestion=False).all())
|
|
|
|
|
|
|
|
|
|
def items_total(self):
|
|
|
|
|
return len(Item.query.filter_by(listofitems_id=self.id, is_suggestion=False).all())
|
|
|
|
|
|
|
|
|
|
class Item(db.Model):
|
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
2026-07-19 11:29:10 +02:00
|
|
|
updated_at = db.Column(db.TIMESTAMP, default=datetime.now, onupdate=datetime.now, nullable=False)
|
2026-01-01 21:07:34 +01:00
|
|
|
listofitems_id = db.Column(db.Integer, nullable=False)
|
|
|
|
|
label = db.Column(db.String(40), nullable=False)
|
|
|
|
|
quantity = db.Column(db.Integer, default=0)
|
|
|
|
|
unit = db.Column(db.String(10))
|
|
|
|
|
label_alt1 = db.Column(db.String(40))
|
|
|
|
|
label_alt2 = db.Column(db.String(40))
|
|
|
|
|
is_checked = db.Column(db.Boolean, default=False)
|
|
|
|
|
is_suggestion = db.Column(db.Boolean, default=False)
|
|
|
|
|
category = db.Column(db.String(20))
|
|
|
|
|
|
2026-01-18 08:22:41 +01:00
|
|
|
def as_dict(self):
|
|
|
|
|
result = {}
|
|
|
|
|
for p in ['id','listofitems_id','label','quantity','unit','is_checked', 'is_suggestion', 'category']:
|
|
|
|
|
result[p] = getattr(self, p)
|
|
|
|
|
return result
|
|
|
|
|
|
2026-01-01 21:07:34 +01:00
|
|
|
class Shared(db.Model):
|
|
|
|
|
user_id = db.Column(db.Integer, primary_key=True)
|
|
|
|
|
listofitem_id = db.Column(db.Integer, primary_key=True)
|
2026-05-14 14:13:41 +02:00
|
|
|
|
|
|
|
|
class Group(db.Model):
|
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
2026-07-19 11:29:10 +02:00
|
|
|
updated_at = db.Column(db.TIMESTAMP, default=datetime.now, onupdate=datetime.now, nullable=False)
|
2026-05-14 14:13:41 +02:00
|
|
|
secret = db.Column(db.String(128))
|