brynq-sdk-bob 2.5.0__tar.gz → 2.6.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.
Files changed (37) hide show
  1. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/PKG-INFO +1 -1
  2. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/custom_tables.py +1 -1
  3. brynq_sdk_bob-2.6.0/brynq_sdk_bob/people.py +59 -0
  4. brynq_sdk_bob-2.6.0/brynq_sdk_bob/salaries.py +38 -0
  5. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/schemas/employment.py +1 -1
  6. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/schemas/payments.py +3 -2
  7. brynq_sdk_bob-2.6.0/brynq_sdk_bob/schemas/people.py +284 -0
  8. brynq_sdk_bob-2.6.0/brynq_sdk_bob/schemas/salary.py +49 -0
  9. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/schemas/timeoff.py +1 -1
  10. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/schemas/work.py +8 -8
  11. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob.egg-info/PKG-INFO +1 -1
  12. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/setup.py +1 -1
  13. brynq_sdk_bob-2.5.0/brynq_sdk_bob/people.py +0 -344
  14. brynq_sdk_bob-2.5.0/brynq_sdk_bob/salaries.py +0 -24
  15. brynq_sdk_bob-2.5.0/brynq_sdk_bob/schemas/people.py +0 -136
  16. brynq_sdk_bob-2.5.0/brynq_sdk_bob/schemas/salary.py +0 -25
  17. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/__init__.py +0 -0
  18. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/bank.py +0 -0
  19. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/company.py +0 -0
  20. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/documents.py +0 -0
  21. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/employment.py +0 -0
  22. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/named_lists.py +0 -0
  23. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/payments.py +0 -0
  24. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/payroll_history.py +0 -0
  25. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/schemas/__init__.py +0 -0
  26. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/schemas/bank.py +0 -0
  27. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/schemas/custom_tables.py +0 -0
  28. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/schemas/named_lists.py +0 -0
  29. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/schemas/payroll_history.py +0 -0
  30. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/timeoff.py +0 -0
  31. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob/work.py +0 -0
  32. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob.egg-info/SOURCES.txt +0 -0
  33. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob.egg-info/dependency_links.txt +0 -0
  34. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob.egg-info/not-zip-safe +0 -0
  35. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob.egg-info/requires.txt +0 -0
  36. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/brynq_sdk_bob.egg-info/top_level.txt +0 -0
  37. {brynq_sdk_bob-2.5.0 → brynq_sdk_bob-2.6.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.0
2
2
  Name: brynq_sdk_bob
3
- Version: 2.5.0
3
+ Version: 2.6.0
4
4
  Summary: Bob wrapper from BrynQ
5
5
  Home-page: UNKNOWN
6
6
  Author: BrynQ
@@ -29,7 +29,7 @@ class CustomTables:
29
29
  record_path=['values']
30
30
  )
31
31
 
32
- df['employee_id'] = employee_id
32
+ df['employeeId'] = employee_id
33
33
  valid_data, invalid_data = Functions.validate_data(df=df, schema=self.schema, debug=True)
34
34
 
35
35
  return valid_data, invalid_data
@@ -0,0 +1,59 @@
1
+ import pandas as pd
2
+ from typing import Optional
3
+ from brynq_sdk_functions import Functions
4
+ from brynq_sdk_functions import BrynQPanderaDataFrameModel
5
+ from .bank import Bank
6
+ from .employment import Employment
7
+ from .salaries import Salaries
8
+ from .schemas.people import PeopleSchema
9
+ from .work import Work
10
+ from .custom_tables import CustomTables
11
+
12
+
13
+ class People:
14
+ def __init__(self, bob):
15
+ self.bob = bob
16
+ self.salaries = Salaries(bob)
17
+ self.employment = Employment(bob)
18
+ self.bank = Bank(bob)
19
+ self.work = Work(bob)
20
+ self.custom_tables = CustomTables(bob)
21
+ self.schema = PeopleSchema
22
+
23
+
24
+ # Build API fields using column metadata if present (api_field), otherwise use the column (alias) name
25
+ def __build_api_fields(self, schema_model: BrynQPanderaDataFrameModel) -> list[str]:
26
+ schema = schema_model.to_schema()
27
+ return [
28
+ ((getattr(col, "metadata", None) or {}).get("api_field")) or col_name
29
+ for col_name, col in schema.columns.items()
30
+ ]
31
+
32
+ def get(self, schema_custom_fields: Optional[BrynQPanderaDataFrameModel] = None) -> pd.DataFrame:
33
+
34
+ core_fields = self.__build_api_fields(PeopleSchema)
35
+ custom_fields = self.__build_api_fields(schema_custom_fields) if schema_custom_fields is not None else []
36
+ fields = core_fields + custom_fields
37
+
38
+ resp = self.bob.session.post(url=f"{self.bob.base_url}people/search",
39
+ json={
40
+ "fields": fields,
41
+ "filters": []
42
+ #"humanReadable": "REPLACE"
43
+ },
44
+ timeout=self.bob.timeout)
45
+ resp.raise_for_status()
46
+ df = pd.json_normalize(resp.json()['employees'])
47
+ df = df.loc[:, ~df.columns.str.contains('value')]
48
+ # Normalize separators in incoming data: convert '/' to '.' to match schema aliases
49
+ df.columns = df.columns.str.replace('/', '.', regex=False)
50
+
51
+ if schema_custom_fields is not None:
52
+ valid_people, invalid_people_custom = Functions.validate_data(df=df, schema=schema_custom_fields, debug=True)
53
+ else:
54
+ valid_people = df
55
+ invalid_people_custom = pd.DataFrame()
56
+
57
+ valid_people, invalid_people = Functions.validate_data(df=valid_people, schema=PeopleSchema, debug=True)
58
+
59
+ return valid_people, pd.concat([invalid_people, invalid_people_custom])
@@ -0,0 +1,38 @@
1
+ import pandas as pd
2
+ import requests
3
+ from brynq_sdk_functions import Functions
4
+ from .schemas.salary import SalarySchema, SalaryCreateSchema
5
+
6
+
7
+ class Salaries:
8
+ def __init__(self, bob):
9
+ self.bob = bob
10
+ self.schema = SalarySchema
11
+
12
+ def get(self) -> tuple[pd.DataFrame, pd.DataFrame]:
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)
17
+ df = pd.json_normalize(
18
+ data,
19
+ record_path='values',
20
+ meta=['employeeId']
21
+ )
22
+ valid_salaries, invalid_salaries = Functions.validate_data(df=df, schema=SalarySchema, debug=True)
23
+
24
+ return valid_salaries, invalid_salaries
25
+
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)
29
+ payload = salary_data.model_dump(exclude_none=True, by_alias=True)
30
+
31
+ resp = self.bob.session.post(url=f"{self.bob.base_url}people/{salary_data.employee_id}/salaries", json=payload)
32
+ resp.raise_for_status()
33
+ return resp
34
+
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}")
37
+ resp.raise_for_status()
38
+ return resp
@@ -6,7 +6,7 @@ from brynq_sdk_functions import BrynQPanderaDataFrameModel
6
6
 
7
7
  class EmploymentSchema(BrynQPanderaDataFrameModel):
8
8
  id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Employment ID", alias="id")
9
- employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
9
+ employee_id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
10
10
  active_effective_date: Series[DateTime] = pa.Field(coerce=True, description="Active Effective Date", alias="activeEffectiveDate")
11
11
  contract: Series[String] = pa.Field(coerce=True, nullable=True, description="Contract", alias="contract") # has a list of possible values
12
12
  creation_date: Series[DateTime] = pa.Field(coerce=True, nullable=True, description="Creation Date", alias="creationDate")
@@ -21,8 +21,9 @@ class VariablePaymentSchema(BrynQPanderaDataFrameModel):
21
21
  end_effective_date: Series[DateTime] = pa.Field(nullable=True, coerce=True, description="End Effective Date", alias="endEffectiveDate")
22
22
  payment_period: Series[String] = pa.Field(coerce=True, description="Payment Period", alias="paymentPeriod")
23
23
  effective_date: Series[DateTime] = pa.Field(coerce=True, description="Effective Date", alias="effectiveDate")
24
- amount_value: Series[Float] = pa.Field(coerce=True, description="Amount Value", alias="amount.value")
25
- amount_currency: Series[String] = pa.Field(coerce=True, description="Amount Currency", alias="amount.currency")
24
+ amount_value: Optional[Series[Float]] = pa.Field(coerce=True, description="Amount Value", alias="amount.value")
25
+ amount_alternative_value: Optional[Series[Float]] = pa.Field(coerce=True, description="Amount Value", alias="amount")
26
+ amount_currency: Optional[Series[String]] = pa.Field(coerce=True, description="Amount Currency", alias="amount.currency")
26
27
  change_reason: Series[String] = pa.Field(nullable=True, coerce=True, description="Change Reason", alias="change.reason")
27
28
  change_changed_by: Series[String] = pa.Field(nullable=True, coerce=True, description="Change Changed By", alias="change.changedBy")
28
29
  change_changed_by_id: Series[pd.Int64Dtype] = pa.Field(nullable=True, coerce=True, description="Change Changed By ID", alias="change.changedById")
@@ -0,0 +1,284 @@
1
+ from datetime import datetime
2
+ from typing import Optional, List, Dict
3
+
4
+ import pandas as pd
5
+ import pandera as pa
6
+ from pandera import Bool
7
+ from pandera.typing import Series, String, Float
8
+ import pandera.extensions as extensions
9
+ from brynq_sdk_functions import BrynQPanderaDataFrameModel
10
+ from pandera.engines.pandas_engine import DateTime
11
+
12
+
13
+ @extensions.register_check_method()
14
+ def check_list(x):
15
+ return isinstance(x, list)
16
+
17
+
18
+ class PeopleSchema(BrynQPanderaDataFrameModel):
19
+ id: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, description="Person ID", alias="id", metadata={"api_field": "root.id"})
20
+ display_name: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Display Name", alias="displayName", metadata={"api_field": "root.displayName"})
21
+ company_id: Optional[Series[String]] = pa.Field(coerce=True, description="Company ID", alias="companyId", metadata={"api_field": "root.companyId"})
22
+ email: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Email", alias="email", metadata={"api_field": "root.email"})
23
+ home_mobile_phone: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Personal Mobile Phone", alias="home.mobilePhone")
24
+ home_private_email: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, alias='home.privateEmail')
25
+ surname: Optional[Series[String]] = pa.Field(coerce=True, description="Surname", alias="surname", metadata={"api_field": "root.surname"})
26
+ first_name: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="First Name", alias="firstName", metadata={"api_field": "root.firstName"})
27
+ full_name: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Full Name", alias="fullName", metadata={"api_field": "root.fullName"})
28
+ # the date is in DD/MM/YYYY format,
29
+ personal_birth_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=False, description="Personal Birth Date", alias="personal.birthDate") # , dtype_kwargs={"to_datetime_kwargs": {"format": "%d/%m/%Y"}}
30
+ personal_pronouns: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Personal Pronouns", alias="personal.pronouns")
31
+ personal_honorific: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Personal Honorific", alias="personal.honorific")
32
+ personal_nationality: Optional[Series[object]] = pa.Field(coerce=True, check_name=check_list, description="Personal Nationality", alias="personal.nationality")
33
+ # employee_payroll_manager: Series[String] = pa.Field(coerce=True, nullable=True)
34
+ # employee_hrbp: Series[String] = pa.Field(coerce=True, nullable=True)
35
+ # employee_it_admin: Series[String] = pa.Field(coerce=True, nullable=True)
36
+ # employee_buddy: Series[String] = pa.Field(coerce=True, nullable=True)
37
+ employee_veteran_status: Optional[Series[object]] = pa.Field(coerce=True, check_name=check_list, description="Employee Veteran Status", alias="employee.veteranStatus")
38
+ employee_disability_status: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Employee Disability Status", alias="employee.disabilityStatus")
39
+ work_start_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Work Start Date", alias="work.startDate")
40
+ work_manager: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Manager", alias="work.manager")
41
+ work_work_phone: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Work Phone", alias="work.workPhone")
42
+ work_tenure_duration_period_i_s_o: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Tenure Duration Period ISO", alias="work.tenureDuration.periodISO")
43
+ work_tenure_duration_sort_factor: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=False, description="Work Tenure Duration Sort Factor", alias="work.tenureDuration.sortFactor")
44
+ work_tenure_duration_humanize: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Tenure Duration Humanize", alias="work.tenureDuration.humanize")
45
+ work_duration_of_employment_period_i_s_o: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Duration of Employment Period ISO", alias="work.durationOfEmployment.periodISO")
46
+ work_duration_of_employment_sort_factor: Optional[Series[String]] = pa.Field(coerce=True, nullable=False, description="Work Duration of Employment Sort Factor", alias="work.durationOfEmployment.sortFactor")
47
+ work_duration_of_employment_humanize: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Duration of Employment Humanize", alias="work.durationOfEmployment.humanize")
48
+ work_reports_to_id_in_company: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Work Reports to ID in Company", alias="work.reportsToIdInCompany")
49
+ work_employee_id_in_company: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Work Employee ID in Company", alias="work.employeeIdInCompany")
50
+ work_reports_to_display_name: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Reports to Display Name", alias="work.reportsTo.displayName")
51
+ work_reports_to_surname: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Reports to Surname", alias="work.reportsTo.surname")
52
+ work_reports_to_first_name: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Reports to First Name", alias="work.reportsTo.firstName")
53
+ work_reports_to_id: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Work Reports to ID", alias="work.reportsTo.id")
54
+ work_work_mobile: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Work Mobile", alias="work.workMobile")
55
+ work_indirect_reports: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Work Indirect Reports", alias="work.indirectReports")
56
+ work_site_id: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Site ID", alias="work.siteId")
57
+ work_department: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Department", alias="work.department")
58
+ work_tenure_duration_years: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Work Tenure Duration Years", alias="work.tenureDurationYears")
59
+ work_tenure_years: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Work Tenure Years", alias="work.tenureYears")
60
+ work_is_manager: Optional[Series[Bool]] = pa.Field(coerce=True, nullable=True, description="Work Is Manager", alias="work.isManager")
61
+ work_title: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Title", alias="work.title")
62
+ work_site: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Site", alias="work.site")
63
+ work_original_start_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Work Original Start Date", alias="work.originalStartDate")
64
+ work_active_effective_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Work Active Effective Date", alias="work.activeEffectiveDate")
65
+ work_direct_reports: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Work Direct Reports", alias="work.directReports")
66
+ # work_work_change_type: Series[String] = pa.Field(coerce=True, nullable=True)
67
+ work_second_level_manager: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Second Level Manager", alias="work.secondLevelManager")
68
+ work_days_of_previous_service: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Work Days of Previous Service", alias="work.daysOfPreviousService")
69
+ work_years_of_service: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Work Years of Service", alias="work.yearsOfService")
70
+ payroll_employment_contract: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Contract Type", alias="payroll.employment.contract")
71
+ payroll_employment_type: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Employment Type", alias="payroll.employment.type")
72
+ tax_id: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Tax ID", alias="payroll.taxCode")
73
+
74
+ about_food_preferences: Optional[Series[object]] = pa.Field(coerce=True, check_name=check_list, description="About Food Preferences", alias="about.foodPreferences")
75
+ # about_social_data_linkedin: Series[String] = pa.Field(coerce=True, nullable=True)
76
+ about_social_data_twitter: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="About Social Data Twitter", alias="about.socialData.twitter")
77
+
78
+ about_social_data_facebook: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="About Social Data Facebook", alias="about.socialData.facebook")
79
+
80
+ about_superpowers: Optional[Series[object]] = pa.Field(coerce=True, check_name=check_list, description="About Superpowers", alias="about.superpowers")
81
+ about_hobbies: Optional[Series[object]] = pa.Field(coerce=True, check_name=check_list, description="About Hobbies", alias="about.hobbies")
82
+ about_about: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="About About", alias="about.about")
83
+ about_avatar: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="About Avatar", alias="about.avatar")
84
+ address_city: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Address City", alias="address.city")
85
+ address_post_code: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Address Post Code", alias="address.postCode")
86
+ address_zip_code: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Address ZIP Code", alias="address.zipCode")
87
+ address_line1: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Address Line 1", alias="address.line1")
88
+ address_line2: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Address Line 2", alias="address.line2")
89
+ address_country: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Address Country", alias="address.country")
90
+ address_active_effective_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Address Active Effective Date", alias="address.activeEffectiveDate") # , dtype_kwargs={"to_datetime_kwargs": {"format": "%d/%m/%Y"}}
91
+ address_full_address: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Address Full Address", alias="address.fullAddress")
92
+ address_site_address1: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Address Site Address 1", alias="address.siteAddress1")
93
+ address_site_address2: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Address Site Address 2", alias="address.siteAddress2")
94
+ address_site_country: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Address Site Country", alias="address.siteCountry")
95
+ address_site_postal_code: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Address Site Postal Code", alias="address.sitePostalCode")
96
+ address_site_city: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Address Site City", alias="address.siteCity")
97
+ address_site_state: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Address Site State", alias="address.siteState")
98
+ home_legal_gender: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Legal Gender", alias="home.legalGender")
99
+ home_family_status: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Family / Marital Status", alias="home.familyStatus")
100
+ home_spouse_first_name: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Spouse First Name", alias="home.spouse.firstName")
101
+ home_spouse_surname: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Spouse Surname", alias="home.spouse.surname")
102
+ # home_spouse_birth_date: Series[DateTime] = pa.Field(coerce=True, nullable=True)
103
+ home_spouse_gender: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Spouse Gender", alias="home.spouse.gender")
104
+ identification_ssn_serial_number: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="SSN Serial Number", alias="identification.ssnSerialNumber")
105
+ internal_termination_reason: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Internal Termination Reason", alias="internal.terminationReason")
106
+ internal_termination_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Internal Termination Date", alias="internal.terminationDate")
107
+ internal_termination_type: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Internal Termination Type", alias="internal.terminationType")
108
+ employee_last_day_of_work: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Employee Last Day of Work", alias="employee.lastDayOfWork")
109
+ financial_iban: Series[String] = pa.Field(coerce=True, nullable=True, alias='financial.iban')
110
+ employee_band_effective_date_ote: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Employee Band Effective Date OTE", alias="employee.band_effectiveDate_ote")
111
+ employee_band_site_ote: Optional[Series[object]] = pa.Field(coerce=True, nullable=True, check_name=check_list, description="Employee Band Site OTE", alias="employee.band_site_ote")
112
+ employee_band_min_ote: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Employee Band Min OTE", alias="employee.band_min_ote")
113
+ employee_band_mid_ote: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Employee Band Mid OTE", alias="employee.band_mid_ote")
114
+ employee_band_max_ote: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Employee Band Max OTE", alias="employee.band_max_ote")
115
+ employee_band_pay_period_ote: Optional[Series[object]] = pa.Field(coerce=True, nullable=True, check_name=check_list, description="Employee Band Pay Period OTE", alias="employee.band_payPeriod_ote")
116
+ employee_band_currency_ote: Optional[Series[object]] = pa.Field(coerce=True, nullable=True, check_name=check_list, description="Employee Band Currency OTE", alias="employee.band_currency_ote")
117
+ employee_band_ote_id: Optional[Series[object]] = pa.Field(coerce=True, nullable=True, check_name=check_list, description="Employee Band OTE ID", alias="employee.band_ote_id")
118
+ employee_band_effective_date_base_salary: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Employee Band Effective Date Base Salary", alias="employee.band_effectiveDate_baseSalary")
119
+ employee_band_site_base_salary: Optional[Series[object]] = pa.Field(coerce=True, nullable=True, check_name=check_list, description="Employee Band Site Base Salary", alias="employee.band_site_baseSalary")
120
+ employee_band_min_base_salary: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Employee Band Min Base Salary", alias="employee.band_min_baseSalary")
121
+ employee_band_mid_base_salary: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Employee Band Mid Base Salary", alias="employee.band_mid_baseSalary")
122
+ employee_band_max_base_salary: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Employee Band Max Base Salary", alias="employee.band_max_baseSalary")
123
+ employee_band_pay_period_base_salary: Optional[Series[object]] = pa.Field(coerce=True, nullable=True, check_name=check_list, description="Employee Band Pay Period Base Salary", alias="employee.band_payPeriod_baseSalary")
124
+ employee_band_currency_base_salary: Optional[Series[object]] = pa.Field(coerce=True, nullable=True, check_name=check_list, description="Employee Band Currency Base Salary", alias="employee.band_currency_baseSalary")
125
+ employee_band_base_salary_id: Optional[Series[object]] = pa.Field(coerce=True, nullable=True, check_name=check_list, description="Employee Band Base Salary ID", alias="employee.band_baseSalary_id")
126
+ employee_band_compa_ratio_ote: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Employee Band Compa Ratio OTE", alias="employee.band_compaRatio_ote")
127
+ employee_band_range_positioning_ote: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Employee Band Range Positioning OTE", alias="employee.band_rangePositioning_ote")
128
+ employee_band_compa_ratio_base_salary: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Employee Band Compa Ratio Base Salary", alias="employee.band_compaRatio_baseSalary")
129
+ employee_band_range_positioning_base_salary: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Employee Band Range Positioning Base Salary", alias="employee.band_rangePositioning_baseSalary")
130
+
131
+ # Additional non-custom fields from available_fields.json
132
+ # Root extras
133
+ creation_date_time: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Root Creation Date Time", alias="creationDateTime", metadata={"api_field": "root.creationDateTime"})
134
+ cover_image_url: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Root Cover Image URL", alias="coverImageUrl", metadata={"api_field": "root.coverImageUrl"})
135
+ avatar_url: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Root Avatar URL", alias="avatarUrl", metadata={"api_field": "root.avatarUrl"})
136
+ second_name: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Middle Name", alias="secondName", metadata={"api_field": "root.secondName"})
137
+ state: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="State", alias="state", metadata={"api_field": "root.state"})
138
+ creation_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Creation Date", alias="creationDate", metadata={"api_field": "root.creationDate"})
139
+
140
+ # Personal extras
141
+ personal_age: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Age", alias="personal.age")
142
+ personal_short_birth_date: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Birthday Short", alias="personal.shortBirthDate")
143
+
144
+ # Personal contact
145
+ home_private_phone: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Personal Phone", alias="home.privatePhone")
146
+
147
+ # Identification extras
148
+ financial_passport_number: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Passport Number", alias="financial.passportNumber")
149
+ financial_identification_number: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Identification Number", alias="financial.identificationNumber")
150
+ payroll_nin: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="NI Number", alias="payroll.nin")
151
+ identification_ssn: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="SSN", alias="identification.ssn")
152
+ employee_sin: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="SIN", alias="employee.sin")
153
+ employee_sin_expiration_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="SIN Expiration Date", alias="employee.sinExpirationDate")
154
+
155
+ # Work extras
156
+ work_short_start_date: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Anniversary", alias="work.shortStartDate")
157
+ work_duration_of_employment: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Accumulated Tenure (duration)", alias="work.durationOfEmployment")
158
+ work_tenure_duration: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Tenure (duration)", alias="work.tenureDuration")
159
+ work_work_change_type: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Change Type", alias="work.workChangeType")
160
+ work_reports_to: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Reports To", alias="work.reportsTo")
161
+ work_reports_to_email: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Manager Email", alias="work.reportsTo.email")
162
+
163
+ # Work roles
164
+ employee_buddy: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Buddy", alias="employee.buddy")
165
+ employee_hrbp: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="HRBP", alias="employee.hrbp")
166
+ employee_payroll_manager: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Payroll Manager", alias="employee.payrollManager")
167
+ employee_it_admin: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="IT Admin", alias="employee.itAdmin")
168
+ employee_org_level: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Org Level", alias="employee.orgLevel")
169
+
170
+ # Work contact
171
+ personal_communication_slack_username: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Slack Username", alias="personal.communication.slackUsername")
172
+ personal_communication_skype_username: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Skype Username", alias="personal.communication.skypeUsername")
173
+
174
+ # Address extras
175
+ address_usa_state: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="USA State", alias="address.usaState")
176
+
177
+ # Home extras
178
+ home_number_of_kids: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Number of Children", alias="home.numberOfKids")
179
+ home_spouse_birth_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Spouse Birth Date", alias="home.spouse.birthDate")
180
+ home_spouse_short_birth_date: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Spouse Birthday", alias="home.spouse.shortBirthDate")
181
+
182
+ # About social extras
183
+ about_social_data_linkedin: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Social LinkedIn", alias="about.socialData.linkedin")
184
+
185
+ # Right to work
186
+ financial_right_to_work_expiry_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Right to Work Expiry Date", alias="financial.rightToWorkExpiryDate")
187
+
188
+ # Employment extras
189
+ payroll_employment_active_effective_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Employment Active Effective Date", alias="payroll.employment.activeEffectiveDate")
190
+ payroll_employment_working_pattern: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Working Pattern", alias="payroll.employment.workingPattern")
191
+ payroll_employment_standard_working_pattern: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Standard Working Pattern", alias="payroll.employment.standardWorkingPattern")
192
+ payroll_employment_standard_working_pattern_id: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Standard Working Pattern ID", alias="payroll.employment.standardWorkingPattern.workingPatternId")
193
+ payroll_employment_personal_working_pattern_type: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Personal Working Pattern Type", alias="payroll.employment.actualWorkingPattern.workingPatternType")
194
+ payroll_employment_hours_in_day_not_worked: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Hours in Day Not Worked", alias="payroll.employment.hoursInDayNotWorked")
195
+ payroll_employment_site_working_pattern: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Site Working Pattern", alias="payroll.employment.siteWorkingPattern")
196
+ payroll_employment_actual_working_pattern: Optional[Series[dict]] = pa.Field(coerce=True, nullable=True, description="Actual Working Pattern", alias="payroll.employment.actualWorkingPattern")
197
+ payroll_employment_actual_working_pattern_days_sunday: Optional[Series[float]] = pa.Field(coerce=True, nullable=True, description="Actual Working Pattern - Sunday", alias="payroll.employment.actualWorkingPattern.days.sunday")
198
+ payroll_employment_actual_working_pattern_days_tuesday: Optional[Series[float]] = pa.Field(coerce=True, nullable=True, description="Actual Working Pattern - Tuesday", alias="payroll.employment.actualWorkingPattern.days.tuesday")
199
+ payroll_employment_actual_working_pattern_days_wednesday: Optional[Series[float]] = pa.Field(coerce=True, nullable=True, description="Actual Working Pattern - Wednesday", alias="payroll.employment.actualWorkingPattern.days.wednesday")
200
+ payroll_employment_actual_working_pattern_days_monday: Optional[Series[float]] = pa.Field(coerce=True, nullable=True, description="Actual Working Pattern - Monday", alias="payroll.employment.actualWorkingPattern.days.monday")
201
+ payroll_employment_actual_working_pattern_days_friday: Optional[Series[float]] = pa.Field(coerce=True, nullable=True, description="Actual Working Pattern - Friday", alias="payroll.employment.actualWorkingPattern.days.friday")
202
+ payroll_employment_actual_working_pattern_days_thursday: Optional[Series[float]] = pa.Field(coerce=True, nullable=True, description="Actual Working Pattern - Thursday", alias="payroll.employment.actualWorkingPattern.days.thursday")
203
+ payroll_employment_actual_working_pattern_days_saturday: Optional[Series[float]] = pa.Field(coerce=True, nullable=True, description="Actual Working Pattern - Saturday", alias="payroll.employment.actualWorkingPattern.days.saturday")
204
+ payroll_employment_actual_working_pattern_hours_per_day: Optional[Series[float]] = pa.Field(coerce=True, nullable=True, description="Actual Working Pattern - Hours Per Day", alias="payroll.employment.actualWorkingPattern.hoursPerDay")
205
+ payroll_employment_actual_working_pattern_working_pattern_id: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Actual Working Pattern - Working Pattern ID", alias="payroll.employment.actualWorkingPattern.workingPatternId")
206
+ payroll_employment_site_working_pattern_working_pattern_type: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Site Working Pattern - Working Pattern Type", alias="payroll.employment.siteWorkingPattern.workingPatternType")
207
+ payroll_employment_site_working_pattern_days_sunday: Optional[Series[float]] = pa.Field(coerce=True, nullable=True, description="Site Working Pattern - Sunday", alias="payroll.employment.siteWorkingPattern.days.sunday")
208
+ payroll_employment_site_working_pattern_days_tuesday: Optional[Series[float]] = pa.Field(coerce=True, nullable=True, description="Site Working Pattern - Tuesday", alias="payroll.employment.siteWorkingPattern.days.tuesday")
209
+ payroll_employment_site_working_pattern_days_wednesday: Optional[Series[float]] = pa.Field(coerce=True, nullable=True, description="Site Working Pattern - Wednesday", alias="payroll.employment.siteWorkingPattern.days.wednesday")
210
+ payroll_employment_site_working_pattern_days_monday: Optional[Series[float]] = pa.Field(coerce=True, nullable=True, description="Site Working Pattern - Monday", alias="payroll.employment.siteWorkingPattern.days.monday")
211
+ payroll_employment_site_working_pattern_days_friday: Optional[Series[float]] = pa.Field(coerce=True, nullable=True, description="Site Working Pattern - Friday", alias="payroll.employment.siteWorkingPattern.days.friday")
212
+ payroll_employment_site_working_pattern_days_thursday: Optional[Series[float]] = pa.Field(coerce=True, nullable=True, description="Site Working Pattern - Thursday", alias="payroll.employment.siteWorkingPattern.days.thursday")
213
+ payroll_employment_site_working_pattern_days_saturday: Optional[Series[float]] = pa.Field(coerce=True, nullable=True, description="Site Working Pattern - Saturday", alias="payroll.employment.siteWorkingPattern.days.saturday")
214
+ payroll_employment_site_working_pattern_hours_per_day: Optional[Series[float]] = pa.Field(coerce=True, nullable=True, description="Site Working Pattern - Hours Per Day", alias="payroll.employment.siteWorkingPattern.hoursPerDay")
215
+ payroll_employment_site_working_pattern_working_pattern_id: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Site Working Pattern - Working Pattern ID", alias="payroll.employment.siteWorkingPattern.workingPatternId")
216
+ payroll_employment_calendar_id: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Calendar ID", alias="payroll.employment.calendarId")
217
+
218
+
219
+ payroll_employment_salary_pay_type: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Salary Pay Type", alias="payroll.employment.salaryPayType")
220
+ payroll_employment_flsa_code: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="FLSA Code", alias="payroll.employment.flsaCode")
221
+ payroll_employment_fte: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="FTE %", alias="payroll.employment.fte")
222
+ payroll_employment_weekly_hours: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Weekly Hours", alias="payroll.employment.weeklyHours")
223
+
224
+ # Emergency contact
225
+ emergency_first_name: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Emergency First Name", alias="emergency.firstName")
226
+ emergency_second_name: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Emergency Middle Name", alias="emergency.secondName")
227
+ emergency_surname: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Emergency Last Name", alias="emergency.surname")
228
+ emergency_relation: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Emergency Relation", alias="emergency.relation")
229
+ emergency_phone: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Emergency Phone", alias="emergency.phone")
230
+ emergency_mobile_phone: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Emergency Mobile Phone", alias="emergency.mobilePhone")
231
+ emergency_email: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Emergency Email", alias="emergency.email")
232
+ emergency_address: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Emergency Address", alias="emergency.address")
233
+ emergency_city: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Emergency City", alias="emergency.city")
234
+ emergency_post_code: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Emergency Post Code", alias="emergency.postCode")
235
+ emergency_country: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Emergency Country", alias="emergency.country")
236
+
237
+ # Temporary address
238
+ temporary_address_line1: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Temporary Address Line 1", alias="temporaryAddress.line1")
239
+ temporary_address_line2: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Temporary Address Line 2", alias="temporaryAddress.line2")
240
+ temporary_address_city: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Temporary City", alias="temporaryAddress.city")
241
+ temporary_address_usa_state: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Temporary State", alias="temporaryAddress.usaState")
242
+ temporary_address_country: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Temporary Country", alias="temporaryAddress.country")
243
+ temporary_address_post_code: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Temporary Post Code", alias="temporaryAddress.postCode")
244
+
245
+ # Lifecycle extras
246
+ internal_period_since_termination: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Period Since Termination", alias="internal.periodSinceTermination")
247
+ internal_years_since_termination: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Years Since Termination", alias="internal.yearsSinceTermination")
248
+ internal_notice: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Notice Period", alias="internal.notice")
249
+ internal_lifecycle_status: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Lifecycle Status", alias="internal.lifecycleStatus")
250
+ internal_status: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Status", alias="internal.status")
251
+ internal_probation_end_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Probation End Date", alias="internal.probationEndDate")
252
+ internal_current_active_status_start_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Current Active Start Date", alias="internal.currentActiveStatusStartDate")
253
+
254
+ # EEO extras
255
+ eeo_ethnicity: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Ethnicity", alias="eeo.ethnicity")
256
+ eeo_job_category: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Job Category", alias="eeo.jobCategory")
257
+
258
+ # People analytics
259
+ people_analytics_age_risk_indicator: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Age Risk Indicator", alias="peopleAnalytics.ageRiskIndicator")
260
+ people_analytics_kids_risk_indicator: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Kids Risk Indicator", alias="peopleAnalytics.kidsRiskIndicator")
261
+ people_analytics_is_manager_risk_indicator: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Is Manager Risk Indicator", alias="peopleAnalytics.isManagerRiskIndicator")
262
+ people_analytics_num_with_same_title_risk_indicator: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Num With Same Title Risk Indicator", alias="peopleAnalytics.numWithSameTitleRiskIndicator")
263
+ people_analytics_team_size_risk_indicator: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Team Size Risk Indicator", alias="peopleAnalytics.teamSizeRiskIndicator")
264
+ people_analytics_years_with_current_title_risk_indicator: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Years With Current Title Risk Indicator", alias="peopleAnalytics.yearsWithCurrentTitleRiskIndicator")
265
+ people_analytics_years_with_recent_salary_risk_indicator: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Years With Recent Salary Risk Indicator", alias="peopleAnalytics.yearsWithRecentSalaryRiskIndicator")
266
+ people_analytics_manager_tenure_risk_indicator: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Manager Tenure Risk Indicator", alias="peopleAnalytics.managerTenureRiskIndicator")
267
+ people_analytics_num_of_direct_reports_risk_indicator: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Number Of Direct Reports Risk Indicator", alias="peopleAnalytics.numOfDirectReportsRiskIndicator")
268
+ people_analytics_team_recent_turnover_risk_indicator: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Team Recent Turnover Risk Indicator", alias="peopleAnalytics.teamRecentTurnoverRiskIndicator")
269
+ people_analytics_timeoff_frequency_risk_indicator: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Timeoff Frequency Risk Indicator", alias="peopleAnalytics.timeoffFrequencyRiskIndicator")
270
+ people_analytics_tenure_rank_risk_indicator: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Tenure Rank Risk Indicator", alias="peopleAnalytics.tenureRankRiskIndicator")
271
+ people_analytics_recent_manager_change_risk_indicator: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Recent Manager Change Risk Indicator", alias="peopleAnalytics.recentManagerChangeRiskIndicator")
272
+ people_analytics_low_risk_counter: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Low Risk Counter", alias="peopleAnalytics.lowRiskCounter")
273
+ people_analytics_some_risk_counter: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Some Risk Counter", alias="peopleAnalytics.someRiskCounter")
274
+ people_analytics_at_risk_counter: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="At Risk Counter", alias="peopleAnalytics.atRiskCounter")
275
+
276
+ # Positions / Job profile
277
+ employee_position_opening_id: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Position Opening ID", alias="employee.positionOpeningId")
278
+ employee_job_profile_id: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Job Profile ID", alias="employee.jobProfileId")
279
+ employee_recent_leave_start_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Recent Leave Start Date", alias="employee.recentLeaveStartDate")
280
+ employee_recent_leave_end_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Recent Leave End Date", alias="employee.recentLeaveEndDate")
281
+ employee_first_day_of_work: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="First Day Of Work", alias="employee.firstDayOfWork")
282
+
283
+ class Config:
284
+ coerce = True
@@ -0,0 +1,49 @@
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
+ from brynq_sdk_functions import BrynQPanderaDataFrameModel
7
+ from pydantic import BaseModel, Field
8
+ from datetime import datetime, date
9
+ from typing import Optional, Dict, Any
10
+
11
+
12
+ class SalarySchema(BrynQPanderaDataFrameModel):
13
+ id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Salary ID", alias="id")
14
+ employee_id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
15
+ pay_frequency: Series[String] = pa.Field(coerce=True, nullable=True, description="Pay Frequency", alias="payFrequency") # has a list of possible values , isin=['Monthly']
16
+ creation_date: Series[DateTime] = pa.Field(coerce=True, nullable=True, description="Creation Date", alias="creationDate")
17
+ is_current: Series[Bool] = pa.Field(coerce=True, description="Is Current", alias="isCurrent")
18
+ modification_date: Series[DateTime] = pa.Field(coerce=True, nullable=True, description="Modification Date", alias="modificationDate")
19
+ effective_date: Series[DateTime] = pa.Field(coerce=True, description="Effective Date", alias="effectiveDate")
20
+ end_effective_date: Series[DateTime] = pa.Field(coerce=True, nullable=True, description="End Effective Date", alias="endEffectiveDate")
21
+ change_reason: Series[str] = pa.Field(coerce=True, nullable=True, description="Change Reason", alias="change.reason")
22
+ pay_period: Series[String] = pa.Field(coerce=True, nullable=True, description="Pay Period", alias="payPeriod")
23
+ base_value: Series[Float] = pa.Field(coerce=True, nullable=True, description="Base Value", alias="base.value") #needs to become base.value?
24
+ base_currency: Series[String] = pa.Field(coerce=True, nullable=True, description="Base Currency", alias="base.currency")
25
+ active_effective_date: Series[DateTime] = pa.Field(coerce=True, nullable=True, description="Active Effective Date", alias="activeEffectiveDate")
26
+
27
+
28
+ class SalaryCreateSchema(BaseModel):
29
+ can_be_deleted: Optional[bool] = Field(None, description="internal flag", alias="canBeDeleted")
30
+ work_change_type: Optional[str] = Field(None, description="The type of the change that was performed for this work entry. This will contain the ID of the value from the Change Type list.", alias="workChangeType")
31
+ salary_change_reason: Optional[str] = Field(None, description="Reason for the change", alias="change.reason")
32
+ salary_change_changed_by: Optional[str] = Field(None, description="Name of the user who changed the entry", alias="change.changedBy")
33
+ salary_change_changed_by_id: Optional[str] = Field(None, description="ID of the user who changed the entry", alias="change.changedById")
34
+ pay_frequency: Optional[str] = Field(None, description="Represents the frequency the salary is paid. This can be one of: Monthly, Semi Monthly, Weekly, or Bi-Weekly.", alias="payFrequency")
35
+ creation_date: Optional[date] = Field(None, description="The date this entry was created.", alias="creationDate")
36
+ is_current: Optional[bool] = Field(None, description="Is 'true' when this is the effective entry which is currently active.", alias="isCurrent")
37
+ modification_date: Optional[date] = Field(None, description="The date this entry was modified.", alias="modificationDate")
38
+ id: Optional[int] = Field(None, description="The id of the entry.", alias="id")
39
+ end_effective_date: Optional[date] = Field(None, description="For entries that are not active - this it the date this entry became not effective.", alias="endEffectiveDate")
40
+ active_effective_date: Optional[date] = Field(None, description="The active effective date for this entry.", alias="activeEffectiveDate")
41
+ custom_columns: Optional[Dict[str, Any]] = Field(None, description="If the table has custom columns, they will appear here.", alias="customColumns")
42
+ base_value: float = Field(..., description="Base amount value", alias="base.value")
43
+ base_currency: str = Field(..., description="Three-letter currency code.", alias="base.currency")
44
+ pay_period: str = Field(..., description="Represents the period for this salary entry. This can be one of: Annual, Hourly, Daily, Weekly, Monthly.", alias="payPeriod")
45
+ effective_date: Optional[date] = Field(None, description="The date this entry becomes effective. This is a mandatory field for a work entry.", alias="effectiveDate")
46
+
47
+ class Config:
48
+ allow_population_by_field_name = True
49
+ coerce = True
@@ -6,7 +6,7 @@ from brynq_sdk_functions import BrynQPanderaDataFrameModel
6
6
 
