예제 #1
0
    def get(self, **kwargs):
        """
        GET /talent-pools/<id>/talent-pipelines  Fetch all talent-pipelines of a Talent-Pool

        :return A dictionary containing all talent-pipelines of a Talent Pool
        :rtype: dict
        """
        talent_pool_id = kwargs.get('id')
        talent_pool = TalentPool.query.get(talent_pool_id)

        if not talent_pool:
            raise NotFoundError(
                "Talent pool with id {} doesn't exist in database".format(
                    talent_pool_id))

        if request.user.role.name != 'TALENT_ADMIN' and talent_pool.domain_id != request.user.domain_id:
            raise ForbiddenError(
                "Talent pool and logged in user belong to different domains")

        talent_pool_group = TalentPoolGroup.query.filter_by(
            user_group_id=request.user.user_group_id,
            talent_pool_id=talent_pool_id).first()
        if request.user.role.name == 'USER' and not talent_pool_group:
            raise ForbiddenError(
                "User {} doesn't have appropriate permissions to "
                "get talent-pipelines".format(request.user.id))

        page = request.args.get('page', DEFAULT_PAGE)
        per_page = request.args.get('per_page', DEFAULT_PAGE_SIZE)

        if not is_number(page) or not is_number(
                per_page) or int(page) < 1 or int(per_page) < 1:
            raise InvalidUsage("page and per_page should be positive integers")

        page = int(page)
        per_page = int(per_page)

        talent_pipelines_query = TalentPipeline.query.filter_by(
            talent_pool_id=talent_pool_id)
        talent_pipelines = talent_pipelines_query.paginate(
            page, per_page, False)
        talent_pipelines = talent_pipelines.items

        headers = generate_pagination_headers(talent_pipelines_query.count(),
                                              per_page, page)

        response = {
            'talent_pipelines': [
                talent_pipeline.to_dict(True, get_stats_generic_function)
                for talent_pipeline in talent_pipelines
            ]
        }

        return ApiResponse(response=response, headers=headers, status=200)
예제 #2
0
    def delete(self, **kwargs):
        """
        DELETE /groups/<group_id>/talent_pools   Remove given input of talent-pool ids from user group
        input: {'talent_pools': [talent_pool_id1, talent_pool_id2, talent_pool_id3, ... ]}

        :return A dictionary containing talent-pool ids which have been removed successfully
        :rtype: dict
        """
        user_group_id = kwargs.get('group_id')

        posted_data = request.get_json(silent=True)
        if not posted_data or 'talent_pools' not in posted_data:
            raise InvalidUsage("Request body is empty or not provided")

        # Save user object(s)
        talent_pool_ids = posted_data['talent_pools']

        # Talent_pool object(s) must be in a list
        if not isinstance(talent_pool_ids, list):
            raise InvalidUsage("Request body is not properly formatted")

        user_group = UserGroup.query.get(user_group_id)

        if not user_group:
            raise NotFoundError(
                "User group with id {} doesn't exist".format(user_group_id))

        if request.user.role.name != 'TALENT_ADMIN' and request.user.domain_id != user_group.domain_id:
            raise ForbiddenError(
                "Logged-in user and given user-group belong to different domains"
            )

        for talent_pool_id in talent_pool_ids:

            if not is_number(talent_pool_id):
                raise InvalidUsage(
                    'Talent pool id {} should be an integer'.format(
                        talent_pool_id))
            else:
                talent_pool_id = int(talent_pool_id)

            talent_pool_group = TalentPoolGroup.query.filter_by(
                user_group_id=user_group_id,
                talent_pool_id=talent_pool_id).first()
            if not talent_pool_group:
                raise NotFoundError(
                    "Talent pool {} doesn't belong to group {}".format(
                        talent_pool_id, user_group_id))
            else:
                db.session.delete(talent_pool_group)

        db.session.commit()

        return {
            'talent_pools':
            [int(talent_pool_id) for talent_pool_id in talent_pool_ids]
        }
예제 #3
0
    def patch(self, **kwargs):
        """
        PATCH list with search params or with list of candidate ids
        Input data:
            json body having following keys
            "name": Name with which smart list will be created
            "search_params": search parameters for smart list in dictionary format
                or  "candidate_ids": if not search_params then candidate_ids should be present
        :return: smartlist id
        """
        auth_user = request.user

        list_id = kwargs.get('id')
        data = request.get_json(silent=True)

        if not data:
            raise InvalidUsage("Received empty request body")

        if not list_id or not is_number(list_id):
            raise InvalidUsage(
                "Either List ID is not provided or It's not an integer")

        smart_list = Smartlist.query.get(list_id)

        if not smart_list:
            raise InvalidUsage("No SmartList exists with id: %s" % list_id)

        # request data must pass through this function, as this will create data in desired format
        data = validate_and_format_smartlist_patch_data(data, auth_user)

        if data.get('name'):
            smart_list.name = data.get('name')

        if data.get('talent_pipeline_id'):
            smart_list.talent_pipeline_id = data.get('talent_pipeline_id')

        if data.get('search_params'):
            smart_list.search_params = data.get('search_params')
        elif data.get('remove_candidate_ids'):
            SmartlistCandidate.query.filter(
                SmartlistCandidate.smartlist_id == smart_list.id,
                SmartlistCandidate.candidate_id.in_(
                    data.get('remove_candidate_ids'))).delete()
        elif data.get('add_candidate_ids'):
            for candidate_id in data.get('add_candidate_ids'):
                row = SmartlistCandidate(smartlist_id=smart_list.id,
                                         candidate_id=candidate_id)
                db.session.add(row)

        db.session.commit()

        candidate_ids = data.get('remove_candidate_ids', []) + data.get(
            'add_candidate_ids', [])
        update_candidates_on_cloudsearch(request.oauth_token, candidate_ids)

        return {'smartlist': {'id': smart_list.id}}, 201
예제 #4
0
    def get(self, **kwargs):
        """
        GET /talent-pipeline/<id>/smart_lists   Fetch all smartlists of a talent_pipeline

        :return A dictionary containing smartlist objects of a talent_pipeline

        :rtype: dict
        """

        talent_pipeline_id = kwargs.get('id')

        talent_pipeline = TalentPipeline.query.get(talent_pipeline_id)

        if not talent_pipeline:
            raise NotFoundError("Talent pipeline with id {} doesn't exist in database".format(talent_pipeline_id))

        if request.user.role.name != 'TALENT_ADMIN' and talent_pipeline.user.domain_id != request.user.domain_id:
            raise ForbiddenError("Logged-in user and talent_pipeline belong to different domain")

        page = request.args.get('page', DEFAULT_PAGE)
        per_page = request.args.get('per_page', DEFAULT_PAGE_SIZE)

        if not is_number(page) or not is_number(per_page) or int(page) < 1 or int(per_page) < 1:
            raise InvalidUsage("page and per_page should be positive integers")

        page = int(page)
        per_page = int(per_page)

        total_number_of_smartlists = Smartlist.query.filter_by(talent_pipeline_id=talent_pipeline_id).count()
        smartlists = Smartlist.query.filter_by(talent_pipeline_id=talent_pipeline_id).order_by(
            Smartlist.added_time.desc()).paginate(page, per_page, False)

        smartlists = smartlists.items

        headers = generate_pagination_headers(total_number_of_smartlists, per_page, page)

        response = {
            'page_number': page, 'smartlists_per_page': per_page,
            'total_number_of_smartlists': total_number_of_smartlists,
            'smartlists': [smartlist.to_dict() for smartlist in smartlists]
        }

        return ApiResponse(response=response, headers=headers, status=200)
예제 #5
0
    def delete(self, **kwargs):
        """
        DELETE /talent-pipeline/<id>/smartlists   Remove smartlists from a talent_pipeline

        Take a JSON dictionary containing smartlist_ids

        :return A dictionary containing smartlist_ids successfully removed from a talent_pipeline

        :rtype: dict
        """

        talent_pipeline_id = kwargs.get('id')

        talent_pipeline = TalentPipeline.query.get(talent_pipeline_id)

        if not talent_pipeline:
            raise NotFoundError("Talent pipeline with id {} doesn't exist in database".format(talent_pipeline_id))

        posted_data = request.get_json(silent=True)
        if not posted_data or 'smartlist_ids' not in posted_data:
            raise InvalidUsage("Request body is empty or not provided")

        # Save user object(s)
        smartlist_ids = posted_data['smartlist_ids']

        # Talent_pool object(s) must be in a list
        if not isinstance(smartlist_ids, list):
            raise InvalidUsage("Request body is not properly formatted")

        if request.user.role.name == 'USER' and talent_pipeline.user.id != request.user.id:
            raise ForbiddenError("Logged-in user doesn't have appropriate permissions to edit this talent-pipeline")

        if request.user.role.name != 'TALENT_ADMIN' and talent_pipeline.user.domain_id != request.user.domain_id:
            raise ForbiddenError("Logged-in user and talent_pipeline belong to different domain")

        for smartlist_id in smartlist_ids:

            if not is_number(smartlist_id):
                raise InvalidUsage('Smart List id {} should be an integer'.format(smartlist_id))
            else:
                smartlist_id = int(smartlist_id)

            smartlist = Smartlist.query.get(smartlist_id)

            if smartlist.talent_pipeline_id != talent_pipeline_id:
                raise ForbiddenError("smartlist {} doesn't belong to talent_pipeline {}".format(
                    smartlist.name, talent_pipeline_id))

            smartlist.talent_pipeline_id = None

        db.session.commit()

        return {
            'smartlist_ids': [int(smartlist_id) for smartlist_id in smartlist_ids]
        }
