#!/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 "%s%s
" % (head, lines)