damned-lies r1074 - in branches/djamnedlies: . stats stats/conf



Author: claudep
Date: Wed Oct 22 21:15:50 2008
New Revision: 1074
URL: http://svn.gnome.org/viewvc/damned-lies?rev=1074&view=rev

Log:
2008-10-22  Claude Paroz  <claude 2xlibre net>

	* stats/conf/settings.py: Added conf variables for notifications.
	* stats/models.py: Progress with updating statistics (still WIP for docs).
	* stats/potdiff.py: Copied from legacy damned lies.
	* stats/utils.py: Added several utility functions for updating statistics.

Added:
   branches/djamnedlies/stats/potdiff.py
Modified:
   branches/djamnedlies/ChangeLog
   branches/djamnedlies/stats/conf/settings.py
   branches/djamnedlies/stats/models.py
   branches/djamnedlies/stats/utils.py

Modified: branches/djamnedlies/stats/conf/settings.py
==============================================================================
--- branches/djamnedlies/stats/conf/settings.py	(original)
+++ branches/djamnedlies/stats/conf/settings.py	Wed Oct 22 21:15:50 2008
@@ -1,6 +1,15 @@
 from django.conf import settings
+import os
 
 DEBUG = getattr(settings, "DEBUG", True)
+WHEREAREWE = 'http://l10n.gnome.org/'
 WEBROOT = "/stats"
-POTDIR = "/home/claude/www/damned-lies/cvs/POT"
+WHOAREWE = 'danilo gnome org'
+
+# When in STRINGFREEZE, where to send notifications (gnome-i18n gnome org) on any POT changes
+NOTIFICATIONS_TO = 'gnome-i18n gnome org'
+
+# Local directories
 SCRATCHDIR = "/home/claude/www/damned-lies/cvs/"
+POTDIR = os.path.join(SCRATCHDIR, "POT")
+

Modified: branches/djamnedlies/stats/models.py
==============================================================================
--- branches/djamnedlies/stats/models.py	(original)
+++ branches/djamnedlies/stats/models.py	Wed Oct 22 21:15:50 2008
@@ -24,6 +24,7 @@
 from stats import utils
 from time import tzname
 import os, sys, re, commands
+import potdiff
 
 class Person(models.Model):
     old_id = models.CharField(max_length=50)
@@ -162,6 +163,13 @@
         """ Returns the path of the local checkout for the branch """
         return os.path.join(settings.SCRATCHDIR, self.module.vcs_type, self.module.name + "." + self.name)
     
+    def output_dir(self):
+        """ Directory where generated pot and po files are written on local system """
+        dirname = os.path.join(settings.POTDIR, self.module.name + "." + self.name)
+        if not os.path.isdir(dirname):
+            os.makedirs(dirname)
+        return dirname
+    
     def get_stats(self, typ):
         """ Get statistics list of type typ ('ui' or 'doc'), in a dict of lists, key is domain.name (POT in 1st position)"""
         stats = {}
@@ -189,6 +197,7 @@
         """ Update statistics for all po files from the branch """
         self.checkout()
         domains = Domain.objects.filter(module=self.module)
+        string_freezed = self.category.release.stringfrozen
         for dom in domains.all():
             domain_path = os.path.join(self.co_path(), dom.directory)
             if not os.access(domain_path, os.X_OK):
@@ -197,12 +206,96 @@
             errors = []
             if dom.dtype == 'ui':
                 # Run intltool-update -m to check for some errors