예제 #6
0
    def get(self, **kwargs):
        """
        GET /talent-pipelines/<id>/campaigns?fields=id,subject&page=1&per_page=20

        Fetch all campaigns of a talent-pipeline.

        :return A dictionary containing list of campaigns belonging to a talent-pipeline
        :rtype: dict
        """

        # Valid talent pipeline
        talent_pipeline_id = kwargs.get('id')
        talent_pipeline = TalentPipeline.query.get(talent_pipeline_id)
        if not talent_pipeline:
            raise NotFoundError("Talent pipeline with id {} doesn't exist in database".format(talent_pipeline_id))

        if request.user.role.name != 'TALENT_ADMIN' and talent_pipeline.user.domain_id != request.user.domain_id:
            raise ForbiddenError("Logged-in user and talent_pipeline belong to different domain")

        page = request.args.get('page', DEFAULT_PAGE)
        per_page = request.args.get('per_page', 20)

        if not is_number(page) or not is_number(per_page) or int(page) < 1 or int(per_page) < 1:
            raise InvalidUsage("page and per_page should be positive integers")

        page = int(page)
        per_page = int(per_page)

        # Get the email campaigns
        include_fields = request.values['fields'].split(',') if request.values.get('fields') else None
        email_campaigns = talent_pipeline.get_email_campaigns(page=page, per_page=per_page)

        headers = generate_pagination_headers(talent_pipeline.get_email_campaigns_count(), per_page, page)

        response = {
            'page_number': page, 'email_campaigns_per_page': per_page,
            'total_number_of_email_campaigns': talent_pipeline.get_email_campaigns_count(),
            'email_campaigns': [email_campaign.to_dict(include_fields) for email_campaign in email_campaigns]
        }

        return ApiResponse(response=response, headers=headers, status=200)
예제 #7
0
    def get(self, **kwargs):
        """
        GET /talent-pipelines/<id>/candidates/engagement?limit=5  Fetch candidates of a talent-pipeline
        :return A dictionary containing list of most engaged candidates belonging to a talent-pipeline

        :rtype: dict
        """

        talent_pipeline_id = kwargs.get('id')
        limit = request.args.get('limit', 5)

        if not is_number(limit) or int(limit) < 1:
            raise InvalidUsage("Limit should be a positive integer")

        talent_pipeline = TalentPipeline.query.get(talent_pipeline_id)

        if not talent_pipeline:
            raise NotFoundError("Talent pipeline with id {} doesn't exist in database".format(talent_pipeline_id))

        if request.user.role.name != 'TALENT_ADMIN' and talent_pipeline.user.domain_id != request.user.domain_id:
            raise ForbiddenError("Logged-in user and talent_pipeline belong to different domain")

        return {'candidates': top_most_engaged_candidates_of_pipeline(talent_pipeline_id, int(limit))}
예제 #8
0
    def get(self, **kwargs):
        """
        GET /candidates/<candidate_id>/talent-pipelines?limit=5  Fetch Engagement score of a
        candidate in each pipeline
        :return A dictionary containing list of most engaged candidates belonging to a talent-pipeline

        :rtype: dict
        """

        candidate_id = kwargs.get('id')
        limit = request.args.get('limit', 5)

        if not is_number(limit) or int(limit) < 1:
            raise InvalidUsage("Limit should be a positive integer")

        candidate = Candidate.query.get(candidate_id)

        if not candidate:
            raise NotFoundError(error_message="Candidate with id {} doesn't exist in database".format(candidate_id))

        if request.user.role.name != 'TALENT_ADMIN' and candidate.user.domain_id != request.user.domain_id:
            raise ForbiddenError("Logged-in user and candidate belong to different domain")

        return {'talent_pipelines': top_most_engaged_pipelines_of_candidate(candidate_id, int(limit))}