7
7
  class TimeOffSchema(BrynQPanderaDataFrameModel):
8
8
  change_type: Series[String] = pa.Field(coerce=True, description="Change Type", alias="changeType")
9
- employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
9
+ employee_id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
10
10
  employee_display_name: Series[String] = pa.Field(coerce=True, description="Employee Display Name", alias="employeeDisplayName")
11
11
  employee_email: Series[String] = pa.Field(coerce=True, description="Employee Email", alias="employeeEmail")
12
12
  request_id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Request ID", alias="requestId")
@@ -18,14 +18,14 @@ class WorkSchema(BrynQPanderaDataFrameModel):
18
18
  active_effective_date: Series[datetime] = pa.Field(coerce=True, nullable=True, description="Active Effective Date", alias="activeEffectiveDate")
19
19
  department: Series[str] = pa.Field(coerce=True, nullable=True, description="Department", alias="department")
20
20
  effective_date: Series[datetime] = pa.Field(coerce=True, nullable=True, description="Effective Date", alias="effectiveDate")
21
- change_reason: Series[str] = pa.Field(coerce=True, nullable=True, description="Change Reason", alias="changeReason")
22
- change_changed_by: Series[str] = pa.Field(coerce=True, nullable=True, description="Change Changed By", alias="changeChangedBy")
23
- change_changed_by_id: Series[str] = pa.Field(coerce=True, nullable=True, description="Change Changed By ID", alias="changeChangedById")
24
- reports_to_id: Series[str] = pa.Field(coerce=True, nullable=True, description="Reports To ID", alias="reportsToId")
25
- reports_to_first_name: Series[str] = pa.Field(coerce=True, nullable=True, description="Reports To First Name", alias="reportsToFirstName")
26
- reports_to_surname: Series[str] = pa.Field(coerce=True, nullable=True, description="Reports To Surname", alias="reportsToSurname")
27
- reports_to_email: Series[str] = pa.Field(coerce=True, nullable=True, description="Reports To Email", alias="reportsToEmail")
28
- reports_to_display_name: Series[str] = pa.Field(coerce=True, nullable=True, description="Reports To Display Name", alias="reportsToDisplayName")
21
+ change_reason: Series[str] = pa.Field(coerce=True, nullable=True, description="Change Reason", alias="change.reason")
22
+ change_changed_by: Series[str] = pa.Field(coerce=True, nullable=True, description="Change Changed By", alias="change.changedBy")
23
+ change_changed_by_id: Series[str] = pa.Field(coerce=True, nullable=True, description="Change Changed By ID", alias="change.changedById")
24
+ reports_to_id: Series[str] = pa.Field(coerce=True, nullable=True, description="Reports To ID", alias="reportsTo.id")
25
+ reports_to_first_name: Series[str] = pa.Field(coerce=True, nullable=True, description="Reports To First Name", alias="reportsTo.firstName")
26
+ reports_to_surname: Series[str] = pa.Field(coerce=True, nullable=True, description="Reports To Surname", alias="reportsTo.surname")
27
+ reports_to_email: Series[str] = pa.Field(coerce=True, nullable=True, description="Reports To Email", alias="reportsTo.email")
28
+ reports_to_display_name: Series[str] = pa.Field(coerce=True, nullable=True, description="Reports To Display Name", alias="reportsTo.displayName")
29
29
  reports_to: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True, description="Reports To", alias="reportsTo")
