# Copyright (c) 2015, Frappe Technologies and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
import os
from frappe import _
from frappe.utils.backups import new_backup
from frappe.utils.background_jobs import enqueue
from frappe.utils import (cint, split_emails, get_request_site_address, cstr,
get_files_path, get_backups_path, encode)
from frappe.integration_broker.doctype.integration_service.integration_service import IntegrationService
import sys
import dropbox
from dropbox.files import WriteMode
from dropbox.exceptions import ApiError, AuthError
ignore_list = [".DS_Store"]
class DropboxSettings(IntegrationService):
scheduler_events = {
"daily_long": [
"frappe.integrations.doctype.dropbox_settings.dropbox_settings.take_backups_daily"
],
"weekly_long": [
"frappe.integrations.doctype.dropbox_settings.dropbox_settings.take_backups_weekly"
]
}
def onload(self):
if not self.app_access_key and frappe.conf.dropbox_access_key:
self.dropbox_setup_via_site_config = 1
def validate(self):
if not self.flags.ignore_mandatory:
self.validate_dropbox_credentails()
def on_update(self):
pass
def enable(self):
""" enable service """
if not self.flags.ignore_mandatory:
self.validate_dropbox_credentails()
def validate_dropbox_credentails(self):
try:
self.get_dropbox_session()
except Exception, e:
frappe.throw(e.message)
def get_dropbox_session(self):
# try:
# from dropbox import session
# except:
# raise Exception(_("Please install dropbox python module"))
app_access_key = self.app_access_key or frappe.conf.dropbox_access_key
# app_secret_key = self.get_password(fieldname="app_secret_key",
# raise_exception=False) if self.app_secret_key else frappe.conf.dropbox_secret_key
if not (app_access_key):
raise Exception(_("Please set Dropbox access keys in your site config"))
dbx = dropbox.Dropbox(app_access_key)
try:
dbx.users_get_current_account()
except AuthError as err:
sys.exit("ERROR: Invalid access token; try re-generating an "
"access token from the app console on the web.")
# sess = session.DropboxSession(app_access_key, app_secret_key, "app_folder")
return app_access_key
@frappe.whitelist()
def get_service_details():
return """
<div>
Steps to enable dropbox backup service:
<ol>
<li> Create a dropbox app then get App Key and App Secret,
<a href="https://www.dropbox.com/developers/apps" target="_blank">
https://www.dropbox.com/developers/apps
</a>
</li>
<br>
<li> Setup credentials on Dropbox Settings doctype.
Click on
<button class="btn btn-default btn-xs disabled"> Dropbox Settings </button>
top right corner
</li>
<br>
<li> After settings up App key and App Secret, generate access token
<button class="btn btn-default btn-xs disabled"> Allow Dropbox Access </button>
</li>
<br>
<li>
After saving settings,
<label>
<span class="input-area">
<input type="checkbox" class="input-with-feedback" checked disabled>
</span>
<span class="label-area small">Enable</span>
</label>
Dropbox Integration Service and Save a document.
</li>
</ol>
<p>
After enabling service, system will take backup of files and database on daily or weekly basis
as per set on Dropbox Settings page and upload it to your dropbox.
</p>
</div>
"""
#get auth token
@frappe.whitelist()
def get_dropbox_authorize_url():
doc = frappe.get_doc("Dropbox Settings")
sess = doc.get_dropbox_session()
# request_token = sess.obtain_request_token()
doc.update({
"dropbox_access_key": sess,
# "dropbox_access_secret": request_token.secret
})
doc.save(ignore_permissions=False)
return_address = get_request_site_address(True) \
+ "?cmd=frappe.integrations.doctype.dropbox_settings.dropbox_settings.dropbox_callback"
# url = sess.build_authorize_url(request_token, return_address)
return {
"url": doc.doctype+"/"+doc.name,
}
@frappe.whitelist(allow_guest=True)
def dropbox_callback(oauth_token=None, not_approved=False):
doc = frappe.get_doc("Dropbox Settings")
close = '<p class="text-muted">' + _('Please close this window') + '</p>'
if not not_approved:
if doc.get_password(fieldname="dropbox_access_key", raise_exception=False)==oauth_token:
sess = doc.get_dropbox_session()
sess.set_request_token(doc.get_password(fieldname="dropbox_access_key", raise_exception=False),
doc.get_password(fieldname="dropbox_access_secret", raise_exception=False))
access_token = sess.obtain_access_token()
frappe.db.set_value("Dropbox Settings", None, "dropbox_access_key", access_token.key)
frappe.db.set_value("Dropbox Settings", None, "dropbox_access_secret", access_token.secret)
frappe.db.commit()
else:
frappe.respond_as_web_page(_("Dropbox Setup"),
_("Illegal Access Token. Please try again") + close,
success=False, http_status_code=frappe.AuthenticationError.http_status_code)
else:
frappe.respond_as_web_page(_("Dropbox Setup"),
_("You did not apporve Dropbox Access.") + close,
success=False, http_status_code=frappe.AuthenticationError.http_status_code)
frappe.respond_as_web_page(_("Dropbox Setup"),
_("Dropbox access is approved!") + close,
success=False, http_status_code=frappe.AuthenticationError.http_status_code)
# backup process
@frappe.whitelist()
def take_backup():
"Enqueue longjob for taking backup to dropbox"
enqueue("frappe.integrations.doctype.dropbox_settings.dropbox_settings.take_backup_to_dropbox", queue='long')
frappe.msgprint(_("Queued for backup. It may take a few minutes to an hour."))
def take_backups_daily():
take_backups_if("Daily")
def take_backups_weekly():
take_backups_if("Weekly")
def take_backups_if(freq):
if frappe.db.get_value("Dropbox Settings", None, "backup_frequency") == freq:
take_backup_to_dropbox()
def take_backup_to_dropbox():
did_not_upload, error_log = [], []
try:
if cint(frappe.db.get_value("Integration Service", "Dropbox", "enabled")):
did_not_upload, error_log = backup_to_dropbox()
if did_not_upload: raise Exception
send_email(True, "Dropbox")
except Exception:
file_and_error = [" - ".join(f) for f in zip(did_not_upload, error_log)]
error_message = ("\n".join(file_and_error) + "\n" + frappe.get_traceback())
frappe.errprint(error_message)
send_email(False, "Dropbox", error_message)
def send_email(success, service_name, error_status=None):
if success:
subject = "Backup Upload Successful"
message ="""<h3>Backup Uploaded Successfully</h3><p>Hi there, this is just to inform you
that your backup was successfully uploaded to your %s account. So relax!</p>
""" % service_name
else:
subject = "[Warning] Backup Upload Failed"
message ="""<h3>Backup Upload Failed</h3><p>Oops, your automated backup to %s
failed.</p>
<p>Error message: <br>
<pre><code>%s</code></pre>
</p>
<p>Please contact your system manager for more information.</p>
""" % (service_name, error_status)
if not frappe.db:
frappe.connect()
recipients = split_emails(frappe.db.get_value("Dropbox Settings", None, "send_notifications_to"))
frappe.sendmail(recipients=recipients, subject=subject, message=message)
def backup_to_dropbox():
if not frappe.db:
frappe.connect()
dropbox_client = get_dropbox_client()
# upload database
backup = new_backup(ignore_files=True)
filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_db))
upload_file_to_dropbox(filename, "/database", dropbox_client)
frappe.db.close()
# upload files to files folder
did_not_upload = []
error_log = []
print get_files_path()
upload_from_folder(get_files_path(), "/files", dropbox_client,did_not_upload,error_log)
upload_from_folder(get_files_path(is_private=1), "/private/files", dropbox_client,did_not_upload,error_log)
frappe.connect()
print did_not_upload
return did_not_upload, list(set(error_log))
def get_dropbox_client(previous_dropbox_client=None):
# from dropbox import client
doc = frappe.get_doc("Dropbox Settings")
sess = doc.get_dropbox_session()
# sess.set_token(doc.get_password(fieldname="dropbox_access_key", raise_exception=False),
# doc.get_password(fieldname="dropbox_access_secret", raise_exception=False))
dropbox_client = dropbox.Dropbox(sess)
# upgrade to oauth2
# token = dropbox_client.create_oauth2_access_token()
# dropbox_client = client.DropboxClient(token)
# if previous_dropbox_client:
# dropbox_client.connection_reset_count = previous_dropbox_client.connection_reset_count + 1
# else:
# dropbox_client.connection_reset_count = 0
return dropbox_client
def upload_file_to_dropbox(filename, folder, dropbox_client):
create_folder_if_not_exists(folder, dropbox_client)
chunk_size = 4 * 1024 * 1024
file_size = os.path.getsize(encode(filename))
mode = (dropbox.files.WriteMode.overwrite)
f = open(encode(filename), 'rb')
path = "{0}/{1}".format(folder, os.path.basename(filename))
try:
if file_size <= chunk_size:
dropbox_client.files_upload(f.read(), path, mode)
else:
upload_session_start_result = dropbox_client.files_upload_session_start(f.read(chunk_size))
cursor = dropbox.files.UploadSessionCursor(session_id=upload_session_start_result.session_id, offset=f.tell())
commit = dropbox.files.CommitInfo(path=path, mode=mode)
while f.tell() < file_size:
if ((file_size - f.tell()) <= chunk_size):
dropbox_client.files_upload_session_finish(f.read(chunk_size), cursor, commit)
else:
dropbox_client.files_upload_session_append(f.read(chunk_size), cursor.session_id,cursor.offset)
cursor.offset = f.tell()
except dropbox.exceptions.ApiError as e:
if isinstance(e.error, dropbox.files.UploadError):
error = "File Path: {path}\n".foramt(path=path)
error += frappe.get_traceback()
frappe.log_error(error)
else:
raise
def create_folder_if_not_exists(folder, dropbox_client):
try:
dropbox_client.files_get_metadata(folder)
except dropbox.exceptions.ApiError as e:
# folder not found
if isinstance(e.error, dropbox.files.GetMetadataError):
dropbox_client.files_create_folder(folder)
else:
raise
def upload_from_folder(path, dropbox_folder, dropbox_client, did_not_upload, error_log):
if not os.path.exists(path):
return
try:
response = dropbox_client.files_list_folder(dropbox_folder)
except dropbox.exceptions.ApiError as e:
# folder not found
if isinstance(e.error, dropbox.files.ListFolderError):
response = frappe._dict({"entries": []})
else:
raise
for root, directory, files in os.walk(path):
for filename in files:
filename = cstr(filename)
filepath = os.path.join(root, filename)
if filename in ignore_list:
continue
found = False
for file_metadata in response.entries:
if (os.path.basename(filepath) == file_metadata.name
and os.stat(encode(filepath)).st_size == int(file_metadata.size)):
found = True
break
if not found:
try:
upload_file_to_dropbox(filepath, dropbox_folder, dropbox_client)
except Exception:
did_not_upload.append(filepath)
error_log.append(frappe.get_traceback())
i solved the problem using above code
thanks for your help