예제 #9
0
def get_stats_generic_function(container_object,
                               container_name,
                               user=None,
                               from_date_string='',
                               to_date_string='',
                               interval=1,
                               is_update=False,
                               offset=0):
    """
    This method will be used to get stats for talent-pools, talent-pipelines or smartlists.
    :param container_object: TalentPipeline, TalentPool or SmartList Object
    :param container_name:
    :param user: Logged-in user
    :param from_date_string: From Date String (In Client's Local TimeZone)
    :param to_date_string: To Date String (In Client's Local TimeZone)
    :param interval: Interval in days
    :param is_update: Either stats update process is in progress
    :param offset: Timezone offset from utc i.e. if client's lagging 4 hours behind utc, offset value should be -4
    :return:
    """
    if not container_object:
        raise NotFoundError("%s doesn't exist in database" % container_name)

    if user and container_object.user.domain_id != user.domain_id:
        raise ForbiddenError(
            "Logged-in user %s is unauthorized to get stats of %s: %s" %
            (user.id, container_name, container_object.id))

    if not is_number(offset) or math.ceil(abs(float(offset))) > 12:
        raise InvalidUsage(
            "Value of offset should be an integer and less than or equal to 12"
        )

    offset = int(math.ceil(float(offset)))

    current_date_time = datetime.utcnow()

    ninety_days_old_date_time = current_date_time - timedelta(days=90)

    try:
        # To convert UTC time to any other time zone we can use `offset_date_time` with inverted value of offset
        from_date = parse(from_date_string).replace(tzinfo=None) if from_date_string \
            else offset_date_time(ninety_days_old_date_time, -1 * offset)
        to_date = parse(to_date_string).replace(tzinfo=None) if to_date_string else \
            offset_date_time(current_date_time, -1 * offset)
    except Exception as e:
        raise InvalidUsage(
            "Either 'from_date' or 'to_date' is invalid because: %s" %
            e.message)

    if offset_date_time(from_date, offset) < container_object.added_time:
        from_date = offset_date_time(container_object.added_time, -1 * offset)

    if offset_date_time(from_date, offset) < ninety_days_old_date_time:
        raise InvalidUsage(
            "`Stats data older than 90 days cannot be retrieved`")

    if from_date > to_date:
        raise InvalidUsage("`to_date` cannot come before `from_date`")

    if offset_date_time(to_date, offset) > current_date_time:
        raise InvalidUsage("`to_date` cannot be in future")

    if not is_number(interval):
        raise InvalidUsage("Interval '%s' should be integer" % interval)

    interval = int(interval)
    if interval < 1:
        raise InvalidUsage(
            "Interval's value should be greater than or equal to 1 day")

    if container_name == 'TalentPipeline':
        get_stats_for_given_day = get_talent_pipeline_stat_for_given_day
    elif container_name == 'TalentPool':
        get_stats_for_given_day = get_talent_pool_stat_for_a_given_day
    elif container_name == 'SmartList':
        get_stats_for_given_day = get_smartlist_stat_for_a_given_day
    else:
        raise Exception("Container %s is not supported for this method" %
                        container_name)

    list_of_stats_dicts = []

    to_date = to_date.replace(hour=23, minute=59, second=59)
    from_date = from_date.replace(hour=23, minute=59, second=59)

    if is_update:
        to_date -= timedelta(days=interval)

    while to_date.date() >= from_date.date():
        list_of_stats_dicts.append({
            'total_number_of_candidates':
            get_stats_for_given_day(container_object,
                                    offset_date_time(to_date, offset)),
            'added_datetime':
            to_date.date().isoformat(),
        })
        to_date -= timedelta(days=interval)

    reference_stat = get_stats_for_given_day(container_object,
                                             offset_date_time(to_date, offset))
    for index, stat_dict in enumerate(list_of_stats_dicts):
        stat_dict['number_of_candidates_added'] = stat_dict[
            'total_number_of_candidates'] - (
                list_of_stats_dicts[index + 1]['total_number_of_candidates']
                if index + 1 < len(list_of_stats_dicts) else reference_stat)

    return list_of_stats_dicts
예제 #10
0
    def get(self, **kwargs):
        """Retrieve list information
        List must belong to auth user's domain
        Call this resource from url:
            /v1/smartlists?page=1&page_size=10 :: to retrieve all the smartlists in user's domain
            /v1/smartlists/<int:id> :: to get single smartlist

        example: http://localhost:8008/v1/smartlists/2
        Returns: List in following json format
            {
              "smartlist": {
                "total_found": 3,
                "user_id": 1,
                "id": 1,
                "name": "my list"
                "search_params": {"location": "San Jose, CA"}
              }
            }
        """
        list_id = kwargs.get('id')
        candidate_count = request.args.get('candidate-count', False)

        if not is_number(candidate_count) or int(candidate_count) not in (
                True, False):
            raise InvalidUsage("`candidate_count` field value can be 0 or 1")

        auth_user = request.user
        if list_id:
            smartlist = Smartlist.query.get(list_id)
            if not smartlist or smartlist.is_hidden:
                raise NotFoundError("List id does not exists")
            # check whether smartlist belongs to user's domain
            if request.user.role.name != 'TALENT_ADMIN' and smartlist.user.domain_id != auth_user.domain_id:
                raise ForbiddenError("List does not belong to user's domain")
            return {
                'smartlist':
                create_smartlist_dict(smartlist, request.oauth_token,
                                      int(candidate_count))
            }
        else:
            # Return all smartlists from user's domain
            page = request.args.get('page', DEFAULT_PAGE)
            per_page = request.args.get('per_page', DEFAULT_PAGE_SIZE)
            total_number_of_smartlists = Smartlist.query.join(
                Smartlist.user).filter(User.domain_id == auth_user.domain_id,
                                       Smartlist.is_hidden == 0).count()

            if not is_number(page) or not is_number(
                    per_page) or int(page) < 1 or int(per_page) < 1:
                raise InvalidUsage(
                    "page and per_page should be positive integers")

            page = int(page)
            per_page = int(per_page)

            headers = generate_pagination_headers(total_number_of_smartlists,
                                                  per_page, page)

            response = {
                'smartlists':
                get_all_smartlists(auth_user, request.oauth_token, int(page),
                                   int(per_page), candidate_count),
                'page_number':
                page,
                'smartlists_per_page':
                per_page,
                'total_number_of_smartlists':
                total_number_of_smartlists
            }
            return ApiResponse(response=response, headers=headers, status=200)
