#!/usr/bin/python
#
# (C) Copyright 2005 Nuxeo SARL <http://nuxeo.com>
# (C) Copyright 2005 Unilog <http://unilog.com>
# Authors:
# M.-A. Darche <madarche@nuxeo.com>
# G. de la Rochemace <gdelaroch@unilog.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.
#
# $Id: i18n_coverage 24779 2005-07-11 12:51:12Z madarche $
"""Generation of a 'po' translated graphic for all the products.

The dependencies are:
python-gdchart (Python interface to GDChart)
libgdchart-gd1-noxpm (Generate graphs using the GD library)
"""

import os, os.path, sys, string, re
from time import strftime, gmtime
from optparse import OptionParser

try:
    import gdchart
except ImportError:
    # Program will work, but without graphic generation
    gdchart = None

LANGUAGE_NAMES = {'en': 'English',
                  'fr': 'French',
                  'it': 'Italian',
                  'es': 'Spanish',
                  'eu': 'Basque',
                  'ro': 'Romanian',
                  'de': 'German',
                  'mg': 'Malagasy',
                  'nl': 'Dutch',
                  'sv': 'Swedish',
                  'pt_br': 'Brazilian',
                  'pt_BR': 'Brazilian',
                  }

DEFAULT_GRAPHIC_FILE_PATH = 'i18n_coverage.png'

# Color coefficient for 3D Bar determined by the percentage
COLOR_STEP = 2

LANGUAGES_FOR_MORE_DETAILS = ['fr', 'en']

TIME_FORMAT = '%Y-%m-%d %H:%M %Z'
time_generated = strftime(TIME_FORMAT, gmtime())

products = []

global detail
detail = 0


def execArgs():
    """Analyze command line arguments.
    """
    global detail, products
    usage = """usage: %prog [options] [Products]

Examples:
$ %prog --vverbose --dir /var/lib/zope2.7/instance_1/Products CPSDefault CPSDirectory CPSBlog -eCPSSkins

$ cd /var/lib/zope2.7/instance/my_instance/Products
$ %prog -v CPS* -eCPSSkins
"""
    parser = OptionParser(usage=usage)

    parser.add_option('-v', '--verbose',
                      action='store_true',
                      dest='verbose',
                      default=False,
                      help="Display more details")

    parser.add_option('--vverbose',
                      action='store_true',
                      dest='veryverbose',
                      default=False,
                      help="Display all the details on fr.po and en.po")

    parser.add_option('-d', '--dir',
                      action="store",
                      type="string",
                      dest="directory",
                      default=".",
                      help="Products directory")

    parser.add_option('-e', '--exclude',
                      action="store",
                      type="string",
                      dest="excluded_products",
                      default=None,
                      help="Products name excluded")

    parser.add_option('-o', '--outfile',
                      action="store",
                      type="string",
                      dest="outfile",
                      default=None,
                      help="Destination file for the graphic. "
                      "Default is: %s" % DEFAULT_GRAPHIC_FILE_PATH)

    (options, args) = parser.parse_args()

    path = options.directory

    if len(args) != 0:
        products = args
        if options.excluded_products:
            excluded_products_names = options.excluded_products.split(',')
            products = [x for x in products if x not in excluded_products_names]

        if options.verbose:
            detail = 1

        if options.veryverbose:
            detail = 2

        if options.outfile:
            makeChart(path, options.outfile)
        else:
            makeChart(path)
    else:
        log("Products names must be passed as arguments")


# #.   <span>Go to the
# #.     menu</span>
# #: from ../skins/cps_default/accessibility.pt
# msgid "accessibility_shortcut_menu"
# msgstr "Go to the menu"
#
TRANSLATION_REGEXP = re.compile(
    r'((\nmsgid "[^\n]+?"\n|msgid ""\n("[^\n]+"\n)+?)msgstr ")',
    re.DOTALL)

def getLanguagesInformations(path):
    """Return a list of all language names included in the
    whole CPS products, example: ['fr', 'en', ...]
    """
    list_po_name = []
    list_total_msgids = []
    po_path = path
    for product in products:
        po_path = os.path.join(path, '%s/i18n/' % product)
        if os.path.exists(po_path):
            for f in os.listdir(po_path):
                if f.endswith('.po'):
                    file_name = os.path.splitext(f)[0]
                    if file_name not in list_po_name:
                        list_po_name.append(file_name)

                if f.endswith('.pot') and not f.startswith('.'):
                    file = open(os.path.join(po_path, f), 'r')
                    file_content = file.read()
                    file.close()
                    match_translation = TRANSLATION_REGEXP.findall(file_content)
                    for m in match_translation:
                        msgid = m[0].split("\nmsgstr")[0]
                        if msgid not in list_total_msgids:
                            list_total_msgids.append(msgid)

        else:
            continue

    if len(list_po_name) == 0:
        log("Directory not found: %s" % po_path)
        sys.exit(1)
    else:
        return [list_po_name, len(list_total_msgids)]



def getPercent(st):
    """Return the percent from a string like this:
       fr: 1952 msgids, 6 fuzzy, 25 empty -> 1921 translated (98% done)
       Return '98'
    """
    temp = st.split("(")[1]
    percent = temp.split("%")[0]

    return int(percent)



def sortFunction(str1, str2):
    """Determine the sort from the percent between two string :
       str1 : fr: 1952 msgids, 6  fuzzy, 25 empty -> 1921 translated (98% done)
       str2 : en: 1952 msgids, 20 fuzzy, 61 empty -> 1871 translated (95% done)
       Return '1'
    """
    if getPercent(str1) < getPercent(str2):
        return 1
    else:
        return -1



