You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

786 line
20 KiB

  1. # Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
  2. #
  3. # MIT License (MIT)
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a
  6. # copy of this software and associated documentation files (the "Software"),
  7. # to deal in the Software without restriction, including without limitation
  8. # the rights to use, copy, modify, merge, publish, distribute, sublicense,
  9. # and/or sell copies of the Software, and to permit persons to whom the
  10. # Software is furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in
  13. # all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  16. # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  17. # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  18. # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  19. # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
  20. # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. #
  22. # util __init__.py
  23. from __future__ import unicode_literals
  24. import webnotes
  25. user_time_zone = None
  26. month_name = ['','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
  27. month_name_full = ['','January','February','March','April','May','June','July','August','September','October','November','December']
  28. no_value_fields = ['Section Break', 'Column Break', 'HTML', 'Table', 'FlexTable', 'Button', 'Image', 'Graph']
  29. default_fields = ['doctype','name','owner','creation','modified','modified_by','parent','parentfield','parenttype','idx','docstatus']
  30. def getCSVelement(v):
  31. """
  32. Returns the CSV value of `v`, For example:
  33. * apple becomes "apple"
  34. * hi"there becomes "hi""there"
  35. """
  36. v = cstr(v)
  37. if not v: return ''
  38. if (',' in v) or ('\n' in v) or ('"' in v):
  39. if '"' in v: v = v.replace('"', '""')
  40. return '"'+v+'"'
  41. else: return v or ''
  42. def get_fullname(profile):
  43. """get the full name (first name + last name) of the user from Profile"""
  44. p = webnotes.conn.sql("""select first_name, last_name from `tabProfile`
  45. where name=%s""", profile, as_dict=1)
  46. if p:
  47. p = p[0]
  48. full_name = (p['first_name'] and (p['first_name'] + ' ') or '') + (p['last_name'] or '')
  49. return full_name or profile
  50. else:
  51. return profile
  52. def decode_email_header(s):
  53. import email.header
  54. # replace double quotes with blank
  55. # double quotes in header prohibit decoding of header
  56. decoded_header_tuple = email.header.decode_header(s.replace('"', ''))
  57. decoded_list = map(lambda h: unicode(h[0], encoding=h[1] or 'utf-8'), decoded_header_tuple)
  58. return " ".join(decoded_list)
  59. def extract_email_id(s):
  60. """
  61. Extract email id from email header format
  62. """
  63. if '<' in s:
  64. s = s.split('<')[1].split('>')[0]
  65. if s:
  66. s = s.strip().lower()
  67. return s
  68. def get_email_id(user):
  69. """get email id of user formatted as: John Doe <johndoe@example.com>"""
  70. if user == "Administrator":
  71. return user
  72. fullname = get_fullname(user)
  73. return "%s <%s>" % (fullname, user)
  74. def validate_email_add(email_str):
  75. """Validates the email string"""
  76. s = extract_email_id(email_str)
  77. import re
  78. #return re.match("^[a-zA-Z0-9._%-]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", email_str)
  79. return re.match("[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", s)
  80. def sendmail(recipients, sender='', msg='', subject='[No Subject]', parts=[], cc=[], attach=[]):
  81. """Send an email. For more details see :func:`email_lib.sendmail`"""
  82. import webnotes.utils.email_lib
  83. return email_lib.sendmail(recipients, sender, msg, subject, parts, cc, attach)
  84. def get_request_site_address():
  85. """get app url from request"""
  86. import os
  87. try:
  88. return 'HTTPS' in os.environ.get('SERVER_PROTOCOL') and 'https://' or 'http://' \
  89. + os.environ.get('HTTP_HOST')
  90. except TypeError, e:
  91. return 'http://localhost'
  92. def generate_hash():
  93. """
  94. Generates random hash for session id
  95. """
  96. import hashlib, time
  97. return hashlib.sha224(str(time.time())).hexdigest()
  98. def random_string(length):
  99. """generate a random string"""
  100. import string
  101. from random import choice
  102. return ''.join([choice(string.letters + string.digits) for i in range(length)])
  103. def db_exists(dt, dn):
  104. return webnotes.conn.sql('select name from `tab%s` where name="%s"' % (dt, dn))
  105. def load_json(arg):
  106. # already a dictionary?
  107. if not isinstance(arg, basestring):
  108. return arg
  109. import json
  110. return json.loads(arg, encoding='utf-8')
  111. # Get Traceback
  112. # ==============================================================================
  113. def getTraceback():
  114. """
  115. Returns the traceback of the Exception
  116. """
  117. import sys, traceback, string
  118. exc_type, value, tb = sys.exc_info()
  119. trace_list = traceback.format_tb(tb, None) + traceback.format_exception_only(exc_type, value)
  120. body = "Traceback (innermost last):\n" + "%-20s %s" % \
  121. (unicode((b"").join(trace_list[:-1]), 'utf-8'), unicode(trace_list[-1], 'utf-8'))
  122. if webnotes.logger:
  123. webnotes.logger.error('Db:'+(webnotes.conn and webnotes.conn.cur_db_name or '') + ' - ' + body)
  124. return body
  125. # Log
  126. # ==============================================================================
  127. def log(event, details):
  128. webnotes.logger.info(details)
  129. # Date and Time
  130. # ==============================================================================
  131. def getdate(string_date):
  132. """
  133. Coverts string date (yyyy-mm-dd) to datetime.date object
  134. """
  135. import datetime
  136. if type(string_date)==unicode:
  137. string_date = str(string_date)
  138. if type(string_date) in (datetime.datetime, datetime.date):
  139. return string_date
  140. if ' ' in string_date:
  141. string_date = string_date.split(' ')[0]
  142. t = string_date.split('-')
  143. if len(t)==3:
  144. return datetime.date(cint(t[0]), cint(t[1]), cint(t[2]))
  145. else:
  146. return ''
  147. def add_days(date, days, format='string'):
  148. """
  149. Adds `days` to the given `string_date`
  150. """
  151. import datetime
  152. if not date:
  153. date = now_datetime()
  154. if type(date) not in (datetime.datetime, datetime.date):
  155. date = getdate(date)
  156. dt = date + datetime.timedelta(days)
  157. if format=='string':
  158. return dt.strftime('%Y-%m-%d')
  159. else:
  160. return dt
  161. def add_months(string_date, months):
  162. import datetime
  163. return webnotes.conn.sql("select DATE_ADD('%s',INTERVAL '%s' MONTH)" % (getdate(string_date),months))[0][0]
  164. def add_years(string_date, years):
  165. import datetime
  166. return webnotes.conn.sql("select DATE_ADD('%s',INTERVAL '%s' YEAR)" % (getdate(string_date),years))[0][0]
  167. def date_diff(string_ed_date, string_st_date=None):
  168. import datetime
  169. return webnotes.conn.sql("SELECT DATEDIFF('%s','%s')" %(getdate(string_ed_date), getdate(string_st_date)))[0][0]
  170. def now_datetime():
  171. global user_time_zone
  172. from datetime import datetime
  173. from pytz import timezone
  174. # get localtime
  175. if not user_time_zone:
  176. user_time_zone = webnotes.conn.get_value('Control Panel', None, 'time_zone') \
  177. or 'Asia/Calcutta'
  178. # convert to UTC
  179. utcnow = timezone('UTC').localize(datetime.utcnow())
  180. # convert to user time zone
  181. return utcnow.astimezone(timezone(user_time_zone))
  182. def now():
  183. """return current datetime as yyyy-mm-dd hh:mm:ss"""
  184. return now_datetime().strftime('%Y-%m-%d %H:%M:%S')
  185. def nowdate():
  186. """return current date as yyyy-mm-dd"""
  187. return now_datetime().strftime('%Y-%m-%d')
  188. def nowtime():
  189. """return current time in hh:mm"""
  190. return now_datetime().strftime('%H:%M')
  191. def get_first_day(dt, d_years=0, d_months=0):
  192. """
  193. Returns the first day of the month for the date specified by date object
  194. Also adds `d_years` and `d_months` if specified
  195. """
  196. import datetime
  197. # d_years, d_months are "deltas" to apply to dt
  198. y, m = dt.year + d_years, dt.month + d_months
  199. a, m = divmod(m-1, 12)
  200. return datetime.date(y+a, m+1, 1)
  201. def get_last_day(dt):
  202. """
  203. Returns last day of the month using:
  204. `get_first_day(dt, 0, 1) + datetime.timedelta(-1)`
  205. """
  206. import datetime
  207. return get_first_day(dt, 0, 1) + datetime.timedelta(-1)
  208. user_format = None
  209. """
  210. User format specified in :term:`Control Panel`
  211. Examples:
  212. * dd-mm-yyyy
  213. * mm-dd-yyyy
  214. * dd/mm/yyyy
  215. """
  216. def formatdate(string_date):
  217. """
  218. Convers the given string date to :data:`user_format`
  219. """
  220. global user_format
  221. if not user_format:
  222. user_format = webnotes.conn.get_value('Control Panel', None, 'date_format')
  223. d = string_date.split('-');
  224. out = user_format
  225. return out.replace('dd', ('%.2i' % cint(d[2]))).replace('mm', ('%.2i' % cint(d[1]))).replace('yyyy', d[0])
  226. def dict_to_str(args, sep='&'):
  227. """
  228. Converts a dictionary to URL
  229. """
  230. import urllib
  231. t = []
  232. for k in args.keys():
  233. t.append(str(k)+'='+urllib.quote(str(args[k] or '')))
  234. return sep.join(t)
  235. def timestamps_equal(t1, t2):
  236. """Returns true if same the two string timestamps are same"""
  237. scrub = lambda x: x.replace(':', ' ').replace('-',' ').split()
  238. t1, t2 = scrub(t1), scrub(t2)
  239. if len(t1) != len(t2):
  240. return
  241. for i in range(len(t1)):
  242. if t1[i]!=t2[i]:
  243. return
  244. return 1
  245. def global_date_format(date):
  246. """returns date as 1 January 2012"""
  247. import datetime
  248. if isinstance(date, basestring):
  249. date = getdate(date)
  250. return cstr(cint(date.strftime('%d'))) + ' ' + month_name_full[int(date.strftime('%m'))] \
  251. + ' ' + date.strftime('%Y')
  252. # Datatype
  253. # ==============================================================================
  254. def isNull(v):
  255. """
  256. Returns true if v='' or v is `None`
  257. """
  258. return (v=='' or v==None)
  259. def has_common(l1, l2):
  260. """
  261. Returns true if there are common elements in lists l1 and l2
  262. """
  263. for l in l1:
  264. if l in l2:
  265. return 1
  266. return 0
  267. def flt(s):
  268. """
  269. Convert to float (ignore commas)
  270. """
  271. if isinstance(s, basestring): # if string
  272. s = s.replace(',','')
  273. try: tmp = float(s)
  274. except: tmp = 0
  275. return tmp
  276. def cint(s):
  277. """
  278. Convert to integer
  279. """
  280. try: tmp = int(float(s))
  281. except: tmp = 0
  282. return tmp
  283. def cstr(s):
  284. if isinstance(s, unicode):
  285. return s
  286. elif s==None:
  287. return ''
  288. elif isinstance(s, basestring):
  289. return unicode(s, 'utf-8')
  290. else:
  291. return unicode(s)
  292. def str_esc_quote(s):
  293. """
  294. Escape quotes
  295. """
  296. if s==None:return ''
  297. return s.replace("'","\'")
  298. def replace_newlines(s):
  299. """
  300. Replace newlines by '<br>'
  301. """
  302. if s==None:return ''
  303. return s.replace("\n","<br>")
  304. # ==============================================================================
  305. def parse_val(v):
  306. """
  307. Converts to simple datatypes from SQL query results
  308. """
  309. import datetime
  310. if type(v)==datetime.date:
  311. v = str(v)
  312. elif type(v)==datetime.timedelta:
  313. v = ':'.join(str(v).split(':')[:2])
  314. elif type(v)==datetime.datetime:
  315. v = str(v)
  316. elif type(v)==long: v=int(v)
  317. return v
  318. # ==============================================================================
  319. def fmt_money(amount, fmt = '%.2f'):
  320. """
  321. Convert to string with commas for thousands, millions etc
  322. """
  323. curr = webnotes.conn.get_value('Control Panel', None, 'currency_format') or 'Millions'
  324. val = 2
  325. if curr == 'Millions': val = 3
  326. if cstr(amount).find('.') == -1: temp = '00'
  327. else: temp = cstr(amount).split('.')[1]
  328. l = []
  329. minus = ''
  330. if flt(amount) < 0: minus = '-'
  331. amount = ''.join(cstr(amount).split(','))
  332. amount = cstr(abs(flt(amount))).split('.')[0]
  333. # main logic
  334. if len(cstr(amount)) > 3:
  335. nn = amount[len(amount)-3:]
  336. l.append(nn)
  337. amount = amount[0:len(amount)-3]
  338. while len(cstr(amount)) > val:
  339. nn = amount[len(amount)-val:]
  340. l.insert(0,nn)
  341. amount = amount[0:len(amount)-val]
  342. if len(amount) > 0: l.insert(0,amount)
  343. amount = ','.join(l)+'.'+temp
  344. amount = minus + amount
  345. return amount
  346. #
  347. # convet currency to words
  348. #
  349. def money_in_words(number, main_currency = None, fraction_currency=None):
  350. """
  351. Returns string in words with currency and fraction currency.
  352. """
  353. d = get_defaults()
  354. if not main_currency:
  355. main_currency = d.get('currency', 'INR')
  356. if not fraction_currency:
  357. fraction_currency = d.get('fraction_currency', 'paise')
  358. n = "%.2f" % flt(number)
  359. main, fraction = n.split('.')
  360. if len(fraction)==1: fraction += '0'
  361. out = main_currency + ' ' + in_words(main).title()
  362. if cint(fraction):
  363. out = out + ' and ' + in_words(fraction).title() + ' ' + fraction_currency
  364. return out + ' only.'
  365. #
  366. # convert number to words
  367. #
  368. def in_words(integer):
  369. """
  370. Returns string in words for the given integer.
  371. """
  372. in_million = webnotes.conn.get_default('currency_format')=='Millions' and 1 or 0
  373. n=int(integer)
  374. known = {0: 'zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 6: 'six', 7: 'seven', 8: 'eight', 9: 'nine', 10: 'ten',
  375. 11: 'eleven', 12: 'twelve', 13: 'thirteen', 14: 'fourteen', 15: 'fifteen', 16: 'sixteen', 17: 'seventeen', 18: 'eighteen',
  376. 19: 'nineteen', 20: 'twenty', 30: 'thirty', 40: 'forty', 50: 'fifty', 60: 'sixty', 70: 'seventy', 80: 'eighty', 90: 'ninety'}
  377. def psn(n, known, xpsn):
  378. import sys;
  379. if n in known: return known[n]
  380. bestguess, remainder = str(n), 0
  381. if n<=20:
  382. print >>sys.stderr, n, "How did this happen?"
  383. assert 0
  384. elif n < 100:
  385. bestguess= xpsn((n//10)*10, known, xpsn) + '-' + xpsn(n%10, known, xpsn)
  386. return bestguess
  387. elif n < 1000:
  388. bestguess= xpsn(n//100, known, xpsn) + ' ' + 'hundred'
  389. remainder = n%100
  390. else:
  391. if in_million:
  392. if n < 1000000:
  393. bestguess= xpsn(n//1000, known, xpsn) + ' ' + 'thousand'
  394. remainder = n%1000
  395. elif n < 1000000000:
  396. bestguess= xpsn(n//1000000, known, xpsn) + ' ' + 'million'
  397. remainder = n%1000000
  398. else:
  399. bestguess= xpsn(n//1000000000, known, xpsn) + ' ' + 'billion'
  400. remainder = n%1000000000
  401. else:
  402. if n < 100000:
  403. bestguess= xpsn(n//1000, known, xpsn) + ' ' + 'thousand'
  404. remainder = n%1000
  405. elif n < 10000000:
  406. bestguess= xpsn(n//100000, known, xpsn) + ' ' + 'lakh'
  407. remainder = n%100000
  408. else:
  409. bestguess= xpsn(n//10000000, known, xpsn) + ' ' + 'crore'
  410. remainder = n%10000000
  411. if remainder:
  412. if remainder >= 100:
  413. comma = ','
  414. else:
  415. comma = ''
  416. return bestguess + comma + ' ' + xpsn(remainder, known, xpsn)
  417. else:
  418. return bestguess
  419. return psn(n, known, psn)
  420. # Get Defaults
  421. # ==============================================================================
  422. def get_defaults(key=None):
  423. """
  424. Get dictionary of default values from the :term:`Control Panel`, or a value if key is passed
  425. """
  426. return webnotes.conn.get_defaults(key)
  427. def set_default(key, val):
  428. """
  429. Set / add a default value to :term:`Control Panel`
  430. """
  431. return webnotes.conn.set_default(key, val)
  432. #
  433. # Clear recycle bin
  434. #
  435. def clear_recycle_bin():
  436. sql = webnotes.conn.sql
  437. tl = sql('show tables')
  438. total_deleted = 0
  439. for t in tl:
  440. fl = [i[0] for i in sql('desc `%s`' % t[0])]
  441. if 'name' in fl:
  442. total_deleted += sql("select count(*) from `%s` where name like '__overwritten:%%'" % t[0])[0][0]
  443. sql("delete from `%s` where name like '__overwritten:%%'" % t[0])
  444. if 'parent' in fl:
  445. total_deleted += sql("select count(*) from `%s` where parent like '__oldparent:%%'" % t[0])[0][0]
  446. sql("delete from `%s` where parent like '__oldparent:%%'" % t[0])
  447. total_deleted += sql("select count(*) from `%s` where parent like 'oldparent:%%'" % t[0])[0][0]
  448. sql("delete from `%s` where parent like 'oldparent:%%'" % t[0])
  449. total_deleted += sql("select count(*) from `%s` where parent like 'old_parent:%%'" % t[0])[0][0]
  450. sql("delete from `%s` where parent like 'old_parent:%%'" % t[0])
  451. return "%s records deleted" % str(int(total_deleted))
  452. # Dictionary utils
  453. # ==============================================================================
  454. def remove_blanks(d):
  455. """
  456. Returns d with empty ('' or None) values stripped
  457. """
  458. empty_keys = []
  459. for key in d:
  460. if d[key]=='' or d[key]==None:
  461. # del d[key] raises runtime exception, using a workaround
  462. empty_keys.append(key)
  463. for key in empty_keys:
  464. del d[key]
  465. return d
  466. def pprint_dict(d, level=1, no_blanks=True):
  467. """
  468. Pretty print a dictionary with indents
  469. """
  470. if no_blanks:
  471. remove_blanks(d)
  472. # make indent
  473. indent, ret = '', ''
  474. for i in range(0,level): indent += '\t'
  475. # add lines
  476. comment, lines = '', []
  477. kl = d.keys()
  478. kl.sort()
  479. # make lines
  480. for key in kl:
  481. if key != '##comment':
  482. tmp = {key: d[key]}
  483. lines.append(indent + str(tmp)[1:-1] )
  484. # add comment string
  485. if '##comment' in kl:
  486. ret = ('\n' + indent) + '# ' + d['##comment'] + '\n'
  487. # open
  488. ret += indent + '{\n'
  489. # lines
  490. ret += indent + ',\n\t'.join(lines)
  491. # close
  492. ret += '\n' + indent + '}'
  493. return ret
  494. def get_common(d1,d2):
  495. """
  496. returns (list of keys) the common part of two dicts
  497. """
  498. return [p for p in d1 if p in d2 and d1[p]==d2[p]]
  499. def get_common_dict(d1, d2):
  500. """
  501. return common dictionary of d1 and d2
  502. """
  503. ret = {}
  504. for key in d1:
  505. if key in d2 and d2[key]==d1[key]:
  506. ret[key] = d1[key]
  507. return ret
  508. def get_diff_dict(d1, d2):
  509. """
  510. return common dictionary of d1 and d2
  511. """
  512. diff_keys = set(d2.keys()).difference(set(d1.keys()))
  513. ret = {}
  514. for d in diff_keys: ret[d] = d2[d]
  515. return ret
  516. def get_file_timestamp(fn):
  517. """
  518. Returns timestamp of the given file
  519. """
  520. import os
  521. from webnotes.utils import cint
  522. try:
  523. return str(cint(os.stat(fn).st_mtime))
  524. except OSError, e:
  525. if e.args[0]!=2:
  526. raise e
  527. else:
  528. return None
  529. # to be deprecated
  530. def make_esc(esc_chars):
  531. """
  532. Function generator for Escaping special characters
  533. """
  534. return lambda s: ''.join(['\\' + c if c in esc_chars else c for c in s])
  535. # esc / unescape characters -- used for command line
  536. def esc(s, esc_chars):
  537. """
  538. Escape special characters
  539. """
  540. for c in esc_chars:
  541. esc_str = '\\' + c
  542. s = s.replace(c, esc_str)
  543. return s
  544. def unesc(s, esc_chars):
  545. """
  546. UnEscape special characters
  547. """
  548. for c in esc_chars:
  549. esc_str = '\\' + c
  550. s = s.replace(esc_str, c)
  551. return s
  552. def strip_html(text):
  553. """
  554. removes anything enclosed in and including <>
  555. """
  556. import re
  557. return re.compile(r'<.*?>').sub('', text)
  558. def escape_html(text):
  559. html_escape_table = {
  560. "&": "&amp;",
  561. '"': "&quot;",
  562. "'": "&apos;",
  563. ">": "&gt;",
  564. "<": "&lt;",
  565. }
  566. return "".join(html_escape_table.get(c,c) for c in text)
  567. def get_doctype_label(dt=None):
  568. """
  569. Gets label of a doctype
  570. """
  571. if dt:
  572. res = webnotes.conn.sql("""\
  573. SELECT name, dt_label FROM `tabDocType Label`
  574. WHERE name=%s""", dt)
  575. return res and res[0][0] or dt
  576. else:
  577. res = webnotes.conn.sql("SELECT name, dt_label FROM `tabDocType Label`")
  578. dt_label_dict = {}
  579. for r in res:
  580. dt_label_dict[r[0]] = r[1]
  581. return dt_label_dict
  582. def get_label_doctype(label):
  583. """
  584. Gets doctype from its label
  585. """
  586. res = webnotes.conn.sql("""\
  587. SELECT name FROM `tabDocType Label`
  588. WHERE dt_label=%s""", label)
  589. return res and res[0][0] or label
  590. def get_system_managers_list():
  591. """Returns a list of system managers' email addresses"""
  592. system_managers_list = webnotes.conn.sql("""\
  593. SELECT DISTINCT p.name
  594. FROM tabUserRole ur, tabProfile p
  595. WHERE
  596. ur.parent = p.name AND
  597. ur.role='System Manager' AND
  598. p.docstatus<2 AND
  599. p.enabled=1 AND
  600. p.name not in ('Administrator', 'Guest')""", as_list=1)
  601. return [sysman[0] for sysman in system_managers_list]
  602. def pretty_date(iso_datetime):
  603. """
  604. Takes an ISO time and returns a string representing how
  605. long ago the date represents.
  606. Ported from PrettyDate by John Resig
  607. """
  608. if not iso_datetime: return ''
  609. from datetime import datetime
  610. import math
  611. if isinstance(iso_datetime, basestring):
  612. iso_datetime = datetime.strptime(iso_datetime, '%Y-%m-%d %H:%M:%S')
  613. now_dt = datetime.strptime(now(), '%Y-%m-%d %H:%M:%S')
  614. dt_diff = now_dt - iso_datetime
  615. # available only in python 2.7+
  616. # dt_diff_seconds = dt_diff.total_seconds()
  617. dt_diff_seconds = dt_diff.days * 86400.0 + dt_diff.seconds
  618. dt_diff_days = math.floor(dt_diff_seconds / 86400.0)
  619. # differnt cases
  620. if dt_diff_seconds < 60.0:
  621. return 'just now'
  622. elif dt_diff_seconds < 120.0:
  623. return '1 minute ago'
  624. elif dt_diff_seconds < 3600.0:
  625. return '%s minutes ago' % cint(math.floor(dt_diff_seconds / 60.0))
  626. elif dt_diff_seconds < 7200.0:
  627. return '1 hour ago'
  628. elif dt_diff_seconds < 86400.0:
  629. return '%s hours ago' % cint(math.floor(dt_diff_seconds / 3600.0))
  630. elif dt_diff_days == 1.0:
  631. return 'Yesterday'
  632. elif dt_diff_days < 7.0:
  633. return '%s days ago' % cint(dt_diff_days)
  634. elif dt_diff_days < 31.0:
  635. return '%s week(s) ago' % cint(math.ceil(dt_diff_days / 7.0))
  636. elif dt_diff_days < 365.0:
  637. return '%s months ago' % cint(math.ceil(dt_diff_days / 30.0))
  638. else:
  639. return 'more than %s year(s) ago' % cint(math.floor(dt_diff_days / 365.0))