Просмотр исходного кода

introducing model 3.0, refactored import module

version-14
Rushabh Mehta 12 лет назад
Родитель
Сommit
4f72d3b686
27 измененных файлов: 833 добавлений и 542 удалений
  1. +1
    -1
      core/doctype/customize_form/customize_form.py
  2. +2
    -2
      core/doctype/doctype/doctype.py
  3. +2
    -2
      core/doctype/doctype_mapper/doctype_mapper.py
  4. +2
    -2
      core/doctype/page/page.py
  5. +12
    -1
      core/doctype/report/report.py
  6. +1
    -1
      core/doctype/report/report.txt
  7. +1
    -1
      core/doctype/search_criteria/search_criteria.py
  8. +3
    -3
      core/page/data_import_tool/data_import_tool.py
  9. +2
    -2
      webnotes/__init__.py
  10. +2
    -2
      webnotes/client.py
  11. +2
    -2
      webnotes/model/__init__.py
  12. +1
    -1
      webnotes/model/code.py
  13. +9
    -9
      webnotes/model/controller.py
  14. +9
    -13
      webnotes/model/doc.py
  15. +123
    -305
      webnotes/model/doclist.py
  16. +2
    -1
      webnotes/model/doctype.py
  17. +13
    -52
      webnotes/model/sync.py
  18. +344
    -0
      webnotes/model/wrapper.py
  19. +24
    -49
      webnotes/modules/__init__.py
  20. +74
    -76
      webnotes/modules/export_file.py
  21. +71
    -0
      webnotes/modules/import_file.py
  22. +10
    -10
      webnotes/modules/import_merge.py
  23. +116
    -0
      webnotes/modules/utils.py
  24. +2
    -2
      webnotes/utils/nestedset.py
  25. +2
    -2
      webnotes/widgets/form/run_method.py
  26. +2
    -2
      webnotes/widgets/form/save.py
  27. +1
    -1
      webnotes/widgets/query_builder.py

+ 1
- 1
core/doctype/customize_form/customize_form.py Просмотреть файл

@@ -139,7 +139,7 @@ class DocType:


def post(self): def post(self):
""" """
Save diff between Customize Form DocList and DocType DocList as property setter entries
Save diff between Customize Form ModelWrapper and DocType ModelWrapper as property setter entries
""" """
if self.doc.doc_type: if self.doc.doc_type:
from webnotes.model import doc from webnotes.model import doc


+ 2
- 2
core/doctype/doctype/doctype.py Просмотреть файл

@@ -86,7 +86,7 @@ class DocType:
self.change_modified_of_parent() self.change_modified_of_parent()
import conf import conf
from webnotes.utils.transfer import in_transfer
from webnotes.modules.import_merge import in_transfer


if (not in_transfer) and getattr(conf,'developer_mode', 0): if (not in_transfer) and getattr(conf,'developer_mode', 0):
self.export_doc() self.export_doc()
@@ -96,7 +96,7 @@ class DocType:


def export_doc(self): def export_doc(self):
from webnotes.modules.export_module import export_to_files
from webnotes.modules.export_file import export_to_files
export_to_files(record_list=[['DocType', self.doc.name]]) export_to_files(record_list=[['DocType', self.doc.name]])
def import_doc(self): def import_doc(self):


+ 2
- 2
core/doctype/doctype_mapper/doctype_mapper.py Просмотреть файл

@@ -27,7 +27,7 @@ import webnotes
from webnotes.utils import cint, cstr, default_fields, flt, formatdate, get_defaults, getdate, now, nowdate, replace_newlines, set_default from webnotes.utils import cint, cstr, default_fields, flt, formatdate, get_defaults, getdate, now, nowdate, replace_newlines, set_default
from webnotes.model import db_exists, default_fields from webnotes.model import db_exists, default_fields
from webnotes.model.doc import Document, addchild, getchildren, make_autoname from webnotes.model.doc import Document, addchild, getchildren, make_autoname
from webnotes.model.doclist import getlist
from webnotes.model.wrapper import getlist
from webnotes.model.code import get_obj from webnotes.model.code import get_obj
from webnotes import session, form, msgprint, errprint from webnotes import session, form, msgprint, errprint
from webnotes.model.doctype import get from webnotes.model.doctype import get
@@ -308,5 +308,5 @@ class DocType:
""" """
import conf import conf
if hasattr(conf, 'developer_mode') and conf.developer_mode: if hasattr(conf, 'developer_mode') and conf.developer_mode:
from webnotes.modules.export_module import export_to_files
from webnotes.modules.export_file import export_to_files
export_to_files(record_list=[[self.doc.doctype, self.doc.name]]) export_to_files(record_list=[[self.doc.doctype, self.doc.name]])

+ 2
- 2
core/doctype/page/page.py Просмотреть файл

@@ -67,9 +67,9 @@ class DocType:
it will write out a .html file it will write out a .html file
""" """
import conf import conf
from webnotes.utils.transfer import in_transfer
from webnotes.modules.import_merge import in_transfer
if not in_transfer and getattr(conf,'developer_mode', 0) and self.doc.standard=='Yes': if not in_transfer and getattr(conf,'developer_mode', 0) and self.doc.standard=='Yes':
from webnotes.modules.export_module import export_to_files
from webnotes.modules.export_file import export_to_files
from webnotes.modules import get_module_path, scrub from webnotes.modules import get_module_path, scrub
import os import os
export_to_files(record_list=[['Page', self.doc.name]]) export_to_files(record_list=[['Page', self.doc.name]])


+ 12
- 1
core/doctype/report/report.py Просмотреть файл

@@ -12,4 +12,15 @@ class DocType:
"""only administrator can save standard report""" """only administrator can save standard report"""
if self.doc.is_standard == "Yes" and webnotes.session.user!="Administrator": if self.doc.is_standard == "Yes" and webnotes.session.user!="Administrator":
webnotes.msgprint("""Only Administrator can save a standard report. webnotes.msgprint("""Only Administrator can save a standard report.
Please rename and save.""", raise_exception=True)
Please rename and save.""", raise_exception=True)

