Όνομα εργαλείου: generate-iw-actionlist.sh


Περιγραφή:

Το generate-iw-actionlist.sh το τρέχουμε πάνω σε αρχείο XML του Βικιλεξικού για να δημιουργήσουμε έναν κατάλογο προτεινόμενων αλλαγών (προσθέσεων ή αφαιρέσεων) στους συνδέσμους προς το ίδιο λήμμα σε άλλες γλώσσες (interwikilinks). Αυτοί οι σύνδεσμοι έχουν τη μορφή [[fr:λήμμα]] (για παράδειγμα) στο wikitext και εμφανίζονται ως όνομα γλώσσας στη στήλη αριστερά στην σελίδα που βλέπει ο αναγνώστης.

Βάζουμε τέτοιους συνδέσμους προς το ίδιο λήμμα σε αλλόγλωσσα Βικιλεξικά ανεξάρτητα αν οι σελίδες αυτές είναι ανακατευθύνσεις προς άλλα λήμματα ή όχι.

Η διαδικασία συνοπτικά έχει ως εξής:

  • Διαβάζουμε το αρχείο XML των δικών μας λημμάτων.
  • Βγάζουμε ό,τι δεν είναι λήμμα (πχ. σελίδες συζήτησης κλπ).
  • Μαζεύουμε τα interwikis που υπάρχουν ήδη για κάθε λήμμα και τα καταγράφουμε.
  • Για κάθε Βικιλεξικό, κατεβάζουμε από το dumps.wikimedia.org το αρχείο με όλους τους τίτλους των λημμάτων.
  • Ζητάμε τους τίτλους όλων των νέων σελίδων, όλων των διαγραφών, όλων των εισαγωγών και όλων των μετακινήσεων που έχουν γίνει από τότε που φτιάχτηκε το αρχείο τίτλων.
  • Δημιουργούμε ενημερωμένο αρχείο τίτλων.
  • Μαζεύουμε όλους τους τίτλους ανά εγχείρημα, και καταγράφουμε τα λήμματα μαζί με τις γλώσσες των Βικιλεξικών στα οποία υπάρχουν τα λήμματα.
  • Πετάμε από αυτή τη λίστα τα λήμματα που δεν υπάρχουν στο δικό μας Βικιλεξικό, δεν μας αφορούν.
  • Τώρα έχουμε: τα iwikis που υπάρχουν στις σελίδες του δικού μας Βικιλεξικού, και τα iwikis όπως θα έπρεπε να είναι. Συγκρίνουμε, και δημιουργούμε μια λίστα με αφαιρέσεις/προσθέσεις ανά λήμμα.


Εντολή

Το κυρίως πρόγραμμα, αυτό που τρέχει ο τελικός χρήστης, είναι το generate-iw-actionlist.sh.

Παράμετροι:

  • --skipdownload
Δεν ξανακατεβάζουμε τα αρχεία τίτλων λημμάτων, ακόμα κι αν υπάρχουν καινούργια που δεν τα έχουμε εμείς.
  • --skipupdate
Δεν ζητάμε τις νέες σελίδες, τις διαγραφές κλπ για τα διάφορα ΒΛ, ακόμα κι αν υπάρχουν.
  • όνομα-αρχείου-XML
Το πρόγραμμα διαβάζει σελίδες XML από αυτό το αρχείο. Θα πρέπει να είναι σε μορφή bz2 (bzip2).
  • όνομα-προτεινόμενων-αλλαγών-iwikis
Το πρόγραμμα γράφει τη λίστα αλλαγών σε αυτό το αρχείο, που αποθηκεύεται και στον τρέχοντα κατάλογο και στον κατάλογο iws_tmp.

Παραδείγματα:

  • generate-iw-actionlist.sh ~/getrcs/last_full.xml.bz2 iw-action-list.txt
  • generate-iw-actionlist.sh --skipdownload ~/getrcs/last_full.xml.bz2 iw-action-list.txt
  • generate-iw-actionlist.sh --skipdownload --skipupdate ~/getrcs/last_full.xml.bz2 iw-action-list.txt

Προϋποθέσεις:

  • περιβάλλον unix/linux ή περιβάλλον με τις εντολές sed, sort, bzcat, cat, grep
  • οι εντολές curl και wget
  • perl, python (ναι, και τις δύο γλώσσες, ήμουν αναποφάσιστος :-P)
  • ενημερωμένο αρχείο XML από το Βικιλεξικό (δείτε το getrcs.sh για περισσότερες πληροφορίες)

Προγράμματα:

Άλλα προγράμματα που καλούνται από το generate-iw-actionlist.sh είναι τα εξής:

περιγραφή: διαβάζει αρχείο XML με το περιεχόμενο των λημμάτων ενός Βικιλεξικού από το stdin και γράφει στο stdout μια λίστα των iwikis ανά λήμμα
παράμετροι: --dir (ο κατάλογος που περιέχει τα αρχεία all.dblist και closed.dblist)
παράδειγμα: cat full-ns0.xml | python dump-our-iws.py --dir=iws_tmp > our-iws.txt
  • getlemmasfromproject.pl
