Version 0 of A Little Meeting Organisation Helper

Updated 2005-08-18 11:52:13

MNO: I often find myself trying to organise via email meetings of friends for drinks/dinner/weekends away.

In the end I started using emails looking like the following (assuming my friends had the initials ABC DEF GHI JKL)

             A  D  G  J  M  
             B  E  H  K  N  
             C  F  I  L  O  

 Mon 22 Aug  ?  ?  ?  ?  ?  
 Tue 23 Aug  ?  ?  ?  ?  ?  
 Wed 24 Aug  ?  ?  ?  ?  ?  
 Thu 25 Aug  ?  ?  ?  ?  ?  
 Fri 26 Aug  ?  ?  ?  ?  ?  

 Mon 29 Aug  ?  ?  ?  ?  ?  
 Tue 30 Aug  ?  ?  ?  ?  ?  
 Wed 31 Aug  ?  ?  ?  ?  ?  
 Thu 01 Sep  ?  ?  ?  ?  ?  
 Fri 02 Sep  ?  ?  ?  ?  ?  

The idea being that everyone changes the "?"'s under their name to "Y" or "N" to indicate availability.

At first I generated these by hand, but eventually put together a short Tcl script to do it. Last weekend, I decided to rewrite it as a Tk GUI app which is included below in case anyone else would find it useful.

There isn't much in the way of error handling including.

The generate button will write the matrix into the text widget in the GUI and also copy the matrix to the clipboard.

Start and end dates are anything recognised by Tcl's [clock scan] command.

