Skip to content
Open
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
4 changes: 0 additions & 4 deletions backend/apps/ai/common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ def create_chunks_and_embeddings(
ValueError: If context is None or invalid

"""
from apps.ai.models.chunk import Chunk

try:
last_request_time = datetime.now(UTC) - timedelta(
seconds=DEFAULT_LAST_REQUEST_OFFSET_SECONDS
Expand Down Expand Up @@ -77,8 +75,6 @@ def regenerate_chunks_for_context(context: Context):
context (Context): The specific context instance to be updated.

"""
from apps.ai.models.chunk import Chunk

context.chunks.all().delete()
new_chunk_texts = Chunk.split_text(context.content)

Expand Down
51 changes: 50 additions & 1 deletion backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ lint.ignore = [
"D407", # https://docs.astral.sh/ruff/rules/missing-dashed-underline-after-section/
"DJ012", # https://docs.astral.sh/ruff/rules/django-unordered-body-content-in-model/
"FIX002", # https://docs.astral.sh/ruff/rules/line-contains-todo/
"PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/
"PLR0912", # https://docs.astral.sh/ruff/rules/too-many-branches/
"PLR0913", # https://docs.astral.sh/ruff/rules/too-many-arguments/
"PLR0915", # https://docs.astral.sh/ruff/rules/too-many-statements/
Expand Down Expand Up @@ -120,6 +119,56 @@ lint.per-file-ignores."**/tests/**/*.py" = [
"SLF001", # https://docs.astral.sh/ruff/rules/private-member-access/
]

# specific files that need PLC0415 ignored
lint.per-file-ignores."apps/github/models/mixins/organization.py" = [
"PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/
]
lint.per-file-ignores."apps/github/models/mixins/user.py" = [
"PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/
]
lint.per-file-ignores."apps/mentorship/apps.py" = [
"PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/
]
lint.per-file-ignores."apps/slack/apps.py" = [
"PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/
]
lint.per-file-ignores."apps/slack/commands/events.py" = [
"PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/
]
lint.per-file-ignores."apps/slack/commands/leaders.py" = [
"PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/
]
lint.per-file-ignores."apps/slack/common/handlers/chapters.py" = [
"PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/
]
lint.per-file-ignores."apps/slack/common/handlers/committees.py" = [
"PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/
]
lint.per-file-ignores."apps/slack/common/handlers/contribute.py" = [
"PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/
]
lint.per-file-ignores."apps/slack/common/handlers/projects.py" = [
"PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/
]
lint.per-file-ignores."apps/slack/common/handlers/users.py" = [
"PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/
]
lint.per-file-ignores."apps/slack/events/member_joined_channel/contribute.py" = [
"PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/
]
lint.per-file-ignores."apps/slack/utils.py" = [
"PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/
]
Comment on lines +122 to +161
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

🧩 Analysis chain

LGTM: targeted PLC0415 per-file ignores look correct.

Entries use valid Ruff v2-style keys (lint.per-file-ignores."...") and are scoped to concrete files, aligning with the objective to remove the global ignore. No nonessential formatting changes observed.

Please confirm:

  • All remaining PLC0415 occurrences were either refactored (imports moved to top-level) or are covered by these per-file entries.
  • No overlapping E402 violations remain (Ruff can also flag top-level import placement via E402). If any exist, consider aligning on a single rule to suppress.

You can run the following repo-scoped reconnaissance to spot non top-level imports that might have been missed (heuristic; will surface imports inside defs/classes):


🏁 Script executed:

#!/bin/bash
# Heuristic scan for indented imports (potential PLC0415 sites)
rg -nP -C2 '^\s{2,}(?:from\s+\S+\s+import\b|import\s+\S+)\b' -g '!**/migrations/**'

Length of output: 148


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== repo root: $(pwd) ==="
echo "=== git status (short) ==="
git rev-parse --abbrev-ref HEAD || true
git status --porcelain --untracked-files=no || true

echo
echo "=== Searching for literal 'PLC0415' ==="
rg -n --hidden -uu 'PLC0415' || true

echo
echo "=== Searching for literal 'E402' ==="
rg -n --hidden -uu '\bE402\b' || true

echo
echo "=== Heuristic scan for indented imports (exclude migrations) ==="
rg -nP -C2 '^\s{2,}(?:from\s+\S+\s+import\b|import\s+\S+)\b' --hidden -uu --glob '!**/migrations/**' || true

echo
echo "=== Show backend/pyproject.toml (first 300 lines) ==="
if [ -f backend/pyproject.toml ]; then
  sed -n '1,300p' backend/pyproject.toml
else
  echo "backend/pyproject.toml not found"
fi

Length of output: 10422


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== repo root: $(pwd) ==="
echo

echo "=== ruff.toml (first 200 lines) ==="
if [ -f ruff.toml ]; then sed -n '1,200p' ruff.toml; else echo "ruff.toml not found"; fi
echo

echo "=== backend/pyproject.toml (first 300 lines) ==="
if [ -f backend/pyproject.toml ]; then sed -n '1,300p' backend/pyproject.toml; else echo "backend/pyproject.toml not found"; fi
echo

echo "=== Grep for literal 'PLC0415' and 'E402' ==="
rg -n --hidden -uu 'PLC0415' || true
rg -n --hidden -uu '\bE402\b' || true
echo

echo "=== Scanning .py files for indented imports (heuristic for PLC0415/E402) ==="
find . -type f -name '*.py' \
  -not -path '*/.git/*' \
  -not -path '*/migrations/*' \
  -not -path '*/.venv/*' \
  -not -path '*/venv/*' \
  -not -path '*/node_modules/*' \
  -print0 |
while IFS= read -r -d '' file; do
  rg -nP '^\s{2,}(?:from\s+\S+\s+import\b|import\s+\S+)' -S -H "$file" || true
done

echo
echo "=== Done ==="

Length of output: 14854


LGTM — per-file PLC0415 ignores look correct, but uncovered PLC0415 sites remain

Repo scan shows additional indented imports not covered by backend/pyproject.toml; move these imports to module top-level or add PLC0415 per-file ignores for the following files:

  • backend/apps/github/api/internal/nodes/repository.py
  • backend/apps/github/models/organization.py
  • backend/apps/nest/models/user.py
  • backend/apps/owasp/models/event.py
  • backend/apps/slack/models/conversation.py
  • backend/manage.py
  • backend/settings/urls.py

Note: ruff.toml currently ignores E402 for **/init.py and **/conftest.py — consider aligning E402 vs PLC0415 suppression to avoid overlapping rules.

lint.per-file-ignores."tests/apps/ai/models/chunk_test.py" = [
"PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/
]
lint.per-file-ignores."tests/apps/ai/models/context_test.py" = [
"PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/
]
lint.per-file-ignores."tests/apps/slack/views_test.py" = [
"PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/
]

[tool.pytest.ini_options]
DJANGO_CONFIGURATION = "Test"
DJANGO_SETTINGS_MODULE = "settings.test"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.core.management.base import BaseCommand

from apps.ai.management.commands.ai_update_chapter_chunks import Command
from apps.owasp.models.chapter import Chapter


@pytest.fixture
Expand All @@ -24,8 +25,6 @@ def test_command_inheritance(self, command):
assert isinstance(command, BaseCommand)

def test_model_class_property(self, command):
from apps.owasp.models.chapter import Chapter

assert command.model_class == Chapter

def test_entity_name_property(self, command):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

import pytest

from apps.ai.common.base.context_command import BaseContextCommand
from apps.ai.management.commands.ai_update_chapter_context import Command
from apps.owasp.models.chapter import Chapter


@pytest.fixture
Expand All @@ -23,8 +25,6 @@ def mock_chapter():
class TestAiCreateChapterContextCommand:
def test_command_inheritance(self, command):
"""Test that the command inherits from BaseContextCommand."""
from apps.ai.common.base.context_command import BaseContextCommand

assert isinstance(command, BaseContextCommand)

def test_command_help_text(self, command):
Expand All @@ -33,8 +33,6 @@ def test_command_help_text(self, command):

def test_model_class_property(self, command):
"""Test the model_class property returns Chapter."""
from apps.owasp.models.chapter import Chapter

assert command.model_class == Chapter

def test_entity_name_property(self, command):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.core.management.base import BaseCommand

from apps.ai.management.commands.ai_update_committee_chunks import Command
from apps.owasp.models.committee import Committee


@pytest.fixture
Expand Down Expand Up @@ -35,8 +36,6 @@ def test_command_inheritance(self, command):

def test_model_class_method(self, command):
"""Test the model_class method returns Committee."""
from apps.owasp.models.committee import Committee

assert command.model_class == Committee

def test_entity_name_method(self, command):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

import pytest

from apps.ai.common.base.context_command import BaseContextCommand
from apps.ai.management.commands.ai_update_committee_context import Command
from apps.owasp.models.committee import Committee


@pytest.fixture
Expand All @@ -30,8 +32,6 @@ class TestAiCreateCommitteeContextCommand:

def test_command_inheritance(self, command):
"""Test that the command inherits from BaseContextCommand."""
from apps.ai.common.base.context_command import BaseContextCommand

assert isinstance(command, BaseContextCommand)

def test_command_help_text(self, command):
Expand All @@ -40,8 +40,6 @@ def test_command_help_text(self, command):

def test_model_class_method(self, command):
"""Test the model_class method returns Committee."""
from apps.owasp.models.committee import Committee

assert command.model_class == Committee

def test_entity_name_method(self, command):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.core.management.base import BaseCommand

from apps.ai.management.commands.ai_update_event_chunks import Command
from apps.owasp.models.event import Event


@pytest.fixture
Expand All @@ -32,8 +33,6 @@ def test_command_inheritance(self, command):

def test_model_class_property(self, command):
"""Test the model_class property returns Event."""
from apps.owasp.models.event import Event

assert command.model_class == Event

def test_entity_name_property(self, command):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

import pytest

from apps.ai.common.base.context_command import BaseContextCommand
from apps.ai.management.commands.ai_update_event_context import Command
from apps.owasp.models.event import Event


@pytest.fixture
Expand All @@ -23,8 +25,6 @@ def mock_event():
class TestAiCreateEventContextCommand:
def test_command_inheritance(self, command):
"""Test that the command inherits from BaseContextCommand."""
from apps.ai.common.base.context_command import BaseContextCommand

assert isinstance(command, BaseContextCommand)

def test_command_help_text(self, command):
Expand All @@ -33,8 +33,6 @@ def test_command_help_text(self, command):

def test_model_class_property(self, command):
"""Test the model_class property returns Event."""
from apps.owasp.models.event import Event

assert command.model_class == Event

def test_entity_name_property(self, command):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.core.management.base import BaseCommand

from apps.ai.management.commands.ai_update_project_chunks import Command
from apps.owasp.models.project import Project


@pytest.fixture
Expand All @@ -24,8 +25,6 @@ def test_command_inheritance(self, command):
assert isinstance(command, BaseCommand)

def test_model_class_property(self, command):
from apps.owasp.models.project import Project

assert command.model_class == Project

def test_entity_name_property(self, command):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import pytest

from apps.ai.common.base.context_command import BaseContextCommand
from apps.ai.management.commands.ai_update_project_context import Command
from apps.owasp.models.project import Project


@pytest.fixture
Expand All @@ -21,8 +23,6 @@ def mock_project():
class TestAiCreateProjectContextCommand:
def test_command_inheritance(self, command):
"""Test that the command inherits from BaseContextCommand."""
from apps.ai.common.base.context_command import BaseContextCommand

assert isinstance(command, BaseContextCommand)

def test_command_help_text(self, command):
Expand All @@ -31,8 +31,6 @@ def test_command_help_text(self, command):

def test_model_class_property(self, command):
"""Test the model_class property returns Project."""
from apps.owasp.models.project import Project

assert command.model_class == Project

def test_entity_name_property(self, command):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.core.management.base import BaseCommand

from apps.ai.management.commands.ai_update_slack_message_chunks import Command
from apps.slack.models.message import Message


@pytest.fixture
Expand All @@ -24,8 +25,6 @@ def test_command_inheritance(self, command):
assert isinstance(command, BaseCommand)

def test_model_class_property(self, command):
from apps.slack.models.message import Message

assert command.model_class == Message

def test_entity_name_property(self, command):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import pytest

from apps.ai.common.base.context_command import BaseContextCommand
from apps.ai.management.commands.ai_update_slack_message_context import Command
from apps.slack.models.message import Message


@pytest.fixture
Expand All @@ -21,14 +23,10 @@ def mock_message():
class TestAiCreateSlackMessageContextCommand:
def test_command_inheritance(self, command):
"""Test that the command inherits from BaseContextCommand."""
from apps.ai.common.base.context_command import BaseContextCommand

assert isinstance(command, BaseContextCommand)

def test_model_class_property(self, command):
"""Test the model_class property returns Message."""
from apps.slack.models.message import Message

assert command.model_class == Message

def test_entity_name_property(self, command):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import pytest
from django.test import SimpleTestCase

from apps.github.management.commands.github_get_installation_id import Command


class TestGitHubGetInstallationId(SimpleTestCase):
"""Test the GitHub get installation ID management command."""
Expand All @@ -22,8 +24,6 @@ def setUp(self):
@mock.patch("apps.github.management.commands.github_get_installation_id.Auth.AppAuth")
def test_get_installation_id_success(self, mock_app_auth, mock_github_integration):
"""Test successful retrieval of installation ID."""
from apps.github.management.commands.github_get_installation_id import Command

# Mock the installation
mock_installation = mock.MagicMock()
mock_installation.id = 12345
Expand Down Expand Up @@ -57,8 +57,6 @@ def test_get_installation_id_success(self, mock_app_auth, mock_github_integratio
@mock.patch("apps.github.management.commands.github_get_installation_id.Auth.AppAuth")
def test_get_installation_id_no_installations(self, mock_app_auth, mock_github_integration):
"""Test when no installations are found."""
from apps.github.management.commands.github_get_installation_id import Command

# Mock empty installations
mock_gi_instance = mock.MagicMock()
mock_gi_instance.get_installations.return_value = []
Expand All @@ -78,16 +76,12 @@ def test_get_installation_id_no_installations(self, mock_app_auth, mock_github_i

def test_get_installation_id_no_app_id(self):
"""Test when no app ID is provided."""
from apps.github.management.commands.github_get_installation_id import Command

command = Command()
with pytest.raises(SystemExit):
command.handle()

def test_get_installation_id_private_key_file_not_found(self):
"""Test when private key file is not found."""
from apps.github.management.commands.github_get_installation_id import Command

command = Command()
with (
mock.patch("pathlib.Path.exists", return_value=False),
Expand All @@ -97,8 +91,6 @@ def test_get_installation_id_private_key_file_not_found(self):

def test_get_installation_id_empty_private_key_file(self):
"""Test when private key file is empty."""
from apps.github.management.commands.github_get_installation_id import Command

command = Command()
with (
mock.patch("pathlib.Path.open", mock.mock_open(read_data="")),
Expand All @@ -109,8 +101,6 @@ def test_get_installation_id_empty_private_key_file(self):

def test_get_installation_id_with_custom_private_key_file(self):
"""Test with custom private key file path."""
from apps.github.management.commands.github_get_installation_id import Command

custom_key_path = "/custom/path/key.pem"

command = Command()
Expand All @@ -130,8 +120,6 @@ def test_get_installation_id_with_environment_app_id(
self, mock_app_auth, mock_github_integration
):
"""Test using app ID from environment variable."""
from apps.github.management.commands.github_get_installation_id import Command

mock_gi_instance = mock.MagicMock()
mock_gi_instance.get_installations.return_value = []
mock_github_integration.return_value = mock_gi_instance
Expand Down