Skip to content

Commit 502b211

Browse files
committed
Merge branch 'dev' into dev-feat-timesheets
2 parents 64ddd06 + a797aa9 commit 502b211

File tree

25 files changed

+1239
-1197
lines changed

25 files changed

+1239
-1197
lines changed

.circleci/config.yml

Lines changed: 0 additions & 81 deletions
This file was deleted.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ Desktop apps are great - let's have more of them. We are consciously developing
5555

5656
Manage your business contacts and contract terms for your project - all in one place.
5757

58-
<img src="assets/images/screenshot-contracts.png" width=768 />
58+
<img src="assets/images/screenshot-contract.png" width=768 />
5959

6060
### Time Tracking
6161

app/app.py

Lines changed: 25 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,24 @@
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
22-
from core.abstractions import TuttleView, TuttleViewParams
16+
from core.abstractions import TView, TViewParams
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
26-
from core.views import get_heading
21+
from core.views import THeading
2722
from error_views.page_not_found_screen import Error404Screen
2823
from home.view import HomeScreen
2924
from loguru import logger
@@ -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(
@@ -156,7 +149,7 @@ def show_snack(
156149
self.page.snack_bar.open = False
157150
self.page.update()
158151
self.page.snack_bar = SnackBar(
159-
get_heading(
152+
THeading(
160153
title=message,
161154
size=HEADLINE_4_SIZE,
162155
color=ERROR_COLOR if is_error else WHITE_COLOR,
@@ -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):
@@ -205,8 +197,8 @@ def on_view_pop(self, view: Optional[View] = None):
205197
self.page.go(current_page_view.route)
206198
if current_page_view.controls:
207199
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]
210202
# notify view that it has been resumed
211203
tuttle_view.on_resume_after_back_pressed()
212204
except Exception as e:
@@ -247,90 +239,36 @@ 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
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(
334272
navigate_to_route=app.change_route,
335273
show_snack=app.show_snack,
336274
dialog_controller=app.control_alert_dialog,
@@ -343,7 +281,7 @@ def __init__(self, app: TuttleApp):
343281
def get_page_route_view(
344282
self,
345283
routeName: str,
346-
view: TuttleView,
284+
view: TView,
347285
) -> RouteView:
348286
"""Constructs the view with a given route"""
349287
view_container = View(
@@ -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

0 commit comments

Comments
 (0)