30
30
  employee_id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
31
31
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.0
2
2
  Name: brynq-sdk-bob
3
- Version: 2.5.0
3
+ Version: 2.6.0
4
4
  Summary: Bob wrapper from BrynQ
5
5
  Home-page: UNKNOWN
6
6
  Author: BrynQ
@@ -2,7 +2,7 @@ from setuptools import setup, find_namespace_packages
2
2
 
3
3
  setup(
4
4
  name='brynq_sdk_bob',
5
- version='2.5.0',
5
+ version='2.6.0',
6
6
  description='Bob wrapper from BrynQ',
7
7
  long_description='Bob wrapper from BrynQ',
8
8
  author='BrynQ',
@@ -1,344 +0,0 @@
1
- import pandas as pd
2
- import re
3
- from brynq_sdk_functions import Functions
4
- from .bank import Bank
5
- from .employment import Employment
6
- from .salaries import Salaries
7
- from .schemas.people import PeopleSchema
8
- from .work import Work
9
- from .custom_tables import CustomTables
10
-
11
-
12
- class People:
13
- def __init__(self, bob):
14
- self.bob = bob
15
- self.salaries = Salaries(bob)
16
- self.employment = Employment(bob)
17
- self.bank = Bank(bob)
18
- self.work = Work(bob)
19
- self.custom_tables = CustomTables(bob)
20
- self.schema = PeopleSchema
21
- self.field_name_in_body, self.field_name_in_response, self.endpoint_to_response = self._init_fields()
22
-
23
- # Define payroll information types and their configurations
24
- self.payroll_types = {
25
- 'entitlement': {
26
- 'pattern': 'payroll.entitlement.',
27
- 'named_list_key': 'entitlementType'
28
- },
29
- 'variable': {
30
- 'pattern': 'payroll.variable.',
31
- 'named_list_key': 'payrollVariableType'
32
- }
33
- }
34
- self.payroll_mappings = {}
35
-
36
- def _add_payroll_fields_to_request(self, body_fields: list, response_fields: list, payroll_type: str) -> tuple[list, list]:
37
- """
38
- Add payroll fields to request fields for a specific payroll type.
39
-
40
- Args:
41
- body_fields: Current body fields
42
- response_fields: Current response fields
43
- payroll_type: Type of payroll information (e.g., 'entitlement', 'variable')
44
-
45
- Returns:
46
- - body_fields: Updated body fields including payroll fields
47
- - response_fields: Updated response fields including payroll fields
48
- """
49
- if payroll_type not in self.payroll_types:
50
- return body_fields, response_fields
51
-
52
- pattern = self.payroll_types[payroll_type]['pattern']
53
- payroll_fields_to_add = [field for field in self.field_name_in_body if pattern in field.lower()]
54
- payroll_response_fields = [self.endpoint_to_response.get(field) for field in payroll_fields_to_add if field in self.endpoint_to_response]
55
-
56
- for field in payroll_fields_to_add:
57
- if field not in body_fields:
58
- body_fields.append(field)
59
-
60
- for field in payroll_response_fields:
61
- if field and field not in response_fields:
62
- response_fields.append(field)
63
-
64
- return body_fields, response_fields
65
-
66
- def _get_payroll_mapping(self, payroll_type: str) -> dict:
67
- """
68
- Get mapping of payroll IDs to their names from the named-lists endpoint for a specific payroll type.
69
-
70
- Args:
71
- payroll_type: Type of payroll information (e.g., 'entitlement', 'variable')
72
-
73
- Returns:
74
- Dictionary mapping IDs to names
75
- """
76
- if payroll_type not in self.payroll_types:
77
- return {}
78
-
79
- # Check if we already have the mapping cached
80
- if payroll_type in self.payroll_mappings:
81
- return self.payroll_mappings[payroll_type]
82
-
83
- resp = self.bob.session.get(
84
- url=f"{self.bob.base_url}company/named-lists",
85
- timeout=self.bob.timeout,
86
- headers=self.bob.headers
87
- )
88
- named_lists = resp.json()
89
-
90
- # Extract the mapping for this payroll type
91
- mapping = {}
92
- named_list_key = self.payroll_types[payroll_type]['named_list_key']
93
- if named_list_key in named_lists:
94
- for value in named_lists[named_list_key]['values']:
95
- mapping[value['id']] = value['name']
96
-
97
- # Cache the mapping
98
- self.payroll_mappings[payroll_type] = mapping
99
- return mapping
100
-
101
- def _flatten_nested_payroll_data(self, df: pd.DataFrame, pattern: str) -> pd.DataFrame:
102
- """
103
- Flatten nested JSON structures in payroll columns.
104
-
105
- Args:
106
- df: DataFrame with potentially nested payroll data
107
- pattern: Pattern to identify payroll columns (e.g., 'payroll.variable.')
108
-
109
- Returns:
110
- DataFrame with flattened payroll columns
111
- """
112
- # Identify payroll columns
113
- payroll_columns = [col for col in df.columns if pattern in col.lower()]
114
-
115
- if not payroll_columns:
116
- return df
117
-
118
- # Create a copy to avoid modifying the original
119
- df_result = df.copy()
120
-
121
- # Process each payroll column
122
- for col in payroll_columns:
123
- # Check if the column contains nested data
124
- if df_result[col].notna().any():
125
- # Get the first non-null value to check structure
126
- sample_value = df_result[col].dropna().iloc[0] if not df_result[col].dropna().empty else None
127
-
128
- if isinstance(sample_value, dict):
129
- # Flatten nested structure
130
- nested_df = pd.json_normalize(df_result[col].tolist())
131
-
132
- # Rename columns to include the original column name as prefix
133
- nested_df.columns = [f"{col}.{subcol}" for subcol in nested_df.columns]
134
-
135
- # Drop the original column and add flattened columns
136
- df_result = df_result.drop(columns=[col])
137
- df_result = pd.concat([df_result, nested_df], axis=1)
138
-
139
- return df_result
140
-
141
- def _extract_payroll_columns(self, df: pd.DataFrame, payroll_type: str) -> pd.DataFrame:
142
- """
143
- Extract payroll columns from DataFrame and rename them based on mapping.
144
-
145
- Args:
146
- df: DataFrame containing all data including payroll columns
147
- payroll_type: Type of payroll information (e.g., 'entitlement', 'variable')
148
-
149
- Returns:
150
- DataFrame with only payroll columns (renamed if mapping available)
151
- """
152
- if payroll_type not in self.payroll_types:
153
- return pd.DataFrame(index=df.index)
154
-
155
- # Get the pattern for this payroll type
156
- pattern = self.payroll_types[payroll_type]['pattern']
157
-
158
- # Identify all payroll columns for this type
159
- payroll_columns = [col for col in df.columns if pattern in col.lower()]
160
-
161
- if not payroll_columns:
162
- # No payroll columns found, return empty DataFrame
163
- return pd.DataFrame(index=df.index)
164
-
165
- # Extract payroll columns
166
- df_payroll = df[payroll_columns].copy()
167
-
168
- # Get mapping for this payroll type
169
- payroll_mapping = self._get_payroll_mapping(payroll_type)
170
-
171
- # Rename payroll columns if mapping is available
172
- rename_dict = {}
173
- if payroll_mapping:
174
- for col in df_payroll.columns:
175
- # Extract the ID from the column name (any digits after the pattern)
176
- # Use case-insensitive match but extract from original column name
177
- pattern_regex = pattern.replace('.', r'\.') # Escape dots for regex
178
- match = re.search(rf'({pattern_regex})(\d+)', col, re.IGNORECASE)
179
- if match:
180
- payroll_id = match.group(2)
181
- if payroll_id in payroll_mapping:
182
- # Replace only the first occurrence of the ID with the name
183
- # This preserves any suffixes like .value, .currency, etc.
184
- new_col_name = col[:match.start(2)] + payroll_mapping[payroll_id] + col[match.end(2):]
185
- rename_dict[col] = new_col_name
186
-
187
- # Apply the renaming
188
- if rename_dict:
189
- df_payroll = df_payroll.rename(columns=rename_dict)
190
-
191
- return df_payroll
192
-
193
- def get(self, additional_fields: list[str] = None, field_selection: list[str] = None, add_payroll_information: list[str] = None) -> tuple[pd.DataFrame, pd.DataFrame]:
194
- """
195
- Get people from Bob
196
-
197
- Args:
198
- additional_fields (list[str]): Additional fields to get (not defined in the schema)
199
- field_selection (list[str]): Fields to get (defined in the schema), if not provided, all fields are returned.
200
- Can be either pythonic field names (e.g., 'work_start_date') or Bob API field names (e.g., 'work.startDate').
201
- add_payroll_information (list[str]): List of payroll information types to include (valid options: 'entitlement', 'variable')
202
-
203
- Returns:
204
- valid_people (pd.DataFrame): Validated people data (with payroll information appended if requested)
205
- invalid_people (pd.DataFrame): Invalid records
206
- """
207
- #resp = self.bob.session.get(url=f"{self.bob.base_url}profiles", timeout=self.bob.timeout)
208
- if additional_fields is None:
209
- additional_fields = []
210
- body_fields = list(set(self.field_name_in_body + additional_fields))
211
- response_fields = list(set(self.field_name_in_response + additional_fields))
212
-
213
- if field_selection:
214
- # Convert pythonic field names to aliases (e.g., 'work_start_date' -> 'work.startDate')
215
- field_selection = PeopleSchema.convert_pythonic_to_alias(field_selection)
216
-
217
- body_fields = [field for field in body_fields if field in field_selection]
218
- response_fields = [self.endpoint_to_response.get(field) for field in field_selection if field in self.endpoint_to_response]
219
-
220
- # Add payroll fields to request if needed (before making the API call)
221
- if add_payroll_information:
222
- # Validate and filter payroll types
223
- valid_payroll_types = [pt for pt in add_payroll_information if pt in self.payroll_types]
224
-
225
- for payroll_type in valid_payroll_types:
226
- body_fields, response_fields = self._add_payroll_fields_to_request(body_fields, response_fields, payroll_type)
227
-
228
- # Bob sucks with default fields so you need to do a search call to retrieve additional fields.
229
- resp_additional_fields = self.bob.session.post(url=f"{self.bob.base_url}people/search",
230
- json={
231
- "fields": body_fields,
232
- "filters": []
233
- },
234
- timeout=self.bob.timeout)
235
- df = pd.json_normalize(resp_additional_fields.json()['employees'])
236
-
237
- # Validate payroll types if requested
238
- valid_payroll_types = []
239
- if add_payroll_information:
240
- valid_payroll_types = [pt for pt in add_payroll_information if pt in self.payroll_types]
241
-
242
- # Flatten nested data for each payroll type
243
- for payroll_type in valid_payroll_types:
244
- pattern = self.payroll_types[payroll_type]['pattern']
245
- df = self._flatten_nested_payroll_data(df, pattern)
246
-
247
- # Now filter columns - include original response_fields plus any flattened payroll columns
248
- columns_to_keep = []
249
- for col in df.columns:
250
- # Keep if it's in response_fields
251
- if col in response_fields:
252
- columns_to_keep.append(col)
253
- # Or if it's a payroll column (original or flattened)
254
- elif valid_payroll_types:
255
- for payroll_type in valid_payroll_types:
256
- pattern = self.payroll_types[payroll_type]['pattern']
257
- if pattern in col.lower():
258
- columns_to_keep.append(col)
259
- break
260
-
261
- df = df[columns_to_keep]
262
-
263
- # Map DataFrame columns (response fields) back to alias (endpoint) fields for validation
264
- response_to_endpoint = {v: k for k, v in self.endpoint_to_response.items() if v}
265
- df = df.rename(columns={col: response_to_endpoint[col] for col in df.columns if col in response_to_endpoint})
266
-
267
- # Extract payroll information if requested
268
- payroll_dataframes = {}
269
- if valid_payroll_types:
270
- for payroll_type in valid_payroll_types:
271
- # Extract payroll columns into separate DataFrame
272
- df_payroll = self._extract_payroll_columns(df, payroll_type)
273
- if not df_payroll.empty:
274
- payroll_dataframes[payroll_type] = df_payroll
275
-
276
- # Remove payroll columns from main DataFrame for validation
277
- pattern = self.payroll_types[payroll_type]['pattern']
278
- payroll_columns = [col for col in df.columns if pattern in col.lower()]
279
- if payroll_columns:
280
- df = df.drop(columns=payroll_columns)
281
-
282
- # Validate the data (without payroll information)
283
- valid_people, invalid_people = Functions.validate_data(df=df, schema=PeopleSchema, debug=True)
284
-
285
- # Append all payroll information to valid_people if they exist
286
- if payroll_dataframes and not valid_people.empty:
287
- for payroll_type, df_payroll in payroll_dataframes.items():
288
- # Only include payroll data for valid rows
289
- df_payroll_valid = df_payroll.loc[valid_people.index]
290
- # Append payroll columns to valid_people
291
- valid_people = pd.concat([valid_people, df_payroll_valid], axis=1)
292
-
293
-
294
- # 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.
295
- resp_named_lists = self.bob.session.get(url=f"{self.bob.base_url}company/named-lists", timeout=self.bob.timeout, headers=self.bob.headers)
296
- named_lists = resp_named_lists.json()
297
-
298
- # Transform named_lists to create id-to-value mappings for each field
299
- named_lists = {key.split('.')[-1]: {item['id']: item['value'] for item in value['values']} for key, value in named_lists.items()}
300
-
301
- for field in valid_people.columns:
302
- # Fields in the response and in the named-list does have different building blocks (e.g. people.payroll.entitlement. or people.entitlement.). But they both end with the same last block
303
- field_df = field.split('.')[-1].split('work_')[-1]
304
- if field_df in named_lists.keys() and field_df not in ['site']:
305
- valid_people[field] = valid_people[field].map(named_lists[field_df])
306
-
307
- return valid_people, invalid_people
308
-
309
-
310
- def _init_fields(self) -> tuple[list[str], list[str], dict[str, str]]:
311
- resp_fields = self.bob.session.get(
312
- url=f"{self.bob.base_url}company/people/fields",
313
- timeout=self.bob.timeout,
314
- headers=self.bob.headers
315
- )
316
- fields = resp_fields.json()
317
- field_name_in_body = [field.get('id') for field in fields]
318
- # For all field names in field_name_in_body containing 'root.', add an alternative without 'root.' in addition to those fields
319
- field_name_in_body = field_name_in_body
320
- field_name_in_response = [field['jsonPath'] for field in fields]
321
- endpoint_to_response = {field['id']: field['jsonPath'] for field in fields}
322
-
323
- return field_name_in_body, field_name_in_response, endpoint_to_response
324
-
325
-
326
-
327
- def _get_employee_id_to_person_id_mapping(self) -> tuple[pd.DataFrame, pd.DataFrame]:
328
- employee_id_in_company = "work.employeeIdInCompany"
329
- person_id = "root.id"
330
-
331
- body_fields = [employee_id_in_company, person_id]
332
- response_fields = [self.endpoint_to_response.get(field) for field in body_fields if field in self.endpoint_to_response]
333
-
334
- resp_additional_fields = self.bob.session.post(url=f"{self.bob.base_url}people/search",
335
- json={
336
- "fields": body_fields,
337
- "filters": []
338
- },
339
- timeout=self.bob.timeout)
340
- df = pd.json_normalize(resp_additional_fields.json()['employees'])
341
- df = df[[col for col in response_fields if col in df.columns]]
342
- # Get the valid column names from PeopleSchema
343
- valid_people, invalid_people = Functions.validate_data(df=df, schema=PeopleSchema, debug=True)
344
- return valid_people, invalid_people
@@ -1,24 +0,0 @@
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
- self.schema = SalarySchema
11
-
12
- def get(self) -> (pd.DataFrame, pd.DataFrame):
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)
17
- df = pd.json_normalize(
18
- data,
19
- record_path='values',
20
- meta=['employeeId']
21
- )
22
- valid_salaries, invalid_salaries = Functions.validate_data(df=df, schema=SalarySchema, debug=True)
23
-
24
- return valid_salaries, invalid_salaries
@@ -1,136 +0,0 @@
1
- from datetime import datetime
2
- from typing import Optional, List, Dict
3
-
4
- import pandas as pd
5
- import pandera as pa
6
- from pandera import Bool
7
- from pandera.typing import Series, String, Float, DateTime
8
- import pandera.extensions as extensions
9
- from brynq_sdk_functions import BrynQPanderaDataFrameModel
10
-
11
-
12
-
13
- @extensions.register_check_method()
14
- def check_list(x):
15
- return isinstance(x, list)
16
-
17
-
18
- class PeopleSchema(BrynQPanderaDataFrameModel):
19
- id: Optional[Series[String]] = pa.Field(coerce=True, description="Person ID", alias="root.id")
20
- display_name: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Display Name", alias="displayName")
21
- company_id: Optional[Series[String]] = pa.Field(coerce=True, description="Company ID", alias="companyId")
22
- email: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Email", alias="root.email")
23
- home_mobile_phone: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Personal Mobile Phone", alias="home.mobilePhone")
24
- surname: Optional[Series[String]] = pa.Field(coerce=True, description="Surname", alias="root.surname")
25
- first_name: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="First Name", alias="root.firstName")
26
- full_name: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Full Name", alias="fullName")
27
- personal_birth_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Personal Birth Date", alias="personal.birthDate")
28
- personal_pronouns: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Personal Pronouns", alias="personal.pronouns")
29
- personal_honorific: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Personal Honorific", alias="personal.honorific")
30
- personal_nationality: Optional[Series[object]] = pa.Field(coerce=True, check_name=check_list, description="Personal Nationality", alias="personal.nationality")
31
- # employee_payroll_manager: Series[String] = pa.Field(coerce=True, nullable=True)
32
- # employee_hrbp: Series[String] = pa.Field(coerce=True, nullable=True)
33
- # employee_it_admin: Series[String] = pa.Field(coerce=True, nullable=True)
34
- # employee_buddy: Series[String] = pa.Field(coerce=True, nullable=True)
35
- employee_veteran_status: Optional[Series[object]] = pa.Field(coerce=True, check_name=check_list, description="Employee Veteran Status", alias="employee.veteranStatus")
36
- employee_disability_status: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Employee Disability Status", alias="employee.disabilityStatus")
37
- work_start_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Work Start Date", alias="work.startDate")
38
- work_manager: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Manager", alias="work.manager")
39
- work_work_phone: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Work Phone", alias="work.workPhone")
40
- work_tenure_duration_period_i_s_o: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Tenure Duration Period ISO", alias="work.tenureDurationPeriodIso")
41
- work_tenure_duration_sort_factor: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=False, description="Work Tenure Duration Sort Factor", alias="work.tenureDurationSortFactor")
42
- work_tenure_duration_humanize: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Tenure Duration Humanize", alias="work.tenureDurationHumanize")
43
- work_duration_of_employment_period_i_s_o: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Duration of Employment Period ISO", alias="work.durationOfEmploymentPeriodIso")
44
- work_duration_of_employment_sort_factor: Optional[Series[String]] = pa.Field(coerce=True, nullable=False, description="Work Duration of Employment Sort Factor", alias="work.durationOfEmploymentSortFactor")
45
- work_duration_of_employment_humanize: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Duration of Employment Humanize", alias="work.durationOfEmploymentHumanize")
46
- work_reports_to_id_in_company: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Work Reports to ID in Company", alias="work.reportsToIdInCompany")
47
- work_employee_id_in_company: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Work Employee ID in Company", alias="work.employeeIdInCompany")
48
- work_reports_to_display_name: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Reports to Display Name", alias="work.reportsToDisplayName")
49
- work_reports_to_email: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Reports to Email", alias="work.reportsToEmail")
50
- work_reports_to_surname: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Reports to Surname", alias="work.reportsToSurname")
51
- work_reports_to_first_name: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Reports to First Name", alias="work.reportsToFirstName")
52
- work_reports_to_id: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Work Reports to ID", alias="work.reportsToId")
53
- work_work_mobile: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Work Mobile", alias="work.workMobile")
54
- work_indirect_reports: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Work Indirect Reports", alias="work.indirectReports")
55
- work_site_id: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Work Site ID", alias="work.siteId")
56
- work_department: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Department", alias="work.department")
57
- work_tenure_duration_years: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Work Tenure Duration Years", alias="work.tenureDurationYears")
58
- work_tenure_years: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Work Tenure Years", alias="work.tenureYears")
59
- work_is_manager: Optional[Series[Bool]] = pa.Field(coerce=True, nullable=True, description="Work Is Manager", alias="work.isManager")
60
- work_title: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Title", alias="work.title")
61
- work_site: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Site", alias="work.site")
62
- work_original_start_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Work Original Start Date", alias="work.originalStartDate")
63
- work_active_effective_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Work Active Effective Date", alias="work.activeEffectiveDate")
64
- work_direct_reports: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Work Direct Reports", alias="work.directReports")
65
- # work_work_change_type: Series[String] = pa.Field(coerce=True, nullable=True)
66
- work_second_level_manager: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Second Level Manager", alias="work.secondLevelManager")
67
- work_days_of_previous_service: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Work Days of Previous Service", alias="work.daysOfPreviousService")
68
- work_years_of_service: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Work Years of Service", alias="work.yearsOfService")
69
- payroll_employment_contract: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Contract Type", alias="payroll.employment.contract")
70
- payroll_employment_type: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Employment Type", alias="payroll.employment.type")
71
- tax_id: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Tax ID", alias="payroll.taxCode")
72
-
73
- about_food_preferences: Optional[Series[object]] = pa.Field(coerce=True, check_name=check_list, description="About Food Preferences", alias="about.foodPreferences")
74
- # about_social_data_linkedin: Series[String] = pa.Field(coerce=True, nullable=True)
75
- about_social_data_twitter: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="About Social Data Twitter", alias="about.socialDataTwitter")
76
- about_social_data_facebook: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="About Social Data Facebook", alias="about.socialDataFacebook")
77
- about_superpowers: Optional[Series[object]] = pa.Field(coerce=True, check_name=check_list, description="About Superpowers", alias="about.superpowers")
78
- about_hobbies: Optional[Series[object]] = pa.Field(coerce=True, check_name=check_list, description="About Hobbies", alias="about.hobbies")
79
- about_about: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="About About", alias="about.about")
80
- about_avatar: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="About Avatar", alias="about.avatar")
81
- address_city: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Address City", alias="address.city")
82
- address_post_code: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Address Post Code", alias="address.postCode")
83
- address_line1: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Address Line 1", alias="address.line1")
84
- address_line2: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Address Line 2", alias="address.line2")
85
- address_country: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Address Country", alias="address.country")
86
- address_active_effective_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Address Active Effective Date", alias="address.activeEffectiveDate")
87
- home_legal_gender: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Legal Gender", alias="home.legalGender")
88
- home_family_status: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Family / Marital Status", alias="home.familyStatus")
89
- home_spouse_first_name: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Spouse First Name", alias="home.SpouseFirstName")
90
- home_spouse_surname: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Spouse Surname", alias="home.SpouseSurname")
91
- # home_spouse_birth_date: Series[DateTime] = pa.Field(coerce=True, nullable=True)
92
- home_spouse_gender: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Spouse Gender", alias="home.SpouseGender")
93
- identification_ssn_serial_number: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="SSN Serial Number", alias="identification.ssnSerialNumber")
94
- internal_termination_reason: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Internal Termination Reason", alias="internal.terminationReason")
95
- internal_termination_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Internal Termination Date", alias="internal.terminationDate")
96
- internal_termination_type: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Internal Termination Type", alias="internal.terminationType")
97
- employee_last_day_of_work: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Employee Last Day of Work", alias="employee.lastDayOfWork")
98
-
99
- class Config:
100
- coerce = True
101
-
102
- @classmethod
103
- def convert_pythonic_to_alias(cls, pythonic_fields: List[str]) -> List[str]:
104
- """
105
- Convert pythonic field names to their Bob API aliases.
106
-
107
- Args:
108
- pythonic_fields: List of pythonic field names (e.g., ['work_start_date', 'first_name'])
109
-
110
- Returns:
111
- List of Bob API field aliases (e.g., ['work.startDate', 'root.firstName'])
112
- """
113
- # Get the alias mapping from the class (populated by BrynQPanderaDataFrameModel.__init_subclass__)
114
- alias_map = getattr(cls, "_alias_map", {})
115
-
116
- # Convert the provided pythonic fields to aliases
117
- result = []
118
- for field in pythonic_fields:
119
- if field in alias_map:
120
- result.append(alias_map[field])
121
- else:
122
- # If not found in schema, keep the field as-is (could be a custom field)
123
- result.append(field)
124
-
125
- return result
126
-
127
- @classmethod
128
- def get_pythonic_to_alias_mapping(cls) -> Dict[str, str]:
129
- """
130
- Get the complete mapping from pythonic field names to Bob API aliases.
131
-
132
- Returns:
133
- Dictionary mapping pythonic field names to aliases
134
- """
135
- # Get the alias mapping from the class (populated by BrynQPanderaDataFrameModel.__init_subclass__)
136
- return getattr(cls, "_alias_map", {})
@@ -1,25 +0,0 @@
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
- from brynq_sdk_functions import BrynQPanderaDataFrameModel
7
-
8
-
9
- class SalarySchema(BrynQPanderaDataFrameModel):
10
- id: Series[String] = pa.Field(coerce=True, description="Salary ID", alias="id")
11
- employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
12
- pay_frequency: Series[String] = pa.Field(coerce=True, nullable=True, description="Pay Frequency", alias="payFrequency") # has a list of possible values , isin=['Monthly']
13
- creation_date: Series[DateTime] = pa.Field(coerce=True, nullable=True, description="Creation Date", alias="creationDate")
14
- is_current: Series[Bool] = pa.Field(coerce=True, description="Is Current", alias="isCurrent")
15
- modification_date: Series[DateTime] = pa.Field(coerce=True, nullable=True, description="Modification Date", alias="modificationDate")
16
- effective_date: Series[DateTime] = pa.Field(coerce=True, description="Effective Date", alias="effectiveDate")
17
- end_effective_date: Series[DateTime] = pa.Field(coerce=True, nullable=True, description="End Effective Date", alias="endEffectiveDate")
18
- change_reason: Series[str] = pa.Field(coerce=True, nullable=True, description="Change Reason", alias="change.reason")
19
- pay_period: Series[String] = pa.Field(coerce=True, nullable=True, description="Pay Period", alias="payPeriod")
20
- base_value: Series[Float] = pa.Field(coerce=True, nullable=True, description="Base Value", alias="base.value") #needs to become base.value?
21
- base_currency: Series[String] = pa.Field(coerce=True, nullable=True, description="Base Currency", alias="base.currency")
22
- active_effective_date: Series[DateTime] = pa.Field(coerce=True, nullable=True, description="Active Effective Date", alias="activeEffectiveDate")
23
-
24
- class Config:
25
- coerce = True
File without changes