περιγραφή: κατεβάζει αρχείο με τους τίτλους λημμάτων ενός ορισμένου Βικιλεξικού, παίρνει τους τίτλους σελίδων που δημιουργήθηκαν, σβήστηκαν κλπ από τότε που φτιάχτηκε το αρχείο, ενημερώνει τη λίστα τίτλων, και τη γράφει στο αρχείο με όνομα xxwiktionary-latest-all-titles-in-ns0.final, στο οποίο το xx είναι κωδικός γλώσσας, πχ. fr, ru.
παράμετροι: --skipdownload δεν κατεβάζουμε αρχείο, --skipupdate δεν ενημερώνουμε τη λίστα λημμάτων, --lang ο κωδικός γλώσσας
παράδειγμα: perl ../getlemmasfromproject.pl --skipdownload --lang=ar
  • sortlemmas.sh
περιγραφή: ταξινόμηση περιεχομένου με βάση το locale C
παράμετροι: αρχείο που θα διαβαστεί, αρχείο που θα γραφτεί
παράδειγμα: sortlemmas.sh to-be-sorted outputfile
  • langs-per-lemma.pl
περιγραφή: διαβάζει από το stdin μια (ταξινομημένη) λίστα λημμάτων με τον κωδικό γλώσσας του Βικιλεξικού στο οποίο υπάρχει, και γράφει στο stdout κάθε λήμμα μαζί με όλες τις γλώσσες των ΒΛ του στην ίδια γραμμή
παράμετροι: δεν υπάρχουν
  • toss-extra-titles.py
περιγραφή: διαβάζει από το stdin μία λίστα λημμάτων με τις γλώσσες του καθενός στην ίδια γραμμή, διαβάζει από ένα αρχείο τα λήμματα που θέλουμε να κρατήσουμε, και γράφει μόνο αυτές τις γραμμές στο stdout
παράμετροι: --file αρχείο με λήμματα
παράδειγμα: cat langs-per-lemma.txt | python toss-extra-titles.py --file=our-iws.txt > their-iws.txt
  • check-our-iws.py
περιγραφή: διαβάζει δύο αρχεία, το ένα περιέχει τα iwikis που έχουμε εμείς και το άλλο περιέχει (υποθετικά) τα iwikis που θα έπρεπε να υπάρχει (δηλαδή ποια Βικιλεξικά έχουν πράγματι το ορισμένο λήμμα) και γράφει στο stdout μια λίστα προτεινόμενων αλλαγών iwikis
παράμετροι: --ouriwfile λίστα των δικών μας iwikis, --theiriwfile λίστα "πραγματικών" iwikis
παράδειγμα: check-our-iws.py --ouriwfile=our-iws.txt --theiriwfile=their-iws.txt > proposed-changes.txt


Αρχεία/κατάλογοι:

  • iws_tmp/
όλα τα αρχεία δημιουργούνται εδώ
  • all.dblist
λίστα όλων των ενεργών Βικιλεξικών
  • closed.dblist
λίστα όλων των κλειδωμένων Βικιλεξικών
  • xxwiktionary-latest-all-titles-in-ns0.gz
το xx αντιστοιχεί με κωδικό ISO για μια γλώσσα (πχ. en, fr)
αρχείο με τίτλους όλων των λημμάτων ενός Βικιλεξικού, όπως κατεβάστηκε
  • xxwiktionary-latest-all-titles-in-ns0
αρχείο με τίτλους όλων των λημμάτων ενός Βικιλεξικού, με χρόνοσφραγίδα
  • xxwiktionary-latest-all-titles-in-ns0.updated
αρχείο με τίτλους όλων των λημμάτων ενός Βικιλεξικού, ενημερωμένο με νέες σελίδες, διαγραφές κλπ.
  • xxwiktionary-latest-all-titles-in-ns0.final
αρχείο με τίτλους όλων των λημμάτων ενός Βικιλεξικού, με κωδικό γλώσσας
  • all-lemmas.txt
αρχείο με τίτλους όλων των λημμάτων όλων των Βικιλεξικών, με κωδικό γλώσσας
  • all-lemmas-sorted.txt
αρχείο με τίτλους όλων των λημμάτων όλων των Βικιλεξικών, με κωδικό γλώσσας, ταξινομημένο ανά λήμμα
  • langs-per-lemma.txt
αρχείο με τίτλους όλων των λημμάτων που υπάρχουν σε έστω κι ένα Βικιλεξικό, μαζί με τις γλώσσες των Βικιλεξικών στα οποία υπάρχουν
  • their-iws.txt
αρχείο με τίτλους όλων των λημμάτων που υπάρχουν στο δικό Βικιλεξικό, μαζί με τις γλώσσες των άλλων Βικιλεξικών στα οποία υπάρχουν


→ Πίσω στα Εργαλεία

generate-iw-actionlist.sh επεξεργασία

#!/bin/bash

# This script generates an iw action list from scratch.
# the contents of said file look like
#
# șarpe||addition:ru fr en ro io
# abut||removal:cs
# téléphone||removal:mg||addition:eu
#
# and so on.

