Skip to content

Commit 4e2e029

Browse files
committed
feat: enhance create_loc_graph function with empty data handling and add unit tests for various scenarios
1 parent d0c46d7 commit 4e2e029

File tree

3 files changed

+188
-15
lines changed

3 files changed

+188
-15
lines changed

.pre-commit-config.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
11
repos:
2-
- repo: https://github.com/pre-commit/pre-commit-hooks
3-
rev: v2.3.0
4-
hooks:
5-
- id: check-yaml
6-
- id: end-of-file-fixer
7-
- id: trailing-whitespace
82
- repo: local
93
hooks:
104
- id: pytest-check
@@ -13,6 +7,12 @@ repos:
137
language: system
148
pass_filenames: false
159
always_run: true
10+
- repo: https://github.com/pre-commit/pre-commit-hooks
11+
rev: v2.3.0
12+
hooks:
13+
- id: check-yaml
14+
- id: end-of-file-fixer
15+
- id: trailing-whitespace
1616
- repo: https://github.com/PyCQA/flake8
1717
rev: 6.0.0
1818
hooks:

sources/graphics_chart_drawer.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
from typing import Dict
22

3-
from numpy import arange, array, add, amax, zeros
43
import matplotlib.patches as mpatches
54
import matplotlib.pyplot as plt
6-
75
from manager_download import DownloadManager as DM
86
from manager_file import FileManager as FM
9-
7+
from numpy import add, amax, arange, array, zeros
108

