diff --git a/.gitignore b/.gitignore index 11a8ec327..becab50db 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ package.json .husky # various virtualenv +/odoo-venv/ /bin/ /build/ /dist/ diff --git a/addons_extensions/hr_emp_dashboard/models/emp_dashboard.py b/addons_extensions/hr_emp_dashboard/models/emp_dashboard.py index a5685ee42..4d561ebdb 100644 --- a/addons_extensions/hr_emp_dashboard/models/emp_dashboard.py +++ b/addons_extensions/hr_emp_dashboard/models/emp_dashboard.py @@ -33,6 +33,7 @@ class HrEmployee(models.Model): @api.model def get_user_employee_details(self): uid = request.session.uid + self.env["hr.employee"].sudo().search([("user_id", "=", uid)])._compute_experience() """To fetch the details of employee""" return self.env["hr.employee"].sudo().search_read( [("user_id", "=", uid)], diff --git a/addons_extensions/hr_employee_extended/__manifest__.py b/addons_extensions/hr_employee_extended/__manifest__.py index ad173df00..ec27233ec 100644 --- a/addons_extensions/hr_employee_extended/__manifest__.py +++ b/addons_extensions/hr_employee_extended/__manifest__.py @@ -24,6 +24,9 @@ 'data': [ 'security/security.xml', 'security/ir.model.access.csv', + 'data/cron.xml', + 'data/mail.xml', + 'views/res_config_settings.xml', 'views/hr_employee.xml', 'views/bank_details.xml', 'wizards/work_location_wizard.xml' diff --git a/addons_extensions/hr_employee_extended/data/cron.xml b/addons_extensions/hr_employee_extended/data/cron.xml new file mode 100644 index 000000000..9903b9986 --- /dev/null +++ b/addons_extensions/hr_employee_extended/data/cron.xml @@ -0,0 +1,15 @@ + + + + + Employee: Event Remainder + + code + model.hr_employee_event_remainder() + 1 + days + + + + + \ No newline at end of file diff --git a/addons_extensions/hr_employee_extended/data/mail.xml b/addons_extensions/hr_employee_extended/data/mail.xml new file mode 100644 index 000000000..6a22e6914 --- /dev/null +++ b/addons_extensions/hr_employee_extended/data/mail.xml @@ -0,0 +1,59 @@ + + + + + + Employee Birthday & Anniversary Reminder + + {{ user.email_formatted }} + hr@ftprotech.com + Upcoming Employee Events: Birthdays & Anniversaries + Notify HR of employees with upcoming birthdays or anniversaries. + + + +

+ Dear HR Team, +
+
+ + Here are the upcoming Employee Events: + +
+
+ + 🎂 Birthdays: +
+

    + +
  • + + — + +
  • +
    +
+
+ + + 💍 Marriage Anniversaries:
+
    + +
  • + — + +
  • +
    +
+
+ +
+ Regards, +
+ +

