-
Notifications
You must be signed in to change notification settings - Fork 102
/
cli.py
236 lines (203 loc) · 8.34 KB
/
cli.py
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
# coding: utf-8
"""
Command line interface for did
This module takes care of processing command line options and
running the main loop which gathers all individual stats.
"""
from __future__ import unicode_literals, absolute_import
import re
import sys
import kerberos
import optparse
import ConfigParser
from dateutil.relativedelta import relativedelta as delta
import did.base
import did.utils as utils
from did.stats import UserStats
from did.base import ConfigError, ReportError
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Options
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class Options(object):
""" Command line options parser """
def __init__(self, arguments=None):
""" Prepare the parser. """
self.parser = optparse.OptionParser(
usage="did [last] [week|month|quarter|year] [opts]",
description=__doc__.strip())
self.arguments = arguments
# Time & user selection
group = optparse.OptionGroup(self.parser, "Selection")
group.add_option(
"--email", dest="emails", default=[], action="append",
help="User email address(es)")
group.add_option(
"--since",
help="Start date in the YYYY-MM-DD format")
group.add_option(
"--until",
help="End date in the YYYY-MM-DD format")
self.parser.add_option_group(group)
# Create sample stats and include all stats objects options
utils.log.debug("Loading Sample Stats group to build Options")
self.sample_stats = UserStats()
self.sample_stats.add_option(self.parser)
# Display mode
group = optparse.OptionGroup(self.parser, "Display mode")
group.add_option(
"--format", default="text",
help="Output style, possible values: text (default) or wiki")
group.add_option(
"--width", default=did.base.Config().width, type="int",
help="Maximum width of the report output (default: %default)")
group.add_option(
"--brief", action="store_true",
help="Show brief summary only, do not list individual items")
group.add_option(
"--verbose", action="store_true",
help="Include more details (like modified git directories)")
group.add_option(
"--total", action="store_true",
help="Append total stats after listing individual users")
group.add_option(
"--merge", action="store_true",
help="Merge stats of all users into a single report")
group.add_option(
"--debug", action="store_true",
help="Turn on debugging output, do not catch exceptions")
self.parser.add_option_group(group)
def parse(self, arguments=None):
""" Parse the options. """
if arguments is not None:
self.arguments = arguments
if (self.arguments is not None
and isinstance(self.arguments, basestring)):
self.arguments = self.arguments.split()
(opt, arg) = self.parser.parse_args(self.arguments)
# Enable debugging output
if opt.debug:
utils.Logging.set(utils.LOG_DEBUG)
# Enable --all if no particular stat or group selected
opt.all = not any([
getattr(opt, stat.dest) or getattr(opt, group.dest)
for group in self.sample_stats.stats
for stat in group.stats])
# Detect email addresses and split them on comma
if not opt.emails:
opt.emails = did.base.Config().email
opt.emails = utils.split(opt.emails, separator=re.compile(r"\s*,\s*"))
# Time period handling
if opt.since is None and opt.until is None:
opt.since, opt.until, period = self.time_period(arg)
else:
opt.since = did.base.Date(opt.since or "1993-01-01")
opt.until = did.base.Date(opt.until or "today")
# Make the 'until' limit inclusive
opt.until.date += delta(days=1)
period = "given date range"
# Validate the date range
if not opt.since.date < opt.until.date:
raise RuntimeError(
"Invalid date range ({0} to {1})".format(
opt.since, opt.until.date - delta(days=1)))
print(u"Status report for {0} ({1} to {2}).".format(
period, opt.since, opt.until.date - delta(days=1)))
# Finito
utils.log.debug("Gathered options:")
utils.log.debug('options = {0}'.format(opt))
return opt
@staticmethod
def time_period(arg):
""" Detect desired time period for the argument """
since, until, period = None, None, None
if "today" in arg:
since = did.base.Date("today")
until = did.base.Date("today")
until.date += delta(days=1)
period = "today"
elif "year" in arg:
if "last" in arg:
since, until = did.base.Date.last_year()
period = "the last fiscal year"
else:
since, until = did.base.Date.this_year()
period = "this fiscal year"
elif "quarter" in arg:
if "last" in arg:
since, until = did.base.Date.last_quarter()
period = "the last quarter"
else:
since, until = did.base.Date.this_quarter()
period = "this quarter"
elif "month" in arg:
if "last" in arg:
since, until = did.base.Date.last_month()
period = "the last month"
else:
since, until = did.base.Date.this_month()
period = "this month"
else:
if "last" in arg:
since, until = did.base.Date.last_week()
period = "the last week"
else:
since, until = did.base.Date.this_week()
period = "this week"
return since, until, period
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Main
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def main(arguments=None):
"""
Parse options, gather stats and show the results
Takes optional parameter ``arguments`` which can be useful for
testing purposes. Function returns tuple of the form::
([user_stats], team_stats)
with the list of all gathered stats objects.
"""
try:
# Parse options, initialize gathered stats
options = Options().parse(arguments)
gathered_stats = []
# Check for user email addresses (command line or config)
users = [did.base.User(email=email) for email in options.emails]
if not users:
raise ConfigError("No user email provided")
# Prepare team stats object for data merging
team_stats = UserStats(options=options)
if options.merge:
utils.header("Total Report")
utils.item("Users: {0}".format(len(users)), options=options)
# Check individual user stats
for user in users:
if options.merge:
utils.item(user, 1, options=options)
else:
utils.header(user)
user_stats = UserStats(user=user, options=options)
user_stats.check()
team_stats.merge(user_stats)
gathered_stats.append(user_stats)
# Display merged team report
if options.merge or options.total:
if options.total:
utils.header("Total Report")
team_stats.show()
# Return all gathered stats objects
return gathered_stats, team_stats
except (ConfigError, ReportError) as error:
utils.log.error(error)
sys.exit(1)
except kerberos.GSSError as error:
utils.log.error("Kerberos authentication failed. Try kinit.")
sys.exit(2)
except ConfigParser.NoSectionError as error:
utils.log.error(error)
utils.log.error(
"No email provided on the command line or in the config file")
utils.info("Create at least a minimum config file {0}:".format(
did.base.CONFIG))
from getpass import getuser
utils.info(
'[general]\nemail = "My Name" <{0}@domain.com>'.format(getuser()))
sys.exit(3)