diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 9798458774..a55ff3f24c 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -171,7 +171,7 @@ def export_json(context, doctype, path, name=None): @click.argument('path') @pass_context def export_csv(context, doctype, path): - "Export data import template for DocType" + "Export data import template with data for DocType" from frappe.core.page.data_import_tool import data_import_tool for site in context.sites: try: diff --git a/frappe/config/core.py b/frappe/config/core.py index a4a9ac860f..e24e317f8a 100644 --- a/frappe/config/core.py +++ b/frappe/config/core.py @@ -51,6 +51,11 @@ def get_data(): "name": "Email Queue", "description": _("Background Email Queue"), }, + { + "type": "page", + "label": _("Background Jobs"), + "name": "background_jobs", + }, { "type": "doctype", "name": "Error Snapshot", diff --git a/frappe/core/page/background_jobs/__init__.py b/frappe/core/page/background_jobs/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/core/page/background_jobs/background_jobs.html b/frappe/core/page/background_jobs/background_jobs.html new file mode 100644 index 0000000000..b1eded471f --- /dev/null +++ b/frappe/core/page/background_jobs/background_jobs.html @@ -0,0 +1,26 @@ +
+ {% if jobs.length %} + + + + + + + + + + {% for j in jobs %} + + + + + + {% endfor %} + +
QueueJobCreated
{{ j.queue }} + {{ frappe.utils.encode_tags(j.job_name) }}{{ j.creation }}
+ {% else %} +

No pending or failed jobs for this site

+ {% endif %} +

Last refreshed {{ frappe.datetime.now_datetime() }}

+
\ No newline at end of file diff --git a/frappe/core/page/background_jobs/background_jobs.js b/frappe/core/page/background_jobs/background_jobs.js new file mode 100644 index 0000000000..525e64ad49 --- /dev/null +++ b/frappe/core/page/background_jobs/background_jobs.js @@ -0,0 +1,31 @@ +frappe.pages['background_jobs'].on_page_load = function(wrapper) { + frappe.pages.background_jobs.page = frappe.ui.make_app_page({ + parent: wrapper, + title: 'Background Jobs', + single_column: true + }); +} + +frappe.pages['background_jobs'].on_page_show = function(wrapper) { + frappe.pages.background_jobs.refresh_jobs(); +} + +frappe.pages.background_jobs.refresh_jobs = function() { + var page = frappe.pages.background_jobs.page; + + // don't call if already waiting for a response + if(page.called) return; + page.called = true; + frappe.call({ + method: 'frappe.core.page.background_jobs.background_jobs.get_info', + callback: function(r) { + page.called = false; + page.body.find('.list-jobs').remove(); + $(frappe.render_template('background_jobs', {jobs:r.message || []})).appendTo(page.body); + + if(frappe.get_route()[0]==='background_jobs') { + frappe.background_jobs_timeout = setTimeout(frappe.pages.background_jobs.refresh_jobs, 2000); + } + } + }); +} diff --git a/frappe/core/page/background_jobs/background_jobs.json b/frappe/core/page/background_jobs/background_jobs.json new file mode 100644 index 0000000000..6701cc54bc --- /dev/null +++ b/frappe/core/page/background_jobs/background_jobs.json @@ -0,0 +1,22 @@ +{ + "content": null, + "creation": "2016-08-18 16:44:14.322642", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2016-08-18 16:48:11.577611", + "modified_by": "Administrator", + "module": "Core", + "name": "background_jobs", + "owner": "Administrator", + "page_name": "background_jobs", + "roles": [ + { + "role": "System Manager" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "title": "Background Jobs" +} \ No newline at end of file diff --git a/frappe/core/page/background_jobs/background_jobs.py b/frappe/core/page/background_jobs/background_jobs.py new file mode 100644 index 0000000000..ddf179d132 --- /dev/null +++ b/frappe/core/page/background_jobs/background_jobs.py @@ -0,0 +1,32 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe + +from rq import Queue +from frappe.utils.background_jobs import get_redis_conn +from frappe.utils import format_datetime + +colors = { + 'queued': 'orange', + 'failed': 'red', + 'started': 'green' +} + +@frappe.whitelist() +def get_info(): + queues = Queue.all(get_redis_conn()) + jobs = [] + for q in queues: + for j in q.get_jobs(): + if j.kwargs.get('site')==frappe.local.site: + jobs.append({ + 'job_name': j.kwargs.get('kwargs', {}).get('playbook_method') \ + or str(j.kwargs.get('job_name')), + 'status': j.status, 'queue': str(q.name), + 'creation': format_datetime(j.created_at), + 'color': colors[j.status] + }) + + return jobs \ No newline at end of file diff --git a/frappe/patches.txt b/frappe/patches.txt index 9044ed4c4c..9e0122d13f 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -7,6 +7,7 @@ execute:frappe.reload_doc('core', 'doctype', 'doctype', force=True) #2016-07-11 execute:frappe.reload_doc('core', 'doctype', 'docfield', force=True) #2016-02-26 execute:frappe.reload_doc('core', 'doctype', 'docperm') #2014-06-24 execute:frappe.reload_doc('core', 'doctype', 'role') +execute:frappe.reload_doc('core', 'doctype', 'user') execute:frappe.reload_doc('custom', 'doctype', 'custom_field') #2015-10-19 execute:frappe.reload_doc('core', 'doctype', 'page') #2013-13-26 execute:frappe.reload_doc('core', 'doctype', 'report') #2014-06-03 diff --git a/frappe/public/css/form.css b/frappe/public/css/form.css index 1adde0186d..734303242f 100644 --- a/frappe/public/css/form.css +++ b/frappe/public/css/form.css @@ -407,6 +407,9 @@ h6.uppercase, .frappe-control .help-box { margin-top: 3px; } +.frappe-control pre { + white-space: pre-wrap; +} .hide-control { display: none !important; } diff --git a/frappe/public/js/frappe/misc/utils.js b/frappe/public/js/frappe/misc/utils.js index 28f805cea8..a785a6eb99 100644 --- a/frappe/public/js/frappe/misc/utils.js +++ b/frappe/public/js/frappe/misc/utils.js @@ -44,6 +44,19 @@ frappe.utils = { strip_whitespace: function(html) { return (html || "").replace(/

\s*<\/p>/g, "").replace(/
(\s*
\s*)+/g, "

"); }, + encode_tags: function(html) { + var tagsToReplace = { + '&': '&', + '<': '<', + '>': '>' + }; + + function replaceTag(tag) { + return tagsToReplace[tag] || tag; + } + + return html.replace(/[&<>]/g, replaceTag); + }, strip_original_content: function(txt) { var out = [], part = [], diff --git a/frappe/public/js/lib/microtemplate.js b/frappe/public/js/lib/microtemplate.js index deb8a067fc..c4b981bda2 100644 --- a/frappe/public/js/lib/microtemplate.js +++ b/frappe/public/js/lib/microtemplate.js @@ -33,6 +33,9 @@ frappe.template.compile = function(str, name) { // {% endfor %} --> {% } %} str = str.replace(/{%\s?endif\s?%}/g, "{% }; %}"); + // {% else %} --> {% } else { %} + str = str.replace(/{%\s?else\s?%}/g, "{% } else { %}"); + // {% endif %} --> {% } %} str = str.replace(/{%\s?endfor\s?%}/g, "{% }; %}"); diff --git a/frappe/public/less/desk.less b/frappe/public/less/desk.less index a3434f3746..aa45e6699f 100644 --- a/frappe/public/less/desk.less +++ b/frappe/public/less/desk.less @@ -284,37 +284,6 @@ textarea.form-control { } } -// .form-page { -// .form-group { -// margin: 20px 0px; -// } -// -// input.form-control, select.form-control { -// position: relative; -// box-shadow: none; -// border: none; -// padding: 0px; -// border-bottom: 1px solid transparent; -// border-radius: 0; -// background-color: inherit; -// font-size: 16px; -// } -// input.form-control:focus, select.form-control:focus { -// box-shadow: none; -// border-color: @text-color; -// } -// -// .control-label { -// margin-bottom: 3px; -// } -// -// .frappe-control:not([data-fieldtype*="Text"]) { -// .control-value { -// font-size: 16px; -// } -// } -// } - .link-field.ui-front { z-index: inherit; } diff --git a/frappe/public/less/form.less b/frappe/public/less/form.less index 43aeea7362..35b95d8c73 100644 --- a/frappe/public/less/form.less +++ b/frappe/public/less/form.less @@ -511,6 +511,10 @@ h6.uppercase, .h6.uppercase { .help-box { margin-top: 3px; } + + pre { + white-space: pre-wrap; + } } .hide-control { diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index afc242b9d5..3a7884066e 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -512,13 +512,14 @@ def get_site_info(): # only get system users users = frappe.get_all('User', filters={'user_type': 'System User', 'name': ('not in', STANDARD_USERS)}, - fields=['name', 'first_name', 'last_name', 'enabled', - 'last_login', 'last_active', 'language', 'time_zone']) + fields=['name', 'enabled', 'last_login', 'last_active', 'language', 'time_zone']) system_managers = get_system_managers(only_name=True) for u in users: # tag system managers u.is_system_manager = 1 if u.name in system_managers else 0 u.full_name = get_fullname(u.name) + u.email = u.name + del u['name'] system_settings = frappe.db.get_singles_dict('System Settings') space_usage = frappe._dict((frappe.local.conf.limits or {}).get('space_usage', {})) diff --git a/frappe/utils/background_jobs.py b/frappe/utils/background_jobs.py index 1d4f86840f..4a98bb767c 100755 --- a/frappe/utils/background_jobs.py +++ b/frappe/utils/background_jobs.py @@ -172,3 +172,10 @@ def get_redis_conn(): return redis.from_url(frappe.local.conf.redis_queue) +def enqueue_test_job(): + enqueue('frappe.utils.background_jobs.test_job', s=100) + +def test_job(s): + import time + print 'sleeping...' + time.sleep(s) \ No newline at end of file