brynq-sdk-bob 0.0.6__tar.gz → 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {brynq_sdk_bob-0.0.6 → brynq_sdk_bob-1.1.0}/PKG-INFO +1 -1
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/__init__.py +77 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/bank.py +26 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/company.py +23 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/custom_tables.py +36 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/documents.py +46 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/employment.py +23 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/named_lists.py +37 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/payments.py +23 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/people.py +61 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/salaries.py +24 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/schemas/__init__.py +0 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/schemas/bank.py +20 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/schemas/custom_tables.py +10 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/schemas/employment.py +28 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/schemas/named_lists.py +13 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/schemas/payments.py +30 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/schemas/people.py +87 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/schemas/salary.py +21 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/schemas/timeoff.py +28 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/schemas/work.py +29 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/timeoff.py +25 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob/work.py +24 -0
- {brynq_sdk_bob-0.0.6 → brynq_sdk_bob-1.1.0}/brynq_sdk_bob.egg-info/PKG-INFO +1 -1
- brynq_sdk_bob-1.1.0/brynq_sdk_bob.egg-info/SOURCES.txt +29 -0
- brynq_sdk_bob-1.1.0/brynq_sdk_bob.egg-info/top_level.txt +1 -0
- {brynq_sdk_bob-0.0.6 → brynq_sdk_bob-1.1.0}/setup.py +1 -1
- brynq_sdk_bob-0.0.6/brynq_sdk_bob.egg-info/SOURCES.txt +0 -7
- brynq_sdk_bob-0.0.6/brynq_sdk_bob.egg-info/top_level.txt +0 -1
- {brynq_sdk_bob-0.0.6 → brynq_sdk_bob-1.1.0}/brynq_sdk_bob.egg-info/dependency_links.txt +0 -0
- {brynq_sdk_bob-0.0.6 → brynq_sdk_bob-1.1.0}/brynq_sdk_bob.egg-info/not-zip-safe +0 -0
- {brynq_sdk_bob-0.0.6 → brynq_sdk_bob-1.1.0}/brynq_sdk_bob.egg-info/requires.txt +0 -0
- {brynq_sdk_bob-0.0.6 → brynq_sdk_bob-1.1.0}/setup.cfg +0 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import re
|
|
3
|
+
from typing import Union, List
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import requests
|
|
6
|
+
from brynq_sdk_brynq import BrynQ
|
|
7
|
+
from .bank import Bank
|
|
8
|
+
from .company import Company
|
|
9
|
+
from .documents import CustomDocuments
|
|
10
|
+
from .employment import Employment
|
|
11
|
+
from .named_lists import NamedLists
|
|
12
|
+
from .payments import Payments
|
|
13
|
+
from .people import People
|
|
14
|
+
from .salaries import Salaries
|
|
15
|
+
from .timeoff import TimeOff
|
|
16
|
+
from .work import Work
|
|
17
|
+
|
|
18
|
+
class Bob(BrynQ):
|
|
19
|
+
def __init__(self, label: Union[str, List], test_environment: bool = True, debug: bool = False, target_system: str = None):
|
|
20
|
+
super().__init__()
|
|
21
|
+
self.headers = self._get_request_headers(label=label)
|
|
22
|
+
if test_environment:
|
|
23
|
+
self.base_url = "https://api.sandbox.hibob.com/v1/"
|
|
24
|
+
else:
|
|
25
|
+
self.base_url = "https://api.hibob.com/v1/"
|
|
26
|
+
self.session = requests.Session()
|
|
27
|
+
self.session.headers.update(self.headers)
|
|
28
|
+
self.people = People(self)
|
|
29
|
+
self.salaries = Salaries(self)
|
|
30
|
+
self.work = Work(self)
|
|
31
|
+
self.bank = Bank(self)
|
|
32
|
+
self.employment = Employment(self)
|
|
33
|
+
self.payments = Payments(self)
|
|
34
|
+
self.time_off = TimeOff(self)
|
|
35
|
+
self.documents = CustomDocuments(self)
|
|
36
|
+
self.companies = Company(self)
|
|
37
|
+
self.named_lists = NamedLists(self)
|
|
38
|
+
|
|
39
|
+
def _get_request_headers(self, label):
|
|
40
|
+
credentials = self.get_system_credential(system='bob', label=label)
|
|
41
|
+
auth_token = base64.b64encode(f"{credentials['User ID']}:{credentials['API Token']}".encode()).decode('utf-8')
|
|
42
|
+
headers = {
|
|
43
|
+
"accept": "application/json",
|
|
44
|
+
"Authorization": f"Basic {auth_token}",
|
|
45
|
+
"Partner-Token": "001Vg00000A6FY6IAN"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return headers
|
|
49
|
+
|
|
50
|
+
def get_paginated_result(self, request: requests.Request) -> List:
|
|
51
|
+
has_next_page = True
|
|
52
|
+
result_data = []
|
|
53
|
+
while has_next_page:
|
|
54
|
+
prepped = request.prepare()
|
|
55
|
+
prepped.headers.update(self.session.headers)
|
|
56
|
+
resp = self.session.send(prepped)
|
|
57
|
+
resp.raise_for_status()
|
|
58
|
+
response_data = resp.json()
|
|
59
|
+
result_data += response_data['results']
|
|
60
|
+
next_cursor = response_data.get('response_metadata').get('next_cursor')
|
|
61
|
+
# If there is no next page, set has_next_page to False, we could use the falsy value of None but this is more readable
|
|
62
|
+
has_next_page = next_cursor is not None
|
|
63
|
+
if has_next_page:
|
|
64
|
+
request.params.update({"cursor": next_cursor})
|
|
65
|
+
|
|
66
|
+
return result_data
|
|
67
|
+
|
|
68
|
+
def rename_camel_columns_to_snake_case(self, df: pd.DataFrame) -> pd.DataFrame:
|
|
69
|
+
def camel_to_snake_case(column):
|
|
70
|
+
# Replace periods with underscores
|
|
71
|
+
column = column.replace('.', '_')
|
|
72
|
+
# Insert underscores before capital letters and convert to lowercase
|
|
73
|
+
return re.sub(r'(?<!^)(?=[A-Z])', '_', column).lower()
|
|
74
|
+
|
|
75
|
+
df.columns = map(camel_to_snake_case, df.columns)
|
|
76
|
+
|
|
77
|
+
return df
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
from brynq_sdk_functions import Functions
|
|
3
|
+
from .schemas.bank import BankSchema
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Bank:
|
|
7
|
+
def __init__(self, bob):
|
|
8
|
+
self.bob = bob
|
|
9
|
+
|
|
10
|
+
def get(self, person_ids: pd.Series) -> (pd.DataFrame, pd.DataFrame):
|
|
11
|
+
data = []
|
|
12
|
+
for person_id in person_ids:
|
|
13
|
+
resp = self.bob.session.get(url=f"{self.bob.base_url}people/{person_id}/bank-accounts")
|
|
14
|
+
resp.raise_for_status()
|
|
15
|
+
temp_data = resp.json()['values']
|
|
16
|
+
# when an employee has one or more bank accounts, the response is a list of dictionaries.
|
|
17
|
+
for account in temp_data:
|
|
18
|
+
account['employee_id'] = person_id
|
|
19
|
+
data += temp_data
|
|
20
|
+
|
|
21
|
+
df = pd.DataFrame(data)
|
|
22
|
+
df = self.bob.rename_camel_columns_to_snake_case(df)
|
|
23
|
+
|
|
24
|
+
valid_banks, invalid_banks = Functions.validate_data(df=df, schema=BankSchema, debug=True)
|
|
25
|
+
|
|
26
|
+
return valid_banks, invalid_banks
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Company:
|
|
5
|
+
def __init__(self, bob):
|
|
6
|
+
self.bob = bob
|
|
7
|
+
|
|
8
|
+
def get_variable_values(self, list_name: str = None) -> dict:
|
|
9
|
+
values = {}
|
|
10
|
+
|
|
11
|
+
if list_name is not None:
|
|
12
|
+
resp = self.bob.session.get(url=f"{self.bob.base_url}company/named-lists/{list_name}")
|
|
13
|
+
resp.raise_for_status()
|
|
14
|
+
data = resp.json()
|
|
15
|
+
values.update({data["name"]: [value['id'] for value in data['values']]})
|
|
16
|
+
else:
|
|
17
|
+
resp = self.bob.session.get(url=f"{self.bob.base_url}company/named-lists")
|
|
18
|
+
resp.raise_for_status()
|
|
19
|
+
data = resp.json()
|
|
20
|
+
for list_key, list_data in data.items():
|
|
21
|
+
values.update({list_key: [value['id'] for value in list_data['values']]})
|
|
22
|
+
|
|
23
|
+
return values
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from brynq_sdk_functions import Functions
|
|
4
|
+
from .schemas.custom_tables import CustomTableSchema
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CustomTables:
|
|
8
|
+
def __init__(self, bob):
|
|
9
|
+
self.bob = bob
|
|
10
|
+
|
|
11
|
+
def get(self, employee_id: str, custom_table_id: str) -> (pd.DataFrame, pd.DataFrame):
|
|
12
|
+
"""
|
|
13
|
+
Get custom table data for an employee
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
employee_id: The employee ID
|
|
17
|
+
custom_table_id: The custom table ID
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
A tuple of (valid_data, invalid_data) as pandas DataFrames
|
|
21
|
+
"""
|
|
22
|
+
resp = self.bob.session.get(url=f"{self.bob.base_url}people/custom-tables/{employee_id}/{custom_table_id}")
|
|
23
|
+
resp.raise_for_status()
|
|
24
|
+
data = resp.json()
|
|
25
|
+
|
|
26
|
+
# Normalize the nested JSON response
|
|
27
|
+
df = pd.json_normalize(
|
|
28
|
+
data,
|
|
29
|
+
record_path=['values']
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
df['employee_id'] = employee_id
|
|
33
|
+
df = self.bob.rename_camel_columns_to_snake_case(df)
|
|
34
|
+
valid_data, invalid_data = Functions.validate_data(df=df, schema=CustomTableSchema, debug=True)
|
|
35
|
+
|
|
36
|
+
return valid_data, invalid_data
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from io import BytesIO
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
from brynq_sdk_functions import Functions
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CustomDocuments:
|
|
9
|
+
def __init__(self, bob):
|
|
10
|
+
self.bob = bob
|
|
11
|
+
# self.headers_upload = self.bob.headers.copy()
|
|
12
|
+
# self.headers_upload['Content-Type'] = 'multipart/form-data'
|
|
13
|
+
# self.headers_upload['Accept'] = 'application/json'
|
|
14
|
+
|
|
15
|
+
def get(self, person_id: datetime) -> pd.DataFrame:
|
|
16
|
+
resp = self.bob.session.get(url=f"{self.bob.base_url}docs/people/{person_id}")
|
|
17
|
+
resp.raise_for_status()
|
|
18
|
+
data = resp.json()['documents']
|
|
19
|
+
df = pd.DataFrame(data)
|
|
20
|
+
# data = self.bob.get_paginated_result(request)
|
|
21
|
+
# df = pd.json_normalize(
|
|
22
|
+
# data,
|
|
23
|
+
# record_path='changes',
|
|
24
|
+
# meta=['employeeId']
|
|
25
|
+
# )
|
|
26
|
+
df = self.bob.rename_camel_columns_to_snake_case(df)
|
|
27
|
+
# valid_documents, invalid_documents = Functions.validate_data(df=df, schema=DocumentsSchema, debug=True)
|
|
28
|
+
|
|
29
|
+
return df
|
|
30
|
+
|
|
31
|
+
def get_folders(self) -> dict:
|
|
32
|
+
resp = self.bob.session.get(url=f"{self.bob.base_url}docs/folders/metadata")
|
|
33
|
+
resp.raise_for_status()
|
|
34
|
+
data = resp.json()
|
|
35
|
+
|
|
36
|
+
return data
|
|
37
|
+
|
|
38
|
+
def create(self,
|
|
39
|
+
person_id: datetime,
|
|
40
|
+
folder_id: str,
|
|
41
|
+
file_name: str,
|
|
42
|
+
file_object: BytesIO):
|
|
43
|
+
files = {"file": (file_name, file_object, "application/pdf")}
|
|
44
|
+
resp = self.bob.session.post(url=f"{self.bob.base_url}docs/people/{person_id}/folders/{folder_id}/upload",
|
|
45
|
+
files=files)
|
|
46
|
+
resp.raise_for_status()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import requests
|
|
3
|
+
from .schemas.employment import EmploymentSchema
|
|
4
|
+
from brynq_sdk_functions import Functions
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Employment:
|
|
8
|
+
def __init__(self, bob):
|
|
9
|
+
self.bob = bob
|
|
10
|
+
|
|
11
|
+
def get(self) -> (pd.DataFrame, pd.DataFrame):
|
|
12
|
+
request = requests.Request(method='GET',
|
|
13
|
+
url=f"{self.bob.base_url}bulk/people/employment")
|
|
14
|
+
data = self.bob.get_paginated_result(request)
|
|
15
|
+
df = pd.json_normalize(
|
|
16
|
+
data,
|
|
17
|
+
record_path='values',
|
|
18
|
+
meta=['employeeId']
|
|
19
|
+
)
|
|
20
|
+
df = self.bob.rename_camel_columns_to_snake_case(df)
|
|
21
|
+
valid_contracts, invalid_contracts = Functions.validate_data(df=df, schema=EmploymentSchema, debug=True)
|
|
22
|
+
|
|
23
|
+
return valid_contracts, invalid_contracts
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from brynq_sdk_functions import Functions
|
|
4
|
+
from .schemas.named_lists import NamedListSchema
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class NamedLists:
|
|
8
|
+
def __init__(self, bob):
|
|
9
|
+
self.bob = bob
|
|
10
|
+
|
|
11
|
+
def get(self) -> (pd.DataFrame, pd.DataFrame):
|
|
12
|
+
"""
|
|
13
|
+
Get custom table data for an employee
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
list_name: The list name
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
A tuple of (valid_data, invalid_data) as pandas DataFrames
|
|
20
|
+
"""
|
|
21
|
+
url = f"{self.bob.base_url}company/named-lists/"
|
|
22
|
+
resp = self.bob.session.get(url=url)
|
|
23
|
+
resp.raise_for_status()
|
|
24
|
+
data = resp.json()
|
|
25
|
+
|
|
26
|
+
df = pd.DataFrame([
|
|
27
|
+
{**item, "type": key}
|
|
28
|
+
for key, group in data.items()
|
|
29
|
+
for item in group["values"]
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
# Normalize the nested JSON response
|
|
33
|
+
# df = pd.DataFrame(data.get('values'))
|
|
34
|
+
df = self.bob.rename_camel_columns_to_snake_case(df)
|
|
35
|
+
valid_data, invalid_data = Functions.validate_data(df=df, schema=NamedListSchema, debug=True)
|
|
36
|
+
|
|
37
|
+
return valid_data, invalid_data
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from brynq_sdk_functions import Functions
|
|
4
|
+
from .schemas.payments import VariablePaymentSchema
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Payments:
|
|
8
|
+
def __init__(self, bob):
|
|
9
|
+
self.bob = bob
|
|
10
|
+
|
|
11
|
+
def get(self, person_id: str) -> (pd.DataFrame, pd.DataFrame):
|
|
12
|
+
resp = self.bob.session.get(url=f"{self.bob.base_url}people/{person_id}/variable")
|
|
13
|
+
resp.raise_for_status()
|
|
14
|
+
data = resp.json()
|
|
15
|
+
df = pd.json_normalize(
|
|
16
|
+
data,
|
|
17
|
+
record_path='values'
|
|
18
|
+
)
|
|
19
|
+
df['employee_id'] = person_id
|
|
20
|
+
df = self.bob.rename_camel_columns_to_snake_case(df)
|
|
21
|
+
valid_payments, invalid_payments = Functions.validate_data(df=df, schema=VariablePaymentSchema, debug=True)
|
|
22
|
+
|
|
23
|
+
return valid_payments, invalid_payments
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
from brynq_sdk_functions import Functions
|
|
3
|
+
from .bank import Bank
|
|
4
|
+
from .employment import Employment
|
|
5
|
+
from .salaries import Salaries
|
|
6
|
+
from .schemas.people import PeopleSchema
|
|
7
|
+
from .work import Work
|
|
8
|
+
from .custom_tables import CustomTables
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class People:
|
|
12
|
+
def __init__(self, bob):
|
|
13
|
+
self.bob = bob
|
|
14
|
+
self.salaries = Salaries(bob)
|
|
15
|
+
self.employment = Employment(bob)
|
|
16
|
+
self.bank = Bank(bob)
|
|
17
|
+
self.work = Work(bob)
|
|
18
|
+
self.custom_tables = CustomTables(bob)
|
|
19
|
+
|
|
20
|
+
def get(self) -> pd.DataFrame:
|
|
21
|
+
resp = self.bob.session.get(url=f"{self.bob.base_url}profiles")
|
|
22
|
+
# Bob sucks with default fields so you need to do a search call to retrieve additional fields.
|
|
23
|
+
additional_fields = [
|
|
24
|
+
"personal.birthDate",
|
|
25
|
+
"address.city",
|
|
26
|
+
"address.postCode",
|
|
27
|
+
"address.line1",
|
|
28
|
+
"address.line2",
|
|
29
|
+
"address.activeEffectiveDate",
|
|
30
|
+
"address.country",
|
|
31
|
+
# "home.legalGender",
|
|
32
|
+
"home.spouse.firstName",
|
|
33
|
+
"home.spouse.surname",
|
|
34
|
+
# "home.spouse.birthDate",
|
|
35
|
+
"home.spouse.gender",
|
|
36
|
+
"internal.terminationReason",
|
|
37
|
+
"internal.terminationDate",
|
|
38
|
+
"internal.terminationType",
|
|
39
|
+
"employee.lastDayOfWork",
|
|
40
|
+
# housenumber addition
|
|
41
|
+
"address.customColumns.column_1740046184782",
|
|
42
|
+
# contract end date (bob only fills this when you get a new contract normally)
|
|
43
|
+
"payroll.employment.customColumns.column_1680013460318",
|
|
44
|
+
# iban
|
|
45
|
+
"financial.iban"
|
|
46
|
+
# ploegentoeslag
|
|
47
|
+
]
|
|
48
|
+
resp_additional_fields = self.bob.session.post(url=f"{self.bob.base_url}people/search",
|
|
49
|
+
json={
|
|
50
|
+
"fields": ["root.id"] + additional_fields,
|
|
51
|
+
"filters": []
|
|
52
|
+
})
|
|
53
|
+
df_extra_fields = pd.json_normalize(resp_additional_fields.json()['employees'])
|
|
54
|
+
resp.raise_for_status()
|
|
55
|
+
data = resp.json()
|
|
56
|
+
df = pd.json_normalize(data['employees'])
|
|
57
|
+
df = pd.merge(df, df_extra_fields[["id"] + additional_fields], left_on='id', right_on='id')
|
|
58
|
+
df = self.bob.rename_camel_columns_to_snake_case(df)
|
|
59
|
+
valid_people, invalid_people = Functions.validate_data(df=df, schema=PeopleSchema, debug=True)
|
|
60
|
+
|
|
61
|
+
return valid_people, invalid_people
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import requests
|
|
3
|
+
from brynq_sdk_functions import Functions
|
|
4
|
+
from .schemas.salary import SalarySchema
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Salaries:
|
|
8
|
+
def __init__(self, bob):
|
|
9
|
+
self.bob = bob
|
|
10
|
+
|
|
11
|
+
def get(self) -> (pd.DataFrame, pd.DataFrame):
|
|
12
|
+
request = requests.Request(method='GET',
|
|
13
|
+
url=f"{self.bob.base_url}bulk/people/salaries",
|
|
14
|
+
params={"limit": 100})
|
|
15
|
+
data = self.bob.get_paginated_result(request)
|
|
16
|
+
df = pd.json_normalize(
|
|
17
|
+
data,
|
|
18
|
+
record_path='values',
|
|
19
|
+
meta=['employeeId']
|
|
20
|
+
)
|
|
21
|
+
df = self.bob.rename_camel_columns_to_snake_case(df)
|
|
22
|
+
valid_salaries, invalid_salaries = Functions.validate_data(df=df, schema=SalarySchema, debug=True)
|
|
23
|
+
|
|
24
|
+
return valid_salaries, invalid_salaries
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import pandera as pa
|
|
2
|
+
from pandera.typing import Series, String
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BankSchema(pa.DataFrameModel):
|
|
7
|
+
id: Series[pd.Int64Dtype] = pa.Field(coerce=True)
|
|
8
|
+
employee_id: Series[pd.Int64Dtype] = pa.Field(coerce=True)
|
|
9
|
+
amount: Series[pd.Int64Dtype] = pa.Field(coerce=True)
|
|
10
|
+
allocation: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
11
|
+
branch_address: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
12
|
+
bank_name: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
13
|
+
account_number: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
14
|
+
routing_number: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
15
|
+
bank_account_type: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
16
|
+
bic_or_swift: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
17
|
+
changed_by: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
18
|
+
iban: Series[String] = pa.Field(coerce=True)
|
|
19
|
+
account_nickname: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
20
|
+
use_for_bonus: Series[pd.BooleanDtype] = pa.Field(coerce=True, nullable=True)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import pandera as pa
|
|
2
|
+
from pandera.typing import Series
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
class CustomTableSchema(pa.DataFrameModel):
|
|
6
|
+
id: Series[pd.Int64Dtype] = pa.Field(coerce=True)
|
|
7
|
+
employee_id: Series[pd.Int64Dtype] = pa.Field(coerce=True)
|
|
8
|
+
|
|
9
|
+
class Config:
|
|
10
|
+
coerce = True
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import pandera as pa
|
|
3
|
+
from pandera import Bool
|
|
4
|
+
from pandera.typing import Series, String, Float, DateTime
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class EmploymentSchema(pa.DataFrameModel):
|
|
8
|
+
id: Series[pd.Int64Dtype] = pa.Field(coerce=True)
|
|
9
|
+
employee_id: Series[pd.Int64Dtype] = pa.Field(coerce=True)
|
|
10
|
+
active_effective_date: Series[DateTime] = pa.Field(coerce=True)
|
|
11
|
+
contract: Series[String] = pa.Field(coerce=True, nullable=True) # has a list of possible values
|
|
12
|
+
creation_date: Series[DateTime] = pa.Field(coerce=True, nullable=True)
|
|
13
|
+
effective_date: Series[DateTime] = pa.Field(coerce=True)
|
|
14
|
+
end_effective_date: Series[DateTime] = pa.Field(coerce=True, nullable=True)
|
|
15
|
+
fte: Series[Float] = pa.Field(coerce=True)
|
|
16
|
+
is_current: Series[Bool] = pa.Field(coerce=True)
|
|
17
|
+
modification_date: Series[DateTime] = pa.Field(coerce=True, nullable=True)
|
|
18
|
+
salary_pay_type: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
19
|
+
weekly_hours: Series[Float] = pa.Field(coerce=True, nullable=True)
|
|
20
|
+
# weekly_hours_sort_factor: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=False)
|
|
21
|
+
actual_working_pattern_working_pattern_type: Series[pa.String] = pa.Field(nullable=True)
|
|
22
|
+
actual_working_pattern_days_sunday: Series[Float] = pa.Field(nullable=True)
|
|
23
|
+
actual_working_pattern_days_tuesday: Series[Float] = pa.Field(nullable=True)
|
|
24
|
+
actual_working_pattern_days_wednesday: Series[Float] = pa.Field(nullable=True)
|
|
25
|
+
actual_working_pattern_days_monday: Series[Float] = pa.Field(nullable=True)
|
|
26
|
+
actual_working_pattern_days_friday: Series[Float] = pa.Field(nullable=True)
|
|
27
|
+
actual_working_pattern_days_thursday: Series[Float] = pa.Field(nullable=True)
|
|
28
|
+
actual_working_pattern_days_saturday: Series[Float] = pa.Field(nullable=True)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import pandera as pa
|
|
2
|
+
from pandera.typing import Series
|
|
3
|
+
|
|
4
|
+
class NamedListSchema(pa.DataFrameModel):
|
|
5
|
+
id: Series[str] = pa.Field(coerce=True)
|
|
6
|
+
value: Series[str] = pa.Field(coerce=True)
|
|
7
|
+
name: Series[str] = pa.Field(coerce=True)
|
|
8
|
+
archived: Series[bool] = pa.Field(coerce=True)
|
|
9
|
+
# children: Series[list] = pa.Field(coerce=True)
|
|
10
|
+
type: Series[str] = pa.Field(coerce=True)
|
|
11
|
+
|
|
12
|
+
class Config:
|
|
13
|
+
coerce = True
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import pandera as pa
|
|
2
|
+
from pandera.typing import Series, String, Float, DateTime
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
class VariablePaymentSchema(pa.DataFrameModel):
|
|
6
|
+
can_be_deleted: Series[bool] = pa.Field(nullable=True, coerce=True)
|
|
7
|
+
department_percent: Series[Float] = pa.Field(nullable=True, coerce=True)
|
|
8
|
+
payout_type: Series[String] = pa.Field(coerce=True)
|
|
9
|
+
num_of_salaries: Series[pd.Int64Dtype] = pa.Field(nullable=True, coerce=True)
|
|
10
|
+
end_date: Series[DateTime] = pa.Field(nullable=True, coerce=True)
|
|
11
|
+
creation_date: Series[DateTime] = pa.Field(coerce=True)
|
|
12
|
+
percentage_of_annual_salary: Series[Float] = pa.Field(nullable=True, coerce=True)
|
|
13
|
+
individual_percent: Series[Float] = pa.Field(nullable=True, coerce=True)
|
|
14
|
+
variable_type: Series[String] = pa.Field(nullable=True, coerce=True)
|
|
15
|
+
is_current: Series[bool] = pa.Field(nullable=True, coerce=True)
|
|
16
|
+
modification_date: Series[DateTime] = pa.Field(nullable=True, coerce=True)
|
|
17
|
+
company_percent: Series[Float] = pa.Field(nullable=True, coerce=True)
|
|
18
|
+
id: Series[pd.Int64Dtype] = pa.Field(coerce=True)
|
|
19
|
+
end_effective_date: Series[DateTime] = pa.Field(nullable=True, coerce=True)
|
|
20
|
+
payment_period: Series[String] = pa.Field(coerce=True)
|
|
21
|
+
effective_date: Series[DateTime] = pa.Field(coerce=True)
|
|
22
|
+
amount_value: Series[Float] = pa.Field(coerce=True)
|
|
23
|
+
amount_currency: Series[String] = pa.Field(coerce=True)
|
|
24
|
+
change_reason: Series[String] = pa.Field(nullable=True, coerce=True)
|
|
25
|
+
change_changed_by: Series[String] = pa.Field(nullable=True, coerce=True)
|
|
26
|
+
change_changed_by_id: Series[pd.Int64Dtype] = pa.Field(nullable=True, coerce=True)
|
|
27
|
+
employee_id: Series[pd.Int64Dtype] = pa.Field(coerce=True)
|
|
28
|
+
|
|
29
|
+
class Config:
|
|
30
|
+
coerce = True
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import pandera as pa
|
|
5
|
+
from pandera import Bool
|
|
6
|
+
from pandera.typing import Series, String, Float, DateTime
|
|
7
|
+
import pandera.extensions as extensions
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@extensions.register_check_method()
|
|
12
|
+
def check_list(x):
|
|
13
|
+
return isinstance(x, list)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PeopleSchema(pa.DataFrameModel):
|
|
17
|
+
id: Series[pd.Int64Dtype] = pa.Field(coerce=True)
|
|
18
|
+
display_name: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
19
|
+
company_id: Series[String] = pa.Field(coerce=True)
|
|
20
|
+
email: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
21
|
+
surname: Series[String] = pa.Field(coerce=True)
|
|
22
|
+
first_name: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
23
|
+
personal_birth_date: Series[DateTime] = pa.Field(coerce=True)
|
|
24
|
+
personal_pronouns: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
25
|
+
personal_honorific: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
26
|
+
personal_nationality: Series[object] = pa.Field(coerce=True, check_name=check_list)
|
|
27
|
+
# employee_payroll_manager: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
28
|
+
# employee_hrbp: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
29
|
+
# employee_it_admin: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
30
|
+
# employee_buddy: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
31
|
+
employee_veteran_status: Series[object] = pa.Field(coerce=True, check_name=check_list)
|
|
32
|
+
employee_disability_status: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
33
|
+
work_start_date: Series[DateTime] = pa.Field(coerce=True, nullable=True)
|
|
34
|
+
work_manager: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
35
|
+
work_work_phone: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
36
|
+
work_tenure_duration_period_i_s_o: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
37
|
+
work_tenure_duration_sort_factor: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=False)
|
|
38
|
+
work_tenure_duration_humanize: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
39
|
+
work_duration_of_employment_period_i_s_o: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
40
|
+
work_duration_of_employment_sort_factor: Series[String] = pa.Field(coerce=True, nullable=False)
|
|
41
|
+
work_duration_of_employment_humanize: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
42
|
+
work_reports_to_id_in_company: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True)
|
|
43
|
+
work_employee_id_in_company: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True)
|
|
44
|
+
work_reports_to_display_name: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
45
|
+
work_reports_to_email: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
46
|
+
work_reports_to_surname: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
47
|
+
work_reports_to_first_name: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
48
|
+
work_reports_to_id: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True)
|
|
49
|
+
work_work_mobile: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
50
|
+
work_indirect_reports: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True)
|
|
51
|
+
work_site_id: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True)
|
|
52
|
+
work_department: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
53
|
+
work_tenure_duration_years: Series[Float] = pa.Field(coerce=True, nullable=True)
|
|
54
|
+
work_tenure_years: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True)
|
|
55
|
+
work_is_manager: Series[Bool] = pa.Field(coerce=True)
|
|
56
|
+
work_title: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
57
|
+
work_site: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
58
|
+
work_original_start_date: Series[DateTime] = pa.Field(coerce=True, nullable=True)
|
|
59
|
+
work_active_effective_date: Series[DateTime] = pa.Field(coerce=True, nullable=True)
|
|
60
|
+
work_direct_reports: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True)
|
|
61
|
+
# work_work_change_type: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
62
|
+
work_second_level_manager: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True)
|
|
63
|
+
work_days_of_previous_service: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True)
|
|
64
|
+
work_years_of_service: Series[Float] = pa.Field(coerce=True, nullable=True)
|
|
65
|
+
about_food_preferences: Series[object] = pa.Field(coerce=True, check_name=check_list)
|
|
66
|
+
# about_social_data_linkedin: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
67
|
+
about_social_data_twitter: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
68
|
+
about_social_data_facebook: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
69
|
+
about_superpowers: Series[object] = pa.Field(coerce=True, check_name=check_list)
|
|
70
|
+
about_hobbies: Series[object] = pa.Field(coerce=True, check_name=check_list)
|
|
71
|
+
about_about: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
72
|
+
about_avatar: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
73
|
+
address_city: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
74
|
+
address_post_code: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
75
|
+
address_line1: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
76
|
+
address_line2: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
77
|
+
# address_country: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
78
|
+
address_active_effective_date: Series[DateTime] = pa.Field(coerce=True, nullable=True)
|
|
79
|
+
# home_legal_gender: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
80
|
+
home_spouse_first_name: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
81
|
+
home_spouse_surname: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
82
|
+
# home_spouse_birth_date: Series[DateTime] = pa.Field(coerce=True, nullable=True)
|
|
83
|
+
home_spouse_gender: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
84
|
+
internal_termination_reason: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
85
|
+
internal_termination_date: Series[DateTime] = pa.Field(coerce=True, nullable=True)
|
|
86
|
+
internal_termination_type: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
87
|
+
employee_last_day_of_work: Series[DateTime] = pa.Field(coerce=True, nullable=True)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import pandera as pa
|
|
3
|
+
from pandera import Bool
|
|
4
|
+
from pandera.typing import Series, String, Float, DateTime
|
|
5
|
+
import pandera.extensions as extensions
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SalarySchema(pa.DataFrameModel):
|
|
9
|
+
id: Series[pd.Int64Dtype] = pa.Field(coerce=True)
|
|
10
|
+
employee_id: Series[pd.Int64Dtype] = pa.Field(coerce=True)
|
|
11
|
+
pay_frequency: Series[String] = pa.Field(coerce=True, nullable=True) # has a list of possible values , isin=['Monthly']
|
|
12
|
+
creation_date: Series[DateTime] = pa.Field(coerce=True, nullable=True)
|
|
13
|
+
is_current: Series[Bool] = pa.Field(coerce=True)
|
|
14
|
+
modification_date: Series[DateTime] = pa.Field(coerce=True, nullable=True)
|
|
15
|
+
effective_date: Series[DateTime] = pa.Field(coerce=True)
|
|
16
|
+
end_effective_date: Series[DateTime] = pa.Field(coerce=True, nullable=True)
|
|
17
|
+
change_reason: Series[str] = pa.Field(coerce=True, nullable=True)
|
|
18
|
+
pay_period: Series[String] = pa.Field(coerce=True, nullable=True)
|
|
19
|
+
base_value: Series[Float] = pa.Field(coerce=True, nullable=True)
|
|
20
|
+
base_currency: Series[String] = pa.Field(coerce=True, isin=['EUR', 'USD'])
|
|
21
|
+
active_effective_date: Series[DateTime] = pa.Field(coerce=True)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import pandera as pa
|
|
2
|
+
from pandera.typing import Series, String, Float
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
class TimeOffSchema(pa.DataFrameModel):
|
|
6
|
+
change_type: Series[String] = pa.Field(coerce=True)
|
|
7
|
+
employee_id: Series[String] = pa.Field(coerce=True)
|
|
8
|
+
employee_display_name: Series[String] = pa.Field(coerce=True)
|
|
9
|
+
employee_email: Series[String] = pa.Field(coerce=True)
|
|
10
|
+
request_id: Series[pd.Int64Dtype] = pa.Field(coerce=True)
|
|
11
|
+
policy_type_display_name: Series[String] = pa.Field(coerce=True)
|
|
12
|
+
type: Series[String] = pa.Field(coerce=True)
|
|
13
|
+
start_date: Series[String] = pa.Field(coerce=True)
|
|
14
|
+
start_portion: Series[String] = pa.Field(coerce=True)
|
|
15
|
+
end_date: Series[String] = pa.Field(coerce=True)
|
|
16
|
+
end_portion: Series[String] = pa.Field(coerce=True)
|
|
17
|
+
day_portion: Series[String] = pa.Field(coerce=True)
|
|
18
|
+
date: Series[String] = pa.Field(coerce=True)
|
|
19
|
+
hours_on_date: Series[Float] = pa.Field(coerce=True)
|
|
20
|
+
daily_hours: Series[Float] = pa.Field(coerce=True)
|
|
21
|
+
duration_unit: Series[String] = pa.Field(coerce=True)
|
|
22
|
+
total_duration: Series[Float] = pa.Field(coerce=True)
|
|
23
|
+
total_cost: Series[Float] = pa.Field(coerce=True)
|
|
24
|
+
change_reason: Series[String] = pa.Field(nullable=True, coerce=True)
|
|
25
|
+
visibility: Series[String] = pa.Field(coerce=True)
|
|
26
|
+
|
|
27
|
+
class Config:
|
|
28
|
+
coerce = True
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import pandera as pa
|
|
2
|
+
from pandera.typing import Series
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
|
|
6
|
+
class WorkSchema(pa.DataFrameModel):
|
|
7
|
+
can_be_deleted: Series[pa.Bool] = pa.Field(coerce=True)
|
|
8
|
+
work_change_type: Series[str] = pa.Field(coerce=True)
|
|
9
|
+
creation_date: Series[datetime] = pa.Field(coerce=True, nullable=True)
|
|
10
|
+
title: Series[str] = pa.Field(coerce=True, nullable=True)
|
|
11
|
+
is_current: Series[pa.Bool] = pa.Field(coerce=True)
|
|
12
|
+
modification_date: Series[datetime] = pa.Field(coerce=True, nullable=True)
|
|
13
|
+
site: Series[str] = pa.Field(coerce=True, nullable=True)
|
|
14
|
+
site_id: Series[pd.Int64Dtype] = pa.Field(coerce=True)
|
|
15
|
+
id: Series[pd.Int64Dtype] = pa.Field(coerce=True)
|
|
16
|
+
end_effective_date: Series[datetime] = pa.Field(coerce=True, nullable=True)
|
|
17
|
+
active_effective_date: Series[datetime] = pa.Field(coerce=True, nullable=True)
|
|
18
|
+
department: Series[str] = pa.Field(coerce=True, nullable=True)
|
|
19
|
+
effective_date: Series[datetime] = pa.Field(coerce=True, nullable=True)
|
|
20
|
+
change_reason: Series[str] = pa.Field(coerce=True, nullable=True)
|
|
21
|
+
change_changed_by: Series[str] = pa.Field(coerce=True, nullable=True)
|
|
22
|
+
change_changed_by_id: Series[str] = pa.Field(coerce=True, nullable=True)
|
|
23
|
+
reports_to_id: Series[str] = pa.Field(coerce=True, nullable=True)
|
|
24
|
+
reports_to_first_name: Series[str] = pa.Field(coerce=True, nullable=True)
|
|
25
|
+
reports_to_surname: Series[str] = pa.Field(coerce=True, nullable=True)
|
|
26
|
+
reports_to_email: Series[str] = pa.Field(coerce=True, nullable=True)
|
|
27
|
+
reports_to_display_name: Series[str] = pa.Field(coerce=True, nullable=True)
|
|
28
|
+
reports_to: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True)
|
|
29
|
+
employee_id: Series[pd.Int64Dtype] = pa.Field(coerce=True)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from datetime import datetime, timezone
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from brynq_sdk_functions import Functions
|
|
4
|
+
from .schemas.timeoff import TimeOffSchema
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TimeOff:
|
|
8
|
+
def __init__(self, bob):
|
|
9
|
+
self.bob = bob
|
|
10
|
+
|
|
11
|
+
def get(self, since: datetime) -> (pd.DataFrame, pd.DataFrame):
|
|
12
|
+
resp = self.bob.session.get(url=f"{self.bob.base_url}timeoff/requests/changes",
|
|
13
|
+
params={'since': since.replace(tzinfo=timezone.utc).isoformat(timespec='milliseconds')})
|
|
14
|
+
resp.raise_for_status()
|
|
15
|
+
data = resp.json()['changes']
|
|
16
|
+
# data = self.bob.get_paginated_result(request)
|
|
17
|
+
df = pd.json_normalize(
|
|
18
|
+
data,
|
|
19
|
+
record_path='changes',
|
|
20
|
+
meta=['employeeId']
|
|
21
|
+
)
|
|
22
|
+
df = self.bob.rename_camel_columns_to_snake_case(df)
|
|
23
|
+
valid_timeoff, invalid_timeoff = Functions.validate_data(df=df, schema=TimeOffSchema, debug=True)
|
|
24
|
+
|
|
25
|
+
return valid_timeoff, invalid_timeoff
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import requests
|
|
3
|
+
from brynq_sdk_functions import Functions
|
|
4
|
+
from .schemas.work import WorkSchema
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Work:
|
|
8
|
+
def __init__(self, bob):
|
|
9
|
+
self.bob = bob
|
|
10
|
+
|
|
11
|
+
def get(self) ->(pd.DataFrame, pd.DataFrame):
|
|
12
|
+
request = requests.Request(method='GET',
|
|
13
|
+
url=f"{self.bob.base_url}bulk/people/work")
|
|
14
|
+
data = self.bob.get_paginated_result(request)
|
|
15
|
+
df = pd.json_normalize(
|
|
16
|
+
data,
|
|
17
|
+
record_path='values',
|
|
18
|
+
meta=['employeeId']
|
|
19
|
+
)
|
|
20
|
+
df = self.bob.rename_camel_columns_to_snake_case(df)
|
|
21
|
+
|
|
22
|
+
valid_work, invalid_work = Functions.validate_data(df=df, schema=WorkSchema, debug=True)
|
|
23
|
+
|
|
24
|
+
return valid_work, invalid_work
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
setup.py
|
|
2
|
+
brynq_sdk_bob/__init__.py
|
|
3
|
+
brynq_sdk_bob/bank.py
|
|
4
|
+
brynq_sdk_bob/company.py
|
|
5
|
+
brynq_sdk_bob/custom_tables.py
|
|
6
|
+
brynq_sdk_bob/documents.py
|
|
7
|
+
brynq_sdk_bob/employment.py
|
|
8
|
+
brynq_sdk_bob/named_lists.py
|
|
9
|
+
brynq_sdk_bob/payments.py
|
|
10
|
+
brynq_sdk_bob/people.py
|
|
11
|
+
brynq_sdk_bob/salaries.py
|
|
12
|
+
brynq_sdk_bob/timeoff.py
|
|
13
|
+
brynq_sdk_bob/work.py
|
|
14
|
+
brynq_sdk_bob.egg-info/PKG-INFO
|
|
15
|
+
brynq_sdk_bob.egg-info/SOURCES.txt
|
|
16
|
+
brynq_sdk_bob.egg-info/dependency_links.txt
|
|
17
|
+
brynq_sdk_bob.egg-info/not-zip-safe
|
|
18
|
+
brynq_sdk_bob.egg-info/requires.txt
|
|
19
|
+
brynq_sdk_bob.egg-info/top_level.txt
|
|
20
|
+
brynq_sdk_bob/schemas/__init__.py
|
|
21
|
+
brynq_sdk_bob/schemas/bank.py
|
|
22
|
+
brynq_sdk_bob/schemas/custom_tables.py
|
|
23
|
+
brynq_sdk_bob/schemas/employment.py
|
|
24
|
+
brynq_sdk_bob/schemas/named_lists.py
|
|
25
|
+
brynq_sdk_bob/schemas/payments.py
|
|
26
|
+
brynq_sdk_bob/schemas/people.py
|
|
27
|
+
brynq_sdk_bob/schemas/salary.py
|
|
28
|
+
brynq_sdk_bob/schemas/timeoff.py
|
|
29
|
+
brynq_sdk_bob/schemas/work.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
brynq_sdk_bob
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|