Skip to content

Commit

Permalink
Merge branch 'dev' into maxim/#1906-Unify-breadcrumbs-behaviour-with-…
Browse files Browse the repository at this point in the history
…other-Grafana-Apps
  • Loading branch information
Maxim committed Sep 21, 2023
2 parents d291d4c + 2848836 commit 835e261
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 33 deletions.
12 changes: 6 additions & 6 deletions docs/docs.mk
Original file line number Diff line number Diff line change
Expand Up @@ -80,35 +80,35 @@ docs-pull: ## Pull documentation base image.

make-docs: ## Fetch the latest make-docs script.
make-docs:
if [[ ! -f "$(PWD)/make-docs" ]]; then
if [[ ! -f "$(CURDIR)/make-docs" ]]; then
echo 'WARN: No make-docs script found in the working directory. Run `make update` to download it.' >&2
exit 1
fi

.PHONY: docs
docs: ## Serve documentation locally, which includes pulling the latest `DOCS_IMAGE` (default: `grafana/docs-base:latest`) container image. See also `docs-no-pull`.
docs: docs-pull make-docs
$(PWD)/make-docs $(PROJECTS)
$(CURDIR)/make-docs $(PROJECTS)

.PHONY: docs-no-pull
docs-no-pull: ## Serve documentation locally without pulling the `DOCS_IMAGE` (default: `grafana/docs-base:latest`) container image.
docs-no-pull: make-docs
$(PWD)/make-docs $(PROJECTS)
$(CURDIR)/make-docs $(PROJECTS)

.PHONY: docs-debug
docs-debug: ## Run Hugo web server with debugging enabled. TODO: support all SERVER_FLAGS defined in website Makefile.
docs-debug: make-docs
WEBSITE_EXEC='hugo server --bind 0.0.0.0 --port 3002 --debug' $(PWD)/make-docs $(PROJECTS)
WEBSITE_EXEC='hugo server --bind 0.0.0.0 --port 3002 --debug' $(CURDIR)/make-docs $(PROJECTS)

.PHONY: doc-validator
doc-validator: ## Run doc-validator on the entire docs folder.
doc-validator: make-docs
DOCS_IMAGE=$(DOC_VALIDATOR_IMAGE) $(PWD)/make-docs $(PROJECTS)
DOCS_IMAGE=$(DOC_VALIDATOR_IMAGE) $(CURDIR)/make-docs $(PROJECTS)

.PHONY: vale
vale: ## Run vale on the entire docs folder.
vale: make-docs
DOCS_IMAGE=$(VALE_IMAGE) $(PWD)/make-docs $(PROJECTS)
DOCS_IMAGE=$(VALE_IMAGE) $(CURDIR)/make-docs $(PROJECTS)

