-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathJSONToCSV.py
More file actions
254 lines (223 loc) · 12.2 KB
/
Copy pathJSONToCSV.py
File metadata and controls
254 lines (223 loc) · 12.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
import json
import csv
import pickle
import os
import sys
import re
from datetime import datetime
#----------------------------------------------------------------------------------------------------------------------
# Function to clean text by replacing newline characters with spaces
# Input - text: a string which may contain newline characters
# Output - a string with newline characters replaced by spaces
# Written by Adonijah Farner
#----------------------------------------------------------------------------------------------------------------------
def clean_text(text):
"""Replace newline characters with spaces, handling None values."""
if text is None:
return ""
return text.replace('\n', ' ').replace('\r', ' ')
#----------------------------------------------------------------------------------------------------------------------
# End of function clean_text
#----------------------------------------------------------------------------------------------------------------------
# ----------------------------------------------------------------------------------------------------------------------
# Function to find linked issues in the body text and the keyword used to describe the PR
# Input - text: The body of a pr
# Output - two lists, one of issues and another of keywords
# Written by Adonijah Farner
# ----------------------------------------------------------------------------------------------------------------------
def find_linked_issues(body_text):
keywords = [
"closes", "fixes", "resolves", "in", "solves",
"addresses", "completes", "connects", "related to", "reverts",
"implements", "references", "incorporates", "updates", "handles",
"patches", "adds", "modifies", "enhances", "improves", "adjusts"
]
linked_issues = []
description_keywords = []
for keyword in keywords:
pattern = fr'{keyword} (#\d+|https://github\.com/\S+/issues/\d+)'
matches = re.findall(pattern, body_text, re.IGNORECASE)
for match in matches:
linked_issues.append(match)
description_keywords.append(keyword)
return linked_issues, description_keywords
# ----------------------------------------------------------------------------------------------------------------------
# End of function find_linked_issues
# ----------------------------------------------------------------------------------------------------------------------
#----------------------------------------------------------------------------------------------------------------------
# Function to extract data from a pull request (PR) dictionary
# Input - pr: a dictionary representing a pull request
# Output - a dictionary with cleaned and structured data from the PR
# Written by Adonijah Farner
# Modified to include created_at, closed_at, userlogin, author_name, comments, and files_changed
# Date: 5/15/2024
#----------------------------------------------------------------------------------------------------------------------
def extract_data(pr):
body_text = clean_text(pr.get("body", ""))
linked_issues, description_keywords = find_linked_issues(body_text)
data = {
"issue": clean_text(pr.get("title", "")),
"Pull Request": pr.get("is_pr", ""),
"issue text": " | ".join(linked_issues),
"issue description": " | ".join(description_keywords),
"pull request text": clean_text(pr.get("title", "")),
"pull request description": clean_text(pr.get("body", "")),
#----------------------------------------------------------------------------------------------------------------------
# Modified to include created_at, closed_at, and userlogin fields
# Date: 5/15/2024
#----------------------------------------------------------------------------------------------------------------------
"created_at": clean_text(pr.get("created_at", "")),
"closed_at": clean_text(pr.get("closed_at", "")),
"userlogin": clean_text(pr.get("userlogin", "")),
}
# ----------------------------------------------------------------------------------------------------------------------
# Modified to include concatenated list of comments
# Date: 5/18/2024
# Modified by Adonijah Farner
# ----------------------------------------------------------------------------------------------------------------------
# Process comments
comments = []
if pr.get("comments"):
# print(f"Processing comments for PR: {pr}")
for comment in pr["comments"].values():
body = clean_text(comment.get("body", ""))
# print(f"Found comment body: {body}")
comments.append(body)
data["comments"] = " | ".join(comments)
# ----------------------------------------------------------------------------------------------------------------------
# Modified to include concatenated list of files changed
# Date: 5/18/2024
# Modified by Adonijah Farner
# ----------------------------------------------------------------------------------------------------------------------
# Collect files changed across all commits
files_changed = []
commit_hashes = []
commits = []
if pr.get("commits"):
for commit in pr["commits"].values():
commit_date = commit.get("date", "")
if commit_date: # Only parse if commit_date is not empty
try:
commit_date_obj = datetime.strptime(commit_date, "%Y-%m-%dT%H:%M:%SZ")
commits.append((commit_date_obj, commit.get("sha", ""), commit.get("author_name", "")))
except ValueError:
continue # Skip this commit if the date is invalid
files = commit.get("files", {})
if files:
files_changed.extend(files.get("file_list", []))
# Sort commits by date (newest to oldest)
commits.sort(key=lambda x: x[0], reverse=True)
sorted_commit_hashes = [commit[1] for commit in commits]
newest_commit_hash = sorted_commit_hashes[0] if sorted_commit_hashes else ""
data["files_changed"] = " | ".join(files_changed)
data["commit_hashes"] = " | ".join(sorted_commit_hashes)
data["newest_commit_hash"] = newest_commit_hash
if commits:
data["author_name"] = commits[0][2] # Use the author of the newest commit
return data
#----------------------------------------------------------------------------------------------------------------------
# End of function extract_data
#----------------------------------------------------------------------------------------------------------------------
#----------------------------------------------------------------------------------------------------------------------
# Function to convert JSON data to a specified pickle format and save it
# Input - data: a dictionary containing pull request data
# pickle_file: the name of the output pickle file
# Output - a pickle file with the processed pull request data
# Written by Adonijah Farner
# Date: 5/21/2024
#----------------------------------------------------------------------------------------------------------------------
def convert_to_pickle(data, pickle_file):
"""Convert the JSON data to the specified pickle format and save it."""
# Create a list to store the converted data
pickle_data = []
for idx, pr_id in enumerate(data.keys(), start=1):
try:
pr = data[pr_id]
row_data = extract_data(pr)
# Format the data into the desired structure
formatted_data = [
idx,
pr_id,
row_data.get("Pull Request", ""),
row_data.get("issue text", ""),
row_data.get("issue description", ""),
row_data.get("pull request text", ""),
row_data.get("pull request description", ""),
row_data.get("created_at", ""),
row_data.get("closed_at", ""),
row_data.get("userlogin", ""),
row_data.get("author_name", ""),
row_data.get("comments", "").split(" | "),
row_data.get("files_changed", "").split(" | "),
row_data.get("commit_hashes", "").split(" | "),
row_data.get("newest_commit_hash", "")
]
# Append the formatted data to the list
pickle_data.append(formatted_data)
except Exception as e:
print(f"Error processing entry {pr_id} for pickle: {e}")
# ----------------------------------------------------------------------------------------------------------------------
# Modified to have pickle file name match json file name
# Date: 6/10/2024
# Modified by Adonijah Farner
# ----------------------------------------------------------------------------------------------------------------------
# Save the list to a pickle file
with open(pickle_file, 'wb') as pf:
pickle.dump(pickle_data, pf)
print(f"Data successfully saved to {pickle_file}")
#----------------------------------------------------------------------------------------------------------------------
# End of function convert_to_pickle
#----------------------------------------------------------------------------------------------------------------------
#----------------------------------------------------------------------------------------------------------------------
# Main script to read JSON data, process it, and write to a CSV file
# Input - JSON file (jabref_output.json) containing pull request data
# Output - CSV file (jabref_output.csv) with processed pull request data
# Written by Adonijah Farner
# Modified to include created_at, closed_at, userlogin, author_name, comments, and files_changed
# Date: 5/15/2024
#----------------------------------------------------------------------------------------------------------------------
# Read the JSON file
# ----------------------------------------------------------------------------------------------------------------------
# Modified to read file from command line and have pickle and csv file name match json file name
# Date: 6/10/2024
# Modified by Adonijah Farner
# ----------------------------------------------------------------------------------------------------------------------
# Check if the filename is provided
if len(sys.argv) != 2:
print("Usage: python JSONToCSV.py <filename.json>")
sys.exit(1)
json_filename = sys.argv[1]
# Extract base name without extension
base_name = os.path.splitext(os.path.basename(json_filename))[0]
# Construct CSV and pickle file names
csv_filename = f"{base_name}.csv"
pickle_filename = f"{base_name}.pkl"
# Read the JSON file
with open(json_filename, 'r', encoding='utf-8') as f:
data = json.load(f)
# Open the CSV file for writing
with open(csv_filename, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
# Write the header
header = [
"Row #", "issue", "Pull Request", "issue text", "issue description",
"pull request text", "pull request description", "created_at", "closed_at", "userlogin", "author_name",
"comments", "files_changed", "commit_hashes", "newest_commit_hash"
]
writer.writerow(header)
# Write the data rows
for idx, pr_id in enumerate(data.keys(), start=1):
try:
pr = data[pr_id]
row_data = extract_data(pr)
row = [idx, pr_id] + [row_data.get(col, "") for col in header[2:]]
# print(f"Writing row for PR {pr_id}: {row}") # Debug statement to check row data
writer.writerow(row)
except Exception as e:
print(f"Error processing entry {pr_id}: {e}")
print(f"Processed {idx} entries.")
# Convert to pickle
convert_to_pickle(data, pickle_filename)
#----------------------------------------------------------------------------------------------------------------------
# End of main script
#----------------------------------------------------------------------------------------------------------------------