Refactored plugin architecture to allow custom script reports
This commit is contained in:
parent
89be73bd9b
commit
49460beed5
10 changed files with 186 additions and 85 deletions
|
|
@ -17,29 +17,11 @@ class DocType:
|
|||
def on_trash(self):
|
||||
webnotes.clear_cache(doctype=self.doc.dt)
|
||||
|
||||
def get_custom_server_script(doctype, plugin=None):
|
||||
import os, MySQLdb
|
||||
custom_script = webnotes.cache().get_value("_server_script:" + doctype)
|
||||
|
||||
if not custom_script:
|
||||
try:
|
||||
script_path = get_custom_server_script_path(doctype, plugin)
|
||||
if os.path.exists(script_path):
|
||||
with open(script_path, 'r') as f:
|
||||
custom_script = f.read()
|
||||
else:
|
||||
custom_script = "Does Not Exist"
|
||||
webnotes.cache().set_value("_server_script:" + doctype, custom_script)
|
||||
except (webnotes.DoesNotExistError, MySQLdb.OperationalError):
|
||||
# this happens when syncing
|
||||
return None
|
||||
|
||||
return None if custom_script == "Does Not Exist" else custom_script
|
||||
|
||||
def make_custom_server_script_file(doctype, script=None):
|
||||
import os
|
||||
from webnotes.plugins import get_path
|
||||
|
||||
file_path = get_custom_server_script_path(doctype)
|
||||
file_path = get_path(None, "DocType", doctype)
|
||||
if os.path.exists(file_path):
|
||||
raise IOError(file_path + " already exists")
|
||||
|
||||
|
|
@ -58,24 +40,4 @@ class CustomDocType(DocType):
|
|||
{script}""".format(script=script or "\tpass")
|
||||
|
||||
with open(file_path, "w") as f:
|
||||
f.write(custom_script)
|
||||
|
||||
def get_custom_server_script_path(doctype, plugin=None):
|
||||
from webnotes.modules import scrub, get_plugin_path
|
||||
from webnotes.utils import get_site_base_path
|
||||
import os
|
||||
|
||||
# check if doctype exists
|
||||
opts = webnotes.conn.get_value("DocType", doctype, ["name", "module", "plugin"])
|
||||
if not opts:
|
||||
raise webnotes.DoesNotExistError("""DocType "{doctype}" does not exist""".format(doctype=doctype))
|
||||
|
||||
name, module, doctype_plugin = opts
|
||||
if not plugin:
|
||||
plugin = doctype_plugin or os.path.basename(get_site_base_path())
|
||||
|
||||
# site_abs_path/plugin_name/module_name/doctype/doctype_name/doctype_name.py
|
||||
path = os.path.join(get_plugin_path(scrub(plugin)), scrub(module),
|
||||
"doctype", scrub(doctype), scrub(doctype) + ".py")
|
||||
|
||||
return path
|
||||
f.write(custom_script)
|
||||
|
|
@ -29,8 +29,12 @@ class DocType:
|
|||
self.export_doc()
|
||||
|
||||
def export_doc(self):
|
||||
# export
|
||||
from webnotes.modules.export_file import export_to_files
|
||||
if self.doc.is_standard == 'Yes' and (conf.get('developer_mode') or 0) == 1:
|
||||
from webnotes.modules.export_file import export_to_files
|
||||
export_to_files(record_list=[['Report', self.doc.name]],
|
||||
record_module=webnotes.conn.get_value("DocType", self.doc.ref_doctype, "module"))
|
||||
record_module=webnotes.conn.get_value("DocType", self.doc.ref_doctype, "module"))
|
||||
elif self.doc.is_standard == 'No' and self.doc.report_type == "Script Report":
|
||||
from webnotes.plugins import get_plugin_name
|
||||
export_to_files(record_list=[['Report', self.doc.name]],
|
||||
record_module=webnotes.conn.get_value("DocType", self.doc.ref_doctype, "module"),
|
||||
plugin=get_plugin_name("Report", self.doc.name), create_init=False)
|
||||
|
|
@ -19,7 +19,7 @@ def get_server_obj(doc, doclist = [], basedoctype = ''):
|
|||
# for test
|
||||
import webnotes
|
||||
from webnotes.modules import scrub, get_doctype_module
|
||||
from core.doctype.custom_script.custom_script import get_custom_server_script
|
||||
from webnotes.plugins import get_code_and_execute
|
||||
|
||||
# get doctype details
|
||||
module = get_doctype_module(doc.doctype) or "core"
|
||||
|
|
@ -33,13 +33,10 @@ def get_server_obj(doc, doclist = [], basedoctype = ''):
|
|||
return DocType(doc, doclist)
|
||||
|
||||
# custom?
|
||||
custom_script = get_custom_server_script(doc.doctype)
|
||||
|
||||
if custom_script:
|
||||
opts = {"DocType": DocType}
|
||||
exec custom_script in opts
|
||||
return opts["CustomDocType"](doc, doclist)
|
||||
|
||||
namespace = {"DocType": DocType}
|
||||
get_code_and_execute(module, "DocType", doc.doctype, namespace=namespace)
|
||||
if namespace.get("CustomDocType"):
|
||||
return namespace["CustomDocType"](doc, doclist)
|
||||
else:
|
||||
return DocType(doc, doclist)
|
||||
|
||||
|
|
|
|||
|
|
@ -228,11 +228,11 @@ def cache_name(doctype, processed):
|
|||
return "doctype:" + doctype + suffix
|
||||
|
||||
def clear_cache(doctype=None):
|
||||
|
||||
import webnotes.plugins
|
||||
def clear_single(dt):
|
||||
webnotes.cache().delete_value(cache_name(dt, False))
|
||||
webnotes.cache().delete_value(cache_name(dt, True))
|
||||
webnotes.cache().delete_value("_server_script:" + dt)
|
||||
webnotes.plugins.clear_cache("DocType", dt)
|
||||
|
||||
if doctype_cache and doctype in doctype_cache:
|
||||
del doctype_cache[dt]
|
||||
|
|
|
|||
|
|
@ -32,10 +32,6 @@ def get_module_path(module):
|
|||
return os.path.join(app_path, 'lib', m)
|
||||
else:
|
||||
return os.path.join(app_path, 'app', m)
|
||||
|
||||
def get_plugin_path(plugin):
|
||||
from webnotes.utils import get_site_path
|
||||
return get_site_path(webnotes.conf.get("plugins_path"), scrub(plugin))
|
||||
|
||||
def get_doc_path(module, doctype, name):
|
||||
dt, dn = scrub_dt_dn(doctype, name)
|
||||
|
|
|
|||
|
|
@ -5,12 +5,13 @@ from __future__ import unicode_literals
|
|||
|
||||
import webnotes, os
|
||||
import webnotes.model.doc
|
||||
from webnotes.modules import scrub, get_module_path, lower_case_files_for, scrub_dt_dn, get_plugin_path
|
||||
from webnotes.modules import scrub, get_module_path, lower_case_files_for, scrub_dt_dn
|
||||
from webnotes.plugins import get_plugin_path
|
||||
|
||||
def export_doc(doc):
|
||||
export_to_files([[doc.doctype, doc.name]])
|
||||
|
||||
def export_to_files(record_list=None, record_module=None, verbose=0, plugin=None):
|
||||
def export_to_files(record_list=None, record_module=None, verbose=0, plugin=None, create_init=None):
|
||||
"""
|
||||
Export record_list to files. record_list is a list of lists ([doctype],[docname] ) ,
|
||||
"""
|
||||
|
|
@ -21,21 +22,22 @@ def export_to_files(record_list=None, record_module=None, verbose=0, plugin=None
|
|||
if record_list:
|
||||
for record in record_list:
|
||||
write_document_file(webnotes.model.doc.get(record[0], record[1]),
|
||||
record_module, plugin=plugin)
|
||||
record_module, plugin=plugin, create_init=create_init)
|
||||
|
||||
def write_document_file(doclist, record_module=None, plugin=None):
|
||||
def write_document_file(doclist, record_module=None, plugin=None, create_init=None):
|
||||
from webnotes.modules.utils import pprint_doclist
|
||||
|
||||
doclist = [filter_fields(d.fields) for d in doclist]
|
||||
|
||||
module = record_module or get_module_name(doclist)
|
||||
code_type = doclist[0]['doctype'] in lower_case_files_for
|
||||
if create_init is None:
|
||||
create_init = doclist[0]['doctype'] in lower_case_files_for
|
||||
|
||||
# create folder
|
||||
folder = create_folder(module, doclist[0]['doctype'], doclist[0]['name'], code_type, plugin=plugin)
|
||||
folder = create_folder(module, doclist[0]['doctype'], doclist[0]['name'], create_init, plugin=plugin)
|
||||
|
||||
# write the data file
|
||||
fname = (code_type and scrub(doclist[0]['name'])) or doclist[0]['name']
|
||||
fname = (doclist[0]['doctype'] in lower_case_files_for and scrub(doclist[0]['name'])) or doclist[0]['name']
|
||||
with open(os.path.join(folder, fname +'.txt'),'w+') as txtfile:
|
||||
txtfile.write(pprint_doclist(doclist))
|
||||
|
||||
|
|
@ -71,7 +73,7 @@ def get_module_name(doclist):
|
|||
|
||||
return module
|
||||
|
||||
def create_folder(module, dt, dn, code_type, plugin=None):
|
||||
def create_folder(module, dt, dn, create_init, plugin=None):
|
||||
if plugin:
|
||||
module_path = os.path.join(get_plugin_path(plugin), scrub(module))
|
||||
else:
|
||||
|
|
@ -85,7 +87,7 @@ def create_folder(module, dt, dn, code_type, plugin=None):
|
|||
webnotes.create_folder(folder)
|
||||
|
||||
# create init_py_files
|
||||
if code_type:
|
||||
if create_init:
|
||||
create_init_py(module_path, dt, dn)
|
||||
|
||||
return folder
|
||||
|
|
|
|||
110
webnotes/plugins.py
Normal file
110
webnotes/plugins.py
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
|
||||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import webnotes
|
||||
|
||||
def get_code_and_execute(module, doctype, docname, plugin=None, namespace=None):
|
||||
code = get_code(module, doctype, docname, plugin)
|
||||
return exec_code(code, namespace)
|
||||
|
||||
def exec_code(code, namespace=None):
|
||||
if namespace is None: namespace = {}
|
||||
|
||||
if code:
|
||||
exec code in namespace
|
||||
|
||||
return namespace
|
||||
|
||||
def get_code(module, doctype, docname, plugin=None):
|
||||
import MySQLdb
|
||||
|
||||
try:
|
||||
code = read_file(module, doctype, docname, plugin, cache=True)
|
||||
except MySQLdb.OperationalError:
|
||||
# this happens when syncing
|
||||
return None
|
||||
|
||||
return code
|
||||
|
||||
def get_cache_key(doctype, docname, extn="py"):
|
||||
return "Plugin File:{doctype}:{docname}:{extn}".format(doctype=doctype, docname=docname, extn=extn)
|
||||
|
||||
def get_plugin_name(doctype, docname):
|
||||
import os
|
||||
from webnotes.utils import get_site_base_path
|
||||
plugin = None
|
||||
|
||||
meta = webnotes.get_doctype(doctype)
|
||||
if meta.get_field("plugin"):
|
||||
plugin = webnotes.conn.get_value(doctype, docname, "plugin")
|
||||
if not plugin:
|
||||
plugin = os.path.basename(get_site_base_path())
|
||||
|
||||
return plugin
|
||||
|
||||
def read_file(module, doctype, docname, plugin=None, extn="py", cache=False):
|
||||
import os
|
||||
content = None
|
||||
|
||||
if cache:
|
||||
content = webnotes.cache().get_value(get_cache_key(doctype, docname, extn))
|
||||
|
||||
if not content:
|
||||
path = get_path(module, doctype, docname, plugin, extn)
|
||||
if os.path.exists(path):
|
||||
with open(path, 'r') as f:
|
||||
content = f.read() or "Does Not Exist"
|
||||
|
||||
if cache:
|
||||
webnotes.cache().set_value(get_cache_key(doctype, docname, extn), content)
|
||||
|
||||
return None if (content == "Does Not Exist") else content
|
||||
|
||||
def get_path(module, doctype, docname, plugin=None, extn="py"):
|
||||
from webnotes.modules import scrub
|
||||
import os
|
||||
|
||||
if not module: module = webnotes.conn.get_value(doctype, docname, "module")
|
||||
if not plugin: plugin = get_plugin_name(doctype, docname)
|
||||
|
||||
# site_abs_path/plugins/module/doctype/docname/docname.py
|
||||
return os.path.join(get_plugin_path(scrub(plugin)), scrub(module),
|
||||
scrub(doctype), scrub(docname), scrub(docname) + "." + extn)
|
||||
|
||||
def get_plugin_path(plugin=None):
|
||||
from webnotes.modules import scrub
|
||||
from webnotes.utils import get_site_path
|
||||
return get_site_path(webnotes.conf.get("plugins_path"), scrub(plugin))
|
||||
|
||||
def remove_init_files():
|
||||
import os
|
||||
from webnotes.utils import get_site_path, cstr
|
||||
for path, folders, files in os.walk(get_site_path(webnotes.conf.get("plugins_path"))):
|
||||
for f in files:
|
||||
# cstr(f) is required when filenames are non-ascii
|
||||
if cstr(f) in ("__init__.py", "__init__.pyc"):
|
||||
os.remove(os.path.join(path, f))
|
||||
|
||||
def clear_cache(doctype=None, docname=None):
|
||||
import os
|
||||
from webnotes.utils import get_site_path
|
||||
|
||||
def titlecase(txt):
|
||||
return txt.replace("_", " ").title()
|
||||
|
||||
def clear_single(dt, dn):
|
||||
dt = titlecase(dt)
|
||||
dn = titlecase(dn)
|
||||
webnotes.cache().delete_value(get_cache_key(dt, dn, "py"))
|
||||
webnotes.cache().delete_value(get_cache_key(dt, dn, "js"))
|
||||
|
||||
if not (doctype and docname):
|
||||
for path, folders, files in os.walk(get_site_path(webnotes.conf.get("plugins_path"))):
|
||||
if files:
|
||||
dt = os.path.basename(os.path.dirname(path))
|
||||
dn = os.path.basename(path)
|
||||
clear_single(dt, dn)
|
||||
else:
|
||||
clear_single(doctype, docname)
|
||||
|
||||
|
|
@ -14,6 +14,7 @@ import json
|
|||
from webnotes.utils import cint
|
||||
import webnotes.model.doctype
|
||||
import webnotes.defaults
|
||||
import webnotes.plugins
|
||||
|
||||
@webnotes.whitelist()
|
||||
def clear(user=None):
|
||||
|
|
@ -26,6 +27,9 @@ def clear_cache(user=None):
|
|||
|
||||
# clear doctype cache
|
||||
webnotes.model.doctype.clear_cache()
|
||||
|
||||
# clear plugins code cache
|
||||
webnotes.plugins.clear_cache()
|
||||
|
||||
if user:
|
||||
cache.delete_value("bootinfo:" + user)
|
||||
|
|
|
|||
|
|
@ -11,33 +11,50 @@ from webnotes import _
|
|||
from webnotes.modules import scrub, get_module_path
|
||||
from webnotes.utils import flt, cint
|
||||
import webnotes.widgets.reportview
|
||||
import webnotes.plugins
|
||||
|
||||
@webnotes.whitelist()
|
||||
def get_script(report_name):
|
||||
report = webnotes.doc("Report", report_name)
|
||||
|
||||
doctype = webnotes.conn.get_value("DocType", report.ref_doctype, "module")
|
||||
script_path = os.path.join(get_module_path(doctype),
|
||||
"report", scrub(report.name), scrub(report.name) + ".js")
|
||||
module = webnotes.conn.get_value("DocType", report.ref_doctype, "module")
|
||||
module_path = get_module_path(module)
|
||||
report_folder = os.path.join(module_path, "report", scrub(report.name))
|
||||
script_path = os.path.join(report_folder, scrub(report.name) + ".js")
|
||||
|
||||
script = None
|
||||
if os.path.exists(script_path):
|
||||
with open(script_path, "r") as script:
|
||||
script = script.read()
|
||||
|
||||
if not script and report.is_standard == "No":
|
||||
script = webnotes.plugins.read_file(module, "Report", report.name, extn="js", cache=True)
|
||||
|
||||
if not script and report.javascript:
|
||||
script = report.javascript
|
||||
|
||||
if not script:
|
||||
script = "wn.query_reports['%s']={}" % report_name
|
||||
|
||||
# load translations
|
||||
if webnotes.lang != "en":
|
||||
from webnotes.translate import get_lang_data
|
||||
locale_path = os.path.join(get_module_path(webnotes.conn.get_value(doctype)),"report", scrub(report.name))
|
||||
messages = get_lang_data(locale_path,
|
||||
webnotes.lang, 'js')
|
||||
webnotes.response["__messages"] = messages
|
||||
if os.path.exists(report_folder):
|
||||
messages = get_lang_data(report_folder, webnotes.lang, 'js')
|
||||
webnotes.response["__messages"] = messages
|
||||
else:
|
||||
# TODO check if language files get exported here
|
||||
plugins_report_folder = webnotes.plugins.get_path(module, "Report", report.name)
|
||||
if os.path.exists(plugins_report_folder):
|
||||
messages = get_lang_data(plugins_report_folder, webnotes.lang, 'js')
|
||||
webnotes.response["__messages"] = messages
|
||||
|
||||
if os.path.exists(script_path):
|
||||
with open(script_path, "r") as script:
|
||||
return script.read()
|
||||
elif report.javascript:
|
||||
return report.javascript
|
||||
else:
|
||||
return "wn.query_reports['%s']={}" % report_name
|
||||
return script
|
||||
|
||||
@webnotes.whitelist()
|
||||
def run(report_name, filters=None):
|
||||
from webnotes.plugins import get_code_and_execute
|
||||
|
||||
report = webnotes.doc("Report", report_name)
|
||||
|
||||
if filters and isinstance(filters, basestring):
|
||||
|
|
@ -58,9 +75,13 @@ def run(report_name, filters=None):
|
|||
result = [list(t) for t in webnotes.conn.sql(report.query, filters)]
|
||||
columns = [c[0] for c in webnotes.conn.get_description()]
|
||||
else:
|
||||
method_name = scrub(webnotes.conn.get_value("DocType", report.ref_doctype, "module")) \
|
||||
+ ".report." + scrub(report.name) + "." + scrub(report.name) + ".execute"
|
||||
columns, result = webnotes.get_method(method_name)(filters or {})
|
||||
module = webnotes.conn.get_value("DocType", report.ref_doctype, "module")
|
||||
if report.is_standard=="Yes":
|
||||
method_name = scrub(module) + ".report." + scrub(report.name) + "." + scrub(report.name) + ".execute"
|
||||
columns, result = webnotes.get_method(method_name)(filters or {})
|
||||
else:
|
||||
namespace = get_code_and_execute(module, "Report", report.name)
|
||||
columns, result = namespace["execute"](filters or {})
|
||||
|
||||
result = get_filtered_data(report.ref_doctype, columns, result)
|
||||
|
||||
|
|
@ -91,7 +112,7 @@ def get_filtered_data(ref_doctype, columns, data):
|
|||
|
||||
linked_doctypes = get_linked_doctypes(columns)
|
||||
match_filters = get_user_match_filters(linked_doctypes, ref_doctype)
|
||||
|
||||
|
||||
if match_filters:
|
||||
matched_columns = get_matched_columns(linked_doctypes, match_filters)
|
||||
for row in data:
|
||||
|
|
|
|||
5
wnf.py
5
wnf.py
|
|
@ -265,6 +265,7 @@ def update(remote=None, branch=None, site=None):
|
|||
def latest(site=None, verbose=True):
|
||||
import webnotes.modules.patch_handler
|
||||
import webnotes.model.sync
|
||||
import webnotes.plugins
|
||||
|
||||
webnotes.connect(site=site)
|
||||
|
||||
|
|
@ -277,6 +278,10 @@ def latest(site=None, verbose=True):
|
|||
|
||||
# sync
|
||||
webnotes.model.sync.sync_all()
|
||||
|
||||
# remove __init__.py from plugins
|
||||
webnotes.plugins.remove_init_files()
|
||||
|
||||
except webnotes.modules.patch_handler.PatchError, e:
|
||||
print "\n".join(webnotes.local.patch_log_list)
|
||||
raise e
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue