Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add BIT personal background to MS schema #1092

Open
wants to merge 10 commits into
base: bit
Choose a base branch
from
146 changes: 146 additions & 0 deletions app/database/models/personal_background.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
from sqlalchemy import null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment here specifying that this is a BIT extension?

from app.database.sqlalchemy_extension import db
from app.database.db_types.JsonCustomType import JsonCustomType
from app.utils.bitschema_utils import (
Gender,
Age,
Ethnicity,
SexualOrientation,
Religion,
PhysicalAbility,
MentalAbility,
SocioEconomic,
HighestEducation,
YearsOfExperience,
)


class PersonalBackgroundModel(db.Model):
"""Defines attributes for user's personal background.

Attributes:
user_id: An integer for storing the user's id.
gender: A string for storing the user's gender.
age: A string for storing the user's age.
ethnicity: A string for storing the user's wthnicity.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ethnicity: A string for storing the user's wthnicity.
ethnicity: A string for storing the user's ethnicity.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @mtreacy002 this is not resolved yet

sexual_orientation: A string for storing the user's sexual orientation.
religion: A string for storing the user's religion.
physical_ability: A string for storing the user's physical ability.
mental_ability: A string for storing the user's mental ability.
socio_economic: A string for storing the user's socio economic level.
highest_education: A string for storing the user's highest education level.
years_of_experience: A string for storing the user's length of expeprience in the It related area.
others: A JSON data type for storing users descriptions of 'other' fields.
is_public: A boolean indicating if user has agreed to display their personal background information publicly to other members.
"""

# Specifying database table used for PersonalBackgroundModel
__tablename__ = "personal_backgrounds"
__table_args__ = {"extend_existing": True}

id = db.Column(db.Integer, primary_key=True)

# User's personal background data
user_id = db.Column(
db.Integer,
db.ForeignKey("users.id", ondelete="CASCADE"),
nullable=False,
unique=True,
)
gender = db.Column(db.Enum(Gender))
age = db.Column(db.Enum(Age))
ethnicity = db.Column(db.Enum(Ethnicity))
sexual_orientation = db.Column(db.Enum(SexualOrientation))
religion = db.Column(db.Enum(Religion))
physical_ability = db.Column(db.Enum(PhysicalAbility))
mental_ability = db.Column(db.Enum(MentalAbility))
socio_economic = db.Column(db.Enum(SocioEconomic))
highest_education = db.Column(db.Enum(HighestEducation))
years_of_experience = db.Column(db.Enum(YearsOfExperience))
others = db.Column(JsonCustomType)
is_public = db.Column(db.Boolean)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why exactly we would need such information. I'll leave that to @isabelcosta

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@epicadk , do you mean years of experience? First of all, I believe this is relevant to support one of BIT goals which is to improve equity in IT because many with no to limited years of experience in IT were left out from employment opportunity. We often see a position for entry level required more than 3 - 5 years of experience. Stating this years of experience encouraging industries to target people within the lower years of experience to be the mentee and people with the higher level of experience to be the mentor. Their willingness to go against the norm in employment market (aka to be more inclusive by targeting those within the minority groups, e.g. minimal years of experience) is going to be highlighted in the BIT Landing page as per the Mockup in my GSoC20 proposal.
image

def __init__(
self,
user_id,
gender,
age,
ethnicity,
sexual_orientation,
religion,
physical_ability,
mental_ability,
socio_economic,
highest_education,
years_of_experience,
):
"""Initialises PersonalBackgroundModel class."""
## required fields
self.user_id = user_id
self.gender = gender
self.age = age
self.ethnicity = ethnicity
self.sexual_orientation = sexual_orientation
self.religion = religion
self.physical_ability = physical_ability
self.mental_ability = mental_ability
self.socio_economic = socio_economic
self.highest_education = highest_education
self.years_of_experience = years_of_experience

# default values
self.others = None
self.is_public = False
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should not be a boolean option.
I would say this could be an enum to represent different visibility levels. E.g.:

  • Not visible to anyone
  • Visible to BIT only
  • Visible to Organizations only
  • Visible to all users (probably no one will select this though 🤔 )


