Browse Source

reformat and make search inclusive of last day

master
Mohamad Safadieh 5 months ago
parent
commit
97cf3d4727
Signed by: mhmd GPG Key ID: 2F3FDF2EAE1E3C36
9 changed files with 203 additions and 141 deletions
  1. +3
    -6
      guineapigs/forms.py
  2. +21
    -3
      guineapigs/models.py
  3. +10
    -0
      guineapigs/utils.py
  4. +40
    -27
      guineapigs/views.py
  5. +11
    -11
      migrations/env.py
  6. +43
    -26
      migrations/versions/1e175859da76_.py
  7. +8
    -8
      migrations/versions/4d3db79640c9_.py
  8. +61
    -54
      migrations/versions/b0fdf277700e_.py
  9. +6
    -6
      migrations/versions/e1bd94980514_.py

+ 3
- 6
guineapigs/forms.py View File

@@ -79,6 +79,7 @@ class LoginForm(ModelForm):
label="first name", validators=[DataRequired("name can't be blank")]
)


class HistoryForm(Form):
"""
fields:
@@ -86,9 +87,5 @@ class HistoryForm(Form):
- end: date
"""

start = DateField(
label="start",
validators=[DataRequired()])
end = DateField(
label="end",
validators=[DataRequired()])
start = DateField(label="start", validators=[DataRequired()])
end = DateField(label="end", validators=[DataRequired()])

+ 21
- 3
guineapigs/models.py View File

@@ -78,7 +78,10 @@ class FoodType(db.Model):
db.Boolean, nullable=False, default=True, info={"label": "show in statistics"}
)
is_hidden = db.Column(
db.Boolean, nullable=False, default=False, info={"label": "hide in food entry list"}
db.Boolean,
nullable=False,
default=False,
info={"label": "hide in food entry list"},
)


@@ -87,6 +90,23 @@ class Entry:
Entry base class that defines a user_id foreign key and timestamp
"""

utc_date = db.Column(db.DateTime, default=datetime.utcnow)

@classmethod
def get_in_time_range(cls, start=None, end=None):
if not (start or end):
return []

query = db.session.query(cls).order_by(cls.utc_date)

if start:
query = query.filter(cls.utc_date >= start)

if end:
query = query.filter(cls.utc_date < end)

return query.all()

@declared_attr
def user_id(cls):
return db.Column(db.Integer, db.ForeignKey("user.id"))
@@ -95,8 +115,6 @@ class Entry:
def user(cls):
return db.relationship("User")

utc_date = db.Column(db.DateTime, default=datetime.utcnow)


class FoodEntry(db.Model, Entry):
"""


+ 10
- 0
guineapigs/utils.py View File

@@ -20,15 +20,25 @@ def beginning_of_day_utc():
.replace(tzinfo=None)
)


def date_to_datetime(date):
return datetime.combine(date, time())


def beginning_of_week_utc():
"""
same as beginning_of_day_utc but subtracts 6 days
"""
return beginning_of_day_utc() - timedelta(days=6)


def next_day(date):
"""
adds one day to datetime object
"""
return date + timedelta(days=1)


