Beispiel #1
0
def get_candidate_if_validated(user, candidate_id):
    """
    Function will return candidate if:
        1. it exists
        2. not archived, and
        3. belongs to user's domain
    :type user: User
    :type candidate_id: int | long
    :rtype: Candidate
    """
    candidate = Candidate.get(candidate_id)
    if not candidate:
        raise NotFoundError(
            error_message='Candidate not found: {}'.format(candidate_id),
            error_code=custom_error.CANDIDATE_NOT_FOUND)
    if candidate.is_archived:
        raise NotFoundError(
            error_message='Candidate not found: {}'.format(candidate_id),
            error_code=custom_error.CANDIDATE_IS_ARCHIVED)

    if user and user.role.name != 'TALENT_ADMIN' and candidate.user.domain_id != user.domain_id:
        raise ForbiddenError("Not authorized",
                             custom_error.CANDIDATE_FORBIDDEN)

    return candidate
Beispiel #2
0
def get_tags(candidate_id, tag_id=None):
    """
    :type candidate_id:  int|long
    :type tag_id:        int|long
    :return:
    """
    # Return specified tag if tag_id is provided
    if tag_id:
        tag_object = Tag.get(tag_id)
        if not tag_object:
            raise NotFoundError(
                'Tag ID ({}) is not recognized.'.format(tag_id),
                custom_error.TAG_NOT_FOUND)

        # Tag must belong to candidate
        if not CandidateTag.query.filter_by(tag_id=tag_id,
                                            candidate_id=candidate_id).first():
            raise ForbiddenError(
                'Tag (id = {}) does not belong to candidate (id = {})'.format(
                    tag_id, candidate_id), custom_error.TAG_FORBIDDEN)

        return {'name': tag_object.name}

    # Return all of candidate's tags if tag_id is not provided
    candidate_tags = CandidateTag.get_all(candidate_id)
    if not candidate_tags:
        raise NotFoundError('Candidate is not associated with any tags',
                            custom_error.TAG_NOT_FOUND)

    tags = []
    for candidate_tag in candidate_tags:
        tag = Tag.get(candidate_tag.tag_id)
        tags.append(dict(id=tag.id, name=tag.name))

    return {'tags': tags}
Beispiel #3
0
def match_candidate_from_openweb(url, auth_user):
    """
    Fetches candidate profiles from openweb and compare it to local db
    :param url:
    :param auth_user:
    :return: candidate sql query
    """
    openweb_response = query_openweb(url, 0)
    urls = []
    users_in_domain = [
        int(domain_user.id) for domain_user in db.session.query(User).filter(
            User.domain_id == auth_user.domain_id).all()
    ]

    if openweb_response:
        for candidate_url in openweb_response['webProfiles']:
            urls.append(openweb_response['webProfiles'][candidate_url]['url'])

        # find if candidate exists in gT database
        candidate_query = db.session.query(Candidate).join(CandidateSocialNetwork) \
            .filter(CandidateSocialNetwork.social_profile_url.in_(urls), Candidate.user_id.in_(users_in_domain)).first()

        if candidate_query:
            return True, candidate_query
        else:
            return False, openweb_response
    else:
        # in case candidate is not found in thesocialcv, try to find the url in directly from the database
        candidate_query = db.session.query(Candidate).join(CandidateSocialNetwork) \
            .filter(CandidateSocialNetwork.social_profile_url == url, Candidate.user_id.in_(users_in_domain)).first()
        if candidate_query:
            return True, candidate_query
        else:
            raise NotFoundError(error_message="candidate not found")
Beispiel #4
0
def update_candidate_tag(candidate_id, tag_id, tag_name):
    """
    Function will update candidate's tag
    :type candidate_id: int | long
    :type tag_id:       int | long
    :type tag_name      str
    :param tag_name     name of the tag, e.g. 'diligent', 'minority'
    :rtype  dict[int|long]
    """
    tag_obj_from_id = Tag.get(tag_id)
    if not tag_obj_from_id:
        raise NotFoundError("Tag ID: {} is not recognized".format(tag_id),
                            custom_error.TAG_NOT_FOUND)

    # Candidate must already be associated with provided tag_id
    candidate_tag_query = CandidateTag.query.filter_by(
        candidate_id=candidate_id, tag_id=tag_id)
    candidate_tag_object = candidate_tag_query.first()
    if not candidate_tag_object:
        raise InvalidUsage(
            'Candidate (id = {}) is not associated with Tag (id = {})'.format(
                candidate_id, tag_id), custom_error.INVALID_USAGE)

    # If Tag is not found, create it
    tag_object = Tag.get_by_name(tag_name)
    if not tag_object:
        tag_object = Tag(name=tag_name)
        db.session.add(tag_object)
        db.session.flush()

    # Update
    candidate_tag_query.update(
        dict(candidate_id=candidate_id, tag_id=tag_object.id))
    db.session.commit()
    return {'id': tag_object.id}