-                errors.extend(self.check_pot_regeneration(domain_path))
-            # TODO: update pot file and update DB
-            # TODO: update po files and update DB
-            #stats = Statistics.objects.filter(branch = self)
-            #for stat in stats.all():
-            #    stat.update()
+                errors.extend(utils.check_pot_regeneration(domain_path))
+            
+            # Update pot file
+            potfile = utils.generate_pot_file(domain_path, dom.potbase(), settings.DEBUG)
+            previous_pot = os.path.join(self.output_dir(), dom.potbase() + "." + self.name + ".pot")
+            if not potfile:
+                if settings.DEBUG: print >> sys.stderr, "Can't generate POT file for %s/%s." % (self.module.name, dom.directory)
+                if os.access(previous_pot, os.R_OK):
+                    # Use old POT file
+                    potfile = previous_pot
+                    errors.append(("error", N_("Can't generate POT file, using old one.")))
+                else:
+                    # Not sure if we should do something here
+                    continue
+
+            pot_has_changed = True
+
+            # If old pot already exists and we are in string freeze
+            if os.access(previous_pot, os.R_OK):
+                if string_freezed:
+                    diff = potdiff.diff(previous_pot, potfile, 1)
+                    if len(diff):
+                        self.notify_list(out_domain, diff)
+
+                # If old pot already exists, lets see if it has changed at all
+                diff = potdiff.diff(previous_pot, potfile)
+                if not len(diff):
+                    pot_has_changed = False
+                    
+            pot_stats = utils.po_file_stats(potfile, 0)
+            errors.extend(pot_stats['errors'])
+
+            if potfile != previous_pot and not utils.copy_file(potfile, previous_pot):
+                errors.append(('error', N_("Can't copy new POT file to public location.")))
+
+            try:
+                stat = Statistics.objects.get(language=None, branch=self, domain=dom)
+                stat.Untranslated = int(pot_stats['untranslated'])
+                stat.Date = datetime.datetime.now()
+                stat.information_set.clear()
+            except:
+                stat = Statistics(language = None, branch = self, domain = dom, Translated = 0,
+                                  Fuzzy = 0, Untranslated = int(pot_stats['untranslated']))
+            stat.information_set = pot_stats['errors']
+            stat.save()
+                
+            # Update po files and update DB with new stats
+            command = "msgmerge -o %(outpo)s %(pofile)s %(potfile)s"
+            for pofile in os.listdir(domain_path):
+                if pofile[-3:] != ".po":
+                    continue
+                lang = pofile[:-3]
+                outpo = os.path.join(self.output_dir(), dom.potbase() + "." + lang + ".po")
+
+                srcpo = os.path.join(domain_path, pofile)
+                if not force and not pot_has_changed and os.access(outpo, os.R_OK) and os.stat(srcpo)[8] < os.stat(outpo)[8]:
+                    continue
+
+                realcmd = command % {
+                    'outpo' : outpo,
+                    'pofile' : srcpo,
+                    'potfile' : potfile,
+                    }
+                if settings.DEBUG: print >>sys.stderr, realcmd
+                (error, output) = commands.getstatusoutput(realcmd)
+                if settings.DEBUG: print >> sys.stderr, output
+
+                langstats = utils.po_file_stats(outpo, 1)
+                langstats['errors'].extend(utils.check_lang_support(self.co_path(), domain_path, lang))
+
+                if settings.DEBUG: print >>sys.stderr, lang + ":\n" + str(langstats)
+                # Save in DB
+                try:
+                    stat = Statistics.objects.get(language__locale=lang, branch=self, domain=dom)
+                    stat.Translated = int(langstats['untranslated'])
+                    stat.Fuzzy = int(langstats['fuzzy'])
+                    stat.Untranslated = int(langstats['untranslated'])
+                    stat.Date = datetime.datetime.now()
+                    stat.information_set.clear()
+                except:
+                    try:
+                        language = Language.objects.get(locale=lang)
+                    except:
+                        language = Language(name=lang, locale=lang)
+                        language.save()
+                    stat = Statistics(language = language, branch = self, domain = dom, Translated = int(langstats['translated']),
+                                      Fuzzy = int(langstats['fuzzy']), Untranslated = int(langstats['untranslated']))
+                stat.information_set = langstats['errors']
+                stat.save()
+                    
     
     def checkout(self):
         """ Do a checkout or an update of the VCS files """
@@ -304,38 +397,6 @@
         else:
             return 1
 
-    def check_pot_regeneration(self, po_path):
-        """Check if there were any problems regenerating a POT file (intltool-update -m)."""
-        errors = []
-
-        command = "cd \"%(dir)s\" && rm -f missing notexist && intltool-update -m" % { "dir" : po_path, }
-        if settings.DEBUG: print >>sys.stderr, command
-        (error, output) = commands.getstatusoutput(command)
-        if settings.DEBUG: print >> sys.stderr, output
-
-        if error:
-            if settings.DEBUG: print >> sys.stderr, "Error running 'intltool-update -m' check."
-            errors.append( ("error", N_("Errors while running 'intltool-update -m' check.")) )
-
-        missing = os.path.join(po_path, "missing")
-        if os.access(missing, os.R_OK):
-            f = open(missing, "r")
-            errors.append( ("warn",
-                            N_("There are some missing files from POTFILES.in: %s")
-                            % ("<ul><li>"
-                            + "</li>\n<li>".join(f.readlines())
-                            + "</li>\n</ul>")) )
-
-        notexist = os.path.join(po_path, "notexist")
-        if os.access(notexist, os.R_OK):
-            f = open(notexist, "r")
-            errors.append(("error",
-                           N_("Following files are referenced in either POTFILES.in or POTFILES.skip, yet they don't exist: %s")
-                           % ("<ul><li>"
-                           + "</li>\n<li>".join(f.readlines())
-                           + "</li>\n</ul>")))
-        return errors
-
         
 class Domain(models.Model):
     module = models.ForeignKey(Module)
