Skip to content

Commit

Permalink
feat: add button to change lunch time or takeaway option
Browse files Browse the repository at this point in the history
add change_order_time_takeaway_button to GraphicInterface and add it to buttons_flexbox
add change_order_time_takeaway_button to reload_on_no_more_order_callback to change its enabled state
change toggle_no_more_order_button icon
add change_order_time_takeaway to core.py
add try-except statement to delete_order to handle database errors (as done in send_order)
update reload_menu to handle change_order_time_takeaway_button visibility
move lunch_time column from 'orders' table to 'users' table and add back_populates to both tables
change queries for postgresql and sqlite to join lunch time to orders_query result
remove confirm_message from GraphicInterface
remove unused parameters from functions in core.py and related callbacks
  • Loading branch information
Michele-Alberti committed Apr 14, 2024
1 parent bf8d4f3 commit 48a8588
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 42 deletions.
1 change: 0 additions & 1 deletion dlunch/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ def create_app(config: DictConfig) -> pn.Template:
app.main.append(gi.results_divider)
app.main.append(gi.res_col)
app.modal.append(gi.error_message)
app.modal.append(gi.confirm_message)

# Set components visibility based on no_more_order_button state
# and reload menu
Expand Down
6 changes: 4 additions & 2 deletions dlunch/conf/db/postgresql.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ url: ${db.dialect}+${db.driver}://${db.username}:${db.password}@${db.host}:${db.
# QUERIES
# Orders
orders_query: |-
SELECT o.user, o.lunch_time, m.item, o.note
SELECT o.user, u.lunch_time, m.item, o.note
FROM {schema}.orders o
LEFT JOIN {schema}.menu m
ON m.id = o.menu_item_id;
ON m.id = o.menu_item_id
LEFT JOIN {schema}.users u
ON u.id = o.user;
# Stats
stats_query: |-
SELECT EXTRACT(YEAR FROM date)::varchar(4) AS "Year",
Expand Down
6 changes: 4 additions & 2 deletions dlunch/conf/db/sqlite.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ url: ${db.dialect}:///${db.db_path}
# QUERIES
# Orders
orders_query: |-
SELECT o.user, o.lunch_time, m.item, o.note
SELECT o.user, u.lunch_time, m.item, o.note
FROM orders o
LEFT JOIN menu m
ON m.id = o.menu_item_id;
ON m.id = o.menu_item_id
LEFT JOIN users u
ON u.id = o.user;
# Stats
stats_query: |-
SELECT STRFTIME('%Y', date) AS "Year",
Expand Down
147 changes: 120 additions & 27 deletions dlunch/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from pytesseract import pytesseract
import random
import re
from sqlalchemy import func, select, delete
from sqlalchemy import func, select, delete, update
from sqlalchemy.sql.expression import true as sql_true
from time import sleep

Expand Down Expand Up @@ -166,7 +166,6 @@ def build_menu(
) -> pd.DataFrame:
# Hide messages
gi.error_message.visible = False
gi.confirm_message.visible = False