Beispiel #5
0
def get_candidate_if_exists(candidate_id):
    """
    Function checks to see if candidate exists in the database and is not web-hidden.
    If candidate is web-hidden or is not found, the appropriate exception will be raised;
    otherwise the Candidate-query-object will be returned.
    :type candidate_id: int|long
    :return  Candidate-object or raises NotFoundError
    """
    assert isinstance(candidate_id, (int, long))
    candidate = Candidate.get_by_id(candidate_id=candidate_id)
    if not candidate:
        raise NotFoundError(
            error_message='Candidate not found: {}'.format(candidate_id),
            error_code=custom_error.CANDIDATE_NOT_FOUND)
    if candidate.is_archived:
        raise NotFoundError(
            error_message='Candidate not found: {}'.format(candidate_id),
            error_code=custom_error.CANDIDATE_IS_ARCHIVED)
    return candidate
Beispiel #6
0
def find_in_openweb_by_email(candidate_email):
    """
    Function search openweb endpoint for email address
    :param candidate_email string
    :return: json object
    """
    openweb_response = query_openweb(candidate_email, 1)
    if openweb_response:
        return False, openweb_response
    else:
        raise NotFoundError(error_message="%s not found" % candidate_email)
Beispiel #7
0
def get_notes(candidate, note_id=None):
    """
    Function will return all of candidate's notes if note_id is not provided, otherwise it
    will return a single note object.
    :type candidate:  Candidate
    :type note_id:  int | long | None
    :rtype: dict | list[dict]
    """
    candidate_id = candidate.id

    # return specified note
    if note_id:

        note = CandidateTextComment.get(note_id)

        # Note ID must be recognized
        if not note:
            raise NotFoundError('Note ID ({}) not recognized'.format(note_id), custom_error.NOTE_NOT_FOUND)

        # Note must belong to the candidate
        if note.candidate_id != candidate_id:
            raise ForbiddenError('Note (id = {}) does not belong to candidate (id = {})'.format(note_id, candidate_id),
                                 custom_error.NOTE_FORBIDDEN)

        return {
            'candidate_note': {
                'id': note_id,
                'candidate_id': candidate_id,
                'owner_user_id': note.owner_user_id,
                'title': note.title,
                'comment': note.comment,
                'added_time': DatetimeUtils.to_utc_str(note.added_time) if note.added_time else None
            }
        }

    # return all of candidate's notes
    else:
        candidate_notes = []
        for note in CandidateTextComment.get_by_candidate_id(candidate_id):
            candidate_notes.append({
                'id': note.id,
                'candidate_id': candidate_id,
                'owner_user_id': note.owner_user_id,
                'title': note.title,
                'comment': note.comment,
                'added_time': DatetimeUtils.to_utc_str(note.added_time) if note.added_time else None
            })

        return {'candidate_notes': candidate_notes}
Beispiel #8
0
def delete_tag(candidate_id, tag_id):
    """
    Function will delete candidate's tag
    :type candidate_id: int | long
    :type tag_id:       int | long
    :rtype  dict
    """
    candidate_tag_object = CandidateTag.get_one(candidate_id, tag_id)
    if not candidate_tag_object:
        raise NotFoundError(
            'Candidate (id = {}) is not associated with tag: {}'.format(
                candidate_id, tag_id), custom_error.INVALID_USAGE)

    db.session.delete(candidate_tag_object)
    db.session.commit()

    return {'id': tag_id}