예제 #11
0
    def get(self, **kwargs):
        """
        GET /talent-pipelines/<id>      Fetch talent-pipeline object
        GET /talent-pipelines           Fetch all talent-pipelines objects of domain of logged-in user

        :return A dictionary containing talent-pipeline's basic info or a dictionary containing all talent-pipelines of
        a domain

        :rtype: dict
        """
        talent_pipeline_id = kwargs.get('id')
        interval_in_days = request.args.get('interval', 30)
        candidate_count = request.args.get('candidate-count', False)
        email_campaign_count = request.args.get('email-campaign-count', False)

        if not is_number(candidate_count) or int(candidate_count) not in (True, False):
            raise InvalidUsage("`candidate_count` field value can be 0 or 1")

        if not is_number(email_campaign_count) or int(email_campaign_count) not in (True, False):
            raise InvalidUsage("`email_campaign_count` field value can be 0 or 1")

        if not is_number(interval_in_days) or int(interval_in_days) < 0:
            raise InvalidUsage("Value of interval should be positive integer")

        if talent_pipeline_id:
            talent_pipeline = TalentPipeline.query.get(talent_pipeline_id)

            if not talent_pipeline:
                raise NotFoundError("Talent pipeline with id {} doesn't exist in database".format(talent_pipeline_id))

            if request.user.role.name != 'TALENT_ADMIN' and talent_pipeline.user.domain_id != request.user.domain_id:
                raise ForbiddenError("Logged-in user and talent_pipeline belong to different domain")

            if not candidate_count:
                talent_pipeline_dict = talent_pipeline.to_dict(email_campaign_count=email_campaign_count)
            else:
                talent_pipeline_dict = talent_pipeline.to_dict(email_campaign_count=email_campaign_count,
                                                               include_candidate_count=True,
                                                               get_candidate_count=get_talent_pipeline_stat_for_given_day)

            talent_pipeline_dict.update({'engagement_score': get_pipeline_engagement_score(talent_pipeline_id)})
            return {'talent_pipeline': talent_pipeline_dict}

        else:
            sort_by = request.args.get('sort_by', 'added_time')
            sort_type = request.args.get('sort_type', 'DESC')
            search_keyword = request.args.get('search', '').strip()
            owner_user_id = request.args.get('user_id', '')
            is_hidden = request.args.get('is_hidden', 0)
            from_date = request.args.get('from_date', EPOCH_TIME_STRING)
            to_date = request.args.get('to_date', datetime.utcnow().isoformat())
            page = request.args.get('page', DEFAULT_PAGE)
            per_page = request.args.get('per_page', DEFAULT_PAGE_SIZE)

            if not is_number(is_hidden) or int(is_hidden) not in (0, 1):
                raise InvalidUsage('`is_hidden` can be either 0 or 1')

            if not is_number(page) or not is_number(per_page) or int(page) < 1 or int(per_page) < 1:
                raise InvalidUsage("page and per_page should be positive integers")

            page = int(page)
            per_page = int(per_page)

            if owner_user_id and is_number(owner_user_id) and not User.query.get(int(owner_user_id)):
                raise InvalidUsage("User: (%s) doesn't exist in system")

            if sort_by not in ('added_time', 'name', 'engagement_score', 'candidate_count'):
                raise InvalidUsage('Value of sort parameter is not valid')

            try:
                from_date = parser.parse(from_date).replace(tzinfo=None)
                to_date = parser.parse(to_date).replace(tzinfo=None)
            except Exception as e:
                raise InvalidUsage("from_date or to_date is not properly formatted: %s" % e)

            talent_pipelines_query = TalentPipeline.query.join(User).filter(
                    TalentPipeline.is_hidden == is_hidden, User.domain_id == request.user.domain_id,
                    from_date <= TalentPipeline.added_time, TalentPipeline.added_time <= to_date, or_(
                            TalentPipeline.name.ilike('%' + search_keyword + '%'),
                            TalentPipeline.description.ilike('%' + search_keyword + '%')))
            if owner_user_id:
                talent_pipelines_query = talent_pipelines_query.filter(User.id == int(owner_user_id))

            total_number_of_talent_pipelines = talent_pipelines_query.count()

            if sort_by not in ("engagement_score", "candidate_count"):
                if sort_by == 'added_time':
                    sort_attribute = TalentPipeline.added_time
                else:
                    sort_attribute = TalentPipeline.name
                talent_pipelines = talent_pipelines_query.order_by(
                    sort_attribute.asc() if sort_type == 'ASC' else sort_attribute.desc()).paginate(page, per_page,
                                                                                                    False)
                talent_pipelines = talent_pipelines.items
            else:
                talent_pipelines = talent_pipelines_query.all()

            if candidate_count or sort_by == "candidate_count":
                talent_pipelines_data = [
                    talent_pipeline.to_dict(include_cached_candidate_count=True,
                                            email_campaign_count=email_campaign_count,
                                            get_candidate_count=get_talent_pipeline_stat_for_given_day)
                    for talent_pipeline in talent_pipelines]
            else:
                talent_pipelines_data = [
                    talent_pipeline.to_dict(email_campaign_count=email_campaign_count)
                    for talent_pipeline in talent_pipelines]

            for talent_pipeline_data in talent_pipelines_data:
                talent_pipeline_data['engagement_score'] = get_pipeline_engagement_score(talent_pipeline_data['id'])

            if sort_by in ("engagement_score", "candidate_count"):
                sort_by = 'total_candidates' if sort_by == "candidate_count" else "engagement_score"
                talent_pipelines_data = sorted(
                        talent_pipelines_data, key=lambda talent_pipeline_data: talent_pipeline_data[sort_by],
                        reverse=(False if sort_type == 'ASC' else True))
                talent_pipelines_data = talent_pipelines_data[(page - 1) * per_page:page * per_page]

            headers = generate_pagination_headers(total_number_of_talent_pipelines, per_page, page)

            response = dict(
                talent_pipelines=talent_pipelines_data,
                page_number=page, talent_pipelines_per_page=per_page,
                total_number_of_talent_pipelines=total_number_of_talent_pipelines
            )

            return ApiResponse(response=response, headers=headers, status=200)
