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)
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
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