Beispiel #9
0
def update_candidate_tags(candidate_id, tags):
    """
    Function will update candidate's tag(s)
    :type candidate_id:  int | long
    :type tags:          list[dict]
    :rtype: list[int|long]
    """
    created_tag_ids, updated_tag_ids = [], []
    for tag in tags:
        tag_name, tag_id = tag['name'], tag.get('id')
        tag_object = Tag.get_by_name(tag_name)

        # If Tag is not found, create it
        if not tag_object:
            tag_object = Tag(name=tag_name)
            db.session.add(tag_object)
            db.session.flush()

        # Data for updating candidate's tag
        update_dict = dict(candidate_id=candidate_id, tag_id=tag_object.id)

        if not tag_id:
            raise InvalidUsage('Tag ID is required for updating',
                               custom_error.INVALID_USAGE)

        tag_obj_from_id = Tag.get(tag_id)
        if not tag_obj_from_id:
            raise NotFoundError("Tag ID: {} is not recognized".format(tag_id),
                                custom_error.TAG_NOT_FOUND)

        # Candidate must already be associated with provided tag_id
        candidate_tag_query = CandidateTag.query.filter_by(
            candidate_id=candidate_id, tag_id=tag_id)
        if not candidate_tag_query.first():
            raise InvalidUsage(
                'Candidate (id = {}) is not associated with Tag (id = {})'.
                format(candidate_id, tag_id), custom_error.INVALID_USAGE)
        # Update
        candidate_tag_query.update(update_dict)
        updated_tag_ids.append(tag_object.id)

    db.session.commit()
    return updated_tag_ids
Beispiel #10
0
def delete_note(candidate_id, note_id):
    """
    Function will delete candidate note.
    Note ID must be recognized & must belong to candidate
    :type candidate_id:  int | long
    :type note_id:  int | long
    """
    note = CandidateTextComment.get(note_id)

    # Note ID must be recognized
    if not note:
        raise NotFoundError('Note ID ({}) not recognized'.format(note_id), custom_error.NOTE_NOT_FOUND)

    # Note must belong to the candidate
    if note.candidate_id != candidate_id:
        raise ForbiddenError('Note (id = {}) does not belong to candidate (id = {})'.format(note_id, candidate_id),
                             custom_error.NOTE_FORBIDDEN)

    db.session.delete(note)
    db.session.commit()
    return note_id
Beispiel #11
0
    def get(self, **kwargs):
        """
        Endpoints:
             i. GET /v1/candidates/:candidate_id/references
            ii. GET /v1/candidates/:candidate_id/references/:id
        """
        # Get authenticated user, candidate ID, and reference ID
        authed_user, candidate_id, reference_id = request.user, kwargs[
            'candidate_id'], kwargs.get('id')

        # Check if candidate exists & is web-hidden
        candidate = get_candidate_if_validated(authed_user, candidate_id)

        if reference_id:
            # Reference ID must be recognized
            reference = CandidateReference.get(reference_id)
            if not reference:
                raise NotFoundError(
                    "Reference ID ({}) not recognized.".format(reference_id),
                    custom_error.REFERENCE_NOT_FOUND)

            # Reference must belong to candidate
            if reference.candidate_id != candidate_id:
                raise ForbiddenError(
                    "Reference (id={}) does not belong to candidate (id={})".
                    format(reference_id,
                           candidate_id), custom_error.REFERENCE_FORBIDDEN)

            return dict(candidate_reference=dict(
                id=reference_id,
                name=reference.person_name,
                position_title=reference.position_title,
                comments=reference.comments,
                reference_email=get_reference_emails(reference_id),
                reference_phone=get_reference_phones(reference_id),
                reference_web_address=get_reference_web_addresses(
                    reference_id)))

        return {'candidate_references': get_references(candidate)}
Beispiel #12
0
    def delete(self, **kwargs):
        """
        Endpoints:
             i. DELETE /v1/candidates/:candidate_id/references
            ii. DELETE /v1/candidates/:candidate_id/references/:id
        :return
            {'candidate_reference': {'id': int}}                        If a single reference was deleted, OR
            {'candidate_references': [{'id': int}, {'id': int}, ...]}   If all references were deleted
            status code: 200
        """
        # Get authenticated user, candidate ID, and reference ID
        authed_user, candidate_id, reference_id = request.user, kwargs[
            'candidate_id'], kwargs.get('id')

        # Check if candidate exists & is web-hidden
        candidate = get_candidate_if_validated(authed_user, candidate_id)

        if reference_id:  # Delete specified reference
            candidate_reference = CandidateReference.get_by_id(reference_id)
            if not candidate_reference:  # Reference must be recognized
                raise NotFoundError(
                    "Candidate reference ({}) not found.".format(reference_id),
                    custom_error.REFERENCE_NOT_FOUND)

            if candidate_reference.candidate_id != candidate_id:  # reference must belong to candidate
                raise ForbiddenError("Not authorized",
                                     custom_error.REFERENCE_FORBIDDEN)

            # Delete candidate reference and return its ID
            return {
                'candidate_reference': delete_reference(candidate_reference)
            }

        else:  # Delete all of candidate's references
            return {
                'candidate_references':
                delete_all_references(candidate.references)
            }
