Nookal

Nookal
import os
from dotenv import load_dotenv
import requests

# Load environment variables from the .env file
load_dotenv()

# Retrieve environment variables
nookal_id = os.getenv('NOOKAL_ID')
nookal_email = os.getenv('NOOKAL_EMAIL')
nookal_password = os.getenv('NOOKAL_PASSWORD')
nookal_api_key = os.getenv('NOOKAL_API_KEY')

# Print the variables to ensure they are loaded correctly (optional, remove in production)
print(f"NOOKAL_ID: {nookal_id}")
print(f"NOOKAL_EMAIL: {nookal_email}")
print(f"NOOKAL_PASSWORD: {nookal_password}")
print(f"NOOKAL_API_KEY: {nookal_api_key}")
NOOKAL_ID: RMETRICS
NOOKAL_EMAIL: bthekkel1@gmail.com
NOOKAL_PASSWORD: Rmetrics
NOOKAL_API_KEY: F626E0eb-2aF8-Ef4B-be21-7C4CE3DAe3c
import os
from dotenv import load_dotenv

print(load_dotenv())

api_key = os.getenv('API_KEY')
True
api_key

API key from Nookal used in .env file

Base url : ‘https://api.nookal.com/production/v2’

Parameters Needed

API key needed in params

import requests

BASE_URL = 'https://api.nookal.com/production/v2'

params = {
    'api_key': nookal_api_key
}

headers = {
    'Content-Type': 'application/json'
}
verify = f'{BASE_URL}/verify'

verify, headers
('https://api.nookal.com/production/v2/verify',
 {'Content-Type': 'application/json'})
response = requests.get(verify, headers=headers, params=params)

print(response.status_code)
response.json()
200
{'status': 'success',
 'data': {'api_call': 'verify',
  'results': {'verify': True,
   'accountID': 47731,
   'apiUrl': 'https://api.nookal.com/'}},
 'details': {'totalItems': 1, 'currentItems': 1},
 'settings': {'currentPage': 1, 'nextPage': None, 'pageLength': 1}}

Example API Classs

import requests

class NookalAPI:
    def __init__(self, api_key, base='https://api.nookal.com/production/v2/'):
        self.api_key = api_key
        self.base = base
        self.params = {
            'api_key': self.api_key
        }
        self.headers = {
            'Content-Type': 'application/json'
        }

    def make_request(self, query_type='verify', method='GET', data=None):
        url = f'{self.base}{query_type}'
        if method == 'GET':
            response = requests.get(url, headers=self.headers, params=self.params)
        elif method == 'POST':
            response = requests.post(url, headers=self.headers, params=self.params, json=data)
        else:
            raise ValueError(f'Unsupported HTTP method: {method}')
        
        if response.status_code == 200:
            return response.json()
        else:
            print(f'Error: {response.status_code}')
            return None

    def verify(self):
        return self.make_request('verify')

    def __str__(self):
        return f'str: {self.verify()}'

    def __repr__(self):
        return f'repr: {self.verify()}'
    
    def __call__(self):
        return f"call: {self.verify()}"

    def get_locations(self):
        """Retrieve all locations."""
        return self.make_request('getLocations')
        
    def get_practitioners(self):
        return self.make_request('getPractitioners')

    def get_patients(self):
        return self.make_request('getPatients')
        
    def get_appointments(self):
        return self.make_request('getAppointments')

    def add_location(self, data):
        """Add a new location."""
        return self.make_request('addLocation', method='POST', data=data)

    def add_practitioner(self, data):
        """Add a new practitioner."""
        return self.make_request('addPractitioner', method='POST', data=data)

    def add_appointment(self, data):
        """Create a new appointment."""
        return self.make_request('addAppointment', method='POST', data=data)

    def add_patient(self, data):
        """Add a new patient."""
        return self.make_request('addPatient', method='POST', data=data)

# Example usage:
# api = NookalAPI(api_key='your_api_key_here')
# practitioners = api.get_practitioners()
# print(practitioners)
nookal = NookalAPI(nookal_api_key)
nookal.verify()
{'status': 'success',
 'data': {'api_call': 'verify',
  'results': {'verify': True,
   'accountID': 47731,
   'apiUrl': 'https://api.nookal.com/'}},
 'details': {'totalItems': 1, 'currentItems': 1},
 'settings': {'currentPage': 1, 'nextPage': None, 'pageLength': 1}}
