MetaPhase-Consulting/State-TalentMAP-API

View on GitHub
talentmap_api/fsbid/services/projected_vacancies.py

Summary

Maintainability
A
2 hrs
Test Coverage
F
51%
import logging
from functools import partial

import requests  # pylint: disable=unused-import
import pydash

from django.conf import settings

from talentmap_api.common.common_helpers import ensure_date
from talentmap_api.fsbid.services import common as services

FAVORITES_LIMIT = settings.FAVORITES_LIMIT
PV_API_V2_URL = settings.PV_API_V2_URL

logger = logging.getLogger(__name__)


def get_projected_vacancy(id, jwt_token):
    '''
    Gets an indivdual projected vacancy by id
    '''
    args = {
        "uri": "",
        "query": {"id": id},
        "query_mapping_function": convert_pv_query,
        "jwt_token": jwt_token,
        "mapping_function": fsbid_pv_to_talentmap_pv,
        "count_function": None,
        "use_post": True,
        "base_url": "/api/v1/fsbid/projected_vacancies/",
        "api_root": PV_API_V2_URL,
    }

    pv = services.send_get_request(
        **args
    )

    return pydash.get(pv, 'results[0]') or None


def get_projected_vacancies(query, jwt_token, host=None):
    args = {
        "uri": "",
        "query": query,
        "query_mapping_function": convert_pv_query,
        "jwt_token": jwt_token,
        "mapping_function": fsbid_pv_to_talentmap_pv,
        "count_function": get_projected_vacancies_count,
        "base_url": "/api/v1/fsbid/projected_vacancies/",
        "host": host,
        "use_post": True,
        "api_root": PV_API_V2_URL,
    }

    return services.send_get_request(
        **args
    )


def get_projected_vacancies_tandem(query, jwt_token, host=None):
    args = {
        "uri": "tandem",
        "query": query,
        "query_mapping_function": partial(convert_pv_query, isTandem=True),
        "jwt_token": jwt_token,
        "mapping_function": fsbid_pv_to_talentmap_pv,
        "count_function": get_projected_vacancies_tandem_count,
        "base_url": "/api/v1/fsbid/projected_vacancies/tandem/",
        "host": host,
        "use_post": True,
        "api_root": PV_API_V2_URL,
    }

    return services.send_get_request(
        **args
    )


def get_projected_vacancies_count(query, jwt_token, host=None):
    '''
    Gets the total number of PVs for a filterset
    '''
    args = {
        "uri": "count",
        "query": query,
        "query_mapping_function": convert_pv_query,
        "jwt_token": jwt_token,
        "host": host,
        "use_post": True,
        "api_root": PV_API_V2_URL,
    }

    return services.send_count_request(**args)


def get_projected_vacancies_tandem_count(query, jwt_token, host=None):
    '''
    Gets the total number of tandem PVs for a filterset
    '''
    args = {
        "uri": "tandem",
        "query": query,
        "query_mapping_function": partial(convert_pv_query, isTandem=True),
        "jwt_token": jwt_token,
        "host": host,
        "use_post": True,
        "api_root": PV_API_V2_URL,
    }

    return services.send_count_request(**args)


def get_projected_vacancies_csv(query, jwt_token, host=None, limit=None, includeLimit=False):
    data = services.send_get_csv_request(
        "",
        query,
        convert_pv_query,
        jwt_token,
        fsbid_pv_to_talentmap_pv,
        PV_API_V2_URL,
        host,
        None,
        limit,
        True,
    )

    count = get_projected_vacancies_count(query, jwt_token)
    response = services.get_ap_and_pv_csv(data, "projected_vacancies", False)

    if includeLimit is True and count['count'] > limit:
        response['Position-Limit'] = limit

    return response


def get_projected_vacancies_tandem_csv(query, jwt_token, host=None, limit=None, includeLimit=False):
    data = services.send_get_csv_request(
        "tandem",
        query,
        partial(convert_pv_query, isTandem=True),
        jwt_token,
        fsbid_pv_to_talentmap_pv,
        PV_API_V2_URL,
        host,
        None,
        limit,
        True,
    )

    count = get_projected_vacancies_tandem_count(query, jwt_token)
    response = services.get_ap_and_pv_csv(data, "projected_vacancies", False, True)

    if includeLimit is True and count['count'] > limit:
        response['Position-Limit'] = limit

    return response