119
MAX_LANGUAGES = 5 # Number of top languages to add to chart, for each year quarter
1210
GRAPH_PATH = f"{FM.ASSETS_DIR}/bar_graph.png" # Chart saving path.
@@ -20,6 +18,15 @@ async def create_loc_graph(yearly_data: Dict, save_path: str):
2018
:param yearly_data: GitHub user yearly data.
2119
:param save_path: Path to save the graph file.
2220
"""
21+
# Handle empty data case
22+
if not yearly_data:
23+
fig = plt.figure()
24+
ax = fig.add_axes([0, 0, 1.5, 1])
25+
ax.set_ylabel("LOC added", fontdict=dict(weight="bold"))
26+
plt.savefig(save_path, bbox_inches="tight")
27+
plt.close(fig)
28+
return
29+
2330
colors = await DM.get_remote_yaml("linguist")
2431
if colors is None:
2532
colors = dict()
@@ -49,7 +56,10 @@ async def create_loc_graph(yearly_data: Dict, save_path: str):
4956
cumulative = zeros((years, 4, 2), dtype=int)
5057

5158
for key, value in languages_all_loc.items():
52-
color = colors[key].get("color", "tab:gray")
59+
# Handle missing colors gracefully
60+
color = "tab:gray"
61+
if key in colors:
62+
color = colors[key].get("color", "tab:gray")
5363
language_handles += [mpatches.Patch(color=color, label=key)]
5464

5565
for quarter in range(4):
@@ -94,11 +104,12 @@ async def create_loc_graph(yearly_data: Dict, save_path: str):
94104
ax.spines["top"].set_visible(False)
95105
ax.spines["right"].set_visible(False)
96106

97-
max_offset = 0.05 * amax(cumulative.flatten())
98-
joined = cumulative.reshape(-1, cumulative.shape[-1])
99-
max_additions = amax(joined[:, 0])
100-
max_deletions = amax(joined[:, 1])
101-
plt.ylim(top=max_additions + max_offset, bottom=-max_deletions - max_offset)
107+
if cumulative.size > 0: # Only calculate limits if there's data
108+
max_offset = 0.05 * amax(cumulative.flatten())
109+
joined = cumulative.reshape(-1, cumulative.shape[-1])
110+
max_additions = amax(joined[:, 0])
111+
max_deletions = amax(joined[:, 1])
112+
plt.ylim(top=max_additions + max_offset, bottom=-max_deletions - max_offset)
102113

103114
plt.savefig(save_path, bbox_inches="tight")
104115
plt.close(fig)
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import os
2+
from unittest.mock import AsyncMock, patch
3+
4+
import pytest
5+
6+
# Import the function and constants we're testing
7+
from graphics_chart_drawer import GRAPH_PATH, MAX_LANGUAGES, create_loc_graph
8+
9+
10+
@pytest.fixture
11+
def sample_yearly_data():
12+
return {
13+
"2022": {
14+
1: {
15+
"Python": {"add": 100, "del": 50},
16+
"JavaScript": {"add": 80, "del": 30},
17+
"Java": {"add": 60, "del": 20},
18+
},
19+
2: {
20+
"Python": {"add": 120, "del": 40},
21+
"JavaScript": {"add": 90, "del": 35},
22+
},
23+
3: {
24+
"Python": {"add": 150, "del": 60},
25+
"TypeScript": {"add": 70, "del": 25},
26+
},
27+
4: {
28+
"Python": {"add": 200, "del": 80},
29+
"Go": {"add": 100, "del": 40},
30+
},
31+
},
32+
"2023": {
33+
1: {
34+
"Python": {"add": 180, "del": 70},
35+
"Rust": {"add": 90, "del": 30},
36+
},
37+
2: {
38+
"Python": {"add": 160, "del": 65},
39+
"JavaScript": {"add": 85, "del": 25},
40+
},
41+
3: {
42+
"Python": {"add": 140, "del": 55},
43+
"TypeScript": {"add": 75, "del": 30},
44+
},
45+
4: {
46+
"Python": {"add": 130, "del": 50},
47+
"Go": {"add": 95, "del": 35},
48+
},
49+
},
50+
}
51+
52+
53+
@pytest.fixture
54+
def sample_colors():
55+
return {
56+
"Python": {"color": "blue"},
57+
"JavaScript": {"color": "yellow"},
58+
"Java": {"color": "red"},
59+
"TypeScript": {"color": "blue"},
60+
"Go": {"color": "cyan"},
61+
"Rust": {"color": "orange"},
62+
}
63+
64+
65+
@pytest.mark.asyncio
66+
async def test_create_loc_graph_success(sample_yearly_data, sample_colors, tmp_path):
67+
test_save_path = str(tmp_path / "test_graph.png")
68+
69+
with patch(
70+
"manager_download.DownloadManager.get_remote_yaml", new_callable=AsyncMock
71+
) as mock_get_yaml:
72+
mock_get_yaml.return_value = sample_colors
73+
74+
await create_loc_graph(sample_yearly_data, test_save_path)
75+
76+
assert os.path.exists(test_save_path)
77+
assert os.path.getsize(test_save_path) > 0
78+
79+
80+
@pytest.mark.asyncio
81+
async def test_create_loc_graph_no_colors(sample_yearly_data, tmp_path):
82+
test_save_path = str(tmp_path / "test_graph_no_colors.png")
83+
84+
with patch(
85+
"manager_download.DownloadManager.get_remote_yaml", new_callable=AsyncMock
86+
) as mock_get_yaml:
87+
mock_get_yaml.return_value = None
88+
89+
await create_loc_graph(sample_yearly_data, test_save_path)
90+
91+
assert os.path.exists(test_save_path)
92+
assert os.path.getsize(test_save_path) > 0
93+
94+
95+
@pytest.mark.asyncio
96+
async def test_create_loc_graph_empty_data(tmp_path):
97+
test_save_path = str(tmp_path / "test_graph_empty.png")
98+
empty_data = {}
99+
100+
with patch(
101+
"manager_download.DownloadManager.get_remote_yaml", new_callable=AsyncMock
102+
) as mock_get_yaml:
103+
mock_get_yaml.return_value = {}
104+
105+
await create_loc_graph(empty_data, test_save_path)
106+
107+
assert os.path.exists(test_save_path)
108+
assert os.path.getsize(test_save_path) > 0
109+
110+
111+
@pytest.mark.asyncio
112+
async def test_create_loc_graph_single_language(tmp_path):
113+
test_save_path = str(tmp_path / "test_graph_single.png")
114+
single_lang_data = {
115+
"2022": {
116+
1: {"Python": {"add": 100, "del": 50}},
117+
2: {"Python": {"add": 120, "del": 40}},
118+
3: {"Python": {"add": 150, "del": 60}},
119+
4: {"Python": {"add": 200, "del": 80}},
120+
}
121+
}
122+
123+
with patch(
124+
"manager_download.DownloadManager.get_remote_yaml", new_callable=AsyncMock
125+
) as mock_get_yaml:
126+
mock_get_yaml.return_value = {"Python": {"color": "blue"}}
127+
128+
await create_loc_graph(single_lang_data, test_save_path)
129+
130+
assert os.path.exists(test_save_path)
131+
assert os.path.getsize(test_save_path) > 0
132+
133+
134+
@pytest.mark.asyncio
135+
async def test_create_loc_graph_max_languages(
136+
sample_yearly_data, sample_colors, tmp_path
137+
):
138+
test_save_path = str(tmp_path / "test_graph_max.png")
139+
140+
# Add more than MAX_LANGUAGES languages to test the limit
141+
sample_yearly_data["2022"][1].update(
142+
{
143+
"Ruby": {"add": 50, "del": 20},
144+
"C++": {"add": 40, "del": 15},
145+
"PHP": {"add": 30, "del": 10},
146+
}
147+
)
148+
149+
with patch(
150+
"manager_download.DownloadManager.get_remote_yaml", new_callable=AsyncMock
151+
) as mock_get_yaml:
152+
mock_get_yaml.return_value = sample_colors
153+
154+
await create_loc_graph(sample_yearly_data, test_save_path)
155+
156+
assert os.path.exists(test_save_path)
157+
assert os.path.getsize(test_save_path) > 0
158+
159+
160+
def test_constants():
161+
assert MAX_LANGUAGES == 5
162+
assert GRAPH_PATH.endswith("/bar_graph.png")

0 commit comments

Comments
 (0)