Annotation of 2002/scribe/scribe.perl, revision 1.23

1.7       dbooth      1: #! perl -w
1.1       dbooth      2: 
1.20      dbooth      3: warn 'This is $Revision: 1.14 $ of $Date: 2003/06/13 21:02:20 $ 
1.11      dbooth      4: Check for newer version at http://dev.w3.org/cvsweb/~checkout~/2002/scribe/
                      5: 
                      6: ';
                      7: 
                      8: 
1.7       dbooth      9: # Generate minutes in HTML from a text IRC/chat Log.
                     10: #
1.1       dbooth     11: # Author: David Booth <dbooth@w3.org1.3       dbooth     12: #
1.1       dbooth     13: # Take a raw W3C IRC log, tidy it up a bit, and put it into HTML
                     14: # to create meeting minutes.  Reads stdin, writes stdout.
1.7       dbooth     15: # Several input formats are accepted (see below).
                     16: # Input must follow certain conventions (see below).
1.16      dbooth     17: #
1.5       dbooth     18: # USAGE: 
1.3       dbooth     19: #      perl scribe.perl [options] ircLogFile.txt > minutesFile.htm
                     20: #      perl scribe.perl -sampleInput
                     21: #      perl scribe.perl -sampleOutput
                     22: #      perl scribe.perl -sampleTemplate
1.1       dbooth     23: #
                     24: # Options:
1.3       dbooth     25: #      -sampleInput            Show me some sample input, so I can
                     26: #                              learn how to use this program.
                     27: #
                     28: #      -sampleOutput           Show me some sample output.
                     29: #
1.1       dbooth     30: #      -scribeOnly             Only keep scribe statements.  (Otherwise
                     31: #                              everyone's statements are kept.)
                     32: #
1.3       dbooth     33: #      -template tfile         Use tfile as the template file for
                     34: #                              generating the HTML.  Without this option,
                     35: #                              the program will default to an embedded
                     36: #                              template, which you can see by using
                     37: #                              the "-sampleTemplate" option.
                     38: #
                     39: #      -sampleTemplate         Show me a sample template file.
1.5       dbooth     40: #                              (The default template.)
                     41: #
                     42: #      -public                 Use a template that is formatted
                     43: #                              for public access.  DEFAULT.
                     44: #                              (Search for "sub PublicTemplate" below.)
                     45: #
                     46: #      -member                 Use a template that is formatted 
                     47: #                              for W3C member-only access.
                     48: #                              (Search for "sub PublicTemplate" below.)
                     49: #
                     50: #      -team                   Use a template that is formatted
                     51: #                              for W3C team-only access.
                     52: #                              (Search for "sub PublicTemplate" below.)
                     53: #
                     54: #      -mit                    Use a template that is formatted for
                     55: #                              W3C MIT style.
                     56: #                              (Search for "sub PublicTemplate" below.)
1.3       dbooth     57: #
1.1       dbooth     58: # The best way to use this program is:
                     59: #      1. Make a backup of your IRC log.
1.5       dbooth     60: #      2. Modify the IRC log to conform to the scribe conventions that this
1.3       dbooth     61: #         program expects (described below).
                     62: #      3. Run this program as described above.
1.1       dbooth     63: #      4. View the output in a browser.
1.3       dbooth     64: #      5. Go back to step 2 to make modifications, and repeat.
                     65: #         Once the output looks good enough, then . . .
                     66: #      6. Manually edit the resulting HTML for any remaining fixes.
1.1       dbooth     67: #
1.5       dbooth     68: # SCRIBE CONVENTIONS:
1.1       dbooth     69: # This program expects certain conventions in the IRC log as follows.
1.17      dbooth     70: # Identify the IRC name of the scribe (Note that names must not contain spaces):
1.7       dbooth     71: #      <dbooth> Scribe: dbooth
1.1       dbooth     72: # Identify the meeting chair:
1.7       dbooth     73: #      <dbooth> Chair: Jonathan
1.1       dbooth     74: # Identify the meeting itself:
1.7       dbooth     75: #      <dbooth> Meeting: Weekly Baking Club Meeting
1.11      dbooth     76: # Fix the meeting date (default is auto set from IRC log name or today if 
                     77: # there is no "Date:" line):
1.3       dbooth     78: #      <dbooth> Date: 05 Dec 2002
1.1       dbooth     79: # Identify the current topic.  You should insert one of
                     80: # these lines before the start of EACH topic:
                     81: #      <dbooth> Topic: Review of Action Items
                     82: # Record an action item:
                     83: #      <dbooth> ACTION: dbooth to send a message to himself about action items
1.11      dbooth     84: # Record an incomplete action item (either syntax):
1.7       dbooth     85: #      <Philippe> PENDING ACTION: Barbara to bake 3 pies 
1.11      dbooth     86: #      <Philippe> ACTION: Barbara to bake 3 pies  -- PENDING
                     87: # Record a completed action item (either syntax):
                     88: #      <Philippe> DONE ACTION: Barbara to bake 3 pies 
                     89: #      <Philippe> ACTION: Barbara to bake 3 pies  -- DONE
1.1       dbooth     90: # Scribe someone's statements:
1.7       dbooth     91: #      <dbooth> Joseph: I think that we should all eat cake
                     92: #      <dbooth> ... with ice creme.
1.11      dbooth     93: # Correct a mistake (changes most recent instance):
1.1       dbooth     94: #      <dbooth> s/creme/cream/
                     95: # Correct a mistake globally from this point back in the input:
                     96: #      <dbooth> s/Baking/Cooking/g
1.16      dbooth     97: # Identify the IRC log produced by RRSAGENT (this also sets the date):
1.1       dbooth     98: #      <dbooth> rrsagent, where am i?
1.7       dbooth     99: #      <RRSAgent> See http://www.w3.org/2002/11/07-ws-arch-irc#T13-59-36
1.5       dbooth    100: # or:
                    101: #      <dbooth> Log: http://www.w3.org/2002/11/07-ws-arch-irc
1.8       dbooth    102: # Separator (at least 4 dashes):
                    103: #      <dbooth> ----
1.1       dbooth    104: #
1.11      dbooth    105: # INPUT FORMATS ACCEPTED
                    106: # These formats are auto detected.  The best match wins.
1.7       dbooth    107: #   MIRC buffer style:
                    108: #      <ericn> Where is our next F2F?
                    109: #      <dbooth> Rennes France.
                    110: #   RRSAgent Text style:
                    111: #      20:41:27 <ericn> Where is our next F2F?
                    112: #      20:41:37 <dbooth> Rennes France.
                    113: #   RRSAgent HTML style:
                    114: #      <dt id="T20-41-27">20:41:27 [ericn]</dt><dd>Where is our next F2F? </dd>
                    115: #      <dt id="T20-41-37">20:41:37 [dbooth]</dt><dd>Rennes France. </dd>
                    116: #   RRSAgent HTML Text style (text copied and pasted from the browser):
                    117: #      20:41:27 [ericn]
                    118: #           Where is our next F2F?
                    119: #      20:41:37 [dbooth]
                    120: #           Rennes France.
                    121: #   Yahoo IM style:
                    122: #      ericn: Where is our next F2F?
                    123: #      dbooth: Rennes France.
                    124: #
1.3       dbooth    125: # WARNING: The code is a horrible mess.  (Sorry!)  Please hold your nose if 
1.16      dbooth    126: # you look at it.  If you have something better, or make improvements
                    127: # to this (and please do!), please let me know.  Perhaps it's a good 
                    128: # example of Fred Brooke's advice in Mythical Man Month: "Plan to throw 
                    129: # one away". 
1.1       dbooth    130: #
1.3       dbooth    131: ######################################################################
1.1       dbooth    132: 
1.7       dbooth    133: my $scribeName = "UNKNOWN"; # Default
                    134: 
                    135: # Formatting:
                    136: # my $preSpeakerHTML = "<strong>";
                    137: # my $postSpeakerHTML = "</strong> <br />";
                    138: my $preSpeakerHTML = "<b>";
                    139: my $postSpeakerHTML = "</b>";
                    140: my $preParagraphHTML = "<p>";
                    141: my $postParagraphHTML = "</p>";
1.15      hugo      142: my $preTopicHTML = "<h3";
1.7       dbooth    143: my $postTopicHTML = "</h3>";
1.1       dbooth    144: 
                    145: # Get options/args
1.3       dbooth    146: my $all = "";                  # Input
1.1       dbooth    147: my $canonicalizeNames = 0;     # Convert all names to their canonical form?
1.7       dbooth    148: my $useTeamSynonyms = 0;       # Accept any short synonyms for team members?
1.1       dbooth    149: my $scribeOnly = 0;            # Only select scribe lines
1.11      dbooth    150: 
                    151: @ARGV = map {glob} @ARGV;      # Expand wildcards in arguments
1.1       dbooth    152: my @args = ();
1.5       dbooth    153: my $template = &DefaultTemplate();
1.1       dbooth    154: while (@ARGV)
                    155:        {
                    156:        my $a = shift @ARGV;
                    157:        if (0) {}
1.3       dbooth    158:        elsif ($a eq "-sampleInput") 
                    159:                { print STDOUT &SampleInput(); exit 0; }
                    160:        elsif ($a eq "-sampleOutput") 
                    161:                { $all = &SampleInput(); }
                    162:        elsif ($a eq "-sampleTemplate") 
                    163:                { print STDOUT &DefaultTemplate(); exit 0; }
1.1       dbooth    164:        elsif ($a eq "-scribeOnly") 
                    165:                { $scribeOnly = 1; }
                    166:        elsif ($a eq "-canon") 
                    167:                { $canonicalizeNames = 1; }
                    168:        elsif ($a eq "-teamSynonyms") 
                    169:                { $useTeamSynonyms = 1; }
                    170:        elsif ($a eq "-mit") 
1.5       dbooth    171:                { $template = &MITTemplate(); }
1.1       dbooth    172:        elsif ($a eq "-team") 
1.5       dbooth    173:                { $template = &TeamTemplate(); }
1.1       dbooth    174:        elsif ($a eq "-member") 
1.5       dbooth    175:                { $template = &MemberTemplate(); }
1.1       dbooth    176:        elsif ($a eq "-world") 
1.5       dbooth    177:                { $template = &PublicTemplate(); }
1.1       dbooth    178:        elsif ($a eq "-public") 
1.5       dbooth    179:                { $template = &PublicTemplate(); }
1.1       dbooth    180:        elsif ($a eq "-template") 
1.5       dbooth    181:                { 
                    182:                my $templateFile = shift @ARGV; 
                    183:                die "ERROR: Template file not found: $templateFile\n"
                    184:                        if !-e $templateFile;
1.7       dbooth    185:                my $t = &GetTemplate($templateFile);
1.5       dbooth    186:                if (!$t)
                    187:                        {
                    188:                        die "ERROR: Empty template: $templateFile\n";
                    189:                        }
                    190:                $template = $t;
                    191:                }
1.1       dbooth    192:        elsif ($a =~ m/\A\-/)
                    193:                { die "ERROR: Unknown option: $a\n"; }
                    194:        else    
                    195:                { push(@args, $a); }
                    196:        }
                    197: @ARGV = @args;
                    198: 
1.7       dbooth    199: # Get input:
                    200: $all =  join("",<>) if !$all;
1.1       dbooth    201: if (!$all)
                    202:        {
1.3       dbooth    203:        warn "WARNING: Empty input.\n";
1.1       dbooth    204:        }
                    205: # Delete control-M's if any.
                    206: $all =~ s/\r//g;
1.7       dbooth    207: 
1.8       dbooth    208: # Normalize input format.  To do so, we need to guess the input format.
                    209: # Try each known formats, and see which one matches best.
1.7       dbooth    210: my $bestScore = 0;
                    211: my $bestAll = "";
                    212: my $bestName = "";
