def __init__(self, cust_id, cust_name, alt_cust_id, user_name, password, office_code, app_name, api_url, back_days=2, forward_days=7):
     # Constructor
     self.customer_id = cust_id
     self.customer_name = cust_name
     self.alt_customer_id = alt_cust_id
     self.back_days = abs(back_days)
     self.forward_days = abs(forward_days)
     self.api_client = AdvancedMDAPIWrapper(user_name, password, office_code, app_name, api_url)
     self.appointments_data = []
     self.serialized_appointments = []
class AdvancedMDCustomer(object):
    # Common shared data
    def __init__(self, cust_id, cust_name, alt_cust_id, user_name, password, office_code, app_name, api_url, back_days=2, forward_days=7):
        # Constructor
        self.customer_id = cust_id
        self.customer_name = cust_name
        self.alt_customer_id = alt_cust_id
        self.back_days = abs(back_days)
        self.forward_days = abs(forward_days)
        self.api_client = AdvancedMDAPIWrapper(user_name, password, office_code, app_name, api_url)
        self.appointments_data = []
        self.serialized_appointments = []
    
    def fetch_data(self):
        """Fetch (back_days + today + forward_days) data. Return True if success. IOError on login failure"""
        LOGGER.info('------------------------------')
        LOGGER.info('|        Fetch Data          |')
        LOGGER.info('------------------------------')
        start_date = datetime.today() - timedelta(days=self.back_days)
        no_of_days = self.back_days + self.forward_days + 1
        LOGGER.info('Fetching appointment data from: {0} to {1}'.format(start_date.strftime('%m/%d/%Y'),\
                                                                         (start_date + timedelta(days=no_of_days-1)).strftime('%m/%d/%Y')))
        LOGGER.debug('Sending AdvancedMD login request.')
        if (self.api_client.login()):
            self.appointments_data = []
            # Fetch seven days of data
            for delta in range(0, no_of_days):
                date = (start_date + timedelta(days=delta)).strftime('%m/%d/%Y')
                LOGGER.info('Fetching date appointments for: {0}'.format(date))
                xml_resp = self.api_client.get_date_visits(date)
                if xml_resp is None:
                    LOGGER.error('Date visit request failed. Date: {0}'.format(date))
                    continue
                
                # Validate response
                error = xml_resp.find('Error')
                if (error is not None):
                    desc = error.find('Fault/detail/description')
                    err_code = error.find('Fault/detail/code')
                    LOGGER.error('Get date visit, response error. Code: {0} - Description: {1}'.format(err_code.text, desc.text))
                    continue
                # Get all xml visit(appointment) elements
                day_appointments = xml_resp.findall('Results/visitlist/visit') 
                LOGGER.info('Appointment(s) fetched: %i', len(day_appointments))
                self.appointments_data = self.appointments_data + day_appointments
            LOGGER.info('Total appointments fetched: %i', len(self.appointments_data))
        else:
            raise IOError('Unable to login to AdvancedMD API')
        return True
        
    def serialize_data(self):
        """Serializes appointment data. Returns list of AppointmentData objects."""
        LOGGER.info('------------------------------')
        LOGGER.info('|      Serialize Data        |')
        LOGGER.info('------------------------------')
        
        self.serialized_appointments = []
        i = 1
        total = len(self.appointments_data)
        LOGGER.info('Serializing appointments data. Total count: %i', total)
        for appt in self.appointments_data:
            LOGGER.info('Serializing appointment (%i of %i)', i, total)
            i = i + 1
            appointment = AppointmentData()
            LOGGER.debug('\tAppointment Id: %s - Raw Data: \n%s', appt.get('altAppointmentId'), ET.tostring(appt))
            LOGGER.info('\tAlt appointment id: %s', appt.get('altAppointmentId'))
            
            # Customer ID
            appointment.customer_id = self.customer_id
            
            # ================
            # Appointment data
            # ================
            appointment.altAppointmentId = appt.get('altAppointmentId')
            appt_date_time = appt.get('apptDateTime')
            if (appt_date_time):
                date_time = datetime.strptime(appt_date_time, "%Y-%m-%dT%H:%M:%S") # Convert string(2012-09-26T10:00:00) to datetime object
                appointment.apptDate = date_time.strftime("%Y-%m-%d")
                appointment.apptTime = date_time.strftime("%Y-%m-%d %H:%M:%S")
            
            appointment.apptTypeName = appt.get('apptTypeName').title()
            #TODO: confirm if apptType="RefReason" can be used
            appointment.altApptTypeId = appt.get('apptTypeName').title()
            (status_id, conformation_id, appt_kept, reschdeuled) = AMDStatusLookup.get_appointment_status_info(appt.get('apptStatusId'))
            appointment.apptStatusId = status_id
            appointment.apptConformationId = conformation_id
            appointment.apptKept = appt_kept
            appointment.reschedule = reschdeuled
            
            # =============
            # Facility data
            # =============
            appointment.altFacilityId = appt.get('altFacilityId')
            appointment.facilityName = appt.get('facilityName').title()
            #TODO: Facility id is empty for Advanced MD, confirm
            appointment.facilityAddr = ""
            
            # ============
            # Patient data
            # ============
            appointment.altPatientId = appt.get('altPatientId')
            appt_patient = appt.find('patientlist/patient')
            appointment.fname = appt_patient.get('fname').title()
            appointment.lname = appt_patient.get('lname').title()
            if (is_email_valid(appt_patient.get('email'))): # Validate email

                appointment.email = appt_patient.get('email').lower()
            if (AMDStatusLookup.is_cell_phone(appt_patient.get('otherPhoneType'))):
                appointment.cellPhone = appt_patient.get('cellPhone')
                #TODO: Handle empty workPhone and homePhone
            appointment.homePhone = appt_patient.get('homePhone')
            appointment.workPhone = appt_patient.get('workPhone')
            appointment.workPhoneExt = appt_patient.get('workPhoneExt')
            appointment.address = (appt_patient.get('address1') + ' ' + appt_patient.get('address2')).title()
            appointment.city = appt_patient.get('city').title()
            appointment.state = appt_patient.get('state').upper()
            appointment.zip = appt_patient.get('zip')
            appointment.language = appt_patient.get('language').strip()
            if appointment.language in NULLS:
                appointment.language = "ENG"
            if(appt_patient.get('dateOfBirth')):
                dob = datetime.strptime(appt_patient.get('dateOfBirth'), "%Y-%m-%dT%H:%M:%S")
                appointment.dateOfBirth = dob.strftime("%Y-%m-%d")
             
            # ==============   
            # Guarantor data
            # ==============
            appointment.guarantorID = appt_patient.get('altGuarantorId')
            appointment.gFname = appt_patient.get('rpFname').title()
            appointment.gLname = appt_patient.get('rpLname').title()
            appointment.gAddress = (appt_patient.get('rpAddress1') + ' ' + appt_patient.get('rpAddress2')).title()
            appointment.gZip = appt_patient.get('rpZip')
            appointment.gCity = appt_patient.get('rpCity').title()
            appointment.gState = appt_patient.get('rpState').upper()
            appointment.gWorkPhone = appt_patient.get('rpWorkPhone')
            appointment.gHomePhone = appt_patient.get('rpHomePhone')
            if (AMDStatusLookup.is_cell_phone(appt_patient.get('rpOtherPhoneType'))):
                appointment.gCellPhone = appt_patient.get('rpCellPhone')
                #TODO: Handle empty workPhone and homePhone
            if (is_email_valid(appt_patient.get('rpEmail'))): # Validate email
                appointment.gEmail = appt_patient.get('rpEmail').lower()
            if(appt_patient.get('rpDob')): # "1945-11-14T00:00:00"
                dob = datetime.strptime(appt_patient.get('rpDob'), "%Y-%m-%dT%H:%M:%S")
                appointment.gDob = dob.strftime("%Y-%m-%d")
            
            # ===========
            # Doctor data
            # ===========
            appointment.altDoctorId = appt_patient.get('altDoctorId')
            appointment.doctorName = (appt_patient.get('drFname') + ' ' + appt_patient.get('drLname')).title()
            # Get doctor titled name
            appointment.doctorName = AMDStatusLookup.get_dr_titled_name(appt_patient.get('drTitle'), appointment.doctorName)
            
            appointment.normalize()
            LOGGER.debug('Appointment ID: %s, serialized result:\n%s', appt.get('altAppointmentId'), json.dumps(appointment.__dict__))
            self.serialized_appointments.append(appointment)
            
        return self.serialized_appointments
    
    def save_data(self):
        """One by one saves serialized appointments to database."""
        LOGGER.info('------------------------------')
        LOGGER.info('|         Save Data           |')
        LOGGER.info('------------------------------')
        rec_count = 1
        total = len(self.serialized_appointments)
        LOGGER.info('Saving appointments to database. Count: %i', total)        
        for appt in self.serialized_appointments:
            LOGGER.info('Saving appointment (%i of %i). Alt appointment id: %s', rec_count, total, appt.altAppointmentId)
            save_appointment(appt.__dict__)
            rec_count = rec_count + 1