#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------
# This file is part of TISBackup
#
# TISBackup is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# TISBackup is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with TISBackup. If not, see .
#
# -----------------------------------------------------------------------
"""Utility functions for date/time formatting, number formatting, and display helpers."""
import datetime
import os
def datetime2isodate(adatetime=None):
"""Convert datetime to ISO format string."""
if not adatetime:
adatetime = datetime.datetime.now()
assert isinstance(adatetime, datetime.datetime)
return adatetime.isoformat()
def isodate2datetime(isodatestr):
"""Convert ISO format string to datetime."""
# we remove the microseconds part as it is not working for python2.5 strptime
return datetime.datetime.strptime(isodatestr.split(".")[0], "%Y-%m-%dT%H:%M:%S")
def time2display(adatetime):
"""Format datetime for display."""
return adatetime.strftime("%Y-%m-%d %H:%M")
def hours_minutes(hours):
"""Convert decimal hours to HH:MM format."""
if hours is None:
return None
else:
return "%02i:%02i" % (int(hours), int((hours - int(hours)) * 60.0))
def fileisodate(filename):
"""Get file modification time as ISO date string."""
return datetime.datetime.fromtimestamp(os.stat(filename).st_mtime).isoformat()
def dateof(adatetime):
"""Get date part of datetime (midnight)."""
return adatetime.replace(hour=0, minute=0, second=0, microsecond=0)
#####################################
# http://code.activestate.com/recipes/498181-add-thousands-separator-commas-to-formatted-number/
# Code from Michael Robellard's comment made 28 Feb 2010
# Modified for leading +, -, space on 1 Mar 2010 by Glenn Linderman
#
# Tail recursion removed and leading garbage handled on March 12 2010, Alessandro Forghieri
def splitThousands(s, tSep=",", dSep="."):
"""Splits a general float on thousands. GIGO on general input"""
if s is None:
return 0
if not isinstance(s, str):
s = str(s)
cnt = 0
numChars = dSep + "0123456789"
ls = len(s)
while cnt < ls and s[cnt] not in numChars:
cnt += 1
lhs = s[0:cnt]
s = s[cnt:]
if dSep == "":
cnt = -1
else:
cnt = s.rfind(dSep)
if cnt > 0:
rhs = dSep + s[cnt + 1 :]
s = s[:cnt]
else:
rhs = ""
splt = ""
while s != "":
splt = s[-3:] + tSep + splt
s = s[:-3]
return lhs + splt[:-1] + rhs
def convert_bytes(bytes):
"""Convert bytes to human-readable format (T/G/M/K/b)."""
if bytes is None:
return None
else:
bytes = float(bytes)
if bytes >= 1099511627776:
terabytes = bytes / 1099511627776
size = "%.2fT" % terabytes
elif bytes >= 1073741824:
gigabytes = bytes / 1073741824
size = "%.2fG" % gigabytes
elif bytes >= 1048576:
megabytes = bytes / 1048576
size = "%.2fM" % megabytes
elif bytes >= 1024:
kilobytes = bytes / 1024
size = "%.2fK" % kilobytes
else:
size = "%.2fb" % bytes
return size
def check_string(test_string):
"""Check if string contains only alphanumeric characters, dots, dashes, and underscores."""
import re
pattern = r"[^\.A-Za-z0-9\-_]"
if re.search(pattern, test_string):
# Character other then . a-z 0-9 was found
print(("Invalid : %r" % (test_string,)))
def str2bool(val):
"""Convert string to boolean."""
if not isinstance(type(val), bool):
return val.lower() in ("yes", "true", "t", "1")
## {{{ http://code.activestate.com/recipes/81189/ (r2)
def pp(cursor, data=None, rowlens=0, callback=None):
"""
pretty print a query result as a table
callback is a function called for each field (fieldname,value) to format the output
"""
def defaultcb(fieldname, value):
return value
if not callback:
callback = defaultcb
d = cursor.description
if not d:
return "#### NO RESULTS ###"
names = []
lengths = []
rules = []
if not data:
data = cursor.fetchall()
for dd in d: # iterate over description
l = dd[1]
if not l:
l = 12 # or default arg ...
l = max(l, len(dd[0])) # handle long names
names.append(dd[0])
lengths.append(l)
for col in range(len(lengths)):
if rowlens:
rls = [len(str(callback(d[col][0], row[col]))) for row in data if row[col]]
lengths[col] = max([lengths[col]] + rls)
rules.append("-" * lengths[col])
format = " ".join(["%%-%ss" % l for l in lengths])
result = [format % tuple(names)]
result.append(format % tuple(rules))
for row in data:
row_cb = []
for col in range(len(d)):
row_cb.append(callback(d[col][0], row[col]))
result.append(format % tuple(row_cb))
return "\n".join(result)
## end of http://code.activestate.com/recipes/81189/ }}}
def html_table(cur, callback=None):
"""
cur est un cursor issu d'une requete
callback est une fonction qui prend (rowmap,fieldname,value)
et renvoie une representation texte
"""
def safe_unicode(iso):
if iso is None:
return None
elif isinstance(iso, str):
return iso # .decode()
else:
return iso
def itermap(cur):
for row in cur:
yield dict((cur.description[idx][0], value) for idx, value in enumerate(row))
head = "
" + "".join(["" + c[0] + " | " for c in cur.description]) + "
"
lines = ""
if callback:
for r in itermap(cur):
lines = (
lines
+ ""
+ "".join(["" + str(callback(r, c[0], safe_unicode(r[c[0]]))) + " | " for c in cur.description])
+ "
"
)
else:
for r in cur:
lines = lines + "" + "".join(["" + safe_unicode(c) + " | " for c in r]) + "
"
return "" % (head, lines)