Sunday, November 28, 2010

Sumptuous Sunday 2

This really should be 'Sumptuous Saturday' as I'm not doing any cooking today....

For Friday night, I prepared a beef stew, basically cholent, in the slow cooker. Unfortunately, this was not a success. Although each ingredient was cooked, the dish as a whole didn't come together. I think that I'm going to abandon this dish and go back to the simple minced meat casserole which I used to cook on the gas.

On Saturday, I cooked a whole chicken in the slow cooker. As ever, I was dubious about placing a whole chicken into the cooker with no liquid, but the results were astounding. Along with the chicken, I placed potato and onion slices in the pot. These came out very nicely cooked and next time, I'll put in more potato, in larger pieces. Although I used some celery as a spice, this wasn't very noticeable in the final result. The chicken was cooked for four hours on high.

I decided to find out what 'high' and 'low' really mean. 'High' is 300 degrees Fahrenheit, which translates to 149 degrees Centigrade; 200 degrees F is 93 degrees C. Both are somewhat higher figures than I had expected. I wonder what the advantages are of cooking a whole chicken at 149 degrees for four hours as opposed to roasting it for two hours at 175 degrees. I suppose that it depends on how much electricity (how many watts) the slow cooker and oven are using. Finding this out for the slow cooker should be easy, but I don't know whether I can find out the oven's requirements.

Thursday, November 25, 2010

Algorithm for quickly drawing activity networks

Caveat: this post is probably going to be long and tedious; it certainly will be boring. If you're not interested in drawing activity networks (the first step in CPM or PERT analyses in project management), then don't read on. You have been warned....

The exam in Project Management is about two weeks in the future. At the moment I am somewhat under-employed at work, so I am using the time to go over exam questions. I have noticed that it takes me about three or four attempts to draw an activity network, time which could be better spent elsewhere. I thus need a method which will allow me to draw the network correctly on the first attempt.

After googling several sites, I finally found what I was looking for. As Heriot Watt displays the data in a slightly different manner, I modified the algorithm displayed therein. After playing with the algorithm a few times, I discovered that I can significantly speed it up whilst not losing any accuracy. So I'm going to present my version of the algorithm here - all errors are thus mine!

Here is the data:

Table 1: Estimated activity times for works to be completed by Contractor X
ActivityOptimistic time (weeks)Likely time (£)Pessimistic time (weeks)
A–B123
B–C369
B–H456
C–D81012
D–E345
D–F234
E–G345
F–J123
G–H135
G–I468
G–K135
H–L123
I–L123
I–M4710
J–K123
K–M123
L–M234
M–N234
As there are optimistic and pessimistic times quoted in the data, one can assume that this is going to be PERT analysis. This makes no difference when drawing the activity network.

Here's the algorithm:
repeat
1. List all the current nodes (A, B, C, etc. This list will shorten during execution)
2. Cross off each destination node which can be reached by a node on the list (if the activity is AB, then one crosses B off the list)
3. Remove from the list all uncrossed nodes and put them aside
until the list is empty

So: the initial list is all the nodes: A, B, C, D, E, F, G, H, I, J, K, L, M, N.
First iteration: everything gets crossed off the list bar A (not surprisingly as this is the starting node).
Second iteration: everything gets crossed off the list bar B (as A is no longer on the list, no node has B as a destination node)
Third iteration: everything gets crossed off the list bar C
Fourth iteration: everything gets crossed off the list bar D (I warned you this would be tedious)
Fifth iteration: everything gets crossed off the list bar E and F
Sixth iteration: everything gets crossed off the list bar G and J
Seventh iteration: everything gets crossed off the list bar H, I and K
Eighth iteration: everything gets crossed off the list bar L
Ninth iteration: everything gets crossed off the list bar M
Tenth iteration: everything gets crossed off the list bar N

The result of the above is a grid with ten columns and three rows, like this:




EGHL
ABCDFJIMN
K


In practice, one doesn't actually draw the grid. Here one discovers that the algorithm isn't sufficient and that one needs two heuristics. When there are two (or even three) nodes in a column, how does one know their vertical placement? One heuristic which I developed is that the node with the most activities should be placed in the middle. If one considers H, I and J: H only leads to L, whereas I leads to L and M; K leads only to M. So I has to be in the middle. Why are H and K the way they are? Because G leads to H whereas J leads to K, and this is the easiest way of drawing the connections. As I leads both to L and M, L has to be "above" M. The second heuristic is that the final position of each node in the grid is dependent on the preceding and succeeding nodes and can only be determined when actually drawing.