# It takes 2 mandatory arguments: 
#
# 1) the name of the full xml file from el wikt,
#    presumably updated with getrcs.sh
#
# 2) the name of the output file for the action list.
#
# and two optional arguments:
#
# 1) --skipdownload
#
# 2) --skipupdate
#

# It produces a number of intermediate files,
# described below.  These are not removed, in case
# the user wants to reuse any of them at any time.
# In particular the downloaded lists of titles
# from each wiktionary is kept intact so that
# future updates won't re-download the same files
# if no newer one is available.

# All intermediate files are written to the subdir
# iws_tmp

# 

usage() {
  echo "Usage: $0 [--skipdownload] [--skipupdate] fullxmlfile outputfile"
  echo 
  echo "  fullxmlfile       name of the xml file with current page contents"
  echo "  outputfile        name of file to which we will write iw action list"
  echo "  --skipdownload    don't download new titles-ns0 files, work with existing ones"
  echo "  --skipupdate      don't update titles-ns0 files, work with existing data"
  echo 
  echo "For example:"
  echo "   $0 ~/getrcs/last_full.xml.bz2 iw-action-list.txt"
  echo "   $0 --skipdownload ~/getrcs/last_full.xml.bz2 iw-action-list.txt"
  echo "   $0 --skipdownload --skipupdate ~/getrcs/last_full.xml.bz2 iw-action-list.txt"
  exit 1
}

if [ "$#" -lt "2" -o "$#" -gt "4" ]; then
  usage
fi

skipdownload=""
skipupdate=""
infile=""
outfile=""

for i in $@; do
    if [ $i == "--skipdownload" ]; then
	skipdownload="--skipdownload"
    elif [ $i == "--skipupdate" ]; then
	skipupdate="--skipupdate"
    elif [ -z "$infile" ]; then
	infile="$i"
    else
	outfile="$i"
    fi
done
	
if [ ! -e "$infile" ]; then
  echo "Error: Failed to find file $infile"
  echo 
  usage
fi

if [ ! -e "iws_tmp" ]; then
    mkdir iws_tmp
fi

filetype=`file $infile | grep bzip2`
if [ -z "$filetype" ]; then
    echo "Input file should be bzip2-compressed file."
    echo
    usage
fi

echo "removing all pages not in ns0 from dump file"
bzcat $infile | perl xml-σελίδες-ns0.pl > iws_tmp/full-ns0.xml

echo "generating list of iws per lemma from our xml dump file"
#cat iws_tmp/full-ns0.xml | python dump-our-iws.py | grep -v 'unicode test: triggers problem' > iws_tmp/our-iws.txt
cat iws_tmp/full-ns0.xml | python dump-our-iws.py --dir=iws_tmp > iws_tmp/our-iws.txt

echo "getting list of all wiktionaries"
cd iws_tmp/
wget -N -q 'http://noc.wikimedia.org/conf/all.dblist'
wget -N -q 'http://noc.wikimedia.org/conf/closed.dblist'
cd ..

if [ ! -e "iws_tmp/all.dblist" ]; then
    echo "Failed to retrieve http://noc.wikimedia.org/conf/all.dblist, exiting"
    exit 1
fi
if [ ! -e "iws_tmp/closed.dblist" ]; then
    echo "Failed to retrieve http://noc.wikimedia.org/conf/closed.dblist, exiting"
    exit 1
fi

langs=`cat iws_tmp/all.dblist | grep wiktionary | sed -e 's/wiktionary//g;'`
echo "generating current list of lemmas for each project"
cd iws_tmp/
for i in $langs; do
    perl ../getlemmasfromproject.pl $skipdownload $skipupdate --lang=$i
done
cd ..

