Refactored plugin architecture to allow custom script reports

This commit is contained in:
Anand Doshi 2013-11-06 14:19:09 +05:30
parent 89be73bd9b
commit 49460beed5
10 changed files with 186 additions and 85 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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