Skip to content
Merged
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ backend-test: ## Test the backend

.PHONY: backend-dbshell
backend-dbshell: ## Start a psql shell
@$(ORCHESTRATOR) compose run -it --rm db psql -Utimed timed
@$(ORCHESTRATOR) compose exec -it db psql -Utimed timed

.PHONY: shellplus
shellplus: ## Run shell_plus
Expand Down
6 changes: 4 additions & 2 deletions backend/timed/projects/factories.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Factories for testing the projects app."""

from factory import Faker, SubFactory
from factory import Faker, Sequence, SubFactory
from factory.django import DjangoModelFactory

from timed.projects import models
Expand All @@ -22,7 +22,9 @@ class Meta:


class BillingTypeFactory(DjangoModelFactory):
name = Faker("currency_name")
# name is unique, therefore don't use a "traditional" faker, but
# something guaranteed distinct
name = Sequence(lambda x: f"Billing Type {x}")
reference = None

class Meta:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ def test_update_project_expenditure(
redmine_instance.issue.get.assert_called_once_with(1000)
assert issue.estimated_hours == project.estimated_time.total_seconds() / 3600
assert issue.custom_fields[0]["value"] == offered
assert issue.custom_fields[1]["value"] == project.amount_invoiced.amount
# Model has Decimal, JSON does only float
assert issue.custom_fields[1]["value"] == float(project.amount_invoiced.amount)
issue.save.assert_called_once_with()
else:
out, _ = capsys.readouterr()
Expand Down

Large diffs are not rendered by default.

43 changes: 40 additions & 3 deletions backend/timed/reports/tests/test_task_statistic.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def test_task_statistic_list(

@pytest.mark.parametrize(
("filter", "expected_result"),
[("from_date", 5), ("customer", 3), ("cost_center", 3), ("reviewer", 3)],
[("from_date", 6), ("customer", 3), ("cost_center", 4), ("reviewer", 3)],
)
def test_task_statistic_filtered(
auth_client,
Expand All @@ -118,12 +118,12 @@ def test_task_statistic_filtered(

ReportFactory.create(duration=timedelta(hours=1), date="2022-08-05", task=task_test)
ReportFactory.create(duration=timedelta(hours=2), date="2022-08-30", task=task_test)
ReportFactory.create(duration=timedelta(hours=3), date="2022-09-01", task=task_z)
ReportFactory.create(duration=timedelta(hours=4), date="2022-09-01", task=task_z)

filter_values = {
"from_date": "2022-08-20", # last two reports
"customer": str(task_test.project.customer.pk), # first two
"cost_center": str(cost_center.pk), # first two
"cost_center": str(cost_center.pk), # last one
"reviewer": str(reviewer.user.pk), # first two
}
the_filter = {filter: filter_values[filter]}
Expand All @@ -138,3 +138,40 @@ def test_task_statistic_filtered(
json = result.json()

assert json["meta"]["total-time"] == f"{expected_result:02}:00:00"


def test_task_statistic_multiple_filters(
auth_client,
setup_customer_and_employment_status,
):
user = auth_client.user
setup_customer_and_employment_status(
user=user,
is_assignee=True,
is_customer=True,
is_employed=True,
is_external=False,
)

cost_center = CostCenterFactory()
task_z = TaskFactory.create(name="Z", cost_center=cost_center)
task_test = TaskFactory.create(name="Test")
reviewer = TaskAssigneeFactory(user=UserFactory(), task=task_test, is_reviewer=True)

ReportFactory.create(duration=timedelta(hours=1), date="2022-08-05", task=task_test)
ReportFactory.create(duration=timedelta(hours=2), date="2022-08-30", task=task_test)
ReportFactory.create(duration=timedelta(hours=4), date="2022-09-01", task=task_z)

url = reverse("task-statistic-list")
result = auth_client.get(
url,
data={
"ordering": "name",
"include": "project,project.customer",
"from_date": "2022-08-20",
"reviewer": str(reviewer.user.pk),
},
)
assert result.status_code == status.HTTP_200_OK
json = result.json()
assert json["meta"]["total-time"] == "02:00:00"
14 changes: 11 additions & 3 deletions backend/timed/reports/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from zipfile import ZipFile

from django.conf import settings
from django.db.models import F, Q, QuerySet, Sum
from django.db.models import Exists, F, OuterRef, Q, QuerySet, Sum
from django.db.models.functions import ExtractMonth, ExtractYear
from django.http import HttpResponse
from django.utils.http import content_disposition_header
Expand Down Expand Up @@ -100,6 +100,9 @@ def __init__(self, catch_prefixes, *args, base_qs=None, agg_filters=None, **kwar
self._catch_prefixes = catch_prefixes

def filter(self, /, **kwargs):
# TODO: we might need a more intelligent method to decide which
# filter to apply where. Some may need to be applied in both "main" QS
# and the SUM call (applied later)
my_filters = {
k: v for k, v in kwargs.items() if not k.startswith(self._catch_prefixes)
}
Expand All @@ -117,9 +120,14 @@ def filter(self, /, **kwargs):
return new_qs

def filter_base(self, *args, **kwargs):
filtered = (
self.model.objects.filter(*args, **kwargs)
.values("pk")
.filter(pk=OuterRef("pk"))
)
return StatisticQueryset(
model=self.model,
base_qs=self._base.filter(*args, **kwargs),
base_qs=self._base.filter(Exists(filtered)),
catch_prefixes=self._catch_prefixes,
agg_filters=self._agg_filters,
)
Expand Down Expand Up @@ -218,7 +226,7 @@ class TaskStatisticViewSet(AggregateQuerysetMixin, ReadOnlyModelViewSet):
)

def get_queryset(self):
return StatisticQueryset(model=Task, catch_prefixes="tasks__")
return StatisticQueryset(model=Task, catch_prefixes="reports__")


class UserStatisticViewSet(AggregateQuerysetMixin, ReadOnlyModelViewSet):
Expand Down
12 changes: 6 additions & 6 deletions backend/timed/tracking/tests/__snapshots__/test_report.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@


* Task
[old] Allen Inc > Cross-platform content-based synergy > and Sons
[new] Allen Inc > Cross-platform content-based synergy > LLC
[old] Allen Inc > Synchronized impactful attitude > and Sons
[new] Allen Inc > Synchronized impactful attitude > LLC

* Comment
[old] foo
Expand All @@ -28,14 +28,14 @@
Comment: some other comment

* Task
[old] Allen Inc > Cross-platform content-based synergy > Ltd
[new] Allen Inc > Cross-platform content-based synergy > LLC
[old] Allen Inc > Synchronized impactful attitude > Ltd
[new] Allen Inc > Synchronized impactful attitude > LLC

---

Date: 20 April 2005
Duration: 0:15 (h:mm)
Task: Allen Inc > Cross-platform content-based synergy > LLC
Task: Allen Inc > Synchronized impactful attitude > LLC
Comment: some other comment

* Not_Billable
Expand All @@ -46,7 +46,7 @@

Date: 23 March 2016
Duration: 1:00 (h:mm)
Task: Allen Inc > Cross-platform content-based synergy > LLC
Task: Allen Inc > Synchronized impactful attitude > LLC


* Comment
Expand Down