@@ -9,11 +9,13 @@ | |||||
"doctype": "DocType", | "doctype": "DocType", | ||||
"document_type": "", | "document_type": "", | ||||
"editable_grid": 1, | "editable_grid": 1, | ||||
"engine": "InnoDB", | |||||
"fields": [ | "fields": [ | ||||
{ | { | ||||
"allow_on_submit": 0, | "allow_on_submit": 0, | ||||
"bold": 0, | "bold": 0, | ||||
"collapsible": 0, | "collapsible": 0, | ||||
"columns": 0, | |||||
"fieldname": "integration_type", | "fieldname": "integration_type", | ||||
"fieldtype": "Select", | "fieldtype": "Select", | ||||
"hidden": 0, | "hidden": 0, | ||||
@@ -40,6 +42,7 @@ | |||||
"allow_on_submit": 0, | "allow_on_submit": 0, | ||||
"bold": 0, | "bold": 0, | ||||
"collapsible": 0, | "collapsible": 0, | ||||
"columns": 0, | |||||
"fieldname": "integration_request_service", | "fieldname": "integration_request_service", | ||||
"fieldtype": "Data", | "fieldtype": "Data", | ||||
"hidden": 0, | "hidden": 0, | ||||
@@ -66,6 +69,7 @@ | |||||
"allow_on_submit": 0, | "allow_on_submit": 0, | ||||
"bold": 0, | "bold": 0, | ||||
"collapsible": 0, | "collapsible": 0, | ||||
"columns": 0, | |||||
"default": "Queued", | "default": "Queued", | ||||
"fieldname": "status", | "fieldname": "status", | ||||
"fieldtype": "Select", | "fieldtype": "Select", | ||||
@@ -73,7 +77,7 @@ | |||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | "in_filter": 0, | ||||
"in_list_view": 0, | |||||
"in_list_view": 1, | |||||
"label": "Status", | "label": "Status", | ||||
"length": 0, | "length": 0, | ||||
"no_copy": 0, | "no_copy": 0, | ||||
@@ -93,6 +97,7 @@ | |||||
"allow_on_submit": 0, | "allow_on_submit": 0, | ||||
"bold": 0, | "bold": 0, | ||||
"collapsible": 0, | "collapsible": 0, | ||||
"columns": 0, | |||||
"fieldname": "data", | "fieldname": "data", | ||||
"fieldtype": "Code", | "fieldtype": "Code", | ||||
"hidden": 0, | "hidden": 0, | ||||
@@ -118,6 +123,7 @@ | |||||
"allow_on_submit": 0, | "allow_on_submit": 0, | ||||
"bold": 0, | "bold": 0, | ||||
"collapsible": 0, | "collapsible": 0, | ||||
"columns": 0, | |||||
"fieldname": "output", | "fieldname": "output", | ||||
"fieldtype": "Code", | "fieldtype": "Code", | ||||
"hidden": 0, | "hidden": 0, | ||||
@@ -143,6 +149,7 @@ | |||||
"allow_on_submit": 0, | "allow_on_submit": 0, | ||||
"bold": 0, | "bold": 0, | ||||
"collapsible": 0, | "collapsible": 0, | ||||
"columns": 0, | |||||
"fieldname": "error", | "fieldname": "error", | ||||
"fieldtype": "Code", | "fieldtype": "Code", | ||||
"hidden": 0, | "hidden": 0, | ||||
@@ -175,7 +182,7 @@ | |||||
"issingle": 0, | "issingle": 0, | ||||
"istable": 0, | "istable": 0, | ||||
"max_attachments": 0, | "max_attachments": 0, | ||||
"modified": "2016-08-11 10:40:32.231331", | |||||
"modified": "2016-10-13 05:01:14.913553", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Integration Broker", | "module": "Integration Broker", | ||||
"name": "Integration Request", | "name": "Integration Request", | ||||
@@ -192,6 +199,7 @@ | |||||
"export": 1, | "export": 1, | ||||
"if_owner": 0, | "if_owner": 0, | ||||
"import": 0, | "import": 0, | ||||
"is_custom": 0, | |||||
"permlevel": 0, | "permlevel": 0, | ||||
"print": 1, | "print": 1, | ||||
"read": 1, | "read": 1, | ||||
@@ -208,5 +216,6 @@ | |||||
"read_only_onload": 0, | "read_only_onload": 0, | ||||
"sort_field": "modified", | "sort_field": "modified", | ||||
"sort_order": "DESC", | "sort_order": "DESC", | ||||
"title_field": "integration_request_service", | |||||
"track_seen": 0 | "track_seen": 0 | ||||
} | } |
@@ -6,7 +6,6 @@ from __future__ import unicode_literals | |||||
import frappe | import frappe | ||||
from frappe import _ | from frappe import _ | ||||
from frappe.model.document import Document | from frappe.model.document import Document | ||||
from frappe.utils.background_jobs import enqueue, get_jobs | |||||
import json, urlparse | import json, urlparse | ||||
from frappe.utils import get_request_session | from frappe.utils import get_request_session | ||||
@@ -17,6 +16,8 @@ class IntegrationService(Document): | |||||
self.enable_service() | self.enable_service() | ||||
self.install_fixtures() | self.install_fixtures() | ||||
frappe.cache().delete_value('scheduler_events') | |||||
def install_fixtures(self): | def install_fixtures(self): | ||||
pass | pass | ||||
@@ -96,13 +97,15 @@ def get_integration_services(): | |||||
return services | return services | ||||
def trigger_integration_service_events(): | |||||
for service in frappe.get_all("Integration Service", filters={"enabled": 1}, fields=["name"]): | |||||
def get_integration_service_events(): | |||||
'''Get scheduler events for enabled integrations''' | |||||
events = {} | |||||
for service in frappe.get_all("Integration Service", filters={"enabled": 1}, | |||||
fields=["name"]): | |||||
controller = get_integration_controller(service.name) | controller = get_integration_controller(service.name) | ||||
if hasattr(controller, "scheduled_jobs"): | |||||
for job in controller.scheduled_jobs: | |||||
for event, handlers in job.items(): | |||||
for handler in handlers: | |||||
if handler not in get_jobs(): | |||||
enqueue(handler, queue='short', event=event) | |||||
if hasattr(controller, "scheduler_events"): | |||||
for key, handlers in controller.scheduler_events: | |||||
events.setdefault(key, []).extend(handlers) | |||||
return events |
@@ -5,8 +5,16 @@ from __future__ import unicode_literals | |||||
import frappe | import frappe | ||||
import unittest | import unittest | ||||
from frappe.utils.scheduler import get_scheduler_events | |||||
# test_records = frappe.get_test_records('Integration Service') | # test_records = frappe.get_test_records('Integration Service') | ||||
class TestIntegrationService(unittest.TestCase): | class TestIntegrationService(unittest.TestCase): | ||||
pass | |||||
def test_scheudler_events(self): | |||||
dropbox_settings = frappe.get_doc('Dropbox Settings') | |||||
dropbox_settings.db_set('enabled', 1) | |||||
events = get_scheduler_events('daily_long') | |||||
self.assertTrue('frappe.integrations.dropbox_integration.take_backups_daily' in events) | |||||
dropbox_settings.db_set('enabled', 0) |
@@ -15,17 +15,15 @@ from frappe.integration_broker.doctype.integration_service.integration_service i | |||||
ignore_list = [".DS_Store"] | ignore_list = [".DS_Store"] | ||||
class DropboxSettings(IntegrationService): | class DropboxSettings(IntegrationService): | ||||
scheduled_jobs = [ | |||||
{ | |||||
"daily_long": [ | |||||
"frappe.integrations.dropbox_integration.take_backups_daily" | |||||
], | |||||
"weekly_long": [ | |||||
"frappe.integrations.dropbox_integration.take_backups_weekly" | |||||
] | |||||
} | |||||
] | |||||
scheduler_events = { | |||||
"daily_long": [ | |||||
"frappe.integrations.dropbox_integration.take_backups_daily" | |||||
], | |||||
"weekly_long": [ | |||||
"frappe.integrations.dropbox_integration.take_backups_weekly" | |||||
] | |||||
} | |||||
def validate(self): | def validate(self): | ||||
if not self.flags.ignore_mandatory: | if not self.flags.ignore_mandatory: | ||||
self.validate_dropbox_credentails() | self.validate_dropbox_credentails() | ||||
@@ -49,10 +47,10 @@ class DropboxSettings(IntegrationService): | |||||
from dropbox import session | from dropbox import session | ||||
except: | except: | ||||
raise Exception(_("Please install dropbox python module")) | raise Exception(_("Please install dropbox python module")) | ||||
if not (self.app_access_key or self.app_secret_key): | if not (self.app_access_key or self.app_secret_key): | ||||
raise Exception(_("Please set Dropbox access keys in your site config")) | raise Exception(_("Please set Dropbox access keys in your site config")) | ||||
sess = session.DropboxSession(self.app_access_key, | sess = session.DropboxSession(self.app_access_key, | ||||
self.get_password(fieldname="app_secret_key", raise_exception=False), "app_folder") | self.get_password(fieldname="app_secret_key", raise_exception=False), "app_folder") | ||||
@@ -64,13 +62,13 @@ def get_service_details(): | |||||
<div> | <div> | ||||
Steps to enable dropbox backup service: | Steps to enable dropbox backup service: | ||||
<ol> | <ol> | ||||
<li> Create a dropbox app then get App Key and App Secret, | |||||
<li> Create a dropbox app then get App Key and App Secret, | |||||
<a href="https://www.dropbox.com/developers/apps" target="_blank"> | <a href="https://www.dropbox.com/developers/apps" target="_blank"> | ||||
https://www.dropbox.com/developers/apps | https://www.dropbox.com/developers/apps | ||||
</a> | </a> | ||||
</li> | </li> | ||||
<br> | <br> | ||||
<li> Setup credentials on Dropbox Settings doctype. | |||||
<li> Setup credentials on Dropbox Settings doctype. | |||||
Click on | Click on | ||||
<button class="btn btn-default btn-xs disabled"> Dropbox Settings </button> | <button class="btn btn-default btn-xs disabled"> Dropbox Settings </button> | ||||
top right corner | top right corner | ||||
@@ -109,7 +107,7 @@ def get_dropbox_authorize_url(): | |||||
"dropbox_access_key": request_token.key, | "dropbox_access_key": request_token.key, | ||||
"dropbox_access_secret": request_token.secret | "dropbox_access_secret": request_token.secret | ||||
}) | }) | ||||
doc.save(ignore_permissions=False) | doc.save(ignore_permissions=False) | ||||
return_address = get_request_site_address(True) \ | return_address = get_request_site_address(True) \ | ||||
@@ -61,28 +61,26 @@ For razorpay payment status is Authorized | |||||
class RazorpaySettings(IntegrationService): | class RazorpaySettings(IntegrationService): | ||||
service_name = "Razorpay" | service_name = "Razorpay" | ||||
supported_currencies = ["INR"] | supported_currencies = ["INR"] | ||||
scheduled_jobs = [ | |||||
{ | |||||
"all": [ | |||||
"frappe.integrations.razorpay.capture_payment" | |||||
] | |||||
} | |||||
] | |||||
scheduler_events = { | |||||
"all": [ | |||||
"frappe.integrations.razorpay.capture_payment" | |||||
] | |||||
} | |||||
def validate(self): | def validate(self): | ||||
if not self.flags.ignore_mandatory: | if not self.flags.ignore_mandatory: | ||||
self.validate_razorpay_credentails() | self.validate_razorpay_credentails() | ||||
def on_update(self): | def on_update(self): | ||||
pass | pass | ||||
def enable(self): | def enable(self): | ||||
call_hook_method('payment_gateway_enabled', gateway='Razorpay') | call_hook_method('payment_gateway_enabled', gateway='Razorpay') | ||||
if not self.flags.ignore_mandatory: | if not self.flags.ignore_mandatory: | ||||
self.validate_razorpay_credentails() | self.validate_razorpay_credentails() | ||||
def validate_razorpay_credentails(self): | def validate_razorpay_credentails(self): | ||||
if self.api_key and self.api_secret: | if self.api_key and self.api_secret: | ||||
try: | try: | ||||
@@ -90,14 +88,14 @@ class RazorpaySettings(IntegrationService): | |||||
auth=(self.api_key, self.get_password(fieldname="api_secret", raise_exception=False))) | auth=(self.api_key, self.get_password(fieldname="api_secret", raise_exception=False))) | ||||
except Exception: | except Exception: | ||||
frappe.throw(_("Seems API Key or API Secret is wrong !!!")) | frappe.throw(_("Seems API Key or API Secret is wrong !!!")) | ||||
def validate_transaction_currency(self, currency): | def validate_transaction_currency(self, currency): | ||||
if currency not in self.supported_currencies: | if currency not in self.supported_currencies: | ||||
frappe.throw(_("Please select another payment method. {0} does not support transactions in currency '{1}'").format(self.service_name, currency)) | frappe.throw(_("Please select another payment method. {0} does not support transactions in currency '{1}'").format(self.service_name, currency)) | ||||
def get_payment_url(self, **kwargs): | def get_payment_url(self, **kwargs): | ||||
return get_url("./integrations/razorpay_checkout?{0}".format(urllib.urlencode(kwargs))) | return get_url("./integrations/razorpay_checkout?{0}".format(urllib.urlencode(kwargs))) | ||||
def create_request(self, data): | def create_request(self, data): | ||||
self.data = frappe._dict(data) | self.data = frappe._dict(data) | ||||
@@ -168,7 +166,7 @@ class RazorpaySettings(IntegrationService): | |||||
"redirect_to": redirect_url, | "redirect_to": redirect_url, | ||||
"status": status | "status": status | ||||
} | } | ||||
def get_settings(self): | def get_settings(self): | ||||
return frappe._dict({ | return frappe._dict({ | ||||
"api_key": self.api_key, | "api_key": self.api_key, | ||||
@@ -212,7 +210,7 @@ def get_checkout_url(**kwargs): | |||||
_("Looks like something is wrong with this site's Razorpay configuration. Don't worry! No payment has been made."), | _("Looks like something is wrong with this site's Razorpay configuration. Don't worry! No payment has been made."), | ||||
success=False, | success=False, | ||||
http_status_code=frappe.ValidationError.http_status_code) | http_status_code=frappe.ValidationError.http_status_code) | ||||
@frappe.whitelist() | @frappe.whitelist() | ||||
def get_service_details(): | def get_service_details(): | ||||
@@ -220,13 +218,13 @@ def get_service_details(): | |||||
<div> | <div> | ||||
<p> Steps to configure Service | <p> Steps to configure Service | ||||
<ol> | <ol> | ||||
<li> Get Razorpay api credentials by login to: | |||||
<li> Get Razorpay api credentials by login to: | |||||
<a href="https://razorpay.com/" target="_blank"> | <a href="https://razorpay.com/" target="_blank"> | ||||
https://razorpay.com/ | https://razorpay.com/ | ||||
</a> | </a> | ||||
</li> | </li> | ||||
<br> | <br> | ||||
<li> Setup credentials on Razorpay Settings doctype. | |||||
<li> Setup credentials on Razorpay Settings doctype. | |||||
Click on | Click on | ||||
<button class="btn btn-default btn-xs disabled"> Razorpay Settings </button> | <button class="btn btn-default btn-xs disabled"> Razorpay Settings </button> | ||||
top right corner | top right corner | ||||
@@ -22,6 +22,8 @@ def get_controller(doctype): | |||||
:param doctype: DocType name as string.""" | :param doctype: DocType name as string.""" | ||||
from frappe.model.document import Document | from frappe.model.document import Document | ||||
global _classes | |||||
if not doctype in _classes: | if not doctype in _classes: | ||||
module_name, custom = frappe.db.get_value("DocType", doctype, ["module", "custom"]) \ | module_name, custom = frappe.db.get_value("DocType", doctype, ["module", "custom"]) \ | ||||
or ["Core", False] | or ["Core", False] | ||||
@@ -48,7 +48,8 @@ def clear_cache(user=None): | |||||
def clear_global_cache(): | def clear_global_cache(): | ||||
frappe.model.meta.clear_cache() | frappe.model.meta.clear_cache() | ||||
frappe.cache().delete_value(["app_hooks", "installed_apps", | frappe.cache().delete_value(["app_hooks", "installed_apps", | ||||
"app_modules", "module_app", "notification_config", 'system_settings']) | |||||
"app_modules", "module_app", "notification_config", 'system_settings' | |||||
'scheduler_events']) | |||||
frappe.setup_module_map() | frappe.setup_module_map() | ||||
def clear_sessions(user=None, keep_current=False, device=None): | def clear_sessions(user=None, keep_current=False, device=None): | ||||
@@ -23,6 +23,7 @@ from frappe.limits import has_expired | |||||
from frappe.utils.data import get_datetime, now_datetime | from frappe.utils.data import get_datetime, now_datetime | ||||
from frappe.core.doctype.user.user import STANDARD_USERS | from frappe.core.doctype.user.user import STANDARD_USERS | ||||
from frappe.installer import update_site_config | from frappe.installer import update_site_config | ||||
from frappe.integration_broker.doctype.integration_service.integration_service import get_integration_service_events | |||||
DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S' | DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S' | ||||
@@ -142,7 +143,11 @@ def trigger(site, event, queued_jobs=(), now=False): | |||||
if not queued_jobs and not now: | if not queued_jobs and not now: | ||||
queued_jobs = get_jobs(site=site, queue=queue) | queued_jobs = get_jobs(site=site, queue=queue) | ||||
for handler in frappe.get_hooks("scheduler_events").get(event, []): | |||||
events = get_scheduler_events(event) | |||||
if not events: | |||||
return | |||||
for handler in events: | |||||
if not now: | if not now: | ||||
if handler not in queued_jobs: | if handler not in queued_jobs: | ||||
enqueue(handler, queue, timeout, event) | enqueue(handler, queue, timeout, event) | ||||
@@ -152,6 +157,20 @@ def trigger(site, event, queued_jobs=(), now=False): | |||||
if frappe.flags.in_test: | if frappe.flags.in_test: | ||||
frappe.flags.ran_schedulers.append(event) | frappe.flags.ran_schedulers.append(event) | ||||
def get_scheduler_events(event): | |||||
'''Get scheduler events from hooks and integrations''' | |||||
scheduler_events = frappe.cache().get_value('scheduler_events') | |||||
if not scheduler_events: | |||||
scheduler_events = frappe.get_hooks("scheduler_events") | |||||
integration_events = get_integration_service_events() | |||||
for key, handlers in integration_events: | |||||
scheduler_events.setdefault(key, []).extend(handlers) | |||||
frappe.cache().set_value('scheduler_events', scheduler_events) | |||||
return scheduler_events.get(event) or [] | |||||
def log(method, message=None): | def log(method, message=None): | ||||
"""log error in patch_log""" | """log error in patch_log""" | ||||
message = frappe.utils.cstr(message) + "\n" if message else "" | message = frappe.utils.cstr(message) + "\n" if message else "" | ||||