1
1
from typing import Callable , Optional
2
2
3
- import re
4
- from pathlib import Path
5
-
6
3
from flet import (
7
4
AlertDialog ,
8
5
FilePicker ,
9
6
FilePickerUploadFile ,
10
7
Page ,
11
8
SnackBar ,
12
9
TemplateRoute ,
13
- Text ,
14
10
View ,
15
11
app ,
16
12
)
17
13
18
- import demo
19
- import sqlmodel
20
14
from auth .view import ProfileScreen , SplashScreen
21
15
from contracts .view import ContractEditorScreen , ViewContractScreen
22
- from core .abstractions import TuttleView , TuttleViewParams
16
+ from core .abstractions import TView , TViewParams
23
17
from core .client_storage_impl import ClientStorageImpl
18
+ from core .database_storage_impl import DatabaseStorageImpl
24
19
from core .models import RouteView
25
20
from core .utils import AlertDialogControls
26
- from core .views import get_heading
21
+ from core .views import THeading
27
22
from error_views .page_not_found_screen import Error404Screen
28
23
from home .view import HomeScreen
29
24
from loguru import logger
@@ -64,9 +59,11 @@ def __init__(
64
59
self .page .fonts = APP_FONTS
65
60
self .page .theme = APP_THEME
66
61
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 ,
69
65
)
66
+ preferences = PreferencesIntent (self .client_storage )
70
67
preferences_result = preferences .get_preference_by_key (
71
68
PreferencesStorageKeys .theme_mode_key
72
69
)
@@ -92,10 +89,6 @@ def __init__(
92
89
self .current_route_view : Optional [RouteView ] = None
93
90
self .page .on_resize = self .page_resize
94
91
95
- # database config
96
- self .app_dir = self .ensure_app_dir ()
97
- self .db_path = self .app_dir / "tuttle.db"
98
-
99
92
def page_resize (self , e ):
100
93
if self .current_route_view :
101
94
self .current_route_view .on_window_resized (
@@ -156,7 +149,7 @@ def show_snack(
156
149
self .page .snack_bar .open = False
157
150
self .page .update ()
158
151
self .page .snack_bar = SnackBar (
159
- get_heading (
152
+ THeading (
160
153
title = message ,
161
154
size = HEADLINE_4_SIZE ,
162
155
color = ERROR_COLOR if is_error else WHITE_COLOR ,
@@ -193,7 +186,6 @@ def control_alert_dialog(
193
186
def change_route (self , to_route : str , data : Optional [any ] = None ):
194
187
"""navigates to a new route"""
195
188
newRoute = to_route if data is None else f"{ to_route } /{ data } "
196
-
197
189
self .page .go (newRoute )
198
190
199
191
def on_view_pop (self , view : Optional [View ] = None ):
@@ -205,8 +197,8 @@ def on_view_pop(self, view: Optional[View] = None):
205
197
self .page .go (current_page_view .route )
206
198
if current_page_view .controls :
207
199
try :
208
- # the controls should contain a TuttleView as first control
209
- tuttle_view : TuttleView = current_page_view .controls [0 ]
200
+ # the controls should contain a TView as first control
201
+ tuttle_view : TView = current_page_view .controls [0 ]
210
202
# notify view that it has been resumed
211
203
tuttle_view .on_resume_after_back_pressed ()
212
204
except Exception as e :
@@ -247,90 +239,36 @@ def on_route_change(self, route):
247
239
self .page .window_width , self .page .window_height
248
240
)
249
241
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
-
281
242
def store_demo_timetracking_dataframe (self , time_tracking_data : DataFrame ):
282
243
"""Caches the time tracking dataframe created from a demo installation"""
283
244
self .timetracking_intent = TimeTrackingIntent (
284
245
client_storage = self .client_storage
285
246
)
286
247
self .timetracking_intent .set_timetracking_data (data = time_tracking_data )
287
248
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 )
313
251
314
252
def close (self ):
315
253
"""Closes the application."""
316
254
self .page .window_close ()
317
255
318
- def build (self ):
319
- self .page .go (self .page .route )
320
-
321
256
def reset_and_quit (self ):
322
257
"""Resets the application and quits."""
323
- self .reset_database ()
258
+ self .db . reset_database ()
324
259
self .close ()
325
260
326
261
327
262
class TuttleRoutes :
328
263
"""Utility class for parsing of routes to destination views"""
329
264
330
265
def __init__ (self , app : TuttleApp ):
331
- self . app = app
266
+ # init callbacks for some views
332
267
self .on_theme_changed = app .on_theme_mode_changed
333
- self .tuttle_view_params = TuttleViewParams (
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
271
+ self .tuttle_view_params = TViewParams (
334
272
navigate_to_route = app .change_route ,
335
273
show_snack = app .show_snack ,
336
274
dialog_controller = app .control_alert_dialog ,
@@ -343,7 +281,7 @@ def __init__(self, app: TuttleApp):
343
281
def get_page_route_view (
344
282
self ,
345
283
routeName : str ,
346
- view : TuttleView ,
284
+ view : TView ,
347
285
) -> RouteView :
348
286
"""Constructs the view with a given route"""
349
287
view_container = View (
@@ -370,14 +308,16 @@ def parse_route(self, pageRoute: str):
370
308
if routePath .match (SPLASH_SCREEN_ROUTE ):
371
309
screen = SplashScreen (
372
310
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 ,
374
312
)
375
313
elif routePath .match (HOME_SCREEN_ROUTE ):
376
314
screen = HomeScreen (
377
315
params = self .tuttle_view_params ,
378
316
)
379
317
elif routePath .match (PROFILE_SCREEN_ROUTE ):
380
- screen = ProfileScreen (params = self .tuttle_view_params )
318
+ screen = ProfileScreen (
319
+ params = self .tuttle_view_params ,
320
+ )
381
321
elif routePath .match (CONTRACT_EDITOR_SCREEN_ROUTE ):
382
322
screen = ContractEditorScreen (params = self .tuttle_view_params )
383
323
elif routePath .match (f"{ CONTRACT_DETAILS_SCREEN_ROUTE } /:contractId" ):
@@ -395,7 +335,7 @@ def parse_route(self, pageRoute: str):
395
335
screen = PreferencesScreen (
396
336
params = self .tuttle_view_params ,
397
337
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 ,
399
339
)
400
340
elif routePath .match (PROJECT_EDITOR_SCREEN_ROUTE ):
401
341
screen = ProjectEditorScreen (params = self .tuttle_view_params )
@@ -431,7 +371,7 @@ def main(page: Page):
431
371
app = TuttleApp (page )
432
372
433
373
# if database does not exist, create it
434
- app .ensure_database ()
374
+ app .db . ensure_database ()
435
375
436
376
app .build ()
437
377
0 commit comments