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.
 
 
 
 
 
 

760 lines
19 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. user_format = None
  27. no_value_fields = ['Section Break', 'Column Break', 'HTML', 'Table', 'FlexTable',
  28. 'Button', 'Image', 'Graph']
  29. default_fields = ['doctype', 'name', 'owner', 'creation', 'modified', 'modified_by',
  30. 'parent', 'parentfield', 'parenttype', 'idx', 'docstatus']
  31. # used in import_docs.py
  32. # TODO: deprecate it
  33. def getCSVelement(v):
  34. """
  35. Returns the CSV value of `v`, For example:
  36. * apple becomes "apple"
  37. * hi"there becomes "hi""there"
  38. """
  39. v = cstr(v)
  40. if not v: return ''
  41. if (',' in v) or ('\n' in v) or ('"' in v):
  42. if '"' in v: v = v.replace('"', '""')
  43. return '"'+v+'"'
  44. else: return v or ''
  45. def get_fullname(profile):
  46. """get the full name (first name + last name) of the user from Profile"""
  47. p = webnotes.conn.sql("""select first_name, last_name from `tabProfile`
  48. where name=%s""", profile, as_dict=1)
  49. if p:
  50. profile = " ".join(filter(None,
  51. [p[0].get('first_name'), p[0].get('last_name')])) or profile
  52. return profile
  53. def get_formatted_email(user):
  54. """get email id of user formatted as: John Doe <johndoe@example.com>"""
  55. if user == "Administrator":
  56. return user
  57. from email.utils import formataddr
  58. fullname = get_fullname(user)
  59. return formataddr((fullname, user))
  60. def extract_email_id(email):
  61. """fetch only the email part of the email id"""
  62. from email.utils import parseaddr
  63. if ',' in email and email.count("@")==1:
  64. email = email.split(",")[-1]
  65. fullname, email_id = parseaddr(email)
  66. return email_id
  67. def validate_email_add(email_str):
  68. """Validates the email string"""
  69. email = extract_email_id(email_str)
  70. import re
  71. 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])?", email.lower())
  72. def sendmail(recipients, sender='', msg='', subject='[No Subject]'):
  73. """Send an email. For more details see :func:`email_lib.sendmail`"""
  74. import webnotes.utils.email_lib
  75. return email_lib.sendmail(recipients, sender, msg, subject)
  76. def get_request_site_address():
  77. """get app url from request"""
  78. import os
  79. try:
  80. return 'HTTPS' in os.environ.get('SERVER_PROTOCOL') and 'https://' or 'http://' \
  81. + os.environ.get('HTTP_HOST')
  82. except TypeError, e:
  83. return 'http://localhost'
  84. def random_string(length):
  85. """generate a random string"""
  86. import string
  87. from random import choice
  88. return ''.join([choice(string.letters + string.digits) for i in range(length)])
  89. def load_json(arg):
  90. # already a dictionary?
  91. if not isinstance(arg, basestring):
  92. return arg
  93. import json
  94. return json.loads(arg, encoding='utf-8')
  95. # Get Traceback
  96. # ==============================================================================
  97. def getTraceback():
  98. """
  99. Returns the traceback of the Exception
  100. """
  101. import sys, traceback, string
  102. exc_type, value, tb = sys.exc_info()
  103. trace_list = traceback.format_tb(tb, None) + \
  104. traceback.format_exception_only(exc_type, value)
  105. body = "Traceback (innermost last):\n" + "%-20s %s" % \
  106. (unicode((b"").join(trace_list[:-1]), 'utf-8'), unicode(trace_list[-1], 'utf-8'))
  107. if webnotes.logger:
  108. webnotes.logger.error('Db:'+(webnotes.conn and webnotes.conn.cur_db_name or '') \
  109. + ' - ' + body)
  110. return body
  111. def log(event, details):
  112. webnotes.logger.info(details)
  113. # datetime functions
  114. def getdate(string_date):
  115. """
  116. Coverts string date (yyyy-mm-dd) to datetime.date object
  117. """
  118. import datetime
  119. if isinstance(string_date, datetime.date):
  120. return string_date
  121. elif isinstance(string_date, datetime.datetime):
  122. return datetime.date()
  123. if " " in string_date:
  124. string_date = string_date.split(" ")[0]
  125. try:
  126. return datetime.datetime.strptime(string_date, "%Y-%m-%d").date()
  127. except ValueError, e:
  128. webnotes.msgprint("Cannot understand date - '%s'" % \
  129. (string_date,), raise_exception=1)
  130. def add_to_date(date, years=0, months=0, days=0):
  131. """Adds `days` to the given date"""
  132. format = isinstance(date, basestring)
  133. if date:
  134. date = getdate(date)
  135. else:
  136. raise Exception, "Start date required"
  137. from dateutil.relativedelta import relativedelta
  138. date += relativedelta(years=years, months=months, days=days)
  139. if format:
  140. return date.strftime("%Y-%m-%d")
  141. else:
  142. return date
  143. def add_days(date, days):
  144. return add_to_date(date, days=days)
  145. def add_months(date, months):
  146. return add_to_date(date, months=months)
  147. def add_years(date, years):
  148. return add_to_date(date, years=years)
  149. def date_diff(string_ed_date, string_st_date):
  150. return (getdate(string_ed_date) - getdate(string_st_date)).days
  151. def time_diff_in_seconds(string_ed_date, string_st_date):
  152. return (getdate(string_ed_date) - getdate(string_st_date)).seconds
  153. def now_datetime():
  154. global user_time_zone
  155. from datetime import datetime
  156. from pytz import timezone
  157. # get localtime
  158. if not user_time_zone:
  159. user_time_zone = webnotes.conn.get_value('Control Panel', None, 'time_zone') \
  160. or 'Asia/Calcutta'
  161. # convert to UTC
  162. utcnow = timezone('UTC').localize(datetime.utcnow())
  163. # convert to user time zone
  164. return utcnow.astimezone(timezone(user_time_zone))
  165. def now():
  166. """return current datetime as yyyy-mm-dd hh:mm:ss"""
  167. return now_datetime().strftime('%Y-%m-%d %H:%M:%S')
  168. def nowdate():
  169. """return current date as yyyy-mm-dd"""
  170. return now_datetime().strftime('%Y-%m-%d')
  171. def today():
  172. return nowdate()
  173. def nowtime():
  174. """return current time in hh:mm"""
  175. return now_datetime().strftime('%H:%M')
  176. def get_first_day(dt, d_years=0, d_months=0):
  177. """
  178. Returns the first day of the month for the date specified by date object
  179. Also adds `d_years` and `d_months` if specified
  180. """
  181. import datetime
  182. dt = getdate(dt)
  183. # d_years, d_months are "deltas" to apply to dt
  184. overflow_years, month = divmod(dt.month + d_months - 1, 12)
  185. year = dt.year + d_years + overflow_years
  186. return datetime.date(year, month + 1, 1)
  187. def get_last_day(dt):
  188. """
  189. Returns last day of the month using:
  190. `get_first_day(dt, 0, 1) + datetime.timedelta(-1)`
  191. """
  192. import datetime
  193. return get_first_day(dt, 0, 1) + datetime.timedelta(-1)
  194. def get_datetime(datetime_str):
  195. from datetime import datetime
  196. if isinstance(datetime_str, datetime):
  197. return datetime_str.replace(microsecond=0, tzinfo=None)
  198. return datetime.strptime(datetime_str, '%Y-%m-%d %H:%M:%S')
  199. def get_datetime_str(datetime_obj):
  200. if isinstance(datetime_obj, basestring):
  201. datetime_obj = get_datetime(datetime_obj)
  202. return datetime_obj.strftime('%Y-%m-%d %H:%M:%S')
  203. def formatdate(string_date=None):
  204. """
  205. Convers the given string date to :data:`user_format`
  206. User format specified in :term:`Control Panel`
  207. Examples:
  208. * dd-mm-yyyy
  209. * mm-dd-yyyy
  210. * dd/mm/yyyy
  211. """
  212. if string_date:
  213. string_date = getdate(string_date)
  214. else:
  215. string_date = nowdate()
  216. global user_format
  217. if not user_format:
  218. user_format = webnotes.conn.get_value('Control Panel', None, 'date_format')
  219. out = user_format
  220. return out.replace("dd", string_date.strftime("%d"))\
  221. .replace("mm", string_date.strftime("%m"))\
  222. .replace("yyyy", string_date.strftime("%Y"))
  223. def global_date_format(date):
  224. """returns date as 1 January 2012"""
  225. formatted_date = getdate(date).strftime("%d %B %Y")
  226. return formatted_date.startswith("0") and formatted_date[1:] or formatted_date
  227. def dict_to_str(args, sep='&'):
  228. """
  229. Converts a dictionary to URL
  230. """
  231. import urllib
  232. t = []
  233. for k in args.keys():
  234. t.append(str(k)+'='+urllib.quote(str(args[k] or '')))
  235. return sep.join(t)
  236. def timestamps_equal(t1, t2):
  237. """Returns true if same the two string timestamps are same"""
  238. scrub = lambda x: x.replace(':', ' ').replace('-',' ').split()
  239. t1, t2 = scrub(t1), scrub(t2)
  240. if len(t1) != len(t2):
  241. return
  242. for i in range(len(t1)):
  243. if t1[i]!=t2[i]:
  244. return
  245. return 1
  246. def has_common(l1, l2):
  247. """Returns truthy value if there are common elements in lists l1 and l2"""
  248. return set(l1) & set(l2)
  249. def flt(s, precision=None):
  250. """Convert to float (ignore commas)"""
  251. if isinstance(s, basestring):
  252. s = s.replace(',','')
  253. try:
  254. num = float(s)
  255. if precision:
  256. num = round(num, precision)
  257. except Exception, e:
  258. num = 0
  259. return num
  260. def cint(s):
  261. """Convert to integer"""
  262. try: num = int(float(s))
  263. except: num = 0
  264. return num
  265. def cstr(s):
  266. if isinstance(s, unicode):
  267. return s
  268. elif s==None:
  269. return ''
  270. elif isinstance(s, basestring):
  271. return unicode(s, 'utf-8')
  272. else:
  273. return unicode(s)
  274. def encode(obj, encoding="utf-8"):
  275. if isinstance(obj, list):
  276. out = []
  277. for o in obj:
  278. if isinstance(o, unicode):
  279. out.append(o.encode(encoding))
  280. else:
  281. out.append(o)
  282. return out
  283. elif isinstance(obj, unicode):
  284. return obj.encode(encoding)
  285. else:
  286. return obj
  287. def parse_val(v):
  288. """Converts to simple datatypes from SQL query results"""
  289. import datetime
  290. if isinstance(v, (datetime.date, datetime.datetime)):
  291. v = unicode(v)
  292. elif isinstance(v, datetime.timedelta):
  293. v = ":".join(unicode(v).split(":")[:2])
  294. elif isinstance(v, long):
  295. v = int(v)
  296. return v
  297. def fmt_money(amount, precision=2):
  298. """
  299. Convert to string with commas for thousands, millions etc
  300. """
  301. curr = webnotes.conn.get_value('Control Panel', None, 'currency_format') or 'Millions'
  302. amount = '%.*f' % (precision, flt(amount))
  303. val = 2
  304. if curr == 'Millions': val = 3
  305. if amount.find('.') == -1: temp = '00'
  306. else: temp = amount.split('.')[1]
  307. l = []
  308. minus = ''
  309. if flt(amount) < 0: minus = '-'
  310. amount = ''.join(amount.split(','))
  311. amount = cstr(abs(flt(amount))).split('.')[0]
  312. # main logic
  313. if len(amount) > 3:
  314. nn = amount[len(amount)-3:]
  315. l.append(nn)
  316. amount = amount[0:len(amount)-3]
  317. while len(amount) > val:
  318. nn = amount[len(amount)-val:]
  319. l.insert(0,nn)
  320. amount = amount[0:len(amount)-val]
  321. if len(amount) > 0: l.insert(0,amount)
  322. amount = ','.join(l)+'.'+temp
  323. amount = minus + amount
  324. return amount
  325. #
  326. # convet currency to words
  327. #
  328. def money_in_words(number, main_currency = None, fraction_currency=None):
  329. """
  330. Returns string in words with currency and fraction currency.
  331. """
  332. d = get_defaults()
  333. if not main_currency:
  334. main_currency = d.get('currency', 'INR')
  335. if not fraction_currency:
  336. fraction_currency = webnotes.conn.get_value("Currency", main_currency, "fraction") or "Cent"
  337. n = "%.2f" % flt(number)
  338. main, fraction = n.split('.')
  339. if len(fraction)==1: fraction += '0'
  340. out = main_currency + ' ' + in_words(main).title()
  341. if cint(fraction):
  342. out = out + ' and ' + in_words(fraction).title() + ' ' + fraction_currency
  343. return out + ' only.'
  344. #
  345. # convert number to words
  346. #
  347. def in_words(integer):
  348. """
  349. Returns string in words for the given integer.
  350. """
  351. in_million = webnotes.conn.get_default('currency_format')=='Millions' and 1 or 0
  352. n=int(integer)
  353. known = {0: 'zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 6: 'six', 7: 'seven', 8: 'eight', 9: 'nine', 10: 'ten',
  354. 11: 'eleven', 12: 'twelve', 13: 'thirteen', 14: 'fourteen', 15: 'fifteen', 16: 'sixteen', 17: 'seventeen', 18: 'eighteen',
  355. 19: 'nineteen', 20: 'twenty', 30: 'thirty', 40: 'forty', 50: 'fifty', 60: 'sixty', 70: 'seventy', 80: 'eighty', 90: 'ninety'}
  356. def psn(n, known, xpsn):
  357. import sys;
  358. if n in known: return known[n]
  359. bestguess, remainder = str(n), 0
  360. if n<=20:
  361. print >>sys.stderr, n, "How did this happen?"
  362. assert 0
  363. elif n < 100:
  364. bestguess= xpsn((n//10)*10, known, xpsn) + '-' + xpsn(n%10, known, xpsn)
  365. return bestguess
  366. elif n < 1000:
  367. bestguess= xpsn(n//100, known, xpsn) + ' ' + 'hundred'
  368. remainder = n%100
  369. else:
  370. if in_million:
  371. if n < 1000000:
  372. bestguess= xpsn(n//1000, known, xpsn) + ' ' + 'thousand'
  373. remainder = n%1000
  374. elif n < 1000000000:
  375. bestguess= xpsn(n//1000000, known, xpsn) + ' ' + 'million'
  376. remainder = n%1000000
  377. else:
  378. bestguess= xpsn(n//1000000000, known, xpsn) + ' ' + 'billion'
  379. remainder = n%1000000000
  380. else:
  381. if n < 100000:
  382. bestguess= xpsn(n//1000, known, xpsn) + ' ' + 'thousand'
  383. remainder = n%1000
  384. elif n < 10000000:
  385. bestguess= xpsn(n//100000, known, xpsn) + ' ' + 'lakh'
  386. remainder = n%100000
  387. else:
  388. bestguess= xpsn(n//10000000, known, xpsn) + ' ' + 'crore'
  389. remainder = n%10000000
  390. if remainder:
  391. if remainder >= 100:
  392. comma = ','
  393. else:
  394. comma = ''
  395. return bestguess + comma + ' ' + xpsn(remainder, known, xpsn)
  396. else:
  397. return bestguess
  398. return psn(n, known, psn)
  399. # Get Defaults
  400. # ==============================================================================
  401. def get_defaults(key=None):
  402. """
  403. Get dictionary of default values from the :term:`Control Panel`, or a value if key is passed
  404. """
  405. return webnotes.conn.get_defaults(key)
  406. def set_default(key, val):
  407. """
  408. Set / add a default value to :term:`Control Panel`
  409. """
  410. return webnotes.conn.set_default(key, val)
  411. def remove_blanks(d):
  412. """
  413. Returns d with empty ('' or None) values stripped
  414. """
  415. empty_keys = []
  416. for key in d:
  417. if d[key]=='' or d[key]==None:
  418. # del d[key] raises runtime exception, using a workaround
  419. empty_keys.append(key)
  420. for key in empty_keys:
  421. del d[key]
  422. return d
  423. def pprint_dict(d, level=1, no_blanks=True):
  424. """
  425. Pretty print a dictionary with indents
  426. """
  427. if no_blanks:
  428. remove_blanks(d)
  429. # make indent
  430. indent, ret = '', ''
  431. for i in range(0,level): indent += '\t'
  432. # add lines
  433. comment, lines = '', []
  434. kl = d.keys()
  435. kl.sort()
  436. # make lines
  437. for key in kl:
  438. if key != '##comment':
  439. tmp = {key: d[key]}
  440. lines.append(indent + str(tmp)[1:-1] )
  441. # add comment string
  442. if '##comment' in kl:
  443. ret = ('\n' + indent) + '# ' + d['##comment'] + '\n'
  444. # open
  445. ret += indent + '{\n'
  446. # lines
  447. ret += indent + ',\n\t'.join(lines)
  448. # close
  449. ret += '\n' + indent + '}'
  450. return ret
  451. def get_common(d1,d2):
  452. """
  453. returns (list of keys) the common part of two dicts
  454. """
  455. return [p for p in d1 if p in d2 and d1[p]==d2[p]]
  456. def get_common_dict(d1, d2):
  457. """
  458. return common dictionary of d1 and d2
  459. """
  460. ret = {}
  461. for key in d1:
  462. if key in d2 and d2[key]==d1[key]:
  463. ret[key] = d1[key]
  464. return ret
  465. def get_diff_dict(d1, d2):
  466. """
  467. return common dictionary of d1 and d2
  468. """
  469. diff_keys = set(d2.keys()).difference(set(d1.keys()))
  470. ret = {}
  471. for d in diff_keys: ret[d] = d2[d]
  472. return ret
  473. def get_file_timestamp(fn):
  474. """
  475. Returns timestamp of the given file
  476. """
  477. import os
  478. from webnotes.utils import cint
  479. try:
  480. return str(cint(os.stat(fn).st_mtime))
  481. except OSError, e:
  482. if e.args[0]!=2:
  483. raise e
  484. else:
  485. return None
  486. # to be deprecated
  487. def make_esc(esc_chars):
  488. """
  489. Function generator for Escaping special characters
  490. """
  491. return lambda s: ''.join(['\\' + c if c in esc_chars else c for c in s])
  492. # esc / unescape characters -- used for command line
  493. def esc(s, esc_chars):
  494. """
  495. Escape special characters
  496. """
  497. if not s:
  498. return ""
  499. for c in esc_chars:
  500. esc_str = '\\' + c
  501. s = s.replace(c, esc_str)
  502. return s
  503. def unesc(s, esc_chars):
  504. """
  505. UnEscape special characters
  506. """
  507. for c in esc_chars:
  508. esc_str = '\\' + c
  509. s = s.replace(esc_str, c)
  510. return s
  511. def strip_html(text):
  512. """
  513. removes anything enclosed in and including <>
  514. """
  515. import re
  516. return re.compile(r'<.*?>').sub('', text)
  517. def escape_html(text):
  518. html_escape_table = {
  519. "&": "&amp;",
  520. '"': "&quot;",
  521. "'": "&apos;",
  522. ">": "&gt;",
  523. "<": "&lt;",
  524. }
  525. return "".join(html_escape_table.get(c,c) for c in text)
  526. def get_doctype_label(dt=None):
  527. """
  528. Gets label of a doctype
  529. """
  530. if dt:
  531. res = webnotes.conn.sql("""\
  532. SELECT name, dt_label FROM `tabDocType Label`
  533. WHERE name=%s""", dt)
  534. return res and res[0][0] or dt
  535. else:
  536. res = webnotes.conn.sql("SELECT name, dt_label FROM `tabDocType Label`")
  537. dt_label_dict = {}
  538. for r in res:
  539. dt_label_dict[r[0]] = r[1]
  540. return dt_label_dict
  541. def get_label_doctype(label):
  542. """
  543. Gets doctype from its label
  544. """
  545. res = webnotes.conn.sql("""\
  546. SELECT name FROM `tabDocType Label`
  547. WHERE dt_label=%s""", label)
  548. return res and res[0][0] or label
  549. def pretty_date(iso_datetime):
  550. """
  551. Takes an ISO time and returns a string representing how
  552. long ago the date represents.
  553. Ported from PrettyDate by John Resig
  554. """
  555. if not iso_datetime: return ''
  556. from datetime import datetime
  557. import math
  558. if isinstance(iso_datetime, basestring):
  559. iso_datetime = datetime.strptime(iso_datetime, '%Y-%m-%d %H:%M:%S')
  560. now_dt = datetime.strptime(now(), '%Y-%m-%d %H:%M:%S')
  561. dt_diff = now_dt - iso_datetime
  562. # available only in python 2.7+
  563. # dt_diff_seconds = dt_diff.total_seconds()
  564. dt_diff_seconds = dt_diff.days * 86400.0 + dt_diff.seconds
  565. dt_diff_days = math.floor(dt_diff_seconds / 86400.0)
  566. # differnt cases
  567. if dt_diff_seconds < 60.0:
  568. return 'just now'
  569. elif dt_diff_seconds < 120.0:
  570. return '1 minute ago'
  571. elif dt_diff_seconds < 3600.0:
  572. return '%s minutes ago' % cint(math.floor(dt_diff_seconds / 60.0))
  573. elif dt_diff_seconds < 7200.0:
  574. return '1 hour ago'
  575. elif dt_diff_seconds < 86400.0:
  576. return '%s hours ago' % cint(math.floor(dt_diff_seconds / 3600.0))
  577. elif dt_diff_days == 1.0:
  578. return 'Yesterday'
  579. elif dt_diff_days < 7.0:
  580. return '%s days ago' % cint(dt_diff_days)
  581. elif dt_diff_days < 31.0:
  582. return '%s week(s) ago' % cint(math.ceil(dt_diff_days / 7.0))
  583. elif dt_diff_days < 365.0:
  584. return '%s months ago' % cint(math.ceil(dt_diff_days / 30.0))
  585. else:
  586. return 'more than %s year(s) ago' % cint(math.floor(dt_diff_days / 365.0))
  587. def execute_in_shell(cmd, verbose=0):
  588. # using Popen instead of os.system - as recommended by python docs
  589. from subprocess import Popen, PIPE
  590. import tempfile
  591. with tempfile.TemporaryFile() as stdout:
  592. with tempfile.TemporaryFile() as stderr:
  593. p = Popen(cmd, shell=True, stdout=stdout, stderr=stderr)
  594. p.wait()
  595. stdout.seek(0)
  596. out = stdout.read()
  597. stderr.seek(0)
  598. err = stderr.read()
  599. if verbose:
  600. if err: print err
  601. if out: print out
  602. return err, out
  603. def comma_or(some_list):
  604. return comma_sep(some_list, " or ")
  605. def comma_and(some_list):
  606. return comma_sep(some_list, " and ")
  607. def comma_sep(some_list, sep):
  608. if isinstance(some_list, (list, tuple)):
  609. # list(some_list) is done to preserve the existing list
  610. some_list = [unicode(s) for s in list(some_list)]
  611. if not some_list:
  612. return ""
  613. elif len(some_list) == 1:
  614. return some_list[0]
  615. else:
  616. some_list = ["'%s'" % s for s in some_list]
  617. return ", ".join(some_list[:-1]) + sep + some_list[-1]
  618. else:
  619. return some_list
  620. def get_base_path():
  621. import conf
  622. import os
  623. return os.path.dirname(os.path.abspath(conf.__file__))