@@ -345,6 +406,12 @@
     directory = models.CharField(max_length=50)
     class Meta:
         db_table = 'domain'
+
+    def potbase(self):
+        if self.name[:2] == 'po':
+            return self.module.name + self.name[2:]
+        else:
+            return self.name
     
 class Release(models.Model):
     name = models.CharField(max_length=50)
@@ -550,12 +617,12 @@
     domain = models.ForeignKey(Domain) #alter table statistics add domain_id integer REFERENCES "domain" ("id");
     language = models.ForeignKey(Language, null=True) #alter table statistics add language_id integer REFERENCES "language" ("id");
     
-    Module = models.TextField(db_column='module')
+    Module = models.TextField(db_column='module', null=True)
     # whether this is about a document or UI translation
-    Type = models.CharField(db_column='type', max_length=3, choices=(('doc', 'Documentation'), ('ui', 'User Interface')))
-    Domain = models.TextField(db_column='domain')
-    Branch = models.TextField(db_column='branch')
-    Language = models.CharField(db_column='language', max_length=15)
+    Type = models.CharField(db_column='type', max_length=3, choices=(('doc', 'Documentation'), ('ui', 'User Interface')), null=True)
+    Domain = models.TextField(db_column='domain', null=True)
+    Branch = models.TextField(db_column='branch', null=True)
+    Language = models.CharField(db_column='language', max_length=15, null=True)
     Date = models.DateTimeField(db_column='date', auto_now_add=True)
     Translated = models.IntegerField(db_column='translated', default=0)
     Fuzzy = models.IntegerField(db_column='fuzzy', default=0)
@@ -613,14 +680,10 @@
         return "%d%%&nbsp;(%d/%d/%d)" % (self.tr_percentage(), self.Translated, self.Fuzzy, self.Untranslated)
     
     def filename(self):
-        if self.domain.name == 'po':
-            potbase = self.module_name()
-        else:
-            potbase = self.domain.name
         if self.Language:
-            return "%s.%s.%s.po" % (potbase, self.branch.name, self.Language)
+            return "%s.%s.%s.po" % (self.domain.potbase(), self.branch.name, self.Language)
         else:
-            return "%s.%s.pot" % (potbase, self.branch.name)
+            return "%s.%s.pot" % (self.domain.potbase(), self.branch.name)
             
     def pot_size(self):
         return int(self.Translated) + int(self.Fuzzy) + int(self.Untranslated)