예제 #12
0
    def put(self, **kwargs):
        """
        PUT /talent-pipelines/<id>  Edit existing talent-pipeline

        Take a JSON dictionary containing TalentPipeline dictionary

        :return A dictionary containing id of edited talent-pipeline

        :rtype: dict
        """

        talent_pipeline_id = kwargs.get('id')

        if not talent_pipeline_id:
            raise InvalidUsage("A valid talent_pipeline_id should be provided")

        talent_pipeline = TalentPipeline.query.get(talent_pipeline_id)
        if not talent_pipeline:
            raise NotFoundError("Talent pipeline with id {} doesn't exist in database".format(talent_pipeline_id))

        if request.user.role.name == 'USER' and talent_pipeline.user.id != request.user.id:
            raise ForbiddenError("Logged-in user doesn't have appropriate permissions to edit this talent-pipeline")

        if request.user.role.name != 'TALENT_ADMIN' and talent_pipeline.user.domain_id != request.user.domain_id:
            raise ForbiddenError("Logged-in user and talent_pipeline belong to different domain")

        posted_data = request.get_json(silent=True)
        if not posted_data or 'talent_pipeline' not in posted_data:
            raise InvalidUsage("Request body is empty or not provided")
        posted_data = posted_data['talent_pipeline']

        name = posted_data.get('name')
        description = posted_data.get('description', '')
        positions = posted_data.get('positions', '')
        date_needed = posted_data.get('date_needed', '')
        talent_pool_id = posted_data.get('talent_pool_id', '')
        is_hidden = posted_data.get('is_hidden', 0)
        search_params = posted_data.get('search_params', dict())

        if name is not None:
            if name:
                if TalentPipeline.query.join(TalentPipeline.user).filter(
                        and_(TalentPipeline.name == name, User.domain_id == request.user.domain_id)).first():
                    raise InvalidUsage("Talent pipeline with name {} already exists in domain {}".format(
                        name, request.user.domain_id))
                talent_pipeline.name = name
            else:
                raise InvalidUsage("Name cannot be an empty string")

        if date_needed:
            try:
                parser.parse(date_needed)
            except Exception as e:
                raise InvalidUsage("Date_needed is not valid as: {}".format(e.message))

            if parser.parse(date_needed) < datetime.utcnow():
                raise InvalidUsage("Date_needed {} cannot be before current date".format(date_needed))

            talent_pipeline.date_needed = date_needed

        if positions:
            if not is_number(positions) or not int(positions) > 0:
                raise InvalidUsage("Number of positions should be integer and greater than zero")

            talent_pipeline.positions = positions

        if description:
            talent_pipeline.description = description

        if talent_pool_id:

            if not is_number(talent_pool_id):
                raise InvalidUsage("talent_pool_id should be an integer")

            talent_pool_id = int(talent_pool_id)
            talent_pool = TalentPool.query.get(talent_pool_id)
            if not talent_pool:
                raise NotFoundError("Talent pool with id {} doesn't exist in database".format(talent_pool_id))

            if talent_pool.domain_id != request.user.domain_id:
                raise ForbiddenError("Logged-in user and given talent-pool belong to different domain")

            talent_pipeline.talent_pool_id = talent_pool_id

        if search_params:
            if not isinstance(search_params, dict):
                raise InvalidUsage("search_params is not provided in valid format")

            # Put into params dict
            for key in search_params:
                if key not in TALENT_PIPELINE_SEARCH_PARAMS and not key.startswith('cf-'):
                    raise NotFoundError("Key[{}] is invalid".format(key))

            talent_pipeline.search_params = json.dumps(search_params)

        if not is_number(is_hidden) or (int(is_hidden) not in (0, 1)):
            raise InvalidUsage("Possible vaues of `is_hidden` are 0 and 1")

        talent_pipeline.is_hidden = int(is_hidden)
        for smartlist in Smartlist.query.filter(Smartlist.talent_pipeline_id == talent_pipeline_id):
            smartlist.is_hidden = int(is_hidden)

        db.session.commit()

        return {
            'talent_pipeline': {'id': talent_pipeline.id}
        }