1.8       dbooth    213: # These are the normalizer functions we'll be calling.
1.11      dbooth    214: # Each one is defined below.
1.8       dbooth    215: # Just add another to the list if you want to recognize another format.
1.11      dbooth    216: # Each function takes $all (the input text) as input and returns
                    217: # a pair: ($score, $newAll). 
                    218: #      $score is a value [0,1] indicating how well it matched.
                    219: #      $newAll is the normalized input.
1.7       dbooth    220: foreach my $f (qw(
                    221:                NormalizerMircTxt
1.8       dbooth    222:                NormalizerRRSAgentText 
1.7       dbooth    223:                NormalizerRRSAgentHtml 
                    224:                NormalizerRRSAgentHTMLText
                    225:                NormalizerYahoo
1.20      dbooth    226:                NormalizerPlainText
1.7       dbooth    227:                ))
                    228:        {
                    229:        my ($score, $newAll) = &$f($all);
                    230:        # warn "$f: $score\n";
                    231:        if ($score > $bestScore)
                    232:                {
                    233:                $bestScore = $score;
                    234:                $bestAll = $newAll;
                    235:                $bestName = $f;
                    236:                }
                    237:        }
                    238: my $bestScoreString = sprintf("%4.2f", $bestScore);
                    239: warn "Input format (score $bestScoreString): $bestName\n";
1.8       dbooth    240: die "ERROR: Could not guess input format.\n" if $bestScore == 0;
                    241: warn "WARNING: Low confidence ($bestScoreString) on guessing input format: $bestName\n"
1.7       dbooth    242:        if $bestScore < 0.7;
                    243: $all = $bestAll;
                    244: 
1.9       dbooth    245: 
1.7       dbooth    246: # Perform s/old/new/ substitutions.
                    247: while($all =~ m/\n\<[^\>]+\>\s*s\/([^\/]+)\/([^\/]*?)((\/(g))|\/?)(\s*)\n/i)
                    248:        {
                    249:        my $old = $1;
                    250:        my $new = $2;
1.4       dbooth    251:        my $global = $5;
1.7       dbooth    252:        my $pre = $`;
                    253:        my $match = $&;
                    254:        my $post = $';
                    255:        my $oldp = quotemeta($old);
1.1       dbooth    256:        # warn "Found match: $match\n";
                    257:        my $told = $old;
                    258:        $told = $& . "...(truncated)...." if ($old =~ m/\A.*\n/);
                    259:        my $tnew = $new;
                    260:        $tnew = $& . "...(truncated)...." if ($old =~ m/\A.*\n/);
                    261:        my $succeeded = 0;
                    262:        if (($global  && $pre =~ s/$oldp/$new/g)
1.7       dbooth    263:        || ((!$global) && $pre =~ s/\A((.|\n)*)($oldp)((.|\n)*?)\Z/$1$new$4/))
                    264:                {
                    265:                warn "SUBSTITUTION: s/$told/$tnew/\n";
1.1       dbooth    266:                warn "(global substitution)\n" if $global;
1.7       dbooth    267:                $all = $pre . "\n" . $post;
                    268:                }
                    269:        else    {
                    270:                warn "s/$told/$tnew/  FAILED\n";
                    271:                $match =~ s/\A\s+//;
                    272:                $match =~ s/\s+\Z//;
                    273:                $all = $pre . "\n[Auto substitution failed:] " . $match . "\n" . $post;
                    274:                }
1.1       dbooth    275:        warn "WARNING: Multiline substitution!!! (Is this correct?)\n" if $tnew ne $new || $told ne $old;
1.7       dbooth    276:        }
                    277: 
                    278: # Strip -home from names.  (Convert alan-home to alan, for example.)
                    279: $all =~ s/(\w+)\-home\b/$1/ig;
                    280: # Strip -lap from names.  (Convert alan-lap to alan, for example.)
                    281: $all =~ s/(\w+)\-lap\b/$1/ig;
                    282: # Strip -iMac from names.  (Convert alan-iMac to alan, for example.)
                    283: $all =~ s/(\w+)\-iMac\b/$1/ig;
                    284: 
                    285: # Determine scribe name if possible:
                    286: $scribeName = $3 if $all =~ s/\s+scribe\s+((today\s+)?)is\s+([\w\-]+)//i;
                    287: $scribeName = $1 if $all =~ s/\s+scribe\s*\:\s*([\w\-]+)//i;
                    288: $scribeName = $1 if $all =~ s/\s([\w\-]+)\s+is\s+scribe//i;
                    289: $scribeName = $1 if $all =~ s/\s([\w\-]+)\s+is\s+scribing//i;
                    290: $scribeName = $1 if $all =~ s/\<([^\>\ ]+)\>\s*I\s+am\s+scribe\s+.*//i;
                    291: $scribeName = $1 if $all =~ s/\<([^\>\ ]+)\>\s*I\s+will\s+scribe\s+.*//i;
                    292: $scribeName = $1 if $all =~ s/\<([^\>\ ]+)\>\s*I\s+am\s+scribing.*//i;
                    293: warn "Scribe: $scribeName\n";
                    294: 
                    295: # Get attendee list, and normalize names within the document:
1.17      dbooth    296: my $namePattern = '([\\w]([\\w\\d\\-]*))';
1.1       dbooth    297: # warn "namePattern: $namePattern\n";
1.7       dbooth    298: my @uniqNames = ();
1.1       dbooth    299: my $allNameRefsRef;
                    300: ($all, $scribeName, $allNameRefsRef, @uniqNames) = &GetNames($all, $scribeName);
                    301: my @allNames = map { ${$_} } @{$allNameRefsRef};
1.7       dbooth    302: my $scribePattern = quotemeta($scribeName);
1.1       dbooth    303: $all =~ s/\<$scribePattern\>/\<Scribe\>/ig;
                    304: $scribePattern = "Scribe";
                    305: push(@allNames,"Scribe");
                    306: my @allSpeakerPatterns = map {quotemeta($_)} @allNames;
                    307: my $speakerPattern = "((" . join(")|(", @allSpeakerPatterns) . "))";
                    308: # warn "speakerPattern: $speakerPattern\n";
                    309: 
                    310: # Escape &
                    311: $all =~ s/\&/\&amp\;/g;
                    312: 
                    313: # Normalize Scribe continuation lines so that the speaker's name is on every line:
                    314: #      <Scribe> SusanW: We had a mtg on July 16.
                    315: #      <DanC_> pointer to minutes?
1.8       dbooth    316: #      <Scribe> SusanW: I'm looking.
1.1       dbooth    317: #      <Scribe> ... The minutes are on the admin timeline page.
                    318: my @allLines = split(/\n/, $all);
                    319: my $currentSpeaker = "UNKNOWN_SPEAKER";
                    320: for (my $i=0; $i<@allLines; $i++)
                    321:        {
                    322:        # warn "$allLines[$i]\n";
                    323:        if ($allLines[$i] =~ m/\A\<Scribe\>\s*($speakerPattern)\s*\:/i)
                    324:                {
                    325:                $currentSpeaker = $1;
                    326:                # warn "SET SPEAKER: $currentSpeaker\n";
                    327:                }
                    328:        elsif ($allLines[$i] =~ s/\A\<Scribe\>\s*\.\.+\s*/\<Scribe\> $currentSpeaker: /i)
                    329:                {
                    330:                # warn "Scribe NORMALIZED: $& --> $allLines[$i]\n";
                    331:                warn "WARNING: UNKNOWN SPEAKER: $allLines[$i]\nPossibly need to add line; <Zakim> +someone\n" if $currentSpeaker eq "UNKNOWN_SPEAKER";
                    332:                }
                    333:        elsif ($allLines[$i] =~ m/\A\<Scribe\>\s*($namePattern)\s*\:/i)
                    334:                {
                    335:                # This doesn't work right.  It matches "Topic:" and known names.
                    336:                # warn "Possibly need to add speaker \"$1\" by adding line:\n<Zakim> +$1\n";
                    337:                }
                    338:        }
                    339: $all = "\n" . join("\n", @allLines) . "\n";
                    340: 
                    341: # Get the list of people present:
1.7       dbooth    342: my @possiblyPresent = @uniqNames;      # People present at the meeting
                    343: my @present = ();                      # People present at the meeting
1.14      dbooth    344: while ($all =~ s/\s+Present\s*\:\s*(.*)//i)
1.7       dbooth    345:        {
                    346:        my $present = $1;
1.14      dbooth    347:        my @p = ();
1.7       dbooth    348:        if ($present =~ m/\,/)
                    349:                {
                    350:                # Comma-separated list
1.14      dbooth    351:                @p = grep {$_ && $_ ne "and"} 
                    352:                                map {s/\A\s+//; s/\s+\Z//; s/\s+/_/g; $_} 
1.7       dbooth    353:                                split(/\,/,$present);
                    354:                }
                    355:        else    {
                    356:                # Space-separated list
1.14      dbooth    357:                @p = grep {$_} split(/\s+/,$present);
1.7       dbooth    358:                }
1.14      dbooth    359:        if (scalar(@p) < scalar(@present))
1.7       dbooth    360:                {
1.14      dbooth    361:                warn "\nWARNING: Combining \"$present\"
                    362: with previous list of people present.\n\n";
                    363:                my %seen = map {($_,$_)} @present;
                    364:                my @newp = grep {!exists($seen{$_})} @p;
                    365:                push(@present, @newp);
                    366:                }
                    367:        else    {
                    368:                warn "\nWARNING: Replacing previous list of people present.\n\n" if @present;
                    369:                @present = @p;
                    370:                }
                    371:        warn "Present: @present\n"; 
                    372:        }
                    373: if (@present < 3) 
                    374:        { 
                    375:        if (@present > 0)  { warn "WARNING: Fewer than 3 people found present!\n\n"; }
                    376:        else    {
1.16      dbooth    377:                warn "\nWARNING: No \"Present: ... \" found!\n";
1.14      dbooth    378:                warn "Possibly Present: @possiblyPresent\n"; 
                    379:                warn "You can indicate the people present like this:
                    380: <scribe> Present: dbooth jonathan mary\n\n";
1.7       dbooth    381:                }
                    382:        }
1.1       dbooth    383: 
                    384: # Get the list of regrets:
                    385: my @regrets = ();      # People who sent regrets
1.7       dbooth    386: if ($all =~ s/\s+Regrets\s*\:\s*(.*)//i)
                    387:        {
                    388:        my $regrets = $1;
                    389:        if ($regrets =~ m/\,/)
                    390:                {
                    391:                # Comma-separated list
                    392:                @regrets = grep {$_ && $_ ne "and"} 
                    393:                                map {s/\As+//; s/s\+\Z//; $_} 
                    394:                                split(/\,/,$regrets);
                    395:                }
                    396:        else    {
                    397:                # Space-separated list
                    398:                @regrets = grep {$_} split(/\s+/,$regrets);
                    399:                }
                    400:        }
                    401: warn "Regrets: @regrets\n" if @regrets;
1.1       dbooth    402: 
1.7       dbooth    403: # Grab meeting name:
1.8       dbooth    404: my $title = "SV_MEETING_TITLE";
1.14      dbooth    405: if ($all =~ s/\n\<$namePattern\>\s*(Meeting|Title)\s*\:\s*(.*)\n/\n/i)
                    406:        { $title = $4; }
                    407: else   { 
                    408:        warn "\nWARNING: No meeting title found!
                    409: You should specify the meeting title like this:
                    410: <scribe> Meeting: Weekly Baking Club Meeting\n\n";
                    411:        }
1.7       dbooth    412: 
                    413: # Grab Previous meeting URL:
1.8       dbooth    414: my $previousURL = "SV_PREVIOUS_MEETING_URL";
1.14      dbooth    415: if ($all =~ s/\n\<$namePattern\>\s*(Previous|PreviousMeeting|Previous Meeting)\s*\:\s*(.*)\n/\n/i)
                    416:        { $previousURL = $4; }
1.7       dbooth    417: 
                    418: # Grab Chair:
1.8       dbooth    419: my $chair = "SV_MEETING_CHAIR";
1.14      dbooth    420: if ($all =~ s/\n\<$namePattern\>\s*(Chair(s?))\s*\:\s*(.*)\n/\n/i)
                    421:        { $chair = $5; }
                    422: else   { 
                    423:        warn "\nWARNING: No meeting chair found!
                    424: You should specify the meeting chair like this:
                    425: <scribe> Chair: dbooth\n\n";
                    426:        }
1.7       dbooth    427: 
1.10      dbooth    428: # Grab IRC Log URL.  Do this before looking for the date, because
                    429: # we can figure out the date from the IRC log name.
1.8       dbooth    430: my $logURL = "SV_MEETING_IRC_URL";
1.7       dbooth    431: # <RRSAgent>   recorded in http://www.w3.org/2002/04/05-arch-irc#T15-46-50
                    432: $logURL = $3 if $all =~ m/\n\<(RRSAgent|Zakim)\>\s*(recorded|logged)\s+in\s+(http\:([^\s\#]+))/i;
                    433: $logURL = $6 if $all =~ s/\n\<$namePattern\>\s*(IRC|Log|(IRC(\s*)Log))\s*\:\s*(.*)\n/\n/i;
                    434: $logURL = $3 if $all =~ m/\n\<(RRSAgent|Zakim)\>\s*(see|recorded\s+in)\s+(http\:([^\s\#]+))/i;
                    435: 
1.10      dbooth    436: # Grab and remove date from $all
1.20      dbooth    437: my ($day0, $mon0, $year, $monthAlpha) = &GetDate($all, $namePattern, $logURL);
1.10      dbooth    438: 
1.21      dbooth    439: # Grab all ACTION items (whether new, done, pending, or dropped):
1.7       dbooth    440: # <dbooth> ACTION: MSM to report back on XMLP IPR policy after W3M mtg.
                    441: # <dbooth> ACTION: MSM to report back on XMLP IPR policy after W3M mtg. [4]
1.21      dbooth    442: # <dbooth> DONE ACTION: MSM to report back on XMLP IPR policy after W3M mtg.
                    443: # <dbooth> PENDING ACTION: MSM to report back on XMLP IPR policy after W3M mtg.
                    444: # <dbooth> ACTION: MSM to report back on XMLP IPR policy after W3M mtg. -- DONE
                    445: # <dbooth> ACTION: MSM to report back on XMLP IPR policy after W3M mtg. -- PENDING
                    446: # <dbooth> ACTION: MSM to report back on XMLP IPR policy after W3M mtg. * DONE *
                    447: # <dbooth> ACTION: MSM to report back on XMLP IPR policy after W3M mtg. * PENDINGa *
                    448: my %actions = ();      # Key: action Value: (NEW|DONE|PENDING|DROPPED)
                    449: # warn "ALL ACTIONs:\n";
                    450: $t = "\n" . $all;
                    451: # $t .= "<dbooth> [PENDING] ACTION: My fake action text1\n";   # Test
                    452: # $t .= "<dbooth> *PENDING* ACTION: My fake action text2\n";   # Test
                    453: # $t .= "<dbooth> PENDING ACTION: My fake action text3\n";     # Test
                    454: # $t .= "<dbooth> ACTION: My fake action text4 (PENDING)\n";   # Test
                    455: # $t .= "<dbooth> ACTION: My fake action text5 PENDING\n";     # Test
                    456: # $t .= "<dbooth> ACTION: My fake action text6 *PENDING*\n";   # Test
                    457: while ($t =~ s/(.|\n)*?\n\<[^\>]+\>\s*[\[\(\-\*\s]*(((NEW)}(DONE)|(PENDING)|(DROPPED))[\]\)\-\*\s]*\s+)?ACTION\s*\:\s*(.*?)\s*\n/\n/)
                    458:        {
                    459:        my $action = $8;
                    460:        my $status = $3 ? $3 : "NEW";
                    461:        # warn "action: $action status: $status\n";
1.7       dbooth    462:        $action =~ s/\A\s+//;
                    463:        $action =~ s/\s+\Z//;
                    464:        $action =~ s/\s*\[\d+\]\Z//;    # Remove action numbers: [4]
1.21      dbooth    465:        $status = $1 if $action =~ s/[\[\(\-\*\s]+((NEW)|(DONE)|(PENDING)|(DROPPED))[\]\)\-\*\s]*\Z//i;
1.7       dbooth    466:        next if exists($actions{$action});
1.21      dbooth    467:        $actions{$action} = $status;
                    468:        # warn "ACTION: '$action' STATUS: '$status'\n";
1.7       dbooth    469:        }
                    470: 
                    471: # Get a list of people who have action items:
                    472: my %actionPeople = ();
1.21      dbooth    473: foreach my $a ((keys %actions))
1.7       dbooth    474:        {
                    475:        # warn "action:$a:\n";
                    476:        if ($a =~ m/\s+(to)\s+/i)
                    477:                {
                    478:                my $pre = $`;
                    479:                my @names = split(/[^a-zA-Z0-9\-\_]+/,$pre);
                    480:                # warn "names: @names\n";
                    481:                foreach my $n (@names)
                    482:                        {
                    483:                        next if $n eq "";
                    484:                        next if $n eq "and";
                    485:                        $actionPeople{$n} = $n;
                    486:                        }
                    487:                }
                    488:        elsif ($a =~ m/\A([a-zA-Z0-9\-\_]+)/)
                    489:                {
                    490:                my $n = $1;
                    491:                $actionPeople{$n} = $n;
                    492:                }
                    493:        else    {
                    494:                warn "NO PERSON FOUND FOR ACTION: $a\n";
                    495:                }
                    496:        }
                    497: warn "People with action items: ",join(" ", keys %actionPeople), "\n";
