brynq-sdk-bob 2.9.5__py3-none-any.whl → 2.10.1__py3-none-any.whl

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/__init__.py CHANGED
@@ -1,16 +1,12 @@
1
1
  import base64
2
2
  import os
3
3
  from typing import List, Literal, Optional
4
-
5
4
  import requests
6
5
  from requests.adapters import HTTPAdapter
7
6
  from urllib3.util.retry import Retry
8
-
9
7
  from brynq_sdk_brynq import BrynQ
10
-
11
8
  from .bank import Bank
12
9
  from .company import Company
13
- from .custom_tables import CustomTables
14
10
  from .documents import CustomDocuments
15
11
  from .employment import Employment
16
12
  from .named_lists import NamedLists
@@ -21,6 +17,8 @@ from .reports import Reports
21
17
  from .salaries import Salaries
22
18
  from .timeoff import TimeOff
23
19
  from .work import Work
20
+ from .custom_tables import CustomTables
21
+ from .entitlements import Entitlements
24
22
 
25
23
 
26
24
  class Bob(BrynQ):
@@ -57,6 +55,7 @@ class Bob(BrynQ):
57
55
  self.custom_tables = CustomTables(self)
58
56
  self.payroll_history = History(self)
59
57
  self.reports = Reports(self)
58
+ self.entitlements = Entitlements(self)
60
59
  self.data_interface_id = os.getenv("DATA_INTERFACE_ID")
61
60
  self.debug = debug
62
61
 
brynq_sdk_bob/bank.py CHANGED
@@ -1,17 +1,17 @@
1
1
  import pandas as pd
2
2
  from brynq_sdk_functions import Functions
3
- from .schemas.bank import BankSchema
3
+ from .schemas.bank import BankGet
4
4
 
5
5
 
6
6
  class Bank:
7
- def __init__(self, bob):
8
- self.bob = bob
9
- self.schema = BankSchema
7
+ def __init__(self, client):
8
+ self.client = client
9
+ self.schema = BankGet
10
10
 
11
- def get(self, person_ids: pd.Series, field_selection: list[str] = []) -> (pd.DataFrame, pd.DataFrame):
11
+ def get(self, person_ids: pd.Series, field_selection: list[str] = []) -> tuple[pd.DataFrame, pd.DataFrame]:
12
12
  data = []
13
13
  for person_id in person_ids:
14
- resp = self.bob.session.get(url=f"{self.bob.base_url}people/{person_id}/bank-accounts", timeout=self.bob.timeout)
14
+ resp = self.client.session.get(url=f"{self.client.base_url}people/{person_id}/bank-accounts", timeout=self.client.timeout)
15
15
  resp.raise_for_status()
16
16
  temp_data = resp.json()['values']
17
17
  # when an employee has one or more bank accounts, the response is a list of dictionaries.
@@ -21,6 +21,6 @@ class Bank:
21
21
 
22
22
  df = pd.DataFrame(data)
23
23
 
24
- valid_banks, invalid_banks = Functions.validate_data(df=df, schema=BankSchema, debug=True)
24
+ valid_banks, invalid_banks = Functions.validate_data(df=df, schema=BankGet, debug=True)
25
25
 
26
26
  return valid_banks, invalid_banks
brynq_sdk_bob/company.py CHANGED
@@ -2,19 +2,19 @@ import pandas as pd
2
2
 
3
3
 
4
4
  class Company:
5
- def __init__(self, bob):
6
- self.bob = bob
5
+ def __init__(self, client):
6
+ self.client = client
7
7
 
8
8
  def get_variable_values(self, list_name: str = None) -> dict:
9
9
  values = {}
10
10
 
11
11
  if list_name is not None:
12
- resp = self.bob.session.get(url=f"{self.bob.base_url}company/named-lists/{list_name}", timeout=self.bob.timeout)
12
+ resp = self.client.session.get(url=f"{self.client.base_url}company/named-lists/{list_name}", timeout=self.client.timeout)
13
13
  resp.raise_for_status()
14
14
  data = resp.json()
15
15
  values.update({data["name"]: [value['id'] for value in data['values']]})
16
16
  else:
17
- resp = self.bob.session.get(url=f"{self.bob.base_url}company/named-lists", timeout=self.bob.timeout)
17
+ resp = self.client.session.get(url=f"{self.client.base_url}company/named-lists", timeout=self.client.timeout)
18
18
  resp.raise_for_status()
19
19
  data = resp.json()
20
20
  for list_key, list_data in data.items():
@@ -4,13 +4,13 @@ import pandas as pd
4
4
 
5
5
  from brynq_sdk_functions import BrynQPanderaDataFrameModel, Functions
6
6
 
7
- from .schemas.custom_tables import CustomTableMetadataSchema, CustomTableSchema
7
+ from .schemas.custom_tables import CustomTableMetadataGet, CustomTableGet
8
8
 
9
9
 
10
10
  class CustomTables:
11
- def __init__(self, bob):
12
- self.bob = bob
13
- self.schema = CustomTableSchema
11
+ def __init__(self, client):
12
+ self.client = client
13
+ self.schema = CustomTableGet
14
14
 
15
15
  def get(self, employee_ids: List[str], custom_table_id: str, schema_custom_fields: Optional[BrynQPanderaDataFrameModel] = None) -> tuple[pd.DataFrame, pd.DataFrame]:
16
16
  """
@@ -26,7 +26,7 @@ class CustomTables:
26
26
  """
27
27
  df = pd.DataFrame()
28
28
  for employee_id in employee_ids:
29
- resp = self.bob.session.get(url=f"{self.bob.base_url}people/custom-tables/{employee_id}/{custom_table_id}", timeout=self.bob.timeout)
29
+ resp = self.client.session.get(url=f"{self.client.base_url}people/custom-tables/{employee_id}/{custom_table_id}", timeout=self.client.timeout)
30
30
  resp.raise_for_status()
31
31
  data = resp.json()
32
32
 
@@ -53,8 +53,8 @@ class CustomTables:
53
53
  Returns:
54
54
  A tuple of (valid_data, invalid_data) as pandas DataFrames containing table and column metadata
55
55
  """
56
- url = f"{self.bob.base_url}people/custom-tables/metadata"
57
- resp = self.bob.session.get(url=url)
56
+ url = f"{self.client.base_url}people/custom-tables/metadata"
57
+ resp = self.client.session.get(url=url)
58
58
  resp.raise_for_status()
59
59
  data = resp.json()
60
60
 
@@ -82,6 +82,6 @@ class CustomTables:
82
82
  df = pd.DataFrame(rows)
83
83
 
84
84
  # Validate against the metadata schema
85
- valid_data, invalid_data = Functions.validate_data(df=df, schema=CustomTableMetadataSchema, debug=True)
85
+ valid_data, invalid_data = Functions.validate_data(df=df, schema=CustomTableMetadataGet, debug=True)
86
86
 
87
87
  return valid_data, invalid_data
@@ -6,30 +6,20 @@ from brynq_sdk_functions import Functions
6
6
 
7
7
 
8
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'
9
+ def __init__(self, client):
10
+ self.client = client
14
11
 
15
12
  def get(self, person_id: datetime) -> pd.DataFrame:
16
- resp = self.bob.session.get(url=f"{self.bob.base_url}docs/people/{person_id}", timeout=self.bob.timeout)
13
+ resp = self.client.session.get(url=f"{self.client.base_url}docs/people/{person_id}", timeout=self.client.timeout)
17
14
  resp.raise_for_status()
18
15
  data = resp.json()['documents']
19
16
  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)
17
+ df = self.client.rename_camel_columns_to_snake_case(df)
28
18
 
29
19
  return df
30
20
 
31
21
  def get_folders(self) -> dict:
32
- resp = self.bob.session.get(url=f"{self.bob.base_url}docs/folders/metadata", timeout=self.bob.timeout)
22
+ resp = self.client.session.get(url=f"{self.client.base_url}docs/folders/metadata", timeout=self.client.timeout)
33
23
  resp.raise_for_status()
34
24
  data = resp.json()
35
25
 
@@ -41,7 +31,7 @@ class CustomDocuments:
41
31
  file_name: str,
42
32
  file_object: BytesIO):
43
33
  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",
34
+ resp = self.client.session.post(url=f"{self.client.base_url}docs/people/{person_id}/folders/{folder_id}/upload",
45
35
  files=files,
46
- timeout=self.bob.timeout)
36
+ timeout=self.client.timeout)
47
37
  resp.raise_for_status()
@@ -1,18 +1,19 @@
1
1
  import pandas as pd
2
2
  import requests
3
- from .schemas.employment import EmploymentSchema
3
+ from .schemas.employment import EmploymentGet
4
4
  from brynq_sdk_functions import Functions
5
5
 
6
6
 
7
7
  class Employment:
8
- def __init__(self, bob):
9
- self.bob = bob
10
- self.schema = EmploymentSchema
8
+ def __init__(self, client):
9
+ self.client = client
10
+ self.schema = EmploymentGet
11
11
 
12
- def get(self) -> (pd.DataFrame, pd.DataFrame):
12
+ def get(self) -> tuple[pd.DataFrame, pd.DataFrame]:
13
13
  request = requests.Request(method='GET',
14
- url=f"{self.bob.base_url}bulk/people/employment")
15
- data = self.bob.get_paginated_result(request)
14
+ url=f"{self.client.base_url}bulk/people/employment",
15
+ params={"includeArchived": "true"})
16
+ data = self.client.get_paginated_result(request)
16
17
  df = pd.json_normalize(
17
18
  data,
18
19
  record_path='values',
@@ -0,0 +1,37 @@
1
+ from typing import Tuple
2
+ import pandas as pd
3
+ from brynq_sdk_functions import Functions
4
+ from brynq_sdk_bob.schemas.entitlements import EntitlementReportGet
5
+
6
+
7
+ class Entitlements:
8
+ def __init__(self, client):
9
+ self.client = client
10
+
11
+ def get(self, report_id: str | None = None, debug: bool = False) -> tuple[pd.DataFrame, pd.DataFrame]:
12
+ """
13
+ Get entitlement history data from the Bob reports.
14
+
15
+ Args:
16
+ report_id: Optional report ID. If not provided, will search for 'Entitlement history' report.
17
+ debug: Whether to print debug information.
18
+
19
+ Returns:
20
+ Tuple of (valid_entitlements, invalid_entitlements) DataFrames
21
+ """
22
+ reports_df, _ = self.client.reports.get()
23
+ if report_id is None:
24
+ entitlement_report = reports_df[reports_df['name'] == 'Entitlement history']
25
+ if entitlement_report.empty:
26
+ raise ValueError("Report 'Entitlement history' not found.")
27
+ report_id = entitlement_report['id'].iloc[0]
28
+
29
+ report_data = self.client.reports.download(report_id=report_id)
30
+ df_raw = pd.json_normalize(report_data, record_path='employees')
31
+ valid_entitlements, invalid_entitlements = Functions.validate_data(
32
+ df=df_raw,
33
+ schema=EntitlementReportGet,
34
+ debug=debug,
35
+ )
36
+
37
+ return valid_entitlements, invalid_entitlements
@@ -1,15 +1,15 @@
1
1
  from datetime import datetime
2
2
  import pandas as pd
3
3
  from brynq_sdk_functions import Functions
4
- from .schemas.named_lists import NamedListSchema
4
+ from .schemas.named_lists import NamedListGet
5
5
 
6
6
 
7
7
  class NamedLists:
8
- def __init__(self, bob):
9
- self.bob = bob
10
- self.schema = NamedListSchema
8
+ def __init__(self, client):
9
+ self.client = client
10
+ self.schema = NamedListGet
11
11
 
12
- def get(self) -> (pd.DataFrame, pd.DataFrame):
12
+ def get(self) -> tuple[pd.DataFrame, pd.DataFrame]:
13
13
  """
14
14
  Get custom table data for an employee
15
15
 
@@ -19,8 +19,8 @@ class NamedLists:
19
19
  Returns:
20
20
  A tuple of (valid_data, invalid_data) as pandas DataFrames
21
21
  """
22
- url = f"{self.bob.base_url}company/named-lists/"
23
- resp = self.bob.session.get(url=url)
22
+ url = f"{self.client.base_url}company/named-lists/"
23
+ resp = self.client.session.get(url=url)
24
24
  resp.raise_for_status()
25
25
  data = resp.json()
26
26
 
@@ -31,7 +31,6 @@ class NamedLists:
31
31
  ])
32
32
 
33
33
  # Normalize the nested JSON response
34
- # df = pd.DataFrame(data.get('values'))
35
- valid_data, invalid_data = Functions.validate_data(df=df, schema=NamedListSchema, debug=True)
34
+ valid_data, invalid_data = Functions.validate_data(df=df, schema=NamedListGet, debug=True)
36
35
 
37
36
  return valid_data, invalid_data
brynq_sdk_bob/payments.py CHANGED
@@ -5,24 +5,25 @@ import pandas as pd
5
5
 
6
6
  from brynq_sdk_functions import Functions
7
7
 
8
- from .schemas.payments import ActualPaymentsSchema, VariablePaymentSchema
8
+ from .schemas.payments import ActualPaymentsGet, VariablePaymentGet
9
9
 
10
10
 
11
11
  class Payments:
12
- def __init__(self, bob):
13
- self.bob = bob
14
- self.schema = VariablePaymentSchema
12
+ def __init__(self, client):
13
+ self.client = client
14
+ self.schema = VariablePaymentGet
15
15
 
16
- def get(self, person_ids: List[str]) -> (pd.DataFrame, pd.DataFrame):
16
+ def get(self, person_ids: List[str]) -> tuple[pd.DataFrame, pd.DataFrame]:
17
17
  records = []
18
18
 
19
19
  for person_id in person_ids:
20
20
  # Throttle requests to respect API rate limits
21
21
  time.sleep(0.2)
22
22
 
23
- resp = self.bob.session.get(
24
- url=f"{self.bob.base_url}people/{person_id}/variable",
25
- timeout=self.bob.timeout
23
+ resp = self.client.session.get(
24
+ url=f"{self.client.base_url}people/{person_id}/variable",
25
+ timeout=self.client.timeout,
26
+ params={"includeArchived": "true"}
26
27
  )
27
28
  resp.raise_for_status()
28
29
 
@@ -46,7 +47,7 @@ class Payments:
46
47
  employee_ids: Optional[List[str]] = None,
47
48
  pay_date_from: Optional[str] = None,
48
49
  pay_date_to: Optional[str] = None
49
- ) -> (pd.DataFrame, pd.DataFrame):
50
+ ) -> tuple[pd.DataFrame, pd.DataFrame]:
50
51
  """
51
52
  Search for actual payments with optional employee and pay date filters.
52
53
  This method auto-paginates until all results are fetched.
@@ -101,10 +102,10 @@ class Payments:
101
102
  if next_cursor:
102
103
  payload["pagination"]["cursor"] = next_cursor
103
104
 