Added: branches/djamnedlies/stats/potdiff.py
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/potdiff.py	Wed Oct 22 21:15:50 2008
@@ -0,0 +1,151 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2006-2007 Danilo Segan <danilo gnome org>.
+#
+# This file is part of Damned Lies.
+#
+# Damned Lies is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# Damned Lies is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Damned Lies; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+# Output differences between two POT files
+
+USE_DIFFLIB = 0
+
+def diff(pota, potb, only_additions = 0):
+    """Returns a list of differing lines between two files."""
+    f1 = open(pota, "r")
+    data1 = f1.read()
+    res1 = _parse_contents(data1)
+    res1.sort()
+
+    f2 = open(potb, "r")
+    data2 = f2.read()
+    res2 = _parse_contents(data2)
+    res2.sort()
+
+    if not USE_DIFFLIB:
+        # since we are working with sorted data, we can speed up the process by doing compares ourselves
+        # instead of using difflib stuff
+        i, j = 0, 0
+        result = []
+        while i < len(res1) and j < len(res2):
+            if res1[i] == res2[j]:
+                i+=1; j+=1
+            elif res1[i] < res2[j]:
+                if not only_additions:
+                    result.append("- " + res1[i])
+                i+=1
+            elif res1[i] > res2[j]:
+                result.append("+ " + res2[j])
+                j+=1
+        #print "\n".join(result)
+        return result
+    else:
+        import difflib
+        d = difflib.Differ()
+        result = list(d.compare(res1, res2))
+
+        onlydiffs = []
+        for line in result:
+            if line[0]!=" ":
+                onlydiffs.append(line)
+                #print line
+        return onlydiffs
+
+
+def _parse_contents(contents):
+    """Parse PO file data, returning a list of msgid's.
+
+    It also supports msgctxt (GNU gettext 0.15) and plural forms,
+    the returning format of each entry is:
+
+         [msgctxt::]msgid[/msgid_plural]"""
+
+    if len(contents) and contents[-1] != "\n": contents += "\n"
+
+    # state machine for parsing PO files
+    msgid = ""; msgstr = ""; msgctxt = ""; comment = ""; plural = ""; 
+    in_msgid = in_msgstr = in_msgctxt = in_msgid_plural = in_plural = 0
+
+    result = []
+    enc = "UTF-8"
+
+    lines = contents.split("\n")
+    lines.append("\n")
+    for line in lines:
+        line = line.strip()
+
+        if line == "":
+            if in_msgstr and msgid != "":
+                onemsg = ""
+                
+                if msgctxt: onemsg += ('"' + msgctxt + '"::')
+                onemsg += ('"' + msgid + '"')
+                if plural: onemsg += ('/"' + plural + '"')
+
+                result.append(onemsg)
+
+            elif in_msgstr and msgid == "":
+                # Ignore PO header
+                pass
+
+            msgid = ""; msgstr = ""; msgctxt = ""
+            in_msgid = 0; in_msgstr = 0; in_msgctxt = 0
+            flags = []; sources = []; othercomments = {}
+            plural = ""; plurals = []; in_msgid_plural = 0; in_plural = 0
+
+        elif line[0] == "\"" and line[-1] == "\"":
+            if in_msgid:
+                if in_msgid_plural:
+                    plural += line[1:-1]
+                else:
+                    msgid += line[1:-1]
+            elif in_msgctxt:
+                msgctxt += line[1:-1]
+            elif in_msgstr:
+                pass
+            else:
+                raise Exception()
+
+        elif line[0] == "#":
+            # Ignore any comments, flags, etc.
+            continue
+
+        elif line[:12] == "msgid_plural" and in_msgid:
+            in_msgid_plural = 1
+            plural = line[13:].strip()[1:-1]
+        elif line[:5] == "msgid" and not in_msgid:
+            in_msgctxt = 0
+            in_msgid = 1
+            msgid = line[6:].strip()[1:-1]
+        elif line[:7] == "msgctxt" and not in_msgid:
+            in_msgctxt = 1
+            msgctxt = line[8:].strip()[1:-1]
+        elif line[:7] == "msgstr[" and in_msgid_plural:
+            in_msgstr = 1
+            in_msgid = 0
+            in_msgctxt = 0
+        elif line[:6] == "msgstr" and in_msgid:
+            in_msgstr = 1
+            in_msgid = 0
+            in_msgctxt = 0
+        else:
+            pass
+    return result
+
+if __name__ == "__main__":
+    import sys
+    print "\n".join(diff(sys.argv[1], sys.argv[2]))
+    

Modified: branches/djamnedlies/stats/utils.py
==============================================================================
--- branches/djamnedlies/stats/utils.py	(original)
+++ branches/djamnedlies/stats/utils.py	Wed Oct 22 21:15:50 2008
@@ -1,5 +1,6 @@
 # -*- coding: utf-8 -*-
 #
+# Copyright (c) 2006-2007 Danilo Segan <danilo gnome org>.
 # Copyright (c) 2008 Claude Paroz <claude 2xlibre net>.
 #
 # This file is part of Damned Lies.
@@ -18,8 +19,9 @@
 # along with Damned Lies; if not, write to the Free Software Foundation, Inc.,
 # 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-from django.utils.translation import ugettext as _
-import re, time
+from django.utils.translation import ugettext as _, ugettext_noop as N_
+from stats.conf import settings
+import sys, os, re, time, commands
 
 def obfuscateEmail(email):
     if email:
@@ -42,6 +44,220 @@
     replacements = {"<ul>": "\n", "</ul>": "\n", "<li>": " * ", "\n</li>": "", "</li>": ""}
     return multiple_replace(replacements, string)
 