1.1       dbooth    498: 
1.21      dbooth    499: # Format the resulting action items:
                    500: # warn "ACTIONS:\n";
                    501: # my $actionTemplate = "<strong>ACTION:</strong> \$action <strong>[\$status]</strong> <br />\n";
                    502: my $actionTemplate = "<strong>[\$status]</strong> <strong>ACTION:</strong> \$action <br />\n";
                    503: my $formattedActions = "";
1.23    ! dbooth    504: foreach my $status (qw(PENDING NEW DROPPED DONE))
1.21      dbooth    505:        {
                    506:        foreach my $action (keys %actions)
                    507:                {
                    508:                next if $actions{$action} ne $status;
                    509:                my $s = $actionTemplate;
                    510:                $s =~ s/\$action/$action/;
                    511:                $s =~ s/\$status/$status/;
                    512:                $formattedActions .= $s;
                    513:                delete($actions{$action});
                    514:                }
                    515:        $formattedActions .= "<br />\n\n";
                    516:        }
                    517: foreach my $status (qw(DONE DROPPED))
                    518:        {
                    519:        foreach my $action (keys %actions)
                    520:                {
                    521:                next if $actions{$action} ne $status;
                    522:                # Don't need to do anything for the done or dropped actions.
                    523:                delete($actions{$action});
                    524:                }
                    525:        }
                    526: # There shouldn't be any more kinds of actions, but if there are, format them
                    527: # $actions{'FAKE ACTION TEXT'} = 'OTHER_STATUS';       # Test
                    528: foreach my $status (sort values %actions)
                    529:        {
                    530:        foreach my $action (keys %actions)
                    531:                {
                    532:                next if $actions{$action} ne $status;
                    533:                my $s = $actionTemplate;
                    534:                $s =~ s/\$action/$action/;
                    535:                $s =~ s/\$status/$status/;
                    536:                $formattedActions .= $s;
                    537:                delete($actions{$action});
                    538:                }
                    539:        $formattedActions .= "<br />\n\n";
                    540:        }
                    541: 
1.1       dbooth    542: # Ignore off-record lines and other lines that should not be minuted.
1.7       dbooth    543: my @lines = split(/\n/, $all);
                    544: my $nLines = scalar(@lines);
                    545: warn "Lines found: $nLines\n";
                    546: my @scribeLines = ();
1.1       dbooth    547: foreach my $line (@lines)
                    548:        {
                    549:        # Ignore /me lines
                    550:        next if $line =~ m/\A\s*\*/;
                    551:        # Select only <speaker> lines
                    552:        next if $line !~ m/\A\<$namePattern\>/i;
                    553:        # Ignore empty lines
                    554:        next if $line =~ m/\A\<$namePattern\>\s*\Z/i;
                    555:        # Ignore join/leave lines:
                    556:        next if $line =~ m/\A\s*\<($namePattern)\>\s*\1\s+has\s+(joined|left|departed|quit)\s*((\S+)?)\s*\Z/i;
                    557:        next if $line =~ m/\A\s*\<(Scribe)\>\s*$namePattern\s+has\s+(joined|left|departed|quit)\s*((\S+)?)\s*\Z/i;
1.4       dbooth    558:        # Ignore topic change lines:
                    559:        # <geoff_a> geoff_a has changed the topic to: Trout Mask Replica
                    560:        next if $line =~ m/\A\s*\<($namePattern)\>\s*\1\s+(has\s+changed\s+the\s+topic\s+to\s*\:.*)\Z/i;
1.1       dbooth    561:        # Ignore zakim lines
                    562:        next if $line =~ m/\A\<Zakim\>/i;
                    563:        next if $line =~ m/\A\<$namePattern\>\s*zakim\s*\,/i;
1.20      dbooth    564:        next if $line =~ m/\A\<$namePattern\>\s*agenda\s*\d*\s*[\+\-\=\?]/i;
1.14      dbooth    565:        next if $line =~ m/\A\<$namePattern\>\s*close\s+agend(a|(um))\s+\d+\Z/i;
                    566:        next if $line =~ m/\A\<$namePattern\>\s*open\s+agend(a|(um))\s+\d+\Z/i;
                    567:        next if $line =~ m/\A\<$namePattern\>\s*take\s+up\s+agend(a|(um))\s+\d+\Z/i;
1.11      dbooth    568:        next if $line =~ m/\A\<$namePattern\>\s*q\s*[\+\-\=\?]/i;
1.14      dbooth    569:        next if $line =~ m/\A\<$namePattern\>\s*ack\s+$namePattern\s*\Z/i;
1.1       dbooth    570:        # Ignore RRSAgent lines
                    571:        next if $line =~ m/\A\<RRSAgent\>/i;
                    572:        next if $line =~ m/\A\<$namePattern\>\s*RRSAgent\s*\,/i;
                    573:        # Remove off the record comments:
                    574:        next if $line =~ m/\A\<$namePattern\>\s*\[\s*off\s*\]/i;
1.11      dbooth    575:        # Select only <scribe> lines?
1.1       dbooth    576:        next if $scribeOnly && $line !~ m/\A\<Scribe\>/i;
                    577:        # warn "KEPT: $line\n";
                    578:        push(@scribeLines, $line);
                    579:        }
1.7       dbooth    580: my $nScribeLines = scalar(@scribeLines);
                    581: warn "Minuted lines found: $nScribeLines\n";
1.1       dbooth    582: @lines = @scribeLines;
                    583: 
                    584: # Verify that we axed all join/leave lines:
                    585: {
                    586: my $all = join("\n", @lines);
                    587: my @matches = ($all =~ m/.*has joined.*\n/g);
                    588: warn "WARNING: Possible join/leave lines remaining: \n\t" . join("\t", @matches)
                    589:        if @matches;
                    590: }
                    591: 
                    592: # Convert from:
                    593: #      <Scribe> DanC: something
                    594: #      <Scribe> DanC: something
                    595: #      <DanC> something
                    596: #      <DanC> something
                    597: #      <Scribe> DanC: something
1.8       dbooth    598: #      <Scribe> ----
                    599: #      <Scribe> Whatever
1.1       dbooth    600: # to:
                    601: #      DanC: something
                    602: #       ... something
                    603: #      <DanC> something
                    604: #       ... something
                    605: #      DanC: something
1.8       dbooth    606: #      ----------------------------------------
                    607: #      <Scribe> Whatever
