Skip to content

Commit e3bde45

Browse files
committed
[metrics] Include mean of commits for month and year
This commit adds the mean number of commits per repository for each week, month, and year. If the number of days requested for metrics is shorter than the interval for the metric, it is not calculated because it would not be meaningful. Signed-off-by: Jose Javier Merchante <[email protected]>
1 parent 53da665 commit e3bde45

File tree

5 files changed

+77
-16
lines changed

5 files changed

+77
-16
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ trustable spdx.xml \
5555
--opensearch-url https://admin:[email protected]:9200 \
5656
--opensearch-index events \
5757
--output metrics.json \
58+
--from-date 2024-01-01 --to-date 2025-01-01 \
5859
--repository-timeout 3600
5960
```
6061

@@ -104,3 +105,11 @@ This is an example of a valid SPDX file:
104105
This is the list of the metrics generated by this tool:
105106

106107
- Number of commits per repository
108+
- Number of developers per repository
109+
- Number of developers producing up to 50% of the total contributions
110+
- Number of companies producing up to 50% of the total number of code contributions
111+
- File type metrics (code, binaries or other)
112+
- Commit side metrics (added lines and removed lines)
113+
- Message size metrics (total, mean and median)
114+
- Frequency metrics for commits (week, month and year)
115+
- Developer categories (core, regular and casual)

tests/end_to_end/test_cli.py

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def test_metrics(self):
5050
"--output",
5151
self.temp_file.name,
5252
"--from-date=2000-01-01",
53+
"--to-date=2025-01-01",
5354
],
5455
)
5556
self.assertEqual(result.exit_code, 0)
@@ -81,7 +82,10 @@ def test_metrics(self):
8182
self.assertEqual(quickstart_metrics["developer_categories_core"], 3)
8283
self.assertEqual(quickstart_metrics["developer_categories_regular"], 13)
8384
self.assertEqual(quickstart_metrics["developer_categories_casual"], 9)
84-
self.assertAlmostEqual(quickstart_metrics["commits_week_mean"], 0.06418, delta=0.1)
85+
# From 2000 to 2025 there are 9132 days
86+
self.assertAlmostEqual(quickstart_metrics["commits_per_week"], 164 / (9132 / 7), delta=0.1)
87+
self.assertAlmostEqual(quickstart_metrics["commits_per_month"], 164 / (9132 / 30), delta=0.1)
88+
self.assertAlmostEqual(quickstart_metrics["commits_per_year"], 164 / (9132 / 365), delta=0.1)
8589

8690
self.assertIn("SPDXRef-angular-seed", metrics["packages"])
8791
self.assertEqual(
@@ -102,7 +106,10 @@ def test_metrics(self):
102106
self.assertEqual(angular_metrics["developer_categories_core"], 16)
103107
self.assertEqual(angular_metrics["developer_categories_regular"], 31)
104108
self.assertEqual(angular_metrics["developer_categories_casual"], 11)
105-
self.assertAlmostEqual(angular_metrics["commits_week_mean"], 0.08101, delta=0.1)
109+
# From 2000 to 2025 there are 9132 days
110+
self.assertAlmostEqual(angular_metrics["commits_per_week"], 207 / (9132 / 7), delta=0.1)
111+
self.assertAlmostEqual(angular_metrics["commits_per_month"], 207 / (9132 / 30), delta=0.1)
112+
self.assertAlmostEqual(angular_metrics["commits_per_year"], 207 / (9132 / 365), delta=0.1)
106113

107114
def test_from_date(self):
108115
"""Check if it returns the number of commits of one repository from a particular date"""
@@ -125,6 +132,7 @@ def test_from_date(self):
125132
"--output",
126133
self.temp_file.name,
127134
"--from-date=2017-01-01",
135+
"--to-date=2025-01-01",
128136
],
129137
)
130138
self.assertEqual(result.exit_code, 0)
@@ -156,7 +164,10 @@ def test_from_date(self):
156164
self.assertEqual(quickstart_metrics["developer_categories_core"], 3)
157165
self.assertEqual(quickstart_metrics["developer_categories_regular"], 3)
158166
self.assertEqual(quickstart_metrics["developer_categories_casual"], 2)
159-
self.assertAlmostEqual(quickstart_metrics["commits_week_mean"], 0.00861, delta=0.1)
167+
# From 2017 to 2025 there are 2922 days
168+
self.assertAlmostEqual(quickstart_metrics["commits_per_week"], 22 / (2922 / 7), delta=0.1)
169+
self.assertAlmostEqual(quickstart_metrics["commits_per_month"], 22 / (2922 / 30), delta=0.1)
170+
self.assertAlmostEqual(quickstart_metrics["commits_per_year"], 22 / (2922 / 365), delta=0.1)
160171

161172
self.assertIn("SPDXRef-angular-seed", metrics["packages"])
162173
self.assertEqual(
@@ -177,7 +188,10 @@ def test_from_date(self):
177188
self.assertEqual(angular_metrics["developer_categories_core"], 1)
178189
self.assertEqual(angular_metrics["developer_categories_regular"], 2)
179190
self.assertEqual(angular_metrics["developer_categories_casual"], 1)
180-
self.assertAlmostEqual(angular_metrics["commits_week_mean"], 0.0043, delta=0.1)
191+
# From 2017 to 2025 there are 2922 days
192+
self.assertAlmostEqual(angular_metrics["commits_per_week"], 11 / (2922 / 7), delta=0.1)
193+
self.assertAlmostEqual(angular_metrics["commits_per_month"], 11 / (2922 / 30), delta=0.1)
194+
self.assertAlmostEqual(angular_metrics["commits_per_year"], 11 / (2922 / 365), delta=0.1)
181195

182196
def test_to_date(self):
183197
"""Check if it returns the number of commits of one repository up to a particular date"""
@@ -232,7 +246,10 @@ def test_to_date(self):
232246
self.assertEqual(quickstart_metrics["developer_categories_core"], 3)
233247
self.assertEqual(quickstart_metrics["developer_categories_regular"], 9)
234248
self.assertEqual(quickstart_metrics["developer_categories_casual"], 8)
235-
self.assertAlmostEqual(quickstart_metrics["commits_week_mean"], 0.003266620657925006, delta=0.1)
249+
# From 2000 to 2017 there are 6210 days
250+
self.assertAlmostEqual(quickstart_metrics["commits_per_week"], 142 / (6210 / 7), delta=0.1)
251+
self.assertAlmostEqual(quickstart_metrics["commits_per_month"], 142 / (6210 / 30), delta=0.1)
252+
self.assertAlmostEqual(quickstart_metrics["commits_per_year"], 142 / (6210 / 365), delta=0.1)
236253

237254
self.assertIn("SPDXRef-angular-seed", metrics["packages"])
238255
self.assertEqual(
@@ -253,7 +270,10 @@ def test_to_date(self):
253270
self.assertEqual(angular_metrics["developer_categories_core"], 16)
254271
self.assertEqual(angular_metrics["developer_categories_regular"], 30)
255272
self.assertEqual(angular_metrics["developer_categories_casual"], 10)
256-
self.assertAlmostEqual(angular_metrics["commits_week_mean"], 0.0045088566827697265, delta=0.1)
273+
# From 2000 to 2017 there are 6210 days
274+
self.assertAlmostEqual(angular_metrics["commits_per_week"], 196 / (6210 / 7), delta=0.1)
275+
self.assertAlmostEqual(angular_metrics["commits_per_month"], 196 / (6210 / 30), delta=0.1)
276+
self.assertAlmostEqual(angular_metrics["commits_per_year"], 196 / (6210 / 365), delta=0.1)
257277

258278
def test_duplicate_repo(self):
259279
"""Check if it ignores duplicated URLs"""
@@ -276,6 +296,7 @@ def test_duplicate_repo(self):
276296
"--output",
277297
self.temp_file.name,
278298
"--from-date=2000-01-01",
299+
"--to-date=2025-01-01",
279300
],
280301
)
281302
self.assertEqual(result.exit_code, 0)
@@ -308,7 +329,10 @@ def test_duplicate_repo(self):
308329
self.assertEqual(quickstart_metrics["developer_categories_core"], 3)
309330
self.assertEqual(quickstart_metrics["developer_categories_regular"], 13)
310331
self.assertEqual(quickstart_metrics["developer_categories_casual"], 9)
311-
self.assertAlmostEqual(quickstart_metrics["commits_week_mean"], 0.06418, delta=0.1)
332+
# From 2000 to 2025 there are 9132 days
333+
self.assertAlmostEqual(quickstart_metrics["commits_per_week"], 164 / (9132 / 7), delta=0.1)
334+
self.assertAlmostEqual(quickstart_metrics["commits_per_month"], 164 / (9132 / 30), delta=0.1)
335+
self.assertAlmostEqual(quickstart_metrics["commits_per_year"], 164 / (9132 / 365), delta=0.1)
312336

313337
def test_non_git_repo(self):
314338
"""Check if it flags non-git dependencies"""
@@ -331,6 +355,7 @@ def test_non_git_repo(self):
331355
"--output",
332356
self.temp_file.name,
333357
"--from-date=2000-01-01",
358+
"--to-date=2025-01-01",
334359
],
335360
)
336361
self.assertEqual(result.exit_code, 0)
@@ -363,7 +388,10 @@ def test_non_git_repo(self):
363388
self.assertEqual(quickstart_metrics["developer_categories_core"], 3)
364389
self.assertEqual(quickstart_metrics["developer_categories_regular"], 13)
365390
self.assertEqual(quickstart_metrics["developer_categories_casual"], 9)
366-
self.assertAlmostEqual(quickstart_metrics["commits_week_mean"], 0.06418, delta=0.1)
391+
# From 2000 to 2025 there are 9132 days
392+
self.assertAlmostEqual(quickstart_metrics["commits_per_week"], 164 / (9132 / 7), delta=0.1)
393+
self.assertAlmostEqual(quickstart_metrics["commits_per_month"], 164 / (9132 / 30), delta=0.1)
394+
self.assertAlmostEqual(quickstart_metrics["commits_per_year"], 164 / (9132 / 365), delta=0.1)
367395

368396
self.assertIn("SPDXRef-sql-dk", metrics["packages"])
369397
self.assertEqual(metrics["packages"]["SPDXRef-sql-dk"]["metrics"], None)

tests/unit/test_metrics.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,23 @@ def test_message_size_metrics(self):
145145
self.assertAlmostEqual(metrics["mean"], 210.11, delta=0.1)
146146
self.assertEqual(metrics["median"], 229)
147147

148-
def test_get_commits_week_mean(self):
148+
def test_get_commits_frequency(self):
149149
"""Test whether the average (mean) commits per week is calculated correctly"""
150150

151151
self.analyzer.process_events(self.events)
152-
avg = self.analyzer.get_commits_week_mean(days_interval=30)
153-
self.assertAlmostEqual(avg, 9 / 30 / 7, delta=0.1)
152+
metrics = self.analyzer.get_commit_frequency_metrics(days_interval=30)
153+
self.assertAlmostEqual(metrics["week"], 9 / (30 / 7), delta=0.1)
154+
self.assertAlmostEqual(metrics["month"], 9, delta=0.1)
155+
self.assertIsNone(metrics["year"])
156+
157+
def test_get_all_commits_frequency(self):
158+
"""Test whether the average (mean) commits per week and year is calculated correctly"""
159+
160+
self.analyzer.process_events(self.events)
161+
metrics = self.analyzer.get_commit_frequency_metrics(days_interval=365)
162+
self.assertAlmostEqual(metrics["week"], 9 / (365 / 7), delta=0.1)
163+
self.assertAlmostEqual(metrics["month"], 9 / (365 / 30), delta=0.1)
164+
self.assertAlmostEqual(metrics["year"], 9, delta=0.1)
154165

155166
def test_get_developer_categories(self):
156167
"""Test if the developer categories are calculated correctly"""

trustable_cli/cli.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@
7373
@click.option(
7474
"--to-date",
7575
type=click.DateTime(formats=["%Y-%m-%d"]),
76-
help="End date",
76+
help="End date, by default today",
77+
default=datetime.datetime.today().strftime("%Y-%m-%d"),
7778
)
7879
@click.option("--verify-certs", is_flag=True, default=False, help="Verify SSL/TLS certificates")
7980
@click.option("--verbose", is_flag=True, default=False, help="Increase output verbosity")

trustable_cli/metrics.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,25 @@ def get_message_size_metrics(self):
140140
}
141141
return metrics
142142

143-
def get_commits_week_mean(self, days_interval: int):
143+
def get_commit_frequency_metrics(self, days_interval: int):
144144
"""
145-
Get the average (mean) number of commits per week
145+
Get the average (mean) number of commits per week, month and
146+
year if the days of interval is greater than the metrics interval.
146147
147148
:param days_interval: Interval of days to calculate the mean
148149
"""
149-
return self.total_commits / days_interval / 7
150+
metrics = {"week": None, "month": None, "year": None}
151+
152+
if days_interval >= 7:
153+
metrics["week"] = self.total_commits / (days_interval / 7)
154+
155+
if days_interval >= 30:
156+
metrics["month"] = self.total_commits / (days_interval / 30)
157+
158+
if days_interval >= 365:
159+
metrics["year"] = self.total_commits / (days_interval / 365)
160+
161+
return metrics
150162

151163
def get_developer_categories(self):
152164
"""Return the number of core, regular and casual developers"""
@@ -248,14 +260,14 @@ def get_repository_metrics(
248260
days = (to_date - from_date).days
249261
else:
250262
days = 365
251-
metrics["metrics"]["commits_week_mean"] = analyzer.get_commits_week_mean(days)
252263

253264
# Flatten two-level metrics
254265
metrics_to_flatten = {
255266
"file_types": analyzer.get_file_type_metrics(),
256267
"commit_size": analyzer.get_commit_size_metrics(),
257268
"message_size": analyzer.get_message_size_metrics(),
258269
"developer_categories": analyzer.get_developer_categories(),
270+
"commits_per": analyzer.get_commit_frequency_metrics(days),
259271
}
260272

261273
for prefix, metrics_set in metrics_to_flatten.items():

0 commit comments

Comments
 (0)