<commit_before>"""
	Execute Patch Files

	Patch files usually lie in the "patches" module specified by "modules_path" in defs.py

	To run directly
	
	python lib/wnf.py patch patch1, patch2 etc
	python lib/wnf.py patch -f patch1, patch2 etc
	
	where patch1, patch2 is module name
"""
import unittest
import sys
import os
import traceback
import webnotes
import webnotes.defs
import webnotes.utils
import webnotes.utils.email_lib
from webnotes.db import Database


class PatchHandler:
	def __init__(self, **kwargs):
		"""
			Gets connection to database

			Arguments can be:
				* db_name
				* verbose --> to enabled printing
		"""
		self.verbose = kwargs.get('verbose')
		try:
			self.db_name = kwargs.get('db_name')
			webnotes.conn = None
			webnotes.conn = Database(user=self.db_name)
			webnotes.conn.use(self.db_name)
			if not (webnotes.session and webnotes.session['user']):
				webnotes.session = {'user': 'Administrator'}
		except Exception, e:
			self.log(log_type='error', msg=webnotes.getTraceback())
			raise e


	def run_patch(self, **kwargs):
		"""
			Run a patch on a db

			Arguments can be:
				* patch_module
				* patch_file
				* force

			* It gets a connection to the db having name db_name
			  and assigns the connection to webnotes.conn
			* If force == 0, it checks if the patch is already executed or not
			* It imports patch_file from patch_module
			* It runs the execute function of patch_file
		"""
		try:
			patch_module = kwargs.get('patch_module', 'patches')
			patch_file = kwargs.get('patch_file')			
			module_file = str(patch_module) + "." + str(patch_file)
			executed_patches = [p['patch'] for p in self.get_executed_patch_list()]
			
			if kwargs.get('force') or module_file not in executed_patches:
				webnotes.conn.begin()
				
				module = __import__(module_file)
				patch = getattr(module, patch_file)
				getattr(patch, 'execute')()

				self.log(log_type='success', patch_module=patch_module, patch_file=patch_file)
				webnotes.conn.commit()
		
		except Exception, e:
			webnotes.conn.rollback()
			self.log(log_type='error', patch_module=patch_module, patch_file=patch_file)


	def reload(self, **kwargs):
		"""
			Reloads a particular doc in the system

			Arguments can be:
				* module
				* doc_type
				* doc_name
		"""
		try:
			module = kwargs.get('module')
			doc_type, doc_name = kwargs.get('doc_type'), kwargs.get('doc_name')
			reload_string = 'Module: %s, DocType: %s, DocName: %s' % (module, doc_type, doc_name)
			
			from webnotes.modules.module_manager import reload_doc
			reload_doc(module, doc_type, doc_name)
			
			self.log(log_type='info', msg='Reload successful. ' + reload_string)
		
		except Exception, e:
			self.log(log_type='error', msg='Reload error. ' + reload_string)


	def get_executed_patch_list(self):
		"""
			Gets a list of dict of patches already executed in a db
			The list is in chronological order
		"""
		try:
			return webnotes.conn.sql("""\
				SELECT * from __PatchLog
				ORDER BY applied_on ASC""", as_dict=1)
		except Exception, e:
			try:
				if e.args[0]==1146:
					self.create_patch_log_table()
					return []
				else:
					self.log(log_type='error')
			except Exception, e:
				self.log(log_type='error')


	def run(self, **kwargs):
		"""
			Runs a patch or reload on a db

			Arguments can be:
				* Either one of 
					+ patch_list --> [{
							'patch_module': <module>, 
							'patch_file': <file>, 
							'force': True/False
						}, ...]
					+ run_latest --> can be any true value

			Before beginning, block user
			After completion, unblock user
		"""
		self.block_user(True, msg="Patches are being executed in the system. Please try again in a few minutes.")

		if kwargs.get('patch_list'):
			for patch in kwargs.get('patch_list'):
				self.run_patch(patch_module=patch.get('patch_module', 'patches'), patch_file=patch.get('patch_file'), force=kwargs.get('force'))

		elif kwargs.get('run_latest'):
			try:
				from patches.patch_list import patch_list
				for patch in patch_list:
					self.run_patch(patch_module=patch.get('patch_module', 'patches'), patch_file=patch.get('patch_file'))
			except Exception, e:
				self.log(log_type='error')


		self.block_user(False)

	
	def log(self, **kwargs):
		"""
			Logs successful patches in the database table __PatchLog
			
			Logs exceptions in patches.patch.log

			Also, if self.verbose is true, then it prints what gets logged

			Arguments can be:
				* log_type = success/error/info
				* patch_module
				* patch_file
				* msg
		"""
		log_type = kwargs.get('log_type')
		patch_module = kwargs.get('patch_module')
		patch_file = kwargs.get('patch_file')
		patch = str(patch_module) + "." + str(patch_file)

		if log_type == 'success':
			webnotes.conn.sql("""\
				INSERT INTO `__PatchLog`
				VALUES (%s, now())""", patch)
			if self.verbose: print 'Patch: %s applied successfully' % patch
	
		elif log_type == 'error' or log_type == 'info':
			args = {
				'log_type': log_type,
				'patch_module': patch_module,
				'patch_file': patch_file,
				'msg': kwargs.get('msg')
			}
			patch_msg = self.write_log(**args)
			if self.verbose: print patch_msg
		


	def write_log(self, **kwargs):
		"""
			Write error/info details into patch.log file

			Arguments can be:
				* log_type = error/info
				* patch_module
				* patch_file<commit_msg>Changes in patch_handler<commit_after>"""
	Execute Patch Files

	Patch files usually lie in the "patches" module specified by "modules_path" in defs.py

	To run directly
	
	python lib/wnf.py patch patch1, patch2 etc
	python lib/wnf.py patch -f patch1, patch2 etc
	
	where patch1, patch2 is module name
