Skip to content
Merged
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
3 changes: 1 addition & 2 deletions autobot-backend/chat_history/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

from autobot_memory_graph import AutoBotMemoryGraph
from autobot_shared.redis_client import get_redis_client
from autobot_shared.ssot_config import config as ssot_config
from config import config as global_config_manager
from constants.network_constants import NetworkConstants
from context_window_manager import ContextWindowManager
Expand Down Expand Up @@ -69,7 +68,7 @@ def _load_config_values(
use_redis if use_redis is not None else redis_config.get("enabled", False)
)
self.redis_host = redis_host or redis_config.get(
"host", os.getenv("AUTOBOT_REDIS_HOST", ssot_config.vm.redis)
"host", os.getenv("AUTOBOT_REDIS_HOST", global_config_manager.get_host("redis"))
)
self.redis_port = redis_port or redis_config.get(
"port",
Expand Down
172 changes: 96 additions & 76 deletions autobot-frontend/src/components/analytics/CodeReviewDashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -438,25 +438,30 @@ const categories = [
]

// Computed
const summary = computed(() => {
const result = {
critical: 0,
high: 0,
medium: 0,
low: 0,
filesAnalyzed: new Set<string>()
}
// Issue #4036: Memoized aggregation - avoid recalculating severity counts on every render
const summary = useAggregationMemo(
() => {
const result = {
critical: 0,
high: 0,
medium: 0,
low: 0,
filesAnalyzed: new Set<string>()
}

issues.value.forEach(issue => {
result[issue.severity]++
result.filesAnalyzed.add(issue.file)
})
issues.value.forEach(issue => {
result[issue.severity]++
result.filesAnalyzed.add(issue.file)
})

return {
...result,
filesAnalyzed: result.filesAnalyzed.size
}
})
return {
...result,
filesAnalyzed: result.filesAnalyzed.size
}
},
() => [issues.value],
{ ttl: 60000 } // 1 minute TTL for summary aggregation
)

const filteredIssues = computed(() => {
if (activeCategory.value === 'all') return issues.value
Expand All @@ -465,70 +470,85 @@ const filteredIssues = computed(() => {

const totalIssues = computed(() => issues.value.length)

const chartSegments = computed(() => {
const categoryColors: Record<string, string> = {
security: '#ef4444',
performance: '#f59e0b',
bugs: '#8b5cf6',
style: '#3b82f6',
documentation: '#10b981'
}

const counts: Record<string, number> = {}
issues.value.forEach(issue => {
counts[issue.category] = (counts[issue.category] || 0) + 1
})
// Issue #4036: Memoized chart calculations - expensive SVG segment computation
const chartSegments = useGroupingMemo(
() => {
const categoryColors: Record<string, string> = {
security: '#ef4444',
performance: '#f59e0b',
bugs: '#8b5cf6',
style: '#3b82f6',
documentation: '#10b981'
}

const total = issues.value.length || 1
const circumference = 2 * Math.PI * 70
let currentOffset = circumference / 4 // Start from top
const counts: Record<string, number> = {}
issues.value.forEach(issue => {
counts[issue.category] = (counts[issue.category] || 0) + 1
})

return Object.entries(counts).map(([category, count]) => {
const percentage = count / total
const dashLength = circumference * percentage
const segment = {
category,
color: categoryColors[category] || '#6b7280',
dashArray: `${dashLength} ${circumference - dashLength}`,
offset: currentOffset
const total = issues.value.length || 1
const circumference = 2 * Math.PI * 70
let currentOffset = circumference / 4 // Start from top

return Object.entries(counts).map(([category, count]) => {
const percentage = count / total
const dashLength = circumference * percentage
const segment = {
category,
color: categoryColors[category] || '#6b7280',
dashArray: `${dashLength} ${circumference - dashLength}`,
offset: currentOffset
}
currentOffset -= dashLength
return segment
})
},
() => [issues.value],
{ ttl: 120000 } // 2 minutes TTL for chart segments
)

// Issue #4036: Memoized legend - avoids recalculating category grouping
const legendItems = useGroupingMemo(
() => {
const categoryColors: Record<string, string> = {
security: '#ef4444',
performance: '#f59e0b',
bugs: '#8b5cf6',
style: '#3b82f6',
documentation: '#10b981'
}
currentOffset -= dashLength
return segment
})
})

const legendItems = computed(() => {
const categoryColors: Record<string, string> = {
security: '#ef4444',
performance: '#f59e0b',
bugs: '#8b5cf6',
style: '#3b82f6',
documentation: '#10b981'
}

const counts: Record<string, number> = {}
issues.value.forEach(issue => {
counts[issue.category] = (counts[issue.category] || 0) + 1
})

return Object.entries(counts).map(([category, count]) => ({
category,
label: getCategoryName(category),
color: categoryColors[category] || '#6b7280',
count
}))
})
const counts: Record<string, number> = {}
issues.value.forEach(issue => {
counts[issue.category] = (counts[issue.category] || 0) + 1
})

const patternsByCategory = computed(() => {
const grouped: Record<string, Pattern[]> = {}
patterns.value.forEach(pattern => {
if (!grouped[pattern.category]) {
grouped[pattern.category] = []
}
grouped[pattern.category].push(pattern)
})
return grouped
})
return Object.entries(counts).map(([category, count]) => ({
category,
label: getCategoryName(category),
color: categoryColors[category] || '#6b7280',
count
}))
},
() => [issues.value],
{ ttl: 120000 } // 2 minutes TTL for legend items
)

// Issue #4036: Memoized grouping - avoid recalculating pattern categories
const patternsByCategory = useGroupingMemo(
() => {
const grouped: Record<string, Pattern[]> = {}
patterns.value.forEach(pattern => {
if (!grouped[pattern.category]) {
grouped[pattern.category] = []
}
grouped[pattern.category].push(pattern)
})
return grouped
},
() => [patterns.value],
{ ttl: 180000 } // 3 minutes TTL for patterns (rarely change)
)

// Methods
function toggleLanguage(lang: string) {
Expand Down
35 changes: 20 additions & 15 deletions autobot-frontend/src/components/analytics/CodeSmellsSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -164,21 +164,26 @@ const severitySummary = computed(() => {
return counts
})

const smellsByType = computed(() => {
const groups: Record<string, { smells: CodeSmell[], severityCounts: Record<string, number> }> = {}
props.smells.forEach(s => {
const type = s.smell_type || 'unknown'
if (!groups[type]) {
groups[type] = { smells: [], severityCounts: { critical: 0, high: 0, medium: 0, low: 0 } }
}
groups[type].smells.push(s)
const sev = (s.severity || 'low').toLowerCase()
if (groups[type].severityCounts[sev] !== undefined) {
groups[type].severityCounts[sev]++
}
})
return groups
})
// Issue #4036: Memoized type grouping with severity counts
const smellsByType = useGroupingMemo(
() => {
const groups: Record<string, { smells: CodeSmell[], severityCounts: Record<string, number> }> = {}
props.smells.forEach(s => {
const type = s.smell_type || 'unknown'
if (!groups[type]) {
groups[type] = { smells: [], severityCounts: { critical: 0, high: 0, medium: 0, low: 0 } }
}
groups[type].smells.push(s)
const sev = (s.severity || 'low').toLowerCase()
if (groups[type].severityCounts[sev] !== undefined) {
groups[type].severityCounts[sev]++
}
})
return groups
},
() => [props.smells],
{ ttl: 120000 } // 2 minutes TTL for type grouping
)

const toggleCodeSmellType = (type: string) => {
expandedCodeSmellTypes.value[type] = !expandedCodeSmellTypes.value[type]
Expand Down
29 changes: 17 additions & 12 deletions autobot-frontend/src/components/analytics/DeclarationsSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -123,18 +123,23 @@ const emit = defineEmits<{

const expandedDeclarationTypes = ref<Record<string, boolean>>({})

const declarationsByType = computed(() => {
const groups: Record<string, { declarations: Declaration[], exportedCount: number }> = {}
props.declarations.forEach(d => {
const type = d.declaration_type || 'unknown'
if (!groups[type]) {
groups[type] = { declarations: [], exportedCount: 0 }
}
groups[type].declarations.push(d)
if (d.is_exported) groups[type].exportedCount++
})
return groups
})
// Issue #4036: Memoized type grouping with export counts
const declarationsByType = useGroupingMemo(
() => {
const groups: Record<string, { declarations: Declaration[], exportedCount: number }> = {}
props.declarations.forEach(d => {
const type = d.declaration_type || 'unknown'
if (!groups[type]) {
groups[type] = { declarations: [], exportedCount: 0 }
}
groups[type].declarations.push(d)
if (d.is_exported) groups[type].exportedCount++
})
return groups
},
() => [props.declarations],
{ ttl: 120000 } // 2 minutes TTL for type grouping
)

const toggleDeclarationType = (type: string) => {
expandedDeclarationTypes.value[type] = !expandedDeclarationTypes.value[type]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,12 @@ const duplicatesBySimilarity = computed(() => {
return groups
})

const totalDuplicateLines = computed(() => {
return props.duplicates.reduce((sum, d) => sum + d.lines, 0)
})
// Issue #4036: Memoized line count aggregation
const totalDuplicateLines = useAggregationMemo(
() => props.duplicates.reduce((sum, d) => sum + d.lines, 0),
() => [props.duplicates],
{ ttl: 60000 } // 1 minute TTL for line counts
)

const toggleDuplicateGroup = (similarity: string) => {
expandedDuplicateGroups.value[similarity] = !expandedDuplicateGroups.value[similarity]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@
* Issue #184: Split oversized Vue components
*/

import { ref, computed } from 'vue'
import { ref } from 'vue'
import { useGroupingMemo } from '@/composables/useComputedMemo'
import EmptyState from '@/components/ui/EmptyState.vue'

interface Problem {
Expand All @@ -148,35 +149,45 @@ const emit = defineEmits<{

const expandedProblemTypes = ref<Record<string, boolean>>({})

const problemsBySeverity = computed(() => {
const groups: Record<string, Problem[]> = {
critical: [],
high: [],
medium: [],
low: []
}
props.problems.forEach(p => {
const sev = (p.severity || 'low').toLowerCase()
if (groups[sev]) groups[sev].push(p)
})
return groups
})

const problemsByType = computed(() => {
const groups: Record<string, { problems: Problem[], severityCounts: Record<string, number> }> = {}
props.problems.forEach(p => {
const type = p.problem_type || p.type || 'unknown'
if (!groups[type]) {
groups[type] = { problems: [], severityCounts: { critical: 0, high: 0, medium: 0, low: 0 } }
// Issue #4036: Memoized grouping - avoid recalculating on every render
const problemsBySeverity = useGroupingMemo(
() => {
const groups: Record<string, Problem[]> = {
critical: [],
high: [],
medium: [],
low: []
}
groups[type].problems.push(p)
const sev = (p.severity || 'low').toLowerCase()
if (groups[type].severityCounts[sev] !== undefined) {
groups[type].severityCounts[sev]++
}
})
return groups
})
props.problems.forEach(p => {
const sev = (p.severity || 'low').toLowerCase()
if (groups[sev]) groups[sev].push(p)
})
return groups
},
() => [props.problems],
{ ttl: 60000 } // 1 minute TTL for severity grouping
)

// Issue #4036: Memoized complex grouping with severity counts
const problemsByType = useGroupingMemo(
() => {
const groups: Record<string, { problems: Problem[], severityCounts: Record<string, number> }> = {}
props.problems.forEach(p => {
const type = p.problem_type || p.type || 'unknown'
if (!groups[type]) {
groups[type] = { problems: [], severityCounts: { critical: 0, high: 0, medium: 0, low: 0 } }
}
groups[type].problems.push(p)
const sev = (p.severity || 'low').toLowerCase()
if (groups[type].severityCounts[sev] !== undefined) {
groups[type].severityCounts[sev]++
}
})
return groups
},
() => [props.problems],
{ ttl: 120000 } // 2 minutes TTL for type grouping (more complex)
)

const toggleProblemType = (type: string) => {
expandedProblemTypes.value[type] = !expandedProblemTypes.value[type]
Expand Down
Loading
Loading