예제 #13
0
    def post(self, **kwargs):
        """
        POST /talent-pipelines  Add new talent-pipelines to Database
        input: {'talent_pipelines': [talent_pipeline_dict1, talent_pipeline_dict2, talent_pipeline_dict3, ... ]}

        Take a JSON dictionary containing array of TalentPipeline dictionaries
        A single talent-pipelines dict must contain pipelines's name, date_needed, talent_pool_id

        :return A dictionary containing ids of added talent-pipelines

        :rtype: dict
        """

        posted_data = request.get_json(silent=True)
        if not posted_data or 'talent_pipelines' not in posted_data:
            raise InvalidUsage("Request body is empty or not provided")

        # Save user object(s)
        talent_pipelines = posted_data['talent_pipelines']

        # TalentPipeline object(s) must be in a list
        if not isinstance(talent_pipelines, list):
            raise InvalidUsage("Request body is not properly formatted")

        talent_pipeline_objects = []
        for talent_pipeline in talent_pipelines:

            name = talent_pipeline.get('name', '')
            description = talent_pipeline.get('description', '')
            positions = talent_pipeline.get('positions', 1)
            date_needed = talent_pipeline.get('date_needed', '')
            talent_pool_id = talent_pipeline.get('talent_pool_id', '')
            search_params = talent_pipeline.get('search_params', dict())

            if not name or not date_needed or not talent_pool_id:
                raise InvalidUsage("A valid name, date_needed, talent_pool_id should be provided to "
                                   "create a new talent-pipeline")

            if TalentPipeline.query.join(TalentPipeline.user).filter(and_(
                            TalentPipeline.name == name, User.domain_id == request.user.domain_id)).first():
                raise InvalidUsage(
                    "Talent pipeline with name {} already exists in domain {}".format(name, request.user.domain_id))

            try:
                date_needed = parser.parse(date_needed)
            except Exception as e:
                raise InvalidUsage("Date_needed is not valid as: {}".format(e.message))

            if not is_number(positions) or not int(positions) > 0:
                raise InvalidUsage("Number of positions should be integer and greater than zero")

            if not is_number(talent_pool_id):
                raise InvalidUsage("talent_pool_id should be an integer")

            talent_pool_id = int(talent_pool_id)
            talent_pool = TalentPool.query.get(talent_pool_id)
            if not talent_pool:
                raise NotFoundError("Talent pool with id {} doesn't exist in database".format(talent_pool_id))

            if talent_pool.domain_id != request.user.domain_id:
                raise ForbiddenError("Logged-in user and given talent-pool belong to different domain")

            if search_params:
                if not isinstance(search_params, dict):
                    raise InvalidUsage("search_params is not provided in valid format")

                # Put into params dict
                for key in search_params:
                    if key not in TALENT_PIPELINE_SEARCH_PARAMS and not key.startswith('cf-'):
                        raise NotFoundError("Key[{}] is invalid".format(key))

            search_params = json.dumps(search_params) if search_params else None

            talent_pipeline = TalentPipeline(name=name, description=description, positions=positions,
                                             date_needed=date_needed, user_id=request.user.id,
                                             talent_pool_id=talent_pool_id, search_params=search_params)

            db.session.add(talent_pipeline)
            talent_pipeline_objects.append(talent_pipeline)

        db.session.commit()

        return {
            'talent_pipelines': [talent_pipeline_object.id for talent_pipeline_object in talent_pipeline_objects]
        }