def json(self):
"""Returns PersonalBackgroundModel object in json format."""
return {
"id": self.id,
"user_id": self.user_id,
"age": self.age,
"ethnicity": self.ethnicity,
"sexual_orientation": self.sexual_orientation,
"religion": self.religion,
"physical_ability": self.physical_ability,
"mental_ability": self.mental_ability,
"socio_economic": self.socio_economic,
"highest_education": self.highest_education,
"years_of_experience": self.years_of_experience,
"others": self.others,
"is_public": self.is_public,
}

def __repr__(self):
"""Returns user's background."""

return (
f"User's id is {self.user_id}.\n"
f"User's age is: {self.age}\n"
f"User's ethnicity is: {self.ethnicity}\n"
f"User's sexual orientation is: {self.sexual_orientation}\n"
f"User's religion is: {self.religion}\n"
f"User's physical ability is: {self.physical_ability}\n"
f"User's mental ability is: {self.mental_ability}\n"
f"User's socio economic category is: {self.socio_economic}\n"
f"User's highest level of education is: {self.highest_education}\n"
f"User's length of experience is: {self.years_of_experience}\n"
)

@classmethod
def find_by_user_id(cls, user_id) -> "PersonalBackgroundModel":

"""Returns the user's background that has the passed user id.
Args:
_id: The id of a user.
"""
return cls.query.filter_by(user_id=user_id).first()

def save_to_db(self) -> None:
"""Adds user's personal background to the database."""
db.session.add(self)
db.session.commit()

def delete_from_db(self) -> None:
"""Deletes user's personal background from the database."""
db.session.delete(self)
db.session.commit()
164 changes: 164 additions & 0 deletions app/utils/bitschema_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -597,3 +597,167 @@ class Timezone(Enum):

def timezone(self):
return list(map(str, self))


# Enum related to personal background
@unique
class ProgramStatus(Enum):
DRAFT = "Draft"
OPEN = "Open"
IN_PROGRESS = "In_Progress"
COMPLETED = "Completed"
CLOSED = "Closed"

def programStatus(self):
return list(map(str, self))


@unique
class OrganizationStatus(Enum):
DRAFT = "Draft"
PUBLISH = "Publish"
ARCHIVED = "Archived"

def OrganizationStatus(self):
return list(map(str, self))


@unique
class Gender(Enum):
FEMALE = "Female"
MALE = "Male"
OTHER = "Other"
DECLINED = "Prefer not to say"
NOT_APPLICABLE = "Not Applicable"

def gender(self):
return list(map(str, self))


@unique
class Age(Enum):
UNDER_18 = "Under 18"
AGE_18_TO_20 = "Between 18 to 20 yo"
AGE_21_TO_24 = "Between 21 to 24 yo"
AGE_25_TO_34 = "Between 25 to 34 yo"
AGE_35_TO_44 = "Between 35 to 44 yo"
AGE_45_TO_54 = "Between 45 to 54 yo"
AGE_55_TO_64 = "Between 55 to 64 yo"
ABOVE_65_YO = "Above 65 yo"
DECLINED = "Prefer not to say"
NOT_APPLICABLE = "Not Applicable"

def age(self):
return list(map(str, self))


@unique
class Ethnicity(Enum):
AFRICAN_AMERICAN = "African-American/Black"
CAUCASIAN = "Caucasian/White"
HISPANIC = "Hispanic/Latinx"
NATIVE_AMERICAN = "Native American/Alaska Native/First Nations"
MIDDLE_EASTERN = "Middle Eastern/North African (MENA)"
ASIAN = "Asian"
OTHER = "Other"
DECLINED = "Prefer not to say"
NOT_APPLICABLE = "Not Applicable"

def ethnicity(self):
return list(map(str, self))


@unique
class SexualOrientation(Enum):
HETEROSEXUAL = "Heterosexual/Straight"
LGBTIA = "LGBTIA+"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you forgot the Q LGBTQIA ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will make the change soon 😉

OTHER = "Other"
DECLINED = "Prefer not to say"
NOT_APPLICABLE = "Not Applicable"

def sexualOrientation(self):
return list(map(str, self))