Here's the activity network as given in the worked answer
Here, E is effectively in row 2 and F in row 3, as opposed to 1 and 2, but that's not important.

For reasons which I don't entirely understand, we are not allowed to use scrap paper during the exam; everything has to be written in the bound answers book, although scrap workings can be crossed out and labeled 'scrap'. Using this technique will save the amount of scrap working that I have to produce, and will naturally save time. Unfortunately, it's going to be a bit difficult copying the clean network from the scrap workings.

Wednesday, November 24, 2010

Thinking of templates

Gerard: What are you doing, Newman?
Newman: Thinking.
Gerard: Well, think me up a cup of coffee.
(from The Fugitive,  starring Harrison Ford and Tommy Lee Jones).

The Occupational Psychologist has upgraded a few of her office computers to Windows 7 and Office 2010, whereas I work with Windows XP and Office 2003. All of the programs which I have written for her work without change ... as long as they don't try to open Word and display data there. As it happens, most of them do have to open Word, which means that we have a serious problem.

I thought that maybe the automation interface had changed, but that seems not to be the case. I looked around on the internet for solutions; one suggested putting in a 'sleep' statement after the call to open Word, another suggested changing protections. Neither solution worked. Suddenly, out of the blue, a word popped into my head - templates. My programs are written to open Word using a certain template and Word 2010 doesn't know where to find that template.

I quickly wrote a small test program which opens a document and types a few words without specifying a template. After transferring the program to the OP's office, we ran the program successfully. In other words, my hypothesis about the non-functioning Word link was correct: Word doesn't know where to find the correct template.

Of course, knowing the solution to the problem doesn't always help in implementing that solution. I've sort of found the correct place in Word to define the templates, but the dialog box has changed from Office 2003, but aside from that, the text appears in small Hebrew letters which are hard to read, and often Microsoft speaks a special dialect of Hebrew which most people can't understand.

So: if anyone reading this is versed in using Word 2010 automation with templates, please let me know.

Update:
It's just as well that I wrote this blog in advance of publishing it. Via the help of the wonderful stack overflow site, I found the place where I can define the default location of templates in Word 2010. If anyone's interested, it's Files|Options|Advanced|File locations. The button which activates the file locations dialog is located at the bottom of a very long dialog box and is almost hidden away. No wonder I didn't find it before (especially as I was looking in the wrong place). Methinks Microsoft is trying to tell us something.

Tuesday, November 23, 2010

Copper socks 2

I discovered that one of the most popular posts that I have written was about copper socks. A few days after writing that entry, I felt that I might have been too enthusiastic, as I was having problems with the socks: they were too tight. I checked whether I had ordered the wrong size, but no, I had ordered 'large' (and being on the small size, my feet are naturally small). It might have been that my wife's laundering caused the socks to shrink slightly; there were no special washing instructions on the packet in which the socks came.

A month later I can say that my problems with the tightness was probably more due to me being unused to wearing shoes and socks after six months in sandals, although I still feel that the socks are a little tight. Until a few days ago, I couldn't wait to get home and take the shoes and socks off, but last night I felt quite comfortable in them.

As for my feet: the fungi seem to have disappeared, although the skin is very dry. I'm going to start rubbing in foot cream before putting on the socks in the morning to alleviate this problem.

I received another letter from Cupron the other day, telling me that they have copper pillowsheets (if I remember correctly) on sale somewhere in Israel. Although I'm pleased that they've started selling to their natural market, it wasn't a product in which I was interested, so I deleted the email.

The gravy boat

My logo states that the blog contains random comments from a working programmer, an amateur chef and a frustrated musician. The previous blog was from the frustrated musician and the one before that was from the working programmer, so I suppose that this blog has to be written by the amateur chef.

