Skip to content

Commit 46c5c95

Browse files
Merge pull request AOSSIE-Org#74 from Ri1tik/refactor/lifecycle-to-activity-classification
Refactor/lifecycle to activity classification
2 parents fe78a19 + 7aef67f commit 46c5c95

4 files changed

Lines changed: 41 additions & 43 deletions

File tree

src/components/UI.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,12 @@ export const C = {
6363
},
6464
}
6565

66-
// Lifecycle badge color map
66+
// Activity badge color map
6767
const LC = {
6868
Thriving: ['#22c55e', 'rgba(34,197,94,.15)'],
69-
Stable: ['#3b82f6', 'rgba(59,130,246,.15)'],
69+
Active: ['#3b82f6', 'rgba(59,130,246,.15)'],
7070
Dormant: ['#f59e0b', 'rgba(245,158,11,.15)'],
71-
Abandoned: ['#ef4444', 'rgba(239,68,68,.15)'],
71+
Hibernating: ['#ef4444', 'rgba(239,68,68,.15)'],
7272
critical: ['#ef4444', 'rgba(239,68,68,.15)'],
7373
high: ['#f59e0b', 'rgba(245,158,11,.15)'],
7474
healthy: ['#22c55e', 'rgba(34,197,94,.15)'],

src/pages/OverviewPage.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export default function OverviewPage() {
3232
const isMulti = orgs.length > 1
3333
const totalStars = allRepos.reduce((s, r) => s + r.stargazers_count, 0)
3434
const totalForks = allRepos.reduce((s, r) => s + r.forks_count, 0)
35-
const activeRepos = allRepos.filter(r => r.lifecycle === 'Thriving' || r.lifecycle === 'Stable').length
35+
const activeRepos = allRepos.filter(r => r.activityClassification === 'Thriving' || r.activityClassification === 'Active').length
3636

3737
const langMap = {}
3838
allRepos.forEach(r => { if (r.language) langMap[r.language] = (langMap[r.language] || 0) + 1 })
@@ -183,7 +183,7 @@ export default function OverviewPage() {
183183

184184
{/* Nav cards */}
185185
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 14 }}>
186-
<NavCard to="/repositories" label="Repositories" sub="Explore and sort repos by health, activity, and lifecycle state" />
186+
<NavCard to="/repositories" label="Repositories" sub="Explore and sort repos by health, and activity classification state" />
187187
<NavCard to="/contributors" label="Contributors" sub="Analyze contribution patterns, bus factor, and connector signals" />
188188
<NavCard to="/network" label="Network Graph" sub="Visualize contributor-repository relationships with D3 force graph" />
189189
<NavCard to="/analytics" label="Analytics" sub="Time-series PR and issue velocity — weekly and monthly trends" />

src/pages/RepositoriesPage.jsx

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ import { exportReposCSV } from '../services/analytics'
88
import EmptyStateCard from '../components/EmptyStateCard'
99
import { useNavigate } from 'react-router-dom'
1010

11-
const LIFECYCLES = ['All', 'Thriving', 'Stable', 'Dormant', 'Abandoned']
12-
const LC_ACTIVE = { Thriving: 'var(--green)', Stable: 'var(--blue)', Dormant: 'var(--amber)', Abandoned: 'var(--red)' }
11+
const ACTIVITY_CLASSIFICATIONS = ['All', 'Thriving', 'Active', 'Dormant', 'Hibernating']
12+
const ACTIVITY_COLORS = { Thriving: 'var(--green)', Active: 'var(--blue)', Dormant: 'var(--amber)', Hibernating: 'var(--red)' }
1313

1414
export default function RepositoriesPage() {
1515
const { model } = useApp()
1616
const [search, setSearch] = useState('')
17-
const [lifecycle, setLifecycle] = useState('All')
17+
const [activityClassification, setActivityClassification] = useState('All')
1818
const [lang, setLang] = useState('All')
1919
const [view, setView] = useState('grid')
2020
const [shown, setShown] = useState(20)
@@ -45,11 +45,11 @@ export default function RepositoriesPage() {
4545
[allRepos])
4646

4747
const filtered = useMemo(() => allRepos.filter(r =>
48-
(lifecycle === 'All' || r.lifecycle === lifecycle) &&
48+
(activityClassification === 'All' || r.activityClassification === activityClassification) &&
4949
(lang === 'All' || r.language === lang) &&
5050
(!search || r.name.toLowerCase().includes(search.toLowerCase()) ||
5151
(r.description || '').toLowerCase().includes(search.toLowerCase()))
52-
), [allRepos, lifecycle, lang, search])
52+
), [allRepos, activityClassification, lang, search])
5353

