Skip to content

Commit

Permalink
Merge pull request #23 from EnhancedJax/dev
Browse files Browse the repository at this point in the history
0.2.0
  • Loading branch information
EnhancedJax authored Jan 4, 2025
2 parents 5afec04 + 08b3af3 commit 6cdc1ad
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 111 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 0.2.0

- Added formula inputs in amount fields!

## 0.1.16

- Updated bar visuals for non-rounded variation
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "Bagels"
version = "0.1.16"
version = "0.2.0"
authors = [
{ name = "Jax", email = "[email protected]" }
]
Expand Down
10 changes: 8 additions & 2 deletions src/bagels/components/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@

from bagels.components.autocomplete import AutoComplete, Dropdown, DropdownItem
from bagels.forms.form import Form, FormField
from bagels.utils.format import parse_formula_expression

_RESTRICT_TYPES = {
"any": None,
"integer": r"[-+]?(?:\d*|\d+_)*",
"number": r"(?:\d*|\d+_)*\.?(?:\d*|\d+_)*", # disallow scientific notation and negative values by default.
"integer": r"^-?\d+$",
"number": r"^-?\d*\.?\d*(?:[+\-*\/]-?\d*\.?\d+)*[+\-*\/\.]?$",
}


Expand Down Expand Up @@ -61,6 +62,11 @@ def on_auto_complete_selected(self, event: AutoComplete.Selected) -> None:
self.input.heldValue = item.value
break

def on_input_changed(self, event: Input.Changed):
if self.field.type == "number":
num_val = parse_formula_expression(event.value)
self.query_one(".label").update(f"{self.field.title} - {num_val}")

def compose(self) -> ComposeResult:
if self.field.type == "hidden":
# Hidden fields just need a static widget to hold the value
Expand Down
47 changes: 0 additions & 47 deletions src/bagels/modals/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,7 @@ def __init__(
self.splitCount = int(len(splitForm) / self.splitFormOneLength)
self.persons = get_all_persons()
self.accounts = get_all_accounts_with_balance()
self.total_amount = 0
self.date = date
self.split_total = Label("", id="split-total")

def on_mount(self):
self._update_split_total()
if self.splitCount > 0:
self._update_split_total_visibility(True)

# -------------- Helpers ------------- #

Expand All @@ -91,25 +84,6 @@ def _get_splits_from_result(self, resultForm: dict):
)
return splits

def _update_split_total(self, update_new: bool = True):
my_amount = self.query_one("#field-amount").value
try:
total = float(my_amount) if my_amount else 0
except ValueError:
total = 0
if update_new:
for i in range(0, self.splitCount):
amount = self.query_one(f"#field-amount-{i}").value
try:
total += float(amount) if amount else 0
except ValueError:
total += 0
self.total_amount = total
if self.splitCount > 0:
self.split_total.update(
f"Total amount: [bold yellow]{total:.2f}[/bold yellow]"
)

def _get_split_widget(self, index: int, fields: Form, isPaid: bool):
widget = Container(Fields(fields), id=f"split-{index}", classes="split")
widget.border_title = "> Paid split <" if isPaid else "> Split <"
Expand All @@ -132,12 +106,6 @@ def _get_init_split_widgets(self):
widgets.append(self._get_split_widget(i, oneSplitForm, isPaid))
return widgets

def _update_split_total_visibility(self, mount: bool):
if mount:
self.query_one(".container").mount(self.split_total)
else:
self.split_total.remove()

def _update_errors(self, errors: dict):
previousErrors = self.query(".error")
for error in previousErrors:
Expand All @@ -163,10 +131,6 @@ def on_auto_complete_created(self, event: AutoComplete.Created) -> None:

# ------------- Callbacks ------------ #

def on_input_changed(self, event: Input.Changed):
if event.input.id.startswith("field-amount"):
self._update_split_total()

def action_add_paid_split(self):
self.action_add_split(paid=True)

Expand All @@ -186,9 +150,6 @@ def action_add_split(self, paid: bool = False):
lambda: self.query_one(f"#field-personId-{current_split_index}").focus()
)
self.splitCount += 1
if self.splitCount == 1:
self._update_split_total_visibility(True)
self._update_split_total(update_new=False)

