Today I close out a chapter of my life at Northeastern University. Tomorrow I start a new chapter (and a new blog) at Granite State College. The unifying factor? Driving continuous improvement through the thoughtful application of technology. And higher education. And Salesforce CRM.
Speaking of Salesforce... I hope that as companies begin adopting tools such as Chatter (and accessories like Chatr.cc), internal transparency translates into more engaged employees, customers and the public at large. I'm thankful for all that I've learned at NU, professionally and academically, and I believe in the power of a social enterprise in both the Salesforce and the charitable senses of the term.
Thursday, June 28, 2012
Thursday, May 3, 2012
FileMaker Pro Equivalent to SQL Query or View
Have you ever wondered how to create queries or views in FileMaker Pro on Mac? I have, and it's one of those odd learning curves that come with being used to straight-forward but fairly technical SQL statements that are customary in Oracle, SQL Server, MySQL and even Access. Searching for the word "join" returns no relevant results in the FileMaker Pro 9 Help files.
Well, for other SQL developers out there, I hope this tutorial will shed some light on how to join two tables and then create a query or a view: FileMaker Pro: Creating a "Query" or "View"
In summary, here are the key takeaways from the tutorial:
Let me know what you think of the tutorial! Personally, I was super excited to figure out how to import two lists from two different databases into FileMaker Pro in order to identify discrepancies between the two lists, like I would've done using Excel or Access. Why would I use FileMaker Pro instead of Excel on Mac to do this? Because Excel is unfortunately case-insensitive.
Well, for other SQL developers out there, I hope this tutorial will shed some light on how to join two tables and then create a query or a view: FileMaker Pro: Creating a "Query" or "View"
In summary, here are the key takeaways from the tutorial:
- A SQL join is analogous to a layout in FileMaker Pro with relationships between tables.
- A SQL query or view is analogous to using Find Mode in FileMaker Pro with a layout.
- Simple filters can be applied in Find Mode using Symbols and the Omit checkbox.
Let me know what you think of the tutorial! Personally, I was super excited to figure out how to import two lists from two different databases into FileMaker Pro in order to identify discrepancies between the two lists, like I would've done using Excel or Access. Why would I use FileMaker Pro instead of Excel on Mac to do this? Because Excel is unfortunately case-insensitive.
Thursday, April 26, 2012
Auto-filling City and State with Postcode Anywhere and Marketo
First, the "product": an HTML
The background for this is that our Marketing department went live with Marketo last week, and that was an awesome accomplishment which involved replacing all of our previous Web-to-Lead forms with Marketo landing pages. With that done, we set our eyes on a next step: making our forms more approachable by automatically filling in a lead's city and state/province if they give us the country and zip/postal code.
For the lookup service, we chose Postcode Anywhere originally because they were listed on the Salesforce AppExchange. But then we realized that the app was for internal use after a lead already went into Salesforce, and what we actually want is for the info to be filled in before a form is even submitted. Plus, we want our leads to go into Marketo first, not Salesforce.
Knowing that our marketers should not have to know JavaScript to use this functionality, it was obvious that any real solution would have to be able to be dragged and dropped on to a Marketo landing page with minimal configuration, if any at all.
Thankfully, it appears that there are standard address fields in Marketo with standard and consistent
This is a great example of two companies, Marketo and Postcode Anywhere, making user-friendly integration a viable and attractive option.
script block that can be added to Marketo as a snippet for looking up the City and State/Province based on a given Country and Zip/Postal Code! All one has to do to use it should be to edit and change the Postcode Anywhere license key.The background for this is that our Marketing department went live with Marketo last week, and that was an awesome accomplishment which involved replacing all of our previous Web-to-Lead forms with Marketo landing pages. With that done, we set our eyes on a next step: making our forms more approachable by automatically filling in a lead's city and state/province if they give us the country and zip/postal code.
For the lookup service, we chose Postcode Anywhere originally because they were listed on the Salesforce AppExchange. But then we realized that the app was for internal use after a lead already went into Salesforce, and what we actually want is for the info to be filled in before a form is even submitted. Plus, we want our leads to go into Marketo first, not Salesforce.
Knowing that our marketers should not have to know JavaScript to use this functionality, it was obvious that any real solution would have to be able to be dragged and dropped on to a Marketo landing page with minimal configuration, if any at all.
Thankfully, it appears that there are standard address fields in Marketo with standard and consistent
name and id attributes. Once I discovered this common characteristic across all forms that we had created, it was simple to customize the JavaScript code template that Postcode Anywhere publishes to work with Marketo.This is a great example of two companies, Marketo and Postcode Anywhere, making user-friendly integration a viable and attractive option.
Monday, April 2, 2012
SObject Utility Class Template
Have you ever had to get the ID of a record type for a specific object? Or have you needed to hard-code a particular picklist value into a Visualforce controller/extension or other Apex class? I've had to do both on a fairly regular basis, and it soon became apparent that stress-related health problems may arise if I ever have to refactor my code, or if a user requests a change in picklist values.
To address this issue, it seems that setting up a global constant accessible to all Apex classes would be hugely valuable in making sure that picklist values and record types (and other items) are consistently referenced in Apex.
For example, if I'm trying to set the Status of a Contract to "Activated", I could write the code as follows:
This is great if it's the only place I ever deal with the Contract Status field. But what're the chances of that? Instead, how about writing the code as follows?
Now I don't need to worry about all the myriad places where I've hardcoded the status value. If I ever need to change the status, I can update the constant in
Here's the sample code for a generic SObject utility class:
Note: I know the above code may be cut off a bit due to width limits in Blogger, but you should be able to copy and paste the code into a text editor to see the full structure if necessary.
What do you think? Feedback on this implementation or other solutions will be appreciated!
To address this issue, it seems that setting up a global constant accessible to all Apex classes would be hugely valuable in making sure that picklist values and record types (and other items) are consistently referenced in Apex.
For example, if I'm trying to set the Status of a Contract to "Activated", I could write the code as follows:
myContract.Status = 'Activated';
This is great if it's the only place I ever deal with the Contract Status field. But what're the chances of that? Instead, how about writing the code as follows?
myContract.Status = ContractUtil.ACTIVATED_STATUS;
Now I don't need to worry about all the myriad places where I've hardcoded the status value. If I ever need to change the status, I can update the constant in
ContractUtil or look for all references to ContractUtil.ACTIVATED_STATUS.Here's the sample code for a generic SObject utility class:
/** * Utility class with supporting methods for * a SalesforceSObject. * * Examples of supporting methods include getting * a Record Type ID, getting an expected picklist * value for a particular field, and conversion * to/from other objects. */ public class GenericSObjectUtil { /** * TheStringvalue that * represents an activated status, which goes * into the Status field. */ public static final String ACTIVATED_STATUS = 'Activated'; /** * The expected default Record Type Name * for all users. */ public static final String DEFAULT_RECORD_TYPE_NAME = 'This SObject'; /** * TheStringvalue that * represents a draft status, which goes * into the Status field. */ public static final String DRAFT_STATUS = 'Draft'; /** * The map ofRecordTypeInfo* objects retrieved by describing the * SObject, keyed by the Record Type Name. * * This is stored to make *getRecordTypeIdmethod calls * more efficient. */ private static final MaprecordTypeInfosByName = Schema.SObjectType.Contract.getRecordTypeInfosByName(); /** * Retrieve the Record Type ID based on a * given Record Type Name. If no match is * found, then return null. * * @param name The name of the Record Type * @return The ID of the Record Type, * if found; null otherwise. */ public static Id getRecordTypeId(String name) { return recordTypeInfosByName.get(name).getRecordTypeId(); } // public static Id getRecordTypeId(String) } // public class GenericSObjectUtil
Note: I know the above code may be cut off a bit due to width limits in Blogger, but you should be able to copy and paste the code into a text editor to see the full structure if necessary.
What do you think? Feedback on this implementation or other solutions will be appreciated!
Wednesday, March 21, 2012
Installing PostgreSQL 9.1.3 on Mac OS X Lion
Unsatisfied and undaunted by the foreboding discussion on installation troubles ("PostgreSQL 9.1 Installer Fails on OS X Lion"), I decided to follow the official PostgreSQL instructions to install the software from source. All so that I could build Ruby on Rails apps to be deployed to Heroku.
Note: PostgreSQL 9.0.5 appeared to have been bundled with my Lion installation, as seen with
So, in short, here are the steps I followed to install PostgreSQL 9.1.3 on Mac OS X Lion 10.7.3 from the source code.
If all went well, you should see something like the screenshot below.
Finally, we can move on to the fun stuff!
Note: PostgreSQL 9.0.5 appeared to have been bundled with my Lion installation, as seen with
pg_config before installing 9.1.3. But I wasn't sure how well it worked since Apple provides zero documentation on this bundled installation, and initdb was not located in a known path.So, in short, here are the steps I followed to install PostgreSQL 9.1.3 on Mac OS X Lion 10.7.3 from the source code.
# Make sure you have the latest version of # GNU Make for Mac OS X. This can be downloaded # through Xcode 4.3 by installing the Command # Line Tools. # Download the source code from the PostgreSQL # website, and start this procedure in the # expanded directory containing the source files. ./configure make sudo make install # At this point, assuming installation was # successful, create a new user to serve as the # unprivileged user that will own the server # process. # Open System Preferences to create a new user. # New Account: Standard # Full Name: PostgreSQL Agent # Account name: postgres cd /usr/local/pgsql/ sudo mkdir data sudo chown postgres data sudo mkdir log sudo chown postgres log # At this point, we're done with configuration # and ready to start the server process. sudo su - postgres # The following commands will be run as the # PostgreSQL Agent user. cd /usr/local/pgsql/ bin/initdb -D data/ bin/postgres -D data/ >log/logfile 2>&1 & # To verify that the server is working properly, # let's create a test database and see whether # we can connect using the interactive terminal. bin/createdb test bin/psql test
If all went well, you should see something like the screenshot below.
Finally, we can move on to the fun stuff!
Labels:
Heroku,
install,
Knowledgebase,
PostgreSQL,
Rails,
Ruby
AppleScript to Copy Message to Clipboard in Outlook 2011
I've created an AppleScript for Outlook 2011 that will copy some key information as plain text and throw it on the clipboard! I expect this to save me a good deal of time in the months to come.
Now that we're using ServiceNow to record our interactions, I'm finding myself having to copy and paste lots of info from emails into plain text fields on an incident or task in ServiceNow.
Specifically, for diligent tracking of communications, I've been recording the following info:
The problem is that copying and pasting all that information into a template that I have to retype every time are all very time-consuming and mundane operations. This script allows me to get a well-formatted snip of the key information with a few simple clicks. Whew!
Oh, well, I guess I'll just take this as a fun exercise in learning AppleScript.
Now that we're using ServiceNow to record our interactions, I'm finding myself having to copy and paste lots of info from emails into plain text fields on an incident or task in ServiceNow.
Specifically, for diligent tracking of communications, I've been recording the following info:
- Subject
- From
- Sent
- To
- Cc
- Body
The problem is that copying and pasting all that information into a template that I have to retype every time are all very time-consuming and mundane operations. This script allows me to get a well-formatted snip of the key information with a few simple clicks. Whew!
EDIT: March 22, 2012
Okay, I've discovered a simpler alternative: Reply to the email you want to copy and then copy what you want. The automatically composed reply will contain the same header information that this script copies.Oh, well, I guess I'll just take this as a fun exercise in learning AppleScript.
Tuesday, March 20, 2012
Test Class Template for Apex Triggers
Having written a lot of test methods to validate trigger functionality, I've developed a general template for writing trigger-related test methods that hopefully encompasses all of the steps needed. I believe that there must be a generic template that can be applied to 99% of the trigger test cases out there, and I hope this is a good start in that direction.
Feedback on the template will be much appreciated.
@isTest
private class GenericTriggerTest {
/**
* Plain English description of what is being
* tested and why.
*/
public static testMethod insertSObjects() {
// Stage the test data.
// Set parameters and expectations.
// Which records am I using?
// What do I expect to change or see
// at the end of the test?
// Validate pre-conditions.
// Impersonate another user
// if appropriate.
//System.runAs(null);
System.assert(false, 'TODO');
// Start the test.
Test.startTest();
// Execute test operations.
// Stop the test.
Test.stopTest();
// Validate the results.
System.assert(false, 'TODO');
} // static insertSObjects()
/**
* Plain English description of what is being
* tested and why.
*/
public static testMethod updateSObjects() {
System.assert(false, 'TODO');
} // static updateSObjects()
/**
* Plain English description of what is being
* tested and why.
*/
public static testMethod deleteSObjects() {
System.assert(false, 'TODO');
} // static deleteSObjects()
} // private class GenericTriggerTestFeedback on the template will be much appreciated.
Labels:
Apex,
best practice,
Salesforce,
test,
trigger,
unit test
Friday, March 2, 2012
Installing Ruby 1.9.2 on Mac OS X Lion 10.7.3
A few simple steps to get Ruby 1.9.2 up and running on Mac OS X Lion 10.7.3:
Then, to use the newly installed version of Ruby (instead of the pre-installed version that came with Lion):
To summarize the results... Before installing Ruby 1.9.2:
After installing Ruby 1.9.2:
Thank you, Mike Clark, for pointing out that with Lion I now have to download Command Line Tools for Xcode in order to compile stuff.

Thank you, Ubuntu community, for giving instructions on how to compile programs from source code.
... And finally, with no further ado, I present: the rant behind this post!
Programming an application is supposed to be difficult and require significant thinking. Installing the compiler or interpreter or whatever package is necessary to run the code should be easy.
Maybe it's just me... but why in the world did it take me 2 hours and so much frustration to get Ruby setup on my Mac? Ruby's website says, "Compiling from Source is the standard way that software has been delivered for many, many years. This will be most familiar to the largest number of software developers." Thanks. For assuming incorrectly that I know how to "compile from source" and providing zero instructions for how to do that on my OS.
- Download Xcode from the App Store.
- Run Xcode and open the app's Preferences.
- Open the Downloads tab, then download and install Command Line Tools for Xcode.
- Download and expand Ruby 1.9.2 (stable) source from the official Ruby website.
- Launch the Terminal app.
- Change to the directory containing the expanded Ruby source code.
$ ./configure$ make$ sudo make install
Then, to use the newly installed version of Ruby (instead of the pre-installed version that came with Lion):
$ export PATH=/usr/local/bin:$PATHTo summarize the results... Before installing Ruby 1.9.2:
$ irb -v irb 0.9.5(05/04/13) $ ruby --version ruby 1.8.7 (2010-01-10 patchlevel 249) [universal-darwin11.0]
After installing Ruby 1.9.2:
$ ruby --version ruby 1.9.2p290 (2011-07-09 revision 32553) [x86_64-darwin11.3.0] $ irb -v irb 0.9.6(09/06/30)
Thank you, Mike Clark, for pointing out that with Lion I now have to download Command Line Tools for Xcode in order to compile stuff.

Thank you, Ubuntu community, for giving instructions on how to compile programs from source code.
... And finally, with no further ado, I present: the rant behind this post!
Programming an application is supposed to be difficult and require significant thinking. Installing the compiler or interpreter or whatever package is necessary to run the code should be easy.
Maybe it's just me... but why in the world did it take me 2 hours and so much frustration to get Ruby setup on my Mac? Ruby's website says, "Compiling from Source is the standard way that software has been delivered for many, many years. This will be most familiar to the largest number of software developers." Thanks. For assuming incorrectly that I know how to "compile from source" and providing zero instructions for how to do that on my OS.
Monday, February 27, 2012
Select All Visible or Read-Only Checkboxes in Field-Level Security
It looks like I'm not the only one who has historically wanted a way to mark all Visible or Read-Only checkboxes on the Field-Level Security page in Salesforce.
Fortunately, while Salesforce (hopefully) works to make this a standard feature, administrators can use the following bookmarklets on this Salesforce Hacks page:
Try it out with a profile in your org!
Fortunately, while Salesforce (hopefully) works to make this a standard feature, administrators can use the following bookmarklets on this Salesforce Hacks page:
- Mark all fields as visible
- Mark all fields as read-only
Try it out with a profile in your org!
Friday, February 24, 2012
What (Infomation) Technology Really Does
In an explanation to a colleague today about what I do at work, I caught myself saying, "I'm here to make life easier for other departments." While I'm just as much a sucker for warm-and-fuzzy as the next guy, I realized I was telling a white lie that only sounds uplifting from the outside. It's not that I have no intention of helping my colleagues and teammates at the office; it's just that the nature of my job, of information technology really has nothing to do with "making life easier".
If you're thinking that I'm crazy and confused, let me ask you: Does having computers, email, word processing and spreadsheet software or any other piece of technology really make your life easier? The answer for me is simply "no". None of it makes my life easier; all of the above simply raise expectations for my output at the office. If I had to crunch numbers by hand, maybe 20 calculations a day would be considered amazing. But if I'm crunching numbers with Microsoft Excel, 200 calculations a day could get me fired.
So, this was a little depressing to me... Until I thought about the bigger picture. Increasing productivity is the only way for us to raise the standard of living and the quality of life for you, me and everyone else. But I won't bore you by waxing poetic about world peace, food for all, etc.
All I wanted to say are really two things.
If you're thinking that I'm crazy and confused, let me ask you: Does having computers, email, word processing and spreadsheet software or any other piece of technology really make your life easier? The answer for me is simply "no". None of it makes my life easier; all of the above simply raise expectations for my output at the office. If I had to crunch numbers by hand, maybe 20 calculations a day would be considered amazing. But if I'm crunching numbers with Microsoft Excel, 200 calculations a day could get me fired.
So, this was a little depressing to me... Until I thought about the bigger picture. Increasing productivity is the only way for us to raise the standard of living and the quality of life for you, me and everyone else. But I won't bore you by waxing poetic about world peace, food for all, etc.
All I wanted to say are really two things.
- Information technology is here to increase productivity, not to make work cushier.
- My work as an IT professional is to improve my organization, my country, and... you get the idea.
Wednesday, January 25, 2012
Simple Method to Identify Blank Fields in Visualforce
I've often wondered why there is no ISBLANK() equivalent in Apex for developers to use when validating a Visualforce page. Maybe Salesforce always intended for developers to use the
The problem with relying on field settings, Visualforce
To help this process along, I modularized the concept of ISBLANK() in the simple instance method below.
With this simple method (or variations thereof), developers can iterate through a
required attribute for the standard Visualforce components.The problem with relying on field settings, Visualforce
required attributes or object validation rules is that there is no consistent presentation of the error messages. For different sites, especially public ones, sometimes the validation errors need to be tailored specifically for the site.To help this process along, I modularized the concept of ISBLANK() in the simple instance method below.
/**
* Determine whether a given object is blank
* or not, in the same manner as the ISBLANK()
* Visualforce function.
*
* @param o The object to examine
* @return Whether the object is blank
*/
public Boolean isBlank(Object o) {
return o == null;
} // public Boolean isBlank(Object o)With this simple method (or variations thereof), developers can iterate through a
List<Schema.SObjectField> of required fields and use controller.isBlank(Object) to validate the fields.for (Schema.SObjectField field : requiredFields) {
if (isBlank(application.get(field))) {
addRequiredFieldError(field);
isValid = false;
} // if (isBlank(application.get(field)))
} // for each Schema.SObjectField in requiredFields
Monday, January 16, 2012
What is an enrollment product in higher education?
When it comes to enrollment management, I hypothesize that institutions in the education industry share many common administrative concerns as companies in other industries.
But one big difference I see between education institutions and other companies is how the above questions are answered. While other companies usually answer Questions 1 and 3 above with dollar amounts, education institutions (from the enrollment perspective) answer with student headcount or with class enrollments.
This may seem like a trivial distinction, but how useful would it be for Apple to announce their forecasts using the numbers of iPhones, iPads and MacBooks it plans to sell in the next year? Or worse yet, what if Apple simply said it would sell 1,000,000 products this year? And how would Apple's Marketing department calculate ROI for its myriad campaigns if this was the case?
Back in the world of education, I think there is a mental roadblock which prevents us from recognizing what our products truly are. Culturally, we, the administrators, think highly of education (which we should). But perhaps we've become ignorant of the underlying business model for education, which in the simplest sense revolves around the sale of products to customers. Put another way, the business model revolves around the enrollment of students in classes.
Let's explore the potential similarities and see whether they are legitimate.
We have recruiters, enrollment coaches and advisers who work with students to identify which courses to take to best meet their career aspirations and personal goals. Once the courses are identified, our staff help students to register for classes, thereby reserving their seats in the upcoming term. Once the add/drop period ends for a term, the enrollments are "closed" and invoices are sent to the students.
Other companies have salespeople working with customers to pick the most suitable products for each customer. Once customers decide to buy, they put in orders that are fulfilled in lots, resulting in assets (and associated invoices) delivered to each individual customer.
So, how different is an education institution from other companies? And if it's not that different, could adoption (or at least reconciliation) of real sales terminology be the way for an education institution to start answering the tough questions directly with real dollar amounts?
Note: I speak largely from my own experiences, and I recognize that there are other institutions which are already far along on the path of leveraging the concept of products in their CRM operations.
- What's our forecast for the next quarter or year?
- How much are we allocating to Marketing?
- What's our historical ROI on past campaigns? And what are we expecting for new Marketing campaigns this year?
But one big difference I see between education institutions and other companies is how the above questions are answered. While other companies usually answer Questions 1 and 3 above with dollar amounts, education institutions (from the enrollment perspective) answer with student headcount or with class enrollments.
This may seem like a trivial distinction, but how useful would it be for Apple to announce their forecasts using the numbers of iPhones, iPads and MacBooks it plans to sell in the next year? Or worse yet, what if Apple simply said it would sell 1,000,000 products this year? And how would Apple's Marketing department calculate ROI for its myriad campaigns if this was the case?
Back in the world of education, I think there is a mental roadblock which prevents us from recognizing what our products truly are. Culturally, we, the administrators, think highly of education (which we should). But perhaps we've become ignorant of the underlying business model for education, which in the simplest sense revolves around the sale of products to customers. Put another way, the business model revolves around the enrollment of students in classes.
Let's explore the potential similarities and see whether they are legitimate.
- Student vs. Customer
- Enrollment vs. Sale
- Course vs. Product
- Class vs. Lot
- Seat in a class vs. Asset
We have recruiters, enrollment coaches and advisers who work with students to identify which courses to take to best meet their career aspirations and personal goals. Once the courses are identified, our staff help students to register for classes, thereby reserving their seats in the upcoming term. Once the add/drop period ends for a term, the enrollments are "closed" and invoices are sent to the students.
Other companies have salespeople working with customers to pick the most suitable products for each customer. Once customers decide to buy, they put in orders that are fulfilled in lots, resulting in assets (and associated invoices) delivered to each individual customer.
So, how different is an education institution from other companies? And if it's not that different, could adoption (or at least reconciliation) of real sales terminology be the way for an education institution to start answering the tough questions directly with real dollar amounts?
Note: I speak largely from my own experiences, and I recognize that there are other institutions which are already far along on the path of leveraging the concept of products in their CRM operations.
Labels:
architecture,
best practice,
CRM,
higher education,
marketing,
ROI,
Salesforce
Friday, January 13, 2012
What is a program in higher education?
As system maintenance becomes more difficult, and as user dissatisfaction with reporting grows, I've been thinking a lot about one of the most controversial 4-word questions in higher education: "What is a program?"
I still remember some of the answers from when I first asked the question on Chatter last October.
Unfortunately, we never reached a conclusive answer. And the systems architect in me wanted a firm definition of a "program" in higher education so that I could help to design a system that best fit our business processes. Argh! "Frustrated" only begins to describe how I felt at the time.
But now, I have a different take on the question, with much less frustration.
For starters, let me back up and say something about our industry. I think the notion that higher education is somehow inherently different from all of the other industries out there has made me blind to the broader definition of the word "program". People in higher education can elevate and romanticize the industry to the point where colleagues will frown upon the application of business terminology (like "programs") to our operations. By removing the higher education context from the word, I feel like my grasp of "programs" has improved by leaps and bounds.
Let's jump back start with a program in higher education and see if we can discern any important characteristics.
All programs in higher education seem to have the following:
What's so special about this notion of a program is that this concept is not rigidly constrained by the generic, systematic definition (to which there are currently many exceptions) at our institution that programs are defined by distinct combinations of degree, major, and concentration.
And the real value of this discussion may be simply acknowledging that creating, changing, splitting, merging and retiring programs are more parts of an art than a science, which is how I imagine programs are treated in other industries like retail and fitness.
To give this concept a test drive: How may the different notions of a "program" play out in the following two scenarios?
In our CRM system (Salesforce), should the program records match 1-for-1 those in our student information system?
Again, in our CRM system, should the program records match 1-for-1 those in our student information system?
I still remember some of the answers from when I first asked the question on Chatter last October.
- "I would define a program as a sequence of college level courses (credit bearing) that lead a student to a certificate, degree, or diploma."
- "I think specializations would be subcategories or tags within programs. I don't see Fast-Track degrees as separate programs. To me, that is similar to online vs. on-campus delivery. Same program, different delivery methods."
- "I think what makes one program different from another is its required content. FT requires a certain and specific content (in a specific order even), so I would categorize it as a different program. However, online v on-ground wouldn't be a different program. Specializations have specific required content, so even though they don't end in different degrees, I would probably classify them as different programs."
Unfortunately, we never reached a conclusive answer. And the systems architect in me wanted a firm definition of a "program" in higher education so that I could help to design a system that best fit our business processes. Argh! "Frustrated" only begins to describe how I felt at the time.
But now, I have a different take on the question, with much less frustration.
For starters, let me back up and say something about our industry. I think the notion that higher education is somehow inherently different from all of the other industries out there has made me blind to the broader definition of the word "program". People in higher education can elevate and romanticize the industry to the point where colleagues will frown upon the application of business terminology (like "programs") to our operations. By removing the higher education context from the word, I feel like my grasp of "programs" has improved by leaps and bounds.
Let's jump back start with a program in higher education and see if we can discern any important characteristics.
All programs in higher education seem to have the following:
- Eligibility criteria: Who is eligible to be considered for entry into the program? For example, to be eligible for a master's degree program, an individual should currently hold a bachelor's degree.
- Entry procedure: If eligible, how does a person enter the program? Usually, a person enters a program by submitting an application for admission and then being accepted after a formal review of that application.
- Curriculum: Once in the program, what courses is a student expected to take?
- Services and perks: While in the program, what services or perks can a student expect to receive or have access to?
- Maintaining program membership: What does a student have to do to remain in a program? For example, if a student does not complete a program within 7 years then that student is effectively out of the program and must apply again for re-entry.
- Completion criteria: What does a student have to do to complete the program? Usually this involves passing all of the courses in a prescribed curriculum.
- Reward for program completion: What does a student get for completing a program and graduating? In higher education, a graduate typically receives a combination of degree, major, minor, concentration and specialization from the institution to attest that the graduate now holds certain level of skill and knowledge within a field of expertise.
What's so special about this notion of a program is that this concept is not rigidly constrained by the generic, systematic definition (to which there are currently many exceptions) at our institution that programs are defined by distinct combinations of degree, major, and concentration.
And the real value of this discussion may be simply acknowledging that creating, changing, splitting, merging and retiring programs are more parts of an art than a science, which is how I imagine programs are treated in other industries like retail and fitness.
To give this concept a test drive: How may the different notions of a "program" play out in the following two scenarios?
Scenario 1: Doctoral program with specializations
Historically, we have had a doctoral program with specializations that was only ever tracked as a single program record in our student information system. However, Marketing would typically promote the handful of specializations as if they were separate programs on our website, because the target audiences were different enough and the rewards were different enough (in the eyes of our students).In our CRM system (Salesforce), should the program records match 1-for-1 those in our student information system?
Scenario 2: Doctoral program with varying requirements
We also have a doctoral program that has slightly different curricula for students entering with a bachelor's degree compared to students entering with a master's degree. Historically and in the near future, the plan would still be to market this program as a single program on our website. But on the back end, the registrar's office created two separate programs in our student information system in order to better track our applicants and students.Again, in our CRM system, should the program records match 1-for-1 those in our student information system?
Labels:
architecture,
best practice,
definition,
higher education,
program
Thursday, January 5, 2012
NO-SOFTWARE Salesforce Tutorial: Internal Web Service Callouts
Another developer recently talked about a situation where an Apex web service needed to be exposed to internal, authenticated users in order to work around a DML limitation.
The concept behind the workaround was very intriguing, and after playing around with some Apex code I came up with a tutorial that I hope others may find useful: NO-SOFTWARE Salesforce Tutorial: Internal Web Service Callouts
The key concepts demonstrated in the tutorial include:
The concept behind the workaround was very intriguing, and after playing around with some Apex code I came up with a tutorial that I hope others may find useful: NO-SOFTWARE Salesforce Tutorial: Internal Web Service Callouts
The key concepts demonstrated in the tutorial include:
- Generating a WSDL from a global Apex class
- Generating Apex from a WSDL created from a global Apex class
- Using the
UserInfo.getSessionIdmethod to make callouts to internal web services as an authenticated user
Thursday, December 29, 2011
Last Modified Date Rounding to Whole Seconds
The
A video demonstrating the behavior: http://www.youtube.com/watch?v=IyRd1woiS24
It seems that Salesforce only includes the year, month, day, hour, minute and second when stamping the Last Modified Date. The millisecond value is simply zeroed out without any rounding. So, if a record was actually modified at 12/29/2011 12:31:20.578, Salesforce will stamp the record as being modified at 12/29/2011 12:31:20.000.
Okay, so there's a tiny discrepancy. I mean, we're talking tiny differences here. The million-dollar question: Why do I care?
I care because this little quirk caused my unit test to fail over 80% of the time due to the fact that comparing the Last Modified Date to
The oddity with Last Modified Date is not an impossible problem to work around, but should I really be working around it? What does this mean for test-driven development that Apex is supposed to enable?
DateTime class in Salesforce has precision down to the order of milliseconds, as implied by the getTime method which "returns the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this DateTime object." Since the Last Modified Date field is a Date/Time field, one would assume that the Last Modified Date includes the millisecond value when stamping a record, right? Alas, this is not the case.A video demonstrating the behavior: http://www.youtube.com/watch?v=IyRd1woiS24
It seems that Salesforce only includes the year, month, day, hour, minute and second when stamping the Last Modified Date. The millisecond value is simply zeroed out without any rounding. So, if a record was actually modified at 12/29/2011 12:31:20.578, Salesforce will stamp the record as being modified at 12/29/2011 12:31:20.000.
Okay, so there's a tiny discrepancy. I mean, we're talking tiny differences here. The million-dollar question: Why do I care?
I care because this little quirk caused my unit test to fail over 80% of the time due to the fact that comparing the Last Modified Date to
DateTime.now() was producing unexpected results. For example:- At the beginning of the test method, DateTime.now() returned 12/29/2011 4:00:01.500.
- At the end of the test method, the updated record's Last Modified Date shows 12/29/2011 4:00:01.000.
System.assert(record.LastModifiedDate >= testStartDateTime)fails unexpectedly and inconsistently.
The oddity with Last Modified Date is not an impossible problem to work around, but should I really be working around it? What does this mean for test-driven development that Apex is supposed to enable?
Thursday, December 22, 2011
Org ID Automatically Replaced in Sandboxes
I discovered something that made my blood run cold today: The
My
The obvious question: How does something so simple fail in a sandbox org?
The surprising answer: When a sandbox is created or refreshed, Salesforce automatically does a search and replace and replaces the production org ID with the sandbox org ID. Once a sandbox is created or refreshed, a single line of code changes in the above class:
This tiny, almost unnoticeable change has been screwing everything up for a long time, and its discovery also explains why we would periodically get strange data in production and also strange responses in our sandboxes.
I wish I had known about this earlier, but who would've guessed? Anyhow, the fix that I've implemented (and confirmed by creating a new sandbox) is to split the ID into 3 parts when assigning it to the constant.
Amazing... the things one discovers in the worst possible ways...
OrganizationInfo.isProduction method I was relying on in Apex to communicate to the correct web service endpoint was returning true in my sandbox orgs.My
OrganizationInfo class is super simple, created as suggested in a comment on the IdeaExchange (Determine sandbox name vs production from apex). Shown below for reference:public class OrganizationInfo {
/**
* The Organization ID of the production org.
*/
private static final Id PRODUCTION_ID =
'00DA0000000Kb9R';
/**
* Determine whether the current org is a
* production org or a sandbox org,
* based on whether the Org ID matches the
* production App org's Org ID.
*
* @return Whether the org is Production
*/
public static Boolean isProduction() {
return UserInfo.getOrganizationId()
== PRODUCTION_ID;
} // public static Boolean isProduction()
} // public class OrganizationInfoThe obvious question: How does something so simple fail in a sandbox org?
The surprising answer: When a sandbox is created or refreshed, Salesforce automatically does a search and replace and replaces the production org ID with the sandbox org ID. Once a sandbox is created or refreshed, a single line of code changes in the above class:
private static final Id PRODUCTION_ID =
'00DZ000000056dv';This tiny, almost unnoticeable change has been screwing everything up for a long time, and its discovery also explains why we would periodically get strange data in production and also strange responses in our sandboxes.
I wish I had known about this earlier, but who would've guessed? Anyhow, the fix that I've implemented (and confirmed by creating a new sandbox) is to split the ID into 3 parts when assigning it to the constant.
private static final Id PRODUCTION_ID =
'00DA0' + '00000' + '0Kb9R';Amazing... the things one discovers in the worst possible ways...
Discrepancies in Reports and Report Actions
I learned an interesting thing about reports today: What's displayed in a report can be different from what gets exported or from what gets added to a campaign. Let me try giving an example to clarify my statement.
Here's what we expect to happen:
Here's what could actually happen:
The cause of this behavior is that when an action is performed on a report, namely Add to Campaign or Export, the report is run again in the background to as part of performing the action. In other words, what we see on the report before we export or add to a campaign can be thought of as a "preview" of what would happen after we perform an action.
In most cases this is probably a non-issue, but I thought the phenomenon was worth noting in the off case that someone is perplexed by an odd discrepancy between a report and a campaign member list or an export file.
Here's what we expect to happen:
- Run a Leads report.
- Review the data on the report. Let's say that the report shows 3 Leads.
- Click Add to Campaign. The 3 Leads we saw are added to a campaign.
Here's what could actually happen:
- Run a Leads report.
- Review the data on the report. Let's say that the report shows 3 Leads.
- Click Add to Campaign. Four (4) Leads, not 3 Leads as we just saw, are added to a campaign.
- Return to the report. 4 Leads are now shown, instead of 3.
- Click Export and complete the export. Five (5) Leads, not 4 Leads, are exported
The cause of this behavior is that when an action is performed on a report, namely Add to Campaign or Export, the report is run again in the background to as part of performing the action. In other words, what we see on the report before we export or add to a campaign can be thought of as a "preview" of what would happen after we perform an action.
In most cases this is probably a non-issue, but I thought the phenomenon was worth noting in the off case that someone is perplexed by an odd discrepancy between a report and a campaign member list or an export file.
Sunday, December 18, 2011
ApplyYourself Hack to Mass Update Choice Group Values
How annoying is it that there is no easy way to mass update choice group values in ApplyYourself? All mass updates have to be sent to an account manager, who then uses some magical tool to make the changes that should've taken 1 minute for an administrator to complete.
For a change that I needed to make immediately, outside of business hours, I had to come up with an alternative: ChoiceGroupUpdateHack.js
When this script (making sure the code is prefixed with
To use the hack UI, all one has to do is paste new values directly from Excel into the textarea element and then click the button!
The Excel spreadsheet should be formatted as it is exported from ApplyYourself. The spreadsheet should include the following columns, in order:
Although this hack took 3 hours to develop, the ability to mass update choice groups on demand autonomously is priceless to me.
Note: If ApplyYourself starts throwing bizarre errors that don't make sense, another hack may be necessary to clear the choice group before loading the new values: ChoiceGroupClearHack.js
This hack was validated in Safari 5.1.2 and in Google Chrome 16.0 on Mac OS X Lion.
For a change that I needed to make immediately, outside of business hours, I had to come up with an alternative: ChoiceGroupUpdateHack.js
When this script (making sure the code is prefixed with
javascript:) is entered into the address bar and executed, a small textarea element is created at the top of the page along with an Update Values button.To use the hack UI, all one has to do is paste new values directly from Excel into the textarea element and then click the button!
The Excel spreadsheet should be formatted as it is exported from ApplyYourself. The spreadsheet should include the following columns, in order:
- Choice Value
- Choice Code
- Choice Order
- Header: "Yes" or blank
- Related Value
- Inactive Date: MM/DD/YYYY
Although this hack took 3 hours to develop, the ability to mass update choice groups on demand autonomously is priceless to me.
Note: If ApplyYourself starts throwing bizarre errors that don't make sense, another hack may be necessary to clear the choice group before loading the new values: ChoiceGroupClearHack.js
This hack was validated in Safari 5.1.2 and in Google Chrome 16.0 on Mac OS X Lion.
Friday, December 16, 2011
ApplyYourself Hack to Use Free-form Text Filters for Choice Group Fields
I discovered an interesting bug in ApplyYourself that makes it possible to do something that should've been standard functionality: Setup normal text filters using the Contains operator with Choice Group fields.
Imagine trying to get a list of all records that have Program value containing the word "Bachelor" when your Program field is setup as a Choice Group with over 100 options. The standard query interface forces you to use the following filter:
Or, you may have smartly added "Bachelor" as an extra value to the associated Choice Group so that you can select that single value when using the Contains operator.
However, both of these methods are annoyances. What if I wanted to query something on the fly with a value that I haven't predicted to need before?
The hack workaround or solution is simpler than both alternatives:
Imagine trying to get a list of all records that have Program value containing the word "Bachelor" when your Program field is setup as a Choice Group with over 100 options. The standard query interface forces you to use the following filter:
- Program In this List ... (manually selecting every singe value using the tiny 3-line picklist)
Or, you may have smartly added "Bachelor" as an extra value to the associated Choice Group so that you can select that single value when using the Contains operator.
However, both of these methods are annoyances. What if I wanted to query something on the fly with a value that I haven't predicted to need before?
The hack workaround or solution is simpler than both alternatives:
- Setup the filter with the desired field and the Contains operator with any value at all.
- Click save and run.
- Click the Back button in your browser, not in ApplyYourself. The picklist will have now magically turned into a free-form text field!
Monday, December 12, 2011
Problem with Surveys Asking for "Agree" or "Disagree" Using Radio Buttons
I filled out a short survey today (which I appreciated for being short) that asked me to assess my satisfaction for an event I recently attended. What's notable about the experience was that just before I was about to click the Submit button I decided to check my 3 responses one more time. I was very glad I checked, because I realized that all of my responses were the exact opposite of what I intended, and so I corrected each response before making my final submission.
The problem: I had selected "disagree" for every item with which I agreed and vice versa for ones with which I disagreed.
How often do people encounter surveys that list a bunch of statements and then give radio buttons for indicating one of the following (or similar) sentiments:
I think that based on each person's individual experiences, the person may assume that "Strongly Agree" either always falls on the left or always falls on the right side of the response matrix without pausing to actually read the survey. If the respondent is in a hurry to complete a survey, are the differences between the following two screenshots really all that apparent?
To reduce the chance of survey results being invalidated by responses that are completely wrong because the person intended the response on the other end of the spectrum, picklists may be used in place of radio buttons. I'll outline a few reasons why I think picklists are the better choice.
When a user tabs to the input and when the input has focus, the user can:
In my mind, this makes surveys easier to fill out in less time, which should increase the response rate by telling users that a survey would only take 1 minute instead of 2.
The problem: I had selected "disagree" for every item with which I agreed and vice versa for ones with which I disagreed.
How often do people encounter surveys that list a bunch of statements and then give radio buttons for indicating one of the following (or similar) sentiments:
- Strongly agree
- Agree
- Neutral
- Disagree
- Strongly disagree
I think that based on each person's individual experiences, the person may assume that "Strongly Agree" either always falls on the left or always falls on the right side of the response matrix without pausing to actually read the survey. If the respondent is in a hurry to complete a survey, are the differences between the following two screenshots really all that apparent?
To reduce the chance of survey results being invalidated by responses that are completely wrong because the person intended the response on the other end of the spectrum, picklists may be used in place of radio buttons. I'll outline a few reasons why I think picklists are the better choice.
1. Picklists force people to read.
When a user is confronted with a picklist that starts with an option like "--Select a Response--", he or she must read all of the picklist options in order to pick the right value.2. Keyboard shortcuts make picklists more usable.
Imagine a standardized picklist that has the following options:- Agree
- Agree, Strongly
- Disagree
- Disagree, Strongly
- Neutral
When a user tabs to the input and when the input has focus, the user can:
- Press A once for "Agree";
- Press A twice for "Agree, Strongly";
- Press D once for "Disagree";
- Press D twice for "Disagree, Strongly"; or
- Press N for "Neutral".
In my mind, this makes surveys easier to fill out in less time, which should increase the response rate by telling users that a survey would only take 1 minute instead of 2.
Subscribe to:
Posts (Atom)





