Restoring Old Blog Posts


This upcoming Monday is the 25th anniversary of CSPro's first release. In anticipation of celebrating CSPro's longevity, I took a look at the second CSPro Users website, which was designed in WordPress:

2025-05-01-old-website

(The first website was designed with Drupal. The current version—the third iteration you are viewing now—is powered by Jekyll.)

We added a blog for the WordPress website but the posts did not make the transition when we moved to this new site. For this #ThrowbackThursday, I restored the 47 blog posts from the old site, originally published between December 2011 and March 2016.

If the blog looks different than you remember, that is because it is now generated using CSDocument. This tool, which also creates CSPro's documentation, colorizes code and automatically links functions and keywords to their help pages.

Although the blog has seen little activity over the past decade, we hope to post more frequently in the future.

CSPro 8.0 is Out!


The latest iteration of CSPro, version 8.0, has been released! Dive into the newest features and enhancements packed into CSPro 8.0.

What's new with CSPro 8.0

  • Many specification files are now saved in JSON format, which facilitates working with them in other programming languages or when using other tools.
  • In addition to being able to declare numeric and alphanumeric data types in the dictionary, you can now specify four new data types: Audio, Document, Geometry, and Image.
  • A new framework, the Action Invoker, provides a standard way of running actions from CSPro logic, embedded JavaScript, JavaScript executed on web pages, and in JSON format.
  • A new view, questionnaire view, displays the contents of a case and its components (dictionary, forms, question text) in a read-only mode that facilitates reviewing or printing.
  • The introduction of a new version of logic that enhances the ability to work with string literals and newline characters, and positions the syntax of CSPro logic closer to other languages.
  • A new tool, CSCode, is a text editor with support for tabbed editing of documents. The tool also facilitates running JavaScript, editing and validating JSON, designing and testing HTML dialogs, validating specification files, and more.

To get started with CSPro 8.0, be sure to update CSPro, CEntry Android, and CSWeb to the latest version.

A New CSEntry Path on a More Secure Android OS


When installing CSEntry 7.5+ you may notice the CSEntry folder has moved. The location of the CSEntry folder will depend on whether your installation is a new install or an upgrade of an existing installation. This was necessary to support more stringent security requirements on the Android OS.

New Installations of CSEntry 7.5+

With a new installation of CSEntry 7.5+ (i.e., CSEntry is not installed on the device) the CSEntry folder will be found at /Android/data/gov.census.cspro.csentry/files/csentry.

Upgrading Installations to CSEntry 7.5 – 7.6.1

Upgrading an earlier version of CSEntry to CSEntry 7.5 - 7.6.1 the location of the CSEntry folder will continue to be found at /csentry. For example, an upgrade from CSEntry 7.4 to CSEntry 7.5 will continue to use the CSEntry folder at /csentry. Upgrading to CSEntry 7.6 will again not change the location of the CSEntry folder.

Android 11+

On Android 11+, when upgrading a version of CSEntry which runs applications from /csentry to CSEntry 7.6.2+ it is necessary to complete a new installation. This can be done by uninstalling the current installation of CSEntry and then installing the new version. Note that this is necessary for all versions less than 7.5. For CSEntry 7.5 – 7.6.1 this will be necessary if it is running CSPro applications from the folder at /csentry due to an upgrade installation.

Also, with Android 11+, the only application automatically granted access to manage the contents of /Android/data is the AOSP Files application. You can access the AOSP Files application by going to Settings > Storage > tap on Files. You will be prompted to select the default application. Choose the AOSP Files application.

Caution: Do Not Delete Your Data

A consequence of this change is that uninstalling CSEntry will now remove the CSEntry folder, if it is located at /Android/data/gov.census.cspro.csentry/files/csentry. This will delete your applications and more importantly delete your data!

New and Improved Recode


CSPro 7.4 has introduced a new and improved recode statement that will allow you to get more done in a single recode. To see the differences between the old and new recode you will compare implementations of a typical education edit. Note that the old recode has been deprecated.

Specification

The goal of the two recode implementations will be to verify that students currently attending school are within the allowable age range for their grade. The specfication below defines the valid combination of grades and ages.

current-grade-attending-specification

Old Recode Implementation

Using the old recode syntax notice that you will need two recode statements. One for the minimum age and another for the maximum age.

