Skip to content

Commit

Permalink
Merge pull request #69 from Rutvik-Trivedi/master
Browse files Browse the repository at this point in the history
Added Senseloaf Resume Parser Provider
  • Loading branch information
coscialp authored Nov 7, 2023
2 parents 77ae6f6 + fa38317 commit 6ea0a46
Show file tree
Hide file tree
Showing 11 changed files with 1,052 additions and 0 deletions.
7 changes: 7 additions & 0 deletions edenai_apis/api_keys/senseloaf_settings_template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"api_key": "",
"email": "",
"password": "",
"comment": "You can either set your API Key, or your Login Credentials (Email and Password) for Authentication"
}

1 change: 1 addition & 0 deletions edenai_apis/apis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,6 @@
from .winstonai import WinstonaiApi
from .vernai import VernaiApi
from .readyredact import ReadyRedactApi
from .senseloaf import SenseloafApi

# THIS NEEDS TO BE DONE AUTOMATICALLY
1 change: 1 addition & 0 deletions edenai_apis/apis/senseloaf/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .senseloaf_api import SenseloafApi
352 changes: 352 additions & 0 deletions edenai_apis/apis/senseloaf/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,352 @@
from edenai_apis.utils.http import HTTPMethod
from edenai_apis.utils.exception import ProviderException
import requests
from enum import Enum
from typing import Optional
from http import HTTPStatus
import json
from json import JSONDecodeError
import tempfile, os, mimetypes

from .models import ResponseData

class Parser(Enum):

RESUME = 'resume'
JD = 'job_description'
INVOICE = 'invoice' # Coming Soon
RECEIPT = 'receipt' # Coming Soon
INDOID = 'indonesian_id' # Coming Soon
AADHAAR = 'aadhaar' # Coming Soon



class Client:

BASE_URL = 'https://service.senseloaf.com'
__api_key: Optional[str]
__last_api_response: Optional[dict]
__last_api_response_type: Optional[str]
__last_api_response_code: Optional[str]

def __init__(
self,
api_key: Optional[str] = None,
email: Optional[str] = None,
password: Optional[str] = None
) -> None:
if all([i == '' for i in [api_key, email, password]]):
raise ProviderException('Please provide api_key or email and password for authentication')
if api_key == '' and (email == '' or password == ''):
raise ProviderException('Please provide both email and password for authentication')
if api_key:
self.__api_key = api_key.replace('Bearer ', '')
else:
self.__login(email, password)


def __request(
self,
method: HTTPMethod,
url: str,
data: Optional[dict] = None,
headers: Optional[dict] = None,
json_field: Optional[dict] = None,
files: Optional[dict] = None,
params: Optional[dict] = None,
return_type: Optional[str] = 'json'
) -> ResponseData:
response: requests.Response = requests.request(
method=method.value,
url=url,
data=data,
params=params,
files=files,
headers=headers,
json=json_field,
)

try:
response.raise_for_status()

if response.status_code == HTTPStatus.NO_CONTENT:
return ResponseData(
response={},
response_code=str(response.status_code),
response_type='json'
)

if return_type == 'headers':
self.__last_api_response = response.headers
self.__last_api_response_type = 'headers'
self.__last_api_response_code = str(response.status_code)
return ResponseData(
response=response.headers,
response_code=str(response.status_code),
response_type='headers'
)
elif return_type == 'content':
self.__last_api_response = response.content
self.__last_api_response_type = 'content'
self.__last_api_response_code = str(response.status_code)
return ResponseData(
response={'content': response.content},
response_code=str(response.status_code),
response_type='content'
)
elif return_type == 'text':
self.__last_api_response = response.text
self.__last_api_response_type = 'text'
self.__last_api_response_code = str(response.status_code)
return ResponseData(
response={'text': response.text},
response_code=str(response.status_code),
response_type='text'
)
else:
self.__last_api_response = response.json()
self.__last_api_response_type = 'json'
self.__last_api_response_code = str(response.status_code)
return ResponseData(
response=response.json(),
response_code=str(response.status_code),
response_type='json'
)
except requests.exceptions.HTTPError as exc:
message = json.loads(exc.response.text)
if message.get('errorCode') == 'AUTHENTICATION_FAILED':
message['errorMessage'] = 'Authentication Failed. Please check your EmailID/Password Combination or API Key'
message['response']['error']['faultDetail'] = ['Authentication Failed. Please check your EmailID/Password Combination or API Key']
else:
message = exc.response.text
raise ProviderException(
message=f"{exc}\nError message: {message}",
code=str(response.status_code),
) from exc
except JSONDecodeError:
raise ProviderException(
message="Internal server error", code=str(response.status_code)
)


def __login(self, email, password):
url = f"{self.BASE_URL}/login"
headers = {
"Content-Type": "application/json",
}
payload = json.dumps({
"emailId": email,
"password": password
})
response = self.__request(
method=HTTPMethod.POST,
url=url,
data=payload,
headers=headers,
json_field=None,
files=None,
params=None,
return_type='headers'
)
self.__api_key = response.response.get('Authorization').replace("Bearer ", '')


def __parse_resume_from_file(
self,
file: str
) -> ResponseData:
url = f'{self.BASE_URL}/api/v2/parse-resume'
files = [
('files',(file.split('/')[-1], open(file,'rb'),mimetypes.guess_type(file)[0]))
]
headers = {
'Authorization': f'Bearer {self.__api_key}'
}
response = self.__request(
method=HTTPMethod.POST,
url=url,
data=None,
headers=headers,
json_field=None,
files=files,
params=None,
return_type='json'
)
if response.response_code != '200':
raise ProviderException(
message=response.response,
code=response.response_code
)

else:
errors = response.response.get('errors', [])
if len(errors) > 0:
raise ProviderException(
message=errors[0],
code=response.response_code
)
else:
return response


def __parse_resume_from_url(
self,
url: str
) -> ResponseData:
parser_url = f'{self.BASE_URL}/api/v2/parse-resume-url'
data = json.dumps({
'resumeUrl': url
})
headers = {
'Authorization': f'Bearer {self.__api_key}'
}
response = self.__request(
method=HTTPMethod.POST,
url=parser_url,
data=data,
headers=headers,
json_field=None,
files=None,
params=None,
return_type='json'
)
if str(response.response_code) != 200:
raise ProviderException(
message=response.response.get('message'),
code=response.response_code
)

else:
errors = response.response.get('errors', [])
if len(errors) > 0:
raise ProviderException(
message=errors[0].get('message'),
code=errors[0].get('code')
)
else:
return response


def __parse_jd_from_file(
self,
file: str
) -> ResponseData:
url = f'{self.BASE_URL}/api/parse-jd'
files = [
('files',(file, open(file,'rb'),'application/pdf'))
]
headers = {
'Authorization': f'Bearer {self.__api_key}'
}
response = self.__request(
method=HTTPMethod.POST,
url=url,
data=None,
headers=headers,
json_field=None,
files=files,
params=None,
return_type='json'
)
if str(response.response_code) != 200:
raise ProviderException(
message=response.response.get('message'),
code=response.response_code
)
else:
errors = response.response.get('errors', [])
if len(errors) > 0:
raise ProviderException(
message=errors[0].get('message'),
code=errors[0].get('code')
)
else:
return response


def __parse_jd_from_url(
self,
url: str
) -> ResponseData:
tempdir = tempfile.gettempdir()
filename = url.split('/')[-1]
filepath = os.path.join(tempdir, filename)
with open(filepath, 'wb') as f:
f.write(requests.get(url).content)
return self.__parse_jd_from_file(filepath)


def __parse_resume(
self,
file: Optional[str] = '',
url: Optional[str] = ''
) -> ResponseData:
if file == '' and url == '':
raise ProviderException('Please provide path to file or url for parsing resume')
elif file != '' and url != '':
raise ProviderException('Please provide file or url for parsing resume, not both')
elif file != '':
return self.__parse_resume_from_file(file)
elif url != '':
return self.__parse_resume_from_url(url)



def __parse_jd(
self,
file: Optional[str] = None,
url: Optional[str] = None
):
if file == '' and url == '':
raise ProviderException('Please provide path to file or url for parsing resume')
elif file != '' and url != '':
raise ProviderException('Please provide file or url for parsing resume, not both')
elif file != '':
return self.__parse_jd_from_file(file)
elif url != '':
return self.__parse_jd_from_url(url)


def parse_document(
self,
parse_type: Parser,
file: Optional[str] = '',
url: Optional[str] = ''
) -> ResponseData:
if parse_type.value == 'resume':
return self.__parse_resume(file, url)
elif parse_type.value == 'job_description':
return self.__parse_jd(file, url)
elif parse_type.value == 'invoice':
raise NotImplementedError(
'Invoice parsing is not implemented yet. Reach out to us at team@senseloaf.com for requesting early release'
)
elif parse_type.value == 'receipt':
raise NotImplementedError(
'Receipt parsing is not implemented yet. Reach out to us at team@senseloaf.com for requesting early release'
)
elif parse_type.value == 'indonesian_id':
raise NotImplementedError(
'Indonesian ID card parsing is not implemented yet. Reach out to us at team@senseloaf.com for requesting early release'
)
elif parse_type.value == 'aadhaar':
raise NotImplementedError(
'Indian Aadhaar card parsing is not implemented yet. Reach out to us at team@senseloaf.com for requesting early release'
)
else:
raise NotImplementedError(
f'Parsing type {parse_type} is not implemented yet. Reach out to us at team@senseloaf.com for requesting early release'
)

@property
def cache(self):
return ResponseData(
response = self.__last_api_response,
response_code = self.__last_api_response_code,
response_type = self.__last_api_response_type
)

def clear_cache(self):
self.__last_api_response = {}
self.__last_api_response_code = ''
self.__last_api_response_type = ''
8 changes: 8 additions & 0 deletions edenai_apis/apis/senseloaf/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from edenai_apis.utils.exception import (
ProviderErrorLists,
ProviderInternalServerError
)

ERRORS: ProviderErrorLists = {
ProviderInternalServerError : [r".*Internal server error.*"],
}
Loading

0 comments on commit 6ea0a46

Please sign in to comment.