I didn't cook supper on Friday night; a few days previously, my 22 year old daughter said that she wanted to cook cottage pie for us. Her interest in cooking started when she was in Australia a few years ago and had to start looking after herself; she now avidly watches cooking programs on television (Gordon Ramsay is a particular favourite, although I'm not too sure how much a neophyte chef can learn from him). The pie itself was a bit watery; when I asked her about this, she said that she added water to the meat. ??? I asked myself.

On Saturday, I cooked again chicken breasts in the slow cooker. The supermarket didn't have the usual frozen chicken breasts so I had to make do with chicken shnitzel. This is basically the same, but the cuts are thinner and so cooked more quickly, a fact which I did not take into account. The sauce is counter-intuitive: mayonnaise, jam and mustard. I am tempted to add some peanut butter to this mix to see what effect that has.

Having started making sauces, I have been looking for the past month or so for a gravy boat in which to serve them; I've looked in several shops which sell kitchen utensils but without success.
During my searches, I also discovered what it's called in Hebrew, although I suspect that many people won't know the word (although it's derived from the word for sauce and thus immediately understandable). In the end, I started looking for a local supplier on the Internet and found one rapidly enough (the above picture is from their website). The postage is more expensive than the boat itself, but together they cost barely more than a kilo of beef, so I'm not complaining. It arrived in the post yesterday and today my wife will pick it up; on Friday, I'll serve something in it (although I haven't yet decided what to serve).

Monday, November 22, 2010

The road to Antibes

I wrote a little about this song a few days ago. Since writing, I have completed the arrangement, lowered the song's key so that I can sing it, recorded the vocal track and uploaded the song to Soundclick where it can be found in all its glory. Please give it a play.

A few words about the arrangement: when I first started sequencing the song, I gave it a fairly mundane introduction of alternating Em and Bm chords. Later on I changed this for a sequence Am|Bm7|C|Bm7|Am|Bm7|C|D, which then led into the Em verse.This introduction stayed for about three days, until suddenly I played a little oboe lick with a flattened fifth. This sounded so good that I had to start with this lick - upon which the rest of the introduction seemed out of place, so I dropped it. Starting in Am also reminded me very strongly of my song 'In silence' which effectively uses the same trick of starting in a different key from the song, so I transposed the intro down to Em.

I looked at the words which I had written on 15 December 1972; I remember being unsatisfied with them then but being unable to write anything better. Of the two extant verses, I kept the first four lines and threw away the rest, meaning that on Friday night I basically wrote all the  song's lyrics from scratch. They're still not particularly good but they're not that bad. The third verse references the jazz festival -
The roads of Antibes at festival time
Jazz percolates from the streets
The sounds of Antibes head straight for the heart
Arpeggios for free
Saxophones and oboes too
What's my wish? Well, here's a clue
Take me back
As it happens, the arrangement was featuring my two favourite wind instruments, the flute and the oboe, but if the lyrics are referencing the saxophone (surely the instrument most identified with jazz) then the arrangement too will have to feature a saxophone. So the flute became a soprano saxophone and all was well with the world again.

I always forget how to record my vocals in order to get a good result. The first recording on Saturday morning was fine from a technique point of view but did not sound good; no amount of effects (eq, reverb etc) could give me the sound that I wanted. So I sang the song again, very quietly, from the back of my throat. This produces a very warm sound which can be treated in order to create the final vocal.

Sunday, November 21, 2010

Improving the dual list box dialog

Database theories state that there are three types of relations: one to one, one to many, and many to many. The first two are easy to model, and the third is generally implemented by some kind of link table. This link table can either masquerade as a normal table, if it includes other information above the simple many to many link, or it can be degenerate, in which it includes solely a link to one table and a link to the other table.

In the programs which I have written for the Occupational Psychologist (OP), there have always been at least one such degenerate link table. I've been trying to think of several past examples, but they all require understanding of the kibbutz way of life to make sense, so I'll  give an example from the medical world. If a doctor suspects that a person is suffering from complaint A, then he will order a blood test which will include testing for A1 (say glucose), A2 (say urea) and A3 (creatinine). The doctor might order a blood test for set B which will include A1 as well as B1, B2 and B3. The many to many relationship is that one specific blood test (glucose) can appear in many 'blood test sets', and each 'blood test set' can include several individual blood tests.

