This repository has been archived by the owner on Feb 12, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 65
Time off request API methods #57
Open
JosephvB-JBA
wants to merge
11
commits into
smeggingsmegger:master
Choose a base branch
from
JosephvB-JBA:jvb/time_off
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
926042a
wip(main): add 3 planned methods
JosephJvB f5f8d38
fix(main): fix get_time_off_request 400 error
JosephJvB 1213cc8
add(create timeoff req): api method works with xml, add get timeoff p…
JosephJvB 7843812
tidy(main): tidy PUT timeoff xml format
JosephJvB 880fda4
add(main): add API notes to methods
JosephJvB 70c897d
tidy(main): remove duplicate dict key, update util comment
JosephJvB fcb6cd2
docs(readme): add readme examples
JosephJvB 2bb7a56
fix(main): fix get_time_off_request date params, make meta timeoff qu…
JosephJvB 3a41456
test(timeoff): add test for timeoff requests and timeoff meta
JosephJvB 3c35bf2
test(timeoff): add tests for create timeoff request and update status
JosephJvB 8e2493c
add(docs): add API method description
JosephJvB File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -181,7 +181,6 @@ def __init__(self, subdomain='', api_key='', datatype='JSON', underscore_keys=Fa | |
"commisionDate": ("date", ""), | ||
"commissionAmount": ("currency", ""), | ||
"commissionComment": ("text", ""), | ||
"commissionComment": ("text", ""), | ||
"benefitClassDate": ("date", ""), | ||
"benefitClassClass": ("list", ""), | ||
"benefitClassChangeReason": ("list", ""), | ||
|
@@ -245,6 +244,35 @@ def _format_report_xml( | |
xml = '''<report output="{0}">\n\t<title>{1}</title>\n\t{2}<fields>\n{3}\t</fields>\n</report>'''.format(report_format, title, xml_filters, xml_fields) | ||
return xml | ||
|
||
def _format_time_off_xml(self, request_data): | ||
""" | ||
Utility method for turning dictionary of time_off request data into valid xml. | ||
https://www.bamboohr.com/api/documentation/time_off.php#addRequest | ||
@param request_data: Dictionary containing incoming data for time off request | ||
""" | ||
fields = ['status', 'start', 'end', 'timeOffTypeId', 'amount'] | ||
xml = '' | ||
for f in fields: | ||
value = request_data.get(f) | ||
if value: | ||
xml += '\n\t<{0}>{1}</{0}>'.format(f, value) | ||
|
||
if request_data.get('notes') and len(request_data.get('notes')) > 0: | ||
notes = '' | ||
for n in request_data['notes']: | ||
f = n.get('type', 'employee') | ||
t = n.get('text', '') | ||
notes += '\n\t\t<note from="{0}">{1}</note>'.format(f, t) | ||
xml += '\n\t<notes>{0}\n\t</notes>'.format(notes) | ||
|
||
if request_data.get('dates') and len(request_data.get('dates')) > 0: | ||
dates = '' | ||
for d in request_data['dates']: | ||
dates += '\n\t\t<date ymd="{0}" amount="{1}" />'.format(d['ymd'], d['amount']) | ||
xml += '\n\t<dates>{0}\n\t</dates>'.format(dates) | ||
|
||
return '<request>{0}\n</request>'.format(xml) | ||
|
||
def add_employee(self, employee): | ||
""" | ||
API method for creating a new employee from a dictionary. | ||
|
@@ -628,24 +656,105 @@ def get_whos_out(self, start_date=None, end_date=None): | |
# return utils.transform_whos_out(r.content) | ||
|
||
def get_time_off_requests(self, start_date=None, end_date=None, status=None, type=None, employee_id=None): | ||
""" | ||
API method for returning a list of time off requests | ||
https://www.bamboohr.com/api/documentation/time_off.php#getRequests | ||
Success response: 200 | ||
@return: List containing time off request dictionaries | ||
""" | ||
start_date = utils.resolve_date_argument(start_date) | ||
end_date = utils.resolve_date_argument(end_date) | ||
|
||
params = {} | ||
if start_date: | ||
params['start'] = start_date | ||
if end_date: | ||
params['end'] = end_date | ||
if status: | ||
params['status'] = status | ||
# Must set dates or request errors 400 Bad Request | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BambooHR's API says it is optional. 🤔
|
||
params['start']= start_date if start_date else '0000-01-01' | ||
params['end']= end_date if end_date else '9999-01-01' | ||
if type: | ||
params['type'] = type | ||
if employee_id: | ||
params['employeeId'] = employee_id | ||
|
||
r = self._query('time_off/requests', params, raw=True) | ||
return r.json() | ||
# return utils.transform_time_off(r.content) | ||
|
||
def get_time_off_policies(self): | ||
""" | ||
Api method for returning list of time off policies for a subdomain | ||
https://www.bamboohr.com/api/documentation/time_off.php#getTimeOffPolicies | ||
Success Response: 200 | ||
@return: List containing time off policies | ||
""" | ||
url = 'meta/time_off/policies' | ||
r = self._query(url, {}) | ||
return r | ||
|
||
def get_time_off_types(self): | ||
""" | ||
Api method for returning list of time off types for a subdomain | ||
https://www.bamboohr.com/api/documentation/time_off.php#getTimeOffTypes | ||
Success Response: 200 | ||
@return: List containing time off types | ||
""" | ||
url = 'meta/time_off/types' | ||
r = self._query(url, {}) | ||
return r['timeOffTypes'] | ||
|
||
def create_time_off_request(self, data, raw=False): | ||
""" | ||
API method for creating a new time off request | ||
https://www.bamboohr.com/api/documentation/time_off.php#addRequest | ||
Success Response: 201 | ||
@return: A dictionary containing the created time off request | ||
""" | ||
data['status'] = 'requested' | ||
return self.update_time_off_request(data, raw) | ||
|
||
def update_time_off_request(self, data, raw=False): | ||
""" | ||
API method for creating or updating a new time off request | ||
https://www.bamboohr.com/api/documentation/time_off.php#addRequest | ||
@param data = { | ||
'status': 'requested', | ||
'employee_id': '113', | ||
'start': '2040-02-01', | ||
'end': '2040-02-02', | ||
'timeOffTypeId': '78', | ||
'amount': '2', | ||
'dates': [ | ||
{ 'ymd': '2040-02-01', 'amount': '1' }, | ||
{ 'ymd': '2040-02-02', 'amount': '1' } | ||
], | ||
'notes': [ | ||
{ 'type': 'employee', 'text': 'Going overseas with family' }, | ||
{ 'type': 'manager', 'text': 'Enjoy!' } | ||
] | ||
} | ||
Success Response: 201 | ||
@return: A dictionary containing the created/updated time off request | ||
""" | ||
url = self.base_url + 'employees/{0}/time_off/request'.format(data.get('employee_id')) | ||
xml = self._format_time_off_xml(data) | ||
r = requests.put(url, timeout=self.timeout, headers=self.headers, auth=(self.api_key, ''), data=xml) | ||
r.raise_for_status() | ||
if raw: | ||
return r | ||
else: | ||
return r.json() | ||
|
||
def update_time_off_request_status(self, request_id, data, raw=False): | ||
""" | ||
https://www.bamboohr.com/api/documentation/time_off.php#changeRequest | ||
Success Response: 200 | ||
@param data = d = { | ||
'status': 'denied', | ||
'note': 'have fun!' | ||
} | ||
@return: Boolean of request success (Status Code == 200). | ||
""" | ||
url = self.base_url + 'time_off/requests/{0}/status'.format(request_id) | ||
r = requests.put(url, timeout=self.timeout, headers=self.headers, auth=(self.api_key, ''), json=data) | ||
r.raise_for_status() | ||
return True | ||
|
||
def get_meta_fields(self): | ||
""" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
"""Unittests for time off api | ||
""" | ||
|
||
import httpretty | ||
import os | ||
import sys | ||
import unittest | ||
|
||
from json import dumps | ||
from requests import HTTPError | ||
|
||
# Force parent directory onto path | ||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | ||
|
||
from PyBambooHR import PyBambooHR | ||
|
||
class test_time_off(unittest.TestCase): | ||
# Used to store the cached instance of PyBambooHR | ||
bamboo = None | ||
|
||
def setUp(self): | ||
if self.bamboo is None: | ||
self.bamboo = PyBambooHR(subdomain='test', api_key='testingnotrealapikey') | ||
|
||
@httpretty.activate | ||
def test_get_time_off_requests(self): | ||
body = [{"id": "1342", "employeeId": "4", "status": {"lastChanged": "2019-09-12", "lastChangedByUserId": "2369", "status": "approved"}, "name": "Charlotte Abbott", "start": "2019-05-30", "end": "2019-06-01", "created": "2019-09-11", "type": {"id": "78", "name": "Vacation", "icon": "palm-trees"}, "amount": {"unit": "hours", "amount": "24"}, "actions": {"view": True, "edit": True, "cancel": False, "approve": False, "deny": False, "bypass": False}, "dates": {"2019-05-30": "24"}, "notes": {"manager": "Home sick with the flu."}}] | ||
httpretty.register_uri(httpretty.GET, | ||
"https://api.bamboohr.com/api/gateway.php/test/v1/time_off/requests", | ||
body=dumps(body), | ||
content_type="application/json") | ||
|
||
response = self.bamboo.get_time_off_requests() | ||
self.assertIsNotNone(response) | ||
self.assertTrue(len(response) > 0) | ||
self.assertEquals(response[0]['id'], '1342') | ||
return | ||
|
||
@httpretty.activate | ||
def test_get_time_off_policies(self): | ||
body = [{'id': '70', 'timeOffTypeId': '77', 'name': 'Testing Manual Policy', 'effectiveDate': None, 'type': 'manual'}] | ||
httpretty.register_uri(httpretty.GET, | ||
"https://api.bamboohr.com/api/gateway.php/test/v1/meta/time_off/policies", | ||
body=dumps(body), | ||
content_type="application/json") | ||
response = self.bamboo.get_time_off_policies() | ||
self.assertIsNotNone(response) | ||
self.assertTrue(len(response) > 0) | ||
self.assertEquals(response[0]['id'], '70') | ||
return | ||
|
||
@httpretty.activate | ||
def test_get_time_off_types(self): | ||
body = {'timeOffTypes': [{'id': '78', 'name': 'Vacation', 'units': 'hours', 'color': None, 'icon': 'palm-trees'}]} | ||
httpretty.register_uri(httpretty.GET, | ||
"https://api.bamboohr.com/api/gateway.php/test/v1/meta/time_off/types", | ||
body=dumps(body), | ||
content_type="application/json") | ||
response = self.bamboo.get_time_off_types() | ||
self.assertIsNotNone(response) | ||
self.assertTrue(len(response) > 0) | ||
self.assertEquals(response[0]['id'], '78') | ||
return | ||
|
||
@httpretty.activate | ||
def test_create_time_off_request(self): | ||
body = {'id': '1675', 'employeeId': '111', 'start': '2040-02-01', 'end': '2040-02-02', 'created': '2019-12-24', 'status': {'status': 'requested', 'lastChanged': '2019-12-24 02:29:45', 'lastChangedByUserId': '2479'}, 'name': 'xdd xdd', 'type': {'id': '78', 'name': 'Vacation'}, 'amount': {'unit': 'hours', 'amount': '2'}, 'notes': {'employee': 'Going overseas with family', 'manager': 'Enjoy!'}, 'dates': {'2040-02-01': '1', '2040-02-02': '1'}, 'comments': [{'employeeId': '111', 'comment': 'Enjoy!', 'commentDate': '2019-12-24', 'commenterName': 'Test use'}], 'approvers': [{'userId': '2479', 'displayName': 'Test user', 'employeeId': '111', 'photoUrl': 'https://resources.bamboohr.com/employees/photos/initials.php?initials=testuser'}], 'actions': {'view': True, 'edit': True, 'cancel': True, 'approve': True, 'deny': True, 'bypass': True}, 'policyType': 'discretionary', 'usedYearToDate': 0, 'balanceOnDateOfRequest': 0} | ||
httpretty.register_uri(httpretty.PUT, | ||
"https://api.bamboohr.com/api/gateway.php/test/v1/employees/111/time_off/request", | ||
body=dumps(body), | ||
content_type="application/json") | ||
data = { | ||
'status': 'requested', | ||
'employee_id': '111', | ||
'start': '2040-02-01', | ||
'end': '2040-02-02', | ||
'timeOffTypeId': '78', | ||
'amount': '2', | ||
'dates': [ | ||
{ 'ymd': '2040-02-01', 'amount': '1' }, | ||
{ 'ymd': '2040-02-02', 'amount': '1' } | ||
], | ||
'notes': [ | ||
{ 'type': 'employee', 'text': 'Going overseas with family' }, | ||
{ 'type': 'manager', 'text': 'Enjoy!' } | ||
] | ||
} | ||
response = self.bamboo.create_time_off_request(data) | ||
self.assertIsNotNone(response) | ||
self.assertEquals(response['id'], '1675') | ||
return | ||
|
||
@httpretty.activate | ||
def test_update_time_off_request_status(self): | ||
body = {'id': '1675', 'employeeId': '111', 'start': '2040-02-01', 'end': '2040-02-02', 'created': '2019-12-24', 'status': {'status': 'declined', 'lastChanged': '2019-12-24 02:29:45', 'lastChangedByUserId': '2479'}, 'name': 'xdd xdd', 'type': {'id': '78', 'name': 'Vacation'}, 'amount': {'unit': 'hours', 'amount': '2'}, 'notes': {'employee': 'Going overseas with family', 'manager': 'Enjoy!'}, 'dates': {'2040-02-01': '1', '2040-02-02': '1'}, 'comments': [{'employeeId': '111', 'comment': 'Enjoy!', 'commentDate': '2019-12-24', 'commenterName': 'Test use'}], 'approvers': [{'userId': '2479', 'displayName': 'Test user', 'employeeId': '111', 'photoUrl': 'https://resources.bamboohr.com/employees/photos/initials.php?initials=testuser'}], 'actions': {'view': True, 'edit': True, 'cancel': True, 'approve': True, 'deny': True, 'bypass': True}, 'policyType': 'discretionary', 'usedYearToDate': 0, 'balanceOnDateOfRequest': 0} | ||
httpretty.register_uri(httpretty.PUT, | ||
"https://api.bamboohr.com/api/gateway.php/test/v1/time_off/requests/1675/status", | ||
body=dumps(body), | ||
content_type="application/json") | ||
data = { | ||
'status': 'declined', | ||
'note': 'Have fun!' | ||
} | ||
response = self.bamboo.update_time_off_request_status(body['id'] ,data) | ||
self.assertIsNotNone(response) | ||
self.assertTrue(response) | ||
return |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same pattern can be used for dates 😉