def is_safe_url(target, host_url):
"""
checks target URL is safe to redirect to


+ 40
- 27
guineapigs/views.py View File

@@ -8,7 +8,13 @@ from pytz import utc
from guineapigs.app import app, db
from guineapigs.forms import *
from guineapigs.models import *
from guineapigs.utils import beginning_of_day_utc, beginning_of_week_utc, date_to_datetime, is_safe_url
from guineapigs.utils import (
beginning_of_day_utc,
beginning_of_week_utc,
date_to_datetime,
is_safe_url,
next_day,
)


@app.context_processor
@@ -86,7 +92,7 @@ def history():

start = end = None

if request.method == 'GET':
if request.method == "GET":
start = beginning_of_week
end = today
form.start.data = beginning_of_week.date()
@@ -97,28 +103,35 @@ def history():

entries = []
if start and end:
end = next_day(end)
food_entries = FoodEntry.get_in_time_range(start, end)
weight_entries = WeightEntry.get_in_time_range(start, end)
vitamin_c_entries = VitaminCEntry.get_in_time_range(start, end)

food_entries = (
db.session.query(FoodEntry)
.filter(FoodEntry.utc_date >= start)
.filter(FoodEntry.utc_date <= end)
.order_by(FoodEntry.utc_date)
(
f.utc_date.astimezone(app.config["TIMEZONE"]),
"🍽️",
f.food_type.label,
", ".join(gp.name for gp in f.guinea_pigs),
f.user.name,
)
for f in food_entries
)
weight_entries = (
db.session.query(WeightEntry)
.filter(WeightEntry.utc_date >= start)
.filter(WeightEntry.utc_date <= end)
.order_by(WeightEntry.utc_date)
(
w.utc_date.astimezone(app.config["TIMEZONE"]),
"⚖️",
w.value,
w.guinea_pig.name,
w.user.name,
)
for w in weight_entries
)
vitamin_c_entries = (
db.session.query(VitaminCEntry)
.filter(VitaminCEntry.utc_date >= start)
.filter(VitaminCEntry.utc_date <= end)
.order_by(VitaminCEntry.utc_date)
(v.utc_date.astimezone(app.config["TIMEZONE"]), "🌻", "", "", v.user.name,)
for v in vitamin_c_entries
)

food_entries = ((f.utc_date.astimezone(app.config["TIMEZONE"]), "🍽️", f.food_type.label, ", ".join(gp.name for gp in f.guinea_pigs), f.user.name, ) for f in food_entries)
weight_entries = ((w.utc_date.astimezone(app.config["TIMEZONE"]), "⚖️", w.value, w.guinea_pig.name, w.user.name, ) for w in weight_entries)
vitamin_c_entries = ((v.utc_date.astimezone(app.config["TIMEZONE"]), "🌻", "", "", v.user.name, ) for v in vitamin_c_entries)
entries = heapq.merge(food_entries, weight_entries, vitamin_c_entries)

return render_template("history.html", form=form, entries=entries)
@@ -321,17 +334,17 @@ def guinea_pig_form(id=None):


@app.route("/food_type/add", methods=["GET", "POST"])
@app.route("/food_type/edit/<int:id>", methods=["GET", "POST"])
@app.route("/food_type/edit/<int:id_>", methods=["GET", "POST"])
@login_required
def food_type_form(id=None):
def food_type_form(id_=None):
"""
insert/edit guinea pigs
"""
form = FoodTypeForm()
food_entry = None

if id:
food_entry = FoodType.query.filter(FoodType.id == id).first()
if id_:
food_entry = FoodType.query.filter(FoodType.id == id_).first()

if form.validate_on_submit():
food_entry = food_entry or FoodType()
@@ -353,11 +366,11 @@ def food_type_form(id=None):


NAV_PAGES_LOGGED_IN = (
("dashboard", "dashboard", ),
("history", "history", ),
("statistics", "statistics", ),
("settings", "settings", ),
("log out", "logout_view", ),
("dashboard", "dashboard",),
("history", "history",),
("statistics", "statistics",),
("settings", "settings",),
("log out", "logout_view",),
)

NAV_PAGES_LOGGED_OUT = (("log in", "login",),)

+ 11
- 11
migrations/env.py View File

@@ -15,17 +15,19 @@ config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)
logger = logging.getLogger('alembic.env')
logger = logging.getLogger("alembic.env")

# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
from flask import current_app

config.set_main_option(
'sqlalchemy.url',
str(current_app.extensions['migrate'].db.engine.url).replace('%', '%%'))
target_metadata = current_app.extensions['migrate'].db.metadata
"sqlalchemy.url",
str(current_app.extensions["migrate"].db.engine.url).replace("%", "%%"),
)
target_metadata = current_app.extensions["migrate"].db.metadata

# other values from the config, defined by the needs of env.py,
# can be acquired:
@@ -46,9 +48,7 @@ def run_migrations_offline():

"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url, target_metadata=target_metadata, literal_binds=True
)
context.configure(url=url, target_metadata=target_metadata, literal_binds=True)

with context.begin_transaction():
context.run_migrations()
@@ -66,15 +66,15 @@ def run_migrations_online():
# when there are no changes to the schema
# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
def process_revision_directives(context, revision, directives):
if getattr(config.cmd_opts, 'autogenerate', False):
if getattr(config.cmd_opts, "autogenerate", False):
script = directives[0]
if script.upgrade_ops.is_empty():
directives[:] = []
logger.info('No changes in schema detected.')
logger.info("No changes in schema detected.")

connectable = engine_from_config(
config.get_section(config.config_ini_section),
prefix='sqlalchemy.',
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)

@@ -83,7 +83,7 @@ def run_migrations_online():
connection=connection,
target_metadata=target_metadata,
process_revision_directives=process_revision_directives,
**current_app.extensions['migrate'].configure_args
**current_app.extensions["migrate"].configure_args
)

with context.begin_transaction():


+ 43
- 26
migrations/versions/1e175859da76_.py View File

@@ -10,41 +10,58 @@ import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '1e175859da76'
down_revision = 'b0fdf277700e'
revision = "1e175859da76"
down_revision = "b0fdf277700e"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('food_entry', sa.Column('user_id', sa.Integer(), nullable=True))
op.drop_constraint('food_entry_user_fkey', 'food_entry', type_='foreignkey')
op.create_foreign_key(None, 'food_entry', 'user', ['user_id'], ['id'])
op.drop_column('food_entry', 'user')
op.add_column('vitamin_c_entry', sa.Column('user_id', sa.Integer(), nullable=True))
op.drop_constraint('vitamin_c_entry_user_fkey', 'vitamin_c_entry', type_='foreignkey')
op.create_foreign_key(None, 'vitamin_c_entry', 'user', ['user_id'], ['id'])
op.drop_column('vitamin_c_entry', 'user')
op.add_column('weight_entry', sa.Column('user_id', sa.Integer(), nullable=True))
op.drop_constraint('weight_entry_user_fkey', 'weight_entry', type_='foreignkey')
op.create_foreign_key(None, 'weight_entry', 'user', ['user_id'], ['id'])
op.drop_column('weight_entry', 'user')
op.add_column("food_entry", sa.Column("user_id", sa.Integer(), nullable=True))
op.drop_constraint("food_entry_user_fkey", "food_entry", type_="foreignkey")
op.create_foreign_key(None, "food_entry", "user", ["user_id"], ["id"])
op.drop_column("food_entry", "user")
op.add_column("vitamin_c_entry", sa.Column("user_id", sa.Integer(), nullable=True))
op.drop_constraint(
"vitamin_c_entry_user_fkey", "vitamin_c_entry", type_="foreignkey"
)
op.create_foreign_key(None, "vitamin_c_entry", "user", ["user_id"], ["id"])
op.drop_column("vitamin_c_entry", "user")
op.add_column("weight_entry", sa.Column("user_id", sa.Integer(), nullable=True))
op.drop_constraint("weight_entry_user_fkey", "weight_entry", type_="foreignkey")
op.create_foreign_key(None, "weight_entry", "user", ["user_id"], ["id"])
op.drop_column("weight_entry", "user")
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('weight_entry', sa.Column('user', sa.INTEGER(), autoincrement=False, nullable=True))
op.drop_constraint(None, 'weight_entry', type_='foreignkey')
op.create_foreign_key('weight_entry_user_fkey', 'weight_entry', 'user', ['user'], ['id'])
op.drop_column('weight_entry', 'user_id')
op.add_column('vitamin_c_entry', sa.Column('user', sa.INTEGER(), autoincrement=False, nullable=True))
op.drop_constraint(None, 'vitamin_c_entry', type_='foreignkey')
op.create_foreign_key('vitamin_c_entry_user_fkey', 'vitamin_c_entry', 'user', ['user'], ['id'])
op.drop_column('vitamin_c_entry', 'user_id')
op.add_column('food_entry', sa.Column('user', sa.INTEGER(), autoincrement=False, nullable=True))
op.drop_constraint(None, 'food_entry', type_='foreignkey')
op.create_foreign_key('food_entry_user_fkey', 'food_entry', 'user', ['user'], ['id'])
op.drop_column('food_entry', 'user_id')
op.add_column(
"weight_entry",
sa.Column("user", sa.INTEGER(), autoincrement=False, nullable=True),
)
op.drop_constraint(None, "weight_entry", type_="foreignkey")
op.create_foreign_key(
"weight_entry_user_fkey", "weight_entry", "user", ["user"], ["id"]
)
op.drop_column("weight_entry", "user_id")
op.add_column(
"vitamin_c_entry",
sa.Column("user", sa.INTEGER(), autoincrement=False, nullable=True),
)
op.drop_constraint(None, "vitamin_c_entry", type_="foreignkey")
op.create_foreign_key(
"vitamin_c_entry_user_fkey", "vitamin_c_entry", "user", ["user"], ["id"]
)
op.drop_column("vitamin_c_entry", "user_id")
op.add_column(
"food_entry",
sa.Column("user", sa.INTEGER(), autoincrement=False, nullable=True),
)
op.drop_constraint(None, "food_entry", type_="foreignkey")
op.create_foreign_key(
"food_entry_user_fkey", "food_entry", "user", ["user"], ["id"]
)
op.drop_column("food_entry", "user_id")
# ### end Alembic commands ###

+ 8
- 8
migrations/versions/4d3db79640c9_.py View File

@@ -10,25 +10,25 @@ import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '4d3db79640c9'
down_revision = 'e1bd94980514'
revision = "4d3db79640c9"
down_revision = "e1bd94980514"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('food_type', sa.Column('is_hidden', sa.Boolean(), nullable=True))
op.add_column('food_type', sa.Column('in_statistics', sa.Boolean(), nullable=True))
op.add_column("food_type", sa.Column("is_hidden", sa.Boolean(), nullable=True))
op.add_column("food_type", sa.Column("in_statistics", sa.Boolean(), nullable=True))
# ### end Alembic commands ###
op.execute("UPDATE food_type SET is_hidden = false")
op.execute("UPDATE food_type SET in_statistics = true")
op.alter_column('food_type', 'is_hidden', nullable=False)
op.alter_column('food_type', 'in_statistics', nullable=False)
op.alter_column("food_type", "is_hidden", nullable=False)
op.alter_column("food_type", "in_statistics", nullable=False)


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('food_type', 'in_statistics')
op.drop_column('food_type', 'hidden')
op.drop_column("food_type", "in_statistics")
op.drop_column("food_type", "hidden")
# ### end Alembic commands ###

+ 61
- 54
migrations/versions/b0fdf277700e_.py View File

@@ -10,7 +10,7 @@ import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = 'b0fdf277700e'
revision = "b0fdf277700e"
down_revision = None
branch_labels = None
depends_on = None
@@ -18,69 +18,76 @@ depends_on = None

def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('food_type',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('label', sa.String(length=64), nullable=False),
sa.Column('recommendations', sa.String(length=512), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('label')
op.create_table(
"food_type",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("label", sa.String(length=64), nullable=False),
sa.Column("recommendations", sa.String(length=512), nullable=True),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("label"),
)
op.create_table('guinea_pig',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=64), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name')
op.create_table(
"guinea_pig",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sa.String(length=64), nullable=False),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("name"),
)
op.create_table('user',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=64), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name')
op.create_table(
"user",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sa.String(length=64), nullable=False),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("name"),
)
op.create_table('food_entry',
sa.Column('utc_date', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('food_type_id', sa.Integer(), nullable=False),
sa.Column('notes', sa.String(length=512), nullable=True),
sa.Column('user', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['food_type_id'], ['food_type.id'], ),
sa.ForeignKeyConstraint(['user'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
op.create_table(
"food_entry",
sa.Column("utc_date", sa.DateTime(), nullable=True),
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("food_type_id", sa.Integer(), nullable=False),
sa.Column("notes", sa.String(length=512), nullable=True),
sa.Column("user", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(["food_type_id"], ["food_type.id"],),
sa.ForeignKeyConstraint(["user"], ["user.id"],),
sa.PrimaryKeyConstraint("id"),
)
op.create_table('vitamin_c_entry',
sa.Column('utc_date', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['user'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
op.create_table(
"vitamin_c_entry",
sa.Column("utc_date", sa.DateTime(), nullable=True),
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("user", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(["user"], ["user.id"],),
sa.PrimaryKeyConstraint("id"),
)
op.create_table('weight_entry',
sa.Column('utc_date', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('value', sa.Float(), nullable=False),
sa.Column('guinea_pig_id', sa.Integer(), nullable=False),
sa.Column('user', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['guinea_pig_id'], ['guinea_pig.id'], ),
sa.ForeignKeyConstraint(['user'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
op.create_table(
"weight_entry",
sa.Column("utc_date", sa.DateTime(), nullable=True),
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("value", sa.Float(), nullable=False),
sa.Column("guinea_pig_id", sa.Integer(), nullable=False),
sa.Column("user", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(["guinea_pig_id"], ["guinea_pig.id"],),
sa.ForeignKeyConstraint(["user"], ["user.id"],),
sa.PrimaryKeyConstraint("id"),
)
op.create_table('food_entries',
sa.Column('food_entry_id', sa.Integer(), nullable=False),
sa.Column('guinea_pig_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['food_entry_id'], ['food_entry.id'], ),
sa.ForeignKeyConstraint(['guinea_pig_id'], ['guinea_pig.id'], ),
sa.PrimaryKeyConstraint('food_entry_id', 'guinea_pig_id')
op.create_table(
"food_entries",
sa.Column("food_entry_id", sa.Integer(), nullable=False),
sa.Column("guinea_pig_id", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(["food_entry_id"], ["food_entry.id"],),
sa.ForeignKeyConstraint(["guinea_pig_id"], ["guinea_pig.id"],),
sa.PrimaryKeyConstraint("food_entry_id", "guinea_pig_id"),
)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('food_entries')
op.drop_table('weight_entry')
op.drop_table('vitamin_c_entry')
op.drop_table('food_entry')
op.drop_table('user')
op.drop_table('guinea_pig')
op.drop_table('food_type')
op.drop_table("food_entries")
op.drop_table("weight_entry")
op.drop_table("vitamin_c_entry")
op.drop_table("food_entry")
op.drop_table("user")
op.drop_table("guinea_pig")
op.drop_table("food_type")
# ### end Alembic commands ###

+ 6
- 6
migrations/versions/e1bd94980514_.py View File

@@ -10,21 +10,21 @@ import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = 'e1bd94980514'
down_revision = '1e175859da76'
revision = "e1bd94980514"
down_revision = "1e175859da76"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint('food_type_label_key', 'food_type', type_='unique')
op.drop_constraint('guinea_pig_name_key', 'guinea_pig', type_='unique')
op.drop_constraint("food_type_label_key", "food_type", type_="unique")
op.drop_constraint("guinea_pig_name_key", "guinea_pig", type_="unique")
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_unique_constraint('guinea_pig_name_key', 'guinea_pig', ['name'])
op.create_unique_constraint('food_type_label_key', 'food_type', ['label'])
op.create_unique_constraint("guinea_pig_name_key", "guinea_pig", ["name"])
op.create_unique_constraint("food_type_label_key", "food_type", ["label"])
# ### end Alembic commands ###

Loading…
Cancel
Save