1.1       dbooth    608: 
                    609: my $prevSpeaker = "UNKNOWN_SPEAKER:";  # "DanC:" or "<DanC>"
                    610: my $prevPattern = quotemeta($prevSpeaker);
                    611: foreach my $line (@lines)
                    612:        {
                    613:        # warn "$line";
                    614:        if ($line =~ m/\A\<$namePattern\>\s*Topic\s*\:/i )
                    615:                {
                    616:                # New topic.
                    617:                # Force the speaker name to be repeated next time
                    618:                $prevSpeaker = "UNKNOWN_SPEAKER:";      # "DanC:" or "<DanC>"
                    619:                $prevPattern = quotemeta($prevSpeaker);
                    620:                }
1.8       dbooth    621:        # Separator:
                    622:        #       <Scribe> ----
                    623:        elsif ($line =~ m/\A(\<$namePattern\>)\s*\-\-\-\-+\s*\Z/i)
                    624:                {
                    625:                my $dashes = '-' x 30;
                    626:                $line = $dashes;
                    627:                }
                    628:        # Separator:
                    629:        #       <Scribe> ====
                    630:        elsif ($line =~ m/\A(\<$namePattern\>)\s*\=\=\=\=+\s*\Z/i)
                    631:                {
                    632:                my $dashes = '=' x 30;
                    633:                $line = $dashes;
                    634:                }
1.1       dbooth    635:        elsif ($line =~ s/\A\<Scribe\>\s*($prevPattern)\s*/ ... /i )
                    636:                {
                    637:                }
                    638:        elsif ($line =~ s/\A\<Scribe\>\s*($speakerPattern\:)\s*/$1 /i )
                    639:                {
                    640:                # New speaker
                    641:                $prevSpeaker = "$1";
                    642:                $prevPattern = quotemeta($prevSpeaker);
                    643:                }
                    644:        elsif ($line =~ s/\A$prevPattern\s*/ ... /i)
                    645:                {
                    646:                }
                    647:        elsif ($line =~ s/\A(\<Scribe\>)\s*/Scribe\: /i )
                    648:                {
                    649:                # Scribe speaks
                    650:                $prevSpeaker = $1;
                    651:                $prevPattern = quotemeta($prevSpeaker);
                    652:                }
                    653:        elsif ($line =~ m/\A(\<$namePattern\>)/i)
                    654:                {
                    655:                $prevSpeaker = $1;
                    656:                $prevPattern = quotemeta($prevSpeaker);
                    657:                }
                    658:        else    {
                    659:                die "DIED FROM UNKNOWN LINE FORMAT: $line\n";
                    660:                }
                    661:        }
                    662: 
1.7       dbooth    663: $all = join("\n", @lines);
                    664: $all = "\n" . $all . "\n";     # Easier pattern matching
1.1       dbooth    665: 
                    666: ##############  Escape < > as &lt; &gt; ################
1.7       dbooth    667: # Escape < and >:
                    668: $all =~ s/\</\&lt\;/g;
                    669: $all =~ s/\>/\&gt\;/g;
                    670: 
                    671: # Format topic titles (i.e., collect agenda):
                    672: my %agenda = ();
                    673: my $itemNum = "item01";