def fsbid_pv_to_talentmap_pv(pv):
    '''
    Converts the response projected vacancy from FSBid to a format more in line with the Talentmap position
    '''
    ted = ensure_date(pv.get("fv_override_ted_date", None), utc_offset=-5)
    if ted is None:
        ted = ensure_date(pv.get("ted", None), utc_offset=-5)
    skill2 = services.get_secondary_skill(pv)
    return {
        "id": pv.get("fv_seq_num", None),
        "ted": ted,
        "bidcycle": {
            "id": pv.get("bsn_id", None),
            "name": pv.get("bsn_descr_text", None),
            "cycle_start_date": None,
            "cycle_deadline_date": None,
            "cycle_end_date": None,
            "active": True
        },
        "tandem_nbr": pv.get("tandem_nbr", None),  # Only appears in tandem searches
        "position": {
            "grade": pv.get("pos_grade_code", None),
            "skill": f"{pv.get('pos_skill_desc', None)} ({pv.get('pos_skill_code')})",
            "skill_code": pv.get("pos_skill_code", None),
            "skill_secondary": skill2.get("skill_secondary"),
            "skill_secondary_code": skill2.get("skill_secondary_code"),
            "bureau": f"({pv.get('pos_bureau_short_desc', None)}) {pv.get('pos_bureau_long_desc', None)}",
            "organization": f"({pv.get('org_short_desc', None)}) {pv.get('org_long_desc', None)}",
            "tour_of_duty": pv.get("tod", None),
            "languages": list(filter(None, [
                services.parseLanguage(pv.get("lang1", None)),
                services.parseLanguage(pv.get("lang2", None)),
            ])),
            "commuterPost": {
                "description": pv.get("cpn_desc", None),
                "frequency": pv.get("cpn_freq_desc", None),
            },
            "post": {
                "tour_of_duty": pv.get("tod", None),
                "post_overview_url": services.get_post_overview_url(pv.get("pos_location_code", None)),
                "post_bidding_considerations_url": services.get_post_bidding_considerations_url(pv.get("pos_location_code", None)),
                "obc_id": services.get_obc_id(pv.get("pos_location_code", None)),
                "differential_rate": pv.get("bt_differential_rate_num", None),
                "danger_pay": pv.get("bt_danger_pay_num", None),
                "location": {
                    "country": pv.get("location_country", None),
                    "code": pv.get("pos_location_code", None),
                    "city": pv.get("location_city", None),
                    "state": pv.get("location_state", None),
                },
            },
            "current_assignment": {
                "user": pv.get("incumbent", None),
                "estimated_end_date": ensure_date(pv.get("ted", None), utc_offset=-5)
            },
            "assignee": pv.get("assignee", None),
            "position_number": pv.get("position", None),
            "title": pv.get("pos_title_desc", None),
            "availability": {
                "availability": True,
                "reason": None
            },
            "bid_cycle_statuses": [
                {
                    "id": pv.get("fv_seq_num", None),
                    "bidcycle": pv.get("bsn_descr_text", None),
                    "position": pv.get("pos_title_desc", None),
                    "status_code": None,
                    "status": None
                }
            ],
            "latest_bidcycle": {
                "id": pv.get("bsn_id", None),
                "name": pv.get("bsn_descr_text", None),
                "cycle_start_date": None,
                "cycle_deadline_date": None,
                "cycle_end_date": None,
                "active": True
            },
            "description": {
                "content": pv.get("ppos_capsule_descr_txt", None),
                "date_updated": ensure_date(pv.get("ppos_capsule_modify_dt", None), utc_offset=5),
            }
        },
        "unaccompaniedStatus": pv.get("us_desc_text", None),
        "isConsumable": pv.get("bt_consumable_allowance_flg", None) == "Y",
        "isServiceNeedDifferential": pv.get("bt_service_needs_diff_flg", None) == "Y",
        "isDifficultToStaff": pv.get("bt_most_difficult_to_staff_flg", None) == "Y",
        "isEFMInside": pv.get("bt_inside_efm_employment_flg", None) == "Y",
        "isEFMOutside": pv.get("bt_outside_efm_employment_flg", None) == "Y",
    }