def action_delete_last_split(self):
if self.splitCount > 0:
Expand All @@ -204,23 +165,15 @@ def action_delete_last_split(self):
for i in range(self.splitFormOneLength):
self.splitForm.fields.pop()
self.splitCount -= 1
if self.splitCount == 0:
self._update_split_total_visibility(False)

def action_submit(self):
# We set the amount field to the total amount for the form to read the value
input: Input = self.query_one("#field-amount")
input.__setattr__("heldValue", str(self.total_amount))

resultRecordForm, errors, isValid = validateForm(self, self.form)
resultSplitForm, errorsSplit, isValidSplit = validateForm(self, self.splitForm)
if isValid and isValidSplit:
resultSplits = self._get_splits_from_result(resultSplitForm)
self.dismiss({"record": resultRecordForm, "splits": resultSplits})
return
self._update_errors({**errors, **errorsSplit})
# Remove the custom value we set for the field if not valid
input.__setattr__("heldValue", None)

# -------------- Compose ------------- #

Expand Down
2 changes: 1 addition & 1 deletion src/bagels/textualrun.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from bagels.locations import set_custom_root

if __name__ == "__main__":
set_custom_root(Path("../../../bagels_instance/"))
# set_custom_root(Path("../../../bagels_instance/"))

from bagels.config import load_config

Expand Down
13 changes: 13 additions & 0 deletions src/bagels/utils/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@
from bagels.config import CONFIG


def parse_formula_expression(value: str) -> float:
# parse value formula expression. Assumes valid expression
# fix possible -+123.123 to -123.123
try:
value = value.replace("+-", "-")
# remove ending operators
if value[-1] in "+-*/.":
value = value[:-1]
return round(float(eval(value)), CONFIG.defaults.round_decimals)
except Exception:
return 0


def format_date_to_readable(date) -> str:
today = datetime.now().date()
date = date.date() if isinstance(date, datetime) else date
Expand Down
45 changes: 19 additions & 26 deletions src/bagels/utils/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from textual.widget import Widget

from bagels.forms.form import Form, FormField
from bagels.utils.format import parse_formula_expression


def _validate_number(
Expand All @@ -12,40 +13,30 @@ def _validate_number(
"""Validate a number field and return (is_valid, error_message)"""
if not value:
if field.is_required:
return False, "Required"
return True, None

# Check if valid number
if is_float:
# Allow negative sign at start
test_value = value.lstrip("-").replace(".", "", 1)
is_valid = test_value.isdigit()
type_name = "number"
else:
# Allow negative sign at start
test_value = value.lstrip("-")
is_valid = test_value.isdigit()
type_name = "integer"
return False, "Required", None
return True, None, value

if not is_valid:
return False, f"Must be a {type_name}"
# Skip checking if numbers are valid as they are restricted

# Convert to number for comparisons
num_val = float(value) if is_float else int(value)
if not is_float:
num_val = int(value)
else:
num_val = parse_formula_expression(value)

# Check minimum
if field.min is not None:
min_val = float(field.min) if is_float else int(field.min)
if num_val <= min_val:
return False, f"Must be greater than {field.min}"
return False, f"Must be greater than {field.min}", None

# Check maximum
if field.max is not None:
max_val = float(field.max) if is_float else int(field.max)
if num_val > max_val:
return False, f"Must be less than {field.max}"
return False, f"Must be less than {field.max}", None

return True, None
return True, None, num_val


def _validate_date(
Expand Down Expand Up @@ -123,14 +114,16 @@ def validateForm(

match field.type:
case "integer":
is_valid, error = _validate_number(fieldValue, field)
if is_valid and fieldValue:
result[fieldKey] = int(fieldValue)
is_valid, error, num_val = _validate_number(fieldValue, field)
if is_valid and fieldValue and num_val:
result[fieldKey] = num_val

case "number":
is_valid, error = _validate_number(fieldValue, field, is_float=True)
if is_valid and fieldValue:
result[fieldKey] = float(fieldValue)
is_valid, error, num_val = _validate_number(
fieldValue, field, is_float=True
)
if is_valid and fieldValue and num_val:
result[fieldKey] = num_val

case "date":
date, error = _validate_date(fieldValue, field)
Expand Down
Loading

0 comments on commit 6cdc1ad

Please sign in to comment.