Beispiel #1
0
    def test_get_iterable(self):
        """
        Test to check if iterable is returned by method `get_iterable`
        """
        # Case 1: When `parameter` is not iterable
        # Setup
        parameter = 2000
        expected_parameter = [2000]

        # Call to :func:`get_iterable`
        actual_parameter = get_iterable(parameter)

        # Asert for iterable `parameter`
        self.assertEqual(expected_parameter, actual_parameter)

        # Case 2: When `parameter` is iterable.
        # Setup
        parameter = ["test", "parameter"]
        expected_parameter = ["test", "parameter"]

        # Call to :func:`get_iterable`
        actual_parameter = get_iterable(parameter)

        # Asert for iterable `parameter`
        self.assertEqual(expected_parameter, actual_parameter)
Beispiel #2
0
def get_year_wise_generator(year=None, namcs_raw_dataset_file=None,
                            do_export = False):
    """
    Method to translated NAMCS data for `year` and/or `namcs_dataset_file`
    into human readable form,

    Parameters:
        year (:class:`int` or :class:`tuple` or :class:`list`): NAMCS year(s)
            for which raw data needs to be translated. If year is not specified,
            the conversion will be carried out for all the years defined in
            `YEARS_AVAILABLE`.
        namcs_raw_dataset_file (:class:`str`): Absolute path of
            raw dataset input file. If not specified, local file path will be
            deduced on the basis of `year` specified by user.
            Note: Local (extracted) file must exists for this method to yield
            desired response.
        do_export (:class:`bool`): Indicates whether to export translated NAMCS
            data to csv file.**Default** :const:`False`.

    Returns:
        :class:`defaultdict`: Dictionary containing generator of translated
        NAMCS patient case data for given year along with source file info.
        Further if `do_export` is True, it returns the absolute path of csv
        file where the data is exported.
    """
    year_wise_translated_data = defaultdict(dict)

    # If `year` not specified, translate data for all years `YEARS_AVAILABLE`
    year = YEARS_AVAILABLE if year is None else get_iterable(year)

    # Using integer value for `year`
    for _year in map(int, year):
        year_wise_translated_data[_year]["generator"] = \
            get_generator_by_year(_year, namcs_raw_dataset_file)
        # NAMCS dataset source file info
        year_wise_translated_data[_year]["source_file_info"] = \
            get_namcs_source_file_info(_year)
        # Export translated data to csv file
        if do_export:
            # Using :func:`tee` of module `itertools` to have copy of generator.
            gen_object = tee(
                year_wise_translated_data.get(_year).get("generator"), 1
            )[0]
            year_wise_translated_data[_year]["file_name"] = \
                export_to_csv(_year, gen_object)

    return year_wise_translated_data
Beispiel #3
0
def initiate_namcs_dataset_download(year=None,
                                    force_download = False,
                                    extract_path = EXTRACTED_DATA_DIR_PATH,
                                    download_path = DOWNLOADED_FILES_DIR_PATH):
    """
    Download and extract all the NAMCS dataset files available for public use
    in ftp.cdc.gov FTP server.

    Parameters:
        year(:class:`int` or :class:`list` or :class:`tuple`): Year(s) for
            which dataset files will be downloaded and extracted.
        force_download (:class:`bool`): Whether to force download
            NAMCS raw dataset file even if data set file exists locally.
            *Default** :const:`False`.
        extract_path(:class:`str`): Extract path where zip files will be
            extracted.
        download_path (:class:`str`): Downloaded zipped dataset file path for
            `year`.
    Note:
        >>> from hdx_ahcd.namcs.config import YEARS_AVAILABLE
        >>> YEARS_AVAILABLE
        [1973, 1975, 1976, 1977, 1978, 1979, 1980, 1981, 1985, 1989, 1990, 1992,
        1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
        2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015]
    """
    year = YEARS_AVAILABLE if year is None else get_iterable(year)

    # Download files for all the `year`
    # Using integer value for `year`
    for _year in map(int, year):
        # Checking if NAMCS dataset file already exists in the
        # `EXTRACTED_DATA_DIR_PATH`
        if get_namcs_dataset_path_for_year(_year) is None or force_download:
            # Download files for `_year`
            full_file_name = \
                download_namcs_zipfile(_year, download_path=download_path)
            # Extract downloaded zipped file
            extract_data_zipfile(
                _year, full_file_name, extract_path=extract_path
            )
            # Rename NAMCS file
            rename_namcs_dataset_for_year(_year)
            # Delete downloaded zip file.
            delete_namcs_zipfile(_year, download_path=download_path)
