chore(deps): upgrade psycopg packages to version 3.3.0 (#8222)

* chore(deps): upgrade psycopg packages to version 3.3.0

* chore: update Python version to 3.12.x in CI workflow

* refactor: clean up imports and improve code formatting across multiple files
This commit is contained in:
Nikhil
2025-12-05 16:06:19 +05:30
committed by GitHub
parent 85d90030cf
commit f81e120328
9 changed files with 47 additions and 87 deletions

View File

@@ -31,7 +31,7 @@ jobs:
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: "3.x" python-version: "3.12.x"
- name: Install Pylint - name: Install Pylint
run: python -m pip install ruff run: python -m pip install ruff
- name: Install API Dependencies - name: Install API Dependencies

View File

@@ -1,6 +1,10 @@
from django.urls import path from django.urls import path
from plane.api.views import ProjectMemberListCreateAPIEndpoint, ProjectMemberDetailAPIEndpoint, WorkspaceMemberAPIEndpoint from plane.api.views import (
ProjectMemberListCreateAPIEndpoint,
ProjectMemberDetailAPIEndpoint,
WorkspaceMemberAPIEndpoint,
)
urlpatterns = [ urlpatterns = [
# Project members # Project members

View File

@@ -13,8 +13,6 @@ from django.utils import timezone
from rest_framework import status from rest_framework import status
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from rest_framework.exceptions import APIException from rest_framework.exceptions import APIException
from rest_framework.generics import GenericAPIView from rest_framework.generics import GenericAPIView

View File

@@ -65,9 +65,7 @@ from plane.utils.openapi import (
ADMIN_ONLY_RESPONSE, ADMIN_ONLY_RESPONSE,
REQUIRED_FIELDS_RESPONSE, REQUIRED_FIELDS_RESPONSE,
MODULE_ISSUE_NOT_FOUND_RESPONSE, MODULE_ISSUE_NOT_FOUND_RESPONSE,
ARCHIVED_RESPONSE,
CANNOT_ARCHIVE_RESPONSE, CANNOT_ARCHIVE_RESPONSE,
UNARCHIVED_RESPONSE,
) )

View File

@@ -129,9 +129,7 @@ class GlobalSearchEndpoint(BaseAPIView):
return ( return (
cycles.order_by("-created_at") cycles.order_by("-created_at")
.distinct() .distinct()
.values( .values("name", "id", "project_id", "project__identifier", "workspace__slug")
"name", "id", "project_id", "project__identifier", "workspace__slug"
)
) )
def filter_modules(self, query, slug, project_id, workspace_search): def filter_modules(self, query, slug, project_id, workspace_search):
@@ -155,9 +153,7 @@ class GlobalSearchEndpoint(BaseAPIView):
return ( return (
modules.order_by("-created_at") modules.order_by("-created_at")
.distinct() .distinct()
.values( .values("name", "id", "project_id", "project__identifier", "workspace__slug")
"name", "id", "project_id", "project__identifier", "workspace__slug"
)
) )
def filter_pages(self, query, slug, project_id, workspace_search): def filter_pages(self, query, slug, project_id, workspace_search):
@@ -177,9 +173,7 @@ class GlobalSearchEndpoint(BaseAPIView):
) )
.annotate( .annotate(
project_ids=Coalesce( project_ids=Coalesce(
ArrayAgg( ArrayAgg("projects__id", distinct=True, filter=~Q(projects__id=True)),
"projects__id", distinct=True, filter=~Q(projects__id=True)
),
Value([], output_field=ArrayField(UUIDField())), Value([], output_field=ArrayField(UUIDField())),
) )
) )
@@ -196,20 +190,16 @@ class GlobalSearchEndpoint(BaseAPIView):
) )
if workspace_search == "false" and project_id: if workspace_search == "false" and project_id:
project_subquery = ProjectPage.objects.filter( project_subquery = ProjectPage.objects.filter(page_id=OuterRef("id"), project_id=project_id).values_list(
page_id=OuterRef("id"), project_id=project_id "project_id", flat=True
).values_list("project_id", flat=True)[:1] )[:1]
pages = pages.annotate(project_id=Subquery(project_subquery)).filter( pages = pages.annotate(project_id=Subquery(project_subquery)).filter(project_id=project_id)
project_id=project_id
)
return ( return (
pages.order_by("-created_at") pages.order_by("-created_at")
.distinct() .distinct()
.values( .values("name", "id", "project_ids", "project_identifiers", "workspace__slug")
"name", "id", "project_ids", "project_identifiers", "workspace__slug"
)
) )
def filter_views(self, query, slug, project_id, workspace_search): def filter_views(self, query, slug, project_id, workspace_search):
@@ -233,9 +223,7 @@ class GlobalSearchEndpoint(BaseAPIView):
return ( return (
issue_views.order_by("-created_at") issue_views.order_by("-created_at")
.distinct() .distinct()
.values( .values("name", "id", "project_id", "project__identifier", "workspace__slug")
"name", "id", "project_id", "project__identifier", "workspace__slug"
)
) )
def filter_intakes(self, query, slug, project_id, workspace_search): def filter_intakes(self, query, slug, project_id, workspace_search):
@@ -294,9 +282,7 @@ class GlobalSearchEndpoint(BaseAPIView):
# Determine which entities to search # Determine which entities to search
if entities_param: if entities_param:
requested_entities = [ requested_entities = [e.strip() for e in entities_param.split(",") if e.strip()]
e.strip() for e in entities_param.split(",") if e.strip()
]
requested_entities = [e for e in requested_entities if e in MODELS_MAPPER] requested_entities = [e for e in requested_entities if e in MODELS_MAPPER]
else: else:
requested_entities = list(MODELS_MAPPER.keys()) requested_entities = list(MODELS_MAPPER.keys())
@@ -306,9 +292,7 @@ class GlobalSearchEndpoint(BaseAPIView):
for entity in requested_entities: for entity in requested_entities:
func = MODELS_MAPPER.get(entity) func = MODELS_MAPPER.get(entity)
if func: if func:
results[entity] = func( results[entity] = func(query or None, slug, project_id, workspace_search)
query or None, slug, project_id, workspace_search
)
return Response({"results": results}, status=status.HTTP_200_OK) return Response({"results": results}, status=status.HTTP_200_OK)
@@ -320,7 +304,6 @@ class SearchEndpoint(BaseAPIView):
query_types = [qt.strip() for qt in query_types] query_types = [qt.strip() for qt in query_types]
count = int(request.query_params.get("count", 5)) count = int(request.query_params.get("count", 5))
project_id = request.query_params.get("project_id", None) project_id = request.query_params.get("project_id", None)
issue_id = request.query_params.get("issue_id", None)
response_data = {} response_data = {}
@@ -367,14 +350,10 @@ class SearchEndpoint(BaseAPIView):
.order_by("-created_at") .order_by("-created_at")
) )
users = ( users = users.distinct().values(
users "member__avatar_url",
.distinct() "member__display_name",
.values( "member__id",
"member__avatar_url",
"member__display_name",
"member__id",
)
) )
response_data["user_mention"] = list(users[:count]) response_data["user_mention"] = list(users[:count])
@@ -389,15 +368,12 @@ class SearchEndpoint(BaseAPIView):
projects = ( projects = (
Project.objects.filter( Project.objects.filter(
q, q,
Q(project_projectmember__member=self.request.user) Q(project_projectmember__member=self.request.user) | Q(network=2),
| Q(network=2),
workspace__slug=slug, workspace__slug=slug,
) )
.order_by("-created_at") .order_by("-created_at")
.distinct() .distinct()
.values( .values("name", "id", "identifier", "logo_props", "workspace__slug")[:count]
"name", "id", "identifier", "logo_props", "workspace__slug"
)[:count]
) )
response_data["project"] = list(projects) response_data["project"] = list(projects)
@@ -456,20 +432,16 @@ class SearchEndpoint(BaseAPIView):
.annotate( .annotate(
status=Case( status=Case(
When( When(
Q(start_date__lte=timezone.now()) Q(start_date__lte=timezone.now()) & Q(end_date__gte=timezone.now()),
& Q(end_date__gte=timezone.now()),
then=Value("CURRENT"), then=Value("CURRENT"),
), ),
When( When(
start_date__gt=timezone.now(), start_date__gt=timezone.now(),
then=Value("UPCOMING"), then=Value("UPCOMING"),
), ),
When(end_date__lt=timezone.now(), then=Value("COMPLETED")),
When( When(
end_date__lt=timezone.now(), then=Value("COMPLETED") Q(start_date__isnull=True) & Q(end_date__isnull=True),
),
When(
Q(start_date__isnull=True)
& Q(end_date__isnull=True),
then=Value("DRAFT"), then=Value("DRAFT"),
), ),
default=Value("DRAFT"), default=Value("DRAFT"),
@@ -587,9 +559,7 @@ class SearchEndpoint(BaseAPIView):
) )
) )
.order_by("-created_at") .order_by("-created_at")
.values( .values("member__avatar_url", "member__display_name", "member__id")[:count]
"member__avatar_url", "member__display_name", "member__id"
)[:count]
) )
response_data["user_mention"] = list(users) response_data["user_mention"] = list(users)
@@ -603,15 +573,12 @@ class SearchEndpoint(BaseAPIView):
projects = ( projects = (
Project.objects.filter( Project.objects.filter(
q, q,
Q(project_projectmember__member=self.request.user) Q(project_projectmember__member=self.request.user) | Q(network=2),
| Q(network=2),
workspace__slug=slug, workspace__slug=slug,
) )
.order_by("-created_at") .order_by("-created_at")
.distinct() .distinct()
.values( .values("name", "id", "identifier", "logo_props", "workspace__slug")[:count]
"name", "id", "identifier", "logo_props", "workspace__slug"
)[:count]
) )
response_data["project"] = list(projects) response_data["project"] = list(projects)
@@ -668,20 +635,16 @@ class SearchEndpoint(BaseAPIView):
.annotate( .annotate(
status=Case( status=Case(
When( When(
Q(start_date__lte=timezone.now()) Q(start_date__lte=timezone.now()) & Q(end_date__gte=timezone.now()),
& Q(end_date__gte=timezone.now()),
then=Value("CURRENT"), then=Value("CURRENT"),
), ),
When( When(
start_date__gt=timezone.now(), start_date__gt=timezone.now(),
then=Value("UPCOMING"), then=Value("UPCOMING"),
), ),
When(end_date__lt=timezone.now(), then=Value("COMPLETED")),
When( When(
end_date__lt=timezone.now(), then=Value("COMPLETED") Q(start_date__isnull=True) & Q(end_date__isnull=True),
),
When(
Q(start_date__isnull=True)
& Q(end_date__isnull=True),
then=Value("DRAFT"), then=Value("DRAFT"),
), ),
default=Value("DRAFT"), default=Value("DRAFT"),

View File

@@ -210,7 +210,7 @@ class UserEndpoint(BaseViewSet):
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
except Exception as e: except Exception:
return Response( return Response(
{"error": "Failed to verify code. Please try again."}, {"error": "Failed to verify code. Please try again."},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,

View File

@@ -2,7 +2,6 @@
import io import io
import zipfile import zipfile
from typing import List from typing import List
from collections import defaultdict
import boto3 import boto3
from botocore.client import Config from botocore.client import Config
from uuid import UUID from uuid import UUID

View File

@@ -1,8 +1,7 @@
import pytest import pytest
from rest_framework import status from rest_framework import status
from django.db import IntegrityError
from django.utils import timezone from django.utils import timezone
from datetime import datetime, timedelta from datetime import timedelta
from uuid import uuid4 from uuid import uuid4
from plane.db.models import Cycle, Project, ProjectMember from plane.db.models import Cycle, Project, ProjectMember
@@ -58,8 +57,6 @@ def create_cycle(db, project, create_user):
) )
@pytest.mark.contract @pytest.mark.contract
class TestCycleListCreateAPIEndpoint: class TestCycleListCreateAPIEndpoint:
"""Test Cycle List and Create API Endpoint""" """Test Cycle List and Create API Endpoint"""
@@ -85,7 +82,6 @@ class TestCycleListCreateAPIEndpoint:
assert created_cycle.project == project assert created_cycle.project == project
assert created_cycle.owned_by_id is not None assert created_cycle.owned_by_id is not None
@pytest.mark.django_db @pytest.mark.django_db
def test_create_cycle_invalid_data(self, api_key_client, workspace, project): def test_create_cycle_invalid_data(self, api_key_client, workspace, project):
"""Test cycle creation with invalid data""" """Test cycle creation with invalid data"""
@@ -197,7 +193,7 @@ class TestCycleListCreateAPIEndpoint:
# Create cycles in different states # Create cycles in different states
now = timezone.now() now = timezone.now()
# Current cycle (started but not ended) # Current cycle (started but not ended)
Cycle.objects.create( Cycle.objects.create(
name="Current Cycle", name="Current Cycle",
@@ -207,7 +203,7 @@ class TestCycleListCreateAPIEndpoint:
end_date=now + timedelta(days=6), end_date=now + timedelta(days=6),
owned_by=create_user, owned_by=create_user,
) )
# Upcoming cycle # Upcoming cycle
Cycle.objects.create( Cycle.objects.create(
name="Upcoming Cycle", name="Upcoming Cycle",
@@ -217,7 +213,7 @@ class TestCycleListCreateAPIEndpoint:
end_date=now + timedelta(days=8), end_date=now + timedelta(days=8),
owned_by=create_user, owned_by=create_user,
) )
# Completed cycle # Completed cycle
Cycle.objects.create( Cycle.objects.create(
name="Completed Cycle", name="Completed Cycle",
@@ -227,7 +223,7 @@ class TestCycleListCreateAPIEndpoint:
end_date=now - timedelta(days=3), end_date=now - timedelta(days=3),
owned_by=create_user, owned_by=create_user,
) )
# Draft cycle # Draft cycle
Cycle.objects.create( Cycle.objects.create(
name="Draft Cycle", name="Draft Cycle",
@@ -320,7 +316,9 @@ class TestCycleDetailAPIEndpoint:
assert response.status_code in [status.HTTP_400_BAD_REQUEST, status.HTTP_200_OK] assert response.status_code in [status.HTTP_400_BAD_REQUEST, status.HTTP_200_OK]
@pytest.mark.django_db @pytest.mark.django_db
def test_update_cycle_with_external_id_conflict(self, api_key_client, workspace, project, create_cycle, create_user ): def test_update_cycle_with_external_id_conflict(
self, api_key_client, workspace, project, create_cycle, create_user
):
"""Test cycle update with conflicting external ID""" """Test cycle update with conflicting external ID"""
url = self.get_cycle_detail_url(workspace.slug, project.id, create_cycle.id) url = self.get_cycle_detail_url(workspace.slug, project.id, create_cycle.id)
@@ -363,7 +361,7 @@ class TestCycleDetailAPIEndpoint:
response = api_key_client.get(url) response = api_key_client.get(url)
assert response.status_code == status.HTTP_200_OK assert response.status_code == status.HTTP_200_OK
# Check that metrics are included in response # Check that metrics are included in response
cycle_data = response.data cycle_data = response.data
assert "total_issues" in cycle_data assert "total_issues" in cycle_data
@@ -372,11 +370,11 @@ class TestCycleDetailAPIEndpoint:
assert "started_issues" in cycle_data assert "started_issues" in cycle_data
assert "unstarted_issues" in cycle_data assert "unstarted_issues" in cycle_data
assert "backlog_issues" in cycle_data assert "backlog_issues" in cycle_data
# All should be 0 for a new cycle # All should be 0 for a new cycle
assert cycle_data["total_issues"] == 0 assert cycle_data["total_issues"] == 0
assert cycle_data["completed_issues"] == 0 assert cycle_data["completed_issues"] == 0
assert cycle_data["cancelled_issues"] == 0 assert cycle_data["cancelled_issues"] == 0
assert cycle_data["started_issues"] == 0 assert cycle_data["started_issues"] == 0
assert cycle_data["unstarted_issues"] == 0 assert cycle_data["unstarted_issues"] == 0
assert cycle_data["backlog_issues"] == 0 assert cycle_data["backlog_issues"] == 0

View File

@@ -5,9 +5,9 @@ Django==4.2.27
# rest framework # rest framework
djangorestframework==3.15.2 djangorestframework==3.15.2
# postgres # postgres
psycopg==3.2.9 psycopg==3.3.0
psycopg-binary==3.2.9 psycopg-binary==3.3.0
psycopg-c==3.2.9 psycopg-c==3.3.0
dj-database-url==2.1.0 dj-database-url==2.1.0
# mongo # mongo
pymongo==4.6.3 pymongo==4.6.3