from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import now_datetime, getdate, flt, cint, get_fullname
from frappe.installer import update_site_config
from frappe.utils.data import formatdate
from frappe.utils.user import get_enabled_system_users, get_system_managers
import os, subprocess, urlparse, urllib
class SiteExpiredError(frappe.ValidationError):
pass
EXPIRY_WARNING_DAYS = 10
def check_if_expired():
"""check if account is expired. If expired, do not allow login"""
if not has_expired():
return
limits = get_limits()
expires_on = formatdate(limits.get("expiry"))
support_email = limits.get("support_email")
if limits.upgrade_url:
message = _("""Your subscription expired on {0}. To renew, {1}.""").format(expires_on, get_upgrade_link(limits.upgrade_url))
elif support_email:
message = _("""Your subscription expired on {0}. To renew, please send an email to {1}.""").format(expires_on, support_email)
else:
message = _("""Your subscription expired on {0}""").format(expires_on)
frappe.throw(message, SiteExpiredError)
def has_expired():
if frappe.session.user=="Administrator":
return False
expires_on = get_limits().expiry
if not expires_on:
return False
if now_datetime().date() <= getdate(expires_on):
return False
return True
def get_expiry_message():
if "System Manager" not in frappe.get_roles():
return ""
limits = get_limits()
if not limits.expiry:
return ""
expires_on = getdate(get_limits().get("expiry"))
today = now_datetime().date()
message = ""
if today > expires_on:
message = _("Your subscription has expired.")
else:
days_to_expiry = (expires_on - today).days
if days_to_expiry == 0:
message = _("Your subscription will expire today.")
elif days_to_expiry == 1:
message = _("Your subscription will expire tomorrow.")
elif days_to_expiry <= EXPIRY_WARNING_DAYS:
message = _("Your subscription will expire on {0}.").format(formatdate(expires_on))
if message and limits.upgrade_url:
upgrade_link = get_upgrade_link(limits.upgrade_url)
message += ' ' + _('To renew, {0}.').format(upgrade_link)
return message
@frappe.whitelist()
def get_usage_info():
'''Get data to show for Usage Info'''
# imported here to prevent circular import
from frappe.email.queue import get_emails_sent_this_month
limits = get_limits()
if not (limits and any([limits.users, limits.space, limits.emails, limits.expiry])):
# no limits!
return
limits.space = limits.space * 1024.0 # to MB
if not limits.space_usage:
# hack! to show some progress
limits.space_usage = {
'database_size': 26,
'files_size': 1,
'backup_size': 1,
'total': 28
}
usage_info = frappe._dict({
'limits': limits,
'enabled_users': len(get_enabled_system_users()),
'emails_sent': get_emails_sent_this_month(),
'space_usage': limits.space_usage['total'],
})
if limits.expiry:
usage_info['expires_on'] = formatdate(limits.expiry)
usage_info['days_to_expiry'] = (getdate(limits.expiry) - getdate()).days
if limits.upgrade_url:
usage_info['upgrade_url'] = get_upgrade_url(limits.upgrade_url)
return usage_info
def get_upgrade_url(upgrade_url):
parts = urlparse.urlsplit(upgrade_url)
params = dict(urlparse.parse_qsl(parts.query))
params.update({
'site': frappe.local.site,
'email': frappe.session.user,
'full_name': get_fullname()
})
query = urllib.urlencode(params, doseq=True)
url = urlparse.urlunsplit((parts.scheme, parts.netloc, parts.path, query, parts.fragment))
return url
def get_upgrade_link(upgrade_url, label=None):
upgrade_url = get_upgrade_url(upgrade_url)
upgrade_link = '{click_here}'.format(upgrade_url=upgrade_url, click_here=label or _('click here'))
return upgrade_link
def get_limits():
'''
"limits": {
"users": 1,
"space": 0.5, # in GB
"emails": 1000 # per month
"expiry": "2099-12-31"
}
'''
return frappe._dict(frappe.local.conf.limits or {})
def update_limits(limits_dict):
'''Add/Update limit in site_config'''
limits = get_limits()
limits.update(limits_dict)
update_site_config("limits", limits, validate=False)
frappe.conf.limits = limits
def clear_limit(key):
'''Remove a limit option from site_config'''
limits = get_limits()
to_clear = [key] if isinstance(key, basestring) else key
for key in to_clear:
if key in limits:
del limits[key]
update_site_config("limits", limits, validate=False)
frappe.conf.limits = limits
def validate_space_limit(file_size):
"""Stop from writing file if max space limit is reached"""
from frappe.utils.file_manager import MaxFileSizeReachedError
limits = get_limits()
if not limits.space:
return
# to MB
space_limit = flt(limits.space * 1024.0, 2)
# in MB
usage = frappe._dict(limits.space_usage or {})
if not usage:
# first time
usage = frappe._dict(update_space_usage())
file_size = file_size / (1024.0 ** 2)
if flt(flt(usage.total) + file_size, 2) > space_limit:
# Stop from attaching file
frappe.throw(_("You have exceeded the max space of {0} for your plan. {1}.").format(
"{0}MB".format(cint(space_limit)) if (space_limit < 1024) else "{0}GB".format(limits.space),
'{0}'.format(_("Click here to check your usage or upgrade to a higher plan"))),
MaxFileSizeReachedError)
# update files size in frappe subscription
usage.files_size = flt(usage.files_size) + file_size
update_limits({ 'space_usage': usage })
def update_space_usage():
# public and private files
files_size = get_folder_size(frappe.get_site_path("public", "files"))
files_size += get_folder_size(frappe.get_site_path("private", "files"))
backup_size = get_folder_size(frappe.get_site_path("private", "backups"))
database_size = get_database_size()
usage = {
'files_size': flt(files_size, 2),
'backup_size': flt(backup_size, 2),
'database_size': flt(database_size, 2),
'total': flt(flt(files_size) + flt(backup_size) + flt(database_size), 2)
}
update_limits({ 'space_usage': usage })
return usage
def get_folder_size(path):
'''Returns folder size in MB if it exists'''
if os.path.exists(path):
return flt(subprocess.check_output(['du', '-ms', path]).split()[0], 2)
def get_database_size():
'''Returns approximate database size in MB'''
db_name = frappe.conf.db_name
# This query will get the database size in MB
db_size = frappe.db.sql('''
SELECT table_schema "database_name", sum( data_length + index_length ) / 1024 / 1024 "database_size"
FROM information_schema.TABLES WHERE table_schema = %s GROUP BY table_schema''', db_name, as_dict=True)
return flt(db_size[0].get('database_size'), 2)