Sunday, October 13, 2013

Tasks

One of the Occupational Psychologist's team has left and has been replaced by a new person. Fortunately the integration of this new person with my management program is going very smoothly (as opposed to the one who is leaving who tended not to utilise it very well) and I am slowly getting feedback via the OP on new ideas to implement.

Friday's meeting centered around tasks: apparently the new person wanted to insert into the program a reminder to call someone in a few days time. Although in a sense, this capability exists within the program, the existing function was not really meant for this and certainly doesn't display a reminder.

At first, I suggested that they use Outlook to provide the reminder: why re-invent the wheel? But it turns out that they don't use Outlook very much in the office - they use Gmail combined with the management program. So I had to provide capability. The actual idea of tasks didn't seem to be a problem - this can easily be implemented with a database table of tuples consisting of a few fields, including the date and time of when the reminder has to appear - but what seemed problematic was how to pop the reminder up at the correct date and time.

Originally I considered using an external program which would display the reminder; its execution would be triggered by the management program adding an entry to the Windows scheduler. As I'm not familiar with the interface to the scheduler, this idea had to be put to one side. For a while I considered using a separate thread within the program, but later on I realised that this too was overkill. In the end, I implemented a simple scheme with a timer: every five minutes the timer wakes up and checks whether there are any tasks for the current user whose date and time have passed; if so, display the task non-modally. It is true that this means that the program is polling the database, whereas on the surface the Windows scheduler and thread approaches simply 'wake up' at the correct time. After thinking about this, I realised that these approaches too use polling; as far as the user is concerned, there's no polling but the system itself also uses a timer exactly in the same way that I'm doing. In other words, it's implicit polling as opposed to explicit polling.

Surprisingly, the hardest part of implementing tasks was recording for which date and at what time the task was set. I had naively thought that the TDateTimePicker component of Delphi would do this, but it turns out that I needed two such components: one for date and one for time. I also had great difficulty in getting the time from the component into the database as well as reading back a date/time combination into two components. Eventually I used the following code....

Saving the date and time values in one database field (edDate is the component showing the date, edTime is the component showing the time)
decodedate (edDate.Date, y, m, d); s:= inttostr (y) + '-' + inttostr (m) + '-' + inttostr (d) + ' '; decodetime (edTime.Time, y, m, d, ms); s:= s + inttostr (y) + ':' + inttostr (m); qInsert.parambyname ('p3').asstring:= s;
Reading the value back from the database and displaying it in two components
dt:= qGetTask.fieldbyname ('curdate').asdatetime; decodedate (dt, y, m, d); edDate.Date:= encodedate (y, m, d); decodetime (dt, y, m, d, ms); edTime.time:= encodetime (y, m, d, ms);
Once I had developed a method of reliably saving and restoring the date/time combination, I could write the timer code which pops the task up. This was actually very easy. So in the end, the part which most worried me in advance turned out to be easy, and the part which I assumed to be easy turned out to be hard! 

The contents of a task are minimal at the moment; they are sufficient for the purpose, but I have no doubt that additions will be required. Such additions don't bother me as they only affect data insertion and retrieval; they won't affect the date/time parts.

Another germ of an idea which popped up (sorry about the pun!) during our discussion was the possibility of my program actually downloading letters from Gmail! Let's say that we have sent a letter to contact A (the letter was sent from the program so we have the complete text) and contact A replies. The reply would arrive in the Gmail inbox and my program will be blissfully unaware of this. My current idea would be for the program to scan the inbox and download letters which have been sent from addresses which are known to the program. Once downloaded, the contents will be stored in the database and linked to the contact, so we will be able to see the complete conversation. I have written code which accesses Gmail's sent items folder so this part isn't too problematic; I will have to consider how to download letters only once. Should such a program run only once a day and check the previous day's mail? Maybe Gmail assigns each mail in the inbox an id number and I can use this to my advantage.

Whilst I was implementing the 'add task' code, I noticed that there was a routine for loading comboboxes with the contents of a database table that I had written and included in at least ten different forms. It didn't take much to turn this routine into a library procedure and simply call it from all those different forms. This is a minor example of refactoring; every now and then I find such examples in my code and improve them. The management program is a fine example because it is definitely the largest program (by far) that I have ever written, although it's not complicated: it's composed of many simple (and very similar) forms.

No comments: