Der [[Date Determinator]] ist ein MediaWiki-Bot, welcher Terminfindungen vereinfachen sollen. Die Eingabe passiert als YAML, die Ausgabe kann vom Bot periodisch als hybsche Tabelle erstellt werden. =Beispiele= ==CodingNight zu dritt== '''Eingabe:''' BEGIN DATA "Test" Cool Hacker: 24.5.: nein 23.5.: ja Astro: 23.5.: ja 22.5.: weiss noch nicht 1337 Cracker: 24.5.: jo 23.5.: yezz! END DATA '''Ausgabe:''' {| border="1" cellpadding="2" cellspacing="0" style="border-collapse:collapse;" |- ! !22.5. !23.5. !24.5. |- |[[User:Cool Hacker|Cool Hacker]] | | | bgcolor="#7fff7f" | ja | bgcolor="#ff7f7f" | nein |- |[[User:1337 Cracker|1337 Cracker]] | | | bgcolor="#7fff7f" | yezz! | bgcolor="#7fff7f" | jo |- |[[User:Astro|Astro]] | bgcolor="#bfbfbf" | weiss noch nicht | bgcolor="#7fff7f" | ja | | |- |'''sum(ja)''' |'''0''' |'''3''' |'''1''' |} ==Realitätsabgleich 2== ''Hier sind die Daten in einem HTML-Kommentar. '''Artikel bearbeiten''' um zu schauen.'' {| border="1" cellpadding="2" cellspacing="0" style="border-collapse:collapse;" |- ! !16.6. !17.6. !18.6. A !18.6. N !19.6. N !19.6. A !20.6. !21.6. !22.6. !23.6. !24.6. !25.6. |- |[[User:matthias|matthias]] | bgcolor="#7fff7f" | j | bgcolor="#ff7f7f" | n | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#ff7f7f" | n | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j |- |[[User:fukami|fukami]] | bgcolor="#ff7f7f" | n | bgcolor="#7fff7f" | j | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n |- |[[User:Caldrin|Caldrin]] | bgcolor="#ff7f7f" | n | bgcolor="#7fff7f" | j | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#7fff7f" | j | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#7fff7f" | j | bgcolor="#ff7f7f" | n |- |[[User:nulli|nulli]] | bgcolor="#ff7f7f" | n | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n |- |[[User:toidinamai|toidinamai]] | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j |- |[[User:Astro|Astro]] | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#ff7f7f" | n | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#ff7f7f" | n | bgcolor="#ff7f7f" | n | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j | bgcolor="#7fff7f" | j |- |'''sum(ja)''' |'''3''' |'''5''' |'''3''' |'''4''' |'''5''' |'''3''' |'''3''' |'''2''' |'''1''' |'''3''' |'''4''' |'''3''' |} =Source= Open-source, yada-yada! ==date_determinator.rb==
WIKI_USER = 'AstRobot'
WIKI_PASSWORD = '...'
PAGE = 'Benutzer:Astro/Date_Determinator'
WIKI = 'http://wiki.c3d2.de/wikipgedia'
HTTP_USER = 'eris'
HTTP_PASSWORD = '...'

require 'yaml'
require 'mediawiki'

include MediaWiki

class DateUser
  attr_reader :name

  def initialize(name, hsh)
    @name = name
    @hsh = hsh
  end

  def dates
    @hsh.keys
  end

  def day(d)
    @hsh[d]
  end

  def day_class(d)
    if day(d) == nil
      nil
    elsif day(d) =~ /^[yj]/i
      true
    elsif day(d) =~ /^n/i
      false
    else
      ""
    end
  end

  def day_style(d)
    case day_class(d)
      when nil then ""
      when true then 'bgcolor="#7fff7f"'
      when false then 'bgcolor="#ff7f7f"'
      else 'bgcolor="#bfbfbf"'
    end
  end
end

class DateData
  def initialize(yaml)
    @users = []
    @error = nil
    begin
      YAML::load(yaml).each { |user,dates|
        @users << DateUser.new(user, dates)
      }
    rescue Exception => e
      puts "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
      @error = e.to_s
    end
  end

  def table
    return @error if @error
    
    s = ''

    ##
    # Collect dates
    ##
    dates = []
    dates_yes = {}
    @users.each { |user| dates += user.dates }

    ##
    # Sort dates
    ##
    dates.uniq!
    dates.sort! { |a,b|
      if a =~ /^(\d+)\.(\d+)\./
        a_day = $1
        a_month = $2
        if b =~ /^(\d+)\.(\d+)\./
          b_day = $1
          b_month = $2
          if a_month.to_i == b_month.to_i
            a_day.to_i <=> b_day.to_i
          else
            a_month.to_i <=> b_month.to_i
          end
        else
          a <=> b
        end
      else
        a <=> b
      end
    }

    ##
    # Construct header
    ##
    s += "{| border=\"1\" cellpadding=\"2\" cellspacing=\"0\" style=\"border-collapse:collapse;\"\n|-\n! \n"
    dates.each { |date|
      s += "!#{date}\n"
      dates_yes[date] = 0
    }

    ##
    # Construct rows
    ##
    @users.each { |user|
      s += "|-\n|[[User:#{user.name}|#{user.name}]]\n"
      dates.each { |date|
        s += "| #{user.day_style(date)} | #{user.day(date)}\n"
        dates_yes[date] += 1 if user.day_class(date) == true
      }
    }

    ##
    # Build summary
    ##
    s += "|-\n|'''sum(ja)'''\n"
    dates.each { |date|
      s += "|'''#{dates_yes[date]}'''\n"
    }
    s += "|}"
  end
end



wiki = Wiki.new(WIKI, HTTP_USER, HTTP_PASSWORD)
wiki.login(WIKI_USER, WIKI_PASSWORD)
page = wiki.article(PAGE)

datasets = {}
current_data_name = nil
current_data_yaml = ''
page.text.split(/\n/).each { |line|
  if line =~ /BEGIN DATA "(.+?)"/
    current_data_name = $1
    current_data_yaml = ''
  elsif line =~ /END DATA/ and current_data_name
    datasets[current_data_name] = DateData.new(current_data_yaml)
    current_data_name = nil
    current_data_yaml = ''
  elsif current_data_name
    current_data_yaml += "#{line}\n"
  end
}


text_old = page.text.dup

signature = /()(.+?)()/m
page.text.gsub!(signature) { |part|
  begin1,name,begin2,obsolete,end1 = part.match(signature).to_a[1..-1]
  table = datasets[name] ? datasets[name].table : "DATA #{name} not found!"
  "#{begin1}#{name}#{begin2}\n#{table}\n#{end1}"
}

page.submit('Date Determinator run') if page.text != text_old
==mediawiki.rb==
require 'http-access2'
require 'cgi'
require 'rexml/document'

module MediaWiki
  class Wiki
    ##
    # HTTPAccess2 object, must be accessible by the Wiki's
    # Article children
    attr_accessor :http

    def initialize(url, auth_name=nil, auth_password=nil)
      @url = (url =~ /\/$/) ? url : "#{url}/"

      @http = HTTPAccess2::Client.new(nil, 'Ruby-MediaWiki::Wiki/0.1')

      add_auth(url, auth_name, auth_password) if auth_name and auth_password
    end

    def add_auth(uri, auth_name, auth_password)
      @http.set_basic_auth(uri, auth_name, auth_password)
    end

    def login(username, password)
      postdata = "wpName=#{CGI::escape(username)}&wpPassword=#{CGI::escape(password)}&wpLoginattempt="
      result = @http.post_content(article_url('Special:Userlogin'), postdata)
      if result =~ /

/ raise "Unable to authenticate as #{username}" end end ## # Return a new article with the given name # name:: [String] Article name # result:: [Article] def article(name) Article.new(self, name) end ## # Return def article_url(name) "#{@url}index.php?title=#{CGI::escape(name)}" end ## # Return a hash of special pages: # 'Link title' => 'Article name' def specialpages result = {} Article.new(self, 'Special:Specialpages', false).xhtml.each_element('ul/li/a') { |a| result[a.attributes['title']] = a.text } result end end class Article attr_accessor :name, :text def initialize(wiki, name, load_text=true) @wiki = wiki @name = name @text = nil @xhtml = nil @xhtml_cached = false @wp_edittoken = nil @wp_edittime = nil reload if load_text end def xhtml unless @xhtml_cached xhtml_reload end @xhtml end def xhtml_reload html = @wiki.http.get_content("#{@wiki.article_url(@name)}") html.scan(/(.+)/m) { |content,| @xhtml = REXML::Document.new("#{content}").root } @xhtml_cached = true end def reload puts "Loading #{@wiki.article_url(@name)}&action=edit" doc = REXML::Document.new(@wiki.http.get_content("#{@wiki.article_url(@name)}&action=edit")).root @name = doc.elements['//span[@class="editHelp"]/a'].attributes['title'] form = doc.elements['//form[@name="editform"]'] @text = form.elements['textarea[@name="wpTextbox1"]'].text begin @wp_edittoken = form.elements['input[@name="wpEditToken"]'].attributes['value'] @wp_edittime = form.elements['input[@name="wpEdittime"]'].attributes['value'] rescue NoMethodError # wpEditToken might be missing, that's ok end end ## # TODO: minor_edit, watch_this def submit(summary, minor_edit=false, watch_this=false) puts "Posting to #{@wiki.article_url(@name)}&action=submit" postdata = "wpTextbox1=#{CGI::escape(@text)}&wpSummary=#{CGI::escape(summary)}&wpSave=1&wpEditToken=#{@wp_edittoken}&wpEdittime=#{@wp_edittime}" result = @wiki.http.post_content("#{@wiki.article_url(@name)}&action=submit", postdata) # TODO: Was edit successful? (We received the document anyways) end end end