Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ classifiers = [
]

dependencies = [
"calendula @ file:///Users/cls/Documents/Work/RnD/FLOSS/calendula",
"pytest",
"pandas",
"matplotlib",
"altair",
"pydantic",
"sqlmodel",
"ics",
"tatsu<5.8",
"babel",
"loguru",
Expand All @@ -40,7 +40,6 @@ dependencies = [
"pymupdf",
"flet>=0.81.0,<0.82.0",
"pycountry",
"icloudpy",
]

[project.urls]
Expand All @@ -67,6 +66,9 @@ build = [
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.metadata]
allow-direct-references = true

[tool.hatch.build.targets.wheel]
packages = ["tuttle"]

Expand Down
1 change: 0 additions & 1 deletion tuttle/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from . import (
banking,
calendar,
cloud,
invoicing,
model,
tax,
Expand Down
11 changes: 0 additions & 11 deletions tuttle/app/preferences/intent.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,6 @@ def get_preferences(self) -> IntentResult:
preferences.theme_mode = preference_item_result.data
elif item.value == PreferencesStorageKeys.default_currency_key.value:
preferences.default_currency = preference_item_result.data
elif item.value == PreferencesStorageKeys.cloud_acc_id_key.value:
preferences.cloud_acc_id = preference_item_result.data
elif item.value == PreferencesStorageKeys.cloud_provider_key.value:
preferences.cloud_acc_provider = preference_item_result.data
elif item.value == PreferencesStorageKeys.language_key.value:
preferences.language = preference_item_result.data

Expand All @@ -67,13 +63,6 @@ def save_preferences(self, preferences: Preferences) -> IntentResult:
self.set_preference_key_value_pair(
PreferencesStorageKeys.theme_mode_key, preferences.theme_mode
)
self.set_preference_key_value_pair(
PreferencesStorageKeys.cloud_acc_id_key, preferences.cloud_acc_id
)
self.set_preference_key_value_pair(
PreferencesStorageKeys.cloud_provider_key,
preferences.cloud_acc_provider,
)
self.set_preference_key_value_pair(
PreferencesStorageKeys.default_currency_key,
preferences.default_currency,
Expand Down
4 changes: 0 additions & 4 deletions tuttle/app/preferences/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
@dataclass
class Preferences:
theme_mode: str = ""
cloud_acc_id: str = ""
cloud_acc_provider: str = ""
default_currency: str = ""
language: str = ""

Expand All @@ -15,8 +13,6 @@ class PreferencesStorageKeys(Enum):
"""defines the keys used in storing preferences as key-value pairs"""

theme_mode_key = "preferred_theme_mode"
cloud_acc_id_key = "preferred_cloud_acc_id"
cloud_provider_key = "preferred_cloud_acc_provider"
default_currency_key = "preferred_default_currency"
language_key = "preferred_language"

Expand Down
37 changes: 1 addition & 36 deletions tuttle/app/preferences/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@
)
from ..res.theme import THEME_MODES

from ...cloud import CloudProvider


class PreferencesScreen(TView, Row):
def __init__(
Expand Down Expand Up @@ -69,22 +67,10 @@ def on_currency_selected(self, e):
return
self.preferences.default_currency = e.control.value

def on_cloud_account_id_changed(self, e):
if not self.preferences:
return
self.preferences.cloud_acc_id = e.control.value

def on_cloud_provider_selected(self, e):
if not self.preferences:
return
self.preferences.cloud_acc_provider = e.control.value

def refresh_preferences_items(self):
if self.preferences is None:
return
self.theme_control.update_value(self.preferences.theme_mode)
self.cloud_provider_control.update_value(self.preferences.cloud_acc_provider)
self.cloud_account_id_control.value = self.preferences.cloud_acc_id
self.currencies_control.update_value(self.preferences.default_currency)
self.languages_control.update_value(self.preferences.language)

Expand Down Expand Up @@ -182,16 +168,6 @@ def build(self):
label="Appearance",
hint="",
)
self.cloud_provider_control = views.TDropDown(
label="Cloud Provider",
on_change=self.on_cloud_provider_selected,
items=[item.value for item in CloudProvider],
)
self.cloud_account_id_control = views.TTextField(
label="Cloud Account Name",
hint="Your cloud account name",
on_change=self.on_cloud_account_id_changed,
)
self.currencies_control = views.TDropDown(
label="Default Currency",
on_change=self.on_currency_selected,
Expand All @@ -216,7 +192,7 @@ def build(self):
self.tabs = Tabs(
selected_index=0,
animation_duration=300,
length=3,
length=2,
width=self.body_width - SPACE_MD,
height=MIN_WINDOW_HEIGHT,
content=Column(
Expand All @@ -225,7 +201,6 @@ def build(self):
TabBar(
tabs=[
self._make_tab_header("General", Icons.SETTINGS_OUTLINED),
self._make_tab_header("Cloud", Icons.CLOUD_OUTLINED),
self._make_tab_header("Locale", Icons.LANGUAGE_OUTLINED),
],
),
Expand All @@ -239,16 +214,6 @@ def build(self):
self.reset_button,
]
),
self._make_tab_content(
[
views.TBodyText(
txt="Setting up your cloud account will enable you to import time tracking data from your cloud calendar.",
),
views.Spacer(sm_space=True),
self.cloud_provider_control,
self.cloud_account_id_control,
]
),
self._make_tab_content(
[
self.languages_control,
Expand Down
99 changes: 20 additions & 79 deletions tuttle/app/timetracking/data_source.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
from typing import Type, Union, Any, Optional

from pathlib import Path
from typing import Optional

from loguru import logger
import icloudpy

from ..core.abstractions import SQLModelDataSourceMixin
from ..core.intent_result import IntentResult
from pandas import DataFrame

from ...calendar import ICSCalendar, ICloudCalendar, CloudCalendar
from ...calendar import ICSCalendar, system_calendar_available
from ...dev import singleton
from ...cloud import CloudConnector, CloudProvider
from ... import timetracking

SYSTEM_CALENDAR_AVAILABLE = system_calendar_available()


@singleton
class TimeTrackingDataFrameSource:
Expand All @@ -40,14 +35,7 @@ def load_data(
self,
file_path: str,
) -> DataFrame:
"""loads time tracking data from a spreadsheet file

Arguments:
file_path : path to an uploaded spreadsheet file

Returns:
DataFrame: time tracking data
"""
"""Loads time tracking data from a spreadsheet file."""
logger.info(f"Loading time tracking data from {file_path}...")
timetracking_data: DataFrame = timetracking.import_from_spreadsheet(
path=file_path,
Expand All @@ -66,18 +54,7 @@ def load_data(
self,
ics_file_path,
) -> DataFrame:
"""loads time tracking data from a .ics file

Args:
ics_file_path : path to an uploaded ics or spreadsheet file

Returns:
IntentResult:
was_intent_successful : bool
data : Calendar if was_intent_successful else None
log_message : str if an error or exception occurs
exception : Exception if an exception occurs
"""
"""Loads time tracking data from a .ics file."""
file_calendar: ICSCalendar = ICSCalendar(
name=ics_file_path.name,
path=ics_file_path,
Expand All @@ -86,57 +63,21 @@ def load_data(
return calendar_data


class TimeTrackingCloudCalendarSource:
"""Configures and processes calendar data from the cloud"""

def __init__(self):
super().__init__()
class TimeTrackingSystemCalendarSource:
"""Loads time tracking data from the system calendar."""

def load_data(
self,
calendar_name: str,
cloud_connector: CloudConnector,
) -> DataFrame:
"""Loads data from a cloud calendar"""
calendar = None
if cloud_connector.provider == CloudProvider.ICloud.value:
icloud_connector: icloudpy.ICloudPyService = (
cloud_connector.concrete_connector
)
calendar: CloudCalendar = ICloudCalendar(
name=calendar_name,
icloud_connector=icloud_connector,
)
else:
raise NotImplementedError

calendar_data: DataFrame = calendar.to_data()
return calendar_data
def list_calendars(self) -> list[dict]:
"""Returns all calendars available on this system."""
if not SYSTEM_CALENDAR_AVAILABLE:
return []
from ...calendar import SystemCalendar

def login_to_icloud(
self,
apple_id: str,
password: str,
) -> CloudConnector:
"""Attempts to authenticate user with their icloud account"""
# TODO: error handling - login may fail
logger.info(f"Logging in to iCloud with {apple_id}...")
icloud_connector = icloudpy.ICloudPyService(
apple_id=apple_id,
password=password,
cookie_directory=Path.home() / ".tuttle" / "cookies",
)
return CloudConnector(
cloud_connector=icloud_connector,
account_name=apple_id,
)
cal = SystemCalendar()
return cal.list_available_calendars()

""" GOOGLE LOGIN STEPS """
def load_data(self, calendar_name: str) -> DataFrame:
"""Loads events from a named system calendar as time tracking data."""
from ...calendar import SystemCalendar

def login_to_google(
self,
google_account: str,
google_account_password: str,
):
"""TODO Attempts to authenticate user with their google account"""
raise NotImplementedError
cal = SystemCalendar(name=calendar_name)
return cal.to_data()
Loading
Loading