.PHONY: update
update: ## Fetch the latest version of this Makefile and the `make-docs` script from Writers' Toolkit.
Expand Down
2 changes: 1 addition & 1 deletion engine/apps/api/tests/test_schedules.py
Original file line number Diff line number Diff line change
Expand Up @@ -2081,7 +2081,7 @@ def test_get_schedule_on_call_now(
client = APIClient()
url = reverse("api-internal:schedule-list")
with patch(
"apps.schedules.models.on_call_schedule.OnCallScheduleQuerySet.get_oncall_users",
"apps.api.views.schedule.get_oncall_users_for_multiple_schedules",
return_value={schedule.pk: [user]},
):
response = client.get(url, format="json", **make_user_auth_headers(user, token))
Expand Down
43 changes: 23 additions & 20 deletions engine/apps/api/views/schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from apps.auth_token.constants import SCHEDULE_EXPORT_TOKEN_NAME
from apps.auth_token.models import ScheduleExportAuthToken
from apps.mobile_app.auth import MobileAppAuthTokenAuthentication
from apps.schedules.ical_utils import get_oncall_users_for_multiple_schedules
from apps.schedules.models import OnCallSchedule
from apps.slack.models import SlackChannel
from apps.slack.tasks import update_slack_user_group_for_schedules
Expand Down Expand Up @@ -136,10 +137,8 @@ def oncall_users(self):
The result of this method is cached and is reused for the whole lifetime of a request,
since self.get_serializer_context() is called multiple times for every instance in the queryset.
"""
current_page_schedules = self.paginate_queryset(self.filter_queryset(self.get_queryset()))
pks = [schedule.pk for schedule in current_page_schedules]
queryset = OnCallSchedule.objects.filter(pk__in=pks)
return queryset.get_oncall_users()
current_page_schedules = self.paginate_queryset(self.filter_queryset(self.get_queryset(annotate=False)))
return get_oncall_users_for_multiple_schedules(current_page_schedules)

def get_serializer_context(self):
context = super().get_serializer_context()
Expand Down Expand Up @@ -167,7 +166,7 @@ def _annotate_queryset(self, queryset):
)
return queryset

def get_queryset(self, ignore_filtering_by_available_teams=False):
def get_queryset(self, ignore_filtering_by_available_teams=False, annotate=True):
is_short_request = self.request.query_params.get("short", "false") == "true"
filter_by_type = self.request.query_params.getlist("type")
mine = BooleanField(allow_null=True).to_internal_value(data=self.request.query_params.get("mine"))
Expand All @@ -181,7 +180,7 @@ def get_queryset(self, ignore_filtering_by_available_teams=False):
)
if not ignore_filtering_by_available_teams:
queryset = queryset.filter(*self.available_teams_lookup_args).distinct()
if not is_short_request:
if not is_short_request or annotate:
queryset = self._annotate_queryset(queryset)
queryset = self.serializer_class.setup_eager_loading(queryset)
if filter_by_type:
Expand Down Expand Up @@ -231,15 +230,16 @@ def perform_destroy(self, instance):
if instance.user_group is not None:
update_slack_user_group_for_schedules.apply_async((instance.user_group.pk,))

def get_object(self) -> OnCallSchedule:
def get_object(self, annotate=True) -> OnCallSchedule:
# get the object from the whole organization if there is a flag `get_from_organization=true`
# otherwise get the object from the current team
get_from_organization: bool = self.request.query_params.get("from_organization", "false") == "true"
if get_from_organization:
return self.get_object_from_organization()
return super().get_object()
return self.get_object_from_organization(annotate=annotate)
queryset_kwargs = {"annotate": annotate}
return super().get_object(queryset_kwargs)

def get_object_from_organization(self, ignore_filtering_by_available_teams=False):
def get_object_from_organization(self, ignore_filtering_by_available_teams=False, annotate=True):
# use this method to get the object from the whole organization instead of the current team
pk = self.kwargs["pk"]
organization = self.request.auth.organization
Expand All @@ -248,7 +248,10 @@ def get_object_from_organization(self, ignore_filtering_by_available_teams=False
)
if not ignore_filtering_by_available_teams:
queryset = queryset.filter(*self.available_teams_lookup_args).distinct()
queryset = self._annotate_queryset(queryset)

if annotate:
queryset = self._annotate_queryset(queryset)
queryset = self.serializer_class.setup_eager_loading(queryset)

try:
obj = queryset.get()
Expand Down Expand Up @@ -283,7 +286,7 @@ def events(self, request, pk):
with_empty = self.request.query_params.get("with_empty", False) == "true"
with_gap = self.request.query_params.get("with_gap", False) == "true"

schedule = self.get_object()
schedule = self.get_object(annotate=False)

pytz_tz = pytz.timezone(user_tz)
datetime_start = datetime.datetime.combine(starting_date, datetime.time.min, tzinfo=pytz_tz)
Expand Down Expand Up @@ -319,7 +322,7 @@ def filter_events(self, request: Request, pk: str) -> Response:
raise BadRequest(detail="Invalid type value")
resolve_schedule = filter_by is None or filter_by == EVENTS_FILTER_BY_FINAL

schedule = self.get_object()
schedule = self.get_object(annotate=False)

pytz_tz = pytz.timezone(user_tz)
datetime_start = datetime.datetime.combine(starting_date, datetime.time.min, tzinfo=pytz_tz)
Expand Down Expand Up @@ -349,7 +352,7 @@ def filter_events(self, request: Request, pk: str) -> Response:
@action(detail=True, methods=["get"])
def filter_shift_swaps(self, request: Request, pk: str) -> Response:
user_tz, starting_date, days = get_date_range_from_request(self.request)
schedule = self.get_object()
schedule = self.get_object(annotate=False)

pytz_tz = pytz.timezone(user_tz)
datetime_start = datetime.datetime.combine(starting_date, datetime.time.min, tzinfo=pytz_tz)
Expand All @@ -367,7 +370,7 @@ def next_shifts_per_user(self, request, pk):
"""Return next shift for users in schedule."""
now = timezone.now()
datetime_end = now + datetime.timedelta(days=30)
schedule = self.get_object()
schedule = self.get_object(annotate=False)

events = schedule.final_events(now, datetime_end)

Expand All @@ -382,23 +385,23 @@ def next_shifts_per_user(self, request, pk):

@action(detail=True, methods=["get"])
def related_users(self, request, pk):
schedule = self.get_object()
schedule = self.get_object(annotate=False)
serializer = ScheduleUserSerializer(schedule.related_users(), many=True)
result = {"users": serializer.data}
return Response(result, status=status.HTTP_200_OK)

@action(detail=True, methods=["get"])
def related_escalation_chains(self, request, pk):
"""Return escalation chains associated to schedule."""
schedule = self.get_object()
schedule = self.get_object(annotate=True)
escalation_chains = EscalationChain.objects.filter(escalation_policies__notify_schedule=schedule).distinct()

result = [{"name": e.name, "pk": e.public_primary_key} for e in escalation_chains]
return Response(result, status=status.HTTP_200_OK)

@action(detail=True, methods=["get"])
def quality(self, request, pk):
schedule = self.get_object()
schedule = self.get_object(annotate=False)

_, date = self.get_request_timezone()
datetime_start = datetime.datetime.combine(date, datetime.time.min, tzinfo=pytz.UTC)
Expand Down Expand Up @@ -440,7 +443,7 @@ def type_options(self, request):

@action(detail=True, methods=["post"])
def reload_ical(self, request, pk):
schedule = self.get_object()
schedule = self.get_object(annotate=False)
schedule.drop_cached_ical()
schedule.check_empty_shifts_for_next_week()
schedule.check_gaps_for_next_week()
Expand All @@ -452,7 +455,7 @@ def reload_ical(self, request, pk):

@action(detail=True, methods=["get", "post", "delete"])
def export_token(self, request, pk):
schedule = self.get_object()
schedule = self.get_object(annotate=False)

if self.request.method == "GET":
try:
Expand Down
6 changes: 3 additions & 3 deletions engine/apps/schedules/ical_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,18 +366,18 @@ def list_users_to_notify_from_ical_for_period(


def get_oncall_users_for_multiple_schedules(
schedules: "OnCallScheduleQuerySet", events_datetime=None
schedules: typing.List["OnCallSchedule"], events_datetime=None
) -> typing.Dict["OnCallSchedule", UserQuerySet]:
if events_datetime is None:
events_datetime = datetime.datetime.now(timezone.utc)

# Exit early if there are no schedules
if not schedules.exists():
if not schedules:
return {}

# Get on-call users
oncall_users = {}
for schedule in schedules.all():
for schedule in schedules:
# pass user list to list_users_to_notify_from_ical
schedule_oncall_users = list_users_to_notify_from_ical(schedule, events_datetime=events_datetime)
oncall_users.update({schedule.pk: schedule_oncall_users})
Expand Down
2 changes: 1 addition & 1 deletion engine/apps/schedules/models/on_call_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ def generate_public_primary_key_for_oncall_schedule_channel():

class OnCallScheduleQuerySet(PolymorphicQuerySet):
def get_oncall_users(self, events_datetime=None):
return get_oncall_users_for_multiple_schedules(self, events_datetime)
return get_oncall_users_for_multiple_schedules(self.all(), events_datetime)

def related_to_user(self, user):
username_regex = RE_ICAL_SEARCH_USERNAME.format(user.username)
Expand Down
6 changes: 4 additions & 2 deletions engine/common/api_helpers/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,11 @@ def handle_exception(self, exc):


class PublicPrimaryKeyMixin(typing.Generic[_MT]):
def get_object(self) -> _MT:
def get_object(self, queryset_kwargs=None) -> _MT:
pk = self.kwargs["pk"]
queryset = self.filter_queryset(self.get_queryset())
if queryset_kwargs is None:
queryset_kwargs = {}
queryset = self.filter_queryset(self.get_queryset(**queryset_kwargs))

try:
obj = queryset.get(public_primary_key=pk)
Expand Down

0 comments on commit 835e261

Please sign in to comment.