Tested under Windows with 8.4.3.

 #!/usr/bin/env tclsh
 #
 # mmm.tcl -- generate meeting matrix suitable for sending out in 
 # an email to organise drinks, weekends away etc.  The idea is that
 # each person then changes the "?" to a "Y" or "N" as appropriate
 # and recirculates the message until all have been filled in and
 # hopefully a number of common available dates are identifiable.
 #
 # Author: Mark Oakden http://wiki.tcl.tk/MNO
 #
 # e.g. for 5 people, with initials ABC, DEF, GHI, JKL and MNO a
 # matrix generated for two weeks from today (18th Aug 2005) looks 
 # like this:-
 #
 #
 #             A  D  G  J  M  
 #             B  E  H  K  N  
 #             C  F  I  L  O  
 # 
 # Thu 18 Aug  ?  ?  ?  ?  ?  
 # Fri 19 Aug  ?  ?  ?  ?  ?  
 # 
 # Mon 22 Aug  ?  ?  ?  ?  ?  
 # Tue 23 Aug  ?  ?  ?  ?  ?  
 # Wed 24 Aug  ?  ?  ?  ?  ?  
 # Thu 25 Aug  ?  ?  ?  ?  ?  
 # Fri 26 Aug  ?  ?  ?  ?  ?  
 # 
 # Mon 29 Aug  ?  ?  ?  ?  ?  
 # Tue 30 Aug  ?  ?  ?  ?  ?  
 # Wed 31 Aug  ?  ?  ?  ?  ?  
 # 
 package require Tk
 wm withdraw .
 # daylist determines the order of the days as displayed in the GUI
 # 0 == sunday 1 == monday ... 6 == saturday
 set ::daylist [list 1 2 3 4 5 6 0]
 # daynamelist should have the short daynames in order 0 .. 6 since
 # we will use lindex to pull out the appropriate dayname.
 # I'd prefer to use the fact that [clock] knows the mapping 0 - 6 
 # --> local day name abbreviations (%a in [clock format]) but for
 # the purposes of this short script don't want to jump through the
 # hoops to extract these
 set ::daynamelist [list Sun Mon Tue Wed Thu Fri Sat]
 #
 set ::start today
 set ::end "+ 2 weeks"
 #
 proc buildGUI {} {
     if { [info exists .mmmGUI] } {
         return
     }
     option add Labelframe*padX 5
     set t [toplevel .mmmGUI]
     # attendee pane
     set attf [labelframe $t.attf -borderwidth 2 -relief groove \
                   -text "Attendees"]
     set namesl [label $attf.namesl -text "Names/Initials:"]
     set namese [entry $attf.namese -width 36 -textvariable ::att]
     set namesinfo [label $attf.namesinfo -text "Please enter below your attendee names/initials, separated by spaces."]
     grid $namesinfo - -sticky w
     grid $namesl $namese -sticky ew
     grid columnconfigure $attf 1 -weight 1
     pack $attf -side top -expand true -fill x
     # day selection pane
     set dayf [labelframe $t.dayf -borderwidth 2 -relief groove -text "Days"]
     set dayinfo [label $dayf.dayinfo -text "Select below the days to be included"]
     set daysf [frame $dayf.inner]
     foreach day $::daylist {
         set dl [label $daysf.dl$day -text [lindex $::daynamelist $day]]
         set dc [checkbutton $daysf.dc$day -variable ::include($day)]
         pack $dl -side left -anchor e
         pack $dc -side left -anchor w
     }
     set daybuttonf [frame $dayf.buttons]
     set b1 [button $daybuttonf.weekdays \
                 -text "Toggle Weekdays" -command {toggleDays 1 2 3 4 5}]
     set b2 [button $daybuttonf.weekends \
                 -text "Toggle Weekends" -command {toggleDays 0 6}]
     set b3 [button $daybuttonf.alldays \
                 -text "Toggle All" -command {toggleDays 0 1 2 3 4 5 6}]
     pack $b1 $b2 $b3 -side left
     pack $dayinfo -side top -anchor w
     pack $daysf -side top -expand true -fill x
     pack $dayf -side top -expand true -fill x
     pack $daybuttonf -side top -expand true -fill x
     # date range pane
     set datef [labelframe $t.datef -borderwidth 2 -relief groove \
                    -text "Date Range"]
     set datestartl [label $datef.startl -text "Start:"]
     set datestarte [entry $datef.starte -textvariable ::start]
     set dateendl [label $datef.endl -text "End:"]
     set dateende [entry $datef.ende -textvariable ::end]
     pack $datestartl $datestarte $dateendl $dateende -side left
     pack $datef -side top -expand true -fill x
     # output pane
     set outf [labelframe $t.outf -borderwidth 2 -relief groove \
                    -text "Meeting Matrix"]
     # use a var from global namespace here - we will manipulate the text 
     # widget outside this proc
     set ::outt [text $outf.t -font {Courier 10} -width 48 \
                     -height 20 -wrap none \
                     -xscrollcommand [list $outf.xs set] \
                     -yscrollcommand [list $outf.ys set]]
     scrollbar $outf.xs -orient horizontal -command [list $::outt xview]
     scrollbar $outf.ys -orient vertical -command [list $::outt yview]
     set genbut [button $outf.genbut -command generate \
                     -text "Generate the Meeting Matrix"]
     grid $::outt $outf.ys -sticky nsew
     grid $outf.xs x -sticky ew
     grid $genbut - -sticky ew
     grid columnconfigure $outf 0 -weight 1
     grid rowconfigure $outf 0 -weight 1
     pack $outf -side top -expand true -fill both
 }

 proc toggleDays { args } {
     foreach d $args {
         set ::include($d) [expr {! $::include($d)}]
     }
 }

 proc normaliseNamelist { names } {
     set longest 0
     foreach n $names {
         if { [set l [string length $n]] > $longest } {
             set longest $l
         }
     }
     set norm [list]
     foreach n $names {
         set i [string length $n]
         set s [expr {$longest - $i}]
         lappend norm [string repeat " " $s]$n
     }
     return $norm
 }

 proc generate {} {
     $::outt delete 1.0 end
     set namelist [normaliseNamelist $::att]
     set titleprefix "            "
     # write the title
     set index 0
     foreach char [split [lindex $namelist 0] {}] {
         set str $titleprefix
         set bodystr ""
         foreach name $namelist {
             append str "[string index $name $index]  "
             append bodystr "?  "
         }
         $::outt insert end "$str\n"
         incr index
     }
     $::outt insert end "\n"
     set now [clock seconds]
     # start relative to now
     set current [clock scan $::start -base $now]
     # end relative to start
     set _end [clock scan $::end -base $current]
     while { $current < $_end } {
         set daynum [clock format $current -format "%w"]
         if { $::include($daynum) } {
             $::outt insert end [clock format $current -format "%a %d %b  "]
             $::outt insert end "$bodystr\n"
         }
         if { $daynum == 0 } {
             $::outt insert end "\n"
         }
         set current [clock scan "+1 day" -base $current]
     }
     clipboard clear
     clipboard append [$::outt get 1.0 end]
 }

 buildGUI
 # That's all, folks.

[ Category Application ]