| 1 |
#!/opt/trac/0.10.3/install/bin/python |
|---|
| 2 |
|
|---|
| 3 |
import reporter, slimtimer, users, time_store |
|---|
| 4 |
import datetime |
|---|
| 5 |
import trac.env |
|---|
| 6 |
import logging |
|---|
| 7 |
import logging.handlers |
|---|
| 8 |
import os |
|---|
| 9 |
import sys |
|---|
| 10 |
import getopt |
|---|
| 11 |
import time |
|---|
| 12 |
|
|---|
| 13 |
class ReportDumper: |
|---|
| 14 |
|
|---|
| 15 |
def dump(self, trac_base, range_start, range_end): |
|---|
| 16 |
|
|---|
| 17 |
# |
|---|
| 18 |
# Get the trac environment. |
|---|
| 19 |
# |
|---|
| 20 |
env = trac.env.open_environment(trac_base) |
|---|
| 21 |
if not env: |
|---|
| 22 |
print "Couldn't open trac environment at: %s" % trac_base |
|---|
| 23 |
return |
|---|
| 24 |
|
|---|
| 25 |
# |
|---|
| 26 |
# Setup a logger |
|---|
| 27 |
# |
|---|
| 28 |
log_file = env.config.get('slimtimer', 'report_log') |
|---|
| 29 |
if log_file: |
|---|
| 30 |
logger = logging.getLogger("ReportDumper") |
|---|
| 31 |
if not os.path.exists(os.path.dirname(log_file)): |
|---|
| 32 |
print "Couldn't find log file: %s. Aborting. " % log_file |
|---|
| 33 |
return |
|---|
| 34 |
handler = \ |
|---|
| 35 |
logging.handlers.RotatingFileHandler(log_file, 'a', 20000, 5) |
|---|
| 36 |
formatter = \ |
|---|
| 37 |
logging.Formatter("[%(asctime)s] %(levelname)s: %(message)s") |
|---|
| 38 |
handler.setFormatter(formatter) |
|---|
| 39 |
logger.addHandler(handler) |
|---|
| 40 |
logger.setLevel(logging.DEBUG) |
|---|
| 41 |
else: |
|---|
| 42 |
logger = env.log |
|---|
| 43 |
|
|---|
| 44 |
# |
|---|
| 45 |
# Check we have some basic stuff for connecting to ST |
|---|
| 46 |
# |
|---|
| 47 |
st_api_key = env.config.get('slimtimer', 'api_key') |
|---|
| 48 |
if not st_api_key: |
|---|
| 49 |
logger.warn("API key for connecting to SlimTimer was not found.") |
|---|
| 50 |
return |
|---|
| 51 |
|
|---|
| 52 |
# |
|---|
| 53 |
# Connect to the database |
|---|
| 54 |
# |
|---|
| 55 |
db_host = env.config.get('slimtimer', 'db_host') |
|---|
| 56 |
db_username = env.config.get('slimtimer', 'db_username') |
|---|
| 57 |
db_password = env.config.get('slimtimer', 'db_password') |
|---|
| 58 |
db_dsn = env.config.get('slimtimer', 'db_dsn') |
|---|
| 59 |
missing = [] |
|---|
| 60 |
|
|---|
| 61 |
if not db_host: missing.append('host') |
|---|
| 62 |
if not db_username: missing.append('username') |
|---|
| 63 |
if not db_dsn: missing.append('database name') |
|---|
| 64 |
|
|---|
| 65 |
if len(missing): |
|---|
| 66 |
logger.warn("Missing %s for connecting to the datastore" % |
|---|
| 67 |
','.join(missing)) |
|---|
| 68 |
return |
|---|
| 69 |
|
|---|
| 70 |
try: |
|---|
| 71 |
ts = time_store.TimeStore(host = db_host, |
|---|
| 72 |
user = db_username, |
|---|
| 73 |
password = db_password, |
|---|
| 74 |
database = db_dsn) |
|---|
| 75 |
except: |
|---|
| 76 |
logger.error("Couldn't connect to database %s (%s@%s)" % |
|---|
| 77 |
(db_dsn, db_username, db_host)) |
|---|
| 78 |
return |
|---|
| 79 |
|
|---|
| 80 |
# |
|---|
| 81 |
# Load the list of users |
|---|
| 82 |
# |
|---|
| 83 |
users_config_file = os.path.join(trac_base, 'conf', 'users.xml') |
|---|
| 84 |
usrs = users.Users(users_config_file) |
|---|
| 85 |
|
|---|
| 86 |
# |
|---|
| 87 |
# Get the trac db to update |
|---|
| 88 |
# |
|---|
| 89 |
trac_db = env.get_db_cnx() |
|---|
| 90 |
if not trac_db: |
|---|
| 91 |
logger.warn("Could not fetch the trac database. " |
|---|
| 92 |
"trac will not be updated.") |
|---|
| 93 |
|
|---|
| 94 |
# |
|---|
| 95 |
# Iterate through users for whom we should do reporting |
|---|
| 96 |
# |
|---|
| 97 |
for user in usrs.users.values(): |
|---|
| 98 |
if user.get('report',False) and user.get('st_user',''): |
|---|
| 99 |
st_username = user['st_user'] |
|---|
| 100 |
st_password = user.get('st_pass','') |
|---|
| 101 |
|
|---|
| 102 |
try: |
|---|
| 103 |
st = slimtimer.SlimTimerSession(st_username, st_password, |
|---|
| 104 |
st_api_key) |
|---|
| 105 |
except: |
|---|
| 106 |
logger.error( |
|---|
| 107 |
"Could not log in to SlimTimer with username %s,"\ |
|---|
| 108 |
" password %s character(s) in length, and API "\ |
|---|
| 109 |
"key %s." % |
|---|
| 110 |
(st_username, len(st_password), st_api_key)) |
|---|
| 111 |
return None |
|---|
| 112 |
|
|---|
| 113 |
rpt = reporter.Reporter(ts, st, usrs, logger, trac_db) |
|---|
| 114 |
rpt.fetch_entries(range_start, range_end) |
|---|
| 115 |
|
|---|
| 116 |
logger.debug("Dumped records from %s to %s" % (range_start, range_end)) |
|---|
| 117 |
|
|---|
| 118 |
def usage(): |
|---|
| 119 |
print """Usage: |
|---|
| 120 |
dump_users --tracbase=<base_path> [range] |
|---|
| 121 |
|
|---|
| 122 |
Options are: |
|---|
| 123 |
--tracbase, -b The absolute path to the base of the trac installation. |
|---|
| 124 |
e.g. /var/trac/0.10.3/var/trac |
|---|
| 125 |
|
|---|
| 126 |
--rangestart, -s A date and time specifying the beginning of the range for |
|---|
| 127 |
filtering time entries. This range filters time entry by |
|---|
| 128 |
their START time. Format is YYYY-MM-DD or |
|---|
| 129 |
YYYY-MM-DDTHH:MM:SS. This option may be omitted but noone's |
|---|
| 130 |
quite sure what will happen if you do that. |
|---|
| 131 |
|
|---|
| 132 |
--rangeempty, -e A date an time specifying the end of the range of for |
|---|
| 133 |
filtering time entries. Format is YYYY-MM-DD or |
|---|
| 134 |
YYYY-MM-DDTHH:MM:SS. This option may also be omitted at |
|---|
| 135 |
your own risk. Have a nice day. |
|---|
| 136 |
|
|---|
| 137 |
--days, -d Set a date range by specifying the number of days in the |
|---|
| 138 |
past to go back. Don't use this with --rangestart and |
|---|
| 139 |
--rangeempty. Who knows what will happen. |
|---|
| 140 |
""" |
|---|
| 141 |
|
|---|
| 142 |
def parse_date(date_str): |
|---|
| 143 |
# |
|---|
| 144 |
# Hey, look over there! |
|---|
| 145 |
# |
|---|
| 146 |
try: |
|---|
| 147 |
return datetime.datetime(*(time.strptime(date_str, |
|---|
| 148 |
"%Y-%m-%d %H:%M:%S")[0:6])) |
|---|
| 149 |
except: pass |
|---|
| 150 |
|
|---|
| 151 |
try: |
|---|
| 152 |
return datetime.datetime(*(time.strptime(date_str, |
|---|
| 153 |
"%Y-%m-%dT%H:%M:%S")[0:6])) |
|---|
| 154 |
except: pass |
|---|
| 155 |
|
|---|
| 156 |
try: |
|---|
| 157 |
return datetime.datetime(*(time.strptime(date_str, |
|---|
| 158 |
"%Y-%m-%d")[0:6])) |
|---|
| 159 |
except: |
|---|
| 160 |
return None |
|---|
| 161 |
|
|---|
| 162 |
def main(argv): |
|---|
| 163 |
try: |
|---|
| 164 |
opts, args = getopt.getopt(argv, "b:s:e:d:", |
|---|
| 165 |
["tracbase=", "rangestart=", "rangeend=", "days="]) |
|---|
| 166 |
except getopt.GetoptError: |
|---|
| 167 |
usage() |
|---|
| 168 |
sys.exit(2) |
|---|
| 169 |
|
|---|
| 170 |
tracbase = '' |
|---|
| 171 |
range_start = None |
|---|
| 172 |
range_end = None |
|---|
| 173 |
ndays = -1 |
|---|
| 174 |
|
|---|
| 175 |
for opt, arg in opts: |
|---|
| 176 |
if opt in ("-b", "--tracbase"): |
|---|
| 177 |
tracbase = arg |
|---|
| 178 |
elif opt in ("-s", "--rangestart"): |
|---|
| 179 |
range_start = parse_date(arg) |
|---|
| 180 |
elif opt in ("-e", "--rangeend"): |
|---|
| 181 |
range_end = parse_date(arg) |
|---|
| 182 |
elif opt in ("-d", "--days"): |
|---|
| 183 |
ndays = int(arg) |
|---|
| 184 |
|
|---|
| 185 |
if not tracbase: |
|---|
| 186 |
usage() |
|---|
| 187 |
sys.exit(2) |
|---|
| 188 |
|
|---|
| 189 |
if ndays != -1: |
|---|
| 190 |
range_end = datetime.datetime.today() |
|---|
| 191 |
range_end += datetime.timedelta(days=1) |
|---|
| 192 |
range_end -= datetime.timedelta(hours=range_end.hour, |
|---|
| 193 |
minutes=range_end.minute, |
|---|
| 194 |
seconds=range_end.second) |
|---|
| 195 |
range_start = range_end |
|---|
| 196 |
range_start -= datetime.timedelta(days=ndays) |
|---|
| 197 |
|
|---|
| 198 |
dumper = ReportDumper() |
|---|
| 199 |
dumper.dump(tracbase, range_start, range_end) |
|---|
| 200 |
|
|---|
| 201 |
if __name__ == '__main__': |
|---|
| 202 |
main(sys.argv[1:]) |
|---|