nookal.get_appointments()
nookal.get_practitioners()
{'status': 'success',
 'data': {'api_call': 'getPractitioners',
  'results': {'practitioners': [{'ID': '1',
     'FirstName': 'Benedict',
     'LastName': 'Thekkel',
     'Speciality': 'Practitioner',
     'ShowInDiary': '1',
     'status': '1',
     'Title': '',
     'Email': 'bthekkel1@gmail.com',
     'locations': ['1', '2']},
    {'ID': '2',
     'FirstName': 'Brett',
     'LastName': 'Beeson',
     'Speciality': 'Practitioner',
     'ShowInDiary': '1',
     'status': '1',
     'Title': 'Mr',
     'Email': 'brettbeeson@fastmail.com',
     'locations': ['1', '2']}]}},
 'details': {'totalItems': 2, 'currentItems': 2},
 'settings': {'currentPage': 1, 'nextPage': None, 'pageLength': 2}}
nookal.get_appointments()
{'status': 'success',
 'data': {'api_call': 'getAppointments',
  'results': {'appointments': [{'ID': '4',
     'patientID': '2',
     'appointmentDate': '2024-09-01',
     'appointmentStartTime': '14:00:00',
     'appointmentEndTime': '15:00:00',
     'locationID': '1',
     'appointmentType': 'Consultation',
     'appointmentTypeID': '3',
     'practitionerID': '1',
     'emailReminderSent': '0',
     'status': 'Pending',
     'arrived': '0',
     'DNA': '0',
     'cancelled': '0',
     'cancellationDate': None,
     'Notes': None,
     'lastModified': '2024-08-30 03:53:13',
     'dateCreated': '2024-08-30 03:53:13',
     'invoiceGenerated': '0',
     'TimeZone': 'Australia/Sydney',
     'caseID': '2',
     'PaymentMethod': None,
     'passID': None,
     'appointmentStartDateTimeUTC': '2024-09-01 04:00:00',
     'appointmentEndDateTimeUTC': '2024-09-01 05:00:00'},
    {'ID': '3',
     'patientID': '1',
     'appointmentDate': '2024-08-31',
     'appointmentStartTime': '14:00:00',
     'appointmentEndTime': '15:00:00',
     'locationID': '1',
     'appointmentType': 'Consultation',
     'appointmentTypeID': '3',
     'practitionerID': '1',
     'emailReminderSent': '0',
     'status': 'Pending',
     'arrived': '0',
     'DNA': '0',
     'cancelled': '0',
     'cancellationDate': None,
     'Notes': None,
     'lastModified': '2024-08-30 03:52:57',
     'dateCreated': '2024-08-30 03:52:58',
     'invoiceGenerated': '0',
     'TimeZone': 'Australia/Sydney',
     'caseID': '1',
     'PaymentMethod': None,
     'passID': None,
     'appointmentStartDateTimeUTC': '2024-08-31 04:00:00',
     'appointmentEndDateTimeUTC': '2024-08-31 05:00:00'},
    {'ID': '2',
     'patientID': '1',
     'appointmentDate': '2024-08-29',
     'appointmentStartTime': '10:00:00',
     'appointmentEndTime': '11:00:00',
     'locationID': '1',
     'appointmentType': 'Consultation',
     'appointmentTypeID': '3',
     'practitionerID': '2',
     'emailReminderSent': '0',
     'status': 'Pending',
     'arrived': '0',
     'DNA': '0',
     'cancelled': '0',
     'cancellationDate': None,
     'Notes': None,
     'lastModified': '2024-08-30 03:52:31',
     'dateCreated': '2024-08-30 03:52:30',
     'invoiceGenerated': '0',
     'TimeZone': 'Australia/Sydney',
     'caseID': '1',
     'PaymentMethod': None,
     'passID': None,
     'appointmentStartDateTimeUTC': '2024-08-29 00:00:00',
     'appointmentEndDateTimeUTC': '2024-08-29 01:00:00'},
    {'ID': '5',
     'patientID': '1',
     'appointmentDate': '2024-08-30',
     'appointmentStartTime': '14:15:00',
     'appointmentEndTime': '14:45:00',
     'locationID': '1',
     'appointmentType': 'Class',
     'appointmentTypeID': '1',
     'practitionerID': '1',
     'emailReminderSent': '0',
     'status': 'Pending',
     'arrived': None,
     'DNA': '0',
     'cancelled': '0',
     'cancellationDate': None,
     'Notes': None,
     'lastModified': '2024-08-30 03:58:37',
     'dateCreated': '2024-08-30 03:58:21',
     'invoiceGenerated': '0',
     'TimeZone': 'Australia/Sydney',
     'caseID': None,
     'PaymentMethod': None,
     'participantDateAdded': '2024-08-30 13:58:37',
     'participantLastModified': '2024-08-30 13:58:37',
     'appointmentStartDateTimeUTC': '2024-08-30 04:15:00',
     'appointmentEndDateTimeUTC': '2024-08-30 04:45:00'}]}},
 'details': {'totalItems': 4, 'currentItems': 4},
 'settings': {'currentPage': 1, 'nextPage': None, 'pageLength': 100}}
