Skip to content

Commit fc1ea49

Browse files
authored
Merge pull request #230 from tuttle-dev/dev-packaging
fix: profile photo upload
2 parents e3d6d2f + 5bfeda4 commit fc1ea49

File tree

6 files changed

+65
-70
lines changed

6 files changed

+65
-70
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,4 @@ app/assets/uploads
143143

144144
# visual studio workspaces
145145
*.code-workspace
146+
assets/uploads/*

app.py

-20
Original file line numberDiff line numberDiff line change
@@ -105,38 +105,19 @@ def page_resize(self, e):
105105
def pick_file_callback(
106106
self,
107107
on_file_picker_result,
108-
on_upload_progress,
109108
allowed_extensions,
110109
dialog_title,
111110
file_type,
112111
):
113112
# used by views to request a file upload
114113
self.file_picker.on_result = on_file_picker_result
115-
self.file_picker.on_upload = on_upload_progress
116114
self.file_picker.pick_files(
117115
allow_multiple=False,
118116
allowed_extensions=allowed_extensions,
119117
dialog_title=dialog_title,
120118
file_type=file_type,
121119
)
122120

123-
def upload_file_callback(self, file):
124-
try:
125-
upload_to = self.page.get_upload_url(file.name, 600)
126-
upload_item = FilePickerUploadFile(
127-
file.name,
128-
upload_url=upload_to,
129-
)
130-
self.file_picker.upload([upload_item])
131-
132-
upload_path_in_assets = f"{get_assets_uploads_url()}/{file.name}"
133-
return upload_path_in_assets
134-
except Exception as e:
135-
logger.error(
136-
f"Exception @app.upload_file_callback raised during file upload {e.__class__.__name__}"
137-
)
138-
logger.exception(e)
139-
return None
140121

141122
def on_theme_mode_changed(self, selected_theme: str):
142123
"""callback function used by views for changing app theme mode"""
@@ -281,7 +262,6 @@ def __init__(self, app: TuttleApp):
281262
dialog_controller=app.control_alert_dialog,
282263
on_navigate_back=app.on_view_pop,
283264
client_storage=app.client_storage,
284-
upload_file_callback=app.upload_file_callback,
285265
pick_file_callback=app.pick_file_callback,
286266
)
287267

tuttle/app/auth/view.py

+24-26
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from typing import Callable, Optional
2-
2+
from pathlib import Path
33
from flet import (
44
Column,
55
Container,
@@ -501,7 +501,6 @@ def on_update_photo_clicked(self, e):
501501
"""Callback for when user clicks on the update photo button"""
502502
self.pick_file_callback(
503503
on_file_picker_result=self.on_profile_photo_picked,
504-
on_upload_progress=self.uploading_profile_pic_progress_listener,
505504
allowed_extensions=["png", "jpeg", "jpg"],
506505
dialog_title="Tuttle profile photo",
507506
file_type="custom",
@@ -511,30 +510,29 @@ def on_profile_photo_picked(self, e):
511510
"""Callback for when profile photo has been picked"""
512511
if e.files and len(e.files) > 0:
513512
file = e.files[0]
514-
upload_url = self.upload_file_callback(file)
513+
upload_url = Path(file.path)
515514
if upload_url:
516-
self.uploaded_photo_path = upload_url
517-
518-
def uploading_profile_pic_progress_listener(self, e):
519-
"""Callback for when profile photo is being uploaded"""
520-
if e.progress == 1.0:
521-
if self.uploaded_photo_path:
522-
result = self.intent.update_user_photo_path(
523-
self.user_profile,
524-
self.uploaded_photo_path,
525-
)
526-
# assume error occurred
527-
msg = result.error_msg
528-
is_err = True
529-
if result.was_intent_successful:
530-
self.profile_photo_img.src = self.uploaded_photo_path
531-
msg = "Profile photo updated"
532-
is_err = False
533-
self.show_snack(msg, is_err)
534-
if is_err:
535-
self.user_profile.profile_photo_path = ""
536-
self.uploaded_photo_path = None # clear
537-
self.update_self()
515+
self.uploaded_photo_path = str(upload_url)
516+
self.setProfilePic()
517+
518+
def setProfilePic(self):
519+
"""Updates the profile photo"""
520+
if not self.uploaded_photo_path:
521+
return
522+
result = self.intent.update_user_photo_path(self.user_profile, self.uploaded_photo_path,)
523+
# assume error occurred
524+
msg = result.error_msg
525+
is_err = True
526+
if result.was_intent_successful:
527+
self.profile_photo_img.src_base64 = utils.toBase64(self.uploaded_photo_path)
528+
msg = "Profile photo updated"
529+
is_err = False
530+
self.show_snack(msg, is_err)
531+
if is_err:
532+
self.user_profile.profile_photo_path = ""
533+
self.uploaded_photo_path = None # clear
534+
self.update_self()
535+
538536

539537
def build(self):
540538
self.profile_photo_img = views.TProfilePhotoImg()
@@ -564,7 +562,7 @@ def did_mount(self):
564562
else:
565563
self.user_profile: User = result.data
566564
if self.user_profile.profile_photo_path:
567-
self.profile_photo_img.src = self.user_profile.profile_photo_path
565+
self.profile_photo_img.src_base64 = utils.toBase64(self.user_profile.profile_photo_path)
568566
self.update_self()
569567

570568
def will_unmount(self):

tuttle/app/core/abstractions.py

-2
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ class TViewParams:
9595
navigate_to_route: Callable
9696
show_snack: Callable
9797
dialog_controller: Callable
98-
upload_file_callback: Callable
9998
pick_file_callback: Callable[[file_picker.FilePickerFile], str]
10099
client_storage: ClientStorage
101100
vertical_alignment_in_parent: str = START_ALIGNMENT
@@ -118,7 +117,6 @@ def __init__(self, params: TViewParams):
118117
self.keep_back_stack = params.keep_back_stack
119118
self.navigate_back = params.on_navigate_back
120119
self.page_scroll_type = params.page_scroll_type
121-
self.upload_file_callback = params.upload_file_callback
122120
self.pick_file_callback = params.pick_file_callback
123121
self.client_storage = params.client_storage
124122
self.mounted = False

tuttle/app/core/utils.py

+15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import warnings
2+
import base64
23

34
warnings.warn(
45
"wastebasket module, content will be moved to other modules",
@@ -144,3 +145,17 @@ def get_currencies() -> List[Tuple[str, str, str]]:
144145
# sort alphabetically by abbreviation
145146
currencies.sort(key=lambda tup: tup[1])
146147
return currencies
148+
149+
150+
def toBase64(
151+
image_path,
152+
) -> str:
153+
"""Returns base64 encoded image from the path"""
154+
155+
# Read the image file as bytes
156+
with open(image_path, "rb") as image_file:
157+
image_bytes = image_file.read()
158+
# Convert the bytes to a base64
159+
stringbase64_string = base64.b64encode(image_bytes).decode("utf-8")
160+
161+
return stringbase64_string

tuttle/app/timetracking/view.py

+25-22
Original file line numberDiff line numberDiff line change
@@ -204,11 +204,12 @@ def on_add_timetrack_from_file(
204204
):
205205
"""Open file picker to select a file to upload"""
206206
self.close_pop_up_if_open()
207+
if not is_spreadsheet and not is_ics:
208+
return
207209
allowed_exts = ["ics"] if is_ics else ["xlsx", "csv", "xls", "tsv", "ods"]
208210
title = "Select .ics file" if is_ics else "Select excel file"
209211
self.pick_file_callback(
210212
on_file_picker_result=self.on_file_picker_result,
211-
on_upload_progress=self.on_upload_progress,
212213
allowed_extensions=allowed_exts,
213214
dialog_title=title,
214215
file_type="custom",
@@ -220,33 +221,35 @@ def on_file_picker_result(self, e: FilePickerResultEvent):
220221
if e.files and len(e.files) > 0:
221222
file = e.files[0]
222223
self.set_progress_hint(f"Uploading file {file.name}")
223-
self.upload_file_callback(file)
224224
upload_path = Path(file.path)
225225
if upload_path:
226226
self.uploaded_file_path = upload_path
227+
self.extract_dataframe_from_file()
227228
else:
228229
self.set_progress_hint(hide_progress=True)
229230

230-
def on_upload_progress(self, e: FilePickerUploadEvent):
231-
"""Handle file upload progress"""
232-
if e.progress == 1.0:
233-
# upload complete
234-
self.set_progress_hint(f"Upload complete, processing file...")
235-
intent_result = self.intent.process_timetracking_file(
236-
self.uploaded_file_path,
237-
)
238-
msg = (
239-
"New work progress recorded."
240-
if intent_result.was_intent_successful
241-
else intent_result.error_msg
242-
)
243-
is_error = not intent_result.was_intent_successful
244-
self.show_snack(msg, is_error)
245-
if intent_result.was_intent_successful:
246-
self.dataframe_to_display = intent_result.data
247-
self.update_timetracking_dataframe()
248-
self.display_dataframe()
249-
self.set_progress_hint(hide_progress=True)
231+
def extract_dataframe_from_file(self,):
232+
233+
"""Execute intent to process file"""
234+
if not self.uploaded_file_path:
235+
return
236+
# upload complete
237+
self.set_progress_hint(f"Upload complete, processing file...")
238+
intent_result = self.intent.process_timetracking_file(
239+
self.uploaded_file_path,
240+
)
241+
msg = (
242+
"New work progress recorded."
243+
if intent_result.was_intent_successful
244+
else intent_result.error_msg
245+
)
246+
is_error = not intent_result.was_intent_successful
247+
self.show_snack(msg, is_error)
248+
if intent_result.was_intent_successful:
249+
self.dataframe_to_display = intent_result.data
250+
self.update_timetracking_dataframe()
251+
self.display_dataframe()
252+
self.set_progress_hint(hide_progress=True)
250253

251254
"""Cloud calendar setup"""
252255

0 commit comments

Comments
 (0)