@unique
class Religion(Enum):
CHRISTIANITY = "Christianity"
JUDAISM = "Judaism"
ISLAM = "Islam"
HINDUISM = "Hinduism"
BUDDHISM = "Buddhism"
OTHER = "Other"
DECLINED = "Prefer not to say"
NOT_APPLICABLE = "Not Applicable"

def religion(self):
return list(map(str, self))


@unique
class PhysicalAbility(Enum):
WITH_DISABILITY = "With/had limited physical ability (or with/had some type of physical disability/ies)"
WITHOUT_DISABILITY = "Without/have no limitation to physical ability/ies"
OTHER = "Other"
DECLINED = "Prefer not to say"
NOT_APPLICABLE = "Not Applicable"

def physicalAbility(self):
return list(map(str, self))


@unique
class MentalAbility(Enum):
WITH_DISORDER = "With/previously had some type of mental disorders"
WITHOUT_DISORDER = "Without/have no mental disorders"
OTHER = "Other"
DECLINED = "Prefer not to say"
NOT_APPLICABLE = "Not Applicable"

def mentalAbility(self):
return list(map(str, self))


@unique
class SocioEconomic(Enum):
UPPER = "Upper class/Elite"
UPPER_MIDDLE = "Upper Middle class (or High-level Professionals/white collars e.g. enginers/accountants/lawyers/architects/managers/directors"
LOWER_MIDDLE = "Lower Middle class (e.g. blue collars in skilled trades/Paralegals/Bank tellers/Sales/Clerical-Admin/other support workers)"
WORKING = "Working class (e.g. craft workers factory labourers restaurant/delivery services workers"
BELOW_POVERTY = "Underclass working but with wages under poverty line receiving Social Benefit from Government"
OTHER = "Other"
DECLINED = "Prefer not to say"
NOT_APPLICABLE = "Not Applicable"

def socioEconomic(self):
return list(map(str, self))


@unique
class HighestEducation(Enum):
BELOW_HIGH_SCHOOL = "Have/did not completed High School"
HIGH_SCHOOL = "High School Diploma"
ASSOCIATE = "Associate Degree"
BACHELOR = "Bachelor's Degree"
MASTER = "Master's Degree"
PHD = "PhD or other Doctorate Degrees"
OTHER = "Other"
DECLINED = "Prefer not to say"
NOT_APPLICABLE = "Not Applicable"

def highestEducation(self):
return list(map(str, self))


@unique
class YearsOfExperience(Enum):
UNDER_ONE = "Less than a year"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to just store numbers (number of years of experience) in the database instead of storing strings.

Copy link
Member Author

@mtreacy002 mtreacy002 May 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@epicadk , the reason I did it like this (in word instead of number) is because IMO it makes more sense when an organization open a mentoring program opportunity, they can state the target candidate based on a range of years of experience (e.g. between 3 to 5 years) instead of a distinct year 1,2,3, or 4 yr etc. This is also to make it easier for later when we make the logic which allows user to filter mentoring program based on certain categories (e.g. years of experience required "between 3 - 5 years" instead of selecting a particular number like 1,2,3 or 4 yr etc). This way it also limits the options to 5 only. Hope this makes sense 😉 .
image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can still make the same query with databases ( possibly more optimized as well if we use indexing) further every year we could just ++ the count in the database rather than having to migrate the strings.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@epicadk , what do you mean by

further every year we could just ++ the count in the database rather than having to migrate the strings.

Do you mean we need to make a scheduler to automatically add one year to the years of experience of each users who have this data instead of users manually updating their years of experience from their end? We could do that I suppose, but we're at disadvantage when the user is no longer in the industry but hasn't cancelled their membership/delete their account with us as that would be an inefficient use of overhead, plus we don't know how many of our users are in this category 🤔 .

Let's wait for @isabelcosta's opinion on this 😉 .

UP_TO_3 = "Between 1 to 3 years"
UP_TO_5 = "Between 3 to 5 years"
UP_TO_10 = "Between 5 to 10 year"
OVER_10 = "Over 10 years of experience"
DECLINED = "Prefer not to say"
NOT_APPLICABLE = "Not Applicable"

def yearsOfExperience(self):
return list(map(str, self))
Loading