PROC P10_GRADE_NOW_ATTENDING

preproc

   
ask if P09_ATTEND = 1 and P04_AGE >= 3// Currently attending school

postproc

   
numeric minAgeForGrade, maxAgeForGrade;

   
recode P10_GRADE_NOW_ATTENDING => minAgeForGrade;
                                 
0 => 3// Preschool and kindergarten
                                 1 => 5;
                                 
2 => 6;
                                 
3 => 7;
                                 
4 => 8;
                                 
5 => 9;
                                 
6 => 10;
                                 
7 => 11;
                                 
8 => 12;
                                 
9 => 13;
                               
10 => 14;
                               
11 => 15;
                               
12 => 16;
                               
13 => 16// University but not graduate school
                                14 => 18// Graduate school
                                15 => 15// Trade or technical school
    endrecode;

   
recode P10_GRADE_NOW_ATTENDING => maxAgeForGrade;
                                 
0 => 6// Preschool and kindergarten
                                 1 => 8;
                                 
2 => 9;
                                 
3 => 10;
                                 
4 => 11;
                                 
5 => 12;
                                 
6 => 13;
                                 
7 => 14;
                                 
8 => 15;
                                 
9 => 18;
                               
10 => 20;
                               
11 => 21;
                               
12 => 22;
                               
13 => 95// University but not graduate school
                                14 => 95// Graduate school
                                15 => 95// Trade or technical school
    endrecode;

   
if P04_AGE in minAgeForGrade:maxAgeForGrade then
       
errmsg("P10_GRADE_NOW_ATTENDING(=%d) is valid for age(=%d)", $, P04_AGE);
   
else
       
errmsg("P10_GRADE_NOW_ATTENDING(=%d) NOT valid for age(=%d)", $, P04_AGE);
       
reenter;
   
endif;

Syntax Change Between Old and New Recode

To make use of the new recode statement you will have to write your recodes with a slighly modified syntax. The table below documents these changes.

OperatorOld Recode SyntaxNew Recode Syntax
Assignment=>->
And:::
Range-:

New Recode Implementation

With this new recode statement you can determine the minimum and maximum ages within a single recode. Making your logic easier to understand and maintain.

PROC P10_GRADE_NOW_ATTENDING

preproc

   
ask if P09_ATTEND = 1 and P04_AGE >= 3// Currently attending school

postproc

   
numeric minAgeForGrade, maxAgeForGrade;

   
recode P10_GRADE_NOW_ATTENDING -> minAgeForGrade :: maxAgeForGrade;
                                 
0 ->              3 :: 6// Preschool and kindergarten
                                 1 ->              5 :: 8;
                                 
2 ->              6 :: 9;
                                 
3 ->              7 :: 10;
                                 
4 ->              8 :: 11;
                                 
5 ->              9 :: 12;
                                 
6 ->             10 :: 13;
                                 
7 ->             11 :: 14;
                                 
8 ->             12 :: 15;
                                 
9 ->             13 :: 18;
                               
10 ->             14 :: 20;
                               
11 ->             15 :: 21;
                               
12 ->             16 :: 22;
                               
13 ->             16 :: 95// University but not graduate school
                                14 ->             18 :: 95// Graduate school
                                15 ->             15 :: 95// Trade or technical school
    endrecode;

   
if P04_AGE in minAgeForGrade:maxAgeForGrade then
       
errmsg("P10_GRADE_NOW_ATTENDING(=%d) is valid for age(=%d)", $, P04_AGE);
   
else
       
errmsg("P10_GRADE_NOW_ATTENDING(=%d) NOT valid for age(=%d)", $, P04_AGE);
       
reenter;
   
endif;

Flag Implementation

Another approach is to create a test flag. In the logic below, grade_is_valid is used to show whether or not the combination of grades and ages are valid. This can further increase the readability of your logic.

PROC P10_GRADE_NOW_ATTENDING

preproc

   
ask if P09_ATTEND = 1 and P04_AGE >= 3// Currently attending school

postproc

   
numeric grade_is_valid = false;

   
recode P10_GRADE_NOW_ATTENDING :: P04_AGE -> grade_is_valid;
                                 
0 ::  3:6    -> true// Preschool and kindergarten
                                 1 ::  5:8    -> true;
                                 
2 ::  6:9    -> true;
                                 