data = {
    'first_name': 'first_name',
    'last_name': 'last_name',
    'email': 'email',
    'phone': 'phone'
}
nookal.add_practitioner(data)
Error: 404
data = {
    'practitioner_id': 'practitioner_id',
    'client_id': 'client_id',
    'date': 'date',
    'time': 'time',
    'duration': 'duration',
    'notes': 'notes'
}
nookal.add_appointment(data)
Error: 404
import os
from dotenv import load_dotenv

print(load_dotenv())
True
class Nookaltest():
    def __init__(self, *args, **kwargs):
        self.base_url = 'https://api.nookal.com/production/v2/'
        self.crm_key = os.getenv('CRM_KEY')

    def make_request(self, endpoint, method='GET', data=None):
        """
        Generic method to make requests to the Nookal API
        """
        url = f"{self.base_url}{endpoint}"
        headers = {
            'Content-Type': 'application/json',
        }
        params = {'api_key': self.crm_key} if method == 'GET' else {}
        response = requests.request(method, url, headers=headers, params=params, json=data)
        response.raise_for_status()  # This will raise an exception for non-200 responses
        return response.json()
    def verify(self):
        """
        Verify the API key's validity by making a test request to the Nookal API.
        """
        try:
            response = self.make_request('verify')
            return {'status': 'success', 'message': 'API key is valid.', 'response': f'{response}'}
        except requests.exceptions.HTTPError as e:
            return {'status': 'error', 'message': str(e)}

    def sync_locations(self):
        """
        Synchronize locations from Nookal to the local Location model.
        """
        locations_data = self.make_request('getLocations')
        locations = locations_data.get('data', {}).get('results', {}).get('locations', [])
        for i, location in enumerate(locations):
            keys = location.keys()
            full_address = ', '.join(filter(None, [
                    location.get('AddressLine1', ''),
                    location.get('AddressLine2', ''),
                    location.get('AddressLine3', ''),
                    location.get('City', ''),
                    location.get('State', ''),
                    location.get('Postcode', ''),
                    location.get('Country', ''),
            ])).strip(', ')
                        
            print({'address': full_address, 'phone_number': location.get('Telephone',''), 'email': location.get('Email', '')})
        return keys


    def sync_practitioners(self):
        """
        Synchronize practitioners from Nookal to the local Practitioner model.
        """
        practitioners_data = self.make_request('getPractitioners')
        practitioners = practitioners_data.get('data', {}).get('results', {}).get('practitioners', [])
        for practitioner in practitioners:
            keys = practitioner.keys()
            # print(practitioner)

        return keys
    


    def sync_clients(self):
        """
        Synchronize clients from Nookal to the local Client model.
        """
        clients_data = self.make_request('getPatients')
        clients = clients_data.get('data', {}).get('results', {}).get('patients', [])
        for client in clients:
            keys = client.keys()
        #     Client.objects.update_or_create(
        #         email=client['email'],
        #         defaults={
        #             'first_name': client['first_name'],
        #             'last_name': client['last_name'],
        #             'phone_number': client.get('phone_number', ''),
        #             'address': client.get('address', ''),
        #             'dob': client.get('dob'),  # Assumed correct format
        #             'sex': client.get('sex', 'O'),  # Default to 'Other'
        #             'crm_id': client.get('id')
        #         }
        #     )
        # self.synced_at = timezone.now()
        # self.save()
        return clients

    def sync_appointments(self):
        """
        Synchronize appointments from Nookal to the local Appointment model.
        """
        appointments_data = self.make_request('getAppointments')
        if appointments_data.get('status') == 'success' and 'results' in appointments_data['data']:
            appointments = appointments_data['data']['results']['appointments']

            for appointment_data in appointments:
                keys = appointment_data.keys()
                # client = Client.objects.get(email=appointment['client_email'])
                # practitioner = Practitioner.objects.get(user__email=appointment['practitioner_email'])
                # Appointment.objects.update_or_create(
                #     client=client,
                #     practitioner=practitioner,
                #     date=appointment['date'],
                #     defaults={
                #         'reason': appointment.get('reason', '')
                #     }
                # )
            # self.synced_at = timezone.now()
            # self.save()
            # return keys
            return keys
new = Nookaltest()
new.verify()
{'status': 'success',
 'message': 'API key is valid.',
 'response': "{'status': 'success', 'data': {'api_call': 'verify', 'results': {'verify': True, 'accountID': 47731, 'apiUrl': 'https://api.nookal.com/'}}, 'details': {'totalItems': 1, 'currentItems': 1}, 'settings': {'currentPage': 1, 'nextPage': None, 'pageLength': 1}}"}
