Browse Source

[refactor] code for integration service scheduled events

version-14
Rushabh Mehta 8 years ago
parent
commit
1026a48a97
8 changed files with 86 additions and 48 deletions
  1. +11
    -2
      frappe/integration_broker/doctype/integration_request/integration_request.json
  2. +12
    -9
      frappe/integration_broker/doctype/integration_service/integration_service.py
  3. +9
    -1
      frappe/integration_broker/doctype/integration_service/test_integration_service.py
  4. +14
    -16
      frappe/integrations/doctype/dropbox_settings/dropbox_settings.py
  5. +16
    -18
      frappe/integrations/doctype/razorpay_settings/razorpay_settings.py
  6. +2
    -0
      frappe/model/base_document.py
  7. +2
    -1
      frappe/sessions.py
  8. +20
    -1
      frappe/utils/scheduler.py

+ 11
- 2
frappe/integration_broker/doctype/integration_request/integration_request.json View File

@@ -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
} }

+ 12
- 9
frappe/integration_broker/doctype/integration_service/integration_service.py View File

@@ -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

+ 9
- 1
frappe/integration_broker/doctype/integration_service/test_integration_service.py View File

@@ -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)

+ 14
- 16
frappe/integrations/doctype/dropbox_settings/dropbox_settings.py View File

@@ -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) \


+ 16
- 18
frappe/integrations/doctype/razorpay_settings/razorpay_settings.py View File

@@ -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


+ 2
- 0
frappe/model/base_document.py View File

@@ -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]


+ 2
- 1
frappe/sessions.py View File

@@ -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):


+ 20
- 1
frappe/utils/scheduler.py View File

@@ -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 ""


Loading…
Cancel
Save