diff --git a/src/ado_files/iecodebook.ado b/src/ado_files/iecodebook.ado index 6b6ed10..5a77e1a 100644 --- a/src/ado_files/iecodebook.ado +++ b/src/ado_files/iecodebook.ado @@ -4,6 +4,8 @@ cap program drop iecodebook program def iecodebook + + qui do "${GitHub}\iefieldkit\src\ado_files/iefieldkit_aux.do" version 13 // Requires 13.0 due to use of long macros @@ -29,49 +31,24 @@ cap program drop iecodebook // Select subcommand noi di " " gettoken subcommand anything : anything - - // Check folder exists - - // Start by standardize all slashes to forward slashes, and get the position of the last slash - local using = subinstr("`using'","\","/",.) - local r_lastslash = strlen(`"`using'"') - strpos(strreverse(`"`using'"'),"/") - if strpos(strreverse(`"`using'"'),"/") == 0 local r_lastslash -1 // Set to -1 if there is no slash - - // Get the full folder path and the file name - local r_folder = substr(`"`using'"',1,`r_lastslash') - local r_file = substr(`"`using'"',`r_lastslash'+2,.) - - // Test that the folder for the report file exists - mata : st_numscalar("r(dirExist)", direxists("`r_folder'")) - if `r(dirExist)' == 0 { - noi di as error `"{phang}The folder [`r_folder'/] does not exist.{p_end}"' - error 601 - } - - // Find the position of the last dot in the file name and get the file format extension - local r_lastsdot = strlen(`"`r_file'"') - strpos(strreverse(`"`r_file'"'),".") - local r_fileextension = substr(`"`r_file'"',`r_lastsdot'+1,.) - - // If no fileextension was used, then add .xslx to "`using'" - if "`r_fileextension'" == "" { - local using "`using'.xlsx" - } - // Throw an error if user input uses any extension other than the allowed - else if !inlist("`r_fileextension'",".xlsx",".xls") & !regexm(`"`options'"',"tempfile") { - di as error "The codebook may only have the file extension [.xslx] or [.xls]. The format [`r_fileextension'] is not allowed." - error 601 - } - - + + // Test that the folder exists + ieutil_folderpath using `using', description("in the file") + + + // Test the form file: xls or xlsx + ieutil_fileext using `using', allowed_exts(.xlsx .xls) default_ext(.xlsx) + local using "`r(file_path)'" + + // Throw error on [template] if codebook cannot be created if inlist("`subcommand'","template","export") & !regexm(`"`options'"',"replace") & !regexm(`"`options'"',"verify") { - + cap confirm file "`using'" if (_rc == 0) & (!strpos(`"`options'"',"replace")) { di as err "That codebook already exists. {bf:iecodebook} will only overwrite it if you specify the [replace] option." error 602 } - cap confirm new file "`using'" if (_rc != 0) & (!strpos(`"`options'"',"replace")) { di as error "{bf:iecodebook} could not create file `using'. Check that the file path is correctly specified." diff --git a/src/ado_files/ieduplicates.ado b/src/ado_files/ieduplicates.ado index 62e6600..a9a38ea 100644 --- a/src/ado_files/ieduplicates.ado +++ b/src/ado_files/ieduplicates.ado @@ -4,7 +4,7 @@ program ieduplicates , rclass - qui { + qui { syntax varname [using/] , UNIQUEvars(varlist) /// [force /// @@ -17,7 +17,9 @@ duplistid(string) datelisted(string) datefixed(string) correct(string) drop(string) newid(string) initials(string) notes(string) listofdiffs(string)] version 11.0 - + + qui do "${GitHub}\iefieldkit\src\ado_files/iefieldkit_aux.do" + *Add version of Stata fix //Make sure that keepvars are still saved if saved if the duplicates file * is generated on a subset of the data. For example, duplicates from @@ -30,7 +32,6 @@ * message to this that is more informative. preserve - /*********************************************************************** ************************************************************************ Section 1 - Set up locals needed in data @@ -867,7 +868,7 @@ * the orignal data set in case of error. use `dataToReturn', clear - } + } end @@ -932,68 +933,31 @@ cap program drop testpath Prepare file name if option using was specified *******************************************************************************/ - * Parse using option to get (1) the folder path (2) the name of the report and - * (3) the format selected else if "`using'" != "" { - - * Replace any backslashes with forward slashes so it's compatible with - * different OS and so we know what to look for as separators when parsing - local using = subinstr("`using'", "\", "/", .) - - * Separate the folder name from the file name - strlast, expression("`using'") character("/") - - * If a folder was specified, get the folder path - if `r(lastpos)' > 0 { - local folder = substr("`using'", 1, `r(lastpos)') - } - else { + + * Check if the folder exist + ieutil_parse_filepath using `using' + local folder `r(folderpath)' + if missing("`folder'") { noi di as error "{phang}You have not specified a folder path to the duplicates report. An absolute folder path is required.{p_end}" noi di as error `"{phang}This command will not work if you are trying to use {inp:cd} to set the directory and open or save files. To know more about why this practice is not allowed, {browse "https://dimewiki.worldbank.org/wiki/Stata_Coding_Practices#File_paths":see this article in the DIME Wiki}.{p_end}"' noi di as error "" error 198 - exit - } - - * Everything that comas after the folder path is the file name and format - local file = substr("`using'", `r(lastpos)' + 1, .) - - * If a filename was specified, separate the file name from the file format - if "`file'" != "" { - - * Get index of separation between file name and file format - strlast, expression("`file'") character(".") - - * If a format was specified, separate name and format - if `r(lastpos)' > 0 { - local ext = substr("`file'", `r(lastpos)', .) // File format starts at the last period and ends at the end of the string - local name = substr("`file'", 1, `r(lastpos)' - 1) // File name starts at the beginning and ends at the last period - } - * If a format was not specified, the name is everything that follows the - * folder path - else { - local name `file' - } - } - * Check that a folder name was specified and through an error if it wasn't - if ("`file'" == "" | ( "`file'" != "" & "`name'" == "")) { - noi di as error "{phang}`using' not a valid filename.{p_end}" - noi di "" - error 198 - exit - } - - * The default format is xlsx. Other possible formats are xls - if "`ext'" == "" { - local ext .xlsx - } - else if !inlist("`ext'", ".xls", ".xlsx") { - noi di "" - noi di as error `"{phang}`ext' is not currently supported as a format for the duplicates report. Supported formats are: xls, xslx. If you have a suggestion of a different format to support, please e-mail dimeanalytics@worldbank.org or {browse "https://github.com/worldbank/iefieldkit/issues":create an issue on the iefieldkit GitHub repository.}{p_end}"' - noi di "" - error 198 //TODO: check error code - exit } + + * Check if the file extension is the correct. + ieutil_fileext using `using', allowed_exts(.xlsx .xls) default_ext(.xlsx) + local using "`r(file_path)'" + + * Test that the folder exists + ieutil_folderpath using `using' + + * Get the folder path, the name of the report and, the format selected + ieutil_parse_filepath using `using' + local folder `r(folderpath)'/ + local name `r(filename)' + local ext `r(fileext)' + } * Create file name if using was not specified @@ -1004,24 +968,11 @@ cap program drop testpath if "`suffix'" != "" local name `name'_`suffix' local ext .xlsx } - - * Test that the folder indicated existes - mata : st_numscalar("r(dirExist)", direxists("`folder'")) - - ** If the folder does not exist, throw an error - if `r(dirExist)' == 0 { - - *Variable exist, output error - noi di as error "{phang}The folder specified does not exist :`folder'.{p_end}" - noi di "" - error 198 // TODO: check error code - exit - } - + return local name `name' return local ext `ext' - return local folder `folder' - + return local folder `folder' + end /******************************************************************************* diff --git a/src/ado_files/iefieldkit_aux.do b/src/ado_files/iefieldkit_aux.do new file mode 100644 index 0000000..97a66ce --- /dev/null +++ b/src/ado_files/iefieldkit_aux.do @@ -0,0 +1,131 @@ +/* + This do-file contains functions to perform the following tasks: +*/ + +/******************************************************************************* + GET FILE COMPONENTS + - Folder path + - File extension and file name +********************************************************************************/ +cap program drop ieutil_parse_filepath + program ieutil_parse_filepath, rclass + + syntax using/ + + * Standardize file path so Mac and Linux systems handle this path correctly + local using = subinstr("`using'", "\", "/", .) + + * Separate the folder name from the file name + local r_lastslash = strlen(`"`using'"') - strpos(strreverse(`"`using'"'),"/") + if strpos(strreverse(`"`using'"'),"/") == 0 local r_lastslash -1 // Set to -1 if there is no slash + + * If a folder was specified, get the folder path + local folder = substr(`"`using'"',1,`r_lastslash') + + * Everything that comes after the folder path is the file name and format + local file = substr("`using'", `r_lastslash' + 2, .) + + * If a filename was specified, separate the file name from the file format + if "`file'" != "" { + + * Get index of separation between file name and file format + local r_lastsdot = strlen(`"`file'"') - strpos(strreverse( `"`file'"'),".") + + local fileext = substr(`"`file'"',`r_lastsdot'+1,.) // File format starts at the last period and ends at the end of the + + local filename = substr("`file'", 1, `r_lastsdot') // File name starts at the beginning and ends at the last period + + } + + return local folderpath `folder' + return local file `file' + return local filename `filename' + return local fileext `fileext' + return local filepath `using' +end + + +/******************************************************************************* +TEST IF A FOLDER ALREADY EXISTS +- option "folderpath" is the path to the directory +- option "description" will be used in the error message to explain where this folder path was referenced + +********************************************************************************/ +cap program drop ieutil_folderpath + program ieutil_folderpath + + syntax using/, [description(string)] + ieutil_parse_filepath using `using' + + * Test that the folder for the report file exists + if !missing("`r(folderpath)'"){ + mata : st_numscalar("r(dirExist)", direxists("`r(folderpath)'")) + if `r(dirExist)' == 0 { + noi di as error `"{phang}The folder path [`r(folderpath)'/] used `description' does not exist.{p_end}"' + error 601 + } + } + +end + + +/******************************************************************************* +TEST IF THE FILE EXTENSION IS THE CORRECT +- option "testfileext" is a namelist of the correct extensions that the file may only have + +********************************************************************************/ + +cap program drop ieutil_fileext + program ieutil_fileext, rclass + + syntax using/, allowed_exts(string) [default_ext(string)] + + *Parse the input + ieutil_parse_filepath using `using' + local this_filename "`r(filename)'" + local this_ext "`r(fileext)'" + + *Test if using has no file + if missing("`this_filename'") { + noi di as error `"{phang}The path {bf:`using'} does not have a file name for which extension can be tested.{p_end}"' + error 198 + } + *Test is using has no file extension + else if missing("`this_ext'") { + + *Test if no deafult ext was provided + if missing("`default_ext'") { + noi di as error `"{phang}The file in {bf:`using'} does not have a file extension and no default was provided.{p_end}"' + error 198 + } + + *Apply the deafult extension + else { + + local return_file "`using'`default_ext'" + + } + } + + * Using has both file and extension + else { + + * Test if extension is among the allowed extensions + if `: list this_ext in allowed_exts' == 1 { + *Extension is allowed, return file name as is + local return_file "`using'" + } + + * File extension used is not allowed + else { + noi di as error `"{phang}The file extension [`this_ext'] in file {bf:`using'} is not allowed. Allowed extensions: [`allowed_exts'].{p_end}"' + error 198 + } + } + + *Return checked filename + return local file_path `return_file' +end + + + \ No newline at end of file diff --git a/src/ado_files/ietestform.ado b/src/ado_files/ietestform.ado index 1fd9a90..411da22 100644 --- a/src/ado_files/ietestform.ado +++ b/src/ado_files/ietestform.ado @@ -3,18 +3,21 @@ capture program drop ietestform program ietestform , rclass -qui { - +qui{ version 13 preserve syntax [using/] , Reportsave(string) [Surveyform(string) STATAlanguage(string) date replace] + /*********************************************** Test input ***********************************************/ + * ieutil commands + qui do "${GitHub}\iefieldkit\src\ado_files/iefieldkit_aux.do" + /********* Test survey file input *********/ @@ -30,15 +33,15 @@ qui { } local surveyform `"`using'`surveyform'"' + + *Test that the folder exists + ieutil_folderpath using `surveyform', description("in surveyform ") + + * Test if the form file is xls or xlsx + ieutil_fileext using `surveyform', allowed_exts(.xlsx .xls) default_ext(.xlsx) + local surveyform "`r(file_path)'" - * Test for form file is xls or xlsx - local surveyformtype = substr(`"`surveyform'"',strlen(`"`surveyform'"')-strpos(strreverse(`"`surveyform'"'),".")+1,.) - if !(`"`surveyformtype'"' == ".xls" | `"`surveyformtype'"' == ".xlsx") { - noi di as error `"{phang}The survey form file [`surveyform'] must have file extension .xls or .xlsx specified in the option.{p_end}"' - error 601 - } - - *Test that the form file exists + *Test if the form file exists cap confirm file "`surveyform'" if _rc { @@ -51,49 +54,13 @@ qui { *********/ ********* - *Get the folder for the report file - - **Start by finding the position of the last forward slash. If no forward - * slash exist, it is zero, then replace to to string len so it is never - * the min() below. - local r_f_slash = strpos(strreverse(`"`reportsave'"'),"\") - if `r_f_slash' == 0 local r_f_slash = strlen(`"`reportsave'"') - - **Start by finding the position of the last backward slash. If no backward - * slash exist, it is zero, then replace to to string len so it is never - * the min() below. - local r_b_slash = strpos(strreverse(`"`reportsave'"'),"/") - if `r_b_slash' == 0 local r_b_slash = strlen(`"`reportsave'"') - - *Get the last slash in the report file path regardless of back or forward - local r_lastslash = strlen(`"`reportsave'"')-min(`r_f_slash',`r_b_slash') - - *Get the folder - local r_folder = substr(`"`reportsave'"',1,`r_lastslash') *Test that the folder for the report file exists - mata : st_numscalar("r(dirExist)", direxists("`r_folder'")) - if `r(dirExist)' == 0 { - noi di as error `"{phang}The folder used in [`reportsave'] does not exist.{p_end}"' - error 601 - } - - *Get the filename and the file extension type from the report file - local r_filename = substr(`"`reportsave'"',`r_lastslash'+1, .) - local r_filenametype = substr(`"`r_filename'"',strlen(`"`r_filename'"')-strpos(strreverse(`"`r_filename'"'),".")+1,.) - *Test what the file extension type is - if (`"`r_filenametype'"' == "") { - *No file type specified, add .csv - local reportsave `"`reportsave'.csv"' - } - else if (`"`r_filenametype'"' != ".csv") { - *Incorrect file type added. Throw error - noi di as error `"{phang}The report file [`reportsave'] may only have the file extension .csv.{p_end}"' - error 601 - } - else { - * All is correct, do nothing - } + ieutil_folderpath using `using', description("in [`reportsave'] ") + + *Test if the file extension type from the report file is csv + ieutil_fileext `using', allowed_exts(.csv) default_ext(.csv) + local using "`r(file_path)'" *Tempfile that will be used to write the report tempfile report_tempfile