def on_update(self):
self.export_doc()
def export_doc(self):
# export
import conf
if self.doc.is_standard == 'Yes' and getattr(conf, 'developer_mode', 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"))

+ 1
- 1
core/doctype/report/report.txt Просмотреть файл

@@ -5,7 +5,7 @@
{ {
u'creation': '2012-10-04 18:45:27', u'creation': '2012-10-04 18:45:27',
u'docstatus': 0, u'docstatus': 0,
u'modified': '2012-11-12 12:53:08',
u'modified': '2012-11-12 12:53:09',
u'modified_by': u'Administrator', u'modified_by': u'Administrator',
u'owner': u'Administrator' u'owner': u'Administrator'
}, },


+ 1
- 1
core/doctype/search_criteria/search_criteria.py Просмотреть файл

@@ -61,7 +61,7 @@ class DocType:
# export # export
import conf import conf
if self.doc.standard == 'Yes' and getattr(conf, 'developer_mode', 0) == 1: if self.doc.standard == 'Yes' and getattr(conf, 'developer_mode', 0) == 1:
from webnotes.modules.export_module import export_to_files
from webnotes.modules.export_file import export_to_files
export_to_files(record_list=[['Search Criteria', self.doc.name]]) export_to_files(record_list=[['Search Criteria', self.doc.name]])


# patch to rename search criteria from old style numerical to # patch to rename search criteria from old style numerical to


+ 3
- 3
core/page/data_import_tool/data_import_tool.py Просмотреть файл

@@ -282,20 +282,20 @@ def delete_child_rows(rows, doctype):
def import_doc(d, doctype, overwrite, row_idx): def import_doc(d, doctype, overwrite, row_idx):
"""import main (non child) document""" """import main (non child) document"""
from webnotes.model.doclist import DocList
from webnotes.model.wrapper import ModelWrapper


if webnotes.conn.exists(doctype, d['name']): if webnotes.conn.exists(doctype, d['name']):
if overwrite: if overwrite:
doclist = webnotes.model.doc.get(doctype, d['name']) doclist = webnotes.model.doc.get(doctype, d['name'])
doclist[0].fields.update(d) doclist[0].fields.update(d)
DocList(doclist).save()
ModelWrapper(doclist).save()
return 'Updated row (#%d) %s' % (row_idx, getlink(doctype, d['name'])) return 'Updated row (#%d) %s' % (row_idx, getlink(doctype, d['name']))
else: else:
return 'Ignored row (#%d) %s (exists)' % (row_idx, return 'Ignored row (#%d) %s (exists)' % (row_idx,
getlink(doctype, d['name'])) getlink(doctype, d['name']))
else: else:
d['__islocal'] = 1 d['__islocal'] = 1
dl = DocList([webnotes.model.doc.Document(fielddata = d)])
dl = ModelWrapper([webnotes.model.doc.Document(fielddata = d)])
dl.save() dl.save()
return 'Inserted row (#%d) %s' % (row_idx, getlink(doctype, return 'Inserted row (#%d) %s' % (row_idx, getlink(doctype,
dl.doc.fields['name'])) dl.doc.fields['name']))


+ 2
- 2
webnotes/__init__.py Просмотреть файл

@@ -262,5 +262,5 @@ def generate_hash():
return hashlib.sha224(str(time.time())).hexdigest() return hashlib.sha224(str(time.time())).hexdigest()


def model_wrapper(doctype, name=None): def model_wrapper(doctype, name=None):
from webnotes.model.doclist import DocList
return DocList(doctype, name)
from webnotes.model.wrapper import ModelWrapper
return ModelWrapper(doctype, name)

+ 2
- 2
webnotes/client.py Просмотреть файл

@@ -29,12 +29,12 @@ def save():
"""insert or update from form query""" """insert or update from form query"""
doclist = json.loads(webnotes.form_dict.doclist) doclist = json.loads(webnotes.form_dict.doclist)
from webnotes.model.doclist import DocList
from webnotes.model.wrapper import ModelWrapper
if not webnotes.has_permission(doclist[0]["doctype"], "write"): if not webnotes.has_permission(doclist[0]["doctype"], "write"):
webnotes.msgprint("No Write Permission", raise_exception=True) webnotes.msgprint("No Write Permission", raise_exception=True)


doclistobj = DocList(doclist)
doclistobj = ModelWrapper(doclist)
doclistobj.save() doclistobj.save()
return [d.fields for d in doclist] return [d.fields for d in doclist]

+ 2
- 2
webnotes/model/__init__.py Просмотреть файл

@@ -30,10 +30,10 @@ default_fields = ['doctype','name','owner','creation','modified','modified_by','
#================================================================================= #=================================================================================


def insert(doclist): def insert(doclist):
from webnotes.model.doclist import DocList
from webnotes.model.wrapper import ModelWrapper
for d in doclist: for d in doclist:
d["__islocal"] = 1 d["__islocal"] = 1
dl = DocList(doclist)
dl = ModelWrapper(doclist)
dl.save() dl.save()
return dl return dl


+ 1
- 1
webnotes/model/code.py Просмотреть файл

@@ -32,7 +32,7 @@ methods in following modules are imported for backward compatibility
* webnotes.* * webnotes.*
* webnotes.utils.* * webnotes.utils.*
* webnotes.model.doc.* * webnotes.model.doc.*
* webnotes.model.doclist.*
* webnotes.model.wrapper.*
""" """
custom_class = ''' custom_class = '''
# Please edit this list and import only required elements # Please edit this list and import only required elements


+ 9
- 9
webnotes/model/controller.py Просмотреть файл

@@ -22,7 +22,7 @@


from __future__ import unicode_literals from __future__ import unicode_literals
""" """
Transactions are defined as collection of classes, a DocList represents collection of Document
Transactions are defined as collection of classes, a ModelWrapper represents collection of Document
objects for a transaction with main and children. objects for a transaction with main and children.


Group actions like save, etc are performed on doclists Group actions like save, etc are performed on doclists
@@ -32,7 +32,7 @@ import webnotes
import json, os import json, os
import webnotes.model import webnotes.model
import webnotes.model.doc import webnotes.model.doc
import webnotes.model.doclist
import webnotes.model.wrapper
import webnotes.utils.cache import webnotes.utils.cache


from webnotes import _ from webnotes import _
@@ -79,12 +79,12 @@ def get_controller_class(module_name, doctype, module_path):
for attr in dir(module): for attr in dir(module):
attrobj = getattr(module, attr) attrobj = getattr(module, attr)
if inspect.isclass(attrobj) and attr.startswith(doctype.replace(' ', '').replace('-', '')) \ if inspect.isclass(attrobj) and attr.startswith(doctype.replace(' ', '').replace('-', '')) \
and issubclass(attrobj, DocListController):
and issubclass(attrobj, ModelWrapperController):
return attrobj return attrobj
return DocListController
return ModelWrapperController
class DocListController(object):
class ModelWrapperController(object):
""" """
Collection of Documents with one parent and multiple children Collection of Documents with one parent and multiple children
""" """
@@ -104,11 +104,11 @@ class DocListController(object):
self.set_doclist(self.load_doclist(doctype, name)) self.set_doclist(self.load_doclist(doctype, name))
def load_doclist(self, doctype, name): def load_doclist(self, doctype, name):
return webnotes.model.doclist.load(doctype, name)
return webnotes.model.wrapper.load(doctype, name)
def set_doclist(self, doclist): def set_doclist(self, doclist):
if not isinstance(doclist, webnotes.model.doclist.DocList):
self.doclist = webnotes.model.doclist.objectify(doclist)
if not isinstance(doclist, webnotes.model.wrapper.ModelWrapper):
self.doclist = webnotes.model.wrapper.objectify(doclist)
else: else:
self.doclist = doclist self.doclist = doclist
self.doc = self.doclist[0] self.doc = self.doclist[0]
@@ -125,7 +125,7 @@ class DocListController(object):
from webnotes.model.doctype import get_property from webnotes.model.doctype import get_property
if get_property(self.doc.doctype, "document_type") in ["Master", "Transaction"]\ if get_property(self.doc.doctype, "document_type") in ["Master", "Transaction"]\
and not self.doc.get("__islocal"): and not self.doc.get("__islocal"):
from webnotes.model.doclist import load
from webnotes.model.wrapper import load
# get the old doclist # get the old doclist
try: try:
oldlist = load(self.doc.doctype, self.doc.name) oldlist = load(self.doc.doctype, self.doc.name)


+ 9
- 13
webnotes/model/doc.py Просмотреть файл

@@ -632,6 +632,7 @@ def getseries(key, digits, doctype=''):


def getchildren(name, childtype, field='', parenttype='', from_doctype=0, prefix='tab'): def getchildren(name, childtype, field='', parenttype='', from_doctype=0, prefix='tab'):
import webnotes import webnotes
from webnotes.model.doclist import DocList
tmp = '' tmp = ''
@@ -640,17 +641,11 @@ def getchildren(name, childtype, field='', parenttype='', from_doctype=0, prefix
if parenttype: if parenttype:
tmp = ' and parenttype="%s" ' % parenttype tmp = ' and parenttype="%s" ' % parenttype


try:
dataset = webnotes.conn.sql("select * from `%s%s` where parent='%s' %s order by idx" \
% (prefix, childtype, name, tmp))
desc = webnotes.conn.get_description()
except Exception, e:
if prefix=='arc' and e.args[0]==1146:
return []
else:
raise e
dataset = webnotes.conn.sql("select * from `%s%s` where parent='%s' %s order by idx" \
% (prefix, childtype, name, tmp))
desc = webnotes.conn.get_description()


l = []
l = DocList()
for i in dataset: for i in dataset:
d = Document() d = Document()
@@ -692,7 +687,8 @@ def get(dt, dn='', with_children = 1, from_get_obj = 0, prefix = 'tab'):
""" """
import webnotes import webnotes
import webnotes.model import webnotes.model

from webnotes.model.doclist import DocList
dn = dn or dt dn = dn or dt


# load the main doc # load the main doc
@@ -709,13 +705,13 @@ def get(dt, dn='', with_children = 1, from_get_obj = 0, prefix = 'tab'):


if not with_children: if not with_children:
# done # done
return [doc,]
return DocList([doc,])
# get all children types # get all children types
tablefields = webnotes.model.meta.get_table_fields(dt) tablefields = webnotes.model.meta.get_table_fields(dt)


# load chilren # load chilren
doclist = [doc,]
doclist = DocList([doc,])
for t in tablefields: for t in tablefields:
doclist += getchildren(doc.name, t[0], t[1], dt, prefix=prefix) doclist += getchildren(doc.name, t[0], t[1], dt, prefix=prefix)




+ 123
- 305
webnotes/model/doclist.py Просмотреть файл

@@ -20,324 +20,142 @@
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #


from __future__ import unicode_literals
"""
Transactions are defined as collection of classes, a DocList represents collection of Document
objects for a transaction with main and children.

Group actions like save, etc are performed on doclists
"""

import webnotes import webnotes
from webnotes.utils import cint
import webnotes.model
from webnotes.model.doc import Document from webnotes.model.doc import Document


class DocList:
"""
Collection of Documents with one parent and multiple children
"""
def __init__(self, dt=None, dn=None):
self.docs = []
self.obj = None
self.to_docstatus = 0
if dt and dn:
self.load_from_db(dt, dn)
if type(dt) is list:
self.set_doclist(dt)

def load_from_db(self, dt=None, dn=None, prefix='tab'):
"""
Load doclist from dt
"""
from webnotes.model.doc import Document, getchildren
if not dt: dt = self.doc.doctype
if not dn: dn = self.doc.name

doc = Document(dt, dn, prefix=prefix)

# get all children types
tablefields = webnotes.model.meta.get_table_fields(dt)

# load chilren
doclist = [doc,]
for t in tablefields:
doclist += getchildren(doc.name, t[0], t[1], dt, prefix=prefix)

self.set_doclist(doclist)

def __iter__(self):
"""
Make this iterable
"""
return self.docs.__iter__()

def from_compressed(self, data, docname):
"""
Expand called from client
"""
from webnotes.model.utils import expand
self.docs = expand(data)
self.set_doclist(self.docs)
class DocList(list):
"""DocList object as a wrapper around a list"""
def get(self, filters, limit=0):
"""pass filters as:
{"key": "val", "key": ["!=", "val"],
"key": ["in", "val"], "key": ["not in", "val"], "key": "^val"}"""
# map reverse operations to set add = False
import operator
ops_map = {
"!=": lambda (a, b): operator.ne(a, b),
"in": lambda (a, b): operator.contains(b, a),
"not in": lambda (a, b): not operator.contains(b, a)
}
out = []
def set_doclist(self, docs):
for i, d in enumerate(docs):
if isinstance(d, dict):
docs[i] = Document(fielddata=d)
for doc in self:
d = isinstance(getattr(doc, "fields", None), dict) and doc.fields or doc
add = True
for f in filters:
fval = filters[f]
if isinstance(fval, list):
if fval[0] in ops_map and not ops_map[fval[0]]((d.get(f), fval[1])):
add = False
break
elif isinstance(fval, basestring) and fval.startswith("^"):
if not (d.get(f) or "").startswith(fval[1:]):
add = False
break
elif d.get(f)!=fval:
add = False
break

if add:
out.append(doc)
if limit and (len(out)-1)==limit:
break
self.docs = self.doclist = docs
self.doc, self.children = docs[0], docs[1:]
if self.obj:
self.obj.doclist = self.doclist
self.obj.doc = self.doc

def make_obj(self):
"""
Create a DocType object
"""
if self.obj: return self.obj

from webnotes.model.code import get_obj
self.obj = get_obj(doc=self.doc, doclist=self.children)
return self.obj

def to_dict(self):
"""
return as a list of dictionaries
"""
return [d.fields for d in self.docs]

def check_if_latest(self):
"""
Raises exception if the modified time is not the same as in the database
"""
from webnotes.model.meta import is_single

if (not is_single(self.doc.doctype)) and (not cint(self.doc.fields.get('__islocal'))):
tmp = webnotes.conn.sql("""
SELECT modified FROM `tab%s` WHERE name="%s" for update"""
% (self.doc.doctype, self.doc.name))

if tmp and str(tmp[0][0]) != str(self.doc.modified):
webnotes.msgprint("""
Document has been modified after you have opened it.
To maintain the integrity of the data, you will not be able to save your changes.
Please refresh this document. [%s/%s]""" % (tmp[0][0], self.doc.modified), raise_exception=1)

def check_permission(self):
"""
Raises exception if permission is not valid
"""
if not self.doc.check_perm(verbose=1):
webnotes.msgprint("Not enough permission to save %s" % self.doc.doctype, raise_exception=1)

def check_links(self):
"""
Checks integrity of links (throws exception if links are invalid)
"""
ref, err_list = {}, []
for d in self.docs:
if not ref.get(d.doctype):
ref[d.doctype] = d.make_link_list()

err_list += d.validate_links(ref[d.doctype])

if err_list:
webnotes.msgprint("""[Link Validation] Could not find the following values: %s.
Please correct and resave. Document Not Saved.""" % ', '.join(err_list), raise_exception=1)

def update_timestamps_and_docstatus(self):
"""
Update owner, creation, modified_by, modified, docstatus
"""
from webnotes.utils import now
ts = now()
user = webnotes.__dict__.get('session', {}).get('user') or 'Administrator'

for d in self.docs:
if self.doc.fields.get('__islocal'):
d.owner = user
d.creation = ts

d.modified_by = user
d.modified = ts
if d.docstatus != 2: # don't update deleted
d.docstatus = self.to_docstatus

def prepare_for_save(self, check_links):
"""
Set owner, modified etc before saving
"""
self.check_if_latest()
self.check_permission()
if check_links:
self.check_links()
self.update_timestamps_and_docstatus()
return DocList(out)


def run_method(self, method):
"""
Run a method and custom_method
"""
self.make_obj()
if hasattr(self.obj, method):
getattr(self.obj, method)()
if hasattr(self.obj, 'custom_' + method):
getattr(self.obj, 'custom_' + method)()
def getone(self, filters):
return self.get(filters, limit=1)[0]


trigger(method, self.obj.doc)
def extend(self, n):
list.extend(self, n)
return self
self.set_doclist([self.obj.doc] + self.obj.doclist)

def save_main(self):
"""
Save the main doc
"""
try:
self.doc.save(cint(self.doc.fields.get('__islocal')))
except NameError, e:
webnotes.msgprint('%s "%s" already exists' % (self.doc.doctype, self.doc.name))

# prompt if cancelled
if webnotes.conn.get_value(self.doc.doctype, self.doc.name, 'docstatus')==2:
webnotes.msgprint('[%s "%s" has been cancelled]' % (self.doc.doctype, self.doc.name))
webnotes.errprint(webnotes.utils.getTraceback())
raise e

def save_children(self):
"""
Save Children, with the new parent name
"""
child_map = {}
for d in self.children:
if (d.fields.has_key('parent') and d.fields.get('parent')) or \
(d.fields.has_key("parentfield") and d.fields.get("parentfield")):
# if d.parent:
d.parent = self.doc.name # rename if reqd
d.parenttype = self.doc.doctype
def copy(self):
out = []
for d in self:
out.append(Document(d))
return DocList(out)
def filter_valid_fields(self):
import webnotes.model
fieldnames = {}
for d in self:
remove = []
for f in d:
if f not in fieldnames.setdefault(d.doctype,
webnotes.model.get_fieldnames(d.doctype)):
remove.append(f)
for f in remove:
del d[f]
d.save(new = cint(d.fields.get('__islocal')))
def append(self, doc):
if not isinstance(doc, Document):
doc = Document(doc)
child_map.setdefault(d.doctype, []).append(d.name)
self._prepare_doc(doc)

super(DocList, self).append(doc)
# delete all children in database that are not in the child_map
def extend(self, doclist):
doclist = objectify(doclist)
for doc in doclist:
self._prepare_doc(doc)
# get all children types
tablefields = webnotes.model.meta.get_table_fields(self.doc.doctype)
for dt in tablefields:
cnames = child_map.get(dt[0]) or []
if cnames:
webnotes.conn.sql("""delete from `tab%s` where parent=%s and parenttype=%s and
name not in (%s)""" % (dt[0], '%s', '%s', ','.join(['%s'] * len(cnames))),
tuple([self.doc.name, self.doc.doctype] + cnames))
else:
webnotes.conn.sql("""delete from `tab%s` where parent=%s and parenttype=%s""" \
% (dt[0], '%s', '%s'), (self.doc.name, self.doc.doctype))

def save(self, check_links=1):
"""
Save the list
"""
self.prepare_for_save(check_links)
self.run_method('validate')
self.save_main()
self.save_children()
self.run_method('on_update')

def submit(self):
"""
Save & Submit - set docstatus = 1, run "on_submit"
"""
if self.doc.docstatus != 0:
webnotes.msgprint("Only draft can be submitted", raise_exception=1)
self.to_docstatus = 1
self.save()
self.run_method('on_submit')

def cancel(self):
"""
Cancel - set docstatus 2, run "on_cancel"
"""
if self.doc.docstatus != 1:
webnotes.msgprint("Only submitted can be cancelled", raise_exception=1)
self.to_docstatus = 2
self.prepare_for_save(1)
self.save_main()
self.save_children()
self.run_method('on_cancel')

def update_after_submit(self):
"""
Update after submit - some values changed after submit
"""
if self.doc.docstatus != 1:
webnotes.msgprint("Only to called after submit", raise_exception=1)
self.to_docstatus = 1
self.prepare_for_save(1)
self.save_main()
self.save_children()
self.run_method('on_update_after_submit')

# clone

def clone(source_doclist):
""" Copy previous invoice and change dates"""
from webnotes.model.doc import Document
new_doclist = []
new_parent = Document(fielddata = source_doclist.doc.fields.copy())
new_parent.name = 'Temp/001'
new_parent.fields['__islocal'] = 1
new_parent.fields['docstatus'] = 0

if new_parent.fields.has_key('amended_from'):
new_parent.fields['amended_from'] = None
new_parent.fields['amendment_date'] = None

new_parent.save(1)

new_doclist.append(new_parent)

for d in source_doclist.doclist[1:]:
newd = Document(fielddata = d.fields.copy())
newd.name = None
newd.fields['__islocal'] = 1
newd.fields['docstatus'] = 0
newd.parent = new_parent.name
new_doclist.append(newd)
super(DocList, self).extend(doclist)
def _prepare_doc(self, doc):
if not doc.name:
doc.fields["__islocal"] = 1
if doc.parentfield:
if not doc.parenttype:
doc.parenttype = self[0].doctype
if not doc.parent:
doc.parent = self[0].name

def load(doctype, name):
# load main doc
return objectify(load_doclist(doctype, name))

def load_doclist(doctype, name):
doclist = DocList([load_main(doctype, name)])
doclistobj = DocList()
doclistobj.docs = new_doclist
doclistobj.doc = new_doclist[0]
doclistobj.doclist = new_doclist
doclistobj.children = new_doclist[1:]
doclistobj.save()
return doclistobj
# load children
table_fields = map(lambda f: (f.options, name, f.fieldname, doctype),
webnotes.conn.get_table_fields(doctype))



def trigger(method, doc):
"""trigger doctype events"""
try:
import startup.event_handlers
except ImportError:
return
for args in table_fields:
children = load_children(*args)
if children: doclist += children
if hasattr(startup.event_handlers, method):
getattr(startup.event_handlers, method)(doc)
return doclist

def load_main(doctype, name):
"""retrieves doc from database"""
if webnotes.conn.is_single(doctype):
doc = webnotes.conn.sql("""select field, value from `tabSingles`
where doctype=%s""", doctype, as_list=1)
doc = dict(doc)
doc["name"] = doctype
else:
doc = webnotes.conn.sql("""select * from `tab%s` where name = %s""" % \
(doctype, "%s"), name, as_dict=1)
if not doc:
raise NameError, """%s: "%s" does not exist""" % (doctype, name)
doc = doc[0]

doc["doctype"] = doctype
return doc

def load_children(options, parent, parentfield, parenttype):
"""load children based on options, parentfield, parenttype and parent"""
options = options.split("\n")[0].strip()
if hasattr(startup.event_handlers, 'doclist_all'):
startup.event_handlers.doclist_all(doc, method)

# for bc
def getlist(doclist, parentfield):
"""
Return child records of a particular type
"""
import webnotes.model.utils
return webnotes.model.utils.getlist(doclist, parentfield)

def copy_doclist(doclist, no_copy = []):
"""
Make a copy of the doclist
"""
import webnotes.model.utils
return webnotes.model.utils.copy_doclist(doclist, no_copy)

return webnotes.conn.sql("""select *, "%s" as doctype from `tab%s` where parent = %s
and parentfield = %s and parenttype = %s order by idx""" % (options, options, "%s", "%s", "%s"),
(parent, parentfield, parenttype), as_dict=1)
def objectify(doclist):
from webnotes.model.doc import Document
return map(lambda d: isinstance(d, Document) and d or Document(d), doclist)

+ 2
- 1
webnotes/model/doctype.py Просмотреть файл

@@ -77,7 +77,8 @@ class _DocType:
""" """
Gets a list of custom field docs masked as type DocField Gets a list of custom field docs masked as type DocField
""" """
custom_doclist = []
from webnotes.model.doclist import DocList
custom_doclist = DocList()
res = webnotes.conn.sql("""SELECT * FROM `tabCustom Field` res = webnotes.conn.sql("""SELECT * FROM `tabCustom Field`
WHERE dt = %s AND docstatus < 2""", doc_type, as_dict=1) WHERE dt = %s AND docstatus < 2""", doc_type, as_dict=1)
for r in res: for r in res:


+ 13
- 52
webnotes/model/sync.py Просмотреть файл

@@ -21,10 +21,10 @@ def sync_all(force=0):


def sync_core_doctypes(force=0): def sync_core_doctypes(force=0):
# doctypes # doctypes
return walk_and_sync(os.path.join(os.path.dirname(conf.__file__), 'lib'), force)
return walk_and_sync(os.path.join(os.path.dirname(os.path.abspath(conf.__file__)), 'lib'), force)


def sync_modules(force=0): def sync_modules(force=0):
return walk_and_sync(os.path.join(os.path.dirname(conf.__file__), 'app'), force)
return walk_and_sync(os.path.join(os.path.dirname(os.path.abspath(conf.__file__)), 'app'), force)


def walk_and_sync(start_path, force=0): def walk_and_sync(start_path, force=0):
"""walk and sync all doctypes and pages""" """walk and sync all doctypes and pages"""
@@ -32,8 +32,9 @@ def walk_and_sync(start_path, force=0):


modules = [] modules = []


document_type = ['page', 'workflow', 'module_def', 'workflow_state', 'workflow_action']
for path, folders, files in os.walk(start_path): for path, folders, files in os.walk(start_path):
if os.path.basename(os.path.dirname(path)) in ('doctype', 'page'):
if os.path.basename(os.path.dirname(path)) in (['doctype'] + document_type):
for f in files: for f in files:
if f.endswith(".txt"): if f.endswith(".txt"):
# great grand-parent folder is module_name # great grand-parent folder is module_name
@@ -49,7 +50,7 @@ def walk_and_sync(start_path, force=0):
if doctype == 'doctype': if doctype == 'doctype':
sync(module_name, name, force) sync(module_name, name, force)
elif doctype in ['page']:#, 'search_criteria', 'Print Format', 'DocType Mapper']:
elif doctype in document_type:
if reload_doc(module_name, doctype, name): if reload_doc(module_name, doctype, name):
print module_name + ' | ' + doctype + ' | ' + name print module_name + ' | ' + doctype + ' | ' + name
@@ -60,14 +61,18 @@ def walk_and_sync(start_path, force=0):
def sync(module_name, docname, force=0): def sync(module_name, docname, force=0):
"""sync doctype from file if modified""" """sync doctype from file if modified"""
with open(get_file_path(module_name, docname), 'r') as f: with open(get_file_path(module_name, docname), 'r') as f:
from webnotes.model.utils import peval_doclist
from webnotes.modules.utils import peval_doclist
doclist = peval_doclist(f.read()) doclist = peval_doclist(f.read())
modified = doclist[0]['modified'] modified = doclist[0]['modified']
if not doclist: if not doclist:
raise Exception('DocList could not be evaluated')
if modified == str(webnotes.conn.get_value(doclist[0].get('doctype'),
doclist[0].get('name'), 'modified')) and not force:
raise Exception('ModelWrapper could not be evaluated')

db_modified = str(webnotes.conn.get_value(doclist[0].get('doctype'),
doclist[0].get('name'), 'modified'))
if modified == db_modified and not force:
return return

webnotes.conn.begin() webnotes.conn.begin()
delete_doctype_docfields(doclist) delete_doctype_docfields(doclist)
@@ -162,47 +167,3 @@ def create_doc(data):
d.fields.update(data) d.fields.update(data)
d.save() d.save()
print 'Created %(doctype)s %(name)s' % d.fields print 'Created %(doctype)s %(name)s' % d.fields

import unittest
class TestSync(unittest.TestCase):
def setUp(self):
self.test_case = {
'module_name': 'selling',
'docname': 'sales_order'
}
webnotes.conn.begin()

def tearDown(self):
pass
#from webnotes.utils import cstr
#webnotes.conn.rollback()

def test_sync(self):
#old_doctype, old_docfields = self.query('Profile')
#self.parent = sync(self.test_case.get('module_name'), self.test_case.get('docname'))
#new_doctype, new_docfields = self.query(self.parent)
#self.assertNotEqual(old_doctype, new_doctype)
#self.assertNotEqual(old_docfields, new_docfields)

#from webnotes.utils import cstr
#print new_doctype[0]
#print
#print "\n".join((cstr(d) for d in new_docfields))
#print "\n\n"
pass

def test_sync_all(self):
sync_all()

def query(self, parent):
doctype = webnotes.conn.sql("SELECT name FROM `tabDocType` \
WHERE name=%s", parent)
docfields = webnotes.conn.sql("SELECT idx, fieldname, label, fieldtype FROM `tabDocField` \
WHERE parent=%s order by idx", parent)
#doctype = webnotes.conn.sql("SELECT * FROM `tabDocType` \
# WHERE name=%s", parent)
#docfields = webnotes.conn.sql("SELECT * FROM `tabDocField` \
# WHERE parent=%s order by idx", parent)
return doctype, docfields



+ 344
- 0
webnotes/model/wrapper.py Просмотреть файл

@@ -0,0 +1,344 @@
# Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
#
# MIT License (MIT)
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#

from __future__ import unicode_literals
"""
Transactions are defined as collection of classes, a ModelWrapper represents collection of Document
objects for a transaction with main and children.

Group actions like save, etc are performed on doclists
"""

import webnotes
from webnotes.utils import cint
from webnotes.model.doc import Document

class ModelWrapper:
"""
Collection of Documents with one parent and multiple children
"""
def __init__(self, dt=None, dn=None):
self.docs = []
self.obj = None
self.to_docstatus = 0
if dt and dn:
self.load_from_db(dt, dn)
if type(dt) is list:
self.set_doclist(dt)

def load_from_db(self, dt=None, dn=None, prefix='tab'):
"""
Load doclist from dt
"""
from webnotes.model.doc import Document, getchildren
from webnotes.model.doclist import DocList
if not dt: dt = self.doc.doctype
if not dn: dn = self.doc.name

doc = Document(dt, dn, prefix=prefix)

# get all children types
tablefields = webnotes.model.meta.get_table_fields(dt)

# load chilren
doclist = DocList([doc,])
for t in tablefields:
doclist += getchildren(doc.name, t[0], t[1], dt, prefix=prefix)

self.set_doclist(doclist)

def __iter__(self):
"""
Make this iterable
"""
return self.docs.__iter__()

def from_compressed(self, data, docname):
"""
Expand called from client
"""
from webnotes.model.utils import expand
self.docs = expand(data)
self.set_doclist(self.docs)
def set_doclist(self, docs):
for i, d in enumerate(docs):
if isinstance(d, dict):
docs[i] = Document(fielddata=d)
self.docs = self.doclist = docs
self.doc, self.children = docs[0], docs[1:]
if self.obj:
self.obj.doclist = self.doclist
self.obj.doc = self.doc

def make_obj(self):
"""
Create a DocType object
"""
if self.obj: return self.obj

from webnotes.model.code import get_obj
self.obj = get_obj(doc=self.doc, doclist=self.children)
return self.obj

def to_dict(self):
"""
return as a list of dictionaries
"""
return [d.fields for d in self.docs]

def check_if_latest(self):
"""
Raises exception if the modified time is not the same as in the database
"""
from webnotes.model.meta import is_single

if (not is_single(self.doc.doctype)) and (not cint(self.doc.fields.get('__islocal'))):
tmp = webnotes.conn.sql("""
SELECT modified FROM `tab%s` WHERE name="%s" for update"""
% (self.doc.doctype, self.doc.name))

if tmp and str(tmp[0][0]) != str(self.doc.modified):
webnotes.msgprint("""
Document has been modified after you have opened it.
To maintain the integrity of the data, you will not be able to save your changes.
Please refresh this document. [%s/%s]""" % (tmp[0][0], self.doc.modified), raise_exception=1)

def check_permission(self):
"""
Raises exception if permission is not valid
"""
if not self.doc.check_perm(verbose=1):
webnotes.msgprint("Not enough permission to save %s" % self.doc.doctype, raise_exception=1)

def check_links(self):
"""
Checks integrity of links (throws exception if links are invalid)
"""
ref, err_list = {}, []
for d in self.docs:
if not ref.get(d.doctype):
ref[d.doctype] = d.make_link_list()

err_list += d.validate_links(ref[d.doctype])

if err_list:
webnotes.msgprint("""[Link Validation] Could not find the following values: %s.
Please correct and resave. Document Not Saved.""" % ', '.join(err_list), raise_exception=1)

def update_timestamps_and_docstatus(self):
"""
Update owner, creation, modified_by, modified, docstatus
"""
from webnotes.utils import now
ts = now()
user = webnotes.__dict__.get('session', {}).get('user') or 'Administrator'

for d in self.docs:
if self.doc.fields.get('__islocal'):
d.owner = user
d.creation = ts

d.modified_by = user
d.modified = ts
if d.docstatus != 2: # don't update deleted
d.docstatus = self.to_docstatus

def prepare_for_save(self, check_links):
"""
Set owner, modified etc before saving
"""
self.check_if_latest()
self.check_permission()
if check_links:
self.check_links()
self.update_timestamps_and_docstatus()

def run_method(self, method):
"""
Run a method and custom_method
"""
self.make_obj()
if hasattr(self.obj, method):
getattr(self.obj, method)()
if hasattr(self.obj, 'custom_' + method):
getattr(self.obj, 'custom_' + method)()

trigger(method, self.obj.doc)
self.set_doclist([self.obj.doc] + self.obj.doclist)

def save_main(self):
"""
Save the main doc
"""
try:
self.doc.save(cint(self.doc.fields.get('__islocal')))
except NameError, e:
webnotes.msgprint('%s "%s" already exists' % (self.doc.doctype, self.doc.name))

# prompt if cancelled
if webnotes.conn.get_value(self.doc.doctype, self.doc.name, 'docstatus')==2:
webnotes.msgprint('[%s "%s" has been cancelled]' % (self.doc.doctype, self.doc.name))
webnotes.errprint(webnotes.utils.getTraceback())
raise e

def save_children(self):
"""
Save Children, with the new parent name
"""
child_map = {}
for d in self.children:
if (d.fields.has_key('parent') and d.fields.get('parent')) or \
(d.fields.has_key("parentfield") and d.fields.get("parentfield")):
# if d.parent:
d.parent = self.doc.name # rename if reqd
d.parenttype = self.doc.doctype
d.save(new = cint(d.fields.get('__islocal')))
child_map.setdefault(d.doctype, []).append(d.name)
# delete all children in database that are not in the child_map
# get all children types
tablefields = webnotes.model.meta.get_table_fields(self.doc.doctype)
for dt in tablefields:
cnames = child_map.get(dt[0]) or []
if cnames:
webnotes.conn.sql("""delete from `tab%s` where parent=%s and parenttype=%s and
name not in (%s)""" % (dt[0], '%s', '%s', ','.join(['%s'] * len(cnames))),
tuple([self.doc.name, self.doc.doctype] + cnames))
else:
webnotes.conn.sql("""delete from `tab%s` where parent=%s and parenttype=%s""" \
% (dt[0], '%s', '%s'), (self.doc.name, self.doc.doctype))

def save(self, check_links=1):
"""
Save the list
"""
self.prepare_for_save(check_links)
self.run_method('validate')
self.save_main()
self.save_children()
self.run_method('on_update')

def submit(self):
"""
Save & Submit - set docstatus = 1, run "on_submit"
"""
if self.doc.docstatus != 0:
webnotes.msgprint("Only draft can be submitted", raise_exception=1)
self.to_docstatus = 1
self.save()
self.run_method('on_submit')

def cancel(self):
"""
Cancel - set docstatus 2, run "on_cancel"
"""
if self.doc.docstatus != 1:
webnotes.msgprint("Only submitted can be cancelled", raise_exception=1)
self.to_docstatus = 2
self.prepare_for_save(1)
self.save_main()
self.save_children()
self.run_method('on_cancel')

def update_after_submit(self):
"""
Update after submit - some values changed after submit
"""
if self.doc.docstatus != 1:
webnotes.msgprint("Only to called after submit", raise_exception=1)
self.to_docstatus = 1
self.prepare_for_save(1)
self.save_main()
self.save_children()
self.run_method('on_update_after_submit')

# clone

def clone(source_doclist):
""" Copy previous invoice and change dates"""
from webnotes.model.doc import Document
new_doclist = []
new_parent = Document(fielddata = source_doclist.doc.fields.copy())
new_parent.name = 'Temp/001'
new_parent.fields['__islocal'] = 1
new_parent.fields['docstatus'] = 0

if new_parent.fields.has_key('amended_from'):
new_parent.fields['amended_from'] = None
new_parent.fields['amendment_date'] = None

new_parent.save(1)

new_doclist.append(new_parent)

for d in source_doclist.doclist[1:]:
newd = Document(fielddata = d.fields.copy())
newd.name = None
newd.fields['__islocal'] = 1
newd.fields['docstatus'] = 0
newd.parent = new_parent.name
new_doclist.append(newd)
doclistobj = ModelWrapper()
doclistobj.docs = new_doclist
doclistobj.doc = new_doclist[0]
doclistobj.doclist = new_doclist
doclistobj.children = new_doclist[1:]
doclistobj.save()
return doclistobj


def trigger(method, doc):
"""trigger doctype events"""
try:
import startup.event_handlers
except ImportError:
return
if hasattr(startup.event_handlers, method):
getattr(startup.event_handlers, method)(doc)
if hasattr(startup.event_handlers, 'doclist_all'):
startup.event_handlers.doclist_all(doc, method)

# for bc
def getlist(doclist, parentfield):
"""
Return child records of a particular type
"""
import webnotes.model.utils
return webnotes.model.utils.getlist(doclist, parentfield)

def copy_doclist(doclist, no_copy = []):
"""
Make a copy of the doclist
"""
import webnotes.model.utils
return webnotes.model.utils.copy_doclist(doclist, no_copy)


+ 24
- 49
webnotes/modules/__init__.py Просмотреть файл

@@ -24,9 +24,23 @@ from __future__ import unicode_literals
""" """
Utilities for using modules Utilities for using modules
""" """
import webnotes
import webnotes, os, conf


transfer_types = ['Role', 'Print Format','DocType','Page','DocType Mapper','GL Mapper','Search Criteria', 'Patch', 'Report']
transfer_types = ['Role', 'Print Format','DocType','Page','DocType Mapper',
'GL Mapper','Search Criteria', 'Patch', 'Report']
lower_case_files_for = ['DocType', 'Page', 'Search Criteria', 'Report',
"Workflow", 'Module Def', 'Desktop Item', 'Workflow State', 'Workflow Action']

code_fields_dict = {
'Page':[('script', 'js'), ('content', 'html'), ('style', 'css'), ('static_content', 'html'), ('server_code', 'py')],
'DocType':[('server_code_core', 'py'), ('client_script_core', 'js')],
'Search Criteria':[('report_script', 'js'), ('server_script', 'py'), ('custom_query', 'sql')],
'Patch':[('patch_code', 'py')],
'Stylesheet':['stylesheet', 'css'],
'Page Template':['template', 'html'],
'Control Panel':[('startup_code', 'js'), ('startup_css', 'css')]
}


def scrub(txt): def scrub(txt):
return txt.replace(' ','_').replace('-', '_').replace('/', '_').lower() return txt.replace(' ','_').replace('-', '_').replace('/', '_').lower()
@@ -34,14 +48,13 @@ def scrub(txt):
def scrub_dt_dn(dt, dn): def scrub_dt_dn(dt, dn):
"""Returns in lowercase and code friendly names of doctype and name for certain types""" """Returns in lowercase and code friendly names of doctype and name for certain types"""
ndt, ndn = dt, dn ndt, ndn = dt, dn
if dt.lower() in ('doctype', 'search criteria', 'page', 'report'):
if dt in lower_case_files_for:
ndt, ndn = scrub(dt), scrub(dn) ndt, ndn = scrub(dt), scrub(dn)


return ndt, ndn return ndt, ndn
def get_module_path(module): def get_module_path(module):
"""Returns path of the given module""" """Returns path of the given module"""
import os, conf
m = scrub(module) m = scrub(module)
app_path = os.path.dirname(conf.__file__) app_path = os.path.dirname(conf.__file__)
@@ -50,56 +63,18 @@ def get_module_path(module):
return os.path.join(app_path, 'lib', 'core') return os.path.join(app_path, 'lib', 'core')
else: else:
return os.path.join(app_path, 'app', m) return os.path.join(app_path, 'app', m)

def reload_doc(module, dt=None, dn=None):
"""reload single / list of records"""

if type(module) is list:
for m in module:
reload_single_doc(m[0], m[1], m[2])
else:
reload_single_doc(module, dt, dn)
def reload_single_doc(module, dt, dn, force=False):
"""Sync a file from txt if modifed, return false if not updated"""
if dt.lower() == 'doctype':
return
import os
dt, dn = scrub_dt_dn(dt, dn)

path = os.path.join(get_module_path(module),
os.path.join(dt, dn, dn + '.txt'))
if os.path.exists(path):
from webnotes.model.utils import peval_doclist
with open(path, 'r') as f:
doclist = peval_doclist(f.read())
if doclist:
doc = doclist[0]
if not force:
# check if timestamps match
if doc['modified']== str(webnotes.conn.get_value(doc['doctype'], doc['name'], 'modified')):
return False
from webnotes.utils.transfer import set_doc
set_doc(doclist, 1, 1, 1)


# since there is a new timestamp on the file, update timestamp in
webnotes.conn.sql("update `tab%s` set modified=%s where name=%s" % \
(doc['doctype'], '%s', '%s'),
(doc['modified'],doc['name']))
return True
else:
raise Exception, '%s missing' % path
def get_doc_path(module, doctype, name):
dt, dn = scrub_dt_dn(doctype, name)
return os.path.join(get_module_path(module), dt, dn)


def reload_doc(module, dt=None, dn=None):
from webnotes.modules.import_file import import_files
return import_files(module, dt, dn)


def export_doc(doctype, name, module=None): def export_doc(doctype, name, module=None):
"""write out a doc""" """write out a doc"""
from webnotes.modules.export_module import write_document_file
from webnotes.modules.export_file import write_document_file
import webnotes.model.doc import webnotes.model.doc
if not module: module = webnotes.conn.get_value(doctype, name, 'module') if not module: module = webnotes.conn.get_value(doctype, name, 'module')
doclist = [d.fields for d in webnotes.model.doc.get(doctype, name)] doclist = [d.fields for d in webnotes.model.doc.get(doctype, name)]


webnotes/modules/export_module.py → webnotes/modules/export_file.py Просмотреть файл

@@ -21,113 +21,111 @@
# #


from __future__ import unicode_literals from __future__ import unicode_literals
"""
Export files to modules
"""


from webnotes.modules import scrub, get_module_path
import webnotes, os
import webnotes.model.doc
from webnotes.modules import scrub, get_module_path, lower_case_files_for, \
code_fields_dict, scrub_dt_dn

def export_doc(doc):
export_to_files([[doc.doctype, doc.name]])

def export_to_files(record_list=[], record_module=None, verbose=0): def export_to_files(record_list=[], record_module=None, verbose=0):
""" """
Export record_list to files. record_list is a list of lists ([doctype],[docname] ) , Export record_list to files. record_list is a list of lists ([doctype],[docname] ) ,
""" """
import webnotes.model.doc
from webnotes.modules.import_merge import in_transfer
if in_transfer:
return
module_doclist =[] module_doclist =[]
if record_list: if record_list:
for record in record_list: for record in record_list:
doclist = [d.fields for d in webnotes.model.doc.get(record[0], record[1])]
write_document_file(doclist, record_module)
write_document_file(webnotes.model.doc.get(record[0], record[1]),
record_module)
def write_document_file(doclist, record_module=None):
from webnotes.modules.utils import pprint_doclist


def create_init_py(module_path, dt, dn):
"""
Creates __init__.py in the module directory structure
"""
import os
doclist = [filter_fields(d.fields) for d in doclist]


def create_if_not_exists(path):
initpy = os.path.join(path, '__init__.py')
if not os.path.exists(initpy):
open(initpy, 'w').close()
create_if_not_exists(os.path.join(module_path))
create_if_not_exists(os.path.join(module_path, dt))
create_if_not_exists(os.path.join(module_path, dt, dn))
def create_folder(module, dt, dn):
"""
Creates directories for module and their __init__.py
"""
import webnotes, os
# get module path by importing the module
module_path = get_module_path(module)
code_type = dt in ['DocType', 'Page', 'Search Criteria', 'Report']
module = record_module or get_module_name(doclist)
code_type = doclist[0]['doctype'] in lower_case_files_for
# create folder # create folder
folder = os.path.join(module_path, code_type and scrub(dt) or dt, code_type and scrub(dn) or dn)
folder = create_folder(module, doclist[0]['doctype'], doclist[0]['name'], code_type)
webnotes.create_folder(folder)
# separate code files
clear_code_fields(doclist, folder, code_type)
# write the data file
fname = (code_type 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))

def filter_fields(doc):
from webnotes.model.doctype import get
from webnotes.model import default_fields

doctypelist = get(doc.doctype, False)
valid_fields = [d.fieldname for d in doctypelist.get({"parent":doc.doctype,
"doctype":"DocField"})]
to_remove = []
# create init_py_files
if code_type:
create_init_py(module_path, scrub(dt), scrub(dn))
for key in doc:
if (not key in default_fields) and (not key in valid_fields):
to_remove.append(key)
elif doc[key]==None:
to_remove.append(key)
for key in to_remove:
del doc[key]
return folder
return doc


def get_module_name(doclist, record_module=None):
"""
Returns the module-name of a doclist
"""
# module name
def get_module_name(doclist):
if doclist[0]['doctype'] == 'Module Def': if doclist[0]['doctype'] == 'Module Def':
module = doclist[0]['name'] module = doclist[0]['name']
elif doclist[0]['doctype']=='Control Panel': elif doclist[0]['doctype']=='Control Panel':
module = 'Core' module = 'Core'
elif record_module:
module = record_module
elif doclist[0]['doctype']=="Workflow":
module = webnotes.conn.get_value("DocType", doclist[0]["document_type"], "module")
else: else:
module = doclist[0]['module'] module = doclist[0]['module']


return module return module
def write_document_file(doclist, record_module=None):
"""
Write a doclist to file, can optionally specify module name
"""
import os
from webnotes.model.utils import pprint_doclist

module = get_module_name(doclist, record_module)
def create_folder(module, dt, dn, code_type):
# get module path by importing the module
module_path = get_module_path(module)


# create the folder
code_type = doclist[0]['doctype'] in ['DocType','Page','Search Criteria', 'Report']
dt, dn = scrub_dt_dn(dt, dn)
# create folder # create folder
folder = create_folder(module, doclist[0]['doctype'], doclist[0]['name'])
folder = os.path.join(module_path, dt, dn)
# separate code files
clear_code_fields(doclist, folder, code_type)
# write the data file
fname = (code_type and scrub(doclist[0]['name'])) or doclist[0]['name']
txtfile = open(os.path.join(folder, fname +'.txt'),'w+')
txtfile.write(pprint_doclist(doclist))
#dict_list = [pprint_dict(d) for d in doclist]
#txtfile.write('[\n' + ',\n'.join(dict_list) + '\n]')
txtfile.close()

def clear_code_fields(doclist, folder, code_type):
"""
Removes code from the doc
"""
webnotes.create_folder(folder)
import os
import webnotes
# code will be in the parent only
code_fields = webnotes.code_fields_dict.get(doclist[0]['doctype'], [])
# create init_py_files
if code_type:
create_init_py(module_path, dt, dn)
return folder

def create_init_py(module_path, dt, dn):
def create_if_not_exists(path):
initpy = os.path.join(path, '__init__.py')
if not os.path.exists(initpy):
open(initpy, 'w').close()
create_if_not_exists(os.path.join(module_path))
create_if_not_exists(os.path.join(module_path, dt))
create_if_not_exists(os.path.join(module_path, dt, dn))


def clear_code_fields(doclist, folder, code_type):
code_fields = code_fields_dict.get(doclist[0]['doctype'], [])
for code_field in code_fields: for code_field in code_fields:
if doclist[0].get(code_field[0]): if doclist[0].get(code_field[0]):

doclist[0][code_field[0]] = None doclist[0][code_field[0]] = None



+ 71
- 0
webnotes/modules/import_file.py Просмотреть файл

@@ -0,0 +1,71 @@
# Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
#
# MIT License (MIT)
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#

from __future__ import unicode_literals

import webnotes, os
from webnotes.modules import scrub, get_module_path, scrub_dt_dn

def import_files(module, dt=None, dn=None):
if type(module) is list:
for m in module:
import_file(m[0], m[1], m[2])
else:
import_file(module, dt, dn)
def import_file(module, dt, dn, force=False):
"""Sync a file from txt if modifed, return false if not updated"""
if dt.lower() == 'doctype':
return
dt, dn = scrub_dt_dn(dt, dn)

path = os.path.join(get_module_path(module),
os.path.join(dt, dn, dn + '.txt'))
import_file_by_path(path, force)

def import_file_by_path(path, force=False):
if os.path.exists(path):
from webnotes.modules.utils import peval_doclist
with open(path, 'r') as f:
doclist = peval_doclist(f.read())
if doclist:
doc = doclist[0]
if not force:
# check if timestamps match
if doc['modified']== str(webnotes.conn.get_value(doc['doctype'], doc['name'], 'modified')):
return False
from webnotes.modules.import_merge import set_doc
set_doc(doclist, 1, 1, 1)

# since there is a new timestamp on the file, update timestamp in
webnotes.conn.sql("update `tab%s` set modified=%s where name=%s" % \
(doc['doctype'], '%s', '%s'),
(doc['modified'],doc['name']))
return True
else:
raise Exception, '%s missing' % path


webnotes/utils/transfer.py → webnotes/modules/import_merge.py Просмотреть файл

@@ -121,7 +121,7 @@ class UpdateDocument:
def save(self): def save(self):
# parent # parent
self.doc.save(new = 1, ignore_fields = 1, check_links=0)
self.doc.save(new = 1, check_links=0)
self.doclist = [self.doc] self.doclist = [self.doc]
self.save_children() self.save_children()
@@ -131,12 +131,12 @@ class UpdateDocument:
def save_one_doc(self, df, as_new=1): def save_one_doc(self, df, as_new=1):
d = Document(fielddata = df) d = Document(fielddata = df)
d.save(new = as_new, ignore_fields = 1, check_links=0)
d.save(new = as_new, check_links=0)
self.doclist.append(d) self.doclist.append(d)


def run_on_update(self): def run_on_update(self):
from webnotes.model.code import get_server_obj
so = get_server_obj(self.doc, self.doclist)
from webnotes.model.controller import get_obj
so = get_obj(doc=self.doc, doclist=self.doclist)
if hasattr(so, 'on_update'): if hasattr(so, 'on_update'):
so.on_update() so.on_update()


@@ -165,7 +165,7 @@ class UpdateDocumentMerge(UpdateDocument):
if self.exists: if self.exists:
# save main doc # save main doc
self.keep_values(self.doc) self.keep_values(self.doc)
self.doc.save(ignore_fields = 1, check_links=0)
self.doc.save(check_links=0)
self.doclist.append(self.doc) self.doclist.append(self.doc)
self.save_children() self.save_children()
self.on_save() self.on_save()
@@ -186,10 +186,10 @@ class UpdateDocumentMerge(UpdateDocument):
# is it new? # is it new?
if self.child_exists(d): if self.child_exists(d):
self.keep_values(d) self.keep_values(d)
d.save(ignore_fields = 1, check_links=0)
d.save(check_links=0)
self.log.append('updated %s, %s' % (d.doctype, d.name)) self.log.append('updated %s, %s' % (d.doctype, d.name))
else: else:
d.save(1, ignore_fields = 1, check_links=0)
d.save(1, check_links=0)
self.log.append('new %s' % d.doctype) self.log.append('new %s' % d.doctype)
self.doclist.append(d) self.doclist.append(d)


@@ -272,7 +272,7 @@ class UpdateDocType(UpdateDocumentMerge):
tmp = Document(fielddata = d) tmp = Document(fielddata = d)
tmp.fieldname = '' tmp.fieldname = ''
tmp.name = None tmp.name = None
tmp.save(1, ignore_fields = 1, check_links=0)
tmp.save(1, check_links=0)
else: else:
webnotes.conn.sql("update tabDocField set idx=%s where %s=%s and parent=%s" % \ webnotes.conn.sql("update tabDocField set idx=%s where %s=%s and parent=%s" % \
('%s', d.get('fieldname') and 'fieldname' or 'label', '%s', '%s'), (d.get('idx'), d.get('fieldname') or d.get('label'), self.doc.name)) ('%s', d.get('fieldname') and 'fieldname' or 'label', '%s', '%s'), (d.get('idx'), d.get('fieldname') or d.get('label'), self.doc.name))
@@ -294,8 +294,8 @@ class UpdateDocType(UpdateDocumentMerge):
('%s', e[0] and 'fieldname' or 'label', '%s', '%s'), (idx+1, e[0] or e[1], self.doc.name)) ('%s', e[0] and 'fieldname' or 'label', '%s', '%s'), (idx+1, e[0] or e[1], self.doc.name))


def run_on_update(self): def run_on_update(self):
from webnotes.model.code import get_server_obj
so = get_server_obj(self.doc, self.doclist)
from webnotes.model.controller import get_obj
so = get_obj(doc=self.doc, doclist=self.doclist)
if hasattr(so, 'on_update'): if hasattr(so, 'on_update'):
so.on_update() so.on_update()



+ 116
- 0
webnotes/modules/utils.py Просмотреть файл

@@ -58,3 +58,119 @@ def switch_module(dt, dn, to, frm=None, export=None):


for ext in ('py','js','html','css'): for ext in ('py','js','html','css'):
os.system('cp %s %s') os.system('cp %s %s')

def commonify_doclist(doclist, with_comments=1):
"""
Makes a doclist more readable by extracting common properties.
This is used for printing Documents in files
"""
from webnotes.utils import get_common_dict, get_diff_dict

def make_common(doclist):
c = {}
if with_comments:
c['##comment'] = 'These values are common in all dictionaries'
for k in common_keys:
c[k] = doclist[0][k]
return c

def strip_common_and_idx(d):
for k in common_keys:
if k in d: del d[k]
if 'idx' in d: del d['idx']
return d

def make_common_dicts(doclist):

common_dict = {} # one per doctype

# make common dicts for all records
for d in doclist:
if not d['doctype'] in common_dict:
d1 = d.copy()
del d1['name']
common_dict[d['doctype']] = d1
else:
common_dict[d['doctype']] = get_common_dict(common_dict[d['doctype']], d)
return common_dict