def convert_pv_query(query, isTandem=False):
    '''
    Converts TalentMap filters into FSBid filters

    The TalentMap filters align with the position search filter naming
    '''

    prefix = ""

    values = {
        # Pagination
        f"{prefix}order_by": services.sorting_values(query.get("ordering", None)),
        f"{prefix}page_index": int(query.get("page", 1)),
        f"{prefix}page_size": query.get("limit", 25),

        # Tandem 1 filters
        f"{prefix}seq_nums": services.convert_multi_value(query.get("id", None)),
        f"{prefix}bid_seasons": services.convert_multi_value(query.get("is_available_in_bidseason")),
        f"{prefix}overseas_ind": services.convert_multi_value(services.overseas_values(query)),
        f"{prefix}languages": services.convert_multi_value(query.get("language_codes")),
        f"{prefix}bureaus": services.convert_multi_value(query.get("position__bureau__code__in")),
        f"{prefix}grades": services.convert_multi_value(query.get("position__grade__code__in")),
        f"{prefix}org_codes": services.convert_multi_value(query.get("position__org__code__in")),
        f"{prefix}location_codes": services.post_values(query),
        f"{prefix}assign_cycles": services.convert_multi_value(query.get("is_available_in_bidcycle")),
        f"{prefix}danger_pays": services.convert_multi_value(query.get("position__post__danger_pay__in")),
        f"{prefix}differential_pays": services.convert_multi_value(query.get("position__post__differential_rate__in")),
        f"{prefix}pos_numbers": services.convert_multi_value(query.get("position__position_number__in", None)),
        f"{prefix}post_ind": services.convert_multi_value(query.get("position__post_indicator__in")),
        f"{prefix}tod_codes": services.convert_multi_value(query.get("position__post__tour_of_duty__code__in")),
        f"{prefix}skills": services.convert_multi_value(query.get("position__skill__code__in")),
        f"{prefix}us_codes": services.convert_multi_value(query.get("position__us_codes__in")),
        f"{prefix}cpn_codes": services.convert_multi_value(query.get("position__cpn_codes__in")),
        f"{prefix}freeText": query.get("q", None),
    }

    if not isTandem:
        values[f"{prefix}get_count"]: query.get("getCount", 'false')

    if isTandem:
        ordering = query.get("ordering", None)
        values[f"{prefix}count"] = query.get("getCount", 'false')
        values[f"{prefix}order_by"] = services.sorting_values(f"commuterPost,location,tandem,{ordering}")
        # Common filters
        values[f"{prefix}overseas_ind2"] = services.convert_multi_value(services.overseas_values(query))
        values[f"{prefix}location_codes2"] = services.post_values(query)
        values[f"{prefix}danger_pays2"] = services.convert_multi_value(query.get("position__post__danger_pay__in"))
        values[f"{prefix}differential_pays2"] = services.convert_multi_value(query.get("position__post__differential_rate__in"))
        values[f"{prefix}post_ind2"] = services.convert_multi_value(query.get("position__post_indicator__in"))
        values[f"{prefix}us_codes2"] = services.convert_multi_value(query.get("position__us_codes__in"))
        values[f"{prefix}cpn_codes2"] = services.convert_multi_value(query.get("position__cpn_codes__in"))
        values[f"{prefix}freeText2"] = query.get("q", None)

        # Tandem 2 filters
        values[f"{prefix}seq_nums2"] = services.convert_multi_value(query.get("id-tandem", None))
        values[f"{prefix}bid_seasons2"] = services.convert_multi_value(query.get("is_available_in_bidseason-tandem"))
        values[f"{prefix}languages2"] = services.convert_multi_value(query.get("language_codes-tandem"))
        values[f"{prefix}bureaus2"] = services.convert_multi_value(query.get("position__bureau__code__in-tandem"))
        values[f"{prefix}grades2"] = services.convert_multi_value(query.get("position__grade__code__in-tandem"))
        values[f"{prefix}pos_numbers2"] = services.convert_multi_value(query.get("position__position_number__in-tandem", None))
        values[f"{prefix}tod_codes2"] = services.convert_multi_value(query.get("position__post__tour_of_duty__code__in-tandem"))
        values[f"{prefix}skills2"] = services.convert_multi_value(query.get("position__skill__code__in-tandem"))

    if isinstance(values[f"{prefix}order_by"], list):
        values[f"{prefix}order_by"] = pydash.compact(values[f"{prefix}order_by"])

    valuesToReturn = pydash.omit_by(values, lambda o: o is None or o == [])
    return valuesToReturn


def get_pv_favorite_ids(query, jwt_token, host=None):
    return services.send_get_request(
        "",
        query,
        convert_pv_query,
        jwt_token,
        fsbid_favorites_to_talentmap_favorites_ids,
        get_projected_vacancies_count,
        "/api/v1/fsbid/projected_vacancies/",
        host,
        PV_API_V2_URL,
        True,
    ).get('results')


def fsbid_favorites_to_talentmap_favorites_ids(pv):
    return pv.get("fv_seq_num", None)