+def check_pot_regeneration(po_path):
+    """Check if there were any problems regenerating a POT file (intltool-update -m).
+       Return a list of errors """
+    errors = []
+
+    command = "cd \"%(dir)s\" && rm -f missing notexist && intltool-update -m" % { "dir" : po_path, }
+    if settings.DEBUG: print >>sys.stderr, command
+    (error, output) = commands.getstatusoutput(command)
+    if settings.DEBUG: print >> sys.stderr, output
+
+    if error:
+        if settings.DEBUG: print >> sys.stderr, "Error running 'intltool-update -m' check."
+        errors.append( ("error", N_("Errors while running 'intltool-update -m' check.")) )
+
+    missing = os.path.join(po_path, "missing")
+    if os.access(missing, os.R_OK):
+        f = open(missing, "r")
+        errors.append( ("warn",
+                        N_("There are some missing files from POTFILES.in: %s")
+                        % ("<ul><li>"
+                        + "</li>\n<li>".join(f.readlines())
+                        + "</li>\n</ul>")) )
+
+    notexist = os.path.join(po_path, "notexist")
+    if os.access(notexist, os.R_OK):
+        f = open(notexist, "r")
+        errors.append(("error",
+                       N_("Following files are referenced in either POTFILES.in or POTFILES.skip, yet they don't exist: %s")
+                       % ("<ul><li>"
+                       + "</li>\n<li>".join(f.readlines())
+                       + "</li>\n</ul>")))
+    return errors
+
+def generate_pot_file(vcspath, potbase, verbose):
+    """ Return the pot file generated or empty string on error """
+    
+    command = "cd \"%(dir)s\" && intltool-update -g '%(domain)s' -p" % {
+        "dir" : vcspath,
+        "domain" : potbase,
+        }
+    if verbose: print >>sys.stderr, command
+    (error, output) = commands.getstatusoutput(command)
+    if verbose: print >> sys.stderr, output
+
+    potfile = os.path.join(vcspath, potbase + ".pot")
+
+    if error or not os.access(potfile, os.R_OK):
+        return ""
+    else:
+        return potfile
+
+def po_file_stats(pofile, msgfmt_checks = 1):
+
+    errors = []
+
+    try: os.stat(pofile)
+    except OSError: errors.append(("error", N_("PO file '%s' doesn't exist.") % pofile))
+
+    if msgfmt_checks:
+        command = "LC_ALL=C LANG=C LANGUAGE=C msgfmt -cv -o /dev/null %s" % pofile
+    else:
+        command = "LC_ALL=C LANG=C LANGUAGE=C msgfmt --statistics -o /dev/null %s" % pofile
+
+    if settings.DEBUG: print >>sys.stderr, command
+    (error, output) = commands.getstatusoutput(command)
+    if settings.DEBUG: print >>sys.stderr, output
+
+    if error:
+        if msgfmt_checks:
+            errors.append(("error", N_("PO file '%s' doesn't pass msgfmt check: not updating.") % (pofile)))
+        else:
+            errors.append(("error", N_("Can't get statistics for POT file '%s'.") % (pofile)))
+
+    if msgfmt_checks and os.access(pofile, os.X_OK):
+        errors.append(("warn", N_("This PO file has an executable bit set.")))
+
+    r_tr = re.search(r"([0-9]+) translated", output)
+    r_un = re.search(r"([0-9]+) untranslated", output)
+    r_fz = re.search(r"([0-9]+) fuzzy", output)
+
+    if r_tr: translated = r_tr.group(1)
+    else: translated = 0
+    if r_un: untranslated = r_un.group(1)
+    else: untranslated = 0
+    if r_fz: fuzzy = r_fz.group(1)
+    else: fuzzy = 0
+
+    if msgfmt_checks:
+        # Lets check if PO files are in UTF-8
+        command = ("LC_ALL=C LANG=C LANGUAGE=C " +
+                   "msgconv -t UTF-8 %s |" +
+                   "diff -i -u %s - >/dev/null") % (pofile,
+                                                    pofile)
+        if settings.DEBUG: print >>sys.stderr, command
+        (error, output) = commands.getstatusoutput(command)
+        if settings.DEBUG: print >>sys.stderr, output
+        if error:
+            myfile = os.path.basename(pofile)
+            errors.append(("warn",
+                           N_("PO file '%s' is not UTF-8 encoded.") % (myfile)))
+    return {
+        'translated' : translated,
+        'fuzzy' : fuzzy,
+        'untranslated' : untranslated,
+        'errors' : errors,
+        }
+
+def check_lang_support(module_path, po_path, lang):
+    """Checks if language is listed in one of po/LINGUAS, configure.ac or configure.in"""
+
+    LINGUAShere = os.path.join(po_path, "LINGUAS")
+    LINGUASpo = os.path.join(module_path, "po", "LINGUAS") # if we're in eg. po-locations/
+    configureac = os.path.join(module_path, "configure.ac")
+    configurein = os.path.join(module_path, "configure.in")
+
+    errors = []
+
+    # is "lang" listed in either of po/LINGUAS, ./configure.ac(ALL_LINGUAS) or ./configure.in(ALL_LINGUAS)
+    in_config = 0
+    for LINGUAS in [LINGUAShere, LINGUASpo]:
+        if os.access(LINGUAS, os.R_OK):
+            lfile = open(LINGUAS, "r")
+            for line in lfile:
+                line = line.strip()
+                if len(line) and line[0]=="#": continue
+                if lang in line.split(" "):
+                    if settings.DEBUG: print >>sys.stderr, "Language '%s' found in LINGUAS." % (lang)
+                    in_config = 1
+                    break
+            lfile.close()
+            if not in_config:
+                errors.append(("warn", N_("Entry for this language is not present in LINGUAS file.")))
+            return errors
+
+    for configure in [configureac, configurein]:
+        if not in_config and os.access(configure, os.R_OK):
+            cfile = open(configure, "r")
+            lines = []
+            prev = ""
+            for line in cfile:
+                line = prev + line.strip()
+                if line.count('"') % 2 == 1:
+                    prev = line
+                else:
+                    lines.append(line)
+                    prev = ""
+
+            for line in lines:
+                line = line.strip()
+                test = re.search('ALL_LINGUAS\s*[=,]\s*"([^"]*)"', line)
+                if test:
+                    value = test.groups(1)[0]
+                    if lang in value.split(" "):
+                        if settings.DEBUG: print >>sys.stderr, "Language '%s' found in %s." % (lang, configure)
+                        in_config = 1
+                    break
+            cfile.close()
+            if not in_config:
+                errors.append(("warn", N_("Entry for this language is not present in ALL_LINGUAS in configure file.")))
+            return errors
+
+    errors.append(("warn", N_("Don't know where to look if this language is actually used, ask the module maintainer.")))
+    return errors
+
+def copy_file(file1, file2):
+    try:
+        fin = open(file1, "rb")
+        fout = open(file2, "wb")
+
+        block = fin.read(16*1024)
+        while block:
+            fout.write(block)
+            block = fin.read(16*1024)
+        fout.close()
+        fin.close()
+
+        if os.access(file2, os.R_OK):
+            return 1
+        else:
+            return 0
+    except:
+        return 0
+
+def notify_list(out_domain, diff):
+    """Send notification about string changes described in diff.
+
+    Uses settings.NOTIFICATIONS_TO as "to" address,
+    settings.WHOAREWE as "from" address, and sends
+    using SMTP on localhost:25."""
+    import smtplib
+    from email.mime.text import MIMEText
+    text = u"""This is an automatic notification from status generation scripts on:
+%(ourweb)s.
+
+There have been following string additions to module '%(module)s':
+
+%(potdiff)s
+
+Note that this doesn't directly indicate a string freeze break, but it
+might be worth investigating.
+""" % { 'module' : out_domain,
+    'ourweb' : settings.WHEREAREWE,
+    'potdiff' : "\n    ".join(diff).decode('utf-8') }
+
+    msg = MIMEText(text.encode('utf-8'), 'plain', 'utf-8')
+    msg['Subject'] = "String additions to '%s'" % (out_domain)
+    msg['From'] = "GNOME Status Pages <%s>" % (settings.WHOAREWE)
+    msg['To'] = settings.NOTIFICATIONS_TO
+
+    s = smtplib.SMTP()
+    s.connect()
+    s.sendmail(settings.WHOAREWE, settings.NOTIFICATIONS_TO, msg.as_string())
+    s.close()
+
 class Profiler():
     def __init__(self):
         self.start = time.clock()



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