common_keys = ['owner','docstatus','creation','modified','modified_by']
common_dict = make_common_dicts(doclist)

# make docs
final = []
for d in doclist:
f = strip_common_and_idx(get_diff_dict(common_dict[d['doctype']], d))
f['doctype'] = d['doctype'] # keep doctype!

# strip name for child records (only an auto generated number!)
if f['doctype'] != doclist[0]['doctype']:
del f['name']

if with_comments:
f['##comment'] = d['doctype'] + ('name' in f and (', ' + f['name']) or '')
final.append(f)

# add commons
commons = []
for d in common_dict.values():
d['name']='__common__'
if with_comments:
d['##comment'] = 'These values are common for all ' + d['doctype']
commons.append(strip_common_and_idx(d))

common_values = make_common(doclist)
return [common_values]+commons+final

def uncommonify_doclist(dl):
"""
Expands an commonified doclist
"""
# first one has common values
common_values = dl[0]
common_dict = {}
final = []
idx_dict = {}

for d in dl[1:]:
if 'name' in d and d['name']=='__common__':
# common for a doctype -
del d['name']
common_dict[d['doctype']] = d
else:
dt = d['doctype']
if not dt in idx_dict: idx_dict[dt] = 1;
d1 = common_values.copy()

# update from common and global
d1.update(common_dict[dt])
d1.update(d)