echo "generating list of all lemmas on all projects"
cat iws_tmp/*final > iws_tmp/all-lemmas.txt

echo "sorting list of lemmas"
bash sortlemmas.sh iws_tmp/all-lemmas.txt iws_tmp/all-lemmas-sorted.txt

echo "collecting languages per lemma"
cat iws_tmp/all-lemmas-sorted.txt | perl langs-per-lemma.pl | grep -vaP "\xc2\x85" > iws_tmp/langs-per-lemma.txt

echo "getting rid of all entries not contained in our wiktionary"
cat iws_tmp/langs-per-lemma.txt | python toss-extra-titles.py --file=iws_tmp/our-iws.txt > iws_tmp/their-iws.txt

echo "writing iw action list"
python check-our-iws.py --ouriwfile=iws_tmp/our-iws.txt --theiriwfile=iws_tmp/their-iws.txt > iws_tmp/$outfile

echo "making copy of $outfile in current directory"
cp iws_tmp/$outfile .

echo "done!  please check $outfile for your results."
exit 0

xml-σελίδες-ns0.pl επεξεργασία

Ο κώδικας βρίσκεται εδώ.

dump-our-iws.py επεξεργασία

#!/usr/bin/python
# -*- coding: utf-8  -*-

import sys, getopt
import re, codecs
sys.stdin = codecs.getreader("utf-8")(sys.stdin)
sys.stdout = codecs.getwriter("utf-8")(sys.stdout)

class languageLinks(object):
  def __init__(self, fileLocation = ""):
    if fileLocation:
      if not fileLocation.endswith('/'):
        fileLocation = fileLocation + '/'
    
    self.validLangs = self.getLanguages('wiktionary',fileLocation + 'all.dblist')
    self.obsoleteLangs = self.getLanguages('wiktionary',fileLocation + 'closed.dblist')

  def getLanguages(self, family, filename):
    langs = []
    try:
      langfile = open(filename,'r')
    except:
      print "failed to open file ", filename
      raise

    line = langfile.readline()
    while line != "":
      line = line.strip()
      if line.endswith(family):
        line = line.replace(family,'')
        # cases like zh_min_nan
        line = line.replace('_','-')
        langs.append(line)
      line = langfile.readline()
    langfile.close()
    return langs

class iwLinkRetriever(object):
  def __init__(self, fileDir = ""):
    self.langLinks = languageLinks(fileDir)
    self.regexes = {
        'comment':     r'(?s)<!--.*?-->',
        'nowiki':      r'<nowiki>.*?</nowiki>',
        'pre':         r'<pre>.*?</pre>',
        }
    tags = self.regexes.keys()
    self.toRemove = re.compile('|'.join([self.regexes[tag] for tag in tags]),
                           re.IGNORECASE | re.DOTALL)
    self.interwikiregexp =  re.compile(u'(?i)\[\[\s*(?:%s)\s*:\s*[^\]]+\]\]'
                                   % '|'.join(self.langLinks.validLangs + self.langLinks.obsoleteLangs))

  # from pywikipediabot
  def unescape(self, s):
    """Replace escaped HTML-special characters by their originals"""
    if '&' not in s:
        return s
    s = s.replace('&lt;', "<")
    s = s.replace('&gt;', ">")
    s = s.replace('&apos;', "'")
    s = s.replace('&quot;', '"')
    s = s.replace('&amp;', "&") # Must be last
    return s

  # from pywikipediabot
  def removeComments(self, text):
    # remove comments, also skip stuff in <nowiki> tags
    return self.toRemove.sub('', text)

  def getIWLinks(self, keimeno):
    iws = {}
    text = self.unescape(keimeno)
    text = self.removeComments(text)

    iws = self.interwikiregexp.findall(text)
    iwhash = {}
    for iw in iws:
      key,value = iw.split(':',1)
      key = key.strip()
      key = key.lstrip('[')
      value = value.strip()
      value = value.rstrip(']')
      iwhash[key] = value
    return iwhash

  def getiws(self, title, keimeno, site):
    interwiki = self.getIWLinks(keimeno)
    for key in interwiki.keys():
      # keep only if the iw points to the same word
      if interwiki[key] == title:
        sys.stdout.write('||')
        sys.stdout.write(key)
    sys.stdout.write('||')
    # include ourselves
    sys.stdout.write(site)
    print

class Xmlparse(object):
  def __init__(self, filename = None, fileDir = ""):
    self.filename = filename
    self.xmltags()
    self.getIWs = iwLinkRetriever(fileDir)

  def xmltags(self):
    self.titleStart = re.compile('<title>')
    self.titleContent = re.compile('<title>(.*)<\/title>')
    self.textStart = re.compile('<text ')
    self.textEnd = re.compile('<\/text>')
    self.redir = re.compile('<redirect ')

  def openfile(self):
    if (self.filename):
      self.fin = open(self.filename,"r", "utf-8")
    else:
      self.fin = sys.stdin

  def parsefile(self):
    self.openfile()
      
    pageTitle=""
    arxiko=""
    inText = False
    redir = False
    line = self.fin.readline()

    while not line == "":
      if inText:
        arxiko = arxiko + line
        if self.textEnd.search(line):
          inText = False
          sys.stdout.write(pageTitle)
          self.getIWs.getiws(pageTitle, arxiko, 'el')
          arxiko=""
      elif self.titleStart.search(line):
        result = self.titleContent.search(line)
        pageTitle=result.group(1)
        redir = False
      elif self.redir.search(line):
        redir = True
      elif self.textStart.search(line) and not redir:
        arxiko = arxiko + line
        if (not self.textEnd.search(line)):
          inText = True
        else:
          sys.stdout.write(pageTitle)
          self.getIWs.getiws(pageTitle, arxiko, 'el')
          arxiko=""
      line = self.fin.readline()

def usage(message = None):
  if message:
    print >> sys.stderr, message
    print >> sys.stderr, "Usage: dump-our-iws.py --dir=location"
    print >> sys.stderr, "  where location is the directory where all.dblist and"
    print >> sys.stderr, "  closed.dblist are located"
    sys.exit(1)

def getOpts():
  dir = ""
  try:
    (options, remainder) = getopt.gnu_getopt(sys.argv[1:], "", ['dir='])
  except:
    usage("Unknown option specified")
  for (opt, val) in options:
    if opt == "--dir":
      dir = val
  return dir

if __name__ == "__main__":
  fileDir = getOpts()
  xmlParser = Xmlparse(fileDir=fileDir)
  xmlParser.parsefile()

getlemmasfromproject.pl επεξεργασία

#!/usr/bin/perl

# Encoding : UTF8
# Is used with RAW XML !!!
# 

sub get_opts {
    my $download = 1;
    my $update = 1;
    if ($#ARGV > 2 || $#ARGV < 0) {
	print STDERR "Usage: $0 --skipdownload --skipupdate --lang=lang-code\n";
	exit 1;
    }
    foreach $arg (@ARGV) {
	if ($arg eq "--skipdownload") {
	    $download =0;
	}
	elsif ($arg =~ /^--lang=/) {
	    ($opt, $lang) = split(/=/,$arg);
	}
	elsif ($arg eq "--skipupdate") {
	    $update = 0;
	}
	else {
	    print STDERR "Usage: $0 --skipdownload --skipupdate --lang=lang-code\n";
	    exit 1;
	}
    }
    return($download, $update, $lang);
}

sub get_url {
    my $lang = shift(@_);

    my $url1 = "http://download.wikimedia.org/" ;
    my $url2 = "wiktionary/latest/" ;
    my $url3 = "wiktionary-latest-all-titles-in-ns0" ;
    my $url = "$url1$lang$url2$lang$url3.gz";
    return($url);
}

sub download_file {
  my $filename = shift(@_);
  my $url = shift(@_);

  my $retrycount = 5;
  my $result = `wget -N -q $url` ;
  while ($retrycount && ( ! -e "$filename.gz" ) ) {
      print STDERR "WARNING: failed to download $url\n";
      print STDERR "WARNING: retrying\n";
      sleep 6;
      $retrycount--;
      $result = `wget -N -q $url` ;
  }
  return ($result);
}

sub get_mtime_of_file_minus_fudge {
    my $filename = shift(@_);

    # first, mark all entries with timestamp
    my @statinfo = stat( $filename );
    if (@statinfo == () ) {
	return (0, "");
    }
    # get last modified
    my $time_in_secs = @statinfo[9];

    # adjust for length of time it might have taken to run the file, say 4 hour (this
    # is a vast overestimation)
    my $time_in_secs = $time_in_secs - (3600*4);
    my $ts = get_timestamp($time_in_secs);
    return($time_in_secs, $ts);
}

# takes time in secs as returned by time() for example
sub get_timestamp {
    my $time = shift(@_);

    # convert to this form:  <timestamp>2009-11-10T16:32:30Z</timestamp>
    ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($time);
    # year is counted since 1900 for gmtime
    $year += 1900;
    # month starts from 0 for gmtime
    $mon++;
    my $timestamp = sprintf("%4d-%02d-%02dT%02d:%02d:%02dZ", $year, $mon, $mday, $hour, $min, $sec);
    return($timestamp);
}

sub process_titles_ns0_file {
    my $timestamp = shift(@_);
    my $filename = shift(@_);

    local (*FIN);
    local (*OUT);

    open(FOUT, ">", "$filename");
    print (FOUT "$timestamp\n");

    open(FIN, "zcat -n '$filename.gz'|");
    # first line is crap
    my $line = <FIN>;
    while (<FIN>) {
	chomp($_);
	print(FOUT $_."|".$timestamp."\n");
    }
    close(FIN);
    close(FOUT);
}

sub create_titles_ts_hash {
    my $filename = shift(@_);
    my %wikititles = ();

    local (*FIN);

    open(FIN,"<",$filename);
    # first line is the timestamp (changes later than this will not be reflected
    # in the file)
    my $line = <FIN>;
    chomp($line);
    my $timestamp = $line;
    while (<FIN>) {
	chomp($_);
	my ($title, $ts) = split(/\|/,$_);
	$wikititles{$title} = $ts;
    }
    close(FIN);
    return($timestamp, %wikititles);
}

sub write_updated_file {
    my $filename = shift(@_);
    my $fileout = shift(@_);
    my $timestamp = shift(@_);
    my $wikititlesref = shift(@_);
    my %wikititles = %$wikititlesref;

    local (*FIN);
    local (*FOUT);

    open(FOUT, '>', $fileout);
    print(FOUT "$timestamp\n");
    foreach $key (keys(%wikititles)) {
    $key1 = $key;
    $key1 =~ s/&#039;/'/g;
	print(FOUT "$key1|$wikititles{$key}\n");
    }
    close(FOUT);
    return;
}

sub write_titles_with_langs {
    my $filename = shift(@_);
    my $fileout = shift(@_);
    my $lang = shift(@_);

    local (*FIN);

    open( FIN, "<", $filename ) ;
    open( FOUT, ">", $fileout ) ;

    my $line = <FIN> ; 
    # skip first line, it's timestamp
    while ( <FIN> ) {
	$_ =~ s/_/ /g;
	chomp($_);
	my ($title, $rest) = split(/\|/,$_);
	print (FOUT $title."||".$lang."\n");
    }
}

sub get_new_titles {
    my $rcstartdate = shift(@_);
    my $rcenddate = shift(@_);
    my $wikititlesref = shift(@_);
    my %wikititles = %$wikititlesref;
    my $lang = shift(@_);

    my $oldinput = "";
    while (1) {
	print (STDERR "getting new titles $rcstartdate to $rcenddate\n");
	$input=`curl -s --retry 10 -H Expect: -f "http://$lang.wiktionary.org/w/api.php?action=query&list=recentchanges&rclimit=500&rctype=new|edit&format=xml&rcstart=$rcstartdate&rcend=$rcenddate&rcnamespace=0&rctype=new"`;
	if ($input eq "") { 
	    die ("Error from curl, unable to get recent changes, bailing\n");
	}
	if ($oldinput eq $input) {
	    last;
	}
	if ($input =~ /<recentchanges \/>/) {
	    last;
	}
	$oldinput = $input;
	@chunks = split(/>/,$input);
	foreach $chunk (@chunks) {
	    if ($chunk =~ /<rc type=/) {
		if ($chunk =~ m/title="([^"]+)".*timestamp="([^"]+)"/) {
		    $this_title = $1;
		    $this_timestamp = $2;
		    unless ($wikititles{$this_title}) {
		        $wikititles{$this_title} = $this_timestamp;
		    } else {
		    $wikititles{$this_title} = $this_timestamp if ($wikititles{$this_title} lt $this_timestamp)	;
		    }
		}
	    }
	}
	# παίρνουμε το χρονοσφραγίδα από την τελευταία γραμμή
	$nextstartdate = $this_timestamp;
	$rcstartdate=$nextstartdate;
    }
    return(%wikititles);
}

sub get_moves {
    my $mvstartdate = shift(@_);
    my $mvenddate = shift(@_);
    my $wikititlesref = shift(@_);
    my %wikititles = %$wikititlesref;
    my $lang = shift(@_);

    $oldinput = "";
    while (1) {
	print (STDERR "getting moves $mvenddate to $mvstartdate\n");
	$input=`curl -s --retry 10 -H "Expect:" -f "http://$lang.wiktionary.org/w/api.php?action=query&list=logevents&letype=move&lelimit=500&format=xml&leend=$mvstartdate&lestart=$mvenddate&ledir=newer"`;
	if ($input eq "") { 
	    die ("Error from curl, unable to get moves, bailing\n");
	}
	if ($oldinput eq $input) {
	    last;
	}
	$oldinput = $input;
	if ($input =~ /<logevents \/>/) {
	    last;
	}
	@chunks = split(/item>/,$input);
	foreach $chunk (@chunks) {
	   if ($chunk =~ /<item logid/) {
	       if ($chunk =~ /ns="([0-9]*)" title="([^"]+)".*timestamp="([^"]+)".*<move new_ns="0" new_title="([^"]+)"/) {
			$old_title = $2;
			$this_timestamp = $3;
			$new_title = $4;
			if ($wikititles{$new_title} lt $this_timestamp) {
				$wikititles{$new_title} = $this_timestamp;
		        }
			if ($wikititles{$old_title}) {
			    if ($wikititles{$old_title} lt $this_timestamp) {
			    	$wikititles{$old_title} = $this_timestamp;
			    	if ($chunk =~ / suppressedredirect=""/) {
			        	delete($wikititles{$old_title});
		    		}
			     }
			}
               }
	   }
	}
	$nextstartdate = $this_timestamp;
	$mvenddate="$nextstartdate";
    }
    return(%wikititles);
}

sub get_imports {
    my $impstartdate = shift(@_);
    my $impenddate = shift(@_);
    my $wikititlesref = shift(@_);
    my %wikititles = %$wikititlesref;
    my $lang = shift(@_);

    $oldinput = "";
    while (1) {
	print (STDERR "getting imports $impstartdate to $impenddate\n");
	$input=`curl -s --retry 10 -H "Expect:" -f "http://$lang.wiktionary.org/w/api.php?action=query&list=logevents&letype=import&lelimit=500&format=xml&lestart=$impstartdate&leend=$impenddate"`;
	if ($input eq "") { 
	    die ("Error from curl, unable to get imports, bailing\n");
	}
	if ($oldinput eq $input) {
	    last;
	}
	if ($input =~ /<logevents \/>/) {
	    last;
	}
	$oldinput = $input;
	@chunks = split(/>/,$input);
	foreach $chunk (@chunks) {
	    if ($chunk =~ /<item logid/) {
		if ($chunk =~ / ns="0" /) {
		    if ($chunk =~ m/ title="([^"]+)".*timestamp="([^"]+)"/) {
			$this_title = $1;
			$this_timestamp = $2;
			if ($wikititles{$this_title}) {
			    if ($wikititles{$this_title} lt $this_timestamp) {
				$wikititles{$this_title} = $this_timestamp;
			    }
			}
		    	else { $wikititles{$this_title} = $this_timestamp; }
		    }
		}
	    }
	}
	$nextstartdate = $this_timestamp;
	$impstartdate="$nextstartdate";
    }
    return(%wikititles);
}

sub get_deletes {
    my $delstartdate = shift(@_);
    my $delenddate = shift(@_);
    my $wikititlesref = shift(@_);
    my %wikititles = %$wikititlesref;
    my $lang = shift(@_);

    $oldinput = "";
    while (1) {
	print (STDERR "getting deletes $delstartdate to $delenddate\n");
	$input=`curl -s --retry 10 -H "Expect:" -f "http://$lang.wiktionary.org/w/api.php?action=query&list=logevents&letype=delete&lelimit=500&format=xml&lestart=$delstartdate&leend=$delenddate"`;
	if ($input eq "") { 
	    die ("Error from curl, unable to get imports, bailing\n");
	}
	if ($oldinput eq $input) {
	    last;
	}
	if ($input =~ /<logevents \/>/) {
	    last;
	}
	$oldinput = $input;
	@chunks = split(/>/,$input);
	foreach $chunk (@chunks) {
	    if ($chunk =~ /<item logid/) {
		# deletions of pages, not revisions.
		if ($chunk =~ / action="delete" /) {
		    if ($chunk =~ m/ title="([^"]+)".*timestamp="([^"]+)"/) {
			$this_title = $1;
			$this_timestamp = $2;
			if ($wikititles{$this_title}) {
			    if ($wikititles{$this_title} lt $this_timestamp) {
				delete($wikititles{$this_title});
			    }
			}
		    }
		}
		# restored pages
		if ($chunk =~ / action="restore" /) {
		    if ($chunk =~ m/ title="([^"]+)".*timestamp="([^"]+)"/) {
		        $this_title = $1;
		        $this_timestamp = $2;
		        unless ($wikititles{$this_title}) {
		               $wikititles{$this_title} = $this_timestamp;
		        } 
		        else {
		            if ($wikititles{$this_title} lt $this_timestamp) {
			         $wikititles{$this_title} = $this_timestamp;
			    }
			}
		    }
                } 
	    }
	}
	$nextstartdate = $this_timestamp;
	$delstartdate="$nextstartdate";
    }
    return(%wikititles);
}


($download,$update,$lang) = get_opts();

print (STDERR "processing...\n");

$url = get_url($lang);
$filename = "$lang"."wiktionary-latest-all-titles-in-ns0" ;
print (STDERR "$url: ");
if ($download) {
    ($oldtime_in_secs, $oldtimestamp) = get_mtime_of_file_minus_fudge("$filename.gz");
    $result = download_file($filename, $url);
    ($time_in_secs, $timestamp) = get_mtime_of_file_minus_fudge("$filename.gz");
    # did we actually download it?
    if ($oldtime_in_secs != $time_in_secs) {
	print (STDERR "new file downloaded\n");
	# and have to process it too
	process_titles_ns0_file($timestamp,$filename);
	unlink "$filename.updated" if (stat("$filename.updated"));
	unlink "$filename.final" if (stat("$filename.final"));
    }
    else {
	print (STDERR "no download needed\n");
    }
}
else {
    print (STDERR "skipping download as requested\n");
}
($time_in_secs, $timestamp) = get_mtime_of_file_minus_fudge("$filename.gz");
if (! stat($filename)) {
    # we never created the title/timestamp file from the titles-ns0 file. do so now.
    process_titles_ns0_file($timestamp,$filename);
    # and read from it. 
    ($timestamp, %wikititles) = create_titles_ts_hash($filename);
}
# we created the title/timestamp file but never updated it
# so read from the original
elsif (! stat("$filename.updated")) {
    ($timestamp, %wikititles) = create_titles_ts_hash($filename);
}
# otherwise read from the updated file
else {
    ($timestamp, %wikititles) = create_titles_ts_hash("$filename.updated");
}

# urls all have languange name with - in them
# and so do the iwikis, so we change it here
$lang =~ s/_/-/g;

if ($update) {
    $enddate=$timestamp;
    $startdatesecs=time();
    $startdate=get_timestamp($startdatesecs);

    # call with startdate = time of latest change we want, 
    # enddate = time of oldest change we want
    %wikititles = get_new_titles($startdate, $enddate, \%wikititles, $lang);
    %wikititles = get_moves($startdate, $enddate, \%wikititles, $lang);
    %wikititles = get_imports($startdate, $enddate, \%wikititles, $lang);
    %wikititles = get_deletes($startdate, $enddate, \%wikititles, $lang);
}

# allow for a bit of drift
$startdate_fudged = get_timestamp($startdatesecs -60);
if ($update) {
    write_updated_file($filename, "$filename.updated", $startdate_fudged, \%wikititles);
}
write_titles_with_langs("$filename.updated", "$filename.final", $lang);

exit 0;

sortlemmas.sh επεξεργασία

#!/bin/bash
LC_ALL=C
export LC_ALL
sort $1 > $2

langs-per-lemma.pl επεξεργασία

#!/usr/bin/perl

binmode(STDOUT, ":utf8");
binmode(STDIN, ":utf8");

use encoding(UTF8);
use utf8;


$line = <STDIN>;
chomp($line);
($lemma, $value) = split(/\|\|/,$line,2);
@values = ();
push(@values, $value);
while (<STDIN>) { 
    chomp($_);
    ($curlemma, $value) = split(/\|\|/,$_,2);
    
    if ($lemma ne $curlemma) {
	print $lemma,"||", join('||',@values),"\n";
	@values = ();
	$lemma = $curlemma
    }
    push(@values, $value);
}

toss-extra-titles.py επεξεργασία

#!/usr/bin/python
# -*- coding: utf-8  -*-

import sys, getopt
import re, codecs
#reload(sys)
#sys.setdefaultencoding('utf-8')
sys.stdin = codecs.getreader("utf-8")(sys.stdin)
sys.stdout = codecs.getwriter("utf-8")(sys.stdout)

def getOurTitles(filename):
  ourTitles = set()
  fin = codecs.open(filename, 'r', 'utf-8')
  line = fin.readline()
  while line != "":
    lemma, junk = line.split('||',1)
    ourTitles.add(lemma)
    line = fin.readline()
#  for key in ourTitles:    
#    print key.encode('utf-8')
  fin.close()
  return ourTitles

def tossExtraTitles(keepTitles):
#  fin = codecs.open('langs-per-lemma.txt', 'r', 'utf-8')
  line = sys.stdin.readline()
  while line != "":
    lemma, langs = line.split('||',1)
    if lemma in keepTitles:
      sys.stdout.write(line)
    line = sys.stdin.readline()
#  fin.close()
  
def usage(message = None):
  if message:
    print >> sys.stderr, message
    print >> sys.stderr, "Usage: toss-extra-titles.py --file=filename"
    sys.exit(1)

def getOpts():
  try:
    (options, remainder) = getopt.gnu_getopt(sys.argv[1:], "", ['file='])
  except:
    usage("Unknown option specified")
  for (opt, val) in options:
    if opt == "--file":
      filename = val
  if not filename:
    usage()
  return filename

if __name__ == "__main__":
  filename = getOpts()
  titles = getOurTitles(filename)
  tossExtraTitles(titles)

check-our-iws.py επεξεργασία

#!/usr/bin/python
# -*- coding: utf-8  -*-

import sys, getopt
import re, codecs
reload(sys)
sys.setdefaultencoding('utf8')

def getOurTitles(filename):
  ourTitles = {}
  fin = codecs.open(filename, 'r', 'utf-8')
  line = fin.readline()
  while line != "":
    line = line.strip()
    lemma, langs = line.split('||',1)
    ourTitles[lemma] = set()
    if ('||' in langs):
      langList = langs.split('||')
    else:
      langList = [ langs ]
    for lang in langList:
      # skip us
      if (lang != 'el'):
        ourTitles[lemma].add(lang)
    line = fin.readline()
  fin.close()
  return ourTitles

def getTheirTitles(filename):
  theirTitles = {}
  fin = codecs.open(filename, 'r', 'utf-8')
  line = fin.readline()
  while line != "":
    line = line.strip()
    lemma, langs = line.split('||',1)
    theirTitles[lemma] = set()
    if ('||' in langs):
      langList = langs.split('||')
    else:
      langList = [ langs ]
    for lang in langList:
      # skip us
      if (lang != 'el'):
        theirTitles[lemma].add(lang)
    line = fin.readline()
  fin.close()
  return theirTitles

def usage(message = None):
  if message:
    print >> sys.stderr, message
    print >> sys.stderr, "Usage: toss-extra-titles.py --ouriwfile=filename --theiriwfile=filename"
    sys.exit(1)

def getOpts():
  ourfilename = ""
  theirfilename = ""
  try:
    (options, remainder) = getopt.gnu_getopt(sys.argv[1:], "", [ 'ouriwfile=', 'theiriwfile=' ])
  except:
    usage("Unknown option specified")
  for (opt, val) in options:
    if opt == "--ouriwfile":
      ourfilename = val
    elif opt == "--theiriwfile":
      theirfilename = val
  if not ourfilename or not theirfilename:
    usage()
  return [ ourfilename,theirfilename ]

if __name__ == "__main__":
  ourfilename, theirfilename = getOpts()
  ourTitles = getOurTitles(ourfilename)
  theirTitles = getTheirTitles(theirfilename)
  # now we see which ones don't match and produce a list
  for lemma in ourTitles:
    if lemma not in theirTitles:
      if len(ourTitles[lemma]):
        sys.stdout.write(lemma)
        sys.stdout.write('||removal:')
        sys.stdout.write(" ".join(ourTitles[lemma]))
        sys.stdout.write('\n')
    else:
      theyHave = theirTitles[lemma] - ourTitles[lemma]
      addition = " ".join(theyHave)
      weHave = ourTitles[lemma] - theirTitles[lemma]
      removal = " ".join(weHave)
      if addition:
        addition = 'addition' + ':' + addition
      if removal:
        removal = 'removal' + ':' + removal
      if addition or removal:
        sys.stdout.write(lemma)
        sys.stdout.write('||')
      if removal:
        sys.stdout.write(removal)
        if addition:
          sys.stdout.write('||')
      if addition:
        sys.stdout.write(addition)
      if addition or removal:
        sys.stdout.write('\n')