Beispiel #13
0
    def post(self, **kwargs):
        """
        Endpoints:  POST /v1/candidates/:candidate_id/custom_fields
        Usage:
            >>> headers = {"Authorization": "Bearer access_token", "content-type": "application/json"}
            >>> data = {"candidate_custom_fields": [{"custom_field_id": 547, "value": "scripted"}]}
            >>> requests.post(url="hots/v1/candidates/4/custom_fields", headers=headers, data=json.dumps(data))
            <Response [201]>
        :return  {'candidate_custom_fields': [{'id': int}, {'id': int}, ...]}
        """
        # Validate data
        body_dict = get_json_data_if_validated(request, ccf_schema)

        # Get authenticated user and candidate ID
        authed_user, candidate_id = request.user, kwargs['candidate_id']

        # Candidate must exists and must belong to user's domain
        candidate = get_candidate_if_validated(authed_user, candidate_id)

        created_candidate_custom_field_ids = [
        ]  # aggregate created CandidateCustomField IDs
        candidate_custom_fields = body_dict['candidate_custom_fields']

        for candidate_custom_field in candidate_custom_fields:

            # Custom field value(s) must not be empty
            values = filter(None, [value.strip() for value in (candidate_custom_field.get('values') or []) if value]) \
                     or [candidate_custom_field['value'].strip()]
            if not values:
                raise InvalidUsage("Custom field value must be provided.",
                                   custom_error.INVALID_USAGE)

            # Custom Field must be recognized
            custom_field_id = candidate_custom_field['custom_field_id']
            custom_field = CustomField.get_by_id(custom_field_id)
            if not custom_field:
                raise NotFoundError(
                    "Custom field ID ({}) not recognized".format(
                        custom_field_id), custom_error.CUSTOM_FIELD_NOT_FOUND)

            # Custom Field must belong to user's domain
            if custom_field.domain_id != candidate.user.domain_id:
                raise ForbiddenError(
                    "Custom field ID ({}) does not belong to user ({})".format(
                        custom_field_id, authed_user.id),
                    custom_error.CUSTOM_FIELD_FORBIDDEN)

            custom_field_dict = dict(values=values,
                                     custom_field_id=custom_field_id)

            for value in custom_field_dict.get('values'):

                custom_field_id = candidate_custom_field.get('custom_field_id')

                # Prevent duplicate insertions
                if not does_candidate_cf_exist(candidate, custom_field_id,
                                               value):

                    added_time = datetime.datetime.utcnow()

                    candidate_custom_field = CandidateCustomField(
                        candidate_id=candidate_id,
                        custom_field_id=custom_field_id,
                        value=value,
                        added_time=added_time)
                    db.session.add(candidate_custom_field)
                    db.session.flush()

                    created_candidate_custom_field_ids.append(
                        candidate_custom_field.id)

        db.session.commit()
        upload_candidate_documents.delay([candidate_id])
        return {
            'candidate_custom_fields': [{
                'id': custom_field_id
            } for custom_field_id in created_candidate_custom_field_ids]
        }, http_status_codes.CREATED