5454
const { sorted, sortConfig, onSort } = useSortedData(filtered, 'healthScore', 'desc')
5555
const visible = sorted.slice(0, shown)
@@ -60,7 +60,7 @@ export default function RepositoriesPage() {
6060
['forks_count', 'Forks'],
6161
['open_issues_count', 'Open Issues'],
6262
['healthScore', 'Health'],
63-
['lifecycle', 'Lifecycle'],
63+
['activityClassification', 'Activity Classification'],
6464
['pushed_at', 'Last Push'],
6565
]
6666

@@ -80,7 +80,7 @@ export default function RepositoriesPage() {
8080
</button>
8181
</div>
8282
}
83-
subtitle="Technical health and lifecycle across all repositories in the portfolio"
83+
subtitle="Repository insights and activity classification across all repositories."
8484
right={
8585
<span style={{ fontSize: 28, fontWeight: 700, color: 'var(--accent)' }}>
8686
{filtered.length}
@@ -117,8 +117,8 @@ export default function RepositoriesPage() {
117117
</div>
118118

119119
<p style={{ fontSize: 13, color: 'var(--text2)', marginBottom: 12 }}>
120-
OrgExplorer evaluates repositories using activity, issue health,
121-
contributor diversity, and lifecycle status.
120+
OrgExplorer evaluates repositories using issue health,
121+
contributor diversity, and activity classification status.
122122
</p>
123123

124124
<div style={{ fontSize: 12, lineHeight: 1.7 }}>
@@ -129,12 +129,12 @@ export default function RepositoriesPage() {
129129
<li>Contributor Diversity → 30%</li>
130130
</ul>
131131

132-
<strong>Lifecycle Classification</strong>
132+
<strong>Activity Classification</strong>
133133
<ul style={{ marginLeft: 18 }}>
134134
<li>🟢 Thriving → Updated within 30 days</li>
135-
<li>🔵 Stable → Updated within 90 days</li>
135+
<li>🔵 Active → Updated within 90 days</li>
136136
<li>🟡 Dormant → Updated within 180 days</li>
137-
<li>🔴 Abandoned → No updates for 180+ days</li>
137+
<li>🔴 Hibernating → No updates for 180+ days</li>
138138
</ul>
139139

140140
<strong>Repository Signals</strong>
@@ -172,14 +172,14 @@ export default function RepositoriesPage() {
172172
</button>
173173
</div>
174174
<div style={{ display: 'flex', gap: 6, marginTop: 12, flexWrap: 'wrap' }}>
175-
{LIFECYCLES.map(l => (
175+
{ACTIVITY_CLASSIFICATIONS.map(l => (
176176
<button
177-
key={l} onClick={() => { setLifecycle(l); setShown(20) }}
177+
key={l} onClick={() => { setActivityClassification(l); setShown(20) }}
178178
style={{
179179
padding: '4px 12px', borderRadius: 4, fontSize: 12, fontWeight: 500, cursor: 'pointer',
180-
border: lifecycle === l ? 'none' : '1px solid var(--border)',
181-
background: lifecycle === l ? (LC_ACTIVE[l] || 'var(--accent)') : 'transparent',
182-
color: lifecycle === l ? '#000' : 'var(--text2)',
180+
border: activityClassification === l ? 'none' : '1px solid var(--border)',
181+
background: activityClassification === l ? (ACTIVITY_COLORS[l] || 'var(--accent)') : 'transparent',
182+
color: activityClassification === l ? '#000' : 'var(--text2)',
183183
}}
184184
>
185185
{l}
@@ -211,7 +211,7 @@ export default function RepositoriesPage() {
211211
<td style={{ padding: '10px 14px', fontSize: 13, color: 'var(--text2)' }}>{r.forks_count.toLocaleString()}</td>
212212
<td style={{ padding: '10px 14px', fontSize: 13, color: r.open_issues_count > 30 ? 'var(--red)' : 'var(--text2)' }}>{r.open_issues_count}</td>
213213
<td style={{ padding: '10px 14px', minWidth: 130 }}><HealthBar score={r.healthScore} /></td>
214-
<td style={{ padding: '10px 14px' }}><Badge text={r.lifecycle} /></td>
214+
<td style={{ padding: '10px 14px' }}><Badge text={r.activityClassification} /></td>
215215
<td style={{ padding: '10px 14px', fontSize: 12, color: 'var(--text2)' }}>{r.pushed_at?.slice(0, 10)}</td>
216216
</tr>
217217
))}
@@ -229,12 +229,10 @@ export default function RepositoriesPage() {
229229
<div
230230
key={r.id}
231231
onMouseEnter={e => e.currentTarget.style.borderColor = 'var(--accent)'}
232-
onMouseLeave={e => e.currentTarget.style.borderColor =
233-
r.lifecycle === 'Thriving' ? 'rgba(34,197,94,.25)' :
234-
r.lifecycle === 'Abandoned' ? 'rgba(239,68,68,.25)' : 'var(--border)'}
232+
onMouseLeave={e => e.currentTarget.style.borderColor = ACTIVITY_COLORS[r.activityClassification]}
235233
style={{
236234
...C.card,
237-
borderColor: r.lifecycle === 'Thriving' ? 'rgba(34,197,94,.25)' : r.lifecycle === 'Abandoned' ? 'rgba(239,68,68,.25)' : 'var(--border)',
235+
borderColor: ACTIVITY_COLORS[r.activityClassification],
238236
transition: 'border-color .2s', display: 'flex', flexDirection: 'column', gap: 10,
239237
}}
240238
>
@@ -243,7 +241,7 @@ export default function RepositoriesPage() {
243241
<div style={{ fontWeight: 600, fontSize: 14 }}>{r.name}</div>
244242
{r.orgLogin && <div style={{ fontSize: 11, color: 'var(--text2)' }}>{r.orgLogin}</div>}
245243
</div>
246-
<Badge text={r.lifecycle} />
244+
<Badge text={r.activityClassification} />
247245
</div>
248246
<p style={{ fontSize: 12, color: 'var(--text2)', minHeight: 34, overflow: 'hidden', display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical' }}>
249247
{r.description || 'No description provided'}

src/services/analytics.js

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Repo Health Indicator (Section 3.2.6)
1+
// Repo Health Indicator
22
// Activity (40%) + Issue Health (30%) + Diversity (30%)
33
export function computeHealthScore(repo, contributorCount = 0) {
44
const daysSince = (Date.now() - new Date(repo.pushed_at)) / 86_400_000
@@ -9,16 +9,16 @@ export function computeHealthScore(repo, contributorCount = 0) {
99
return Math.round(activity * 0.4 + issueHealth * 0.3 + diversity * 0.3)
1010
}
1111

12-
// Repo Lifecycle (Section 3.2.6) — Thriving, Stable, Dormant, Abandoned based on recency of last push
13-
export function computeLifecycle(repo) {
12+
// Repo Lifecycle — Thriving, Active, Dormant, Hibernating based on recency of last push
13+
export function computeActivityClassification(repo) {
1414
const days = (Date.now() - new Date(repo.pushed_at)) / 86_400_000
1515
if (days <= 30) return 'Thriving'
16-
if (days <= 90) return 'Stable'
16+
if (days <= 90) return 'Active'
1717
if (days <= 180) return 'Dormant'
18-
return 'Abandoned'
18+
return 'Hibernating'
1919
}
2020

21-
// Bus Factor (Section 3.2.6)
21+
// Bus Factor
2222
export function computeBusFactor(contributors = []) {
2323
if (!contributors.length) return { factor: 0, risk: 'unknown' }
2424
const total = contributors.reduce((s, c) => s + c.contributions, 0)
@@ -34,9 +34,9 @@ export function computeBusFactor(contributors = []) {
3434
return { factor: contributors.length, risk: 'healthy' }
3535
}
3636

37-
// Unified Analytical Data Model (Section 3.2.0)
37+
// Unified Analytical Data Model
3838
// Merges multiple orgs into one normalized graph:
39-
// Organization → Repositories → Contributors → Issues/PRs
39+
// Organization → Repositories → Contributors → Issues/PRs
4040
export function buildAnalyticalModel(orgs, reposPerOrg, contribsPerRepo) {
4141
const allRepos = []
4242
const contributorMap = {}
@@ -48,9 +48,9 @@ export function buildAnalyticalModel(orgs, reposPerOrg, contribsPerRepo) {
4848
const key = `${org.login}/${repo.name}`
4949
const contribs = contribsPerRepo[key] || []
5050
const health = computeHealthScore(repo, contribs.length)
51-
const lc = computeLifecycle(repo)
51+
const activityClassification = computeActivityClassification(repo)
5252
const bf = computeBusFactor(contribs)
53-
allRepos.push({ ...repo, orgLogin: org.login, contributors: contribs, healthScore: health, lifecycle: lc, busFactor: bf })
53+
allRepos.push({ ...repo, orgLogin: org.login, contributors: contribs, healthScore: health, activityClassification: activityClassification, busFactor: bf })
5454

5555
// Build contributor map — deduplicated by login across orgs
5656
contribs.forEach(c => {
@@ -86,11 +86,11 @@ export function buildAnalyticalModel(orgs, reposPerOrg, contribsPerRepo) {
8686
: 0,
8787
})).sort((a, b) => b.totalContribs - a.totalContribs)
8888

89-
// Graph is constructed here and persisted through cache layers (Section 3.2.0)
89+
// Graph is constructed here and persisted through cache layers
9090
return { allRepos, contributors }
9191
}
9292

93-
// Time-Series Bucketing (Section 3.2.9)
93+
// Time-Series Bucketing
9494
// Parses created_at, closed_at, merged_at into weekly/monthly bins
9595
export function buildTimeSeries(issues = [], granularity = 'monthly') {
9696
const buckets = {}
@@ -142,7 +142,7 @@ export function buildTimeSeries(issues = [], granularity = 'monthly') {
142142
.slice(-12)
143143
}
144144

145-
// CSV Export (Section 3.2.9)
145+
// CSV Export
146146
function download(content, filename, type = 'text/csv') {
147147
const blob = new Blob([content], { type })
148148
const url = URL.createObjectURL(blob)
@@ -152,8 +152,8 @@ function download(content, filename, type = 'text/csv') {
152152
}
153153

154154
export function exportReposCSV(repos) {
155-
const header = ['Repository','Org','Stars','Forks','Open Issues','Health Score','Lifecycle','Language','Last Active']
156-
const rows = repos.map(r => [r.name, r.orgLogin, r.stargazers_count, r.forks_count, r.open_issues_count, r.healthScore, r.lifecycle, r.language || 'N/A', r.pushed_at?.slice(0, 10)])
155+
const header = ['Repository','Org','Stars','Forks','Open Issues','Health Score','Activity Classification','Language','Last Active']
156+
const rows = repos.map(r => [r.name, r.orgLogin, r.stargazers_count, r.forks_count, r.open_issues_count, r.healthScore, r.activityClassification, r.language || 'N/A', r.pushed_at?.slice(0, 10)])
157157
download([header, ...rows].map(r => r.join(',')).join('\n'), 'orgexplorer-repos.csv')
158158
}
159159

0 commit comments

Comments
 (0)