new.sync_locations()
{'address': '12, 0, Australia', 'phone_number': '0415051025', 'email': 'bthekkel1@gmail.com'}
{'address': '113, 0, Australia', 'phone_number': '041243123', 'email': 'NewLocation1@gmail.com'}
dict_keys(['ID', 'Name', 'AddressLine1', 'AddressLine2', 'AddressLine3', 'City', 'State', 'Telephone', 'Email', 'Website', 'Postcode', 'Timezone', 'lockNewPatientsToLocation', 'countryID', 'Country'])
new.sync_practitioners()
dict_keys(['ID', 'FirstName', 'LastName', 'Speciality', 'ShowInDiary', 'status', 'Title', 'Email', 'locations'])
new.sync_clients()
[{'ID': '1',
  'Title': '',
  'FirstName': 'Client',
  'MiddleName': '',
  'Nickname': '',
  'LastName': 'Number1',
  'Notes': '',
  'Alerts': '',
  'DOB': None,
  'Gender': '',
  'DateCreated': '2024-08-30 03:50:19',
  'DateModified': '2024-09-06 05:44:54',
  'RegistrationDate': '2024-08-30 13:50:19',
  'onlineQuickCode': 'JPUHV9',
  'consent': {'reminder': {'sms': 0, 'email': 0},
   'marketing': {'sms': 0, 'email': 0},
   'integration': {'sms': 0, 'email': 0},
   'parent': '',
   'history': []},
  'Occupation': '',
  'Employer': '',
  'category': '',
  'LocationID': '0',
  'allowOnlineBookings': '1',
  'HealthFundData': None,
  'PrivateHealthType': None,
  'PrivateHealthNo': None,
  'PensionNo': None,
  'allergies': None,
  'active': '1',
  'deceased': '0',
  'NextOfKin': None,
  'Doctor': '',
  'Email': 'clientnumber1@gmail.com',
  'Mobile': '',
  'Home': '40515405',
  'Work': '5405050',
  'Fax': '',
  'Addr1': '',
  'Addr2': '',
  'Addr3': '',
  'City': '',
  'Country': 'Australia',
  'State': '',
  'Postcode': '',
  'Postal_Addr1': '',
  'Postal_Addr2': '',
  'Postal_Addr3': '',
  'Postal_City': '',
  'Postal_Country': '',
  'Postal_State': '',
  'Postal_Postcode': ''},
 {'ID': '2',
  'Title': '',
  'FirstName': 'client',
  'MiddleName': '',
  'Nickname': '',
  'LastName': 'Number2',
  'Notes': None,
  'Alerts': None,
  'DOB': None,
  'Gender': None,
  'DateCreated': '2024-08-30 03:51:04',
  'DateModified': '2024-08-30 03:51:03',
  'RegistrationDate': '2024-08-30 13:51:04',
  'onlineQuickCode': 'QHZG',
  'consent': {'reminder': {'sms': 0, 'email': 0},
   'marketing': {'sms': 0, 'email': 0},
   'integration': {'sms': 0, 'email': 0},
   'parent': '',
   'history': []},
  'Occupation': None,
  'Employer': None,
  'category': None,
  'LocationID': '0',
  'allowOnlineBookings': '1',
  'HealthFundData': None,
  'PrivateHealthType': None,
  'PrivateHealthNo': None,
  'PensionNo': None,
  'allergies': None,
  'active': '1',
  'deceased': '0',
  'NextOfKin': None,
  'Doctor': '',
  'Email': None,
  'Mobile': '04504545',
  'Home': '05454545',
  'Work': '0545454',
  'Fax': None,
  'Addr1': '',
  'Addr2': '',
  'Addr3': '',
  'City': '',
  'Country': 'Australia',
  'State': '',
  'Postcode': '',
  'Postal_Addr1': '',
  'Postal_Addr2': '',
  'Postal_Addr3': '',
  'Postal_City': '',
  'Postal_Country': '',
  'Postal_State': '',
  'Postal_Postcode': ''}]
new.sync_appointments()
dict_keys(['ID', 'patientID', 'appointmentDate', 'appointmentStartTime', 'appointmentEndTime', 'locationID', 'appointmentType', 'appointmentTypeID', 'practitionerID', 'emailReminderSent', 'status', 'arrived', 'DNA', 'cancelled', 'cancellationDate', 'Notes', 'lastModified', 'dateCreated', 'invoiceGenerated', 'TimeZone', 'caseID', 'PaymentMethod', 'participantDateAdded', 'participantLastModified', 'appointmentStartDateTimeUTC', 'appointmentEndDateTimeUTC'])
Back to top