3 ::  7:10   -> true;
                                 
4 ::  8 11   -> true;
                                 
5 ::  9:12   -> true;
                                 
6 :: 10:13   -> true;
                                 
7 :: 11:14   -> true;
                                 
8 :: 12:15   -> true;
                                 
9 :: 13:18   -> true;
                               
10 :: 14:20   -> true;
                               
11 :: 15:21   -> true;
                               
12 :: 16:22   -> true;
                               
13 :: 16:95   -> true// University but not graduate school
                                14 :: 18:95   -> true// Graduate school
                                15 :: 15:95   -> true// Trade or technical school
    endrecode;

   
if grade_is_valid then
       
errmsg("P10_GRADE_NOW_ATTENDING(=%d) is valid for age(=%d)", $, P04_AGE);
   
else
       
errmsg("P10_GRADE_NOW_ATTENDING(=%d) NOT valid for age(=%d)", $, P04_AGE);
       
reenter;
   
endif;

Why Use IsChecked Instead of Pos


CSPro 7.4 has a new function ischecked. This function returns whether a code is part of a check box field's selections. Prior to CSPro 7.4 we would use the pos function. So why use the ischecked function rather than the pos function?

Issue with Pos

Let's look at how the LANGUAGE_SPOKEN variable would be set up. Since a person could speak multiple languages we will use a check box and the language question might look like:

language-check-box

Here English, French, Russian, and Spanish are checked. In previous versions of CSPro we would use the pos function to see if a specific language was checked. For example, if we wanted to know if French was checked we would use:

if pos("21", LANGUAGE_SPOKEN) then
   
errmsg("French is checked");
else
   
errmsg("French is not checked");
endif;

The error message "French is checked" would be issued. Continuing with our example we could ask if Russian is checked:

if pos("23", LANGUAGE_SPOKEN) then
   
errmsg("Russian is checked");
else
   
errmsg("Russian is not checked");
endif;

In this case pos would return a 1 (true) since Russian is checked and the error message "Russian is checked" would be issued.

If we asked if Hindi is checked:

if pos("33", LANGUAGE_SPOKEN) then
   
errmsg("Hindi is checked");
else
   
errmsg("Hindi is not checked");
endif;

The pos function would return a 0 (false) and the message "Hindi is not checked" would be issued.

Now let's test if Bengali is checked:

if pos("32", LANGUAGE_SPOKEN) then
   
errmsg("Bengali is checked");
else
   
errmsg("Bengali is not checked");
endif;

The pos would return a 6 (true) and the message "Bengali is checked" would be issued.

But Bengali is not checked. What happened? The pos("32", LANGUAGE_SPOKEN) searched the string "11212324" found "32" in positions 6-7 returning a 6.

Explanation

The check box codes are placed at uniformly spaced offsets based on the size of the code. For example, if the check box field has a length of 20 and each code has a length of 2, then each selected code is placed in the respective 2-digit offset. That is, positions 1-2 for the 1st code, position 3-4 for the next code, and so on.

language-vs-by-form

The data are stored in the file as shown here:

language-data

The pos function does not look by offset, but instead looks for a substring match. Unfortunately, the substring "32" does exist in the data and a false match is found. In previous versions of CSPro we would need to loop through the string being searched by 2 for the language code:

numeric languageFound = 0;

do varying numeric idx = 1 while idx <= length(LANGUAGE_SPOKEN) by 2
   
if LANGUAGE_SPOKEN[idx:2] = "32" then
        languageFound = 
1;
       
break;
   
endif;
enddo;

if languageFound then
   
errmsg("Bengali is checked");
else
   
errmsg("Bengali is not checked");
endif;

Moving Forward with IsChecked

CSPro 7.4 greatly simplifies this check with the ischecked function. The ischecked function checks for the codes at the appropriate offsets, in this case the function checks positions 1-2, 3-4, 5-6, 7-8, ..., 19-29 for the code "32". To check for Bengali we simply use:

if ischecked("32", LANGUAGE_SPOKEN) then
   
errmsg("Bengali is checked");
else
   
errmsg("Bengali is not checked");
endif;

The ischecked function returns a 0 (false) since "32" is not found within one of the uniformly spaced offsets. To do this CSPro requires the codes to be a uniform length. Notice all codes in this example were length 2.