-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdrupalcodeswarmlog.py
More file actions
198 lines (181 loc) · 8.23 KB
/
Copy pathdrupalcodeswarmlog.py
File metadata and controls
198 lines (181 loc) · 8.23 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
#!/usr/bin/python
"""
The script that writes codeswarm xml.
"""
from optparse import OptionParser
import git, re, os, sys
"""
Return a special git-log output of the repo and using target.
"""
def get_git_log(repo, target, whole_history=False):
if whole_history:
return repo.git.log(target, "--all", "--reverse", date="iso", format="%H\t%at\t%an\t%s")
else:
return repo.git.log(target, "--reverse", date="iso", format="%H\t%at\t%an\t%s")
"""
Return one commit_id git-whatchanged output of the repo.
"""
def get_git_changes(repo, commit_id):
#git whatchanged -1 --format="%an" $HASH
return repo.git.whatchanged(commit_id, "-1", format="%an")
"""
Process git-whatchanged ouput and return the list of files relevant to
the commit.
"""
def get_changes(git_changes):
#grep ^:| cut -f 2 | while read FILE; do
changes = []
for line in git_changes.split("\n"):
if line.startswith(":"):
changes.append(line.split("\t")[1])
return changes
"""
Take a one-line comit message and return a list of string with one
author per item.
The standard message should look like:
[issue category] #[issue number] by [comma-separated usernames]: [Short summary of the change].
This is a list of example input subjects:
- "#645790 by arianek, jhodgdon, and lisarex: Convert Locale module to new help standard."
- "#645790: Convert Locale module to new help standard."
- "by arianek, jhodgdon, and lisarex: Convert Locale module to new help standard."
- "- Patch #88892 by darthsteven et al: improved the PHPdoc of form_set_value(). Great work. Much better. :)"
- "#198579 by webernet and hswong3i: a huge set of coding style fixes, including:"
- "#193274 by dmitrig01 and quicksketch: send submit button data with AHAH submissions"
- "#601806 by sun, effulgentsia, and Damien Tournoud: drupal_render() should not set default element properties that make no sense."
- "#307477 by clemens.tolboom and boombatower: Test how XML-RPC responds to large messages."
- "Patch #164532 by catch, pwolanin, David Strauss, et al.: improve table indicies for common queries."
"""
def get_authors(subject):
pattern = r"by (?P<raw_authors>[\w\d\s,.]+):"
matches = re.search(pattern, subject)
# if checking invalid_strings affects performance, take invalid
# messages out grepping `author="<something>"`
invalid_strings = ["", "me", "myself",
"their authors",
"in the following manner",
"going to http",
"improve to the core doxygen PHPdoc",
"after coding style fixes",
"and lots",
"lots of other people",
"thousands of other people",
]
strip_chars = ". "
if matches is None:
return
raw_authors = matches.group("raw_authors").split(",")
authors = []
for raw_author in raw_authors:
raw_author = raw_author.strip()
# try to avoid common non-standard messages once(non recursive)
pos1 = raw_author.find("et al")
pos2 = raw_author.find(" and ")
# the following can be moved to a post-process grep if
# performance is affected
pos3 = raw_author.find("slightly")
pos4 = raw_author.find("people")
pos5 = raw_author.find("contributor")
pos6 = raw_author.find("by ")
if pos1 != -1:
raw_author = raw_author.replace("et all", "").strip(strip_chars)
raw_author = raw_author.replace("et al", "").strip(strip_chars)
if raw_author not in invalid_strings:
authors.append(raw_author)
elif pos2 != -1:
raw_author1, raw_author2 = raw_author.split(" and ")
raw_author1 = raw_author1.strip(strip_chars)
raw_author2 = raw_author2.strip(strip_chars)
if raw_author1 not in invalid_strings:
authors.append(raw_author1)
if raw_author2 not in invalid_strings:
authors.append(raw_author2)
elif raw_author.startswith("and "):
raw_author = raw_author[4:].strip(strip_chars)
if raw_author not in invalid_strings:
authors.append(raw_author)
elif pos3 != -1:
raw_author = raw_author.replace("slightly modified", "").strip(strip_chars)
raw_author = raw_author.replace("slightly extended", "").strip(strip_chars)
if raw_author not in invalid_strings:
authors.append(raw_author)
elif pos4 != -1:
raw_author = raw_author.replace("various people", "").strip(strip_chars)
raw_author = raw_author.replace("several people", "").strip(strip_chars)
if raw_author not in invalid_strings:
authors.append(raw_author)
elif pos5 != -1:
raw_author = raw_author.replace("numerous contributors", "").strip(strip_chars)
raw_author = raw_author.replace("multiple contributors", "").strip(strip_chars)
if raw_author not in invalid_strings:
authors.append(raw_author)
elif pos6 != -1:
raw_author = raw_author[pos6+2:].strip(strip_chars)
if raw_author not in invalid_strings:
authors.append(raw_author)
elif raw_author.strip(strip_chars) not in invalid_strings:
authors.append(raw_author.strip(strip_chars))
return authors
def main():
# handling argv
usage = "usage: %prog [options] path [target]\n"\
" path is the local clone of the drupalfr git repository\n"\
" target is the argument passed to git-log as <since>..<until>"
parser = OptionParser(usage=usage)
parser.add_option("-o", "--output", metavar="FILE", dest="filename",
help="write output to FILE")
parser.add_option("-a", "--full-history", action="store_true", dest="full_history",
help="Use `--all` at git-log. This option is incompatible with target argument")
(options, args) = parser.parse_args()
if options.full_history:
if len(args) != 1:
parser.error("Incorrect number of arguments.\n"
"If you provide -a you can not set the target since\n"
"git-log anyway is going to retrieve whole history")
target = 'master'
else:
if len(args) != 2:
parser.error("Incorrect number of arguments.")
target = args[1]
if options.filename is not None:
sys.stdout = open(options.filename, 'w')
path_to_repo = os.path.abspath(args[0])
repo = git.Repo(path_to_repo)
# get git output
git_log = get_git_log(repo, target, options.full_history)
print '<?xml version="1.0"?>\n<file_events>'
for log_line in git_log.splitlines():
commit_id, timestamp, commiter, subject = log_line.split("\t")
git_changes = get_git_changes(repo, commit_id)
changes = get_changes(git_changes)
for commited_file in changes:
print '<event date="' + timestamp + '000" filename="/cvs/drupal/drupal/' + commited_file + '" author="' + commiter + '" />'
# process each author parsing subject
authors = get_authors(subject)
if authors is not None:
for author in authors:
print '<event date="' + timestamp + '000" filename="/cvs/drupal/drupal/' + commited_file + '" author="' + author + '" />'
print '</file_events>'
"""
At post-process we need to take care of non-standard messages,
replacing:
- simple author replace by errors:
"Alexander. You can use this script to check your code against the Drupal coding style": Alexander
"Jeremy to fix a module loading bug": Jeremy
"with help from Nick": Nick
"Ax to fixe": Ax
"Usability Poobah Chris": factoryjoe
"with help from Nick": Nick
"Jose A Reyero with further cleanup by myself": Jose Reyero
"kkaefer with fixes from myself": kkaefer
"with help from Kristi Wachter": Kristi Wachter
"in patch form by Rob Loach": Rob Loach
- simple author replace by identification:
"Damien": Damien Tournoud
"DamZ": Damien Tournoud
- split one line in two:
"Mathias with help from Steven": Mathias, Steven
"keith.smith based on initial suggestions from O Govinda": keith.smith, O Govinda
- join same names with different capitalization
"""
if __name__ == "__main__":
main()