# idx by sequence
d1['idx'] = idx_dict[dt]
# increment idx
idx_dict[dt] += 1

final.append(d1)

return final

def pprint_doclist(doclist, with_comments = 1):
"""
Pretty Prints a doclist with common keys separated and comments
"""
from json import dumps

return dumps(commonify_doclist(doclist, False), indent=1)

def peval_doclist(txt):
"""
Restore a pretty printed doclist
"""
from json import loads
if txt.startswith("#"):
return uncommonify_doclist(eval(txt))
else:
return uncommonify_doclist(loads(txt))

+ 2
- 2
webnotes/utils/nestedset.py Просмотреть файл

@@ -33,7 +33,7 @@ from __future__ import unicode_literals


import webnotes, unittest import webnotes, unittest
from webnotes import msgprint from webnotes import msgprint
from webnotes.model.doclist import DocList
from webnotes.model.wrapper import ModelWrapper
from webnotes.model.doc import Document from webnotes.model.doc import Document


class TestNSM(unittest.TestCase): class TestNSM(unittest.TestCase):
@@ -53,7 +53,7 @@ class TestNSM(unittest.TestCase):
] ]
for d in self.data: for d in self.data:
self.__dict__[d[0]] = DocList([Document(fielddata = {
self.__dict__[d[0]] = ModelWrapper([Document(fielddata = {
"doctype": "Item Group", "item_group_name": d[0], "parent_item_group": d[1], "doctype": "Item Group", "item_group_name": d[0], "parent_item_group": d[1],
"__islocal": 1 "__islocal": 1
})]) })])