When one of the sets (in this case, the individual blood test) contains only a small, finite number of members (let's say 30), I often implement this with a 'dual list box dialog'. This is one of the standard dialog boxes in Delphi which I have been using for years and looks like this


Over the years I have refined this dialog box to work in Hebrew and to load its items from database tables. The right hand list box would be populated by (in our example) the individual blood tests which have already been assigned to the blood test set, and the left hand list box would be populated by the tests which have not been assigned to this set.

A database table would be defined with two fields: one containing the key of each blood test set and one containing the key of the individual blood tests. Thus in the simple example given above, the link table would look like this (using the actual names of the entities as opposed to their key numbers; of course, in real life, the keys are used)


SetTest
AA1
AA2
AA3
BA1
BB1
BB2

Populating the right hand list box is easy: all one needs to do is write an SQL query which returns the tests when the set is A. Populating the left hand list box is less easy and for years I had been using the following method: iterate over a list of all the tests in the test table (ie A1, A2 etc), check whether the current value is in the right hand list box, and if it isn't, then add it to the left hand list box. Whilst this is clearly inefficient, I excused the inefficiency by noting that the list shouldn't contain more than 30 members.

When implementing this dialog box for the n'th time yesterday, it finally dawned on me that I could populate the left hand list box much more efficiently by means of a theta query : return only the values of the blood tests which aren't joined to any blood set test. This is easy to say but slightly difficult to define in SQL -
select items.name, items.id
from items
where not exists
(select 'x' from blood2item
where blood2item.item = items.id
and blood2item.blood = :p1)
'Blood2Item' is the name of the link table, 'blood' is the field holding the blood set key and 'item' is the individual blood test.

Another improvement suggested itself: the dialog box came with code which handles moving an item from one list box to the other -
procedure TDualListDlg.MoveSelected(List: TCustomListBox; Items: TStrings);
var
  I: Integer;
begin
  for I := List.Items.Count - 1 downto 0 do
    if List.Selected[I] then
    begin
      Items.AddObject(List.Items[I], List.Items.Objects[I]);
      List.Items.Delete(I);
    end;
end;
I had never really looked at this code, but yesterday I noticed that it moved an object, not just the name. Several months ago, I wrote about adding objects to a combo box; on the basis of this code I was able to improve my dual list box code when adding to the left hand list box
with qSrcList do
  begin
   params[0].asinteger:= akey;
   open;
   while not eof do
    begin
     srclist.items.addobject (fieldbyname ('name').asstring,
     tobject (fieldbyname ('id').asinteger));
     next
    end;
   close
  end;
Previously, when retrieving the value of an item, I would have to perform a table lookup to establish the value of an item's key from its name, but now the key has already been added to the list box and retrieving it means accessing the item's object value. The scales remaining in dstlist are ones which weren't there before we started, so add them to the 'blood2item' table

 with dstlist do
  for index:= 1 to items.count do
   with qInsert do
    begin
     close;
     parambyname ('p1').asinteger:= gid;
     parambyname ('p2').asinteger:= longint (Items.Objects[index - 1]);
     execsql;
    end;

I realise that this explanation is a bit muddled, but I'm fairly sure that anyone who uses this dual list box dialog will understand. The main thing is that I understand, so now I can improve all the previous uses of this dialog box and improve (once again) the efficiency of my programs.

Thursday, November 18, 2010

Alesis Q49

I haven't been producing any music at home for a long time. One reason is that my creative mind seems to be dwarfed by the intellectual need of my MBA courses, but the real reason is that I don't have the means to create music. My old MIDI keyboard controller simply will not connect to my computer via a USB interface. After successfully ignoring this fact for several months, I thought it about time that I do something about it, and so instructed my wife that the next time that she is in Tel Aviv, she should enter a certain musical instrument shop and buy me a keyboard.

She was in Tel Aviv on Monday and bought me an Alesis Q49 usb/midi keyboard controller, for the princely sum of about 650 NIS (about $180); this might seem a lot, but the keyboard is going to last for years. As its name hints, the keyboard has 49 keys, which makes it at least twice the size of my previous keyboard. I can put it on the computer desk in front of the monitor and play it there, but storing it is going to be a slight problem. The main thing is that the keyboard works perfectly with all my software; sometimes there are small problems in detecting the keyboard, but I'm learning to overcome those.

In honour of the new keyboard, I am in the process of resurrecting a very old song which I wrote in 1972 called 'The road to Antibes'. The song itself is not particularly special, which is probably why I haven't given it a second thought for the last 38 years. So what suddenly brought it to mind?

A few weeks ago, I decided to burn a compilation disk composed of ballads performed by King Crimson. Yes, them heavy people knew how to turn out a deft ballad. Listening to this to and from my studies reminded me what a deft hand Robert Fripp had (still has?) with the acoustic guitar. What jigged my memory was the song "Cascade and Cascade"; I was very enamoured of this song when it came out in 1970, so much so that a year or so later, I had the idea of creating a band whose musical direction would be similar to that of C&C, along with Sandy's "Who knows where the time goes" and "The sea" - lots of open guitar chords along with a sunny flute (true, there's no flute on the Sandy songs, but the guitar obbligatos could easily be transformed to warbling on the flute). The group would be called Antibes - because of the jazz festival held in the city on the French riviera (Côte d'Azur). It seemed the perfect sunny name for the sunny music that I had in mind.

The idea never reached fruition primarily because I didn't know anyone who played the flute! Somehow a female violinist was suggested, and I remember that one rehearsal was held with the violinist and a bassist (and me on acoustic guitar), playing my song "Sunday Rain", but nothing else came of this. I know that I wrote 'The road to Antibes' so that the group would have some material, but looking back on it now, TRTA was not really the right kind of song.

Exhuming the song, I performed a kind of Lennon-McCartney trick; one would bring the other an almost completed song, whereupon the other would change a chord here or there, add a middle section and possibly write the words for another verse. That's what I've done: almost automatically I changed the chords for one sequence, making it much more interesting. I have yet to add a middle section (which is sorely lacking) and I may well write another verse. I'm trying to give the song a sunny arrangement, but that seems more difficult than it sounds.

I wonder what it's like in Antibes.

How to save money when ordering books from abroad

Not living in Britain or anywhere else which has 'super saving shipping' or whatever it's called, ordering books from Amazon can be an expensive business. True, the books themselves are sold at reasonable discounts from their list price, but the shipping can be a killer.

For example, I have just been looking at the book 'Cider with Roadies' by Stuart Maconie, which would very much seem to be my kind of book. The list price is 7.99 GBP, and Amazon (in Britain) is selling it for 5.11 GBP, which works out to a 36% discount. Not bad ... until one considers how much the shipping to Israel costs. I don't know how they calculate the shipping: whether it depends on the weight of the items, their size, their cost or the day of the month. Anyway, almost completing my Amazon order with this book and a disk brought the shipping costs to 10.00 GBP! Assuming that the shipping cost for one book would be 5 GBP (not necessarily an assumption which holds), this means that I could purchase the book in Israel for 10.11 GBP, or 1.26 times its list price.

I don't know whether that book has been imported into Israel and distributed by the major book chain here, Steimatzky, but if it were, I imagine that the book would cost around 80 NIS, which works out at around 13.8 GBP at current exchange rates. So despite the hefty postage cost, it's still cheaper to order directly from Amazon.

Or so I thought until a few weeks ago, when I read on a blog discussing the fact that it's almost impossible to use an i-pad in Israel. Someone suggested ordering books from The Book Depository, which promises free delivery worldwide for all books. I checked into the site, saw that the above book was selling for 7.19 GBP and ordered it, paying by PayPal. I was not charged for postage, meaning that I bought the book with a 10% discount from its list price, but more importantly, at least 29% less than it would have cost from Amazon (depending on the postage, natch). Need I say more?

The only downside to this deal is that the Book Depository does not seem to sell DVDs nor musical disks, meaning that I will still have to order from Amazon and pay their inflationary postage prices whenever I want a non-book item. Consider the fact that Amazon started selling books.

Monday, November 15, 2010

Project Management lectures

As mentioned previously, last week there were three evening lectures held in the Project Management course, delivered by the course supervisor from Edinburgh. I am told that such lectures occur for each course about once every two years; out of the four courses that I have taken so far, three have had guest lecturers. After a quick check, I see that I wrote a year ago about attending the lectures for the accountancy course.

In my opinion, these lectures are a great help and those who don't attend can only blame themselves. The reason for their non-attendance is probably in my blind spot: maybe they have difficulty in understanding spoken English, and even more of a problem in understanding English spoken in a Scottish accent.

The PM exam seems to be more rigidly structured than the other subjects: the first question demands an OBS (organisational breakdown structure), the second is either CPM or PERT, the third is EVA and the fourth is a general essay question, normally about risk management. There are no multiple choice questions. This makes preparing for the exam somewhat easier than it might have otherwise been. As the lecturer pointed out, one only needs 50% in order to pass, and most numerically inclined people should have no problem in achieving the maximum marks from the CPM/PERT and EVA questions.

But of course we have to aim for the sky! The OBS question also should not be too hard, especially as we now know all the downfalls that await us. The lecturer had put at our disposal thirty six different files (I know - I printed them!) which give background and foreground material; one of these documents discusses where students went wrong in the exam. So I know, for example, that it is a very good idea to take several coloured pens with me into the exam in order to differentiate the types of links which I will have to draw in the OBS.

Possibly, revising for the exam will boil down to committing my thirteen pages of notes to memory (and two pages of those are CPM and PERT, for which I don't really need notes).

I wrote to the lecturer today, thanking him for the lectures. I noted that these lectures always have the same psychological affect on the students who participate: their self-confidence increases, and a certain amount of social binding occurs. Unfortunately, there is very little socialising in class and I rarely know anybody's name, so this affect is welcome. Actually, our regular lecturer asked in the first lecture for everybody to say their name and a few words about themselves, but social cretin that I am, most of what was said was quickly forgotten.

The Scottish lecturer several times used his flight back to Heathrow as an example for PERT: when El Al buys a landing window for a huge sum, it has to be confident that it can land 95% of its planes within that window. Planes landing outside of that window will mean fines for the company, an expense which has to be weighed up against the cost of widening the landing window. As it happens, the "plane missed its allocated landing slot! An air conditioning unit failed while the aircraft was still on the runway so there was a delay of about one hour at Ben Gurion. The pilot made up about half an hour en route but then we had to circle for over an hour at Heathrow in the ‘stacking queue’ resulting from extreme high winds over the South of England".

As a side affect of these lectures, I know see everything as a project!

Sunday, November 14, 2010

Sumptuous Sunday

One music blog to which I subscribe uses alliterative titles for the days of the week, eg Traditional Thursday. On that basis, today's entry will be Sumptuous Sunday, in which I discuss the various dishes which I cooked over the weekend.

The slow cooker was definitely the star this weekend. For Friday night, I prepared again chicken drumsticks and rice (10 drumsticks, a cup and a half of rice, 1 litre coca cola, one diced onion: place in slow cooker and cook for three hours on high, one hour on low), which my wife enjoyed. The children (now young adults) didn't like the way the rice turned out, so I won't cook this dish again for a Friday.

For lunch yesterday, I tried out a recipe which I had found on an Israeli slow cooking blog. From the supermarket, I bought a packet of frozen chicken breasts; once thawed, this revealed four breasts, which I sliced lengthways, making eight portions. The weight was around 1.2 kg. I diced an onion, mixed the pieces with two tablespoons of olive oil, and then spread the resulting mix onto the bottom of the slow cooker. On top of the onions I placed the chicken breasts, and on top of the breasts I smeared a mixture of mayonnaise and jam (two tablespoons each). The original recipe also called for another two tablespoons of mustard, but we didn't have any at home (I thought that we did have, but my wife threw the container out). I set the slow cooker to cook for four hours on low. I served the breasts with cabbage and my patent mashed potato.

The result was sublime. I had been worried about the lack of liquid available, but instead of having too much free liquid bubbling, the recipe produced a sauce of a nice consistency. Next time around, I'm going to reduce the amount of onion; it will be interesting to see what contribution the mustard makes to the sauce.

Friday, November 12, 2010

What are the 10 Songs that defined one's tastes?

I haven't written much about music lately, primarily because music has had to take a back seat in my life in the past few months, and there hasn't been any new music that has entered my life. In lieu of something more up to date, I include here a posting which I sent to the Richard Thompson mailing list a fortnight ago which answers the above question.

I suppose one has to define what a "song that defined one's taste" means before answering. I take this to mean the songs that made the biggest impression on me when I started listening to music "properly" (aged 13-14), which for me would be 1970/1 (I list the years below). Each song defines for me a genre (although I'm not so sure about King Crimson - VdGG meant and still means much more to me).

1. The Beatles - I want to hold your hand (1963)
2. The Beach Boys - God only knows (1966)
3. Fairport Convention - Who knows where the time goes (Unhalfbricking version) (1970)
4. Van der Graaf Generator - Refugees (1970)
5. Frank Zappa - Peaches en regalia (1970)
6. King Crimson - Epitaph (1970)
7. Randy Newman - I think it's going to rain today (1971)
8. Dave Evans - Grey lady morning (1971)
9. The Band - It makes no difference (1978)
10. Robin Frederick - Water falls down (2003)

Looking at this list today, I would probably substitute "Winter wine" by Caravan for "God only knows" - WW was my entree into Canterbury music.

Saturday, November 06, 2010

Trains and meals

The last few days have been very hectic, not really affording me the small amount of time necessary to write a few words here. Next week is going to be even worse: one evening, I will be the 'duty driver' for the kibbutz, from 5-10pm, and three evenings I will be attending lectures in Project Management, courtesy of a guest lecturer from the home university.

I am known as a vocal supporter of the Israeli train system; most of the time it's very good, but occasionally there are problems. On Wednesday I was due to make the familiar trip to Haifa Bay; when I went to buy the ticket, the station had mysteriously disappeared from the list of destinations available from the automatic ticket machine. When I bought my ticket from a human cashier, I was told that there were no trains north of Haifa. This happens for a few days every now and then, but this time I hadn't noticed and hadn't bothered to plan my trip for a day when there is no maintenance being performed on the line. Although this news disappointed me, I wasn't totally upset as this meant that I could catch an earlier train from Tel Aviv to Haifa and thus save twenty minutes which would be devoted to the problem of catching a bus from Haifa to the bay (normally this is two buses).

After buying a return ticket to Haifa and passing through the turnstiles, I prepared to wait the ten minutes until the train to Tel Aviv was due to arrive. I hadn't been there for more than a minute before an announcement came over the tannoy that the train was twenty minutes late. Everybody groaned. Whilst this wasn't much of a set-back to me (the train should arrive in Tel Aviv in time to make my normal connection), it was a problem for those who have to be at work by a certain time.

Eventually the train arrived and off we went. By the time we arrived at Lod, so many people had come aboard that the train operator had to make several announcements asking people to disembark as the train was too heavy and could not move. There are trains every few minutes from Lod to Tel Aviv so there was no real reason for people to get on our train, but even so it took about another ten minutes before sufficient people disembarked and the train could continue its journey.

A few minutes after arriving in Tel Aviv, a train arrived which took me to Haifa. This train was one of the modern design with electrical sockets distributed generously; as the train was barely occupied, this was an excellent opportunity to get some work done. I had barely set a foot outside the train station in Haifa when I noticed a minibus waiting for passengers to take further North. Whilst I had to wait a while for the minibus to fill, it took me reasonably quickly to within 400 metres of my destination - although traversing the packed streets of Haifa made me remember how much easier the train makes this trip.

I arrived at my destination about 25 minutes later than I would have arrived normally. Not too bad considering the disturbances.

Returning home, I left the office a bit early, but again was fortunate to arrive at the main road just in time for a minibus to stop and pick me up. Whilst this bus went back to Haifa, it did not go to the train station but rather what's called the 'upper city' (Haifa is built on the side of a hill), so I had to walk for about 15 minutes before arriving at the train station, just in time for a train back to Tel Aviv. Fortunately I knew where I was in Haifa, so it wasn't difficult to find my way to the station.

I decided a few days ago that today I would cook chicken drumsticks and prunes in the slow cooker. I literally jumped out of bed at 6:30am; put the drumsticks in the slow cooker, covered them with about a litre of coca cola, added a cup and a bit of rice, then added diced raw onion and diced prunes. I put the slow cooker on 'high' for two hours then lowered the heat to 'low' for another three.

I knew that the chicken would be ok but I wasn't sure how the rice would be. I had checked a recipe for slow cooked rice the day before with proportions 1:1 (ie one cup of rice/one cup of water) whereas normally I use 1:2. Here I was using a proportion of about 1:5! The result was fine: the rice was indeed more like a pudding than distinct grains, but that doesn't bother us. The onion and prunes weren't particularly evident, and the chicken was cooked to perfection. My wife gave it the thumbs up, and it's much easier cooking this dish in the slow cooker than in the oven. Another recipe for the book.

Tuesday, November 02, 2010

Sorting up and down

Back to basics today: the occupational psychologist (OP) wanted a report in which she could sort by each column, like in Excel. Clicking the first column would sort by this column, clicking the second would sort by the second column, etc. This isn't difficult to do in Delphi; it requires one to understand the dbGridTitleClick method and how to build indices for a clientdataset.

The other day, she asked me whether it would be possible once the report is sorted by a column to click again on the titlebar and get the column to sort itself in reverse. I was non-committal, but a little thought and work gave me the answer.

I am enclosing the code to a very simple but complete example program. The program uses a dbgrid, a datasource and a clientdataset, all inter-connected. For the example, I am using a standalone clientdataset, in which I define the fields and populate them; the indexing technique works exactly the same when the clientdataset is populated via queries.


As the data can be sorted by several columns (in the example, only two) and each column has to 'remember' in which direction it was last sorted (so that clicking on the grid's title bar will cause the data to be sorted in the reverse order), an array of boolean has to be declared, one element per column (this is called 'directions' in the example). 

For every sortable column, two indices have to be created - one for ascending sort and one for descending sort. Each index is created using the 'AddIndex' method of the clientdataset; a descending index has the third parameter set to ixDescending, whereas an ascending index has the third parameter empty.


Every time the grid's title bar is clicked, the index of the clientdataset is changed. But how does the program know which index to use, especially as there are two indices per column? The elegant solution that I invented was to number each index, where the number is the column number doubled; if the index is descending, then the number is incremented. Thus for column 0, the ascending index will be idx0 (0 times 2 = 0, no increment), whereas the descending index will be idx1. For column 1, the ascending index will be idx2 and the descending index idx3. This simple scheme will allow the indices to be chosen as a function of the column number.


Here is the program...
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, DB, DBClient, Grids, DBGrids, StdCtrls;

type
  TForm1 = class(TForm)
    DBGrid1: TDBGrid;
    qTest: TClientDataSet;
    DataSource1: TDataSource;
    procedure FormShow(Sender: TObject);
    procedure DBGrid1TitleClick(Column: TColumn);
  private
    { Private declarations }
    directions: array [0..1] of boolean;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormShow(Sender: TObject);
var
 strings: tstrings;

begin
 directions[0]:= true; directions[1]:= true;
 with qTest do
  begin
   fielddefs.add ('num', ftInteger, 0, false);
   fielddefs.add ('extra', ftInteger, 0, false);
   createdataset;
   open;
   addindex ('idx0', 'num', [], '', '', 0);
   addindex ('idx1', 'num', [ixDescending], '', '', 0);
   addindex ('idx2', 'extra', [], '', '', 0);
   addindex ('idx3', 'extra', [ixDescending], '', '', 0);
   strings:= tstringlist.create;
   getindexnames (strings);   // this line seems to be essential!
   strings.free;
   append;
   fieldbyname ('num').asinteger:= 2;
   fieldbyname ('extra').AsInteger:= 10;
   post;
   append;
   fieldbyname ('num').asinteger:= 1;
   fieldbyname ('extra').AsInteger:= 14;
   post;
   append;
   fieldbyname ('num').asinteger:= 3;
   fieldbyname ('extra').AsInteger:= 20;
   post;
  end;
end;

procedure TForm1.DBGrid1TitleClick(Column: TColumn);
var
 n, ex: word;

begin
 n:= column.Index;
 directions[n]:= not directions[n];
 ex:= n * 2;
 if directions[n] then inc (ex);
 with qTest do
  try
   disablecontrols;
   close;
   indexname:= 'idx' + inttostr (ex);
   open
  finally
   enablecontrols
  end;
end;

end.
There is one small 'gotcha' - the cliendataset 'GetIndexNames' method has to be called (its results are stored in a temporary string list). Without this, one receives an error message 'qTest: Index idx0 not found' should one click on the grid's title bar.