def _validate_namcs_year(year):
    """
    Method to validate year of NAMCS dataset file.

    Parameters:
        year (:class:`str` or :class:`int`): Year for NAMCS raw dataset.

    Returns:
        :class:`TrackValidationError`: Object of :class:`TrackValidationError`
        having errors if any.
    """
    validation_obj = TrackValidationError()
    year = get_iterable(year)
    for _year in map(int, year):
        if _year not in YEARS_AVAILABLE:
            validation_obj.errors.append(
                "Year {} is not valid year, please specify valid years"
                ",valid years are: {}".format(_year, YEARS_AVAILABLE)
            )
    return validation_obj
    def _add_method_to_mapping_dict(method_to_decorate):
        """
        Inside wrapper to construct key value pair for `method_to_decorate`.

        Parameters:
            method_to_decorate (:class:`function`): Method object.

        Returns:
            :class:`function`: Decorated method.
        """
        nonlocal method_identifiers
        # Avoids cyclic import issue
        from hdx_ahcd.helpers.functions import get_iterable
        method_identifiers = get_iterable(method_identifiers)

        # Adding all method name identifiers
        # to global dict CONVERSION_METHOD_MAPPING
        for identifier in method_identifiers:
            CONVERSION_METHOD_MAPPING[identifier] = method_to_decorate

        @wraps(method_to_decorate)
        def wrapper(*arg, **kwargs):
            """
            Inside wrapper.

            Parameters:
                arg (:class:`tuple`): Positional arguments to
                    `method_to_decorate`.
                kwargs (:class:`dict`): Keywords arguments to
                    `method_to_decorate`.

            Returns:
                :class:`object`: Return value of `method_to_decorate`.
            """
            return method_to_decorate(*arg, **kwargs)

        return wrapper
    def _create_path_if_does_not_exists(method_to_decorate):
        """
        Inside decorator to decorate `method_to_decorate`.

        Parameters:
            method_to_decorate (:class:`function`): Method object.

        Returns:
            :class:`function`: Decorated method.
        """
        nonlocal paths

        # Avoids cyclic import issue
        from hdx_ahcd.helpers.functions import get_iterable
        paths = get_iterable(paths)
        for path in paths:
            # Creating file path if doesn't exist
            if not os.path.exists(path):
                os.makedirs(path)

        @wraps(method_to_decorate)
        def wrapper(*arg, **kwargs):
            """
            Inside wrapper.

            Parameters:
                arg (:class:`tuple`): Positional arguments to
                    `method_to_decorate`.
                kwargs (:class:`dict`): Keywords arguments to
                    `method_to_decorate`.

            Returns:
                :class:`object`: Return value of `method_to_decorate`.
            """
            return method_to_decorate(*arg, **kwargs)

        return wrapper
    def test_initiate_namcs_dataset_download_with_year(
            self, mocked_delete_namcs_zipfile, mocked_extract_data_zipfile,
            mocked_download_namcs_zipfile):
        """
        Test if download and extraction of NAMCS public files is successful
        when `year` is provided.
        """
        # Setup
        download_namcs_zipfile_mocked_return = "path-to-downloaded-file.zip"

        # Patch `EXTRACTED_DATA_DIR_PATH`
        config.DOWNLOADED_FILES_DIR_PATH = "/tmp/namcs_downloaded_files"
        config.EXTRACTED_DATA_DIR_PATH = "/tmp/namcs_extracted_files"

        # Mocking return value of `download_namcs_zipfile` call
        mocked_download_namcs_zipfile.return_value = \
            download_namcs_zipfile_mocked_return

        # Case 1: when year is 2000
        # Setup
        year = 2000
        # Call to func :func:`initiate_namcs_dataset_download`
        initiate_namcs_dataset_download(year=year, force_download=True)

        # Assert `download_namcs_zipfile` calls
        self.assertEqual(get_iterable(year), [
            call[0][0] for call in mocked_download_namcs_zipfile.call_args_list
        ])

        # Assert :func:`extract_data_zipfile` calls
        self.assertEqual(
            [(year, download_namcs_zipfile_mocked_return)],
            [
                call[0]  # Accessing tuple directly here
                for call in mocked_extract_data_zipfile.call_args_list
            ])
        def _wrapper(*arg, **kwargs):
            """
            Inside wrapper.

            Parameters:
                arg (:class:`tuple`): Positional arguments to
                    `method_to_decorate`.
                kwargs (:class:`dict`): Keywords arguments to
                    `method_to_decorate`.

            Returns:
                :class:`object`: Return value of `method_to_decorate`.
            """
            nonlocal types, return_type, use_regex, method_to_decorate
            method_name = None
            if method_to_decorate.__name__ in map(
                    lambda method: method.__name__,
                    CONVERSION_METHOD_MAPPING.values()):
                method_name = list(
                    filter(
                        lambda key: CONVERSION_METHOD_MAPPING[key].__name__ ==
                        method_to_decorate.__name__,
                        CONVERSION_METHOD_MAPPING.keys()))[0]
            if types and len(arg):
                # Decorator called as @enforce_type((list, tuple))
                if isinstance(types[0], (list, tuple)):
                    types = types[0]
                if len(types) > len(arg):
                    raise Exception(
                        "More positional arguments are required to check type")

                # Zip will stop as soon as shortest iterable finishes in this
                # case iteration will stop as soon as types finishes,
                # so types will exactly mapped to argument in `arg`
                # positionally.
                for _type, _arg in zip(types, arg):
                    # Skip _arg if type object
                    if _type is not object and not isinstance(_arg, _type):
                        raise Exception(
                            "Method: {} needs positional argument of "
                            "type: {}, actual type is :{}".format(
                                method_to_decorate.__name__, _type,
                                type(_arg)))
            # Avoids cyclic import issue
            from hdx_ahcd.helpers.functions import get_iterable
            if use_regex is not None and len(arg):
                use_regex = get_iterable(use_regex)

                if len(use_regex) > len(arg):
                    raise Exception("More positional arguments are required"
                                    "to validate against regular expression")
                if any([isinstance(_regex, int) for _regex in use_regex]):
                    raise Exception(
                        "`use_regex` can not have regex of type`int`")

                for _regex, _arg in zip(use_regex, arg):
                    # Skip blank regex
                    if _regex != "":
                        try:
                            _regex_pattern = re.compile(_regex)
                            if not re.search(_regex_pattern, str(_arg)):
                                raise Exception(
                                    "Value {} for field {} does not match "
                                    "with specified regex pattern.".format(
                                        _arg, method_name))
                        except Exception as ex:
                            raise Exception("Error: {}".format(str(ex)))

            # Call to method
            method_return_value = method_to_decorate(*arg, **kwargs)

            return_type = get_iterable(return_type)
            method_return_value = get_iterable(method_return_value)

            # Method returns less value than expected
            if len(return_type) > len(method_return_value):
                raise Exception(
                    "`return_type` contains more types "
                    "than values returned by method, expected "
                    "return types :{}, values returned by method:{}".format(
                        len(return_type), len(method_return_value)))

            # Zip will stop as soon as shortest iterable finishes in this
            # case iteration will stop as soon as types finishes,
            # so return_type will exactly mapped to value in
            # `method_return_value` positionally.
            for _return_type, _method_return_value in \
                    zip(return_type, method_return_value):
                # Skip _return_type if object
                if _return_type is not object and not isinstance(
                        _method_return_value, _return_type):
                    raise Exception(
                        "Method: {} needs to return value of type"
                        "type:{}, actual type of return value is :{}".format(
                            method_to_decorate.__name__, _return_type,
                            type(_method_return_value)))

            # Returning method value
            return method_return_value[0] if len(method_return_value) == 1 \
                else method_return_value