Skip to content
Closed
5 changes: 5 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@
## 2024-05-24 - Memoizing inline array maps
**Learning:** Inline mapping of arrays inside JSX in large React components causes O(N) recalculation on every render.
**Action:** Wrap inline JSX elements that map over arrays (e.g., lists of tasks) in a `useMemo` hook with specific dependencies.
## 2025-02-12 - Python `dict.setdefault(..., []).append(...)` ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น ์˜ค๋ฒ„ํ—ค๋“œ

**Learning:** ํŒŒ์ด์ฌ์—์„œ ๋ฐ˜๋ณต๋ฌธ ๋‚ด์— `dict.setdefault(key, []).append(value)`๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ํ‚ค์˜ ์กด์žฌ ์—ฌ๋ถ€์™€ ์ƒ๊ด€์—†์ด ๋งค ๋ฐ˜๋ณต๋งˆ๋‹ค ๋นˆ ๋ฆฌ์ŠคํŠธ `[]`๋ฅผ ์ƒˆ๋กœ ๋ฉ”๋ชจ๋ฆฌ์— ํ• ๋‹นํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ํฐ ์ปฌ๋ ‰์…˜์ด๋‚˜ ์„ฑ๋Šฅ์ด ์ค‘์š”ํ•œ ๋ฃจํ”„์—์„œ ์ด๋Ÿฌํ•œ ํŒจํ„ด์€ ์‹ฌ๊ฐํ•œ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น ์˜ค๋ฒ„ํ—ค๋“œ์™€ ์†๋„ ์ €ํ•˜๋ฅผ ์ผ์œผํ‚ต๋‹ˆ๋‹ค.

**Action:** ์š”์†Œ๋ฅผ ๊ทธ๋ฃนํ™”ํ•˜๊ฑฐ๋‚˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ `setdefault(key, [])`๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ๋ง๊ณ , ํ•ญ์ƒ `collections.defaultdict(list)`๋ฅผ ์ƒ์„ฑํ•œ ํ›„ `dict[key].append(value)`๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ถˆํ•„์š”ํ•œ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น์„ ๋ฐฉ์ง€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
16 changes: 10 additions & 6 deletions backend/api/data.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from collections import defaultdict
import base64
import binascii
from datetime import datetime, timezone
Expand Down Expand Up @@ -1692,17 +1693,18 @@ def _risk_matrix_key_part(value: str) -> str:
def _diligence_risk_matrix(
snapshot: DataEvidenceSnapshotResponse,
) -> list[DataDiligenceRiskMatrixEntry]:
# โšก Bolt: Use defaultdict(list) instead of dict.setdefault(..., []).append(...) to avoid O(N) empty list allocation overhead inside the loop.
groups: dict[
tuple[RemediationPriority, str, str],
list[DataDiligenceExceptionRegisterEntry],
] = {}
] = defaultdict(list)
for exception in snapshot.diligence_exception_register:
key = (
exception.severity_code,
exception.owner_area,
exception.related_artifact,
)
groups.setdefault(key, []).append(exception)
groups[key].append(exception)

entries: list[DataDiligenceRiskMatrixEntry] = []
for (severity, owner_area, related_artifact), exceptions in sorted(
Expand Down Expand Up @@ -1844,9 +1846,10 @@ def _diligence_close_decision_summary(
def _diligence_close_artifact_review_queue(
snapshot: DataEvidenceSnapshotResponse,
) -> list[DataDiligenceCloseArtifactReviewQueueEntry]:
groups: dict[str, list[DataDiligenceCloseProofPlanEntry]] = {}
# โšก Bolt: Use defaultdict(list) instead of dict.setdefault(..., []).append(...) to avoid O(N) empty list allocation overhead inside the loop.
groups: dict[str, list[DataDiligenceCloseProofPlanEntry]] = defaultdict(list)
for proof in snapshot.diligence_close_proof_plan:
groups.setdefault(proof.required_proof_artifact, []).append(proof)
groups[proof.required_proof_artifact].append(proof)

entries: list[DataDiligenceCloseArtifactReviewQueueEntry] = []
for artifact, proofs in sorted(groups.items()):
Expand Down Expand Up @@ -1889,9 +1892,10 @@ def _diligence_close_artifact_review_queue(
def _diligence_close_owner_handoff_queue(
snapshot: DataEvidenceSnapshotResponse,
) -> list[DataDiligenceCloseOwnerHandoffQueueEntry]:
groups: dict[str, list[DataDiligenceCloseProofPlanEntry]] = {}
# โšก Bolt: Use defaultdict(list) instead of dict.setdefault(..., []).append(...) to avoid O(N) empty list allocation overhead inside the loop.
groups: dict[str, list[DataDiligenceCloseProofPlanEntry]] = defaultdict(list)
for proof in snapshot.diligence_close_proof_plan:
groups.setdefault(proof.owner_area, []).append(proof)
groups[proof.owner_area].append(proof)

entries: list[DataDiligenceCloseOwnerHandoffQueueEntry] = []
for owner_area, proofs in sorted(groups.items()):
Expand Down
11 changes: 7 additions & 4 deletions backend/services/project_graph/project_registration.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from __future__ import annotations
from collections import defaultdict

import datetime
import hashlib
Expand Down Expand Up @@ -612,9 +613,10 @@ def _candidate_groups(
*,
scope: ProjectGraphQueryScope,
) -> tuple[_CandidateGroup, ...]:
records_by_email: dict[int, list[ProjectGraphObjectRecord]] = {}
# โšก Bolt: Use defaultdict(list) instead of dict.setdefault(..., []).append(...) to avoid O(N) empty list allocation overhead inside the loop.
records_by_email: dict[int, list[ProjectGraphObjectRecord]] = defaultdict(list)
for record in records:
records_by_email.setdefault(record.email_id, []).append(record)
records_by_email[record.email_id].append(record)

groups: list[_CandidateGroup] = []
for group_records in records_by_email.values():
Expand Down Expand Up @@ -842,9 +844,10 @@ def _relation_summary(
``relation_count`` descending with a ``relation_type``-ascending tie-break so
the result is deterministic regardless of relation iteration order.
"""
grouped: dict[str, list[ProjectTraceRelation]] = {}
# โšก Bolt: Use defaultdict(list) instead of dict.setdefault(..., []).append(...) to avoid O(N) empty list allocation overhead inside the loop.
grouped: dict[str, list[ProjectTraceRelation]] = defaultdict(list)
for relation in relations:
grouped.setdefault(relation.relation_type, []).append(relation)
grouped[relation.relation_type].append(relation)
type_summaries = [
ProjectRelationTypeSummary(
relation_type=relation_type,
Expand Down
Loading