brynq-sdk-bob 0.0.1__tar.gz → 0.0.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.0
2
2
  Name: brynq_sdk_bob
3
- Version: 0.0.1
3
+ Version: 0.0.3
4
4
  Summary: Bob wrapper from BrynQ
5
5
  Home-page: UNKNOWN
6
6
  Author: BrynQ
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.0
2
2
  Name: brynq-sdk-bob
3
- Version: 0.0.1
3
+ Version: 0.0.3
4
4
  Summary: Bob wrapper from BrynQ
5
5
  Home-page: UNKNOWN
6
6
  Author: BrynQ
@@ -0,0 +1,7 @@
1
+ setup.py
2
+ brynq_sdk_bob.egg-info/PKG-INFO
3
+ brynq_sdk_bob.egg-info/SOURCES.txt
4
+ brynq_sdk_bob.egg-info/dependency_links.txt
5
+ brynq_sdk_bob.egg-info/not-zip-safe
6
+ brynq_sdk_bob.egg-info/requires.txt
7
+ brynq_sdk_bob.egg-info/top_level.txt
@@ -1,14 +1,13 @@
1
- from setuptools import setup
2
-
1
+ from setuptools import setup, find_namespace_packages
3
2
 
4
3
  setup(
5
4
  name='brynq_sdk_bob',
6
- version='0.0.1',
5
+ version='0.0.3',
7
6
  description='Bob wrapper from BrynQ',
8
7
  long_description='Bob wrapper from BrynQ',
9
8
  author='BrynQ',
10
9
  author_email='support@brynq.com',
11
- packages=["brynq_sdk.bob"],
10
+ packages=find_namespace_packages(include=['brynq_sdk*']),
12
11
  license='BrynQ License',
13
12
  install_requires=[
14
13
  'brynq-sdk-brynq>=1',
@@ -1,73 +0,0 @@
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
-
8
- from .bank import Bank
9
- from .employment import Employment
10
- from .payments import Payments
11
- from .people import People
12
- from .salaries import Salaries
13
- from .timeoff import TimeOff
14
- from .work import Work
15
-
16
-
17
- class Bob(BrynQ):
18
- def __init__(self, label: Union[str, List], test_environment: bool = True, debug: bool = False):
19
- super().__init__()
20
- self.headers = self._get_request_headers(label=label)
21
- if test_environment:
22
- self.base_url = "https://api.sandbox.hibob.com/v1/"
23
- else:
24
- self.base_url = "https://api.hibob.com/v1/"
25
- self.session = requests.Session()
26
- self.session.headers.update(self.headers)
27
- self.people = People(self)
28
- self.salaries = Salaries(self)
29
- self.work = Work(self)
30
- self.bank = Bank(self)
31
- self.employment = Employment(self)
32
- self.payments = Payments(self)
33
- self.time_off = TimeOff(self)
34
-
35
- def _get_request_headers(self, label):
36
- credentials = self.get_system_credential(system='bob', label=label)
37
- auth_token = base64.b64encode(f"{credentials['User ID']}:{credentials['API Token']}".encode()).decode('utf-8')
38
- headers = {
39
- "accept": "application/json",
40
- "Authorization": f"Basic {auth_token}",
41
- "Partner-Token": "001Vg00000A6FY6IAN"
42
- }
43
-
44
- return headers
45
-
46
- def get_paginated_result(self, request: requests.Request) -> List:
47
- has_next_page = True
48
- result_data = []
49
- while has_next_page:
50
- prepped = request.prepare()
51
- prepped.headers.update(self.session.headers)
52
- resp = self.session.send(prepped)
53
- resp.raise_for_status()
54
- response_data = resp.json()
55
- result_data += response_data['results']
56
- next_cursor = response_data.get('response_metadata').get('next_cursor')
57
- # 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
58
- has_next_page = next_cursor is not None
59
- if has_next_page:
60
- request.params.update({"cursor": next_cursor})
61
-
62
- return result_data
63
-
64
- def rename_camel_columns_to_snake_case(self, df: pd.DataFrame) -> pd.DataFrame:
65
- def camel_to_snake_case(column):
66
- # Replace periods with underscores
67
- column = column.replace('.', '_')
68
- # Insert underscores before capital letters and convert to lowercase
69
- return re.sub(r'(?<!^)(?=[A-Z])', '_', column).lower()
70
-
71
- df.columns = map(camel_to_snake_case, df.columns)
72
-
73
- return df
@@ -1,27 +0,0 @@
1
- import pandas as pd
2
- from brynq_sdk.functions import Functions
3
-
4
- from src.bob.schemas.bank import BankSchema
5
-
6
-
7
- class Bank:
8
- def __init__(self, bob):
9
- self.bob = bob
10
-
11
- def get(self, person_ids: pd.Series) -> pd.DataFrame:
12
- data = []
13
- for person_id in person_ids:
14
- resp = self.bob.session.get(url=f"{self.bob.base_url}people/{person_id}/bank-accounts")
15
- resp.raise_for_status()
16
- temp_data = resp.json()['values']
17
- # when an employee has one or more bank accounts, the response is a list of dictionaries.
18
- for account in temp_data:
19
- account['employee_id'] = person_id
20
- data += temp_data
21
-
22
- df = pd.DataFrame(data)
23
- df = self.bob.rename_camel_columns_to_snake_case(df)
24
-
25
- valid_banks, invalid_banks = Functions.validate_data(df=df, schema=BankSchema, debug=True)
26
-
27
- return valid_banks
@@ -1,23 +0,0 @@
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
@@ -1,40 +0,0 @@
1
- from datetime import datetime
2
- import pandas as pd
3
- from brynq_sdk.functions import Functions
4
-
5
-
6
- class Documents:
7
- def __init__(self, bob):
8
- self.bob = bob
9
- # self.headers_upload = self.bob.headers.copy()
10
- # self.headers_upload['Content-Type'] = 'multipart/form-data'
11
- # self.headers_upload['Accept'] = 'application/json'
12
-
13
- def get(self, person_id: datetime) -> pd.DataFrame:
14
- resp = self.bob.session.get(url=f"{self.bob.base_url}docs/people/{person_id}")
15
- resp.raise_for_status()
16
- data = resp.json()['documents']
17
- df = pd.DataFrame(data)
18
- # data = self.bob.get_paginated_result(request)
19
- # df = pd.json_normalize(
20
- # data,
21
- # record_path='changes',
22
- # meta=['employeeId']
23
- # )
24
- df = self.bob.rename_camel_columns_to_snake_case(df)
25
- valid_documents, invalid_documents = Functions.validate_data(df=df, schema=DocumentsSchema, debug=True)
26
-
27
- return valid_documents
28
-
29
- def create_confidential(self,
30
- person_id: datetime,
31
- file_name: str,
32
- file_path: str):
33
- files = {"file": (file_name, open(file_path, "rb"), "application/pdf")}
34
- resp = self.bob.session.post(url=f"{self.bob.base_url}people/{person_id}/confidential/upload",
35
- files=files)
36
- resp.raise_for_status()
37
-
38
- # def create_shared(self, person_id: datetime):
39
- # resp = self.bob.session.post(url=f"{self.bob.base_url}people/{person_id}/shared/upload")
40
- # resp.raise_for_status()
@@ -1,23 +0,0 @@
1
- import pandas as pd
2
- import requests
3
- from src.bob.schemas.employment import EmploymentSchema
4
- from src.utils.validation_tracker import enhanced_validate_data, ValidationTracker
5
- from typing import Tuple
6
-
7
- class Employment:
8
- def __init__(self, bob):
9
- self.bob = bob
10
-
11
- def get(self) -> Tuple[pd.DataFrame, ValidationTracker]:
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, tracker = enhanced_validate_data(df=df, schema=EmploymentSchema, debug=True)
22
-
23
- return valid_contracts, tracker
@@ -1,22 +0,0 @@
1
- from datetime import datetime
2
- import pandas as pd
3
- from brynq_sdk.functions import Functions
4
- from src.bob.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: datetime) -> (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()['values']
15
- df = pd.json_normalize(
16
- data,
17
- record_path='values'
18
- )
19
- df = self.bob.rename_camel_columns_to_snake_case(df)
20
- valid_payments, invalid_payments = Functions.validate_data(df=df, schema=VariablePaymentSchema, debug=True)
21
-
22
- return valid_payments, invalid_payments
@@ -1,54 +0,0 @@
1
- import pandas as pd
2
- from brynq_sdk.functions import Functions
3
-
4
- from src.bob.bank import Bank
5
- from src.bob.employment import Employment
6
- from src.bob.salaries import Salaries
7
- from src.bob.schemas.people import PeopleSchema
8
- from src.bob.work import Work
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
-
19
- def get(self) -> pd.DataFrame:
20
- resp = self.bob.session.get(url=f"{self.bob.base_url}profiles")
21
- # Bob sucks with default fields so you need to do a search call to retrieve additional fields.
22
- additional_fields = [
23
- "personal.birthDate",
24
- "address.city",
25
- "address.postCode",
26
- "address.line1",
27
- "address.line2",
28
- "address.activeEffectiveDate",
29
- # "address.fullAddress",
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
- ]
41
- resp_additional_fields = self.bob.session.post(url=f"{self.bob.base_url}people/search",
42
- json={
43
- "fields": ["root.id"] + additional_fields,
44
- "filters": []
45
- })
46
- df_extra_fields = pd.json_normalize(resp_additional_fields.json()['employees'])
47
- resp.raise_for_status()
48
- data = resp.json()
49
- df = pd.json_normalize(data['employees'])
50
- df = pd.merge(df, df_extra_fields[["id"] + additional_fields], left_on='id', right_on='id')
51
- df = self.bob.rename_camel_columns_to_snake_case(df)
52
- valid_people, invalid_people = Functions.validate_data(df=df, schema=PeopleSchema, debug=True)
53
-
54
- return valid_people
@@ -1,24 +0,0 @@
1
- import pandas as pd
2
- import requests
3
- from brynq_sdk.functions import Functions
4
- from src.bob.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:
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
@@ -1,25 +0,0 @@
1
- from datetime import datetime, timezone
2
- import pandas as pd
3
- from brynq_sdk.functions import Functions
4
- from src.bob.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
@@ -1,25 +0,0 @@
1
- import pandas as pd
2
- import requests
3
- from brynq_sdk.functions import Functions
4
-
5
- from src.bob.schemas.work import WorkSchema
6
-
7
-
8
- class Work:
9
- def __init__(self, bob):
10
- self.bob = bob
11
-
12
- def get(self) -> pd.DataFrame:
13
- request = requests.Request(method='GET',
14
- url=f"{self.bob.base_url}bulk/people/work")
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
-
23
- valid_work, invalid_work = Functions.validate_data(df=df, schema=WorkSchema, debug=True)
24
-
25
- return valid_work
@@ -1,17 +0,0 @@
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/documents.py
6
- brynq_sdk/bob/employment.py
7
- brynq_sdk/bob/payments.py
8
- brynq_sdk/bob/people.py
9
- brynq_sdk/bob/salaries.py
10
- brynq_sdk/bob/timeoff.py
11
- brynq_sdk/bob/work.py
12
- brynq_sdk_bob.egg-info/PKG-INFO
13
- brynq_sdk_bob.egg-info/SOURCES.txt
14
- brynq_sdk_bob.egg-info/dependency_links.txt
15
- brynq_sdk_bob.egg-info/not-zip-safe
16
- brynq_sdk_bob.egg-info/requires.txt
17
- brynq_sdk_bob.egg-info/top_level.txt
@@ -1 +0,0 @@
1
- brynq_sdk
File without changes