104
- resp = self.bob.session.post(
105
- url=f"{self.bob.base_url}people/actual-payments/search",
105
+ resp = self.client.session.post(
106
+ url=f"{self.client.base_url}people/actual-payments/search",
106
107
  json=payload,
107
- timeout=self.bob.timeout
108
+ timeout=self.client.timeout
108
109
  )
109
110
  resp.raise_for_status()
110
111
  data = resp.json()
@@ -125,7 +126,7 @@ class Payments:
125
126
 
126
127
  valid_payments, invalid_payments = Functions.validate_data(
127
128
  df=df,
128
- schema=ActualPaymentsSchema,
129
+ schema=ActualPaymentsGet,
129
130
  debug=True
130
131
  )
131
132
 
@@ -1,11 +1,11 @@
1
1
  import pandas as pd
2
2
  from brynq_sdk_functions import Functions
3
- from .schemas.people import PeopleSchema
3
+ from .schemas.people import PeopleGet
4
4
 
5
5
  class History:
6
- def __init__(self, bob):
7
- self.bob = bob
8
- self.schema = PeopleSchema
6
+ def __init__(self, client):
7
+ self.client = client
8
+ self.schema = PeopleGet
9
9
  self.field_name_in_body, self.field_name_in_response, self.endpoint_to_response = self._init_fields()
10
10
 
11
11
  def get(self, additional_fields: list[str] = [], field_selection: list[str] = []) -> tuple[pd.DataFrame, pd.DataFrame]:
@@ -16,7 +16,6 @@ class History:
16
16
  additional_fields (list[str]): Additional fields to get (not defined in the schema)
17
17
  field_selection (list[str]): Fields to get (defined in the schema), if not provided, all fields are returned
18
18
  """
19
- #resp = self.bob.session.get(url=f"{self.bob.base_url}profiles", timeout=self.bob.timeout)
20
19
  body_fields = list(set(self.field_name_in_body + additional_fields))
21
20
  response_fields = list(set(self.field_name_in_response + additional_fields))
22
21
 
@@ -25,25 +24,25 @@ class History:
25
24
  response_fields = [self.endpoint_to_response.get(field) for field in field_selection if field in self.endpoint_to_response]
26
25
 
27
26
  # Bob sucks with default fields so you need to do a search call to retrieve additional fields.
28
- resp_additional_fields = self.bob.session.post(url=f"{self.bob.base_url}people/search",
27
+ resp_additional_fields = self.client.session.post(url=f"{self.client.base_url}people/search",
29
28
  json={
30
29
  "fields": body_fields,
31
30
  "filters": []
32
31
  },
33
- timeout=self.bob.timeout)
32
+ timeout=self.client.timeout)
34
33
  json_response = resp_additional_fields.json()
35
34
  df = pd.json_normalize(resp_additional_fields.json()['employees'])
36
35
  df = df[[col for col in response_fields if col in df.columns]]
37
- # Get the valid column names from PeopleSchema
38
- valid_people, invalid_people = Functions.validate_data(df=df, schema=PeopleSchema, debug=True)
36
+ # Get the valid column names from PeopleGet
37
+ valid_people, invalid_people = Functions.validate_data(df=df, schema=PeopleGet, debug=True)
39
38
  return valid_people, invalid_people
40
39
 
41
40
 
42
41
  def _init_fields(self) -> tuple[list[str], list[str], dict[str, str]]:
43
- resp_fields = self.bob.session.get(
44
- url=f"{self.bob.base_url}company/people/fields",
45
- timeout=self.bob.timeout,
46
- headers=self.bob.headers
42
+ resp_fields = self.client.session.get(
43
+ url=f"{self.client.base_url}company/people/fields",
44
+ timeout=self.client.timeout,
45
+ headers=self.client.headers
47
46
  )
48
47
  fields = resp_fields.json()
49
48
  field_name_in_body = [field.get('id') for field in fields]
@@ -58,14 +57,14 @@ class History:
58
57
  body_fields = [employee_id_in_company, person_id]
59
58
  response_fields = [self.endpoint_to_response.get(field) for field in body_fields if field in self.endpoint_to_response]
60
59
 
61
- resp_additional_fields = self.bob.session.post(url=f"{self.bob.base_url}people/search",
60
+ resp_additional_fields = self.client.session.post(url=f"{self.client.base_url}people/search",
62
61
  json={
63
62
  "fields": body_fields,
64
63
  "filters": []
65
64
  },
66
- timeout=self.bob.timeout)
65
+ timeout=self.client.timeout)
67
66
  df = pd.json_normalize(resp_additional_fields.json()['employees'])
68
67
  df = df[[col for col in response_fields if col in df.columns]]
69
- # Get the valid column names from PeopleSchema
70
- valid_people, invalid_people = Functions.validate_data(df=df, schema=PeopleSchema, debug=True)
68
+ # Get the valid column names from PeopleGet
69
+ valid_people, invalid_people = Functions.validate_data(df=df, schema=PeopleGet, debug=True)
71
70
  return valid_people, invalid_people
brynq_sdk_bob/people.py CHANGED
@@ -9,19 +9,19 @@ from .bank import Bank
9
9
  from .custom_tables import CustomTables
10
10
  from .employment import Employment
11
11
  from .salaries import Salaries
12
- from .schemas.people import PeopleSchema
12
+ from .schemas.people import PeopleGet
13
13
  from .work import Work
14
14
 
15
15
 
16
16
  class People:
17
- def __init__(self, bob):
18
- self.bob = bob
19
- self.salaries = Salaries(bob)
20
- self.employment = Employment(bob)
21
- self.bank = Bank(bob)
22
- self.work = Work(bob)
23
- self.custom_tables = CustomTables(bob)
24
- self.schema = PeopleSchema
17
+ def __init__(self, client):
18
+ self.client = client
19
+ self.salaries = Salaries(client)
20
+ self.employment = Employment(client)
21
+ self.bank = Bank(client)
22
+ self.work = Work(client)
23
+ self.custom_tables = CustomTables(client)
24
+ self.schema = PeopleGet
25
25
 
26
26
 
27
27
 
@@ -33,19 +33,18 @@ class People:
33
33
  for col_name, col in schema.columns.items()
34
34
  ]
35
35
 
36
- def get(self, schema_custom_fields: Optional[BrynQPanderaDataFrameModel] = None) -> pd.DataFrame:
36
+ def get(self, schema_custom_fields: Optional[BrynQPanderaDataFrameModel] = None) -> tuple[pd.DataFrame, pd.DataFrame]:
37
37
 
38
- core_fields = self.__build_api_fields(PeopleSchema)
38
+ core_fields = self.__build_api_fields(PeopleGet)
39
39
  custom_fields = self.__build_api_fields(schema_custom_fields) if schema_custom_fields is not None else []
40
40
  fields = core_fields + custom_fields
41
41
 
42
- resp = self.bob.session.post(url=f"{self.bob.base_url}people/search",
42
+ resp = self.client.session.post(url=f"{self.client.base_url}people/search",
43
43
  json={
44
44
  "fields": fields,
45
45
  "filters": []
46
- #"humanReadable": "REPLACE"
47
46
  },
48
- timeout=self.bob.timeout)
47
+ timeout=self.client.timeout)
49
48
  resp.raise_for_status()
50
49
  df = pd.json_normalize(resp.json()['employees'])
51
50
 
@@ -72,7 +71,7 @@ class People:
72
71
 
73
72
 
74
73
  # A lot of fields from Bob are returned with only ID's. Those fields should be mapped to names. Therefore, we need to get the mapping from the named-lists endpoint.
75
- resp_named_lists = self.bob.session.get(url=f"{self.bob.base_url}company/named-lists", timeout=self.bob.timeout, headers=self.bob.headers)
74
+ resp_named_lists = self.client.session.get(url=f"{self.client.base_url}company/named-lists", timeout=self.client.timeout, headers=self.client.headers)
76
75
  named_lists = resp_named_lists.json()
77
76
 
78
77
  # Transform named_lists to create id-to-value mappings for each field
@@ -102,7 +101,7 @@ class People:
102
101
  invalid_people_custom = pd.DataFrame()
103
102
 
104
103
 
105
- valid_people, invalid_people = Functions.validate_data(df=valid_people, schema=PeopleSchema, debug=True)
104
+ valid_people, invalid_people = Functions.validate_data(df=valid_people, schema=PeopleGet, debug=True)
106
105
 
107
106
  # For columns ending with .value.value, use fillna to fill the corresponding base column since that's not done automatically
108
107
  def _normalize_key(key: str) -> str:
brynq_sdk_bob/reports.py CHANGED
@@ -1,38 +1,41 @@
1
- from datetime import datetime
2
- from io import BytesIO
3
- from typing import Optional, TYPE_CHECKING
1
+ from typing import Literal, TYPE_CHECKING
4
2
 
5
3
  import pandas as pd
6
4
  from brynq_sdk_functions import Functions
5
+ from brynq_sdk_bob.schemas.reports import ReportGet
7
6
  if TYPE_CHECKING:
8
7
  from brynq_sdk_bob import Bob
9
8
 
10
9
 
11
10
  class Reports:
12
- def __init__(self, bob):
13
- self.bob: Bob = bob
11
+ def __init__(self, client):
12
+ self.client: Bob = client
14
13
 
15
- def get(self) -> pd.DataFrame:
16
- resp = self.bob.session.get(url=f"{self.bob.base_url}company/reports", timeout=self.bob.timeout)
14
+ def get(self) -> tuple[pd.DataFrame, pd.DataFrame]:
15
+ resp = self.client.session.get(url=f"{self.client.base_url}company/reports", timeout=self.client.timeout)
17
16
  resp.raise_for_status()
18
17
  data = resp.json()
19
18
  df = pd.json_normalize(
20
19
  data,
21
20
  record_path='views'
22
21
  )
23
- # df = self.bob.rename_camel_columns_to_snake_case(df)
24
- # valid_documents, invalid_documents = Functions.validate_data(df=df, schema=DocumentsSchema, debug=True)
22
+ valid_reports, invalid_reports = Functions.validate_data(df=df, schema=ReportGet, debug=True)
25
23
 
26
- return df
24
+ return valid_reports, invalid_reports
27
25
 
28
- def download(self, report_id: int | str = None, report_format: str = "csv") -> bytes:
26
+ def download(self, report_id: int | str = None, report_format: Literal["json", "csv"] = "json") -> dict | bytes:
29
27
  if report_id:
30
- url = f"{self.bob.base_url}company/reports/{report_id}/download"
28
+ url = f"{self.client.base_url}company/reports/{report_id}/download"
31
29
  else:
32
30
  raise ValueError("Either report_id or report_name must be provided")
33
31
 
34
- resp = self.bob.session.get(url=url, timeout=self.bob.timeout, params={"format": report_format})
32
+ resp = self.client.session.get(url=url, timeout=self.client.timeout, params={"format": report_format})
35
33
  resp.raise_for_status()
36
- data = resp.content
34
+ if report_format == "json":
35
+ data = resp.json()
36
+ elif report_format == "csv":
37
+ data = resp.content
38
+ else:
39
+ raise ValueError(f"Invalid report format: {report_format}")
37
40
 
38
41
  return data
brynq_sdk_bob/salaries.py CHANGED
@@ -1,38 +1,38 @@
1
1
  import pandas as pd
2
2
  import requests
3
3
  from brynq_sdk_functions import Functions
4
- from .schemas.salary import SalarySchema, SalaryCreateSchema
4
+ from .schemas.salary import SalaryGet, SalaryCreate
5
5
 
6
6
 
7
7
  class Salaries:
8
- def __init__(self, bob):
9
- self.bob = bob
10
- self.schema = SalarySchema
8
+ def __init__(self, client):
9
+ self.client = client
10
+ self.schema = SalaryGet
11
11
 
12
12
  def get(self) -> tuple[pd.DataFrame, pd.DataFrame]:
13
13
  request = requests.Request(method='GET',
14
- url=f"{self.bob.base_url}bulk/people/salaries",
15
- params={"limit": 100})
16
- data = self.bob.get_paginated_result(request)
14
+ url=f"{self.client.base_url}bulk/people/salaries",
15
+ params={"limit": 100, "includeArchived": "true"})
16
+ data = self.client.get_paginated_result(request)
17
17
  df = pd.json_normalize(
18
18
  data,
19
19
  record_path='values',
20
20
  meta=['employeeId']
21
21
  )
22
- valid_salaries, invalid_salaries = Functions.validate_data(df=df, schema=SalarySchema, debug=True)
22
+ valid_salaries, invalid_salaries = Functions.validate_data(df=df, schema=SalaryGet, debug=True)
23
23
 
24
24
  return valid_salaries, invalid_salaries
25
25
 
26
26
  def create(self, salary_data: dict) -> requests.Response:
27
- nested_data = self.nmbrs.flat_dict_to_nested_dict(salary_data, SalaryCreateSchema)
28
- salary_data = SalaryCreateSchema(**nested_data)
27
+ nested_data = self.client.flat_dict_to_nested_dict(salary_data, SalaryCreate)
28
+ salary_data = SalaryCreate(**nested_data)
29
29
  payload = salary_data.model_dump(exclude_none=True, by_alias=True)
30
30
 
31
- resp = self.bob.session.post(url=f"{self.bob.base_url}people/{salary_data.employee_id}/salaries", json=payload)
31
+ resp = self.client.session.post(url=f"{self.client.base_url}people/{salary_data.employee_id}/salaries", json=payload)
32
32
  resp.raise_for_status()
33
33
  return resp
34
34
 
35
35
  def delete(self, employee_id: str, salary_id: str) -> requests.Response:
36
- resp = self.bob.session.delete(url=f"{self.bob.base_url}people/{employee_id}/salaries/{salary_id}")
36
+ resp = self.client.session.delete(url=f"{self.client.base_url}people/{employee_id}/salaries/{salary_id}")
37
37
  resp.raise_for_status()
38
38
  return resp
@@ -4,9 +4,9 @@ import pandas as pd
4
4
  from brynq_sdk_functions import BrynQPanderaDataFrameModel
5
5
 
6
6
 
7
- class BankSchema(BrynQPanderaDataFrameModel):
7
+ class BankGet(BrynQPanderaDataFrameModel):
8
8
  id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Bank ID", alias="id")
9
- employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
9
+ employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employee_id")
10
10
  amount: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Amount", alias="amount")
11
11
  allocation: Series[String] = pa.Field(coerce=True, nullable=True, description="Allocation", alias="allocation")
12
12
  branch_address: Series[String] = pa.Field(coerce=True, nullable=True, description="Branch Address", alias="branchAddress")
@@ -3,13 +3,13 @@ from pandera.typing import Series
3
3
  import pandas as pd
4
4
  from brynq_sdk_functions import BrynQPanderaDataFrameModel
5
5
 
6
- class CustomTableSchema(BrynQPanderaDataFrameModel):
6
+ class CustomTableGet(BrynQPanderaDataFrameModel):
7
7
  id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Custom Table ID", alias="id")
8
8
  employee_id: Series[str] = pa.Field(coerce=True, description="Employee ID", alias="employee_id")
9
9
 
10
10
  class Config:
11
11
  coerce = True
12
- class CustomTableMetadataSchema(BrynQPanderaDataFrameModel):
12
+ class CustomTableMetadataGet(BrynQPanderaDataFrameModel):
13
13
  # Table information
14
14
  table_id: Series[str] = pa.Field(coerce=True, description="Table ID", alias="table_id")
15
15
  table_name: Series[str] = pa.Field(coerce=True, description="Table Name", alias="table_name")
@@ -5,7 +5,7 @@ from pandera import Bool
5
5
  from pandera.typing import Series, String, Float, DateTime
6
6
  from brynq_sdk_functions import BrynQPanderaDataFrameModel
7
7
 
8
- class EmploymentSchema(BrynQPanderaDataFrameModel):
8
+ class EmploymentGet(BrynQPanderaDataFrameModel):
9
9
  id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Employment ID", alias="id")
10
10
  employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
11
11
  active_effective_date: Series[DateTime] = pa.Field(coerce=True, description="Active Effective Date", alias="activeEffectiveDate")
@@ -19,14 +19,14 @@ class EmploymentSchema(BrynQPanderaDataFrameModel):
19
19
  salary_pay_type: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Salary Pay Type", alias="salaryPayType")
20
20
  weekly_hours: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Weekly Hours", alias="weeklyHours")
21
21
  # weekly_hours_sort_factor: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=False)
22
- actual_working_pattern_working_pattern_type: Optional[Series[pa.String]] = pa.Field(nullable=True, description="Actual Working Pattern Working Pattern Type", alias="actualWorkingPattern.workingPatternType")
23
- actual_working_pattern_days_sunday: Optional[Series[Float]] = pa.Field(nullable=True, description="Actual Working Pattern Days Sunday", alias="actualWorkingPattern.days.sunday")
24
- actual_working_pattern_days_tuesday: Optional[Series[Float]] = pa.Field(nullable=True, description="Actual Working Pattern Days Tuesday", alias="actualWorkingPattern.days.tuesday")
25
- actual_working_pattern_days_wednesday: Optional[Series[Float]] = pa.Field(nullable=True, description="Actual Working Pattern Days Wednesday", alias="actualWorkingPattern.days.wednesday")
26
- actual_working_pattern_days_monday: Optional[Series[Float]] = pa.Field(nullable=True, description="Actual Working Pattern Days Monday", alias="actualWorkingPattern.days.monday")
27
- actual_working_pattern_days_friday: Optional[Series[Float]] = pa.Field(nullable=True, description="Actual Working Pattern Days Friday", alias="actualWorkingPattern.days.friday")
28
- actual_working_pattern_days_thursday: Optional[Series[Float]] = pa.Field(nullable=True, description="Actual Working Pattern Days Thursday", alias="actualWorkingPattern.days.thursday")
29
- actual_working_pattern_days_saturday: Optional[Series[Float]] = pa.Field(nullable=True, description="Actual Working Pattern Days Saturday", alias="actualWorkingPattern.days.saturday")
22
+ actual_working_pattern_working_pattern_type: Optional[Series[pa.String]] = pa.Field(coerce=True, nullable=True, description="Actual Working Pattern Working Pattern Type", alias="actualWorkingPattern.workingPatternType")
23
+ actual_working_pattern_days_sunday: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Actual Working Pattern Days Sunday", alias="actualWorkingPattern.days.sunday")
24
+ actual_working_pattern_days_tuesday: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Actual Working Pattern Days Tuesday", alias="actualWorkingPattern.days.tuesday")
25
+ actual_working_pattern_days_wednesday: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Actual Working Pattern Days Wednesday", alias="actualWorkingPattern.days.wednesday")
26
+ actual_working_pattern_days_monday: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Actual Working Pattern Days Monday", alias="actualWorkingPattern.days.monday")
27
+ actual_working_pattern_days_friday: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Actual Working Pattern Days Friday", alias="actualWorkingPattern.days.friday")
28
+ actual_working_pattern_days_thursday: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Actual Working Pattern Days Thursday", alias="actualWorkingPattern.days.thursday")
29
+ actual_working_pattern_days_saturday: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Actual Working Pattern Days Saturday", alias="actualWorkingPattern.days.saturday")
30
30
 
31
31
  class Config:
32
32
  coerce = True
@@ -0,0 +1,110 @@
1
+ import pandera as pa
2
+ from typing import Optional
3
+ import pandas as pd
4
+ from brynq_sdk_functions import BrynQPanderaDataFrameModel
5
+
6
+
7
+ class EntitlementReportGet(BrynQPanderaDataFrameModel):
8
+ surname: Optional[pd.StringDtype] = pa.Field(
9
+ coerce=True,
10
+ nullable=True,
11
+ description="Employee surname",
12
+ alias="surname",
13
+ )
14
+ first_name: Optional[pd.StringDtype] = pa.Field(
15
+ coerce=True,
16
+ nullable=True,
17
+ description="Employee first name",
18
+ alias="firstName",
19
+ )
20
+ employee_id: pd.StringDtype = pa.Field(
21
+ coerce=True,
22
+ description="Employee identifier",
23
+ alias="id",
24
+ )
25
+ entitlement_name: pd.StringDtype = pa.Field(
26
+ coerce=True,
27
+ description="Entitlement name",
28
+ alias="payroll.entitlement.entitlement",
29
+ )
30
+ entitlement_is_current: pd.BooleanDtype = pa.Field(
31
+ coerce=True,
32
+ description="Whether entitlement is current",
33
+ alias="payroll.entitlement.isCurrent",
34
+ )
35
+ entitlement_end_date: Optional[str] = pa.Field(
36
+ coerce=True,
37
+ nullable=True,
38
+ description="Entitlement end date",
39
+ alias="payroll.entitlement.endDate",
40
+ )
41
+ entitlement_can_be_deleted: pd.BooleanDtype = pa.Field(
42
+ coerce=True,
43
+ description="Whether entitlement can be deleted",
44
+ alias="payroll.entitlement.canBeDeleted",
45
+ )
46
+ entitlement_amount_value: Optional[pd.Float64Dtype] = pa.Field(
47
+ coerce=True,
48
+ nullable=True,
49
+ description="Entitlement amount value",
50
+ alias="payroll.entitlement.amount.value",
51
+ )
52
+ entitlement_amount_currency: Optional[pd.StringDtype] = pa.Field(
53
+ coerce=True,
54
+ nullable=True,
55
+ description="Entitlement currency",
56
+ alias="payroll.entitlement.amount.currency",
57
+ )
58
+ entitlement_end_effective_date: Optional[str] = pa.Field(
59
+ coerce=True,
60
+ nullable=True,
61
+ description="Entitlement end effective date",
62
+ alias="payroll.entitlement.endEffectiveDate",
63
+ )
64
+ entitlement_change_reason: Optional[pd.StringDtype] = pa.Field(
65
+ coerce=True,
66
+ nullable=True,
67
+ description="Reason for change",
68
+ alias="payroll.entitlement.change.reason",
69
+ )
70
+ entitlement_change_changed_by: Optional[pd.StringDtype] = pa.Field(
71
+ coerce=True,
72
+ nullable=True,
73
+ description="User that changed the entitlement",
74
+ alias="payroll.entitlement.change.changedBy",
75
+ )
76
+ entitlement_change_changed_by_id: Optional[pd.StringDtype] = pa.Field(
77
+ coerce=True,
78
+ nullable=True,
79
+ description="Identifier of the user that changed the entitlement",
80
+ alias="payroll.entitlement.change.changedById",
81
+ )
82
+ entitlement_id: pd.Int64Dtype = pa.Field(
83
+ coerce=True,
84
+ description="Entitlement record identifier",
85
+ alias="payroll.entitlement.id",
86
+ )
87
+ entitlement_effective_date: Optional[str] = pa.Field(
88
+ coerce=True,
89
+ nullable=True,
90
+ description="Entitlement effective date",
91
+ alias="payroll.entitlement.effectiveDate",
92
+ )
93
+ entitlement_creation_date: Optional[str] = pa.Field(
94
+ coerce=True,
95
+ nullable=True,
96
+ description="Entitlement creation date",
97
+ alias="payroll.entitlement.creationDate",
98
+ )
99
+ entitlement_modification_date: Optional[str] = pa.Field(
100
+ coerce=True,
101
+ nullable=True,
102
+ description="Entitlement modification date",
103
+ alias="payroll.entitlement.modificationDate",
104
+ )
105
+
106
+ class Config:
107
+ coerce = True
108
+
109
+ class _Annotation:
110
+ primary_key = "entitlement_id"
@@ -2,7 +2,7 @@ import pandera as pa
2
2
  from pandera.typing import Series
3
3
  from brynq_sdk_functions import BrynQPanderaDataFrameModel
4
4
 
5
- class NamedListSchema(BrynQPanderaDataFrameModel):
5
+ class NamedListGet(BrynQPanderaDataFrameModel):
6
6
  id: Series[str] = pa.Field(coerce=True, description="Named List ID", alias="id")
7
7
  value: Series[str] = pa.Field(coerce=True, description="Named List Value", alias="value")
8
8
  name: Series[str] = pa.Field(coerce=True, description="Named List Name", alias="name")
@@ -7,7 +7,7 @@ from pandera.typing import DateTime, Float, Series, String
7
7
  from brynq_sdk_functions import BrynQPanderaDataFrameModel
8
8
 
9
9
 
10
- class VariablePaymentSchema(BrynQPanderaDataFrameModel):
10
+ class VariablePaymentGet(BrynQPanderaDataFrameModel):
11
11
  can_be_deleted: Series[bool] = pa.Field(nullable=True, coerce=True, description="Can Be Deleted", alias="canBeDeleted")
12
12
  department_percent: Series[Float] = pa.Field(nullable=True, coerce=True, description="Department Percent", alias="departmentPercent")
13
13
  payout_type: Series[String] = pa.Field(coerce=True, description="Payout Type", alias="payoutType")
@@ -33,7 +33,7 @@ class VariablePaymentSchema(BrynQPanderaDataFrameModel):
33
33
  class Config:
34
34
  coerce = True
35
35
 
36
- class ActualPaymentsSchema(BrynQPanderaDataFrameModel):
36
+ class ActualPaymentsGet(BrynQPanderaDataFrameModel):
37
37
  employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
38
38
  id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Payment ID", alias="id")
39
39
  pay_date: Series[DateTime] = pa.Field(coerce=True, description="Pay Date", alias="payDate")
@@ -45,3 +45,4 @@ class ActualPaymentsSchema(BrynQPanderaDataFrameModel):
45
45
 
46
46
  class Config:
47
47
  coerce = True
48
+
@@ -15,7 +15,7 @@ def check_list(x):
15
15
  return isinstance(x, list)
16
16
 
17
17
 
18
- class PayrollHistorySchema(BrynQPanderaDataFrameModel):
18
+ class PayrollHistoryGet(BrynQPanderaDataFrameModel):
19
19
  id: Optional[Series[String]] = pa.Field(coerce=True, description="Person ID", alias="id")
20
20
  display_name: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Display Name", alias="displayName")
21
21
  company_id: Optional[Series[String]] = pa.Field(coerce=True, description="Company ID", alias="companyId")
@@ -16,7 +16,7 @@ def check_list(x):
16
16
  return isinstance(x, list)
17
17
 
18
18
 
19
- class PeopleSchema(BrynQPanderaDataFrameModel):
19
+ class PeopleGet(BrynQPanderaDataFrameModel):
20
20
  id: Optional[Series[String]] = pa.Field(coerce=True, description="Person ID", alias="id", metadata={"api_field": "root.id"})
21
21
  display_name: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Display Name", alias="displayName", metadata={"api_field": "root.displayName"})
22
22
  company_id: Optional[Series[String]] = pa.Field(coerce=True, description="Company ID", alias="companyId", metadata={"api_field": "root.companyId"})
@@ -0,0 +1,129 @@
1
+ import pandera as pa
2
+ from typing import Optional
3
+ import pandas as pd
4
+ from brynq_sdk_functions import BrynQPanderaDataFrameModel
5
+
6
+
7
+ class ReportGet(BrynQPanderaDataFrameModel):
8
+ modification_date: Optional[str] = pa.Field(
9
+ coerce=True,
10
+ nullable=True,
11
+ description="Modification date of the report view",
12
+ alias="modificationDate",
13
+ )
14
+ created_by: pd.StringDtype = pa.Field(
15
+ coerce=True,
16
+ description="User identifier that created the report",
17
+ alias="createdBy",
18
+ )
19
+ configuration_instructions: Optional[object] = pa.Field(
20
+ coerce=True,
21
+ nullable=True,
22
+ description="Configuration instructions for the report view",
23
+ alias="configuration.instructions",
24
+ )
25
+ configuration_changes: Optional[object] = pa.Field(
26
+ coerce=True,
27
+ nullable=True,
28
+ description="Configuration changes associated with the report view",
29
+ alias="configuration.changes",
30
+ )
31
+ configuration_display_preferences_export_configuration: Optional[object] = pa.Field(
32
+ coerce=True,
33
+ nullable=True,
34
+ description="Export configuration within display preferences",
35
+ alias="configuration.displayPreferences.exportConfiguration",
36
+ )
37
+ configuration_display_preferences_include_flat_json: pd.BooleanDtype = pa.Field(
38
+ coerce=True,
39
+ description="Flag to include flat JSON in the export",
40
+ alias="configuration.displayPreferences.includeFlatJson",
41
+ )
42
+ configuration_display_preferences_date_format: Optional[pd.StringDtype] = pa.Field(
43
+ coerce=True,
44
+ nullable=True,
45
+ description="Date format specified in display preferences",
46
+ alias="configuration.displayPreferences.dateFormat",
47
+ )
48
+ configuration_display_preferences_support_error_wrapping: pd.BooleanDtype = pa.Field(
49
+ coerce=True,
50
+ description="Whether error wrapping is supported in display preferences",
51
+ alias="configuration.displayPreferences.supportErrorWrapping",
52
+ )
53
+ configuration_display_preferences_columns_properties: Optional[object] = pa.Field(
54
+ coerce=True,
55
+ nullable=True,
56
+ description="Column properties defined in display preferences",
57
+ alias="configuration.displayPreferences.columnsProperties",
58
+ )
59
+ configuration_display_preferences_include_human_readable: pd.BooleanDtype = pa.Field(
60
+ coerce=True,
61
+ description="Flag to include human-readable output in display preferences",
62
+ alias="configuration.displayPreferences.includeHumanReadable",
63
+ )
64
+ configuration_sort_by: Optional[object] = pa.Field(
65
+ coerce=True,
66
+ nullable=True,
67
+ description="Sort configuration for the report view",
68
+ alias="configuration.sortBy",
69
+ )
70
+ configuration_fields: Optional[object] = pa.Field(
71
+ coerce=True,
72
+ nullable=True,
73
+ description="Fields included in the report view",
74
+ alias="configuration.fields",
75
+ )
76
+ is_comparable: pd.BooleanDtype = pa.Field(
77
+ coerce=True,
78
+ description="Flag indicating if the report is comparable",
79
+ alias="isComparable",
80
+ )
81
+ name: Optional[pd.StringDtype] = pa.Field(
82
+ coerce=True,
83
+ nullable=True,
84
+ description="Name of the report view",
85
+ alias="name",
86
+ )
87
+ description: Optional[pd.StringDtype] = pa.Field(
88
+ coerce=True,
89
+ nullable=True,
90
+ description="Description of the report view",
91
+ alias="description",
92
+ )
93
+ schedule_status: Optional[pd.StringDtype] = pa.Field(
94
+ coerce=True,
95
+ nullable=True,
96
+ description="Scheduling status of the report view",
97
+ alias="scheduleStatus",
98
+ )
99
+ is_scheduled: pd.BooleanDtype = pa.Field(
100
+ coerce=True,
101
+ description="Flag indicating if the report is scheduled",
102
+ alias="isScheduled",
103
+ )
104
+ id: pd.StringDtype = pa.Field(
105
+ coerce=True,
106
+ description="Report identifier",
107
+ alias="id",
108
+ )
109
+ type: pd.StringDtype = pa.Field(
110
+ coerce=True,
111
+ description="Type of the report",
112
+ alias="type",
113
+ )
114
+ creation_date: str = pa.Field(
115
+ coerce=True,
116
+ description="Creation date of the report view",
117
+ alias="creationDate",
118
+ )
119
+ folder_id: pd.Int64Dtype = pa.Field(
120
+ coerce=True,
121
+ description="Identifier of the folder containing the report view",
122
+ alias="folderId",
123
+ )
124
+
125
+ class Config:
126
+ coerce = True
127
+
128
+ class _Annotation:
129
+ primary_key = "id"
@@ -11,7 +11,7 @@ from pydantic import BaseModel, Field
11
11
  from brynq_sdk_functions import BrynQPanderaDataFrameModel
12
12
 
13
13
 
14
- class SalarySchema(BrynQPanderaDataFrameModel):
14
+ class SalaryGet(BrynQPanderaDataFrameModel):
15
15
  id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Salary ID", alias="id")
16
16
  employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
17
17
  pay_frequency: Series[String] = pa.Field(coerce=True, nullable=True, description="Pay Frequency", alias="payFrequency") # has a list of possible values , isin=['Monthly']
@@ -26,7 +26,7 @@ class SalarySchema(BrynQPanderaDataFrameModel):
26
26
  active_effective_date: Series[DateTime] = pa.Field(coerce=True, nullable=True, description="Active Effective Date", alias="activeEffectiveDate")
27
27
 
28
28
 
29
- class SalaryCreateSchema(BaseModel):
29
+ class SalaryCreate(BaseModel):
30
30
  # Function parameters
31
31
  salary_data: Optional[Dict[str, Any]] = Field(None, description="Salary data", alias="salaryData")
32
32
 
@@ -49,5 +49,4 @@ class SalaryCreateSchema(BaseModel):
49
49
  effective_date: Optional[date] = Field(None, description="The date this entry becomes effective. This is a mandatory field for a work entry.", alias="effectiveDate")
50
50
 
51
51
  class Config:
52
- allow_population_by_field_name = True
53
- coerce = True
52
+ populate_by_name = True
@@ -9,7 +9,7 @@ from brynq_sdk_functions import BrynQPanderaDataFrameModel
9
9
  # TimeOffSchema - For /timeoff/requests/changes endpoint (change events)
10
10
  # =============================================================================
11
11
 
12
- class TimeOffSchema(BrynQPanderaDataFrameModel):
12
+ class TimeOffGet(BrynQPanderaDataFrameModel):
13
13
  """Schema for time off change events from /timeoff/requests/changes endpoint."""
14
14
  change_type: Series[String] = pa.Field(coerce=True, description="Change Type", alias="changeType")
15
15
  employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
@@ -219,7 +219,7 @@ class TimeOffRequest(BrynQPanderaDataFrameModel):
219
219
  # TimeOffBalanceSchema - For /timeoff/employees/{id}/balance endpoint
220
220
  # =============================================================================
221
221
 
222
- class TimeOffBalanceSchema(BrynQPanderaDataFrameModel):
222
+ class TimeOffBalanceGet(BrynQPanderaDataFrameModel):
223
223
  """Schema for time off balance from /timeoff/employees/{id}/balance endpoint."""
224
224
  employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
225
225
  policy_type_name: Series[String] = pa.Field(coerce=True, description="Policy Type Name", alias="policyTypeName")
@@ -8,7 +8,7 @@ from pandera.typing import Series
8
8
  from brynq_sdk_functions import BrynQPanderaDataFrameModel
9
9
 
10
10
 
11
- class WorkSchema(BrynQPanderaDataFrameModel):
11
+ class WorkGet(BrynQPanderaDataFrameModel):
12
12
  can_be_deleted: Series[pa.Bool] = pa.Field(coerce=True, description="Can Be Deleted", alias="canBeDeleted")
13
13
  work_change_type: Series[str] = pa.Field(coerce=True, description="Work Change Type", alias="workChangeType")
14
14
  creation_date: Series[datetime] = pa.Field(coerce=True, nullable=True, description="Creation Date", alias="creationDate")
brynq_sdk_bob/timeoff.py CHANGED
@@ -2,15 +2,15 @@ from datetime import datetime, timezone, timedelta
2
2
  from typing import Union
3
3
  import pandas as pd
4
4
  from brynq_sdk_functions import Functions
5
- from .schemas.timeoff import TimeOffSchema, TimeOffBalanceSchema, TimeOffRequest
5
+ from .schemas.timeoff import TimeOffGet, TimeOffBalanceGet, TimeOffRequest
6
6
  import warnings
7
7
 
8
8
 
9
9
  class TimeOff:
10
- def __init__(self, bob):
11
- self.bob = bob
12
- self.schema = TimeOffSchema
13
- self.balance_schema = TimeOffBalanceSchema
10
+ def __init__(self, client):
11
+ self.client = client
12
+ self.schema = TimeOffGet
13
+ self.balance_schema = TimeOffBalanceGet
14
14
 
15
15
  def get(self, since: datetime = None, include_pending: bool = False) -> tuple[pd.DataFrame, pd.DataFrame]:
16
16
  """
@@ -37,9 +37,9 @@ class TimeOff:
37
37
  since = six_months_ago
38
38
 
39
39
  since = since.replace(tzinfo=timezone.utc).isoformat(timespec='milliseconds').replace('+00:00', 'Z')
40
- resp = self.bob.session.get(url=f"{self.bob.base_url}timeoff/requests/changes",
40
+ resp = self.client.session.get(url=f"{self.client.base_url}timeoff/requests/changes",
41
41
  params={'since': since, 'includePending': 'true' if include_pending else 'false'},
42
- timeout=self.bob.timeout)
42
+ timeout=self.client.timeout)
43
43
  resp.raise_for_status()
44
44
  data = resp.json().get('changes', [])
45
45
  df = pd.DataFrame(data)
@@ -63,9 +63,9 @@ class TimeOff:
63
63
  Returns:
64
64
  tuple[pd.DataFrame, pd.DataFrame]: (valid_request, invalid_request) as single-row DataFrames.
65
65
  """
66
- resp = self.bob.session.get(
67
- url=f"{self.bob.base_url}timeoff/employees/{employee_id}/requests/{request_id}",
68
- timeout=self.bob.timeout
66
+ resp = self.client.session.get(
67
+ url=f"{self.client.base_url}timeoff/employees/{employee_id}/requests/{request_id}",
68
+ timeout=self.client.timeout
69
69
  )
70
70
  resp.raise_for_status()
71
71
  data = resp.json()
@@ -96,10 +96,10 @@ class TimeOff:
96
96
  if as_of_date:
97
97
  params['asOfDate'] = as_of_date
98
98
 
99
- resp = self.bob.session.get(
100
- url=f"{self.bob.base_url}timeoff/employees/{employee_id}/balance",
99
+ resp = self.client.session.get(
100
+ url=f"{self.client.base_url}timeoff/employees/{employee_id}/balance",
101
101
  params=params,
102
- timeout=self.bob.timeout
102
+ timeout=self.client.timeout
103
103
  )
104
104
  resp.raise_for_status()
105
105
  data = resp.json()
brynq_sdk_bob/work.py CHANGED
@@ -1,18 +1,19 @@
1
1
  import pandas as pd
2
2
  import requests
3
3
  from brynq_sdk_functions import Functions
4
- from .schemas.work import WorkSchema
4
+ from .schemas.work import WorkGet
5
5
 
6
6
 
7
7
  class Work:
8
- def __init__(self, bob):
9
- self.bob = bob
10
- self.schema = WorkSchema
8
+ def __init__(self, client):
9
+ self.client = client
10
+ self.schema = WorkGet
11
11
 
12
- def get(self) ->(pd.DataFrame, pd.DataFrame):
12
+ def get(self) -> tuple[pd.DataFrame, pd.DataFrame]:
13
13
  request = requests.Request(method='GET',
14
- url=f"{self.bob.base_url}bulk/people/work")
15
- data = self.bob.get_paginated_result(request)
14
+ url=f"{self.client.base_url}bulk/people/work",
15
+ params={"includeArchived": "true"})
16
+ data = self.client.get_paginated_result(request)
16
17
  df = pd.json_normalize(
17
18
  data,
18
19
  record_path='values',
@@ -1,12 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: brynq_sdk_bob
3
- Version: 2.9.5
3
+ Version: 2.10.1
4
4
  Summary: Bob wrapper from BrynQ
5
5
  Author: BrynQ
6
6
  Author-email: support@brynq.com
7
7
  License: BrynQ License
8
8
  Requires-Dist: brynq-sdk-brynq<5,>=4
9
+ Requires-Dist: brynq-sdk-functions>=2.0.5
9
10
  Requires-Dist: pandas<3.0.0,>=2.2.0
11
+ Requires-Dist: pydantic<3.0.0,>=2.5.0
12
+ Requires-Dist: pandera<1.0.0,>=0.16.0
10
13
  Dynamic: author
11
14
  Dynamic: author-email
12
15
  Dynamic: description
@@ -0,0 +1,32 @@
1
+ brynq_sdk_bob/__init__.py,sha256=D922dfjXAPti__4DBtQCAV6ilc60EU59ljHqCRb8nbw,3984
2
+ brynq_sdk_bob/bank.py,sha256=nhePvkOXFQ9TZb90dNGKaB_j3yJ49nBzyf0oVC7dYyw,980
3
+ brynq_sdk_bob/company.py,sha256=MWkDs8tilBIiHR0OQpT-CrbsZYJn-SAUvOtSEtv7VUQ,875
4
+ brynq_sdk_bob/custom_tables.py,sha256=IXxMv6jXn89cQmsISdUNaf6I0zbioP_NwXCaGZlPWPo,3265
5
+ brynq_sdk_bob/documents.py,sha256=hGgl2XGxbEyOGua7MtaCrK8F8iCPFYXEJR8OXsldEOs,1301
6
+ brynq_sdk_bob/employment.py,sha256=2fVHl3nlcntF7tr03F4MD-DLyKuo02yXQaaAJPz7qrs,848
7
+ brynq_sdk_bob/entitlements.py,sha256=Erw0BsY0NWV1RIsh1mUeBMmS177o-RjmZYX-m1MeoaM,1419
8
+ brynq_sdk_bob/named_lists.py,sha256=Om_4zBUNmqaDksjQpaf1HI7S2Pa3Dhpx3b5TZUNiSmM,1032
9
+ brynq_sdk_bob/payments.py,sha256=BEli8K9DoUwb9-WDC014FZ6Me0wy0FbHPdcTkSJt48s,4298
10
+ brynq_sdk_bob/payroll_history.py,sha256=8bhlqlGhOgUo7B0BMWH9AV8jYPhB8TItF6pvKAQrRwI,3894
11
+ brynq_sdk_bob/people.py,sha256=cVgYzVImjbU3JiY6dYK-EpOcGWriRC_uFQxvfruXjyw,6195
12
+ brynq_sdk_bob/reports.py,sha256=FaRQXgca4_bQy6y55jMPzwZR8BgtiKGFO_GVYZ9bEbU,1465
13
+ brynq_sdk_bob/salaries.py,sha256=0JpigQr1lLNPCbk3DEo6LHkK6fJjZxTHeXlgMxvXFYE,1551
14
+ brynq_sdk_bob/timeoff.py,sha256=QGvjgseOKg3znt2l4zXEBXar6Z9jowHelQVVgwn4gCM,4462
15
+ brynq_sdk_bob/work.py,sha256=ERDbj92dWmcbFakFlY7FwLpnitHo6sTeyVW-GINT5js,798
16
+ brynq_sdk_bob/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ brynq_sdk_bob/schemas/bank.py,sha256=Oci8ZN_e0prV3sF5F9sZsUOphhtFCmcCYHT900w1pcM,1844
18
+ brynq_sdk_bob/schemas/custom_tables.py,sha256=WZFReQQxAj2cLzlhnfWoyogR14B56UDXYD4n-Akcq6o,1561
19
+ brynq_sdk_bob/schemas/employment.py,sha256=yG3SI7jma30PAjNBozXrAzfaNfjwSIaCU4Xn1CUWje4,3488
20
+ brynq_sdk_bob/schemas/entitlements.py,sha256=5wAyqLso238MMmjO3tuq_szXB_cdK1Q96qgyE5GH6u0,3648
21
+ brynq_sdk_bob/schemas/named_lists.py,sha256=QBHK1JDbKAvOD2PU6ycZcGd5DI6HP44LeVDeqEVFDIE,722
22
+ brynq_sdk_bob/schemas/payments.py,sha256=Ob4ebX8bVo8UM2XboCgeqN-8tXUDHTD5Q6eo0Ec3fyk,4038
23
+ brynq_sdk_bob/schemas/payroll_history.py,sha256=T2vuzuPDKoEoOiX4RVf_Qnwg0yjx4AMLavAzXc1csy4,803
24
+ brynq_sdk_bob/schemas/people.py,sha256=NFm9-3-4c-7lRBNv5JxnRA_rAMu2RLGoF2mEQs0t_h4,40438
25
+ brynq_sdk_bob/schemas/reports.py,sha256=h0vrrzFkwTYR6DZ1VxkK2B7ZQBNWPWA_ukSDevyDlMs,4541
26
+ brynq_sdk_bob/schemas/salary.py,sha256=czKJYjzMTSP0SHYIWEJFS-rjQ4NYjZCGV2PErrcKLIY,4505
27
+ brynq_sdk_bob/schemas/timeoff.py,sha256=XYN489nn6BhKxYOARrpB6-7IUhsSanlpRCQz6gzGGFg,12811
28
+ brynq_sdk_bob/schemas/work.py,sha256=X9QS2psQfZ9torbjNBUDmp9X2KLYdgRpuS2cvyy4waM,3014
29
+ brynq_sdk_bob-2.10.1.dist-info/METADATA,sha256=vU7LlX1K57bIP4CSVN6ISokIJ1KfSYf2CP5ExaYd5TA,490
30
+ brynq_sdk_bob-2.10.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
31
+ brynq_sdk_bob-2.10.1.dist-info/top_level.txt,sha256=oGiWqOuAAiVoLIzGe6F-Lo4IJBYz5ftOwBft7HtPuoY,14
32
+ brynq_sdk_bob-2.10.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.10.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,29 +0,0 @@
1
- brynq_sdk_bob/__init__.py,sha256=sP1DRvGpViN8e0yMw9RMHMmuFH8-0rgv0m4E5vRZYuI,3901
2
- brynq_sdk_bob/bank.py,sha256=zTdfe_qCZt2FB7SZbQ7njIDspwTinLFdbeH_xUby2FY,966
3
- brynq_sdk_bob/company.py,sha256=rjOpkm0CZ1EeJ-jddBl36GrGKUQviC1ca1aUL2tl1_M,848
4
- brynq_sdk_bob/custom_tables.py,sha256=VHcZYxGU-rRZjo5tBaAVAawsUxgh90CeVT9rsxMfzvY,3253
5
- brynq_sdk_bob/documents.py,sha256=ww101owiBGARCxOANdDtmWrNedSBe9V-BEed6QspQPg,1756
6
- brynq_sdk_bob/employment.py,sha256=uNllQrIBbo8yPG_2-ln1PWeWUFU672T289PpWWvL-V8,763
7
- brynq_sdk_bob/named_lists.py,sha256=ksLXV2ysBFegq4gZiiaC56gjkgdnPzL7WajZTGvjYIM,1069
8
- brynq_sdk_bob/payments.py,sha256=43Ctdt5T8gtpCud67dm75xa90W8m6FyMrU2hAWKrzMc,4221
9
- brynq_sdk_bob/payroll_history.py,sha256=wHo6da7kLDe1ViL4egyMdyJBMZnWVhwjNjmh4cTCTeY,3972
10
- brynq_sdk_bob/people.py,sha256=Cg7I-Ibo4UdhyjOtAtjmcK9nd5AwQWy2B9Zsn6VhZzU,6211
11
- brynq_sdk_bob/reports.py,sha256=Tawmqm_ZmQ487loyk-29-A_fTCrgImbWCEf6zfwuaq4,1245
12
- brynq_sdk_bob/salaries.py,sha256=BGQm-PT9QuKKJ9DP5nX6wmC8SZRAlm9M9I2EJhoZaII,1523
13
- brynq_sdk_bob/timeoff.py,sha256=JtTu14PWFqQIEn9r-Z8ipeNE-5p7hqPz5N6wjjBeLTs,4438
14
- brynq_sdk_bob/work.py,sha256=0bVZkQ0I6z-z2_ql-EsOpFExx8VgsJvpcCQdOfiJYQM,712
15
- brynq_sdk_bob/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- brynq_sdk_bob/schemas/bank.py,sha256=lDmXP4P8091N20fL2CmhPU2wFuaK60Z-p-dvYSNCMaQ,1846
17
- brynq_sdk_bob/schemas/custom_tables.py,sha256=638NH2n50vHzw1LFlbKVCBKBhfSsfGqtEGuor0Z-QS4,1567
18
- brynq_sdk_bob/schemas/employment.py,sha256=1LsPjp-TexEc4B9nXjciMKFw94bl6xhEp3TNsgnLcds,3387
19
- brynq_sdk_bob/schemas/named_lists.py,sha256=HJBRKrAI2vhrkq-5MVXqQcmpGNzFtoOnaZI2Ii_6_vs,725
20
- brynq_sdk_bob/schemas/payments.py,sha256=ZaF34kXCroX_AG5jm9XRrfG2QwMbkn-I4wsnWgJMFpk,4043
21
- brynq_sdk_bob/schemas/payroll_history.py,sha256=JdAq0XaArHHEw8EsXo3GD0EhSAyBhPtYQMmdvjCiY8g,806
22
- brynq_sdk_bob/schemas/people.py,sha256=H0HgSIPVVBWeyXU1N7J6NUgohfFsBWK4hfxwMMqCDR8,40441
23
- brynq_sdk_bob/schemas/salary.py,sha256=WIGlN0CVcG5PvA0OQuSrFdPjeFVJdEN87Hzm3hPRTHc,4550
24
- brynq_sdk_bob/schemas/timeoff.py,sha256=gTYu_bNcfHrkTz4eIHCZ4WzgMTj2U4nI3X6JTzDovhk,12817
25
- brynq_sdk_bob/schemas/work.py,sha256=1odd3ia97SZff8VjLzL1a0FEQaF2ojGeWsfovcWvkhM,3017
26
- brynq_sdk_bob-2.9.5.dist-info/METADATA,sha256=Ur-X7hK8zONgW_QA-jC0CDIzi6dDqtcYspmHvlhE77o,371
27
- brynq_sdk_bob-2.9.5.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
28
- brynq_sdk_bob-2.9.5.dist-info/top_level.txt,sha256=oGiWqOuAAiVoLIzGe6F-Lo4IJBYz5ftOwBft7HtPuoY,14
29
- brynq_sdk_bob-2.9.5.dist-info/RECORD,,