"""
import unittest
import sys
import os
import traceback
import webnotes
import webnotes.defs
import webnotes.utils
import webnotes.utils.email_lib
from webnotes.db import Database


class PatchHandler:
	def __init__(self, **kwargs):
		"""
			Gets connection to database

			Arguments can be:
				* db_name
				* verbose --> to enabled printing
		"""
		self.verbose = kwargs.get('verbose')
		try:
			self.db_name = kwargs.get('db_name')
			webnotes.conn = None
			webnotes.conn = Database(user=self.db_name)
			webnotes.conn.use(self.db_name)
			if not (webnotes.session and webnotes.session['user']):
				webnotes.session = {'user': 'Administrator'}
		except Exception, e:
			self.log(log_type='error', msg=webnotes.getTraceback())
			raise e


	def run_patch(self, **kwargs):
		"""
			Run a patch on a db

			Arguments can be:
				* patch_module
				* patch_file
				* force

			* It gets a connection to the db having name db_name
			  and assigns the connection to webnotes.conn
			* If force == 0, it checks if the patch is already executed or not
			* It imports patch_file from patch_module
			* It runs the execute function of patch_file
		"""
		try:
			patch_module = kwargs.get('patch_module', 'patches')
			patch_file = kwargs.get('patch_file')			
			module_file = str(patch_module) + "." + str(patch_file)
			executed_patches = [p['patch'] for p in self.get_executed_patch_list()]
			
			if kwargs.get('force') or module_file not in executed_patches:
				webnotes.conn.begin()
				
				module = __import__(module_file)
				patch = getattr(module, patch_file)
				getattr(patch, 'execute')()

				self.log(log_type='success', patch_module=patch_module, patch_file=patch_file)
				webnotes.conn.commit()
		
		except Exception, e:
			webnotes.conn.rollback()
			self.log(log_type='error', patch_module=patch_module, patch_file=patch_file)


	def reload(self, **kwargs):
		"""
			Reloads a particular doc in the system

			Arguments can be:
				* module
				* doc_type
				* doc_name
		"""
		try:
			self.block_user(True, msg="Patches are being executed in the system. Please try again in a few minutes.")
			module = kwargs.get('module')
			doc_type, doc_name = kwargs.get('doc_type'), kwargs.get('doc_name')
			reload_string = 'Module: %s, DocType: %s, DocName: %s' % (module, doc_type, doc_name)
			
			from webnotes.modules.module_manager import reload_doc
			reload_doc(module, doc_type, doc_name)
			
			self.log(log_type='info', msg='Reload successful. ' + reload_string)
		except Exception, e:
			self.log(log_type='error', msg='Reload error. ' + reload_string)
		finally:
			self.block_user(False)


	def get_executed_patch_list(self):
		"""
			Gets a list of dict of patches already executed in a db
			The list is in chronological order
		"""
		try:
			return webnotes.conn.sql("""\
				SELECT * from __PatchLog
				ORDER BY applied_on ASC""", as_dict=1)
		except Exception, e:
			try:
				if e.args[0]==1146:
					self.create_patch_log_table()
					return []
				else:
					self.log(log_type='error')
			except Exception, e:
				self.log(log_type='error')


	def run(self, **kwargs):
		"""
			Runs a patch on a db

			Arguments can be:
				* Either one of 
					* patch_list
						--> List of dict containing:
							+ patch_module
							+ patch_file (!Mandatory)
					* force --> True/False (Only application if patch_list)
				* run_latest --> True/False

			Before beginning, block user
			After completion, unblock user
		"""
		self.block_user(True, msg="Patches are being executed in the system. Please try again in a few minutes.")

		if kwargs.get('patch_list'):
			for patch in kwargs.get('patch_list'):
				self.run_patch(patch_module=patch.get('patch_module', 'patches'), patch_file=patch.get('patch_file'), force=kwargs.get('force'))

		elif kwargs.get('run_latest'):
			try:
				from patches.patch_list import patch_list
				for patch in patch_list:
					self.run_patch(patch_module=patch.get('patch_module', 'patches'), patch_file=patch.get('patch_file'))
			except Exception, e:
				self.log(log_type='error')


		self.block_user(False)

	
	def log(self, **kwargs):
		"""
			Logs successful patches in the database table __PatchLog
			
			Logs exceptions in patches.patch.log

			Also, if self.verbose is true, then it prints what gets logged

			Arguments can be:
				* log_type = success/error/info
				* patch_module
				* patch_file
				* msg
		"""
		log_type = kwargs.get('log_type')
		patch_module = kwargs.get('patch_module')
		patch_file = kwargs.get('patch_file')
		patch = str(patch_module) + "." + str(patch_file)

		if log_type == 'success':
			webnotes.conn.sql("""\
				INSERT INTO `__PatchLog`
				VALUES (%s, now())""", patch)
			if self.verbose: print 'Patch: %s applied successfully on %s' % (patch, str(self.db_name))
	
		elif log_type == 'error' or log_type == 'info':
			args = {
				'log_type': log_type,
				'patch_module': patch_module,
				'patch_file': patch_file,
				'msg': kwargs.get('msg')
			}
			patch_msg = self.write_log(**args)
			if self.verbose: print patch_msg
		


	def write_log(self, **kwargs):
		"""
			Write error/info details into patch.log file

			Arguments can be:
				* log_type = error/info
				* patch_module
				* patch_file
{'fraction_non_alphanumeric': 0.11765792390099741, 'fraction_numerical': 0.0024011821204285187, 'mean_word_length': 3.497093023255814, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5919990', 'n_tokens_mistral': 4276, 'n_tokens_neox': 3539, 'n_words': 972}