Changeset 344

Show
Ignore:
Timestamp:
11/09/06 19:55:14 (2 years ago)
Author:
ogawa
Message:

- Accessing Cybozu Office 6 server and parsing the reponses are refactored and moved to a perl extention named WWW::CybozuOffice?6::Calendar.
- Sets "DATE" as date-type for DTSTART and DTEND of the scheduler events, when they don't have "time".
- Supports recurrent events, finally!!

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • cybozu2ical/trunk/cybozu2ical

    r341 r344  
    55 
    66use strict; 
     7use lib 'lib'; 
     8 
    79use YAML; 
    8 use LWP::UserAgent; 
    9 use Encode qw/from_to decode_utf8 encode/; 
    10 use Text::CSV_XS; 
     10use Encode qw/decode_utf8 encode/; 
     11use WWW::CybozuOffice6::Calendar; 
    1112use Data::ICal; 
    1213use Data::ICal::Entry::Event; 
     
    1415use Data::ICal::Entry::TimeZone::Standard; 
    1516use DateTime; 
    16 use DateTime::TimeZone; 
    1717use Pod::Usage; 
    1818use Getopt::Long; 
    1919 
    20 our $VERSION = '0.07'; 
     20our $VERSION = '0.10'; 
    2121 
    2222my %opt = (conf => 'config.yaml'); 
     
    2626my $cfg = YAML::LoadFile($opt{conf}); 
    2727 
    28 my $ua = LWP::UserAgent->new(); 
    29  
    30 my $res = $ua->post($cfg->{cybozu_url} . '?page=SyncCalendar', { 
    31     _System => 'login', 
    32     _Login => '1', 
    33     defined $cfg->{username} ? (_Account => $cfg->{username}) : (), 
    34     defined $cfg->{userid} ? (_Id => $cfg->{userid}) : (), 
    35     Password => $cfg->{password} || '', 
    36     'csv' => 1, 
    37     'notimecard' => 1, 
    38 }); 
    39  
    40 die "Cannot access Cybozu Office 6 (" . $cfg->{cybozu_url} . ")." 
    41     unless $res->is_success; 
    42  
    43 my $content = $res->content; 
    44 from_to($content, $cfg->{input_encoding} || 'shiftjis', 'utf8'); 
    45 my @lines = grep /^\d+,ts\.\d+,/, split(/\r?\n/, $content); 
    46  
    47 my $tzid = $cfg->{time_zone} || 'Asia/Tokyo'; 
    48 my $tz = DateTime::TimeZone->new(name => $tzid); 
     28my $time_zone = $cfg->{time_zone} || 'Asia/Tokyo'; 
    4929 
    5030my $vcalendar = Data::ICal->new(); 
    5131$vcalendar->add_properties( 
    52     prodid => "-//as-is.net/Cybozu2ICal $VERSION//EN", 
     32    prodid   => "-//as-is.net/Cybozu2ICal $VERSION//EN", 
    5333    calscale => 'GREGORIAN', 
    54     method => 'PUBLISH', 
     34    method   => 'PUBLISH', 
    5535    $cfg->{calname} ? ('X-WR-CALNAME' => $cfg->{calname}) : (), 
    56     'X-WR-TIMEZONE' => $tzid 
     36    'X-WR-TIMEZONE' => $time_zone 
    5737); 
    5838 
    59 # current timestamp 
    60 my $dtstamp = dt2ical(DateTime->now); 
    61  
    62 my $csv = Text::CSV_XS->new({ binary => 1 }); 
    63 for my $line (@lines) { 
    64     $csv->parse($line) 
    65         or die 'failed to parse CSV input.'; 
    66     my @fields = $csv->fields; 
    67     next if $#fields < 13; # num. of fields 
    68  
     39my $cal = WWW::CybozuOffice6::Calendar->new(%$cfg); 
     40for my $item ($cal->get_items()) { 
    6941    my $vevent = Data::ICal::Entry::Event->new(); 
    70  
    71     if (my $freq = $fields[7]) { 
    72         # TODO: handle recurrent events 
    73         warn "Recurrent events[$freq] cannot be handled."; 
     42    my %args = ( 
     43        summary     => decode_utf8($item->{summary}), 
     44        description => decode_utf8($item->{description}), 
     45        created     => to_icaldate($item->{created}), 
     46        dtstamp     => to_icaldate($item->{modified}) 
     47    ); 
     48 
     49    if ($item->{is_full_day}) { 
     50        $args{dtstart} = [to_icaldate($item->{start}, 1), { VALUE => 'DATE' }]; 
     51        $args{dtend}   = [to_icaldate($item->{end},   1), { VALUE => 'DATE' }]; 
    7452    } else { 
    75         my ($dtstart, $dtend); 
    76         my $dt0 = cydate2dt($fields[3], $fields[5], $tz); 
    77         my $dt1 = cydate2dt($fields[4], $fields[6], $tz); 
    78         if ($fields[5] =~ /^:$/) { 
    79             $dtstart = $dt0->ymd(''); 
    80             $dtend = $dt1->add(days => 1)->ymd(''); 
    81         } else { 
    82             $dtstart = dt2ical($dt0); 
    83             $dtend = dt2ical(($fields[6] =~ /^:$/) ? 
    84                              $dt0->add(minutes => 10) : $dt1); 
    85         } 
    86         $vevent->add_properties( 
    87             dtstart => [$dtstart, { TZID => $tzid }], 
    88             dtend => [$dtend, { TZID => $tzid }] 
    89         ); 
     53        $args{dtstart} = [to_icaldate($item->{start}, 0), { TZID => $time_zone }]; 
     54        $args{dtend}   = [to_icaldate($item->{end},   0), { TZID => $time_zone }]; 
    9055    } 
    9156 
    92     my($epoch) = $fields[1] =~ m/ts\.(\d+)/; 
    93     my $created = dt2ical(DateTime->from_epoch(epoch => $epoch)); 
    94  
    95     my $summary = $fields[11] || ''; 
    96     $summary .= ': ' if $summary; 
    97     $summary .= $fields[12] || ''; 
    98     $vevent->add_properties( 
    99         summary => decode_utf8($summary), 
    100         description => decode_utf8($fields[13] || $summary), 
    101         dtstamp => $dtstamp, 
    102         created => $created 
    103     ); 
    104  
     57    # handle frequency 
     58    if ($item->{frequency}) { 
     59        my $freq = $item->{frequency}; 
     60        my %rrules = $freq ne 'WEEKDAYS' ? 
     61            ( FREQ => $freq ) : 
     62            ( FREQ => 'WEEKLY', WKST => 'SU', BYDAY => 'MO,TU,WE,TH,FR' ); 
     63        $rrules{UNTIL} = to_icaldate($item->{until}, 1) if $item->{until}; 
     64        $args{rrule} = join ';', map { $_ . '=' . $rrules{$_} } keys %rrules; 
     65    } 
     66 
     67    $vevent->add_properties(%args); 
    10568    $vcalendar->add_entry($vevent); 
    10669} 
    10770 
    10871my $vtimezone = Data::ICal::Entry::TimeZone->new(); 
    109 $vtimezone->add_properties(tzid => $tzid); 
     72$vtimezone->add_properties(tzid => $time_zone); 
    11073 
    11174# probably we need to support the Daylight Saving Time 
    11275my $standard = Data::ICal::Entry::TimeZone::Standard->new(); 
    11376 
    114 my $dt = cydate2dt("1970/01/01", "00:00:00", $tz); 
    115 my $offset = DateTime::TimeZone::offset_as_string($dt->offset) || '+0900'; 
    116 my $tzname = $cfg->{tzname} || $tz->short_name_for_datetime($dt) || 'JST'; 
     77my $std = DateTime->new(year => 1970, month => 1, day => 1, 
     78                        hour => 0, minute => 0, second => 0, 
     79                        time_zone => $time_zone); 
     80my $offset = DateTime::TimeZone::offset_as_string($std->offset) || '+0900'; 
     81my $tzname = $cfg->{tzname} || 'JST'; 
    11782 
    11883$standard->add_properties( 
    11984    tzoffsetfrom => $offset, 
    120     tzoffsetto => $offset, 
    121     tzname => $tzname, 
    122     dtstart => dt2ical($dt
     85    tzoffsetto   => $offset, 
     86    tzname       => $tzname, 
     87    dtstart      => to_icaldate($std
    12388); 
    12489$vtimezone->add_entry($standard); 
     
    12691$vcalendar->add_entry($vtimezone); 
    12792 
    128 my $enc = $cfg->{output_encoding} || 'utf8'; 
    129 print $enc eq 'ncr' ? 
    130     encode_ncr($vcalendar->as_string) : 
    131     encode($enc, $vcalendar->as_string); 
    132  
    133 sub encode_ncr { 
    134     my $text = shift; 
    135     $text =~ s/(\P{ASCII})/sprintf("&#%d;", ord($1))/eg; 
     93print encode_($cfg->{output_encoding} || 'utf8', $vcalendar->as_string); 
     94 
     95sub to_icaldate { 
     96    my($dt, $is_full_day) = @_; 
     97    $is_full_day ? 
     98        $dt->ymd('') : 
     99        $dt->ymd('') . 'T' . $dt->hms('') . ($dt->time_zone->is_utc ? 'Z' : ''); 
     100
     101 
     102sub encode_ { 
     103    my($enc, $text) = @_; 
     104    if ($enc eq 'ncr') { 
     105        $text =~ s/(\P{ASCII})/sprintf("&#%d;", ord($1))/eg; 
     106    } else { 
     107        $text = encode($enc, $text); 
     108    } 
    136109    $text; 
    137110} 
    138111 
    139 sub cydate2dt { 
    140     my($date, $time, $tz) = @_; 
    141     my @d = split("/", $date); 
    142     my @t = split(":", $time); 
    143  
    144     my $dt = DateTime->new( 
    145         year => $d[0], month => $d[1], day => $d[2], 
    146         hour => $t[0] || 0, minute => $t[1] || 0, second => $t[2] || 0, 
    147         time_zone => $tz || 'Asia/Tokyo' 
    148     ); 
    149     return $dt; 
    150 } 
    151  
    152 sub dt2ical { 
    153     my($dt) = @_; 
    154     $dt->ymd('') . 'T' . $dt->hms('') . ($dt->time_zone->is_utc ? 'Z' : ''); 
    155 } 
    156  
    1571121; 
     113__END__ 
     114 
    158115=head1 NAME 
    159116 
     
    182139=over 4 
    183140 
     141=item WWW::CybozuOffice6::Calendar 
     142 
    184143=item Text::CSV_XS 
    185144 
     
    240199subversion repository: 
    241200 
    242   http://code.as-is.net/svn/public/cybozu2ical/trunk 
     201  http://code.as-is.net/svn/public/cybozu2ical/trunk/ 
    243202 
    244203You can browse the files via SVN::Web from the following: