[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[mhc:01669] Re: MHC on Zaurus



乃村です.

On Fri, 27 Sep 2002 13:45:35 JST,
	knok@xxxxxxxxxxxxx (NOKUBI Takatsugu) said:

>   野首です。こちらでは初めまして。

どうも.はじめまして.某所では,お世話になっています.
# 機会があれば,一度お目にかかりたいです.

> 私も昨日ちょっと Zaurus の datebook.xml を生成させてみようといろいろ
> やってました。結局成功していませんが...

一応,途中まで作ったものを下に付けておきます.
繰り返しじゃない普通のスケジュールなら,このまま使えると思います.
ujis で吐くので,

  this-script | iconv -f EUC-JP -t UTF-8 > datebook.xml

してあげて下さい.

繰り返しの処理は,mhc-schedule.rb 中の to_palm をから作れば
いけそうな気がします.でも,ここまで来てexception の扱いが
できない事に気付いて萎えたんです..

> >>   情報公開されていないタグ (rinfo とか) があって,
> >>   その部分のソースコードもない.
> 
>   この辺りの情報は DateBookDB というクラスで操作するようなのですが、が
> んばって Ruby binding を作ればなんとかならないかなあ、などと考えていま
> す。

オープンソース版の DateBookDB には,rinfo というフィールドが存在
しないんですよ.なので,この方法でもあんまり嬉しくない気がしています.

どちらかっていうと,DateBookDB を libmhc.a とかにまるごと
すげかえて GUI 部分だけ datebook から貰うというのが
面白いのかもしれません.

>   Datebook を捨てて KOrganizer Embedded を使うという方法もあるようです。

KO/E って,繰り返しの予定が入れられなかった記憶があるのですが,
既に昔の話でしょうか.
--
nom


#!/usr/local/bin/ruby

require 'mhc-kconv'
require 'mhc-date'
require 'mhc-schedule'

################################################################
##  ZDatebookDB class

class ZDatebookDB

  def initialize()
    @last_uid = -1 * Time .now .to_i
    @last_rid = 0
    @records  = []
  end

  def new_record()
    rec = ZDatebookRecord .new(self, new_uid(), new_rid())
    @records << rec

    return rec
  end

  def new_uid()
    return @last_uid -= 1
  end

  def new_rid()
    return @last_rid += 1
  end

  def each_record()
    @records .each{|rec|
      yield(rec)
    }
  end

  def dump()
    ret  = '<?xml version="1.0" encoding="UTF-8"?>'
    ret += '<!DOCTYPE DATEBOOK ><DATEBOOK>'
    ret += "<RIDMAX>#{@last_rid}</RIDMAX>"
    ret += "<events>\n"

    @records .each{|rec|
      ret += rec .dump + "\n"
    }

    ret += "</events>\n"
    ret += "</DATEBOOK>\n"
  end
end

################################################################
##  ZDatebookRecord class

class ZDatebookRecord

  def initialize(zdb = nil, uid = nil, rid = nil)
    set_zdb(zdb)
    set_uid(uid)
    set_rid(rid)
    set_rinfo(1)  ## XXX
  end

  ################
  ## uid is not always unique.  In cases where rinfo=0|2 uid seems to
  ## be 0 or positive (mostly 0).  In the case that rinfo=1|3 it is
  ## always negative and seems to look like a date field.
  ## Maybe rinfo keeps some sync information...

  ## XXX: usually, you don't have to set these vars by hand.

  def set_uid(uid); 
    @uid = uid
    return self
  end

  def set_rid(rid)
    @rid = rid
    return self
  end

  def set_rinfo(rinfo)  ## myterious field... (inside of QtopiaDesktop?)
    @rinfo = rinfo
    return self
  end

  def set_zdb(db)
    @zdb = db
    return self
  end

  ################
  ## set common fields

  def set_description(string)
    @description = string
    return self
  end

  def set_location(string)
    @location = string
    return self
  end

  def set_note(string)
    @note = string
    return self
  end

  def set_categories(cat_list)
    @categories = cat_list  ## array of category string.
    return self
  end

  def set_time(s, e, allday)
    ## start and endtime are suposed to be Time.

    if @allday = allday
      @start   = Time .local(s .year, s .mon, s .mday)
      @endtime = Time .local(e .year, e .mon, e .mday, 23, 59, 0)
    else
      @start   = s
      @endtime = e
    end

    return self
  end

  def set_alarm(minute)
    @alarm = minute
    return self
  end

  ################
  ## set repeat fields

  def set_repeat_daily(enddt, freq)
    # rtype = "Daily"

    set_repeat(enddt, freq)
    @rtype = 'Daily'
    return self
  end

  def set_repeat_weekdays(enddt, freq, rweekdays)
    # rtype = "Weekly" rweekdays = "127"
    # 127 is bitfield. bit7 (MSB) = Sun, bit1 = Mon

    set_repeat(enddt, freq)
    @rtype = 'Weekly'
    @rweekdays = rweekdays
  end

  def set_repeat_monthlydate(enddt, freq)
    # rtype = "MonthlyDate" rposition = "4"
    # 4 is for 4th week.

    set_repeat(enddt, freq)
    @rtype = 'MonthlyDate'
    return self
  end

  def set_repeat_monthlyday(enddt, freq)
    # rtype = "MonthlyDay" rposition = "4"  -- 4th Tue every month

    set_repeat(enddt, freq)
    @rtype = 'MonthlyDay'
    return self
  end

  def set_repeat_yearly(enddt, freq)
    # rtype="Yearly"

    @rtype = 'Yearly'
    set_repeat(enddt, freq)
    return self
  end

  ################
  ## predicates.

  def allday?()
    return true if @allday
    return false
  end
  
  def has_alarm?()
    return true if @alarm && !allday?
    return false
  end

  def repeat?()
    return true if @rtype
    return false
  end

  def repeat_weekly?()
    return true if @rtype && @rtype == 'Weekly'
    return false
  end

  def repeat_monthly?()
    return true if @rtype && @rtype =~ /^Monthly/
    return false
  end

  ################
  # print/dump fields

  def dump
    return '<event '      +
      fld_description()   +
      fld_location()      +
      fld_categories()    +
      fld_uid()           +
      fld_rid()           +
      fld_rinfo()         +
      fld_type()          +
      fld_alarm()         +

      fld_repeat()        +

      fld_start()         +
      fld_end()           +
      fld_note()          +
      ' />'
  end

  def fld_description()
    return make_field_string('description', @description)
  end

  def fld_location()
    return make_field_string('location', @location)
  end

  def fld_categories()
    return '' ## XXX
  end

  def fld_uid()
    return make_field_string('uid', @uid)
  end

  def fld_rid()
    return make_field_string('rid', @rid)
  end

  def fld_rinfo()
    return make_field_string('rinfo', @rinfo)
  end

  def fld_type()
    return make_field_string('type', 'AllDay') if allday?
    return ''
  end

  def fld_alarm()
    return '' if !has_alarm? || allday?
    return make_field_string('alarm', @alarm) + # in minute
           make_field_string('sound', 'loud')
  end

  def fld_repeat()
    return '' if !repeat?
    return make_field_string('rtype', @rtype) +
	   make_field_string('rweekdays', @rweekdays) +
	   make_field_string('rposition', @rposition) +
	   make_field_string('rfreq', @rfreq) +
	   make_field_string('rhasenddate', @enddt, true) +
	   make_field_string('enddt', @enddt) +
	   make_field_string('created', @created)
  end

  def fld_start()
    return make_field_string('start', @start)
    return ''
  end

  def fld_end()
    return make_field_string('end', @endtime)
    return ''
  end

  def fld_note()
    return make_field_string('note', @note)
    return ''
  end

  ################################################################
  private

  def set_repeat(enddt, freq)
    raise ('set time first') if !@start

    @enddt = Time .local(enddt .year, enddt .mon, enddt .mday)
    @freq  = freq
    @rposition = (@start .mday - 1) / 7 + 1

    return self
  end

  def make_field_string(name, val, val_is_bool = false)

    val = (val ? 1 : 0) if val_is_bool
    val = val .to_i     if val .is_a?(Time)
    val = val .to_s

    return '' if val == ''

    val = Kconv::toeuc(val)
    val = val .gsub('&', '&amp;')
    val = val .gsub('<', '&lt;')
    val = val .gsub('>', '&gt;')
    val = val .gsub('"', '&quot;')
    val = val .gsub("'", '&apos;')

    return name + '="' + val + '" '
  end
end

################################################################
## add methods to MhcScheduleItem.

class MhcScheduleItem

  def to_zaurus(zdb)
    ret     = []
    day_cp  = day .dup
    
    rec = zdb .new_record
    rec .set_description(subject)
    rec .set_location(location)
    # rec .set_note(description)
    rec .set_categories(category)
    rec .set_alarm(alarm / 60) if alarm

    ################

    if day && day .length > 0
      if time_b && time_e
	rec .set_time(day[0] .to_t(time_b), day[0] .to_t(time_e), !time_b)
      else
	rec .set_time(day[0] .to_t, day[0] .to_t, !time_b)
      end
      return rec
    else
      return nil
    end

    ### for repeat
    beg, fin = occur_min, occur_max
    fin = nil if fin == DURATION_MAX

    ## First, treat X-SC-Day: field.
    while day_cp .length > 0
      if day_cp .length > 1 && day_cp .length == day_cp .max - day_cp .min + 1
	## repeat in a series of days -- make up as a daily.
	ret << mk_palm_skel .set_daily(day_cp .min, day_cp .max, 1)
	day_cp = []
      else
	ret << mk_palm_skel .set_nonrepeat_date(day_cp .shift)
      end
    end

    ## Second, treat X-SC-Cond: field.
    if cond .length == cond_wek .length && cond_wek .length > 0
      ## weekly
      weeks = []
      for w in 0 .. 6
	weeks << cond_wek .include?(MhcDate::W_LABEL[w]) ? true : false
      end
      ret << mk_palm_skel .set_weekly(beg, fin, 1, weeks)

    elsif  cond_ord .length >= 1  &&
	  !cond_ord .include?('5th') &&
	   cond_wek .length >= 1  &&
	   cond_num .length == 0  && 
	   cond_mon .length == 0
      ## monthly by day
      cond_ord .each{|ord_str|
	cond_wek .each{|wek_str|
	  ord = MhcDate::O_LABEL .index(ord_str)
	  wek = MhcDate::W_LABEL .index(wek_str)
	  sch2 = MhcScheduleItem .new .add_cond(ord_str) .add_cond(wek_str)
	  beg2 = beg .dup
	  while !sch2 .occur_on?(beg2) ## XXX: unnecessary?
	    beg2 .succ!
	  end
	  ret << mk_palm_skel .set_monthly_by_day(beg2, fin, 1, ord, wek)
	}
      }
    elsif cond_num .length == 1 &&
	  cond_num .length == cond .length
      ## monthly by date
      while !occur_on?(beg) ## XXX: maybe necessary?
	beg .succ!
      end
      ret << mk_palm_skel .set_monthly_by_date(beg, fin, 1)

    elsif cond_num .length == 1 &&
	  cond_mon .length == 1 &&
	  cond_wek .length == 0 &&
	  cond_ord .length == 0
      ## yearly by date
      y = beg .y
      m = MhcDate::M_LABEL .index(cond_mon[0]) + 1
      d = cond_num[0] .to_i
      date = MhcDate .new(y, m, d)
      if date < beg
	date .y_succ! 
      end
      ## 2/29 はどうする?
      ret << mk_palm_skel .set_yearly(date, fin, 1)

    elsif cond_ord .length == 1  &&
	  cond_ord[0] != '5th'   &&
	  cond_wek .length == 1  &&
	  cond_num .length == 0  && 
	  cond_mon .length == 1
      ## yearly by day
      ord = MhcDate::O_LABEL .index(cond_ord[0])
      wek = MhcDate::W_LABEL .index(cond_wek[0])
      m   = MhcDate::M_LABEL .index(cond_mon[0]) + 1
      date = MhcDate .new(beg .y, m, 1)
      if date .m < beg .m 
	date .y_succ!
      end
      while !occur_on?(date)
	date .succ!
      end
      ret << mk_palm_skel .set_monthly_by_day(date, fin, 12, ord, wek)

    elsif cond .empty?
      ## do nothing
    else
      ## conversion failed.
      ret = []
    end

    if ret .empty?
      # STDERR .print "#{occur_min .to_js} : #{subject} unsupported. ignored..\n"
      return nil
    else
      return ret
    end
  end
end


################################################################
## main

$DEBUG = false

STDOUT .sync= true
STDERR .sync= true

def usage
  print '
usage: mhc2z [-r dir] [YYYYMMDD-yyyymmdd]

  mhc2z -- make datebook.xml

    -r dir : Set repository directory of the mhc.
             ~/Mail/schedule

    YYYYMMDD-yyyymmdd : set a start and end date of scanning mhc.
                        if omitted, scan from 3 months ago to
                        3 months after.
'
  exit 1
end

##
## option check.
##

$flag_from, $flag_to = nil, nil
$flag_dir = File .expand_path("~/Mail/schedule")

while ARGV .length > 0
  case ARGV[0]
  when '-r'
    ARGV .shift
    $flag_dir = ARGV[0]
  when /^(\d{8})-(\d{8})$/
    $flag_from, $flag_to = MhcDate .new($1), MhcDate .new($2)
  else
    usage()
  end
  ARGV .shift
end

$flag_from = MhcDate .new .m_succ!(-3) if !$flag_from
$flag_to   = MhcDate .new .m_succ!(+3) if !$flag_to

##
## Open mhc & convert to z.
##

converted, count = 0, 0

mdb = MhcScheduleDB .new($flag_dir)
zdb = ZDatebookDB   .new

mdb .each_sch($flag_from, $flag_to){|sch|
  count += 1

  if sch .to_zaurus(zdb)
    converted += 1
  else
    STDERR .print "failed to convert."
    STDERR .print MhcKconv::todisp("  subject: #{sch .subject}\n")
    STDERR .print "  first occured: #{sch .occur_min}\n"
    STDERR .print "  path: #{sch .path}\n"
  end
}

print zdb .dump

STDERR .print "#{converted}/#{count} successfully converted.\n"

exit 0