From f7f9155af6f82a696c5bc77bb98d510435f99976 Mon Sep 17 00:00:00 2001 From: karthikeyan5 Date: Tue, 31 May 2022 19:01:25 +0530 Subject: [PATCH 1/2] feat: ignore known erpnext exceptions --- erpnext_sync.py | 14 ++++++++++++-- local_config.py.template | 12 +++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/erpnext_sync.py b/erpnext_sync.py index fd3c5a3..6e9f964 100644 --- a/erpnext_sync.py +++ b/erpnext_sync.py @@ -11,7 +11,17 @@ import pickledb from zk import ZK, const -EMPLOYEE_NOT_FOUND_ERROR_MESSAGE = "No Employee found for the given employee field value." +EMPLOYEE_NOT_FOUND_ERROR_MESSAGE = "No Employee found for the given employee field value" +EMPLOYEE_INACTIVE_ERROR_MESSAGE = "Transactions cannot be created for an Inactive Employee" +DUPLICATE_EMPLOYEE_CHECKIN_ERROR_MESSAGE = "This employee already has a log with the same timestamp" +allowlisted_errors = [EMPLOYEE_NOT_FOUND_ERROR_MESSAGE, EMPLOYEE_INACTIVE_ERROR_MESSAGE, DUPLICATE_EMPLOYEE_CHECKIN_ERROR_MESSAGE] + +if hasattr(config,'allowed_exceptions'): + allowlisted_errors_temp = [] + for error_number in config.allowed_exceptions: + allowlisted_errors.append(allowlisted_errors[error_number-1]) + allowlisted_errors = allowlisted_errors_temp + device_punch_values_IN = getattr(config, 'device_punch_values_IN', [0,4]) device_punch_values_OUT = getattr(config, 'device_punch_values_OUT', [1,5]) @@ -125,7 +135,7 @@ def pull_process_and_push_data(device, device_attendance_logs=None): str(device_attendance_log['user_id']), str(device_attendance_log['timestamp'].timestamp()), str(device_attendance_log['punch']), str(device_attendance_log['status']), json.dumps(device_attendance_log, default=str)])) - if EMPLOYEE_NOT_FOUND_ERROR_MESSAGE not in erpnext_message: + if not(any(error in erpnext_message for error in allowlisted_errors)): raise Exception('API Call to ERPNext Failed.') diff --git a/local_config.py.template b/local_config.py.template index 8ffae91..a84b618 100644 --- a/local_config.py.template +++ b/local_config.py.template @@ -21,7 +21,17 @@ devices = [ {'device_id':'test_2','ip':'192.168.2.209', 'punch_direction': None, 'clear_from_device_on_fetch': False} ] -# Configs updating sync timestamp in the Shift Type DocType +# Configs updating sync timestamp in the Shift Type DocType +# please, read this thread to know why this is necessary https://discuss.erpnext.com/t/v-12-hr-auto-attendance-purpose-of-last-sync-of-checkin-in-shift-type/52997 shift_type_device_mapping = [ {'shift_type_name': ['Shift1'], 'related_device_id': ['test_1','test_2']} ] + + +# Ignore following exceptions thrown by ERPNext and continue importing punch logs. +# Note: All other exceptions will halt the punch log import to erpnext. +# 1. No Employee found for the given employee User ID in the Biometric device. +# 2. Employee is inactive for the given employee User ID in the Biometric device. +# 3. Duplicate Employee Checkin found. (This exception can happen if you have cleared the logs/status.json of this script) +# Use the corresponding number to ignore the above exceptions. (Default: Ignores all the listed exceptions) +allowed_exceptions = [1,2,3] \ No newline at end of file From 6af0e40c138696710340f5b4be599f94a4ea2dfb Mon Sep 17 00:00:00 2001 From: karthikeyan5 Date: Tue, 21 Jun 2022 10:07:57 +0530 Subject: [PATCH 2/2] fix: allowed_exceptions bug and dump file bug --- erpnext_sync.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext_sync.py b/erpnext_sync.py index 6e9f964..8884517 100644 --- a/erpnext_sync.py +++ b/erpnext_sync.py @@ -19,7 +19,7 @@ if hasattr(config,'allowed_exceptions'): allowlisted_errors_temp = [] for error_number in config.allowed_exceptions: - allowlisted_errors.append(allowlisted_errors[error_number-1]) + allowlisted_errors_temp.append(allowlisted_errors[error_number-1]) allowlisted_errors = allowlisted_errors_temp device_punch_values_IN = getattr(config, 'device_punch_values_IN', [0,4]) @@ -50,7 +50,7 @@ def main(): for device in config.devices: device_attendance_logs = None info_logger.info("Processing Device: "+ device['device_id']) - dump_file = config.LOGS_DIRECTORY+'/'+device['ip'].replace('.', '_')+'_last_fetch_dump.json' + dump_file = get_dump_file_name_and_directory(device['device_id'], device['ip']) if os.path.exists(dump_file): info_logger.error('Device Attendance Dump Found in Log Directory. This can mean the program crashed unexpectedly. Retrying with dumped data.') with open(dump_file, 'r') as f: @@ -156,7 +156,7 @@ def get_all_attendance_from_device(ip, port=4370, timeout=30, device_id=None, cl if len(attendances): # keeping a backup before clearing data incase the programs fails. # if everything goes well then this file is removed automatically at the end. - dump_file_name = config.LOGS_DIRECTORY+'/' + device_id + "_" + ip.replace('.', '_') + '_last_fetch_dump.json' + dump_file_name = get_dump_file_name_and_directory(device_id, ip) with open(dump_file_name, 'w+') as f: f.write(json.dumps(list(map(lambda x: x.__dict__, attendances)), default=datetime.datetime.timestamp)) if clear_from_device_on_fetch: @@ -286,6 +286,9 @@ def setup_logger(name, log_file, level=logging.INFO, formatter=None): return logger +def get_dump_file_name_and_directory(device_id, device_ip): + return config.LOGS_DIRECTORY + '/' + device_id + "_" + device_ip.replace('.', '_') + '_last_fetch_dump.json' + def _apply_function_to_key(obj, key, fn): obj[key] = fn(obj[key]) return obj