예제 #14
0
    def delete(self, **kwargs):
        """
        DELETE /talent-pools/<id>/candidates   Remove input candidates from talent-pool
        input: {'talent_pool_candidates': [talent_pool_candidate_id1, talent_pool_candidate_id2, ... ]}

        :return A dictionary containing candidate_ids which have been removed successfully
        :rtype: dict
        """

        talent_pool_id = kwargs.get('id')

        posted_data = request.get_json(silent=True)
        if not posted_data or 'talent_pool_candidates' not in posted_data:
            raise InvalidUsage("Request body is empty or not provided")

        # Save user object(s)
        talent_pool_candidate_ids = posted_data['talent_pool_candidates']

        # Talent_pool object(s) must be in a list
        if not isinstance(talent_pool_candidate_ids, list):
            raise InvalidUsage("Request body is not properly formatted")

        talent_pool = TalentPool.query.get(talent_pool_id)

        if not talent_pool:
            raise NotFoundError(
                "Talent pool with id {} doesn't exist in database".format(
                    talent_pool_id))

        if request.user.role.name != 'TALENT_ADMIN' and talent_pool.domain_id != request.user.domain_id:
            raise ForbiddenError(
                "Talent pool and logged in user belong to different domains")

        talent_pool_group = TalentPoolGroup.query.filter_by(
            user_group_id=request.user.user_group_id,
            talent_pool_id=talent_pool_id).first()
        if request.user.role.name == 'USER' and not talent_pool_group:
            raise ForbiddenError(
                "User {} doesn't have appropriate permissions to "
                "remove candidates".format(request.user.id))

        for talent_pool_candidate_id in talent_pool_candidate_ids:
            if not is_number(talent_pool_candidate_id):
                raise InvalidUsage(
                    'Candidate id {} should be an integer'.format(
                        talent_pool_candidate_id))
            else:
                talent_pool_candidate_id = int(talent_pool_candidate_id)

            talent_pool_candidate = TalentPoolCandidate.query.filter_by(
                candidate_id=talent_pool_candidate_id,
                talent_pool_id=talent_pool_id).first()
            if not talent_pool_candidate:
                raise NotFoundError(
                    "Candidate {} doesn't belong to talent-pool {}".format(
                        talent_pool_candidate_id, talent_pool_id))
            else:
                db.session.delete(talent_pool_candidate)

        db.session.commit()

        try:
            # Update Candidate Documents in Amazon Cloud Search
            headers = {
                'Authorization': request.oauth_token,
                'Content-Type': 'application/json'
            }
            response = requests.post(
                CandidateApiUrl.CANDIDATES_DOCUMENTS_URI,
                headers=headers,
                data=json.dumps({'candidate_ids': talent_pool_candidate_ids}))

            if response.status_code != 204:
                raise Exception("Status Code: {} Response: {}".format(
                    response.status_code, response.json()))

        except Exception as e:
            raise InvalidUsage(
                "Couldn't update Candidate Documents in Amazon Cloud Search. Because: {}"
                .format(e.message))

        return {
            'talent_pool_candidates': [
                int(talent_pool_candidate_id)
                for talent_pool_candidate_id in talent_pool_candidate_ids
            ]
        }
예제 #15
0
    def get(self, **kwargs):
        """
        GET /talent-pools/<id>              Fetch talent-pool object
        GET /talent-pools?domain_id=1       Fetch all talent-pool objects of domain of logged-in user

        :return A dictionary containing talent-pool basic info or a dictionary containing all talent-pools of a domain
        :rtype: dict
        """

        talent_pool_id = kwargs.get('id')

        # Getting a single talent-pool
        if talent_pool_id:
            talent_pool = TalentPool.query.get(talent_pool_id)

            if not talent_pool:
                raise NotFoundError(
                    "Talent pool with id {} doesn't exist in database".format(
                        talent_pool_id))

            if request.user.role.name != 'TALENT_ADMIN' and talent_pool.domain_id != request.user.domain_id:
                raise ForbiddenError(
                    "User {} is not authorized to get talent-pool's info".
                    format(request.user.id))

            talent_pool_group = TalentPoolGroup.query.filter_by(
                user_group_id=request.user.user_group_id,
                talent_pool_id=talent_pool_id).all()
            if request.user.role.name == 'USER' and not talent_pool_group:
                raise ForbiddenError(
                    "User {} doesn't have appropriate permissions to get "
                    "talent-pools's info".format(request.user.id))
            return {
                'talent_pool': {
                    'id': talent_pool.id,
                    'name': talent_pool.name,
                    'description': talent_pool.description,
                    'domain_id': talent_pool.domain_id,
                    'user_id': talent_pool.user_id,
                    'added_time': talent_pool.added_time.isoformat(),
                    'updated_time': talent_pool.updated_time.isoformat()
                }
            }
        # Getting all talent-pools of logged-in user's domain
        else:
            domain_id = request.user.domain_id

            # Get all Talent Pools of any domain if user is `TALENT_ADMIN`
            if request.user.role.name == 'TALENT_ADMIN' and request.args.get(
                    'domain_id'):
                domain_id = request.args.get('domain_id')
                if not is_number(domain_id) or not Domain.query.get(
                        int(domain_id)):
                    raise InvalidUsage("Invalid Domain Id is provided")

            talent_pools = TalentPool.query.filter_by(
                domain_id=int(domain_id)).all()
            return {
                'talent_pools': [{
                    'id':
                    talent_pool.id,
                    'name':
                    talent_pool.name,
                    'description':
                    talent_pool.description,
                    'user_id':
                    talent_pool.user_id,
                    'added_time':
                    talent_pool.added_time.isoformat(),
                    'updated_time':
                    talent_pool.updated_time.isoformat(),
                    'accessible_to_user_group_ids': [
                        talent_pool_group.user_group_id
                        for talent_pool_group in TalentPoolGroup.query.
                        filter_by(talent_pool_id=talent_pool.id).all()
                    ]
                } for talent_pool in talent_pools]
            }