Skip to content

Commit 2209faa

Browse files
authored
Merge pull request #197 from tuttle-dev/dev-db-refactor
Merge dev-db-refactor into dev
2 parents c762dc1 + 6ebc504 commit 2209faa

File tree

6 files changed

+177
-143
lines changed

6 files changed

+177
-143
lines changed

app/app.py

Lines changed: 18 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,21 @@
11
from typing import Callable, Optional
22

3-
import re
4-
from pathlib import Path
5-
63
from flet import (
74
AlertDialog,
85
FilePicker,
96
FilePickerUploadFile,
107
Page,
118
SnackBar,
129
TemplateRoute,
13-
Text,
1410
View,
1511
app,
1612
)
1713

18-
import demo
19-
import sqlmodel
2014
from auth.view import ProfileScreen, SplashScreen
2115
from contracts.view import ContractEditorScreen, ViewContractScreen
2216
from core.abstractions import TuttleView, TuttleViewParams
2317
from core.client_storage_impl import ClientStorageImpl
18+
from core.database_storage_impl import DatabaseStorageImpl
2419
from core.models import RouteView
2520
from core.utils import AlertDialogControls
2621
from core.views import get_heading
@@ -64,9 +59,11 @@ def __init__(
6459
self.page.fonts = APP_FONTS
6560
self.page.theme = APP_THEME
6661
self.client_storage = ClientStorageImpl(page=self.page)
67-
preferences = PreferencesIntent(
68-
client_storage=self.client_storage,
62+
self.db = DatabaseStorageImpl(
63+
store_demo_timetracking_dataframe=self.store_demo_timetracking_dataframe,
64+
debug_mode=self.debug_mode,
6965
)
66+
preferences = PreferencesIntent(self.client_storage)
7067
preferences_result = preferences.get_preference_by_key(
7168
PreferencesStorageKeys.theme_mode_key
7269
)
@@ -92,10 +89,6 @@ def __init__(
9289
self.current_route_view: Optional[RouteView] = None
9390
self.page.on_resize = self.page_resize
9491

95-
# database config
96-
self.app_dir = self.ensure_app_dir()
97-
self.db_path = self.app_dir / "tuttle.db"
98-
9992
def page_resize(self, e):
10093
if self.current_route_view:
10194
self.current_route_view.on_window_resized(
@@ -193,7 +186,6 @@ def control_alert_dialog(
193186
def change_route(self, to_route: str, data: Optional[any] = None):
194187
"""navigates to a new route"""
195188
newRoute = to_route if data is None else f"{to_route}/{data}"
196-
197189
self.page.go(newRoute)
198190

199191
def on_view_pop(self, view: Optional[View] = None):
@@ -247,89 +239,35 @@ def on_route_change(self, route):
247239
self.page.window_width, self.page.window_height
248240
)
249241

250-
def create_model(self):
251-
logger.info("Creating database model")
252-
sqlmodel.SQLModel.metadata.create_all(
253-
self.db_engine,
254-
checkfirst=True,
255-
)
256-
257-
def ensure_database(self):
258-
"""
259-
Ensure that the database exists and is up to date.
260-
"""
261-
if not self.db_path.exists():
262-
self.reset_database()
263-
else:
264-
logger.info("Database exists, skipping creation")
265-
266-
def reset_database(self):
267-
"""
268-
Delete the database and rebuild database model.
269-
"""
270-
logger.info("Clearing database")
271-
try:
272-
self.db_path.unlink()
273-
except FileNotFoundError:
274-
logger.info("Database file not found, skipping delete")
275-
self.db_engine = sqlmodel.create_engine(
276-
f"sqlite:///{self.db_path}",
277-
echo=self.debug_mode,
278-
)
279-
self.create_model()
280-
281242
def store_demo_timetracking_dataframe(self, time_tracking_data: DataFrame):
282243
"""Caches the time tracking dataframe created from a demo installation"""
283244
self.timetracking_intent = TimeTrackingIntent(
284245
client_storage=self.client_storage
285246
)
286247
self.timetracking_intent.set_timetracking_data(data=time_tracking_data)
287248

288-
def install_demo_data(self):
289-
"""Install demo data into the database."""
290-
self.reset_database()
291-
try:
292-
demo.install_demo_data(
293-
n_projects=4,
294-
db_path=self.db_path,
295-
on_cache_timetracking_dataframe=self.store_demo_timetracking_dataframe,
296-
)
297-
except Exception as ex:
298-
logger.exception(ex)
299-
logger.error("Failed to install demo data")
300-
301-
def ensure_app_dir(self) -> Path:
302-
"""Ensures that the user directory exists"""
303-
app_dir = Path.home() / ".tuttle"
304-
if not app_dir.exists():
305-
app_dir.mkdir(parents=True)
306-
return app_dir
307-
308-
def ensure_uploads_dir(self) -> Path:
309-
uploads_dir = self.app_dir / "uploads"
310-
if not uploads_dir.exists():
311-
uploads_dir.mkdir(parents=True)
312-
return uploads_dir
249+
def build(self):
250+
self.page.go(self.page.route)
313251

314252
def close(self):
315253
"""Closes the application."""
316254
self.page.window_close()
317255

318-
def build(self):
319-
self.page.go(self.page.route)
320-
321256
def reset_and_quit(self):
322257
"""Resets the application and quits."""
323-
self.reset_database()
258+
self.db.reset_database()
324259
self.close()
325260

326261

327262
class TuttleRoutes:
328263
"""Utility class for parsing of routes to destination views"""
329264

330265
def __init__(self, app: TuttleApp):
331-
self.app = app
266+
# init callbacks for some views
332267
self.on_theme_changed = app.on_theme_mode_changed
268+
self.on_reset_and_quit = app.reset_and_quit
269+
self.on_install_demo_data = app.db.install_demo_data
270+
# init common params for views
333271
self.tuttle_view_params = TuttleViewParams(
334272
navigate_to_route=app.change_route,
335273
show_snack=app.show_snack,
@@ -370,14 +308,16 @@ def parse_route(self, pageRoute: str):
370308
if routePath.match(SPLASH_SCREEN_ROUTE):
371309
screen = SplashScreen(
372310
params=self.tuttle_view_params,
373-
install_demo_data_callback=self.app.install_demo_data,
311+
on_install_demo_data=self.on_install_demo_data,
374312
)
375313
elif routePath.match(HOME_SCREEN_ROUTE):
376314
screen = HomeScreen(
377315
params=self.tuttle_view_params,
378316
)
379317
elif routePath.match(PROFILE_SCREEN_ROUTE):
380-
screen = ProfileScreen(params=self.tuttle_view_params)
318+
screen = ProfileScreen(
319+
params=self.tuttle_view_params,
320+
)
381321
elif routePath.match(CONTRACT_EDITOR_SCREEN_ROUTE):
382322
screen = ContractEditorScreen(params=self.tuttle_view_params)
383323
elif routePath.match(f"{CONTRACT_DETAILS_SCREEN_ROUTE}/:contractId"):
@@ -395,7 +335,7 @@ def parse_route(self, pageRoute: str):
395335
screen = PreferencesScreen(
396336
params=self.tuttle_view_params,
397337
on_theme_changed_callback=self.on_theme_changed,
398-
on_reset_app_callback=self.app.reset_and_quit,
338+
on_reset_app_callback=self.on_reset_and_quit,
399339
)
400340
elif routePath.match(PROJECT_EDITOR_SCREEN_ROUTE):
401341
screen = ProjectEditorScreen(params=self.tuttle_view_params)
@@ -431,7 +371,7 @@ def main(page: Page):
431371
app = TuttleApp(page)
432372

433373
# if database does not exist, create it
434-
app.ensure_database()
374+
app.db.ensure_database()
435375

436376
app.build()
437377

app/auth/view.py

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,20 @@ class PaymentDataForm(UserControl):
3232
def __init__(
3333
self,
3434
on_form_submit: Callable[[User], None],
35-
user: User,
3635
):
3736
super().__init__()
3837
self.on_form_submit = on_form_submit
39-
self.user = user
40-
self.set_form_data()
38+
self.user: User = None
4139

4240
def set_form_data(self):
4341
"""Sets the form data to the user's current data"""
4442
if not self.user.bank_account:
4543
# Create a new bank account if none exists
4644
self.user.bank_account = BankAccount(name="", BIC="", IBAN="")
47-
self.bank_bic = self.user.bank_account.BIC
48-
self.bank_name = self.user.bank_account.name
49-
self.bank_iban = self.user.bank_account.IBAN
50-
self.vat_number = self.user.VAT_number
45+
self.bank_ibc_field.value = self.bank_bic = self.user.bank_account.BIC
46+
self.bank_name_field.value = self.bank_name = self.user.bank_account.name
47+
self.bank_iban_field.value = self.bank_iban = self.user.bank_account.IBAN
48+
self.vat_number_field.value = self.vat_number = self.user.VAT_number
5149

5250
def update_form_data(self, user: User):
5351
"""Updates the user's data with the form data"""
@@ -77,32 +75,35 @@ def on_bank_name_changed(self, e):
7775

7876
def build(self):
7977
"""Called when form is built"""
78+
self.vat_number_field = views.get_std_txt_field(
79+
on_change=self.on_vat_number_changed,
80+
label="VAT Number",
81+
hint="Value Added Tax number of the user, legally required for invoices.",
82+
)
83+
self.bank_name_field = views.get_std_txt_field(
84+
on_change=self.on_bank_name_changed,
85+
label="Name",
86+
hint="Name of account",
87+
)
88+
self.bank_iban_field = views.get_std_txt_field(
89+
on_change=self.on_bank_iban_changed,
90+
label="IBAN",
91+
hint="International Bank Account Number",
92+
)
93+
self.bank_ibc_field = views.get_std_txt_field(
94+
on_change=self.on_bank_bic_changed,
95+
label="BIC",
96+
hint="Bank Identifier Code",
97+
)
8098
return Column(
8199
spacing=dimens.SPACE_MD,
82100
controls=[
83-
views.get_std_txt_field(
84-
on_change=self.on_vat_number_changed,
85-
label="VAT Number",
86-
hint="Value Added Tax number of the user, legally required for invoices.",
87-
initial_value=self.vat_number,
88-
),
101+
self.vat_number_field,
89102
views.xsSpace,
90103
views.get_sub_heading_txt("Bank Account"),
91-
views.get_std_txt_field(
92-
on_change=self.on_bank_name_changed,
93-
label="Name",
94-
initial_value=self.bank_name,
95-
),
96-
views.get_std_txt_field(
97-
on_change=self.on_bank_iban_changed,
98-
label="IBAN",
99-
initial_value=self.bank_iban,
100-
),
101-
views.get_std_txt_field(
102-
on_change=self.on_bank_bic_changed,
103-
label="BIC",
104-
initial_value=self.bank_bic,
105-
),
104+
self.bank_name_field,
105+
self.bank_iban_field,
106+
self.bank_ibc_field,
106107
views.stdSpace,
107108
views.get_primary_btn(
108109
label="Save",
@@ -347,13 +348,13 @@ class SplashScreen(TuttleView, UserControl):
347348
def __init__(
348349
self,
349350
params: TuttleViewParams,
350-
install_demo_data_callback,
351+
on_install_demo_data: Callable,
351352
):
352353
super().__init__(params=params)
353354
self.keep_back_stack = False # User cannot go back from this screen
354355
self.intent = AuthIntent()
355-
self.install_demo_data_callback = install_demo_data_callback
356356
self.client_storage = params.client_storage
357+
self.on_install_demo_data = on_install_demo_data
357358

358359
def show_login_if_signed_out_else_redirect(self):
359360
result = self.intent.get_user_if_exists()
@@ -391,8 +392,9 @@ def set_login_form(self):
391392
self.update_self()
392393

393394
def on_proceed_with_demo_data_clicked(self, e):
394-
self.install_demo_data_callback()
395-
self.navigate_to_route(res_utils.HOME_SCREEN_ROUTE)
395+
"""when the user clicks on the proceed with demo data button"""
396+
self.on_install_demo_data() # install demo data
397+
self.navigate_to_route(res_utils.HOME_SCREEN_ROUTE) # navigate to home screen
396398

397399
def did_mount(self):
398400
self.mounted = True
@@ -580,6 +582,7 @@ def build(self):
580582
)
581583

582584
def did_mount(self):
585+
583586
"""Called when the view is mounted on page"""
584587
self.mounted = True
585588
result: IntentResult = self.intent.get_user_if_exists()
@@ -638,6 +641,7 @@ def build(self):
638641
return profile_destination_content_wrapper(self.user_info_content)
639642

640643
def did_mount(self):
644+
641645
"""Called when the view is mounted on page"""
642646
self.mounted = True
643647
result: IntentResult = self.intent.get_user_if_exists()
@@ -673,11 +677,15 @@ def on_update_payment_info(self, user: User):
673677
self.show_snack(result.error_msg, is_error=True)
674678

675679
def build(self):
680+
self.payment_data_form = PaymentDataForm(
681+
on_form_submit=self.on_update_payment_info,
682+
)
676683
self.payment_info_content = [
677684
views.get_heading(
678685
"Payment Settings",
679686
size=fonts.HEADLINE_4_SIZE,
680687
),
688+
self.payment_data_form,
681689
]
682690
return profile_destination_content_wrapper(self.payment_info_content)
683691

@@ -691,11 +699,7 @@ def did_mount(self):
691699
else:
692700
self.user_profile: User = result.data
693701
# setup payment info form
694-
self.payment_data_form = PaymentDataForm(
695-
on_form_submit=self.on_update_payment_info,
696-
user=self.user_profile,
697-
)
698-
self.payment_info_content.append(self.payment_data_form)
702+
self.payment_data_form.update_form_data(user=self.user_profile)
699703
self.update_self()
700704

701705
def will_unmount(self):

0 commit comments

Comments
 (0)