Skip to content

Commit e3c3978

Browse files
committed
Merge branch 'dev' into dev-feat-timesheets
2 parents a3d5549 + f5f55cf commit e3c3978

File tree

11 files changed

+290
-157
lines changed

11 files changed

+290
-157
lines changed

app/clients/view.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def build(self):
6060
ListTile(
6161
leading=Icon(
6262
utils.TuttleComponentIcons.client_icon,
63-
size=dimens.ICON_SIZE,
63+
size=dimens.MD_ICON_SIZE,
6464
),
6565
title=views.TBodyText(self.client.name),
6666
trailing=editor_controls,

app/contacts/view.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def build(self):
4848
ListTile(
4949
leading=Icon(
5050
utils.TuttleComponentIcons.contact_icon,
51-
size=dimens.ICON_SIZE,
51+
size=dimens.MD_ICON_SIZE,
5252
),
5353
title=views.TBodyText(utils.truncate_str(self.contact.name)),
5454
subtitle=views.TBodyText(

app/contracts/view.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def build(self):
6060
ListTile(
6161
leading=Icon(
6262
utils.TuttleComponentIcons.contract_icon,
63-
size=dimens.ICON_SIZE,
63+
size=dimens.MD_ICON_SIZE,
6464
),
6565
title=views.TBodyText(self.contract.title),
6666
subtitle=views.TBodyText(

app/core/views.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,20 @@
4040
class Spacer(Container):
4141
"""Creates a space between controls"""
4242

43+
# FIXME: Unpythonic code, replace with
44+
# class Spacer(Container):
45+
# SPACE_SIZES = {
46+
# 'lg': 40,
47+
# 'md': 20,
48+
# 'sm': 10,
49+
# 'xs': 5,
50+
# None: 15,
51+
# }
52+
53+
# def __init__(self, size=None, **kwargs):
54+
# self._space_size = self.SPACE_SIZES[size]
55+
# super().__init__(height=self._space_size, width=self._space_size, **kwargs)
56+
4357
def __init__(
4458
self,
4559
lg_space: bool = False,
@@ -827,7 +841,7 @@ def __init__(
827841
leading=Container(
828842
content=TSubHeading(
829843
subtitle=title,
830-
align=utils.START_ALIGNMENT,
844+
align=utils.TXT_ALIGN_LEFT,
831845
expand=True,
832846
color=colors.GRAY_DARK_COLOR,
833847
),

app/demo.py

Lines changed: 53 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,72 @@
1-
from typing import List, Optional, Callable
1+
from typing import Callable, List, Optional
22

3+
import datetime
34
import random
5+
from datetime import date, timedelta
46
from pathlib import Path
5-
from tuttle.calendar import Calendar, ICSCalendar
7+
from decimal import Decimal
8+
69
import faker
7-
import random
8-
import datetime
9-
from datetime import timedelta, date
1010
import ics
11-
from sqlmodel import Field, SQLModel, create_engine, Session, select
11+
import numpy
1212
import sqlalchemy
1313
from loguru import logger
14-
import numpy
14+
from sqlmodel import Field, Session, SQLModel, create_engine, select
1515

16+
from tuttle import rendering
17+
from tuttle.calendar import Calendar, ICSCalendar
1618
from tuttle.model import (
1719
Address,
18-
Contact,
20+
BankAccount,
1921
Client,
20-
Project,
22+
Contact,
2123
Contract,
22-
TimeUnit,
2324
Cycle,
24-
User,
25-
BankAccount,
2625
Invoice,
2726
InvoiceItem,
2827
Timesheet,
2928
TimeTrackingItem,
29+
Project,
30+
TimeUnit,
31+
User,
3032
)
31-
from tuttle import rendering
3233

3334

3435
def create_fake_contact(
3536
fake: faker.Faker,
3637
):
37-
try:
38-
street_line, city_line = fake.address().splitlines()
39-
a = Address(
40-
id=id,
41-
street=street_line.split(" ")[0],
42-
number=street_line.split(" ")[1],
43-
city=city_line.split(" ")[1],
44-
postal_code=city_line.split(" ")[0],
45-
country=fake.country(),
46-
)
47-
first_name, last_name = fake.name().split(" ", 1)
48-
contact = Contact(
49-
id=id,
50-
first_name=first_name,
51-
last_name=last_name,
52-
email=fake.email(),
53-
company=fake.company(),
54-
address_id=a.id,
55-
address=a,
56-
)
57-
return contact
58-
except Exception as ex:
59-
logger.error(ex)
60-
logger.error(f"Failed to create fake contact, trying again")
61-
return create_fake_contact(fake)
38+
39+
split_address_lines = fake.address().splitlines()
40+
street_line = split_address_lines[0]
41+
city_line = split_address_lines[1]
42+
a = Address(
43+
street=street_line,
44+
number=city_line,
45+
city=city_line.split(" ")[1],
46+
postal_code=city_line.split(" ")[0],
47+
country=fake.country(),
48+
)
49+
first_name, last_name = fake.name().split(" ", 1)
50+
contact = Contact(
51+
first_name=first_name,
52+
last_name=last_name,
53+
email=fake.email(),
54+
company=fake.company(),
55+
address_id=a.id,
56+
address=a,
57+
)
58+
return contact
6259

6360

6461
def create_fake_client(
6562
invoicing_contact: Contact,
6663
fake: faker.Faker,
6764
):
6865
client = Client(
69-
id=id,
7066
name=fake.company(),
7167
invoicing_contact=invoicing_contact,
7268
)
69+
assert client.invoicing_contact is not None
7370
return client
7471

7572

@@ -94,7 +91,7 @@ def create_fake_contract(
9491
start_date=fake.date_this_year(after_today=True),
9592
rate=rate,
9693
currency="EUR", # TODO: Use actual currency
97-
VAT_rate=round(random.uniform(0.05, 0.2), 2),
94+
VAT_rate=Decimal(round(random.uniform(0.05, 0.2), 2)),
9895
unit=unit,
9996
units_per_workday=random.randint(1, 12),
10097
volume=fake.random_int(1, 1000),
@@ -108,11 +105,12 @@ def create_fake_project(
108105
fake: faker.Faker,
109106
):
110107
project_title = fake.bs()
108+
project_tag = f"#{'-'.join(project_title.split(' ')[:2]).lower()}"
109+
111110
project = Project(
112111
title=project_title,
113-
tag="-".join(project_title.split(" ")[:2]).lower(),
112+
tag=project_tag,
114113
description=fake.paragraph(nb_sentences=2),
115-
unique_tag=project_title.split(" ")[0].lower(),
116114
is_completed=fake.pybool(),
117115
start_date=datetime.date.today(),
118116
end_date=datetime.date.today() + datetime.timedelta(days=80),
@@ -188,7 +186,7 @@ def create_fake_invoice(
188186
"""
189187
invoice_number = next(invoice_number_counter)
190188
invoice = Invoice(
191-
number=str(invoice_number), # TODO: replace with generated number
189+
number=str(invoice_number),
192190
date=datetime.date.today(),
193191
sent=fake.pybool(),
194192
paid=fake.pybool(),
@@ -200,6 +198,7 @@ def create_fake_invoice(
200198
number_of_items = fake.random_int(min=1, max=5)
201199
for _ in range(number_of_items):
202200
unit = fake.random_element(elements=("hours", "days"))
201+
unit_price = 0
203202
if unit == "hours":
204203
unit_price = abs(round(numpy.random.normal(50, 20), 2))
205204
elif unit == "days":
@@ -210,12 +209,11 @@ def create_fake_invoice(
210209
end_date=fake.date_this_decade(),
211210
quantity=fake.random_int(min=1, max=10),
212211
unit=unit,
213-
unit_price=unit_price,
212+
unit_price=Decimal(unit_price),
214213
description=fake.sentence(),
215-
VAT_rate=vat_rate,
214+
VAT_rate=Decimal(vat_rate),
216215
invoice=invoice,
217216
)
218-
assert invoice_item.invoice == invoice
219217

220218
try:
221219
rendering.render_invoice(
@@ -272,7 +270,6 @@ def create_demo_user() -> User:
272270
phone_number="+55555555555",
273271
VAT_number="27B-6",
274272
address=Address(
275-
name="Harry Tuttle",
276273
street="Main Street",
277274
number="450",
278275
city="Somewhere",
@@ -289,6 +286,14 @@ def create_demo_user() -> User:
289286

290287

291288
def create_fake_calendar(project_list: List[Project]) -> ics.Calendar:
289+
def random_datetime(start, end):
290+
return start + timedelta(
291+
seconds=random.randint(0, int((end - start).total_seconds()))
292+
)
293+
294+
def random_duration():
295+
return timedelta(hours=random.randint(1, 8))
296+
292297
# create a new calendar
293298
calendar = ics.Calendar()
294299

@@ -303,7 +308,7 @@ def create_fake_calendar(project_list: List[Project]) -> ics.Calendar:
303308
for _ in range(random.randint(1, 5)):
304309
# create a new event
305310
event = ics.Event()
306-
event.name = f"Meeting for #{project.tag}"
311+
event.name = f"Meeting for {project.tag}"
307312

308313
# set the event's begin and end datetime
309314
event.begin = random_datetime(month_ago, now)
@@ -314,16 +319,6 @@ def create_fake_calendar(project_list: List[Project]) -> ics.Calendar:
314319
return calendar
315320

316321

317-
def random_datetime(start, end):
318-
return start + timedelta(
319-
seconds=random.randint(0, int((end - start).total_seconds()))
320-
)
321-
322-
323-
def random_duration():
324-
return timedelta(hours=random.randint(1, 8))
325-
326-
327322
def install_demo_data(
328323
n_projects: int,
329324
db_path: str,
@@ -377,7 +372,3 @@ def install_demo_data(
377372
for project in projects:
378373
session.add(project)
379374
session.commit()
380-
381-
382-
if __name__ == "__main__":
383-
install_demo_data(n_projects=10)

app/projects/view.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@ def build(self):
5858
ListTile(
5959
leading=Icon(
6060
utils.TuttleComponentIcons.project_icon,
61-
size=dimens.ICON_SIZE,
61+
size=dimens.MD_ICON_SIZE,
6262
),
6363
title=views.TBodyText(self.project.title),
6464
subtitle=views.TBodyText(
65-
f"#{self.project.tag}",
65+
f"{self.project.tag}",
6666
color=colors.GRAY_COLOR,
6767
weight=FontWeight.BOLD,
6868
),
@@ -76,6 +76,7 @@ def build(self):
7676
on_click=lambda e: self.on_view_details_clicked(self.project.id),
7777
),
7878
views.Spacer(md_space=True),
79+
views.Spacer(md_space=True),
7980
ResponsiveRow(
8081
controls=[
8182
views.TBodyText(

tuttle/calendar.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
def extract_hashtag(string) -> str:
2323
"""Extract the first hashtag from a string."""
24-
match = re.search(r"#(\S+)", string)
24+
match = re.search(r"(#\S+)", string)
2525
if match:
2626
return match.group(1)
2727
else:

0 commit comments

Comments
 (0)