Date Determinator: Unterschied zwischen den Versionen
Zur Navigation springen
Zur Suche springen
(Date Determinator run) |
Astro (Diskussion | Beiträge) (Source) |
||
Zeile 1: | Zeile 1: | ||
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. | 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= | =Beispiele= | ||
Zeile 249: | Zeile 250: | ||
|} | |} | ||
<!-- END TABLE --> | <!-- END TABLE --> | ||
=Source= | |||
Open-source, yada-yada! | |||
==date_determinator.rb== | |||
<pre> | |||
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 = /(<!-- BEGIN TABLE ")(.+?)(" -->)(.+?)(<!-- END TABLE -->)/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 | |||
</pre> | |||
==mediawiki.rb== | |||
<pre> | |||
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 =~ /<p class='error'>/ | |||
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(/<!-- start content -->(.+)<!-- end content -->/m) { |content,| | |||
@xhtml = REXML::Document.new("<xhtml>#{content}</xhtml>").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 | |||
</pre> |
Version vom 24. November 2005, 07:08 Uhr
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:
22.5. | 23.5. | 24.5. | |
---|---|---|---|
Cool Hacker | ja | nein | |
1337 Cracker | yezz! | jo | |
Astro | weiss noch nicht | ja | |
sum(ja) | 0 | 3 | 1 |
Realitätsabgleich 2
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. | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
matthias | j | n | j | j | j | j | j | j | n | j | j | j |
fukami | n | j | n | n | n | n | n | n | n | n | n | n |
Caldrin | n | j | n | n | j | n | n | n | n | n | j | n |
nulli | n | j | j | j | j | n | n | n | n | n | n | n |
toidinamai | j | j | j | j | j | j | j | j | j | j | j | j |
Astro | j | j | n | j | j | j | j | n | n | j | j | 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 = /(<!-- BEGIN TABLE ")(.+?)(" -->)(.+?)(<!-- END TABLE -->)/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 =~ /<p class='error'>/ 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(/<!-- start content -->(.+)<!-- end content -->/m) { |content,| @xhtml = REXML::Document.new("<xhtml>#{content}</xhtml>").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