You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

197 lines
5.9 KiB

  1. # Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
  2. #
  3. # MIT License (MIT)
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a
  6. # copy of this software and associated documentation files (the "Software"),
  7. # to deal in the Software without restriction, including without limitation
  8. # the rights to use, copy, modify, merge, publish, distribute, sublicense,
  9. # and/or sell copies of the Software, and to permit persons to whom the
  10. # Software is furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in
  13. # all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  16. # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  17. # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  18. # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  19. # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
  20. # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. #
  22. from __future__ import unicode_literals
  23. """get diff bettween txt files and database records"""
  24. import webnotes
  25. import os, conf
  26. from webnotes.model.utils import peval_doclist
  27. dt_map = {
  28. 'DocType': {
  29. 'DocField': ['fieldname', 'label']
  30. },
  31. 'Search Criteria': {},
  32. 'Page': {}
  33. }
  34. ignore_fields = ('modified', 'creation', 'owner', 'modified_by',
  35. '_last_update', 'version', 'idx', 'name')
  36. missing = property_diff = 0
  37. property_count = {}
  38. def diff_ref_file():
  39. """get diff using .txt files as reference"""
  40. global missing, property_diff, property_count
  41. missing = property_diff = 0
  42. property_count = {}
  43. get_diff(conf.modules_path)
  44. get_diff(os.path.join(os.getcwd(), 'lib', 'py', 'core'))
  45. print_stats()
  46. def get_diff(path):
  47. for wt in os.walk(path):
  48. for fname in wt[2]:
  49. if fname.endswith('.txt'):
  50. path = os.path.join(wt[0], fname)
  51. with open(path, 'r') as txtfile:
  52. doclist_diff(peval_doclist(txtfile.read()))
  53. def diff_ref_db():
  54. """get diff using database as reference"""
  55. from webnotes.modules import scrub
  56. for dt in dt_map:
  57. # get all main docs
  58. for doc in webnotes.conn.sql("""select * from `tab%s`""" % dt, as_dict=1):
  59. # get file for this doc
  60. doc['doctype'] = dt
  61. #print doc['name'], doc['doctype'], doc['module']
  62. path = os.path.join(conf.modules_path, scrub(doc['module']), \
  63. scrub(dt), scrub(doc['name']), scrub(doc['name']) + '.txt')
  64. path_core = os.path.join(os.getcwd(), 'lib', 'py', 'core', \
  65. scrub(dt), scrub(doc['name']), scrub(doc['name']) + '.txt')
  66. if os.path.exists(path):
  67. with open(path, 'r') as txtfile:
  68. target = peval_doclist(txtfile.read())
  69. elif os.path.exists(path_core):
  70. with open(path_core, 'r') as txtfile:
  71. target = peval_doclist(txtfile.read())
  72. else:
  73. target = [None,]
  74. doc_diff(doc, target[0])
  75. # do diff for child records
  76. if target[0] and dt_map[dt].keys():
  77. for child_dt in dt_map[dt]:
  78. # for each child type, we need to create
  79. # a key (e.g. fieldname, label) based mapping of child records in
  80. # txt files
  81. child_key_map = {}
  82. keys = dt_map[dt][child_dt]
  83. for target_d in target:
  84. if target_d['doctype'] == child_dt:
  85. for key in keys:
  86. if target_d.get(key):
  87. child_key_map[target_d.get(key)] = target_d
  88. break
  89. for d in webnotes.conn.sql("""select * from `tab%s` where
  90. parent=%s and docstatus<2""" % (child_dt, '%s'), doc['name'], as_dict=1):
  91. source_key = None
  92. d['doctype'] = child_dt
  93. for key in keys:
  94. if d.get(key):
  95. source_key = d.get(key)
  96. break
  97. # only if a key is found
  98. if source_key:
  99. doc_diff(d, child_key_map.get(source_key), source_key)
  100. print_stats()
  101. def doclist_diff(doclist):
  102. # main doc
  103. doc = doclist[0]
  104. if doc['doctype'] in dt_map.keys():
  105. # do for main
  106. target = webnotes.conn.sql("""select * from `tab%s`
  107. where name=%s""" % (doc['doctype'], '%s'), doc['name'], as_dict=1)
  108. doc_diff(doc, target and target[0] or None)
  109. if not target:
  110. # no parent, no children!
  111. return
  112. for d in doclist[1:]:
  113. # if child
  114. if d['doctype'] in dt_map[doc['doctype']].keys():
  115. child_keys = dt_map[doc['doctype']][d['doctype']]
  116. # find the key on which a child is unique
  117. child_key = child_key_value = None
  118. for key in child_keys:
  119. if d.get(key):
  120. child_key = key
  121. child_key_value = d[key]
  122. break
  123. # incoming child record has a uniquely
  124. # identifiable key
  125. if child_key:
  126. target = webnotes.conn.sql("""select * from `tab%s`
  127. where `%s`=%s and parent=%s""" % (d['doctype'], child_key, '%s', '%s'),
  128. (child_key_value, d['parent']), as_dict=1)
  129. doc_diff(d, target and target[0] or None, child_key_value)
  130. def doc_diff(source, target, child_key_value=None):
  131. from termcolor import colored
  132. global property_diff, property_count, missing
  133. docname = source.get('parent') and (source.get('parent') + '.' + child_key_value) \
  134. or source['name']
  135. if not target:
  136. missing += 1
  137. print(colored((source['doctype'] + ' -> ' + docname), 'red') + ' missing')
  138. else:
  139. target['doctype'] = source['doctype']
  140. for key in source:
  141. if (key not in ignore_fields) \
  142. and (target.get(key) != source[key]) \
  143. and (not (target.get(key)==None and source[key]==0)):
  144. prefix = source['doctype']+' -> ' + docname + ' -> '+key+' = '
  145. in_db = colored(str(target.get(key)), 'red')
  146. in_file = colored(str(source[key]), 'green')
  147. print(prefix + in_db + ' | ' + in_file)
  148. property_diff += 1
  149. if not key in property_count:
  150. property_count[key] = 0
  151. property_count[key] += 1
  152. def print_stats():
  153. print
  154. print "Missing records: " + str(missing)
  155. print "Property mismatch: " + str(property_diff)
  156. for key in property_count:
  157. print "- " + key + ": " + str(property_count[key] or 0)
  158. print