@@ -171,7 +171,7 @@ def export_json(context, doctype, path, name=None): | |||||
@click.argument('path') | @click.argument('path') | ||||
@pass_context | @pass_context | ||||
def export_csv(context, doctype, path): | 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 | from frappe.core.page.data_import_tool import data_import_tool | ||||
for site in context.sites: | for site in context.sites: | ||||
try: | try: | ||||
@@ -51,6 +51,11 @@ def get_data(): | |||||
"name": "Email Queue", | "name": "Email Queue", | ||||
"description": _("Background Email Queue"), | "description": _("Background Email Queue"), | ||||
}, | }, | ||||
{ | |||||
"type": "page", | |||||
"label": _("Background Jobs"), | |||||
"name": "background_jobs", | |||||
}, | |||||
{ | { | ||||
"type": "doctype", | "type": "doctype", | ||||
"name": "Error Snapshot", | "name": "Error Snapshot", | ||||
@@ -0,0 +1,26 @@ | |||||
<div style="padding: 20px;" class="list-jobs"> | |||||
{% if jobs.length %} | |||||
<table class="table table-bordered"> | |||||
<thead> | |||||
<tr> | |||||
<th style="width: 20%">Queue</th> | |||||
<th>Job</th> | |||||
<th style="width: 20%">Created</th> | |||||
</tr> | |||||
</thead> | |||||
<tbody> | |||||
{% for j in jobs %} | |||||
<tr> | |||||
<td>{{ j.queue }}</td> | |||||
<td><span class="indicator {{ j.color }}" title="{{ j.status }}"> | |||||
{{ frappe.utils.encode_tags(j.job_name) }}</span></td> | |||||
<td>{{ j.creation }}</td> | |||||
</tr> | |||||
{% endfor %} | |||||
</tbody> | |||||
</table> | |||||
{% else %} | |||||
<p class="text-muted">No pending or failed jobs for this site</p> | |||||
{% endif %} | |||||
<p class="text-muted">Last refreshed {{ frappe.datetime.now_datetime() }}</p> | |||||
</div> |
@@ -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); | |||||
} | |||||
} | |||||
}); | |||||
} |
@@ -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" | |||||
} |
@@ -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 |
@@ -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', 'docfield', force=True) #2016-02-26 | ||||
execute:frappe.reload_doc('core', 'doctype', 'docperm') #2014-06-24 | execute:frappe.reload_doc('core', 'doctype', 'docperm') #2014-06-24 | ||||
execute:frappe.reload_doc('core', 'doctype', 'role') | 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('custom', 'doctype', 'custom_field') #2015-10-19 | ||||
execute:frappe.reload_doc('core', 'doctype', 'page') #2013-13-26 | execute:frappe.reload_doc('core', 'doctype', 'page') #2013-13-26 | ||||
execute:frappe.reload_doc('core', 'doctype', 'report') #2014-06-03 | execute:frappe.reload_doc('core', 'doctype', 'report') #2014-06-03 | ||||
@@ -407,6 +407,9 @@ h6.uppercase, | |||||
.frappe-control .help-box { | .frappe-control .help-box { | ||||
margin-top: 3px; | margin-top: 3px; | ||||
} | } | ||||
.frappe-control pre { | |||||
white-space: pre-wrap; | |||||
} | |||||
.hide-control { | .hide-control { | ||||
display: none !important; | display: none !important; | ||||
} | } | ||||
@@ -44,6 +44,19 @@ frappe.utils = { | |||||
strip_whitespace: function(html) { | strip_whitespace: function(html) { | ||||
return (html || "").replace(/<p>\s*<\/p>/g, "").replace(/<br>(\s*<br>\s*)+/g, "<br><br>"); | return (html || "").replace(/<p>\s*<\/p>/g, "").replace(/<br>(\s*<br>\s*)+/g, "<br><br>"); | ||||
}, | }, | ||||
encode_tags: function(html) { | |||||
var tagsToReplace = { | |||||
'&': '&', | |||||
'<': '<', | |||||
'>': '>' | |||||
}; | |||||
function replaceTag(tag) { | |||||
return tagsToReplace[tag] || tag; | |||||
} | |||||
return html.replace(/[&<>]/g, replaceTag); | |||||
}, | |||||
strip_original_content: function(txt) { | strip_original_content: function(txt) { | ||||
var out = [], | var out = [], | ||||
part = [], | part = [], | ||||
@@ -33,6 +33,9 @@ frappe.template.compile = function(str, name) { | |||||
// {% endfor %} --> {% } %} | // {% endfor %} --> {% } %} | ||||
str = str.replace(/{%\s?endif\s?%}/g, "{% }; %}"); | str = str.replace(/{%\s?endif\s?%}/g, "{% }; %}"); | ||||
// {% else %} --> {% } else { %} | |||||
str = str.replace(/{%\s?else\s?%}/g, "{% } else { %}"); | |||||
// {% endif %} --> {% } %} | // {% endif %} --> {% } %} | ||||
str = str.replace(/{%\s?endfor\s?%}/g, "{% }; %}"); | str = str.replace(/{%\s?endfor\s?%}/g, "{% }; %}"); | ||||
@@ -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 { | .link-field.ui-front { | ||||
z-index: inherit; | z-index: inherit; | ||||
} | } | ||||
@@ -511,6 +511,10 @@ h6.uppercase, .h6.uppercase { | |||||
.help-box { | .help-box { | ||||
margin-top: 3px; | margin-top: 3px; | ||||
} | } | ||||
pre { | |||||
white-space: pre-wrap; | |||||
} | |||||
} | } | ||||
.hide-control { | .hide-control { | ||||
@@ -512,13 +512,14 @@ def get_site_info(): | |||||
# only get system users | # only get system users | ||||
users = frappe.get_all('User', filters={'user_type': 'System User', 'name': ('not in', STANDARD_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) | system_managers = get_system_managers(only_name=True) | ||||
for u in users: | for u in users: | ||||
# tag system managers | # tag system managers | ||||
u.is_system_manager = 1 if u.name in system_managers else 0 | u.is_system_manager = 1 if u.name in system_managers else 0 | ||||
u.full_name = get_fullname(u.name) | u.full_name = get_fullname(u.name) | ||||
u.email = u.name | |||||
del u['name'] | |||||
system_settings = frappe.db.get_singles_dict('System Settings') | system_settings = frappe.db.get_singles_dict('System Settings') | ||||
space_usage = frappe._dict((frappe.local.conf.limits or {}).get('space_usage', {})) | space_usage = frappe._dict((frappe.local.conf.limits or {}).get('space_usage', {})) | ||||
@@ -172,3 +172,10 @@ def get_redis_conn(): | |||||
return redis.from_url(frappe.local.conf.redis_queue) | 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) |