Beispiel #14
0
    def delete(self, **kwargs):
        """
        Endpoints:
             i. DELETE /v1/candidates/:candidate_id/custom_fields
            ii. DELETE /v1/candidates/:candidate_id/custom_fields/:id
        Depending on the endpoint requested, function will delete all of Candidate's
        custom fields or just a single one.
        """
        # Get authenticated user, candidate_id, and can_cf_id (CandidateCustomField.id)
        authed_user, candidate_id, can_cf_id = request.user, kwargs[
            'candidate_id'], kwargs.get('id')

        # Candidate must exists and must belong to user's domain
        get_candidate_if_validated(authed_user, candidate_id)

        if can_cf_id:  # Delete specified custom field
            candidate_custom_field = CandidateCustomField.get_by_id(can_cf_id)
            if not candidate_custom_field:
                raise NotFoundError(
                    'Candidate custom field not found: {}'.format(can_cf_id),
                    custom_error.CUSTOM_FIELD_NOT_FOUND)

            # Custom fields must belong to user's domain
            custom_field_id = candidate_custom_field.custom_field_id
            if not is_custom_field_authorized(authed_user.domain_id,
                                              [custom_field_id]):
                raise ForbiddenError('Not authorized',
                                     custom_error.CUSTOM_FIELD_FORBIDDEN)

            # Custom Field must belong to candidate
            if candidate_custom_field.candidate_id != candidate_id:
                raise ForbiddenError(
                    "Candidate custom field ({}) does not belong to candidate ({})"
                    .format(can_cf_id,
                            candidate_id), custom_error.CUSTOM_FIELD_FORBIDDEN)

            db.session.delete(candidate_custom_field)

            # Track change
            db.session.add(
                CandidateEdit(
                    user_id=authed_user.id,
                    candidate_id=candidate_id,
                    field_id=CandidateEdit.field_dict['candidate_custom_field']
                    ['value'],
                    old_value=candidate_custom_field.value,
                    new_value=None,
                    edit_datetime=datetime.datetime.utcnow(),
                    is_custom_field=True))

        else:  # Delete all of Candidate's custom fields
            for ccf in CandidateCustomField.get_candidate_custom_fields(
                    candidate_id):  # type: CandidateCustomField
                db.session.delete(ccf)

                # Track change
                db.session.add(
                    CandidateEdit(
                        user_id=authed_user.id,
                        candidate_id=candidate_id,
                        field_id=CandidateEdit.
                        field_dict['candidate_custom_field']['value'],
                        old_value=ccf.value,
                        new_value=None,
                        edit_datetime=datetime.datetime.utcnow(),
                        is_custom_field=True))

        db.session.commit()

        # Update cloud search
        upload_candidate_documents.delay([candidate_id])
        return '', 204
Beispiel #15
0
    def get(self, **kwargs):
        """
        Endpoints:
             i. GET /v1/candidates/:candidate_id/custom_fields
            ii. GET /v1/candidates/:candidate_id/custom_fields/:id
        Depending on the endpoint requested, function will return all of Candidate's
        custom fields or just a single one.
        """
        # Get authenticated user, candidate_id, and can_cf_id
        authed_user, candidate_id, can_cf_id = request.user, kwargs[
            'candidate_id'], kwargs.get('id')

        # Candidate must exists and must belong to user's domain
        get_candidate_if_validated(authed_user, candidate_id)

        if can_cf_id:  # Retrieve specified custom field
            candidate_custom_field = CandidateCustomField.get_by_id(can_cf_id)
            if not candidate_custom_field:
                raise NotFoundError(
                    'Candidate custom field not found: {}'.format(can_cf_id),
                    custom_error.CUSTOM_FIELD_NOT_FOUND)

            # Custom field must belong to user's domain
            custom_field_id = candidate_custom_field.custom_field_id
            if not is_custom_field_authorized(authed_user.domain_id,
                                              [custom_field_id]):
                raise ForbiddenError('Not authorized',
                                     custom_error.CUSTOM_FIELD_FORBIDDEN)

            # Custom Field must belong to candidate
            if candidate_custom_field.candidate_id != candidate_id:
                raise ForbiddenError(
                    "Candidate custom field ({}) does not belong to candidate ({})"
                    .format(can_cf_id,
                            candidate_id), custom_error.CUSTOM_FIELD_FORBIDDEN)

            return {
                'candidate_custom_field': {
                    'id':
                    can_cf_id,
                    'custom_field_id':
                    custom_field_id,
                    'value':
                    candidate_custom_field.value,
                    'created_at_datetime':
                    candidate_custom_field.added_time.isoformat()
                }
            }

        else:
            # Custom fields must belong user's domain
            return {
                'candidate_custom_fields': [{
                    'id':
                    ccf.id,
                    'custom_field_id':
                    ccf.custom_field_id,
                    'value':
                    ccf.value,
                    'created_at_datetime':
                    ccf.added_time.isoformat()
                } for ccf in CandidateCustomField.get_candidate_custom_fields(
                    candidate_id)]
            }