Skip to content

Commit

Permalink
Feedback changes
Browse files Browse the repository at this point in the history
- Sort by user name instead of id in standup
- Return department on slide and user info
- Add slide order in project
- Properly handle events cache
- Add is_superuser staff in UserMe
- Fix TimeEntry types filter
- Add additional fields to UserType
- Deprecated standup fields
  • Loading branch information
thenav56 committed Aug 26, 2024
1 parent f969ea9 commit f643bca
Show file tree
Hide file tree
Showing 20 changed files with 393 additions and 60 deletions.
56 changes: 56 additions & 0 deletions apps/common/dataloaders.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,70 @@
import typing

# from asgiref.sync import sync_to_async
from django.db import models

# import datetime


# from apps.common.models import Event
# from apps.journal.models import Journal
# from django.utils.functional import cached_property
# from strawberry.dataloader import DataLoader

DjangoModel = typing.TypeVar("DjangoModel", bound=models.Model)


# -- Helper
def load_model_objects(
Model: typing.Type[DjangoModel],
keys: list[int],
) -> list[DjangoModel]:
qs = Model.objects.filter(id__in=keys)
_map = {obj.pk: obj for obj in qs}
return [_map[key] for key in keys]


# -- Common models dataloaders

# def load_user_last_working_date(keys: list[int]) -> list[datetime.date]:
# """
# WITH recursive_days AS (
# SELECT
# DATE_SUB(:input_date, INTERVAL 1 DAY) AS prev_day
# UNION ALL
# SELECT
# DATE_SUB(prev_day, INTERVAL 1 DAY)
# FROM recursive_days
# WHERE
# prev_day NOT IN (
# SELECT
# {Event.date.db_column}
# FROM {Event._meta.db_table}
# WHERE {Event.type.db_column} IN (
# {Event.Type.HOLIDAY.value},
# {Event.Type.RETREAT.value}
# )
# )
# AND DAYOFWEEK(prev_day) NOT IN (1, 7) -- 1 = Sunday, 7 = Saturday
# AND (
# SELECT COUNT(*) FROM recursive_days
# ) < %(NUMBER_OF_WORKING_DAYS_TO_SKIP + 1)s -- Stop after finding N working days
# )
# SELECT MIN(prev_day) AS date_before_n_working_days
# FROM recursive_days;
# """

# recent_user_leave_dates_qs = (
# Journal.as_leave_qs(recent_only=True)
# .filter(user__in=keys)
# .values_list('user', 'date')
# )
# print(recent_user_leave_dates_qs)
# return load_model_objects(User, keys)


class CommonLoader:
# @cached_property
# def load_user_last_working_date(self):
# return DataLoader(load_fn=sync_to_async(load_user_last_working_date))
...
20 changes: 17 additions & 3 deletions apps/common/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import datetime
import functools

from asgiref.sync import sync_to_async
from django.db import models
from django.utils import timezone

Expand Down Expand Up @@ -68,20 +69,33 @@ def get_dates(self, include_weekends=False) -> list[datetime.date]:
def get_last_working_date(
cls,
now_date: datetime.date,
skip_dates: list[datetime.date] | None = None,
offset_count: int | None = None,
) -> datetime.date: # type: ignore[reportReturnType]
# TODO: Add test
event_dates = set(cls.get_relative_event_dates())
dates_to_skip = set(cls.get_relative_event_dates())
if skip_dates:
dates_to_skip.update(skip_dates)
found_count = 0
for x in range(30): # Create a 1 month window, Should be enough
date = now_date - datetime.timedelta(days=x)
if cls.is_weekend(date) or date in event_dates:
if cls.is_weekend(date) or date in dates_to_skip:
continue
if offset_count is not None and found_count < offset_count:
found_count += 1
continue
return date

@classmethod
@sync_to_async
def aget_last_working_date(
cls,
now_date: datetime.date,
skip_dates: list[datetime.date] | None = None,
offset_count: int | None = None,
) -> datetime.date: # type: ignore[reportReturnType]
return cls.get_last_working_date(now_date, skip_dates=skip_dates, offset_count=offset_count)

@classmethod
def get_relative_events(cls) -> models.QuerySet["Event"]:
"""
Expand All @@ -94,7 +108,7 @@ def get_relative_events(cls) -> models.QuerySet["Event"]:
return cls.objects.filter(start_date__gte=start_threshold, end_date__lte=end_threshold)

@classmethod
@functools.cache
@functools.cache # TODO: URGENT! Clear this cache on events CUD
def get_relative_event_dates(cls) -> list[datetime.date]:
"""
Return list of dates with holiday relative to current date
Expand Down
29 changes: 29 additions & 0 deletions apps/journal/dataloaders.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import datetime

from asgiref.sync import sync_to_async
from django.utils import timezone
from django.utils.functional import cached_property
from strawberry.dataloader import DataLoader