1.15      hugo      674: while ($all =~ s/\n(\&lt\;$namePattern\&gt\;\s+)?Topic\:\s*(.*)\n/\n$preTopicHTML id\=\"$itemNum\"\>$4$postTopicHTML\n/i)
1.7       dbooth    675:        {
                    676:        $agenda{$itemNum} = $4;
                    677:        $itemNum++;
                    678:        }
1.13      dbooth    679: if (!scalar(keys %agenda))     # No "Topic:"s found?
                    680:        {
                    681:        warn "\nWARNING: No \"Topic: ...\" lines found!  \nResulting HTML may have an empty (invalid) <ol>...</ol>.\n\nExplanation: \"Topic: ...\" lines are used to indicate the start of \nnew discussion topics or agenda items, such as:\n<dbooth> Topic: Review of Amy's report\n\n";
                    682:        }
1.7       dbooth    683: my $agenda = "";
                    684: foreach my $item (sort keys %agenda)
                    685:        {
                    686:        $agenda .= '<li><a href="#' . $item . '">' . $agenda{$item} . "</a></li>\n";
                    687:        }
1.1       dbooth    688: 
1.7       dbooth    689: # Break into paragraphs:
1.1       dbooth    690: $all =~ s/\n(([^\ \.\<].*)(\n\ *\.\.+.*)*)/\n$preParagraphHTML\n$1\n$postParagraphHTML\n/g;
                    691: 
1.7       dbooth    692: # Highlight in-line ACTION items:
                    693: $all =~ s/(\n)ACTION\:/$1\<strong\>ACTION\:\<\/strong\>/g;
                    694: $all =~ s/(\n\&lt\;$namePattern\&gt\;\ +)ACTION\:/$1\<strong\>ACTION\:\<\/strong\>/g;
1.1       dbooth    695: 
1.7       dbooth    696: # Bold or <strong> speaker name:
1.1       dbooth    697: # Change "<speaker> ..." to "<b><speaker><b> ..."
                    698: my $preUniq = "PreSpEaKerHTmL";
                    699: my $postUniq = "PostSpEaKerHTmL";
1.7       dbooth    700: $all =~ s/\n(\&lt\;($namePattern)\&gt\;)\s*/\n\&lt\;$preUniq$2$postUniq\&gt\; /ig;
1.1       dbooth    701: # Change "speaker: ..." to "<b>speaker:<b> ..."
1.7       dbooth    702: $all =~ s/\n($speakerPattern)\:\s*/\n$preUniq$1\:$postUniq /ig;
1.1       dbooth    703: $all =~ s/$preUniq/$preSpeakerHTML/g;
                    704: $all =~ s/$postUniq/$postSpeakerHTML/g;
                    705: 
1.7       dbooth    706: # Add <br /> before continuation lines:
                    707: $all =~ s/\n(\ *\.)/ <br \/>\n$1/g;
                    708: # Collapse multiple <br />s:
                    709: $all =~ s/<br \/>((\s*<br \/>)+)/<br \/>/g;
                    710: # Standardize continuation lines:
                    711: # $all =~ s/\n\s*\.+/\n\.\.\./g;
                    712: # Make links:
                    713: $all =~ s/(http\:([^\)\]\}\<\>\s\"\']+))/<a href=\"$1\">$1<\/a>/g;
                    714: 
                    715: # Put into template:
                    716: # $all =~ s/\A\s*<\/p>//;
                    717: # $all .= "\n<\/p>\n";
                    718: my $presentAttendees = join(", ", @present);
                    719: my $regrets = join(", ", @regrets);
1.1       dbooth    720: 
1.5       dbooth    721: my $result = $template;
1.8       dbooth    722: $result =~ s/SV_MEETING_DAY/$day0/g;
                    723: $result =~ s/SV_MEETING_MONTH_ALPHA/$monthAlpha/g;
                    724: $result =~ s/SV_MEETING_YEAR/$year/g;
                    725: $result =~ s/SV_MEETING_MONTH_NUMERIC/$mon0/g;
                    726: $result =~ s/SV_PREVIOUS_MEETING_URL/$previousURL/g;
                    727: $result =~ s/SV_MEETING_CHAIR/$chair/g;
                    728: $result =~ s/SV_MEETING_SCRIBE/$scribeName/g;
                    729: $result =~ s/SV_MEETING_AGENDA/$agenda/g;
                    730: $result =~ s/SV_TEAM_PAGE_LOCATION/SV_TEAM_PAGE_LOCATION/g;
                    731: 
                    732: $result =~ s/SV_REGRETS/$regrets/g;
                    733: $result =~ s/SV_PRESENT_ATTENDEES/$presentAttendees/g;
1.21      dbooth    734: if ($result !~ s/SV_ACTION_ITEMS/$formattedActions/)
                    735:        {
                    736:        if ($result =~ s/SV_NEW_ACTION_ITEMS/$formattedActions/)
                    737:                { warn "WARNING: Template format has changed.  SV_NEW_ACTION_ITEMS should now be SV_ACTION_ITEMS\n"; }
                    738:        else { warn "WARNING: SV_ACTION_ITEMS marker not found in template!\n"; } 
                    739:        }
1.8       dbooth    740: $result =~ s/SV_AGENDA_BODIES/$all/;
                    741: $result =~ s/SV_MEETING_TITLE/$title/g;
1.1       dbooth    742: 
1.8       dbooth    743: my $formattedLogURL = '<p>See also: <a href="SV_MEETING_IRC_URL">IRC log</a></p>';
                    744: if ($logURL eq "SV_MEETING_IRC_URL")
1.1       dbooth    745:        {
                    746:        warn "**** Missing IRC LOG!!!! ****\n";
                    747:        $formattedLogURL = "";
                    748:        }
                    749: $formattedLogURL = "" if $logURL =~ m/\ANone\Z/i;
1.8       dbooth    750: $result =~ s/SV_FORMATTED_IRC_URL/$formattedLogURL/g;
                    751: $result =~ s/SV_MEETING_IRC_URL/$logURL/g;
1.1       dbooth    752:        
1.7       dbooth    753: 
                    754: print $result;
1.5       dbooth    755: exit 0;
1.7       dbooth    756: 
                    757: ##################################################################
                    758: ########################## NormalizerMircTxt #########################
                    759: ##################################################################
                    760: # Format from saving MIRC buffer.
                    761: sub NormalizerMircTxt
                    762: {
                    763: die if @_ != 1;
                    764: my ($all) = @_;
1.8       dbooth    765: # Join continued lines:
                    766: $all =~ s/\n\ \ //g;
                    767: # Fix split URLs:
                    768: #      <RalphS> -> http://lists.w3.org/Archives/Team/w3t-mit/2002Mar/00
                    769: #        46.html Simon's two minutes
                    770: # while ($all =~ s/(http\:[^\ ]+)\n\ \ /$1/ig) {}
                    771: # while ($all =~ s/(http\:[^\ ]+)\n\ /$1/ig) {}
                    772: # Count the number of recognized lines
1.7       dbooth    773: my @lines = split(/\n/, $all);
                    774: my $n = 0;
                    775: my $namePattern = '([\\w\\-]([\\w\\d\\-]*))';
                    776: foreach my $line (@lines)
                    777:        {
1.8       dbooth    778:        # * unlogged comment
                    779:        if ($line =~ m/\A\s*\*\s/) { $n++; }
1.7       dbooth    780:        # <ericn> Discussion on how to progress
1.8       dbooth    781:        elsif ($line =~ m/\A\<$namePattern\>\s/) { $n++; }
1.7       dbooth    782:        # warn "LINE: $line\n";
                    783:        }
                    784: # warn "NormalizerMircTxt n matches: $n\n";
                    785: my $score = $n / @lines;
                    786: return($score, $all);
                    787: }
                    788: 
                    789: ##################################################################
1.8       dbooth    790: ########################## NormalizerRRSAgentText #########################
1.7       dbooth    791: ##################################################################
                    792: # Example: http://www.w3.org/2003/03/03-ws-desc-irc.txt
1.8       dbooth    793: sub NormalizerRRSAgentText
1.7       dbooth    794: {
                    795: die if @_ != 1;
                    796: my ($all) = @_;
1.8       dbooth    797: # Join continued lines:
                    798: $all =~ s/\n\ \ //g;
                    799: # Count the number of recognized lines
1.7       dbooth    800: my @lines = split(/\n/, $all);
                    801: my $n = 0;
                    802: my $namePattern = '([\\w\\-]([\\w\\d\\-]*))';
                    803: my $timePattern = '((\s|\d)\d\:(\s|\d)\d\:(\s|\d)\d)';
                    804: foreach my $line (@lines)
                    805:        {
                    806:        # 20:41:27 <ericn> Review of minutes 
                    807:        $n++ if $line =~ s/\A$timePattern\s+(\<$namePattern\>\s)/$5/;
                    808:        # warn "LINE: $line\n";
                    809:        }
1.9       dbooth    810: $all = join("\n", @lines);
1.8       dbooth    811: # warn "NormalizerRRSAgentText n matches: $n\n";
1.7       dbooth    812: my $score = $n / @lines;
                    813: return($score, $all);
                    814: }
                    815: 
                    816: ##################################################################
                    817: ########################## NormalizerRRSAgentHtml #########################
                    818: ##################################################################
                    819: # Example: http://www.w3.org/2003/03/03-ws-desc-irc.html
                    820: sub NormalizerRRSAgentHtml
                    821: {
                    822: die if @_ != 1;
                    823: my ($all) = @_;
                    824: my @lines = split(/\n/, $all);
                    825: my $n = 0;
                    826: my $namePattern = '([\\w\\-]([\\w\\d\\-]*))';
                    827: my $timePattern = '((\s|\d)\d\:(\s|\d)\d\:(\s|\d)\d)';
                    828: foreach my $line (@lines)
                    829:        {
                    830:        # <dt id="T14-35-34">14:35:34 [scribe]</dt><dd>Gudge: why not sufficient?</dd>
                    831:        $n++ if $line =~ s/\A\<dt\s+id\=\"($namePattern)\"\>$timePattern\s+\[($namePattern)\]\<\/dt\>\s*\<dd\>(.*)\<\/dd\>\s*\Z/\<$8\> $11/;
                    832:        # warn "LINE: $line\n";
                    833:        }
                    834: $all = join("\n", @lines);
                    835: # warn "NormalizerRRSAgentHtml n matches: $n\n";
                    836: my $score = $n / @lines;
                    837: # Unescape &entity;
                    838: $all =~ s/\&amp\;/\&/g;
                    839: $all =~ s/\&lt\;/\</g;
                    840: $all =~ s/\&gt\;/\>/g;
                    841: $all =~ s/\&quot\;/\"/g;
                    842: return($score, $all);
                    843: }
                    844: 
                    845: ##################################################################
                    846: ########################## NormalizerRRSAgentHTMLText #########################
                    847: ##################################################################
                    848: # This is for the format that is displayed in the browser from HTML.
                    849: # I.e., when you display the HTML in a browser, and then copy and paste
                    850: # the text from the browser window.
                    851: sub NormalizerRRSAgentHTMLText
                    852: {
                    853: die if @_ != 1;
                    854: my ($all) = @_;
                    855: my $n = 0;
                    856: my $namePattern = '([\\w\\-]([\\w\\d\\-]*))';
                    857: my $timePattern = '((\s|\d)\d\:(\s|\d)\d\:(\s|\d)\d)';
                    858: my $done = "";
                    859: while($all =~ s/\A((.*\n)(.*\n))//)    # Grab next two lines
                    860:        {
                    861:        my $linePair = $1;
                    862:        my $line1 = $2;
                    863:        my $line2 = $3;
                    864:        # This format uses line pairs:
                    865:        #       14:43:30 [Arthur]
                    866:        #       If it's abstract, it goes into portType 
                    867:        if ($linePair =~ s/\A($timePattern)\s+\[($namePattern)\][\ \t]*\n/\<$6\> /)
                    868:                {
                    869:                $n++;
                    870:                # warn "LINE: $line\n";
                    871:                $done .= $linePair;
                    872:                }
                    873:        else    {
                    874:                $done .= $line1;
                    875:                $all = $line2 . $all;
                    876:                }
                    877:        }
                    878: $done .= $all;
                    879: $all = $done;
                    880: # warn "NormalizerRRSAgentHTMLText n matches: $n\n";
                    881: my @lines = split(/\n/, $all);
                    882: my $score = $n / @lines;
                    883: return($score, $all);
                    884: }
                    885: 
                    886: ##################################################################
                    887: ########################## NormalizerYahoo #########################
                    888: ##################################################################
                    889: sub NormalizerYahoo
                    890: {
                    891: die if @_ != 1;
                    892: my ($all) = @_;
                    893: my @lines = split(/\n/, $all);
                    894: my $n = 0;
                    895: my $namePattern = '([\\w\\-]([\\w\\d\\-]*))';
                    896: foreach my $line (@lines)
                    897:        {
                    898:        $n++ if $line =~ s/\A($namePattern)\:\s/\<$1\> /;
                    899:        # warn "LINE: $line\n";
                    900:        }
                    901: $all = join("\n", @lines);
                    902: # warn "NormalizerYahoo n matches: $n\n";
                    903: my $score = $n / @lines;
                    904: return($score, $all);
                    905: }
                    906: 
1.5       dbooth    907: ##################################################################
1.20      dbooth    908: ########################## NormalizerPlainText #########################
                    909: ##################################################################
                    910: # This is just a plain text file of notes made by the scribe.
                    911: # This format does NOT use timestamps, nor does it use <speakerName>
                    912: # at the beginning of each line.  It does still use the "dbooth: ..."
                    913: # convention to indicate what someone said.
                    914: sub NormalizerPlainText
                    915: {
                    916: die if @_ != 1;
                    917: my ($all) = @_;
                    918: # Join continued lines:
                    919: # Count the number of recognized lines
                    920: my @lines = split(/\n/, $all);
                    921: my $n = 0;
                    922: my $timePattern = '((\s|\d)\d\:(\s|\d)\d\:(\s|\d)\d)';
                    923: my $namePattern = '([\\w\\-]([\\w\\d\\-]*))';
                    924: for (my $i=0; $i<@lines; $i++)
                    925:        {
                    926:        # Lines should NOT have timestamps:
                    927:        #       20:41:27 <ericn> Review of minutes 
                    928:        next if $lines[$i] =~ m/\A$timePattern\s+/;
                    929:        # Lines should NOT start with <speakerName>:
                    930:        #       <ericn> Review of minutes 
                    931:        next if $lines[$i] =~ m/\A(\<$namePattern\>\s)/;
                    932:        # warn "LINE: $lines[$i]\n";
                    933:        $n++;
                    934:        }
                    935: # Now add "<scribe> " to the beginning of each line, to make it like
                    936: # the standard format.
                    937: for (my $i=0; $i<@lines; $i++)
                    938:        {
                    939:        $lines[$i] = "<scribe> " . $lines[$i];
                    940:        }
                    941: $all = join("\n", @lines);
                    942: # warn "NormalizerPlainText n matches: $n\n";
                    943: my $score = $n / @lines;
                    944: return($score, $all);
                    945: }
                    946: 
                    947: ##################################################################
1.7       dbooth    948: ##################### GetTemplate ####################
1.5       dbooth    949: ##################################################################
1.7       dbooth    950: sub GetTemplate
                    951: {
1.1       dbooth    952: @_ == 1 || die;
                    953: my ($templateFile) = @_;
1.7       dbooth    954: open($templateFile,"<$templateFile") || return "";
                    955: my $template = join("",<$templateFile>);
1.1       dbooth    956: $template =~ s/\r//g;
1.7       dbooth    957: close($templateFile);
                    958: return $template;
                    959: }
                    960: 
1.5       dbooth    961: ##################################################################
1.7       dbooth    962: ######################## GetDate ####################
1.5       dbooth    963: ##################################################################
1.10      dbooth    964: # Grab date from $all or IRC log name or default to today's date.
1.7       dbooth    965: sub GetDate
                    966: {
1.10      dbooth    967: @_ == 3 || die;
                    968: my ($all, $namePattern, $logURL) = @_;
1.7       dbooth    969: my @days = qw(Sun Mon Tue Wed Thu Fri Sat); 
                    970: @days == 7 || die;
                    971: my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
                    972: @months == 12 || die;
1.1       dbooth    973: my %monthNumbers = map {($months[$_], $_+1)} (0 .. 11);
                    974: # warn "GetDate monthNumbers: ",join(" ",%monthNumbers),"\n";
                    975: my @date = ();
                    976: # Look for Date: 12 Sep 2002
1.7       dbooth    977: if ($all =~ s/\n\<$namePattern\>\s*(Date)\s*\:\s*(.*)\n/\n/i)
1.1       dbooth    978:        {
                    979:        # Parse date from input.
                    980:        # I should have used a library function for this, but I wrote
                    981:        # this without net access, so I couldn't get one.
                    982:        my $d = $4;
                    983:        $d =~ s/\A\s+//;
                    984:        $d =~ s/\s+\Z//;
                    985:        warn "Found Date: $d\n";
                    986:        my @words = split(/\s+/, $d);
                    987:        die "ERROR: Date not understood: $d\n" if @words != 3;
                    988:        my ($mday, $tmon, $year) = @words;
                    989:        exists($monthNumbers{$tmon}) || die;
                    990:        my $mon = $monthNumbers{$tmon};
1.7       dbooth    991:        ($mon > 0 && $mon < 13) || die;
                    992:        ($mday > 0 && $mday < 32) || die;
1.1       dbooth    993:        ($year > 2000 && $year < 2100) || die;
1.7       dbooth    994:        my $day0 = sprintf("%0d", $mday);
                    995:        my $mon0 = sprintf("%0d", $mon);
1.1       dbooth    996:        @date = ($day0, $mon0, $year, $tmon);
                    997:        }
1.10      dbooth    998: # Figure out date from IRC log name:
                    999: elsif ($logURL =~ m/\Ahttp\:\/\/(www\.)?w3\.org\/(\d+)\/(\d+)\/(\d+).+\-irc/)
                   1000:        {
                   1001:        my $year = $2;
                   1002:        my $mon = $3;
                   1003:        my $mday = $4;
                   1004:        ($mon > 0 && $mon < 13) || die;
                   1005:        ($year > 2000 && $year < 2100) || die;
                   1006:        ($mday > 0 && $mday < 32) || die;
                   1007:        my $day0 = sprintf("%0d", $mday);
                   1008:        my $mon0 = sprintf("%0d", $mon);
                   1009:        @date = ($day0, $mon0, $year, $months[$mon-1]);
                   1010:        warn "Got date from IRC log name: $day0 " . $months[$mon-1] . " $year\n";
                   1011:        }
1.1       dbooth   1012: else
                   1013:        {
1.20      dbooth   1014:        warn "WARNING: No date found!  Assuming today.  (Hint: Specify\n";
                   1015:        warn "the IRC log, and the date will be determined from that.)\n";
                   1016:        warn "Or specify the date like this:\n";
                   1017:        warn "<dbooth> Date: 12 Sep 2002\n\n";
1.1       dbooth   1018:        # Assume today's date by default.
1.7       dbooth   1019:        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
                   1020:        $mon++; # put in range [1..12] instead of [0..11].
                   1021:        ($mon > 0 && $mon < 13) || die;
                   1022:        $year += 1900;
1.1       dbooth   1023:        ($year > 2000 && $year < 2100) || die;
1.7       dbooth   1024:        ($mday > 0 && $mday < 32) || die;
                   1025:        my $day0 = sprintf("%0d", $mday);
                   1026:        my $mon0 = sprintf("%0d", $mon);
1.1       dbooth   1027:        @date = ($day0, $mon0, $year, $months[$mon-1]);
                   1028:        }
                   1029: # warn "GetDate Returning date info: @date\n";
1.20      dbooth   1030: return @date;
1.7       dbooth   1031: }
                   1032: 
1.1       dbooth   1033: 
                   1034: 
1.5       dbooth   1035: ##################################################################
1.7       dbooth   1036: ###################### GetNames ######################
1.5       dbooth   1037: ##################################################################
1.7       dbooth   1038: # Look for people in IRC log.
                   1039: sub GetNames
                   1040: {
                   1041: @_ == 2 || die;
                   1042: my($all, $scribe) = @_;
                   1043: 
                   1044: # Some important data/constants:
                   1045: my @rooms = qw(MIT308 SophiaSofa DISA);
1.1       dbooth   1046: 
1.20      dbooth   1047: my @stopList = qw(a q on Re items Zakim Topic muted and agenda Regrets http the
1.17      dbooth   1048:        RRSAgent Loggy Zakim2 ACTION Chair Meeting DONE PENDING WITHDRAWN
                   1049:        Scribe 00AM 00PM P IRC Topics Keio DROPPED ger-logger
1.3       dbooth   1050:        yes no abstain Consensus Participants Question RESOLVED strategy
1.17      dbooth   1051:        AGREED Date speaker queue no one in XachBot got it WARNING);
1.1       dbooth   1052: @stopList = (@stopList, @rooms);
1.7       dbooth   1053: @stopList = map {tr/A-Z/a-z/; $_} @stopList;   # Make stopList lower case
1.1       dbooth   1054: my %stopList = map {($_,$_)} @stopList;
                   1055: 
1.7       dbooth   1056: my %synonyms = ();
                   1057: %synonyms = qw(
                   1058:        alan-home       Alan
                   1059:        amymtg          Amy
                   1060:        caribou         Carine
                   1061:         Chaals         Charles
                   1062:         chaalsGVA      Charles
                   1063:         chaalsNCE      Charles
                   1064:        Dave            DaveRaggett
                   1065:        DaveR           DaveRaggett
                   1066:        DaveRagget      DaveRaggett
                   1067:        dbooth          dbooth
                   1068:        David           dbooth
                   1069:        DB              dbooth
                   1070:        DavidB          dbooth
                   1071:         danbri         DanBri
                   1072:        danc_           DanC
                   1073:        danc            DanC
                   1074:        dand            DanielD
                   1075:        EM              EricM
                   1076:        Eric            EricM
                   1077:        ericP-WA        EricP
                   1078:        ericP-air       EricP
                   1079:        ger             Gerald
1.1       dbooth   1080:        HT              Henry
                   1081:        HH              Hugo
1.7       dbooth   1082:        hugo            Hugo
                   1083:        ivan            Ivan
1.1       dbooth   1084:        janSeaTac       Janet
1.7       dbooth   1085:        karl            Karl
                   1086:        reagle-tu       JosephR
                   1087:        reagleMIT       JosephR
                   1088:        Joseph          JosephR
                   1089:        judy            Judy
1.1       dbooth   1090:        MaxF            MaxF
1.7       dbooth   1091:        MCF             Marie-Claire
                   1092:        MC              Marie-Claire
                   1093:        marie           Marie-Claire
                   1094:        mari            Marisol
                   1095:        MSM             MichaelSM
                   1096:        MSMbot          MichaelSM
                   1097:        Michael         MichaelSM
1.1       dbooth   1098:        MJDuesrst       Martin
                   1099:        olivier         Olivier
1.7       dbooth   1100:        plh             Philippe
                   1101:        plh-sjc         Philippe
                   1102:        Ralph           RalphS
1.1       dbooth   1103:        rigo            Rigo
                   1104:        sandro          Sandro
1.7       dbooth   1105:        sim             Simon
                   1106:        simHOME         Simon
                   1107:        Hernandez       Simon
1.1       dbooth   1108:        SB              Steve
1.7       dbooth   1109:        SusanL          SusanLesch
                   1110:        Ted             Ted
                   1111:        Tim             TimBL
1.1       dbooth   1112:        VQ              VincentQ
1.7       dbooth   1113:        ) if $useTeamSynonyms;
                   1114: foreach my $n (values %synonyms)
                   1115:        {
                   1116:        $synonyms{$n} = $n;     # Make TimBL --> TimBL
                   1117:        }
                   1118: 
                   1119: # Easier pattern matching:
                   1120: $all = "\n" . $all . "\n";
1.1       dbooth   1121: 
1.7       dbooth   1122: # Now start collecting names.
                   1123: my %names =  ();
                   1124: my $t; # Temp
                   1125: 
                   1126: #        ...something.html Simon's two minutes
                   1127: $t = $all;
1.1       dbooth   1128: my $namePattern = '([\\w\\-]([\\w\\d\\-]*))';
                   1129: # warn "namePattern: $namePattern\n";
1.7       dbooth   1130: while($t =~ s/\b($namePattern)\'s\s+(2|two)\s+ minutes/ /)
                   1131:        {
                   1132:        my $n = $1;
                   1133:        $names{$n} = $n;
                   1134:        }
                   1135: 
                   1136: #      <dbooth> MC: I have integrated most of the coments i received 
                   1137: $t = $all;
                   1138: while($t =~ s/\n\<((\w|\-)+)\>(\ +)((\w|\-)+)\:/\n/i)
                   1139:        {
                   1140:        my $n = $4;
                   1141:        next if exists($names{$n});
                   1142:        # warn "Matched #       <dbooth> $n" . ": ...\n";
                   1143:        $names{$n} = $n;
                   1144:        }
1.1       dbooth   1145: # warn "names: ",join(" ",keys %names),"\n";
1.7       dbooth   1146: 
                   1147: #      <Steven> Hello 
                   1148: $t = $all;
                   1149: while($t =~ s/\n\<((\w|\-)+)\>/\n/)
                   1150:        {
                   1151:        my $n = $1;
                   1152:        next if exists($names{$n});
                   1153:        # warn "Matched #       <$n>\n";
                   1154:        $names{$n} = $n;
1.1       dbooth   1155:        # warn "Found name: $n\n";
1.7       dbooth   1156:        }
1.1       dbooth   1157: 
                   1158: #      Zakim sees 4 items remaining on the agenda
1.7       dbooth   1159: $t = $all;
1.1       dbooth   1160: while ($t =~ s/\n\s*((\<Zakim\>)|(\*\s*Zakim\s)).*\b(agenda|agendum)\b.*\n/\n/)
                   1161:        {
                   1162:        my $match = $&;
                   1163:        $match =~ s/\A\s+//;
                   1164:        $match =~ s/\s+\Z//;
                   1165:        # warn "DELETED: $match\n";
                   1166:        }
                   1167: 
                   1168: #      <Zakim> I see no one on the speaker queue
                   1169: #      <Zakim> I see Hugo, Yves, Philippe on the speaker queue
1.7       dbooth   1170: #      <Zakim> I see MIT308, Ivan, Marie-Claire, Steven, Janet, EricM
1.17      dbooth   1171: #      <Zakim> On the phone I see Joseph, m3mSEA, MIT308, Marja
                   1172: #      <Zakim> On IRC I see Nobu, SusanL, RRSAgent, ht, Ian, ericP
                   1173: #      <Zakim> ... simonMIT, XachBot
1.7       dbooth   1174: #      <Zakim> I see MIT308, Ivan, Marie-Claire, Steven, Janet, EricM
1.17      dbooth   1175: #      <Zakim> MIT308 has Martin, Ted, Ralph, Alan, EricP, Vivien
                   1176: #      <Zakim> +Carine, Yves, Hugo; got it
                   1177: #
                   1178: # The stopList will later eliminate "no one" or "on the speaker queue",
                   1179: # so we don't need to worry about them here.
                   1180: while($t =~ s/\n\<Zakim\>\s+((([\w\d\_][\w\d\_\-]+) has\s+)|(I see\s+)|(On the phone I see\s+)|(On IRC I see\s+)|(\.\.\.\s+)|(\+))(.*)\n/\n/)
1.7       dbooth   1181:        {
1.17      dbooth   1182:        my $list = $9;
1.1       dbooth   1183:        $list =~ s/\A\s+//;
                   1184:        $list =~ s/\s+\Z//;
1.7       dbooth   1185:        my @names = split(/[^\w\-]+/, $list);
                   1186:        @names = map {s/\A\s+//; s/\s+\Z//; $_} @names;
                   1187:        @names = grep {$_} @names;
                   1188:        # warn "Matched #       <Zakim> I see: @names\n";
                   1189:        foreach my $n (@names)
                   1190:                {
                   1191:                next if exists($names{$n});
                   1192:                $names{$n} = $n;
                   1193:                }
                   1194:        }
                   1195: 
                   1196: # Make the keys all lower case, so that they'll match:
                   1197: %synonyms = map {my $oldn = $_; tr/A-Z/a-z/; ($_, $synonyms{$oldn})} keys %synonyms;
                   1198: %names = map {my $oldn = $_; tr/A-Z/a-z/; ($_, $names{$oldn})} keys %names;
                   1199: # warn "Lower case name keys:\n";
                   1200: foreach my $n (sort keys %names)
                   1201:        {
                   1202:        # warn "        $n      $names{$n}\n";
                   1203:        }
                   1204: 
                   1205: # Map synonyms to standardize spellings and expand abbreviations.
                   1206: %names = map   {
                   1207:                exists($synonyms{$_}) 
                   1208:                        ? ($_, $synonyms{$_}) 
                   1209:                        : ($_, $names{$_})
                   1210:                } keys %names;
                   1211: # At this point different keys may map to the same value.
                   1212: # warn "After applying synonyms:\n";
                   1213: foreach my $n (keys %names)
                   1214:        {
                   1215:        # warn "        $n      $names{$n}\n";
                   1216:        }
1.1       dbooth   1217: 
                   1218: # Eliminate non-names
1.7       dbooth   1219: foreach my $n (keys %names)
                   1220:        {
1.1       dbooth   1221:        # Filter out names in stopList
                   1222:        if (exists($stopList{$n})) { delete $names{$n}; }
                   1223:        # Filter out names less than two chars in length:
                   1224:        elsif (length($n) < 2) { delete $names{$n}; }
                   1225:        # Filter out names not starting with a letter
                   1226:        elsif ($names{$n} !~ m/\A[a-zA-Z]/) { delete $names{$n}; }
1.7       dbooth   1227:        }
1.1       dbooth   1228: 
1.7       dbooth   1229: # Make a list of unique names for the attendee list:
                   1230: my %uniqNames = ();
                   1231: foreach my $n (values %names)
                   1232:        {
                   1233:        $uniqNames{$n} = $n;
                   1234:        }
1.1       dbooth   1235: 
                   1236: # Make a list of all names seen (all variations) in lower case:
                   1237: my %allNames = ();
                   1238: foreach my $n (%names, %synonyms)
                   1239:        {
                   1240:        my $name = $n;
                   1241:        $name =~ tr/A-Z/a-z/;
                   1242:        $allNames{$name} = $name;
                   1243:        }
                   1244: @allNames = sort keys %allNames;
                   1245: # warn "allNames: @allNames\n";
                   1246: my @allNameRefs = map { \$_ } @allNames;
                   1247: 
1.7       dbooth   1248: # Canonicalize (normalize) the names in the IRC:
                   1249: my $expandedIRC = $all;
                   1250: my $nExpanded = 0;
                   1251: foreach my $n (keys %synonyms)
                   1252:        {
1.1       dbooth   1253:        next if !$canonicalizeNames;
1.7       dbooth   1254:        my $v = $synonyms{$n};
                   1255:        next if $v eq $n;
                   1256:        my $qn = quotemeta($n);
                   1257:        if ($expandedIRC =~ s/([^\w\-])($qn)([^\w\-])/$1$v$3/ig)
                   1258:                {
                   1259:                $nExpanded++;
                   1260:                # warn "============ START ============\n";
                   1261:                # warn "        $expandedIRC\n";
                   1262:                # warn "============ STOP ============\n";
                   1263:                }
                   1264:        }
                   1265: # warn "nExpanded: $nExpanded\n";
                   1266: 
1.1       dbooth   1267: my $lcScribe = $scribe;
1.7       dbooth   1268: $lcScribe =~ tr/A-Z/a-z/;
                   1269: $scribe = $synonyms{$lcScribe} if exists($synonyms{$lcScribe});
                   1270: my @sortedUniqNames = sort values %uniqNames;
                   1271: return($expandedIRC, $scribe, \@allNameRefs, @sortedUniqNames);
                   1272: }
1.1       dbooth   1273: 
1.5       dbooth   1274: ##################################################################
1.1       dbooth   1275: ################ DefaultTemplate ####################
1.5       dbooth   1276: ##################################################################
1.1       dbooth   1277: sub DefaultTemplate
                   1278: {
1.5       dbooth   1279: return &PublicTemplate();
                   1280: }
                   1281: 
                   1282: ##################################################################
                   1283: ####################### SampleInput ##############################
                   1284: ##################################################################
                   1285: sub SampleInput
                   1286: {
                   1287: my $sampleInput = <<'SampleInput-EOF'
1.7       dbooth   1288: <dbooth> Scribe: dbooth
                   1289: <dbooth> Chair: Jonathan
                   1290: <dbooth> Meeting: Weekly Baking Club Meeting
1.5       dbooth   1291: <dbooth> Date: 05 Dec 2002
                   1292: <dbooth> Topic: Review of Action Items
1.7       dbooth   1293: <Philippe> PENDING ACTION: Barbara to bake 3 pies 
1.8       dbooth   1294: <Philippe> ----
1.7       dbooth   1295: <Philippe> DONE ACTION: David to make ice cream 
1.20      dbooth   1296: <Philippe> ACTION: David to make frosting -- DONE
                   1297: <Philippe> ACTION: David to make candles  *DONE*
                   1298: <Philippe> ACTION: David to make world peace  *PENDING*
1.5       dbooth   1299: <dbooth> Topic: What to Eat for Dessert
1.7       dbooth   1300: <dbooth> Joseph: I think that we should all eat cake
                   1301: <dbooth> ... with ice creme.
1.5       dbooth   1302: <dbooth> s/creme/cream/
                   1303: <Philippe> That's a good idea
                   1304: <dbooth> ACTION: dbooth to send a message to himself about action items
                   1305: <dbooth> Topic: Next Week's Meeting
                   1306: <Philippe> I think we should do this again next week.
                   1307: <Jonathan> Sounds good to me.
                   1308: <dbooth> rrsagent, where am i?
                   1309: <RRSAgent> I am logging.
1.7       dbooth   1310: <RRSAgent> See http://www.w3.org/2002/11/07-ws-arch-irc#T13-59-36
1.5       dbooth   1311: SampleInput-EOF
                   1312: ;
                   1313: return $sampleInput;
                   1314: }
                   1315: 
                   1316: ##################################################################
                   1317: ###################### PublicTemplate ############################
                   1318: ##################################################################
                   1319: sub PublicTemplate
                   1320: {
                   1321: my $template = <<'PublicTemplate-EOF'
1.16      dbooth   1322: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
1.7       dbooth   1323: <html>
                   1324: <head>
1.8       dbooth   1325:   <title>SV_MEETING_TITLE -- SV_MEETING_DAY SV_MEETING_MONTH_ALPHA SV_MEETING_YEAR</title>
1.7       dbooth   1326:   <LINK rel="STYLESHEET" href="http://www.w3.org/StyleSheets/base.css">
                   1327:   <LINK rel="STYLESHEET" href="http://www.w3.org/StyleSheets/public.css">
1.8       dbooth   1328:   <meta content="SV_MEETING_TITLE" lang="en" name="Title">  
1.7       dbooth   1329:   <meta content="text/html; charset=iso-8859-1" http-equiv="Content-Type">
                   1330: </head>
                   1331: 
                   1332: <body>
                   1333: <p><a href="http://www.w3.org/"><img src="http://www.w3.org/Icons/WWW/w3c_home" alt="W3C" border="0"
1.1       dbooth   1334: height="48" width="72"></a> 
                   1335: 
1.7       dbooth   1336: </p>
                   1337: 
1.8       dbooth   1338: <h1>SV_MEETING_TITLE<br>
                   1339: SV_MEETING_DAY SV_MEETING_MONTH_ALPHA SV_MEETING_YEAR</h1>
1.7       dbooth   1340: 
1.1       dbooth   1341: <!-- Old:
1.8       dbooth   1342: <p>See also: <a href="SV_MEETING_IRC_URL">IRC log</a></p>
1.1       dbooth   1343: -->
1.8       dbooth   1344: SV_FORMATTED_IRC_URL
1.7       dbooth   1345: 
                   1346: <h2><a name="attendees">Attendees</a></h2>
                   1347: 
                   1348: <div class="intro">
1.8       dbooth   1349: <p>Present: SV_PRESENT_ATTENDEES</p>
                   1350: <p>Regrets: SV_REGRETS</p>
                   1351: <p>Chair: SV_MEETING_CHAIR </p>
                   1352: <p>Scribe: SV_MEETING_SCRIBE</p>
1.7       dbooth   1353: </div>
                   1354: 
                   1355: <h2>Contents</h2>
                   1356: <ul>
1.11      dbooth   1357:   <li><a href="#agenda">Topics</a>
1.7       dbooth   1358:        <ol>
1.8       dbooth   1359:        SV_MEETING_AGENDA
1.7       dbooth   1360:        </ol>
                   1361:   </li>
                   1362:   <li><a href="#newActions">Summary of Action Items</a></li>
                   1363: </ul>
                   1364: <hr>
                   1365: 
                   1366: 
1.1       dbooth   1367: <!--  We don't need the agenda items listed twice.
1.11      dbooth   1368: <h2><a name="agenda">Topics</a></h2>
1.7       dbooth   1369: <ul>
1.8       dbooth   1370:   SV_MEETING_AGENDA
1.7       dbooth   1371: </ul>
1.1       dbooth   1372: -->
1.7       dbooth   1373: 
1.8       dbooth   1374: SV_AGENDA_BODIES
1.7       dbooth   1375: <!--
1.15      hugo     1376: <h3 id="item1">Item name (owner)</h3>
1.7       dbooth   1377: <p>... text of discussion ...</p>
                   1378: 
1.15      hugo     1379: <h3 id="item2">Item name (owner)</h3>
1.7       dbooth   1380: <p>... text of discussion ...</p>
                   1381: -->
                   1382: 
                   1383: 
                   1384: <h2><a name="newActions">Summary of Action Items</a></h2>
1.21      dbooth   1385: <!-- Action Items -->
                   1386: SV_ACTION_ITEMS
1.7       dbooth   1387: 
                   1388: <hr>
                   1389: 
                   1390: <address>
1.1       dbooth   1391:   <!--
1.7       dbooth   1392:   <a href="http://validator.w3.org/check/referer"><img border="0"
                   1393:   src="http://validator.w3.org/images/vh40.gif" alt="Valid HTML 4.0!"
                   1394:   height="31" width="88" align="right">
                   1395:   </a> 
1.1       dbooth   1396:   -->
1.8       dbooth   1397:   <!-- <a href="/Team/SV_TEAM_PAGE_LOCATION">David Booth</a> <br /> -->
1.6       dbooth   1398:   Minutes formatted by David Booth's perl script: 
1.7       dbooth   1399:   <a href="http://dev.w3.org/cvsweb/~checkout~/2002/scribe/">http://dev.w3.org/cvsweb/~checkout~/2002/scribe/</a><br>
1.20      dbooth   1400:   $Date: 2003/06/13 21:02:20 $ 
1.7       dbooth   1401: </address>
                   1402: </body>
                   1403: </html>
1.5       dbooth   1404: PublicTemplate-EOF
                   1405: ;
                   1406: return $template;
                   1407: }
                   1408: 
                   1409: ##################################################################
                   1410: ###################### MemberTemplate ############################
                   1411: ##################################################################
                   1412: sub MemberTemplate
                   1413: {
                   1414: my $template = <<'MemberTemplate-EOF'
1.16      dbooth   1415: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
1.7       dbooth   1416: <html>
                   1417: <head>
1.8       dbooth   1418:   <title>SV_MEETING_TITLE -- SV_MEETING_DAY SV_MEETING_MONTH_ALPHA SV_MEETING_YEAR</title>
1.7       dbooth   1419:   <LINK rel="STYLESHEET" href="http://www.w3.org/StyleSheets/base.css">
                   1420:   <LINK rel="STYLESHEET" href="http://www.w3.org/StyleSheets/member.css">
                   1421:   <link rel="STYLESHEET" href="http://www.w3.org/StyleSheets/member-minutes.css">
1.8       dbooth   1422:   <meta content="SV_MEETING_TITLE" lang="en" name="Title">  
1.7       dbooth   1423:   <meta content="text/html; charset=iso-8859-1" http-equiv="Content-Type">
                   1424: </head>
                   1425: 
                   1426: <body>
                   1427: <p><a href="http://www.w3.org/"><img src="http://www.w3.org/Icons/WWW/w3c_home" alt="W3C" border="0"
1.5       dbooth   1428: height="48" width="72"></a> 
1.7       dbooth   1429: </p>
                   1430: 
1.8       dbooth   1431: <h1>SV_MEETING_TITLE<br>
                   1432: SV_MEETING_DAY SV_MEETING_MONTH_ALPHA SV_MEETING_YEAR</h1>
1.7       dbooth   1433: 
1.5       dbooth   1434: <!-- Old:
1.8       dbooth   1435: <p>See also: <a href="SV_MEETING_IRC_URL">IRC log</a></p>
1.5       dbooth   1436: -->
1.8       dbooth   1437: SV_FORMATTED_IRC_URL
1.7       dbooth   1438: 
                   1439: <h2><a name="attendees">Attendees</a></h2>
                   1440: 
                   1441: <div class="intro">
1.8       dbooth   1442: <p>Present: SV_PRESENT_ATTENDEES</p>
                   1443: <p>Regrets: SV_REGRETS</p>
                   1444: <p>Chair: SV_MEETING_CHAIR </p>
                   1445: <p>Scribe: SV_MEETING_SCRIBE</p>
1.7       dbooth   1446: </div>
                   1447: 
                   1448: <h2>Contents</h2>
                   1449: <ul>
1.11      dbooth   1450:   <li><a href="#agenda">Topics</a>
1.7       dbooth   1451:        <ol>
1.8       dbooth   1452:        SV_MEETING_AGENDA
1.7       dbooth   1453:        </ol>
                   1454:   </li>
                   1455:   <li><a href="#newActions">Summary of Action Items</a></li>
                   1456: </ul>
                   1457: <hr>
                   1458: 
                   1459: 
1.5       dbooth   1460: <!--  We don't need the agenda items listed twice.
1.11      dbooth   1461: <h2><a name="agenda">Topics</a></h2>
1.7       dbooth   1462: <ul>
1.8       dbooth   1463:   SV_MEETING_AGENDA
1.7       dbooth   1464: </ul>
                   1465: -->
                   1466: 
1.8       dbooth   1467: SV_AGENDA_BODIES
1.7       dbooth   1468: <!--
1.15      hugo     1469: <h3 id="item1">Item name (owner)</h3>
1.7       dbooth   1470: <p>... text of discussion ...</p>
                   1471: 
1.15      hugo     1472: <h3 id="item2">Item name (owner)</h3>
1.7       dbooth   1473: <p>... text of discussion ...</p>
1.5       dbooth   1474: -->
1.7       dbooth   1475: 
                   1476: 
                   1477: <h2><a name="newActions">Summary of Action Items</a></h2>
1.5       dbooth   1478: <!-- Done Action Items -->
1.8       dbooth   1479: SV_DONE_ACTION_ITEMS
1.5       dbooth   1480: <!-- Pending Action Items -->
1.8       dbooth   1481: SV_PENDING_ACTION_ITEMS
1.5       dbooth   1482: <!-- New Action Items -->
1.21      dbooth   1483: SV_ACTION_ITEMS
1.7       dbooth   1484: 
                   1485: <hr>
                   1486: 
                   1487: <address>
1.5       dbooth   1488:   <!--
1.7       dbooth   1489:   <a href="http://validator.w3.org/check/referer"><img border="0"
                   1490:   src="http://validator.w3.org/images/vh40.gif" alt="Valid HTML 4.0!"
                   1491:   height="31" width="88" align="right">
                   1492:   </a> 
1.5       dbooth   1493:   -->
1.8       dbooth   1494:   <!-- <a href="/Team/SV_TEAM_PAGE_LOCATION">David Booth</a> <br /> -->
1.6       dbooth   1495:   Minutes formatted by David Booth's perl script: 
1.7       dbooth   1496:   <a href="http://dev.w3.org/cvsweb/~checkout~/2002/scribe/">http://dev.w3.org/cvsweb/~checkout~/2002/scribe/</a><br>
1.20      dbooth   1497:   $Date: 2003/06/13 21:02:20 $ 
1.7       dbooth   1498: </address>
                   1499: </body>
                   1500: </html>
1.5       dbooth   1501: MemberTemplate-EOF
1.1       dbooth   1502: ;
1.5       dbooth   1503: return $template;
1.1       dbooth   1504: }
                   1505: 
1.5       dbooth   1506: ##################################################################
                   1507: ###################### TeamTemplate ############################
                   1508: ##################################################################
                   1509: sub TeamTemplate
                   1510: {
                   1511: my $template = <<'TeamTemplate-EOF'
1.16      dbooth   1512: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
1.7       dbooth   1513: <html>
                   1514: <head>
1.8       dbooth   1515:   <title>SV_MEETING_TITLE -- SV_MEETING_DAY SV_MEETING_MONTH_ALPHA SV_MEETING_YEAR</title>
1.7       dbooth   1516:   <LINK rel="STYLESHEET" href="http://www.w3.org/StyleSheets/base.css">
                   1517:   <LINK rel="STYLESHEET" href="http://www.w3.org/StyleSheets/team.css">
                   1518:   <link rel="STYLESHEET" href="http://www.w3.org/StyleSheets/team-minutes.css">
1.8       dbooth   1519:   <meta content="SV_MEETING_TITLE" lang="en" name="Title">  
1.7       dbooth   1520:   <meta content="text/html; charset=iso-8859-1" http-equiv="Content-Type">
                   1521: </head>
                   1522: 
                   1523: <body>
                   1524: <p><a href="http://www.w3.org/"><img src="http://www.w3.org/Icons/WWW/w3c_home" alt="W3C" border="0"
1.5       dbooth   1525: height="48" width="72"></a> 
                   1526: 
1.7       dbooth   1527: </p>
                   1528: 
1.8       dbooth   1529: <h1>SV_MEETING_TITLE<br>
                   1530: SV_MEETING_DAY SV_MEETING_MONTH_ALPHA SV_MEETING_YEAR</h1>
1.7       dbooth   1531: 
1.5       dbooth   1532: <!-- Old:
1.8       dbooth   1533: <p>See also: <a href="SV_MEETING_IRC_URL">IRC log</a></p>
1.5       dbooth   1534: -->
1.8       dbooth   1535: SV_FORMATTED_IRC_URL
1.7       dbooth   1536: 
                   1537: <h2><a name="attendees">Attendees</a></h2>
                   1538: 
                   1539: <div class="intro">
1.8       dbooth   1540: <p>Present: SV_PRESENT_ATTENDEES</p>
                   1541: <p>Regrets: SV_REGRETS</p>
                   1542: <p>Chair: SV_MEETING_CHAIR </p>
                   1543: <p>Scribe: SV_MEETING_SCRIBE</p>
1.7       dbooth   1544: </div>
                   1545: 
                   1546: <h2>Contents</h2>
                   1547: <ul>
1.11      dbooth   1548:   <li><a href="#agenda">Topics</a>
1.7       dbooth   1549:        <ol>
1.8       dbooth   1550:        SV_MEETING_AGENDA
1.7       dbooth   1551:        </ol>
                   1552:   </li>
                   1553:   <li><a href="#newActions">Summary of Action Items</a></li>
                   1554: </ul>
                   1555: <hr>
                   1556: 
                   1557: 
1.5       dbooth   1558: <!--  We don't need the agenda items listed twice.
1.11      dbooth   1559: <h2><a name="agenda">Topics</a></h2>
1.7       dbooth   1560: <ul>
1.8       dbooth   1561:   SV_MEETING_AGENDA
1.7       dbooth   1562: </ul>
1.5       dbooth   1563: -->
1.7       dbooth   1564: 
1.8       dbooth   1565: SV_AGENDA_BODIES
1.7       dbooth   1566: <!--
1.15      hugo     1567: <h3 id="item1">Item name (owner)</h3>
1.7       dbooth   1568: <p>... text of discussion ...</p>
                   1569: 
1.15      hugo     1570: <h3 id="item2">Item name (owner)</h3>
1.7       dbooth   1571: <p>... text of discussion ...</p>
                   1572: -->
                   1573: 
                   1574: 
                   1575: <h2><a name="newActions">Summary of Action Items</a></h2>
1.5       dbooth   1576: <!-- Done Action Items -->
1.8       dbooth   1577: SV_DONE_ACTION_ITEMS
1.5       dbooth   1578: <!-- Pending Action Items -->
1.8       dbooth   1579: SV_PENDING_ACTION_ITEMS
1.5       dbooth   1580: <!-- New Action Items -->
1.21      dbooth   1581: SV_ACTION_ITEMS
1.7       dbooth   1582: 
                   1583: <hr>
                   1584: 
                   1585: <address>
1.5       dbooth   1586:   <!--
1.7       dbooth   1587:   <a href="http://validator.w3.org/check/referer"><img border="0"
                   1588:   src="http://validator.w3.org/images/vh40.gif" alt="Valid HTML 4.0!"
                   1589:   height="31" width="88" align="right">
                   1590:   </a> 
1.5       dbooth   1591:   -->
1.8       dbooth   1592:   <!-- <a href="/Team/SV_TEAM_PAGE_LOCATION">David Booth</a> <br /> -->
1.6       dbooth   1593:   Minutes formatted by David Booth's perl script: 
1.7       dbooth   1594:   <a href="http://dev.w3.org/cvsweb/~checkout~/2002/scribe/">http://dev.w3.org/cvsweb/~checkout~/2002/scribe/</a><br>
1.20      dbooth   1595:   $Date: 2003/06/13 21:02:20 $ 
1.7       dbooth   1596: </address>
                   1597: </body>
                   1598: </html>
1.5       dbooth   1599: TeamTemplate-EOF
                   1600: ;
                   1601: return $template;
                   1602: }
                   1603: 
                   1604: ##################################################################
                   1605: ###################### MITTemplate ############################
                   1606: ##################################################################
                   1607: sub MITTemplate
1.1       dbooth   1608: {
1.5       dbooth   1609: my $template = <<'MITTemplate-EOF'
1.16      dbooth   1610: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
1.7       dbooth   1611: <html>
                   1612: <head>
1.8       dbooth   1613:   <title>SV_MEETING_TITLE -- SV_MEETING_DAY SV_MEETING_MONTH_ALPHA SV_MEETING_YEAR</title>
1.7       dbooth   1614:   <LINK rel="STYLESHEET" href="http://www.w3.org/StyleSheets/base.css">
                   1615:   <LINK rel="STYLESHEET" href="http://www.w3.org/StyleSheets/team.css">
                   1616:   <link rel="STYLESHEET" href="http://www.w3.org/StyleSheets/team-minutes.css">
1.8       dbooth   1617:   <meta content="SV_MEETING_TITLE" lang="en" name="Title">  
1.7       dbooth   1618:   <meta content="text/html; charset=iso-8859-1" http-equiv="Content-Type">
                   1619: </head>
                   1620: 
                   1621: <body>
                   1622: <p><a href="http://www.w3.org/"><img src="http://www.w3.org/Icons/WWW/w3c_home" alt="W3C" border="0"
                   1623: height="48" width="72"></a> <a href="http://www.w3.org/Team"><img width="48" height="48"
                   1624: alt="W3C Team home" border="0" src="http://www.w3.org/Icons/WWW/team"></a> | <a
1.8       dbooth   1625: href="http://www.w3.org/Team/Meeting/MIT-scribes">MIT Meetings</a> | <a href="http://www.w3.org/">SV_MEETING_MONTH_ALPHA
                   1626: SV_MEETING_YEAR</a></p>
1.7       dbooth   1627: 
1.8       dbooth   1628: <h1>SV_MEETING_TITLE<br>
                   1629: SV_MEETING_DAY SV_MEETING_MONTH_ALPHA SV_MEETING_YEAR</h1>
1.7       dbooth   1630: 
1.5       dbooth   1631: <!-- Old:
1.8       dbooth   1632: <p>See also: <a href="SV_MEETING_IRC_URL">IRC log</a></p>
1.5       dbooth   1633: -->
1.8       dbooth   1634: SV_FORMATTED_IRC_URL
1.7       dbooth   1635: 
                   1636: <h2><a name="attendees">Attendees</a></h2>
                   1637: 
                   1638: <div class="intro">
1.8       dbooth   1639: <p>Present: SV_PRESENT_ATTENDEES</p>
                   1640: <p>Regrets: SV_REGRETS</p>
                   1641: <p>Chair: SV_MEETING_CHAIR </p>
                   1642: <p>Scribe: SV_MEETING_SCRIBE</p>
1.7       dbooth   1643: </div>
                   1644: 
                   1645: <h2>Contents</h2>
                   1646: <ul>
1.11      dbooth   1647:   <li><a href="#agenda">Topics</a>
1.7       dbooth   1648:        <ol>
1.8       dbooth   1649:        SV_MEETING_AGENDA
1.7       dbooth   1650:        </ol>
                   1651:   </li>
                   1652:   <li><a href="#newActions">Summary of Action Items</a></li>
                   1653: </ul>
                   1654: <hr>
                   1655: 
                   1656: 
1.5       dbooth   1657: <!--  We don't need the agenda items listed twice.
1.11      dbooth   1658: <h2><a name="agenda">Topics</a></h2>
1.7       dbooth   1659: <ul>
1.8       dbooth   1660:   SV_MEETING_AGENDA
1.7       dbooth   1661: </ul>
1.5       dbooth   1662: -->
1.7       dbooth   1663: 
1.8       dbooth   1664: SV_AGENDA_BODIES
1.7       dbooth   1665: <!--
1.15      hugo     1666: <h3 id="item1">Item name (owner)</h3>
1.7       dbooth   1667: <p>... text of discussion ...</p>
                   1668: 
1.15      hugo     1669: <h3 id="item2">Item name (owner)</h3>
1.7       dbooth   1670: <p>... text of discussion ...</p>
                   1671: -->
                   1672: 
                   1673: 
                   1674: <h2><a name="newActions">Summary of Action Items</a></h2>
1.5       dbooth   1675: <!-- Done Action Items -->
1.8       dbooth   1676: SV_DONE_ACTION_ITEMS
1.5       dbooth   1677: <!-- Pending Action Items -->
1.8       dbooth   1678: SV_PENDING_ACTION_ITEMS
1.5       dbooth   1679: <!-- New Action Items -->
1.21      dbooth   1680: SV_ACTION_ITEMS
1.7       dbooth   1681: 
                   1682: <hr>
                   1683: 
                   1684: <address>
1.5       dbooth   1685:   <!--
1.7       dbooth   1686:   <a href="http://validator.w3.org/check/referer"><img border="0"
                   1687:   src="http://validator.w3.org/images/vh40.gif" alt="Valid HTML 4.0!"
                   1688:   height="31" width="88" align="right">
                   1689:   </a> 
1.5       dbooth   1690:   -->
1.8       dbooth   1691:   <!-- <a href="/Team/SV_TEAM_PAGE_LOCATION">David Booth</a> <br /> -->
1.6       dbooth   1692:   Minutes formatted by David Booth's perl script: 
1.7       dbooth   1693:   <a href="http://dev.w3.org/cvsweb/~checkout~/2002/scribe/">http://dev.w3.org/cvsweb/~checkout~/2002/scribe/</a><br>
1.20      dbooth   1694:   $Date: 2003/06/13 21:02:20 $ 
1.7       dbooth   1695: </address>
                   1696: </body>
                   1697: </html>
1.5       dbooth   1698: MITTemplate-EOF
1.1       dbooth   1699: ;
1.5       dbooth   1700: return $template;
1.1       dbooth   1701: }
1.5       dbooth   1702: 
1.1       dbooth   1703: 

Webmaster