# DOTALL: Make the "." special character match any character at all, including a
# newline; without this flag, "." will match anything except a newline.
#
# #, fuzzy
# msgid "CatalogNavigation"
# msgstr "Navigation"
#
FUZZY_REGEXP = re.compile(
    r'((#, fuzzy\nmsgid "[^\n]+?"\n|#, fuzzy\nmsgid ""\n("[^\n]+"\n)+?)msgstr ")',
    re.DOTALL)

# msgid "batch_next_x_items"
# msgstr ""
#
# msgid ""
# "Posted by ${item_creator} @ ${item_modification} <block condition=\"info\"> "
# "<span>Label Status</span> <span content=\"info/review_state\" attributes="
# "\"class info/review_state\">Work</span> </block>"
# msgstr ""
#
EMPTY_REGEXP = re.compile(
    r'((msgid "[^\n]+?"\n|msgid ""\n("[^\n]+"\n)+?)msgstr ""\n\n)',
    re.DOTALL)



def makeChart(path, export_file_path=DEFAULT_GRAPHIC_FILE_PATH):
    """Generate the graphic thanks to the 'po' and 'pot' files from all Products.
    """
    global detail
    available_languages, msgids_total = getLanguagesInformations(path)
    names = ['Total of messages']
    values = [msgids_total]
    colors = [0x00ff00] # green
    size = (800, 500)

    nb_languages = len(available_languages)

    if (detail != 0):
        log("\nNumber of languages found: %s" % (nb_languages))
        log("Number of msgids found: %s" % (msgids_total))

    list_results = []

    for language in available_languages:

        list_fuzzy_msgids = []
        list_empty_msgids = []
        list_msgids = []

        # We display only the 'fr.po' and 'en.po' fuzzy and empty msgids because
        # English and French are the two most important language for CPS and
        # also because the other languages have to many fuzzy and empty msgids.
        if (detail == 2):
            if language in LANGUAGES_FOR_MORE_DETAILS:
                log('\n\n//////////////// %s ////////////////\n' % language)

        value = 0

        for product in products:
            file_path = os.path.join(path, '%s/i18n/%s.po'
                                     % (product, language))
            if os.path.isfile(file_path):
                file = open(file_path, 'r')
                file_content = file.read()
                file.close()


                match_translation = TRANSLATION_REGEXP.findall(file_content)
                for m in match_translation:
                    msgid = m[0].split("\nmsgstr")[0]
                    if msgid not in list_msgids:
                        list_msgids.append(msgid)


                match_fuzzy = FUZZY_REGEXP.findall(file_content)
                for m in match_fuzzy:
                    msgid = m[0].split("\nmsgstr")[0]
                    msgid = msgid.split("fuzzy\n")[1]
                    if msgid not in list_fuzzy_msgids:
                        list_fuzzy_msgids.append(msgid)
                        if (detail == 2):
                            if language in LANGUAGES_FOR_MORE_DETAILS:
                                log('%-15s -  fuzzy | %s' % (product, msgid))


                match_empty = EMPTY_REGEXP.findall(file_content)
                for m in match_empty:
                    msgid = m[0].split("\nmsgstr")[0]
                    if msgid not in list_empty_msgids:
                        list_empty_msgids.append(msgid)
                        if (detail == 2):
                            if language in LANGUAGES_FOR_MORE_DETAILS:
                                log('%-15s -  empty | %s' % (product, msgid))

            else:
                continue


        nb_msgid = len(list_msgids)
        nb_fuzzy = len(list_fuzzy_msgids)
        nb_empty = len(list_empty_msgids)
        value = nb_msgid - nb_fuzzy - nb_empty

        complete = int(value / float(msgids_total) * 100)

        result_line = "%-5s:  %-4s msgids,   %-4s fuzzy,   %-4s empty " \
                       " ->  %-5s translated (%-3d%% done)" % (language,
                       nb_msgid, nb_fuzzy, nb_empty, value, complete)


        if nb_msgid > msgids_total:
            result_line = "%s [PROBLEM: %s > %s]" % (result_line, nb_msgid,
                                                     msgids_total)
        if complete > 100:
            complete = 100

        if value > msgids_total:
            value = msgids_total

        list_results.append(result_line)
        names.append("%s (%s)"
                     % (LANGUAGE_NAMES.get(language, language), language))
        values.append(value)
        colors.append(colors[0] * (101 - complete) * COLOR_STEP)

    if (detail != 0):
        print
        list_results.sort(sortFunction)
        for result in  list_results:
            print result


    # Sort by number of translated messages
    # keeping 'en' always on first position
    z = zip(values[1:], names[1:], colors[1:])
    z.sort(lambda x, y: x[0] == y[0] and cmp((x[0], x[1]), (y[0], y[1])) or
                                         cmp((y[0], y[1]), (x[0], x[1])))
    values = values[0:1]
    names = names[0:1]
    colors = colors[0:1]
    for v, n, c in z:
        values.append(v)
        names.append(n)
        colors.append(c)

    if gdchart is not None:
        chart_title = ("Translation coverage of all CPS products (%s)"
                       % time_generated)
        options = {'bg_color': 0xffffff,
                   'border': gdchart.GDC_BORDER_ALL,
                   'title': chart_title,
                   'ext_color': colors,
                   'format': gdchart.GDC_PNG,
                   'xtitle': "Languages",
                   'ytitle': "Number of translated messages",
                   }

        gdchart.option(**options)

        gdchart.chart(gdchart.GDC_3DBAR,
                      size,
                      export_file_path,
                      names,
                      values,
                      )

        if (detail != 0):
            log("\nGraphic created: %s\n" % (export_file_path))

    else:
        log("\nGraphic not created: gdchart not install")


def log(message):
    """Log the given message to stderr.
    """
    print >> sys.stderr, message



execArgs()
