-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Implementation of the feature #6642 Calendar/Kanban View for Individual User across the Projects #8588
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: preview
Are you sure you want to change the base?
Implementation of the feature #6642 Calendar/Kanban View for Individual User across the Projects #8588
Changes from all commits
cda1db0
a0f7cb9
ab68722
3a8c9d4
6781620
e161b0d
163d1c0
e21bcee
55361e1
4bbd88d
e90d7fc
10a85c9
c77db06
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,6 +38,12 @@ | |
| ) | ||
| from plane.utils.issue_filters import issue_filters | ||
| from plane.utils.order_queryset import order_issue_queryset | ||
| from plane.utils.grouper import ( | ||
| issue_group_values, | ||
| issue_on_results, | ||
| issue_queryset_grouper, | ||
| ) | ||
| from plane.utils.paginator import GroupedOffsetPaginator, SubGroupedOffsetPaginator | ||
| from plane.bgtasks.recent_visited_task import recent_visited_task | ||
| from .. import BaseViewSet | ||
| from plane.db.models import UserFavorite | ||
|
|
@@ -132,6 +138,16 @@ def destroy(self, request, slug, pk): | |
|
|
||
|
|
||
| class WorkspaceViewIssuesViewSet(BaseViewSet): | ||
| """ | ||
| ViewSet for workspace-level issue queries with optional grouped pagination. | ||
|
|
||
| Backward Compatibility: | ||
| - Without group_by parameter: Returns flat list of issues (same as original behavior) | ||
| - With group_by parameter: Returns grouped structure with per-group pagination | ||
| - Response fields are identical in both cases, matching the original ViewIssueListSerializer | ||
| - Follows the same pattern as IssueViewSet for project-level issues | ||
| """ | ||
|
|
||
| filter_backends = (ComplexFilterBackend,) | ||
| filterset_class = IssueFilterSet | ||
|
|
||
|
|
@@ -227,9 +243,8 @@ def list(self, request, slug): | |
| # Apply project permission filters to the issue queryset | ||
| issue_queryset = issue_queryset.filter(permission_filters) | ||
|
|
||
| # Base query for the counts | ||
| total_issue_count_queryset = copy.deepcopy(issue_queryset) | ||
| total_issue_count_queryset = total_issue_count_queryset.only("id") | ||
| # Keeping a copy of the queryset before applying annotations (for counts) | ||
| filtered_issue_queryset = copy.deepcopy(issue_queryset) | ||
|
|
||
| # Apply annotations to the issue queryset | ||
| issue_queryset = self.apply_annotations(issue_queryset) | ||
|
|
@@ -239,15 +254,107 @@ def list(self, request, slug): | |
| issue_queryset=issue_queryset, order_by_param=order_by_param | ||
| ) | ||
|
|
||
| # List Paginate | ||
| return self.paginate( | ||
| order_by=order_by_param, | ||
| request=request, | ||
| queryset=issue_queryset, | ||
| on_results=lambda issues: ViewIssueListSerializer(issues, many=True).data, | ||
| total_count_queryset=total_issue_count_queryset, | ||
| # Group by | ||
| group_by = request.GET.get("group_by", False) | ||
| sub_group_by = request.GET.get("sub_group_by", False) | ||
|
|
||
| # Validate group_by and sub_group_by field names | ||
| ALLOWED_GROUP_BY_FIELDS = { | ||
| "state_id", "labels__id", "assignees__id", "issue_module__module_id", | ||
| "cycle_id", "project_id", "priority", "state__group", | ||
| "target_date", "start_date", "created_by", | ||
| } | ||
| if group_by and group_by not in ALLOWED_GROUP_BY_FIELDS: | ||
| return Response( | ||
| {"error": f"Invalid group_by field: {group_by}"}, | ||
| status=status.HTTP_400_BAD_REQUEST, | ||
| ) | ||
| if sub_group_by and sub_group_by not in ALLOWED_GROUP_BY_FIELDS: | ||
| return Response( | ||
| {"error": f"Invalid sub_group_by field: {sub_group_by}"}, | ||
| status=status.HTTP_400_BAD_REQUEST, | ||
| ) | ||
|
|
||
| # Apply grouper to issue queryset | ||
| issue_queryset = issue_queryset_grouper( | ||
| queryset=issue_queryset, group_by=group_by, sub_group_by=sub_group_by | ||
| ) | ||
|
Comment on lines
+278
to
281
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: rg -n "def issue_queryset_grouper" -A 30 apps/api/plane/app/views/view/base.pyRepository: makeplane/plane Length of output: 41 🏁 Script executed: rg -n "def issue_queryset_grouper" -A 30Repository: makeplane/plane Length of output: 4567 🏁 Script executed: rg -n "def issue_queryset_grouper" -A 100 apps/api/plane/utils/grouper.py | head -80Repository: makeplane/plane Length of output: 2965 🏁 Script executed: sed -n '270,290p' apps/api/plane/app/views/view/base.pyRepository: makeplane/plane Length of output: 879 Unconditional invocation of grouper applies unnecessary annotations when grouping is not used. The 🤖 Prompt for AI Agents |
||
|
|
||
| if group_by: | ||
| if sub_group_by: | ||
| if group_by == sub_group_by: | ||
| return Response( | ||
| {"error": "Group by and sub group by cannot have same parameters"}, | ||
| status=status.HTTP_400_BAD_REQUEST, | ||
| ) | ||
| else: | ||
| # Sub-grouped paginate | ||
| return self.paginate( | ||
| request=request, | ||
| order_by=order_by_param, | ||
| queryset=issue_queryset, | ||
| total_count_queryset=filtered_issue_queryset, | ||
| on_results=lambda issues: issue_on_results( | ||
| group_by=group_by, issues=issues, sub_group_by=sub_group_by | ||
| ), | ||
| paginator_cls=SubGroupedOffsetPaginator, | ||
| group_by_fields=issue_group_values( | ||
| field=group_by, | ||
| slug=slug, | ||
| project_id=None, | ||
| filters=filters, | ||
| queryset=filtered_issue_queryset, | ||
| ), | ||
| sub_group_by_fields=issue_group_values( | ||
| field=sub_group_by, | ||
| slug=slug, | ||
| project_id=None, | ||
| filters=filters, | ||
| queryset=filtered_issue_queryset, | ||
| ), | ||
| group_by_field_name=group_by, | ||
| sub_group_by_field_name=sub_group_by, | ||
| count_filter=Q( | ||
| archived_at__isnull=True, | ||
| is_draft=False, | ||
| ), | ||
| ) | ||
| else: | ||
| # Group paginate | ||
| return self.paginate( | ||
| request=request, | ||
| order_by=order_by_param, | ||
| queryset=issue_queryset, | ||
| total_count_queryset=filtered_issue_queryset, | ||
| on_results=lambda issues: issue_on_results( | ||
| group_by=group_by, issues=issues, sub_group_by=sub_group_by | ||
| ), | ||
| paginator_cls=GroupedOffsetPaginator, | ||
| group_by_fields=issue_group_values( | ||
| field=group_by, | ||
| slug=slug, | ||
| project_id=None, | ||
| filters=filters, | ||
| queryset=filtered_issue_queryset, | ||
| ), | ||
| group_by_field_name=group_by, | ||
| count_filter=Q( | ||
| archived_at__isnull=True, | ||
| is_draft=False, | ||
| ), | ||
| ) | ||
| else: | ||
| # List paginate (no grouping) | ||
| return self.paginate( | ||
| order_by=order_by_param, | ||
| request=request, | ||
| queryset=issue_queryset, | ||
| total_count_queryset=filtered_issue_queryset, | ||
| on_results=lambda issues: issue_on_results( | ||
| group_by=group_by, issues=issues, sub_group_by=sub_group_by | ||
| ), | ||
| ) | ||
|
|
||
|
|
||
| class IssueViewViewSet(BaseViewSet): | ||
| serializer_class = IssueViewSerializer | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.