# Build image path
menu_filename = str(
Expand Down Expand Up @@ -316,17 +315,24 @@ def reload_menu(
value_if_missing=False,
)

# Set no more orders toggle button visibility and activation
# Set no more orders toggle button and the change order time button
# visibility and activation
if auth.is_guest(
user=pn_user(config), config=config, allow_override=False
):
# Deactivate the no_more_orders button for guest users
# Deactivate the no_more_orders_button for guest users
gi.toggle_no_more_order_button.disabled = True
gi.toggle_no_more_order_button.visible = False
# Deactivate the change_order_time_button for guest users
gi.change_order_time_takeaway_button.disabled = True
gi.change_order_time_takeaway_button.visible = False
else:
# Activate the no_more_orders button for privileged users
# Activate the no_more_orders_button for privileged users
gi.toggle_no_more_order_button.disabled = False
gi.toggle_no_more_order_button.visible = True
# Show the change_order_time_button for privileged users
# It is disabled by the no more order button if necessary
gi.change_order_time_takeaway_button.visible = True

# Guest graphic configuration
if auth.is_guest(user=pn_user(config), config=config):
Expand Down Expand Up @@ -586,7 +592,6 @@ def send_order(

# Hide messages
gi.error_message.visible = False
gi.confirm_message.visible = False

# Create session
session = models.create_session(config)
Expand Down Expand Up @@ -677,11 +682,13 @@ def send_order(
new_user = models.Users(
id=username_key_press,
guest=person.guest,
lunch_time=person.lunch_time,
takeaway=person.takeaway,
)
else:
new_user = models.Users(
id=username_key_press,
lunch_time=person.lunch_time,
takeaway=person.takeaway,
)
session.add(new_user)
Expand All @@ -691,7 +698,6 @@ def send_order(
# Order
new_order = models.Orders(
user=username_key_press,
lunch_time=person.lunch_time,
menu_item_id=row.Index,
note=getattr(
row, config.panel.gui.note_column_name
Expand Down Expand Up @@ -722,7 +728,7 @@ def send_order(
f"DATABASE ERROR<br><br>ERROR:<br>{str(e)}"
)
gi.error_message.visible = True
log.warning("database error")
log.error("database error")
# Open modal window
app.open_modal()
else:
Expand All @@ -744,15 +750,13 @@ def delete_order(
event,
config: DictConfig,
app: pn.Template,
person: gui.Person,
gi: gui.GraphicInterface,
) -> None:
# Get username, updated on every keypress
username_key_press = gi.person_widget._widgets["username"].value_input

# Hide messages
gi.error_message.visible = False
gi.confirm_message.visible = False

# Create session
session = models.create_session(config)
Expand Down Expand Up @@ -797,21 +801,114 @@ def delete_order(
return

# Delete user
num_rows_deleted_users = session.execute(
delete(models.Users).where(
models.Users.id == username_key_press
try:
num_rows_deleted_users = session.execute(
delete(models.Users).where(
models.Users.id == username_key_press
)
)
)
# Delete also orders (hotfix for Debian)
num_rows_deleted_orders = session.execute(
delete(models.Orders).where(
models.Orders.user == username_key_press
# Delete also orders (hotfix for Debian)
num_rows_deleted_orders = session.execute(
delete(models.Orders).where(
models.Orders.user == username_key_press
)
)
session.commit()
if (num_rows_deleted_users.rowcount > 0) or (
num_rows_deleted_orders.rowcount > 0
):
# Update dataframe widget
reload_menu(
None,
config,
gi,
)

pn.state.notifications.success(
"Order canceled",
duration=config.panel.notifications.duration,
)
log.info(f"{username_key_press}'s order canceled")
else:
pn.state.notifications.warning(
f'No order for user named<br>"{username_key_press}"',
duration=config.panel.notifications.duration,
)
log.info(f"no order for user named {username_key_press}")
except Exception as e:
# Any exception here is a database fault
pn.state.notifications.error(
"Database error",
duration=config.panel.notifications.duration,
)
gi.error_message.object = (
f"DATABASE ERROR<br><br>ERROR:<br>{str(e)}"
)
gi.error_message.visible = True
log.error("database error")
# Open modal window
app.open_modal()
else:
pn.state.notifications.warning(
"Please insert user name",
duration=config.panel.notifications.duration,
)
log.warning("missing username")


def change_order_time_takeaway(
event,
config: DictConfig,
person: gui.Person,
gi: gui.GraphicInterface,
) -> None:
# Get username, updated on every keypress
username_key_press = gi.person_widget._widgets["username"].value_input

# Create session
session = models.create_session(config)

with session:
# Check if the "no more order" toggle button is pressed
if models.get_flag(config=config, id="no_more_orders"):
pn.state.notifications.error(
"It is not possible to update orders (time)",
duration=config.panel.notifications.duration,
)

# Reload the menu
reload_menu(
None,
config,
gi,
)

return

if username_key_press:
# Build and execute the update statement
update_statement = (
update(models.Users)
.where(models.Users.id == username_key_press)
.values(lunch_time=person.lunch_time, takeaway=person.takeaway)
.returning(models.Users)
)

updated_user = session.scalars(update_statement).one_or_none()

session.commit()
if (num_rows_deleted_users.rowcount > 0) or (
num_rows_deleted_orders.rowcount > 0
):

if updated_user:
# Find updated values
updated_time = updated_user.lunch_time
updated_takeaway = (
(" " + config.panel.gui.takeaway_id)
if updated_user.takeaway
else ""
)
updated_items_names = [
order.menu_item.item for order in updated_user.orders
]
# Update dataframe widget
reload_menu(
None,
Expand All @@ -820,10 +917,10 @@ def delete_order(
)

pn.state.notifications.success(
"Order canceled",
f"{username_key_press}'s<br>lunch time changed to<br>{updated_time}{updated_takeaway}<br>({', '.join(updated_items_names)})",
duration=config.panel.notifications.duration,
)
log.info(f"{username_key_press}'s order canceled")
log.info(f"{username_key_press}'s order updated")
else:
pn.state.notifications.warning(
f'No order for user named<br>"{username_key_press}"',
Expand Down Expand Up @@ -952,12 +1049,8 @@ def clean_up_table(

def download_dataframe(
config: DictConfig,
app: pn.Template,
gi: gui.GraphicInterface,
) -> None:
# Hide messages
gi.error_message.visible = False
gi.confirm_message.visible = False

# Build a dict of dataframes, one for each lunch time (the key contains
# a lunch time)
Expand Down
32 changes: 23 additions & 9 deletions dlunch/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,17 @@ def reload_on_guest_override_callback(
button_style="outline",
button_type="warning",
height=generic_button_height,
icon="alarm",
icon="hand-stop",
icon_size="2em",
sizing_mode="stretch_width",
)
# Create change time
self.change_order_time_takeaway_button = pnw.Button(
name="Change Time/Takeaway",
button_type="primary",
button_style="outline",
height=generic_button_height,
icon="clock-edit",
icon_size="2em",
sizing_mode="stretch_width",
)
Expand Down Expand Up @@ -432,6 +442,7 @@ def reload_on_guest_override_callback(
*[
self.send_order_button,
self.toggle_no_more_order_button,
self.change_order_time_takeaway_button,
self.delete_order_button,
],
flex_wrap="nowrap",
Expand All @@ -454,9 +465,10 @@ def reload_on_no_more_order_callback(
# Show "no more order" text
self.no_more_order_alert.visible = toggle

# Deactivate send order and delete order buttons
# Deactivate send, delete and change order buttons
self.send_order_button.disabled = toggle
self.delete_order_button.disabled = toggle
self.change_order_time_takeaway_button.disabled = toggle

# Simply reload the menu when the toggle button value changes
if reload:
Expand Down Expand Up @@ -493,6 +505,14 @@ def reload_on_no_more_order_callback(
e,
config,
app,
self,
)
)
# Change order time button callback
self.change_order_time_takeaway_button.on_click(
lambda e: core.change_order_time_takeaway(
e,
config,
person,
self,
)
Expand All @@ -505,12 +525,6 @@ def reload_on_no_more_order_callback(
sizing_mode="stretch_width",
)
self.error_message.visible = False
# Confirm message
self.confirm_message = pn.pane.HTML(
styles={"color": "green", "font-weight": "bold"},
sizing_mode="stretch_width",
)
self.confirm_message.visible = False

# SIDEBAR -------------------------------------------------------------
# TEXTS
Expand Down Expand Up @@ -610,7 +624,7 @@ def reload_on_no_more_order_callback(
)
# Download button and callback
self.download_button = pn.widgets.FileDownload(
callback=lambda: core.download_dataframe(config, app, self),
callback=lambda: core.download_dataframe(config, self),
filename=config.panel.file_name + ".xlsx",
sizing_mode="stretch_width",
icon="download",
Expand Down
9 changes: 8 additions & 1 deletion dlunch/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ class Orders(Data):
index=True,
nullable=False,
)
lunch_time = Column(String(7), index=True, nullable=False)
user_record = relationship("Users", back_populates="orders", uselist=False)
menu_item_id = Column(
Integer,
ForeignKey("menu.id", ondelete="CASCADE"),
Expand Down Expand Up @@ -265,9 +265,16 @@ class Users(Data):
default="NotAGuest",
server_default="NotAGuest",
)
lunch_time = Column(String(7), index=True, nullable=False)
takeaway = Column(
Boolean, nullable=False, default=False, server_default=sql_false()
)
orders = relationship(
"Orders",
back_populates="user_record",
cascade="all, delete-orphan",
passive_deletes=True,
)

@classmethod
def clear(self, config: DictConfig) -> int:
Expand Down

0 comments on commit 48a8588

Please sign in to comment.