Expand Down Expand Up @@ -39,6 +40,26 @@ def load_user_work_from_home(keys: list[tuple[int, datetime.date]]) -> list[Jour
return [_map.get(key) for key in keys]


def load_user_leave_today(keys: list[int]) -> list[Journal.LeaveType | None]:
qs = Journal.objects.filter(
user__in=keys,
date=timezone.now().date(),
).values_list("user_id", "leave_type")

_map = {user_id: leave_type for user_id, leave_type in qs}
return [_map.get(key) for key in keys]


def load_user_work_from_home_today(keys: list[int]) -> list[Journal.WorkFromHomeType | None]:
qs = Journal.objects.filter(
user__in=keys,
date=timezone.now().date(),
).values_list("user_id", "wfh_type")

_map = {user_id: wfh_type for user_id, wfh_type in qs}
return [_map.get(key) for key in keys]


class JournalDataLoader:
@cached_property
def load_user_leave(self):
Expand All @@ -47,3 +68,11 @@ def load_user_leave(self):
@cached_property
def load_user_work_from_home(self):
return DataLoader(load_fn=sync_to_async(load_user_work_from_home))

@cached_property
def load_user_leave_today(self):
return DataLoader(load_fn=sync_to_async(load_user_leave_today))

@cached_property
def load_user_work_from_home_today(self):
return DataLoader(load_fn=sync_to_async(load_user_work_from_home_today))
18 changes: 18 additions & 0 deletions apps/journal/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import datetime

from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from apps.user.models import User
Expand Down Expand Up @@ -49,6 +52,21 @@ class Meta: # type: ignore[reportIncompatibleVariableOverride]
models.Index(fields=["date"]),
]

@classmethod
def as_leave_qs(cls, recent_only=False) -> models.QuerySet["Journal"]:
"""
Return a Journal queryset with pre-applied leave filters
"""
qs = Journal.objects.filter(
leave_type__in=[
Journal.LeaveType.FULL,
Journal.LeaveType.FIRST_HALF,
],
)
if recent_only:
return qs.filter(date__gte=timezone.now() - datetime.timedelta(days=30))
return qs

def __str__(self):
return f"{self.user_id}#{self.date}"

Expand Down
2 changes: 1 addition & 1 deletion apps/project/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ class ProjectAdmin(PreventDeleteAdminMixin, VersionAdmin, UserResourceAdmin):
AutocompleteFilterFactory("Contractor", "contractor"),
)

list_display = ("name",)
list_display = ("name", "slide_order")
18 changes: 18 additions & 0 deletions apps/project/migrations/0006_project_slide_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.15 on 2024-08-26 12:01

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("project", "0005_project_is_archived_deadline"),
]

operations = [
migrations.AddField(
model_name="project",
name="slide_order",
field=models.PositiveSmallIntegerField(default=0, help_text="Used to order projects in daily stand-up slides"),
),
]
27 changes: 27 additions & 0 deletions apps/project/migrations/0007_project_logo_hd_alter_project_logo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 4.2.15 on 2024-08-26 12:32

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("project", "0006_project_slide_order"),
]

operations = [
migrations.AddField(
model_name="project",
name="logo_hd",
field=models.ImageField(
blank=True, help_text="Hight quality logo", max_length=255, null=True, upload_to="project/logo-hd/"
),
),
migrations.AlterField(
model_name="project",
name="logo",
field=models.ImageField(
blank=True, help_text="Low quality logo.", max_length=255, null=True, upload_to="project/logo/"
),
),
]
14 changes: 14 additions & 0 deletions apps/project/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.db import models
from django.utils.translation import gettext_lazy as _

from apps.common.models import UserResource

Expand All @@ -20,8 +21,17 @@ def __str__(self):
class Project(UserResource):
name = models.CharField(max_length=225)
description = models.TextField(blank=True)
# TODO: Validate image size for optimal performance
logo = models.ImageField(
upload_to="project/logo/",
help_text="Low quality logo.",
max_length=255,
blank=True,
null=True,
)
logo_hd = models.ImageField(
upload_to="project/logo-hd/",
help_text="Hight quality logo",
max_length=255,
blank=True,
null=True,
Expand All @@ -32,6 +42,10 @@ class Project(UserResource):
project_client = models.ForeignKey(Client, on_delete=models.PROTECT, related_name="projects")
contractor = models.ForeignKey(Contractor, on_delete=models.PROTECT, related_name="projects")
is_archived = models.BooleanField(default=False)
slide_order = models.PositiveSmallIntegerField(
default=0,
help_text=_("Used to order projects in daily stand-up slides"),
)

project_client_id: int
contractor_id: int
Expand Down
2 changes: 1 addition & 1 deletion apps/project/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class PrivateQuery:
# Unbound ----------------------------
@strawberry_django.field
async def all_projects(self, info: Info) -> list[ProjectType]:
qs = ProjectType.get_queryset(None, None, info).filter(is_archived=False).all()
qs = ProjectType.get_queryset(None, None, info).filter(is_archived=False).order_by("slide_order").all()
return [project async for project in qs]

@strawberry_django.field
Expand Down
2 changes: 2 additions & 0 deletions apps/project/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ async def remaining_days(self, root: strawberry.Parent[Deadline]) -> int:
class ProjectType(UserResourceTypeMixin):
id: strawberry.ID
logo: strawberry.auto
logo_hd: strawberry.auto
slide_order: strawberry.auto
project_client_id: strawberry.ID
contractor_id: strawberry.ID

Expand Down
Loading

0 comments on commit f643bca

Please sign in to comment.