+ 2
- 2
webnotes/widgets/form/run_method.py Просмотреть файл

@@ -29,7 +29,7 @@ def runserverobj():
Run server objects Run server objects
""" """
import webnotes.model.code import webnotes.model.code
from webnotes.model.doclist import DocList
from webnotes.model.wrapper import ModelWrapper
from webnotes.utils import cint from webnotes.utils import cint


doclist = None doclist = None
@@ -43,7 +43,7 @@ def runserverobj():
so = webnotes.model.code.get_obj(dt, dn) so = webnotes.model.code.get_obj(dt, dn)


else: else:
doclist = DocList()
doclist = ModelWrapper()
doclist.from_compressed(webnotes.form_dict.get('docs'), dn) doclist.from_compressed(webnotes.form_dict.get('docs'), dn)
so = doclist.make_obj() so = doclist.make_obj()
doclist.check_if_latest() doclist.check_if_latest()


+ 2
- 2
webnotes/widgets/form/save.py Просмотреть файл

@@ -27,10 +27,10 @@ import webnotes
def savedocs(): def savedocs():
"""save / submit / cancel / update doclist""" """save / submit / cancel / update doclist"""
try: try:
from webnotes.model.doclist import DocList
from webnotes.model.wrapper import ModelWrapper
form = webnotes.form_dict form = webnotes.form_dict


doclist = DocList()
doclist = ModelWrapper()
doclist.from_compressed(form.get('docs'), form.get('docname')) doclist.from_compressed(form.get('docs'), form.get('docname'))


# action # action


+ 1
- 1
webnotes/widgets/query_builder.py Просмотреть файл

@@ -131,7 +131,7 @@ def exec_report(code, res, colnames=[], colwidths=[], coltypes=[], coloptions=[]
from webnotes import * from webnotes import *
from webnotes.utils import * from webnotes.utils import *
from webnotes.model.doc import * from webnotes.model.doc import *
from webnotes.model.doclist import getlist
from webnotes.model.wrapper import getlist
from webnotes.model.db_schema import updatedb from webnotes.model.db_schema import updatedb
from webnotes.model.code import get_obj from webnotes.model.code import get_obj




Загрузка…
Отмена
Сохранить