damned-lies r1074 - in branches/djamnedlies: . stats stats/conf
- From: claudep svn gnome org
- To: svn-commits-list gnome org
- Subject: damned-lies r1074 - in branches/djamnedlies: . stats stats/conf
- Date: Wed, 22 Oct 2008 21:15:50 +0000 (UTC)
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%% (%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]