diff --git a/Date_Determinator.mw b/Date_Determinator.mw index e6baba92..d3621ceb 100644 --- a/Date_Determinator.mw +++ b/Date_Determinator.mw @@ -1,5 +1,6 @@ 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== @@ -248,3 +249,292 @@ END DATA --> |'''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 +