+
+
+ +
+
\ No newline at end of file diff --git a/addons_extensions/hr_employee_extended/models/__init__.py b/addons_extensions/hr_employee_extended/models/__init__.py index 315b0c858..c60cd7877 100644 --- a/addons_extensions/hr_employee_extended/models/__init__.py +++ b/addons_extensions/hr_employee_extended/models/__init__.py @@ -1,3 +1,4 @@ +from . import res_config_settings from . import hr_employee from . import work_location_history from . import education_history diff --git a/addons_extensions/hr_employee_extended/models/hr_employee.py b/addons_extensions/hr_employee_extended/models/hr_employee.py index 0d980fa55..e23e2c68f 100644 --- a/addons_extensions/hr_employee_extended/models/hr_employee.py +++ b/addons_extensions/hr_employee_extended/models/hr_employee.py @@ -38,7 +38,7 @@ class HrEmployeeBase(models.AbstractModel): @api.constrains('identification_id') def _check_identification_id(self): for record in self: - if not record.identification_id.isdigit(): + if record.identification_id and not record.identification_id.isdigit(): raise ValidationError("Identification ID should only contain numbers.") @api.depends('doj') @@ -156,4 +156,67 @@ class HrEmployee(models.Model): passport_issued_location = fields.Char(string='Passport Issued Location') previous_company_pf_no = fields.Char(string='Previous Company PF No') - previous_company_uan_no = fields.Char(string='Previous Company UAN No') \ No newline at end of file + previous_company_uan_no = fields.Char(string='Previous Company UAN No') + + def hr_employee_event_remainder(self): + self.send_birthday_reminders() + today = fields.Date.today() + tomorrow = today + timedelta(days=1) + day_after = today + timedelta(days=2) + + tomorrow_md = tomorrow.strftime('-%m-%d') + day_after_md = day_after.strftime('-%m-%d') + + if today.weekday() == 4: # Friday + birthday_domain = ['|', ('birthday', 'like', f'%{tomorrow_md}'), ('birthday', 'like', f'%{day_after_md}')] + anniversary_domain = [('marital', '=', 'married'), '|', + ('marriage_anniversary_date','like', f'%{tomorrow_md}'), + ('marriage_anniversary_date', 'like', f'%{day_after_md}')] + else: + birthday_domain = [('birthday','like', f'%{tomorrow_md}')] + anniversary_domain = [('marital', '=', 'married'), + ('marriage_anniversary_date','like', f'%{tomorrow_md}')] + + birthday_emps = self.search(birthday_domain) + anniversary_emps = self.search(anniversary_domain) + + birthday_list = [{'name': emp.name, 'birthday': emp.birthday} for emp in birthday_emps] + anniversary_list = [{'name': emp.name, 'marriage_anniversary_date': emp.marriage_anniversary_date} for emp in anniversary_emps] + context = { + 'birthday_list': birthday_list, + 'anniversary_list': anniversary_list, + } + email_values = {'auto_delete': True} + template = self.env.ref('hr_employee_extended.hr_employee_event_reminder_template') + if template and (birthday_list or anniversary_list): + hr_id = self.env['ir.config_parameter'].sudo().get_param('hr_employee_extended.emp_hr_id') + if hr_id: + emp_hr = self.env['res.users'].sudo().browse(int(hr_id)) if hr_id else False + email_values['email_to'] = emp_hr.email + else: + emp_hr = self.env['res.users'].sudo().search([('groups_id', 'in', self.env.ref('hr.group_hr_manager').id)],limit=1) + + # Use `self[0]` as dummy record for sending + template.with_context(**context).send_mail( + emp_hr.employee_id.id, force_send=True, email_values=email_values, + ) + + @api.model + def send_birthday_reminders(self): + today = datetime.today().strftime('%m-%d') + employees = self.search([('birthday', '!=', False)]) + + birthday_emps = employees.filtered( + lambda emp: emp.birthday.strftime('%m-%d') == today + ) + + if birthday_emps: + channel = self.env['discuss.channel'].search([('name', 'ilike', 'General')], limit=1) + if channel: + for emp in birthday_emps: + message = f"🎉 Happy Birthday {emp.name}! 🎂 Wishing you a fantastic day! 🥳" + channel.message_post( + body=message, + message_type='comment', + subtype_xmlid='mail.mt_comment' + ) diff --git a/addons_extensions/hr_employee_extended/models/res_config_settings.py b/addons_extensions/hr_employee_extended/models/res_config_settings.py new file mode 100644 index 000000000..3167c8558 --- /dev/null +++ b/addons_extensions/hr_employee_extended/models/res_config_settings.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import fields, models, api, _ + + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + emp_hr_id = fields.Many2one('res.users',config_parameter='hr_employee_extended.emp_hr_id', string='Employee HR', + domain=lambda self: [ + ('groups_id', 'in', self.env.ref('hr.group_hr_manager').id)]) + diff --git a/addons_extensions/hr_employee_extended/views/res_config_settings.xml b/addons_extensions/hr_employee_extended/views/res_config_settings.xml new file mode 100644 index 000000000..9c9aff064 --- /dev/null +++ b/addons_extensions/hr_employee_extended/views/res_config_settings.xml @@ -0,0 +1,22 @@ + + + + res.config.settings.view.form.inherit.employee + res.config.settings + + + + + + + + + + + + + + diff --git a/addons_extensions/hr_payroll/__manifest__.py b/addons_extensions/hr_payroll/__manifest__.py index 5e2dd208b..ab74ceec4 100644 --- a/addons_extensions/hr_payroll/__manifest__.py +++ b/addons_extensions/hr_payroll/__manifest__.py @@ -1,9 +1,7 @@ -# Part of Odoo. See LICENSE file for full copyright and licensing details. { 'name': 'Payroll', 'category': 'Human Resources/Payroll', - 'sequence': 290, 'summary': 'Manage your employee payroll records', 'installable': True, 'application': True, @@ -11,7 +9,7 @@ # 'hr_work_entry_contract_enterprise', 'mail', 'web_editor', - 'hr_work_entry_contract', + 'hr_work_entry_contract','web', # 'hr_gantt' ], 'data': [ @@ -59,6 +57,7 @@ 'views/hr_payroll_menu.xml', # 'views/hr_work_entry_views.xml', 'views/hr_work_entry_export_mixin_views.xml', + 'views/ftp_payslip.xml', 'report/hr_contract_history_report_views.xml', 'wizard/hr_payroll_payment_report_wizard.xml', ], @@ -82,6 +81,6 @@ # 'hr_payroll/static/tests/**/*.js', ], }, - 'license': 'OEEL-1', + 'license': 'LGPL-3', 'post_init_hook': '_post_init_hook', } diff --git a/addons_extensions/hr_payroll/models/hr_employee.py b/addons_extensions/hr_payroll/models/hr_employee.py index 5696ba274..f2794ade3 100644 --- a/addons_extensions/hr_payroll/models/hr_employee.py +++ b/addons_extensions/hr_payroll/models/hr_employee.py @@ -69,22 +69,4 @@ class HrEmployee(models.Model): return self.env.cr.dictfetchall() - @api.model - def send_birthday_reminders(self): - today = datetime.today().strftime('%m-%d') - employees = self.search([('birthday', '!=', False)]) - birthday_emps = employees.filtered( - lambda emp: emp.birthday.strftime('%m-%d') == today - ) - - if birthday_emps: - channel = self.env['discuss.channel'].search([('name', 'ilike', 'General')], limit=1) - if channel: - for emp in birthday_emps: - message = f"🎉 Happy Birthday {emp.name}! 🎂 Wishing you a fantastic day! 🥳" - channel.message_post( - body=message, - message_type='comment', - subtype_xmlid='mail.mt_comment' - ) diff --git a/addons_extensions/hr_payroll/views/ftp_payslip.xml b/addons_extensions/hr_payroll/views/ftp_payslip.xml new file mode 100644 index 000000000..21c69b79b --- /dev/null +++ b/addons_extensions/hr_payroll/views/ftp_payslip.xml @@ -0,0 +1,152 @@ + + + + + \ No newline at end of file diff --git a/addons_extensions/hr_payroll/views/hr_payroll_report.xml b/addons_extensions/hr_payroll/views/hr_payroll_report.xml index b130e9ce8..b55cde599 100644 --- a/addons_extensions/hr_payroll/views/hr_payroll_report.xml +++ b/addons_extensions/hr_payroll/views/hr_payroll_report.xml @@ -13,6 +13,19 @@ + + + Payslip Ftp + hr.payslip + qweb-pdf + hr_payroll.report_payslip_ftps + hr_payroll.report_payslip_ftps + 'Payslip - %s' % (object.name) + + report + + + Payslip (Light) hr.payslip diff --git a/addons_extensions/weekly_attendance_report/__init__.py b/addons_extensions/weekly_attendance_report/__init__.py new file mode 100644 index 000000000..9a7e03ede --- /dev/null +++ b/addons_extensions/weekly_attendance_report/__init__.py @@ -0,0 +1 @@ +from . import models \ No newline at end of file diff --git a/addons_extensions/weekly_attendance_report/__manifest__.py b/addons_extensions/weekly_attendance_report/__manifest__.py new file mode 100644 index 000000000..aaa9bcde8 --- /dev/null +++ b/addons_extensions/weekly_attendance_report/__manifest__.py @@ -0,0 +1,11 @@ +{ + 'name': 'Weekly Attendance Email Report', + 'version': '1.0', + 'category': 'Human Resources', + 'depends': ['hr_attendance', 'mail'], + 'data': [ + 'security/ir.model.access.csv', + ], + 'installable': True, + 'application': False, +} diff --git a/addons_extensions/weekly_attendance_report/models/__init__.py b/addons_extensions/weekly_attendance_report/models/__init__.py new file mode 100644 index 000000000..98cad3460 --- /dev/null +++ b/addons_extensions/weekly_attendance_report/models/__init__.py @@ -0,0 +1 @@ +from . import attendance_report \ No newline at end of file diff --git a/addons_extensions/weekly_attendance_report/models/attendance_report.py b/addons_extensions/weekly_attendance_report/models/attendance_report.py new file mode 100644 index 000000000..c529212bb --- /dev/null +++ b/addons_extensions/weekly_attendance_report/models/attendance_report.py @@ -0,0 +1,137 @@ +from odoo import models, fields, api +from datetime import timedelta +import json +from collections import defaultdict +from pytz import timezone, UTC + + +class AttendanceWeeklyReport(models.Model): + _name = 'attendance.weekly.report' + _description = 'Weekly Attendance Report' + + def send_weekly_attendance_email(self): + management_emails = self.env['hr.employee'].search([ + ('department_id.name', '=', 'Administration'), + ('work_email', '!=', False) + ]).mapped('work_email') + + if not management_emails: + return + + today = fields.Date.context_today(self) + last_monday = today - timedelta(days=today.weekday() + 7) + last_sunday = last_monday + timedelta(days=6) + + user_tz = self.env.user.tz or 'UTC' + tz = timezone(user_tz) + + # Search for attendance in UTC (Odoo stores in UTC) + attendances = self.env['hr.attendance'].search([ + ('check_in', '>=', str(last_monday)), + ('check_out', '<=', str(last_sunday + timedelta(days=1))) + ]) + + employee_data = defaultdict(list) + grouped_attendance = defaultdict(lambda: defaultdict(list)) # {emp: {date: [attendances]}} + + # Group attendances by employee and local date + for att in attendances: + emp = att.employee_id.name + check_in_local = att.check_in.astimezone(tz) + date_local = check_in_local.date() + grouped_attendance[emp][date_local].append(att) + + # Process each employee's attendance + for emp_name, dates in grouped_attendance.items(): + for date, records in dates.items(): + records = sorted(records, key=lambda a: a.check_in) + total_seconds = 0 + first_in = records[0].check_in.astimezone(tz).strftime('%H:%M') + last_out = 'N/A' + + for rec in records: + if rec.check_in and rec.check_out: + check_in_local = rec.check_in.astimezone(tz) + check_out_local = rec.check_out.astimezone(tz) + total_seconds += (check_out_local - check_in_local).total_seconds() + last_out = check_out_local.strftime('%H:%M') + + employee_data[emp_name].append({ + 'date': date.strftime('%Y-%m-%d'), + 'in': first_in, + 'out': last_out, + 'hours': f'{total_seconds / 3600:.2f}', + }) + # Inline QWeb-compatible HTML template (must be in a real view in production) + html_template = """ +
+

Dear Management,

+

+ Please find below the attendance summary for the period + %(from_date)s to %(to_date)s. +

+ + %(employee_tables)s + +

Regards,
Odoo HR System

+ For more details, visit ftprotech.in +
+ """ + + employee_tables_html = "" + if employee_data: + for emp_name, records in employee_data.items(): + rows = "" + total = 0 + for line in records: + total += float(line['hours']) if line['hours'] != False else 0 + rows += f""" + + {line['date']} + {line['in']} + {line['out']} + {line['hours']} + + """ + t =f""" + + + Total worked Hours {total:.2f} + + + """ + table = f""" +
+

{emp_name}

+ + + + + + + + + + {rows}{t} + +
DateCheck InCheck OutHours Worked
+
+ """ + employee_tables_html += table + else: + employee_tables_html = "

No attendance data available for this period.

" + + # Final HTML body + body_html = html_template % { + 'from_date': last_monday.strftime('%Y-%m-%d'), + 'to_date': last_sunday.strftime('%Y-%m-%d'), + 'employee_tables': employee_tables_html + } + + # Send email to all management emails + for email in management_emails: + self.env['mail.mail'].create({ + 'email_to': email, + 'subject': f"Weekly Attendance Report: {last_monday} to {last_sunday}", + 'body_html': body_html, + }).send() diff --git a/addons_extensions/weekly_attendance_report/security/ir.model.access.csv b/addons_extensions/weekly_attendance_report/security/ir.model.access.csv new file mode 100644 index 000000000..44a7ad01c --- /dev/null +++ b/addons_extensions/weekly_attendance_report/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id,perm_read,perm_write,perm_create,perm_unlink +access_attendance_weekly_report,access_attendance_weekly_report,model_attendance_weekly_report,,1,1,1,1 diff --git a/third_party_addons/hr_biometric_attendance/models/biometric_device_details.py b/third_party_addons/hr_biometric_attendance/models/biometric_device_details.py index 2629846cd..b42d201e5 100644 --- a/third_party_addons/hr_biometric_attendance/models/biometric_device_details.py +++ b/third_party_addons/hr_biometric_attendance/models/biometric_device_details.py @@ -79,8 +79,6 @@ class BiometricDeviceDetails(models.Model): device_password = fields.Integer(string='Password', help='Enter the device password') - - def device_connect(self, zk): """Function for connecting the device with Odoo""" try: @@ -172,39 +170,40 @@ class BiometricDeviceDetails(models.Model): if conn: conn.disable_device() self.get_all_users() + # self.action_set_timezone() user = conn.get_users() - # fingers = conn.get_templates() - # for use in user: - # for finger in fingers: - # if finger.uid == use.uid: - # templates = conn.get_user_template(uid=use.uid, - # temp_id=finger.fid, - # user_id=use.user_id) - # hex_data = templates.template.hex() - # # Convert hex data to binary - # binary_data = binascii.unhexlify(hex_data) - # base64_data = base64.b64encode(binary_data).decode( - # 'utf-8') - # employee = self.env['hr.employee'].search( - # [('device_id_num', '=', use.user_id),('company_id', '=', self.env.company.id)],limit=1) - # employee.device_ids |= self - # if str(finger.fid) in employee.fingerprint_ids.mapped( - # 'finger_id'): - # employee.fingerprint_ids.search( - # [('finger_id', '=', finger.fid)]).update({ - # 'finger_template': base64_data, - # }) - # else: - # employee.fingerprint_ids.create({ - # 'finger_template': base64_data, - # 'finger_id': finger.fid, - # 'employee_bio_id': employee.id, - # 'filename': f'{employee.name}-finger-{finger.fid}' - # }) + # get All Fingerprints + fingers = conn.get_templates() + for use in user: + for finger in fingers: + if finger.uid == use.uid: + templates = conn.get_user_template(uid=use.uid, + temp_id=finger.fid, + user_id=use.user_id) + hex_data = templates.template.hex() + # Convert hex data to binary + binary_data = binascii.unhexlify(hex_data) + base64_data = base64.b64encode(binary_data).decode( + 'utf-8') + employee = self.env['hr.employee'].search( + [('device_id_num', '=', use.user_id), ('company_id', '=', self.env.company.id)], + limit=1) + employee.device_ids |= self + if str(finger.fid) in employee.fingerprint_ids.mapped( + 'finger_id'): + employee.fingerprint_ids.search( + [('finger_id', '=', finger.fid)]).update({ + 'finger_template': base64_data, + }) + else: + employee.fingerprint_ids.create({ + 'finger_template': base64_data, + 'finger_id': finger.fid, + 'employee_bio_id': employee.id, + 'filename': f'{employee.name}-finger-{finger.fid}' + }) # get all attendances - print(help(zk.get_attendance)) attendance = conn.get_attendance() - print(attendance) if attendance: filtered_attendance = [] @@ -240,50 +239,54 @@ class BiometricDeviceDetails(models.Model): for uid in user: if uid.user_id == each.user_id: - employee = self.env['hr.employee'].search([ - ('device_id_num', '=', each.user_id), - ('company_id', '=', self.env.company.id) - ], limit=1) + get_user_id = self.env['hr.employee'].search( + [('device_id_num', '=', each.user_id), ('company_id', '=', self.env.company.id)], + limit=1) + check_in_today = hr_attendance.search([( + 'employee_id', '=', get_user_id.id), + ('check_in', '!=', False), ('check_out', '=', False)]) + from datetime import timedelta - if not employee: - continue + # Define the tolerance (10 minutes) + tolerance = timedelta(minutes=10) - # Convert atten_time to datetime + # Convert the atten_time string to a datetime object + + # Calculate the lower and upper bounds with the tolerance atten_time_obj = datetime.datetime.strptime(atten_time, "%Y-%m-%d %H:%M:%S") - # Find existing attendance for today - start_of_day = atten_time_obj.replace(hour=0, minute=0, second=0, microsecond=0) - end_of_day = atten_time_obj.replace(hour=23, minute=59, second=59, microsecond=999999) + # Calculate the lower and upper bounds with the tolerance + lower_bound = atten_time_obj - tolerance + upper_bound = atten_time_obj + tolerance - attendance_today = self.env['hr.attendance'].search([ - ('employee_id', '=', employee.id), - ('check_in', '>=', start_of_day), - ('check_in', '<=', end_of_day) - ], limit=1) + # Ensure the 'check_in' and 'check_out' fields are datetime objects and compare them + next_in = hr_attendance.search([ + ('employee_id', '=', get_user_id.id), + ('check_in', '>=', lower_bound), + ('check_in', '<=', upper_bound) + ]) - # IN logic - if self.display_name == 'IN': - if not attendance_today: - # No attendance yet, create new with check_in only - self.env['hr.attendance'].create({ - 'employee_id': employee.id, - 'check_in': atten_time_obj, + next_out = hr_attendance.search([ + ('employee_id', '=', get_user_id.id), + ('check_out', '>=', lower_bound), + ('check_out', '<=', upper_bound) + ]) + if get_user_id: + if self.display_name == 'IN' and not check_in_today: + if next_in: + continue + hr_attendance.create({ + 'employee_id': get_user_id.id, + 'check_in': atten_time, }) - employee.attendance_state = 'checked_in' - else: - attendance_today.check_out = False - employee.attendance_state = 'checked_in' - continue - - # OUT logic - elif self.display_name != 'IN': - if attendance_today: - # Only update checkout if it's not set or is earlier than current atten_time - if not attendance_today.check_out or attendance_today.check_out < atten_time_obj: - attendance_today.write({ - 'check_out': atten_time_obj, - }) - employee.attendance_state = 'checked_out' + get_user_id.attendance_state = 'checked_in' + elif check_in_today and self.display_name != 'IN': + if fields.Datetime.to_string(check_in_today.check_in) > atten_time or next_out: + continue + check_in_today.write({ + 'check_out': atten_time, + }) + get_user_id.attendance_state = 'checked_out' else: pass @@ -293,7 +296,7 @@ class BiometricDeviceDetails(models.Model): ('company_id', '=', self.env.company.id)]) if not duplicate_atten_ids: zk_attendance.create({ - 'employee_id': employee.id, + 'employee_id': get_user_id.id, 'device_id_num': each.user_id, 'attendance_type': str(1), 'punch_type': '0' if self.display_name == 'IN' else '1', @@ -478,13 +481,12 @@ class BiometricDeviceDetails(models.Model): if conn: try: admin_user_id = 9 # You can implement a method for this - conn.unlock(admin_user_id) # Unlock using the admin user ID + conn.unlock(admin_user_id) # Unlock using the admin user ID except Exception as e: raise UserError(_("Failed to unlock door: %s") % str(e)) else: raise UserError(_("Unable to establish a connection to the device.")) - def get_all_users(self): """Function to get all user's details""" for info in self: @@ -504,7 +506,7 @@ class BiometricDeviceDetails(models.Model): users = conn.get_users() for user in users: employee = self.env['hr.employee'].search( - [('device_id_num', '=', user.user_id) ]) + [('device_id_num', '=', user.user_id)]) if employee: pass # employee.write({ @@ -563,7 +565,7 @@ class BiometricDeviceDetails(models.Model): "group_id: %s\n" "user_id: %s\n" "Here is the debugging information:\n%s\n" - "Try Reqing the device") + "Try Restarting the device") % (candidate_uid, employee.name, privilege, password, group_id, str(candidate_uid), e)) conn.enable_device() @@ -697,7 +699,7 @@ class ZKBioAttendance(Thread): live attendance data. """ - def __init__(self, machine_ip, port_no,password, record): + def __init__(self, machine_ip, port_no, password, record): """Function to Initialize the thread""" Thread.__init__(self) self.machine_ip = machine_ip